Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
# Dependencies
node_modules/

# Build output
# Build output (prod, dev, and self targets each emit to their own directory)
**/dist/
**/dist-dev/
**/dist-self/
*.tsbuildinfo
.turbo/

Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,15 @@ The CLI bakes its own invocation string into the skill, command, and recipe
content it installs. Three build targets pick that string, all driven by the
`TASKLESS_BUILD_TARGET` env var via Vite `define` (same source files, no edits):

| Command | Baked invocation | Use for |
| ----------------- | --------------------------------------- | ------------------------------------------------------------------------------------ |
| `pnpm build` | `npx @taskless/cli` | Production / published builds (default). |
| `pnpm build:dev` | `node <abs>/packages/cli/dist/index.js` | Validating this build from **another** repo (absolute path resolves anywhere). |
| `pnpm build:self` | `node packages/cli/dist/index.js` | Dogfooding **in this repo** (path is repo-root-relative; run the CLI from the root). |
Each target also emits to its own directory so the three never overwrite one
another — prod → `dist/`, dev → `dist-dev/`, self → `dist-self/` (all
gitignored):

| Command | Output dir | Baked invocation | Use for |
| ----------------- | ------------ | ------------------------------------------- | ------------------------------------------------------------------------------------ |
| `pnpm build` | `dist/` | `npx @taskless/cli` | Production / published builds (default). |
| `pnpm build:dev` | `dist-dev/` | `node <abs>/packages/cli/dist-dev/index.js` | Validating this build from **another** repo (absolute path resolves anywhere). |
| `pnpm build:self` | `dist-self/` | `node packages/cli/dist-self/index.js` | Dogfooding **in this repo** (path is repo-root-relative; run the CLI from the root). |

`pnpm build:self` builds the CLI with the relative invocation and then runs
`taskless init --no-interactive` to install into this repo — so `.claude` gets
Expand Down
2 changes: 2 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export default tseslint.config(
ignores: [
"node_modules/",
"**/dist/",
"**/dist-dev/",
"**/dist-self/",
"**/*.config.js",
"**/*.config.ts",
".lintstagedrc.js",
Expand Down
2 changes: 1 addition & 1 deletion openspec/specs/infrastructure/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ The repository SHALL have `turbo` as a root devDependency and a `turbo.json` con

### Requirement: Build pipeline runs across all packages

The root `pnpm build` command SHALL invoke `turbo run build`, which runs the `build` script in every workspace package that defines one. Build outputs (`dist/**`) SHALL be cached.
The root `pnpm build` command SHALL invoke `turbo run build`, which runs the `build` script in every workspace package that defines one. Build outputs (`dist/**`, `dist-dev/**`, `dist-self/**`) SHALL be cached.

#### Scenario: Root build command runs CLI build

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"build:dev": "pnpm --filter @taskless/cli build:dev",
"build:self": "run-s build:self:compile build:self:install",
"build:self:compile": "pnpm --filter @taskless/cli build:self",
"build:self:install": "node packages/cli/dist/index.js init --no-interactive",
"build:self:install": "node packages/cli/dist-self/index.js init --no-interactive",
"bump": "run-s bump:version bump:sync",
"bump:sync": "tsx scripts/sync-skill-versions.ts",
"bump:version": "changeset version",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"noUncheckedIndexedAccess": true
},
"include": ["src", "test", "scripts"],
"exclude": ["node_modules", "dist", "test/fixtures"]
"exclude": ["node_modules", "dist", "dist-dev", "dist-self", "test/fixtures"]
}
28 changes: 24 additions & 4 deletions packages/cli/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,37 @@ const pkg = JSON.parse(
readFileSync(resolve(import.meta.dirname, "package.json"), "utf8")
) as { version: string };

// Each build target emits to its own directory so prod, dev, and self builds
// never overwrite one another. Keyed by TASKLESS_BUILD_TARGET; anything other
// than "dev"/"self" is treated as prod.
const OUT_DIRS = {
prod: "dist",
dev: "dist-dev",
self: "dist-self",
} as const;

function resolveBuildTarget(): keyof typeof OUT_DIRS {
const target = process.env.TASKLESS_BUILD_TARGET;
return target === "dev" || target === "self" ? target : "prod";
}

function resolveOutDir(): string {
return OUT_DIRS[resolveBuildTarget()];
}

// The CLI invocation baked into emitted skill/command/recipe content, chosen
// by the TASKLESS_BUILD_TARGET env var (see package.json build:dev/build:self):
// - prod (default): the published `npx @taskless/cli`
// - dev: an absolute path, for validating this build from another repo
// - self: a repo-root-relative path, for dogfooding inside this repo
// The dev/self paths point at their own output directory (dist-dev/dist-self).
function resolveCliInvocation(): string {
switch (process.env.TASKLESS_BUILD_TARGET) {
switch (resolveBuildTarget()) {
case "self": {
return "node packages/cli/dist/index.js";
return `node packages/cli/${OUT_DIRS.self}/index.js`;
}
case "dev": {
return `node ${resolve(import.meta.dirname, "dist/index.js")}`;
return `node ${resolve(import.meta.dirname, OUT_DIRS.dev, "index.js")}`;
}
default: {
return "npx @taskless/cli";
Expand Down Expand Up @@ -125,7 +144,7 @@ function shebang(): Plugin {
writeBundle(options, bundle) {
for (const [fileName, chunk] of Object.entries(bundle)) {
if (chunk.type === "chunk" && chunk.isEntry) {
const outPath = resolve(options.dir ?? "dist", fileName);
const outPath = resolve(options.dir ?? resolveOutDir(), fileName);
chmodSync(outPath, 0o755);
}
}
Expand All @@ -141,6 +160,7 @@ export default defineConfig({
},
plugins: [tsconfigPaths(), assertSkillVersions(), shebang()],
build: {
outDir: resolveOutDir(),
lib: {
entry: resolve(import.meta.dirname, "src/index.ts"),
formats: ["es"],
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true
},
"exclude": ["node_modules", "dist", "packages"]
"exclude": ["node_modules", "dist", "dist-dev", "dist-self", "packages"]
}
2 changes: 1 addition & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"outputs": ["dist/**"],
"outputs": ["dist/**", "dist-dev/**", "dist-self/**"],
"env": ["TASKLESS_BUILD_TARGET"]
},
"test": {
Expand Down