feat(cli): export the knowledge prompts as @taskless/cli/prompts - #87
feat(cli): export the knowledge prompts as @taskless/cli/prompts#87thecodedrift wants to merge 9 commits into
Conversation
Adds a `./prompts` subpath export so consumers can import the CLI's `help/*.txt` recipes instead of keeping a copy that drifts. Prompts are functions returning fully-rendered text: every `%(KEY)s` resolves inside the package, so no consumer handles a template dialect. `PromptOptions` carries `anonymous`, `packageManagerDlx`, and `header`. `header: false` exists because the header carries the CLI version, which would otherwise sit in an LLM consumer's prompt-cache key and be invalidated by every publish. `TOPICS` ships `static` alone, per design D6. The other 17 canonical topics are recorded in `INTERNAL_TOPICS`, and a completeness check fails when a recipe file is neither exported nor explicitly internal. Topic names are semver-tracked public API, so exporting one speculatively spends that promise for nothing. Two build details worth a look: Declarations come from `tsc --emitDeclarationOnly` against a scoped `tsconfig.prompts.json`, not `vite-plugin-dts`. A whole-`src` dts plugin would emit `dist/index.d.ts` as a side effect, and since the `"."` export has no `types` condition, TypeScript would fall back to that sibling file and hand consumers a typed CLI surface the package has never promised. The `"."` export block is untouched. `tsc` mirrors `rootDir`, so the declaration lands at `dist/prompts/index.d.ts` rather than the literal `dist/prompts.d.ts` task 2.2 names; the export map points at it directly. The Vite `shebang()` plugin is now scoped to the `index` entry. It prepended `#!/usr/bin/env node` to every entry chunk, which would have made the importable module an executable script. Also corrects three stale `route` examples in the spec delta that contradicted D6, and archives the change. Unit 2 of 2 for export-knowledge-prompts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new public, typed @taskless/cli/prompts subpath export so non-CLI consumers (e.g. service-side generators) can import fully-rendered knowledge prompt text sourced from the same embedded help/*.txt recipes that power taskless help, without pulling in the CLI runtime.
Changes:
- Extend the CLI build to emit a second Vite library entry (
dist/prompts.js) while scoping the shebang/executable bit to the CLI binary entry only. - Add a dedicated
tsc --emitDeclarationOnlypipeline for the prompts entry and wire it into@taskless/cli’s build, exporting the new subpath with explicittypes+importpaths. - Add comprehensive tests covering placeholder resolution, header suppression, help parity, artifact/declaration presence, and “no CLI runtime” import-graph constraints; archive/promote the OpenSpec change artifacts/spec.
Reviewed changes
Copilot reviewed 9 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/cli/vite.config.ts | Build emits index + prompts entries; shebang/chmod restricted to the CLI binary entry. |
| packages/cli/tsconfig.prompts.json | New scoped declaration-only tsconfig to generate .d.ts for the prompts subpath without producing dist/index.d.ts. |
| packages/cli/test/prompts.test.ts | New test suite validating rendering invariants, parity with help, build outputs, and import-graph constraints. |
| packages/cli/src/prompts/index.ts | New public prompts entrypoint exporting TOPICS, INTERNAL_TOPICS, PromptTopic, PromptOptions, getPrompt, and PROMPTS. |
| packages/cli/package.json | Adds ./prompts export (types + import) and runs tsc -p tsconfig.prompts.json as part of build. |
| openspec/specs/cli-knowledge-prompts/spec.md | Promoted spec for the feature; currently contains some stale/placeholder wording (see comments). |
| openspec/changes/archive/2026-08-06-export-knowledge-prompts/tasks.md | Archived tasks marked complete; one line still references an outdated declaration path. |
| openspec/changes/archive/2026-08-06-export-knowledge-prompts/specs/cli-knowledge-prompts/spec.md | Archived delta spec updated with static-based examples. |
| openspec/changes/archive/2026-08-06-export-knowledge-prompts/proposal.md | Archived proposal describing the feature and delivery shape; one module-layout description is now stale. |
| openspec/changes/archive/2026-08-06-export-knowledge-prompts/design.md | Archived design rationale; a couple references still point at the old module layout. |
| openspec/changes/archive/2026-08-06-export-knowledge-prompts/.openspec.yaml | OpenSpec archive metadata for the change. |
| .changeset/export-knowledge-prompts.md | Changeset documenting the new minor feature export. |
Suppressed comments (2)
openspec/changes/archive/2026-08-06-export-knowledge-prompts/tasks.md:13
- Task 2.2 still references a single-file
dist/prompts.d.ts, but the implementation (andpackage.jsonexport) usesdist/prompts/index.d.ts(plus any referenced.d.tsalongside it). Updating the task text avoids confusion when auditing the archive later.
openspec/specs/cli-knowledge-prompts/spec.md:58 - Similarly, this scenario implies
ciis an exported prompt topic, but it is currently listed under INTERNAL_TOPICS. Consider wording this in terms of rendering a recipe containing the placeholder, rather than calling an exported topic.
#### Scenario: Agent-fill marker defaults and overrides
- **WHEN** a consumer calls the `ci` prompt without options
- **THEN** `%(PACKAGE_MANAGER_DLX)s` renders as the default `<package-manager-dlx>` marker; supplying `packageManagerDlx` substitutes that value instead
…e-prompts-2-export * openspec/export-knowledge-prompts: chore: grow the changeset with the committed sg config docs(openspec): make cli-check internally consistent; correct the generateSgConfig note test(cli): isolate the missing-binary test from the host environment docs(openspec): archive partition-rules-by-engine ref(cli): declare COMMITTED_SG_CONFIG beside the layout it derives from docs(cli): describe the engine-partitioned layout in help and scaffolding feat(cli): run ast-grep against the committed sg config chore: grow the changeset with the runtime discovery path docs(openspec): correct the group-1 note, which no longer describes reality ref(cli): retire layout.ts now that ENGINE_LAYOUTS supersedes it feat(cli): discover runtime rules under the runtime engine directory
|
@claude /review |
|
Claude finished @thecodedrift's task in 15m 46s —— View job Claude finished the review
Overall assessmentThis is a clean, well-scoped addition. The core design holds up under scrutiny: I did find six issues worth a look, filed as inline comments:
None of these are blocking in the sense of breaking the feature as shipped for the Per instructions I did not run |
…-knowledge-prompts-2-export * openspec/export-knowledge-prompts: fix(cli): size the vitest timeout to what the suite actually does
The prompts entry makes the build emit two library entries, so rollup hoists what `index` and `prompts` share into a sibling chunk that `dist/index.js` imports by relative path. The missing-binary test copied only the bin into its isolated directory, leaving that import dangling: the CLI died on ERR_MODULE_NOT_FOUND before it ever looked for ast-grep, and the assertion reported an empty stderr rather than the real cause. Copy the whole `dist/` instead. The isolation the test needs is the empty PATH and HOME, not a single-file bundle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ts scenarios
The promoted spec still carried the archive placeholder for Purpose, which
reads to a later maintainer as an unfinished spec. State what the capability
is for and why one source and one renderer matter.
Two scenarios also described `rule-create`/`rule-improve`/`ci` as prompts a
consumer calls, but all three are INTERNAL_TOPICS and are not members of
`PromptTopic` — `getPrompt("ci")` does not type-check. Reword them around
the recipe carrying the placeholder, which is what the render path actually
guarantees, and name the topics as internal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-knowledge-prompts-2-export * openspec/export-knowledge-prompts: docs(openspec): point the change docs at the module layout that shipped
Unit 1 unchecked tasks 1.3 and 1.4 because it does not implement them. This unit does, so the archived record checks them again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…d CLI entry Three review findings on the build wiring: `isBinEntry` took a hand-rolled structural shape whose fields were all optional, so an `OutputAsset` satisfied it by having none of them. Take Rollup's own bundle union and return a type predicate instead, which also lets `generateBundle` drop its redundant `type === "chunk"` guard. `tsconfig.prompts.json`'s comment implied its include list bounds what gets emitted. It bounds which entry is rooted; emit follows the import graph, so `prompts/recipes`, `util/invocation`, and the two leaf schemas get declarations too. Say so, and record why `declarationDir` stays `dist` while `vite.config.ts` derives its output dir from TASKLESS_BUILD_TARGET: the `./prompts` export resolves to `./dist/prompts.js` unconditionally. The absence of `dist/index.d.ts` was the invariant that scoping exists to protect and the only one with no test behind it. Assert it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All six findings were evaluated; each inline thread has a reply and is resolved. Fixed: the promoted spec's Two were not taken as written. The — AI Coding Agent |
…hange Task 2.2 said the build emits `dist/prompts.d.ts`. Declarations come from `tsc --emitDeclarationOnly` against `tsconfig.prompts.json`, and `tsc` mirrors `rootDir` structure, so `src/prompts/index.ts` emits to `dist/prompts/index.d.ts`, which is the path the `exports` map already points at. Task 2.3 and the matching design risk carried the same imprecision. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixed in 156f01d: task 2.2 now says — AI Coding Agent |
Stack (root → tip):
Adds the
@taskless/cli/promptssubpath export, so the generator and other service-side consumers can source authoring guidance from the same text the CLI serves rather than a local copy that drifts.Unit 2 of 2. Stacked on #70, which moved the recipe glob and the renderer into
src/prompts/recipes.ts. This PR adds the public surface on top of it and archives the change.The export
Prompts are functions returning fully-rendered text. Every
%(KEY)sresolves inside the package (CLI_VERSIONfrom the build version,INPUT_SCHEMAfrom the Zod source,PACKAGE_MANAGER_DLXas an agent-fill marker), so a consumer never handles a template dialect.PromptOptionscarriesanonymous,packageManagerDlx, andheader.header: falsedrops the version-bearing first line. That option exists for a specific reason: the header carries the CLI version, so a consumer placing the text in an LLM system prompt would otherwise have the version sitting in its prompt-cache key, invalidated by every CLI publish.Topic membership
TOPICSshipsstaticand nothing else, per design D6. The other 17 canonical topics are recorded inINTERNAL_TOPICS, and a completeness check fails when a recipe file is neither exported nor explicitly internal, so a new or deleted recipe cannot silently change the published surface.Topic names are semver-tracked public API. Exporting one speculatively spends that promise for nothing, so a topic joins
TOPICSwhen a consumer asks for it.This also corrects three stale
routeexamples in the spec delta that contradicted D6, before the delta was promoted intoopenspec/specs/.Two implementation notes worth a reviewer's attention
Declarations come from
tsc, notvite-plugin-dts. They are emitted bytsc --emitDeclarationOnlyagainst a scopedtsconfig.prompts.jsonwhoseincludeis just the prompts entry plus the ambient build defines. A whole-srcdts plugin would emitdist/index.d.tsas a side effect, and since the"."export has notypescondition, TypeScript would fall back to that sibling file and silently hand consumers a typed CLI surface the package has never promised. The"."export block is untouched, and a test assertsdist/index.d.tsis absent.The declaration lands at
dist/prompts/index.d.ts, not the literaldist/prompts.d.tsthat task 2.2 names.tscmirrorsrootDir, so the layout followssrc/prompts/index.ts. The export map points at it explicitly. Flagging this as a deliberate deviation from the written task rather than an oversight.src/prompts/index.tsimports./recipes.jswith an explicit extension, the only such import in the package.tsccopies the specifier verbatim into the published.d.ts, and the extensionless form fails for consumers onmoduleResolution: node16withTS2834.Build
The Vite
shebang()plugin is now scoped to theindexentry. It previously prepended#!/usr/bin/env nodeto every entry chunk and set the executable bit, which would have made the importable module an executable script.dist/index.jskeeps its shebang and mode755;dist/prompts.jshas neither.Verification
pnpm --filter @taskless/cli typecheck,pnpm lint,pnpm --filter @taskless/cli testall clean; 442 tests across 38 files, including 23 new prompt tests.vite buildemitsdist/index.js(with shebang,755) anddist/prompts.js(no shebang,644), plusdist/prompts/index.d.ts. Nodist/index.d.ts.Fixes OSS-20