-
Notifications
You must be signed in to change notification settings - Fork 0
init: lean AGENTS.md section + opt-in @conciv/skills docs pack #482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
4b44226
feat(cli): shrink AGENTS.md conciv section, add opt-in @conciv/skills…
omridevk 1e40b54
fix(cli): docs-pack — gate on intent block, respect package manager
omridevk cc57777
feat(cli): single-pass init wizard + taskLog streaming execution
omridevk ac863ab
fix(cli): review wave 2 - signal exit, intent block markers, AGENTS.m…
omridevk 87f37c9
fix(cli): signal-exit test self-terminates, no pkill
omridevk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| '@conciv/cli': patch | ||
| --- | ||
|
|
||
| `conciv init` now teaches agents the MCP discovery workflow (`await external_catalog({})`) instead of a static AGENTS.md command list. It also offers an opt-in docs pack step during plan review, declined by default and never enabled by `--yes`, that adds `@conciv/skills` as a dev dependency and runs `@tanstack/intent install` for setup, extension-authoring, and debugging guidance. | ||
|
|
||
| The interactive wizard was reworked to a single-pass flow, modeled on the TanStack CLI: it asks the harness, framework, and docs-pack questions once, renders one plan, and asks for one confirmation, and declining cancels outright instead of dropping into an edit loop. The execution phase now streams each step's live command output through clack's `taskLog`, instead of buffering it behind a spinner until the step settles. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,36 @@ | ||
| import {execFile} from 'node:child_process' | ||
| import {spawn} from 'node:child_process' | ||
|
|
||
| export type CommandOutcome = {code: number; output: string} | ||
|
|
||
| export function execFileOutcome( | ||
| bin: string, | ||
| args: string[], | ||
| options: {cwd: string; env?: NodeJS.ProcessEnv}, | ||
| onLine: (line: string) => void, | ||
| ): Promise<CommandOutcome> { | ||
| return new Promise((settle, reject) => { | ||
| execFile(bin, args, options, (error, stdout, stderr) => { | ||
| if (error === null) { | ||
| settle({code: 0, output: `${stdout}${stderr}`}) | ||
| const child = spawn(bin, args, options) | ||
| let output = '' | ||
| let pending = '' | ||
| const consume = (chunk: Buffer): void => { | ||
| const text = chunk.toString() | ||
| output += text | ||
| pending += text | ||
| const lines = pending.split('\n') | ||
| pending = lines.pop() ?? '' | ||
| for (const line of lines) onLine(line) | ||
| } | ||
| child.stdout?.on('data', consume) | ||
| child.stderr?.on('data', consume) | ||
| child.on('error', reject) | ||
| child.on('close', (code, signal) => { | ||
| if (pending.length > 0) onLine(pending) | ||
| if (code === null) { | ||
| const note = signal ? `terminated by ${signal}` : 'terminated without exit code' | ||
| settle({code: 1, output: output.length > 0 ? `${output}\n${note}` : note}) | ||
| return | ||
| } | ||
| if (typeof error.code !== 'number') { | ||
| reject(error) | ||
| return | ||
| } | ||
| settle({code: error.code, output: `${stdout}${stderr}`}) | ||
| settle({code, output}) | ||
| }) | ||
| }) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import {existsSync, readFileSync} from 'node:fs' | ||
| import {join} from 'node:path' | ||
| import {addDependencyCommand, detectPackageManager, dlxCommand} from 'nypm' | ||
| import {captureFile} from '../interrupt.js' | ||
| import type {ManualCard} from '../ledger.js' | ||
| import type {InitContext, InitStep, SpawnBin} from '../pipeline.js' | ||
| import type {AddDep} from './install-it.js' | ||
| import {hasDependency, readManifest} from './manifest.js' | ||
|
|
||
| const skillsName = '@conciv/skills' | ||
| const intentPackage = '@tanstack/intent@latest' | ||
| const intentBlockStartMarker = '<!-- intent-skills:start -->' | ||
| const intentBlockEndMarker = '<!-- intent-skills:end -->' | ||
|
|
||
| type IntentCommands = { | ||
| spawn: [string, ...string[]] | ||
| intentLine: string | ||
| addCommand: string | ||
| } | ||
|
|
||
| function message(error: unknown): string { | ||
| return error instanceof Error ? error.message : String(error) | ||
| } | ||
|
|
||
| function intentBlockPresent(cwd: string): boolean { | ||
| const agentsMdPath = join(cwd, 'AGENTS.md') | ||
| if (!existsSync(agentsMdPath)) return false | ||
| const content = readFileSync(agentsMdPath, 'utf8') | ||
| return content.includes(intentBlockStartMarker) && content.includes(intentBlockEndMarker) | ||
| } | ||
|
|
||
| async function resolveIntentCommands(cwd: string): Promise<IntentCommands> { | ||
| const found = await detectPackageManager(cwd, {ignoreArgv: true}) | ||
| const pmName = found?.name ?? 'npm' | ||
| const intentLine = dlxCommand(pmName, intentPackage, {args: ['install']}) | ||
| const [bin, ...args] = intentLine.split(' ') | ||
| if (bin === undefined) throw new Error(`nypm dlxCommand returned an empty command: ${intentLine}`) | ||
| return {spawn: [bin, ...args], intentLine, addCommand: addDependencyCommand(pmName, skillsName, {dev: true})} | ||
| } | ||
|
|
||
| async function failureCard(cwd: string, depPresent: boolean): Promise<ManualCard> { | ||
| const commands = await resolveIntentCommands(cwd) | ||
| const lines = depPresent ? [commands.intentLine] : [commands.addCommand, commands.intentLine] | ||
| return { | ||
| title: `Add the ${skillsName} docs pack`, | ||
| body: 'The automatic setup failed. Run these in your project:', | ||
| snippet: lines.join('\n'), | ||
| } | ||
| } | ||
|
|
||
| function present(ctx: InitContext): boolean { | ||
| return hasDependency(readManifest(ctx.cwd), skillsName) && intentBlockPresent(ctx.cwd) | ||
| } | ||
|
|
||
| export function docsPackStep(add: AddDep, spawn: SpawnBin): InitStep { | ||
| return { | ||
| id: 'docs-pack', | ||
| title: `Add the ${skillsName} docs pack`, | ||
| running: `Adding the ${skillsName} docs pack…`, | ||
| completed: `Added the ${skillsName} docs pack`, | ||
| detect: async (ctx) => (present(ctx) ? 'present' : 'missing'), | ||
| plan: async () => ({ | ||
| summary: `add ${skillsName} as a dev dependency and run \`intent install\` for skill-loading guidance`, | ||
| wouldEdit: ['package.json', 'AGENTS.md'], | ||
| }), | ||
| apply: async (ctx) => { | ||
| const depPresent = hasDependency(readManifest(ctx.cwd), skillsName) | ||
| if (!depPresent) { | ||
| ctx.backup(captureFile(join(ctx.cwd, 'package.json'))) | ||
| const addFailure = await add(skillsName, {cwd: ctx.cwd}).then( | ||
| () => null, | ||
| (error: unknown) => message(error), | ||
| ) | ||
| if (addFailure !== null) | ||
| return {status: 'manual', cards: [await failureCard(ctx.cwd, depPresent)], detail: addFailure} | ||
| } | ||
| if (intentBlockPresent(ctx.cwd)) return {status: 'done'} | ||
| ctx.backup(captureFile(join(ctx.cwd, 'AGENTS.md'))) | ||
| const commands = await resolveIntentCommands(ctx.cwd) | ||
| const [bin, ...args] = commands.spawn | ||
| const outcome = await spawn(bin, args, ctx.cwd, ctx.feed).catch((error: unknown) => ({ | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| code: -1, | ||
| output: message(error), | ||
| })) | ||
| if (outcome.code === 0) return {status: 'done'} | ||
| const reason = outcome.output.trim() | ||
| return { | ||
| status: 'manual', | ||
| cards: [await failureCard(ctx.cwd, true)], | ||
| detail: reason.length === 0 ? 'intent install failed' : reason, | ||
| } | ||
| }, | ||
| verify: async (ctx) => present(ctx), | ||
| manualCard: () => ({ | ||
| title: `Add the ${skillsName} docs pack`, | ||
| body: `Add ${skillsName} as a dev dependency, then run \`${intentPackage} install\` with your package manager's dlx equivalent, or re-run conciv init.`, | ||
| }), | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import {readFileSync} from 'node:fs' | ||
| import {join} from 'node:path' | ||
| import {z} from 'zod' | ||
|
|
||
| const manifestSchema = z.object({ | ||
| dependencies: z.record(z.string(), z.string()).optional(), | ||
| devDependencies: z.record(z.string(), z.string()).optional(), | ||
| }) | ||
|
|
||
| export type PackageJson = z.infer<typeof manifestSchema> | ||
|
|
||
| export function readManifest(cwd: string): PackageJson { | ||
| const parsed = manifestSchema.safeParse(JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8'))) | ||
| if (!parsed.success) return {} | ||
| return parsed.data | ||
| } | ||
|
|
||
| export function hasDependency(pkg: PackageJson, name: string): boolean { | ||
| return name in (pkg.dependencies ?? {}) || name in (pkg.devDependencies ?? {}) | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.