@crouton-kit/crouter 0.3.8 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +14 -24
- package/dist/commands/agent.d.ts +4 -0
- package/dist/commands/agent.js +444 -243
- package/dist/commands/debug.d.ts +1 -1
- package/dist/commands/debug.js +20 -7
- package/dist/commands/human.js +51 -19
- package/dist/commands/job.d.ts +9 -0
- package/dist/commands/job.js +50 -10
- package/dist/commands/mode.d.ts +2 -0
- package/dist/commands/mode.js +231 -0
- package/dist/commands/pkg.js +5 -0
- package/dist/commands/plan.d.ts +1 -1
- package/dist/commands/plan.js +24 -11
- package/dist/commands/skill.js +20 -4
- package/dist/commands/spec.d.ts +1 -1
- package/dist/commands/spec.js +24 -11
- package/dist/commands/sys.js +5 -0
- package/dist/core/__tests__/job.test.js +11 -11
- package/dist/core/__tests__/jobs.test.js +33 -1
- package/dist/core/__tests__/resolver.test.js +69 -1
- package/dist/core/__tests__/spawn.test.d.ts +1 -0
- package/dist/core/__tests__/spawn.test.js +138 -0
- package/dist/core/__tests__/subagents.test.d.ts +1 -0
- package/dist/core/__tests__/subagents.test.js +75 -0
- package/dist/core/__tests__/unknown-path.test.d.ts +1 -0
- package/dist/core/__tests__/unknown-path.test.js +52 -0
- package/dist/core/bootstrap.d.ts +2 -0
- package/dist/core/bootstrap.js +66 -0
- package/dist/core/command.d.ts +58 -2
- package/dist/core/command.js +62 -14
- package/dist/core/frontmatter.d.ts +10 -0
- package/dist/core/frontmatter.js +24 -9
- package/dist/core/help.d.ts +39 -8
- package/dist/core/help.js +64 -32
- package/dist/core/jobs.d.ts +8 -2
- package/dist/core/jobs.js +109 -6
- package/dist/core/resolver.js +51 -1
- package/dist/core/spawn.d.ts +140 -23
- package/dist/core/spawn.js +392 -73
- package/dist/core/subagents.d.ts +18 -0
- package/dist/core/subagents.js +163 -0
- package/dist/prompts/agent.d.ts +10 -1
- package/dist/prompts/agent.js +34 -3
- package/dist/types.d.ts +21 -0
- package/dist/types.js +3 -0
- package/package.json +2 -2
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// `crtr mode` umbrella — modes of operation: spec, plan, implement, review, debug.
|
|
2
|
+
//
|
|
3
|
+
// Collects the workflow-oriented commands:
|
|
4
|
+
// spec / plan / debug — the artifact and root-cause workflows (from their
|
|
5
|
+
// own files); planner, implementer, reviewer — the
|
|
6
|
+
// handoff-spawn leaves previously under `agent new`.
|
|
7
|
+
//
|
|
8
|
+
// The spawn primitives (agent new / fork) and subagent management remain under
|
|
9
|
+
// `crtr agent`. Results from spawned workers are collected at `crtr job`.
|
|
10
|
+
import { defineBranch, defineLeaf } from '../core/command.js';
|
|
11
|
+
import { InputError } from '../core/io.js';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { createJob, appendEvent, recordJobPane } from '../core/jobs.js';
|
|
14
|
+
import { spawnAndDetach, spawnAgent } from '../core/spawn.js';
|
|
15
|
+
import { planHandoffPrompt, implementHandoffPrompt, reviewerHandoffPrompt, } from '../prompts/agent.js';
|
|
16
|
+
import { assertTmux, resolveMaxPanes, followUpResult, } from './agent.js';
|
|
17
|
+
import { registerSpec } from './spec.js';
|
|
18
|
+
import { registerPlan } from './plan.js';
|
|
19
|
+
import { registerDebug } from './debug.js';
|
|
20
|
+
const DEFAULT_KILL_SECS = 2;
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// mode planner
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
const newPlanner = defineLeaf({
|
|
25
|
+
name: 'planner',
|
|
26
|
+
help: {
|
|
27
|
+
name: 'mode planner',
|
|
28
|
+
summary: 'launch a planning agent for an approved spec; closes the originating pane after handoff',
|
|
29
|
+
params: [
|
|
30
|
+
{ kind: 'positional', name: 'spec_path', type: 'path', required: true, constraint: 'Absolute path to the spec file.' },
|
|
31
|
+
{ kind: 'flag', name: 'cwd', type: 'path', required: false, constraint: 'Working directory. Defaults to process.cwd().' },
|
|
32
|
+
{ kind: 'flag', name: 'name', type: 'string', required: true, constraint: 'Display name passed to the agent CLI (`-n`); surfaces in pane title and resume picker.' },
|
|
33
|
+
],
|
|
34
|
+
output: [
|
|
35
|
+
{ name: 'job_id', type: 'string', required: true, constraint: 'Use with `crtr job read *` and `crtr job cancel`.' },
|
|
36
|
+
{ name: 'follow_up', type: 'string', required: true, constraint: 'Your own next call — run it and report the worker\'s result; do not relay it to the user.' },
|
|
37
|
+
],
|
|
38
|
+
outputKind: 'object',
|
|
39
|
+
effects: [
|
|
40
|
+
'Spawns a planner agent in a sibling tmux pane.',
|
|
41
|
+
'Closes the originating pane after a short delay.',
|
|
42
|
+
'Creates a job entry and result sidecar.',
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
run: async (input) => {
|
|
46
|
+
assertTmux();
|
|
47
|
+
const specPath = input['spec_path'];
|
|
48
|
+
const cwd = typeof input['cwd'] === 'string' ? input['cwd'] : process.cwd();
|
|
49
|
+
const name = input['name'];
|
|
50
|
+
if (!existsSync(specPath)) {
|
|
51
|
+
throw new InputError({
|
|
52
|
+
error: 'not_found',
|
|
53
|
+
message: `spec not found: ${specPath}`,
|
|
54
|
+
field: 'spec_path',
|
|
55
|
+
next: 'Provide an absolute path to an existing spec file.',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const { jobId } = createJob('planner', { cwd });
|
|
59
|
+
const result = spawnAndDetach({
|
|
60
|
+
prompt: planHandoffPrompt(specPath, jobId),
|
|
61
|
+
cwd,
|
|
62
|
+
jobId,
|
|
63
|
+
placement: 'split-h',
|
|
64
|
+
killAfterSeconds: DEFAULT_KILL_SECS,
|
|
65
|
+
name,
|
|
66
|
+
});
|
|
67
|
+
if (result.status === 'not-in-tmux') {
|
|
68
|
+
throw new InputError({ error: 'not_in_tmux', message: result.message, next: 'Run inside a tmux session.' });
|
|
69
|
+
}
|
|
70
|
+
if (result.status === 'spawn-failed') {
|
|
71
|
+
throw new InputError({ error: 'spawn_failed', message: result.message, next: 'Check tmux is running and try again.' });
|
|
72
|
+
}
|
|
73
|
+
if (result.paneId !== undefined)
|
|
74
|
+
recordJobPane(jobId, result.paneId);
|
|
75
|
+
const plannerPaneLabel = result.paneId !== undefined ? result.paneId : 'unknown';
|
|
76
|
+
appendEvent(jobId, { level: 'info', event: 'worker_started', message: `planner pane ${plannerPaneLabel} spawned` });
|
|
77
|
+
return { job_id: jobId, follow_up: followUpResult(jobId) };
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// mode implementer
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
const newImplementer = defineLeaf({
|
|
84
|
+
name: 'implementer',
|
|
85
|
+
help: {
|
|
86
|
+
name: 'mode implementer',
|
|
87
|
+
summary: 'launch an implementation agent for an approved plan; closes the originating pane after handoff',
|
|
88
|
+
params: [
|
|
89
|
+
{ kind: 'positional', name: 'plan_path', type: 'path', required: true, constraint: 'Absolute path to the plan file.' },
|
|
90
|
+
{ kind: 'flag', name: 'cwd', type: 'path', required: false, constraint: 'Working directory. Defaults to process.cwd().' },
|
|
91
|
+
{ kind: 'flag', name: 'name', type: 'string', required: true, constraint: 'Display name passed to the agent CLI (`-n`); surfaces in pane title and resume picker.' },
|
|
92
|
+
],
|
|
93
|
+
output: [
|
|
94
|
+
{ name: 'job_id', type: 'string', required: true, constraint: 'Use with `crtr job read *` and `crtr job cancel`.' },
|
|
95
|
+
{ name: 'follow_up', type: 'string', required: true, constraint: 'Your own next call — run it and report the worker\'s result; do not relay it to the user.' },
|
|
96
|
+
],
|
|
97
|
+
outputKind: 'object',
|
|
98
|
+
effects: [
|
|
99
|
+
'Spawns an implementer agent in a sibling tmux pane.',
|
|
100
|
+
'Closes the originating pane after a short delay.',
|
|
101
|
+
'Creates a job entry and result sidecar.',
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
run: async (input) => {
|
|
105
|
+
assertTmux();
|
|
106
|
+
const planPath = input['plan_path'];
|
|
107
|
+
const cwd = typeof input['cwd'] === 'string' ? input['cwd'] : process.cwd();
|
|
108
|
+
const name = input['name'];
|
|
109
|
+
if (!existsSync(planPath)) {
|
|
110
|
+
throw new InputError({
|
|
111
|
+
error: 'not_found',
|
|
112
|
+
message: `plan not found: ${planPath}`,
|
|
113
|
+
field: 'plan_path',
|
|
114
|
+
next: 'Provide an absolute path to an existing plan file.',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const { jobId } = createJob('implementer', { cwd });
|
|
118
|
+
const result = spawnAndDetach({
|
|
119
|
+
prompt: implementHandoffPrompt(planPath, jobId),
|
|
120
|
+
cwd,
|
|
121
|
+
jobId,
|
|
122
|
+
placement: 'split-h',
|
|
123
|
+
killAfterSeconds: DEFAULT_KILL_SECS,
|
|
124
|
+
name,
|
|
125
|
+
});
|
|
126
|
+
if (result.status === 'not-in-tmux') {
|
|
127
|
+
throw new InputError({ error: 'not_in_tmux', message: result.message, next: 'Check tmux is running and try again.' });
|
|
128
|
+
}
|
|
129
|
+
if (result.status === 'spawn-failed') {
|
|
130
|
+
throw new InputError({ error: 'spawn_failed', message: result.message, next: 'Check tmux is running and try again.' });
|
|
131
|
+
}
|
|
132
|
+
if (result.paneId !== undefined)
|
|
133
|
+
recordJobPane(jobId, result.paneId);
|
|
134
|
+
const implPaneLabel = result.paneId !== undefined ? result.paneId : 'unknown';
|
|
135
|
+
appendEvent(jobId, { level: 'info', event: 'worker_started', message: `implementer pane ${implPaneLabel} spawned` });
|
|
136
|
+
return { job_id: jobId, follow_up: followUpResult(jobId) };
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// mode reviewer
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
const newReviewer = defineLeaf({
|
|
143
|
+
name: 'reviewer',
|
|
144
|
+
help: {
|
|
145
|
+
name: 'mode reviewer',
|
|
146
|
+
summary: 'launch a reviewer agent for a plan or spec artifact; the originating pane stays alive to collect the verdict',
|
|
147
|
+
params: [
|
|
148
|
+
{ kind: 'positional', name: 'artifact_path', type: 'path', required: true, constraint: 'Absolute path to the artifact to review.' },
|
|
149
|
+
{ kind: 'flag', name: 'kind', type: 'enum', choices: ['plan', 'spec'], required: true, constraint: 'Artifact kind to review.' },
|
|
150
|
+
{ kind: 'flag', name: 'spec-path', type: 'path', required: false, constraint: 'Absolute path to the spec, for plan reviews. Omit for spec reviews.' },
|
|
151
|
+
{ kind: 'flag', name: 'cwd', type: 'path', required: false, constraint: 'Working directory. Defaults to process.cwd().' },
|
|
152
|
+
{ kind: 'flag', name: 'name', type: 'string', required: true, constraint: 'Display name passed to the agent CLI (`-n`); surfaces in pane title and resume picker.' },
|
|
153
|
+
],
|
|
154
|
+
output: [
|
|
155
|
+
{ name: 'job_id', type: 'string', required: true, constraint: 'Use with `crtr job read *` and `crtr job cancel`.' },
|
|
156
|
+
{ name: 'follow_up', type: 'string', required: true, constraint: 'Your own next call — run it and report the worker\'s result; do not relay it to the user.' },
|
|
157
|
+
],
|
|
158
|
+
outputKind: 'object',
|
|
159
|
+
effects: [
|
|
160
|
+
'Spawns a reviewer agent in a sibling tmux pane.',
|
|
161
|
+
'The originating pane stays alive — wait on the result and act on the verdict.',
|
|
162
|
+
'Creates a job entry and result sidecar.',
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
run: async (input) => {
|
|
166
|
+
assertTmux();
|
|
167
|
+
const artifactPath = input['artifact_path'];
|
|
168
|
+
const artifactKind = input['kind'];
|
|
169
|
+
const specPath = typeof input['specPath'] === 'string' ? input['specPath'] : undefined;
|
|
170
|
+
const cwd = typeof input['cwd'] === 'string' ? input['cwd'] : process.cwd();
|
|
171
|
+
const name = input['name'];
|
|
172
|
+
if (!existsSync(artifactPath)) {
|
|
173
|
+
throw new InputError({
|
|
174
|
+
error: 'not_found',
|
|
175
|
+
message: `artifact not found: ${artifactPath}`,
|
|
176
|
+
field: 'artifact_path',
|
|
177
|
+
next: 'Provide an absolute path to an existing artifact file.',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
const { jobId } = createJob('reviewer', { cwd });
|
|
181
|
+
// The reviewer is a subordinate the caller waits on (verdict → revise or
|
|
182
|
+
// hand off), NOT a handoff successor. Use spawnAgent so the originating
|
|
183
|
+
// pane (planner/orchestrator) stays alive to collect the result; do not
|
|
184
|
+
// self-kill the caller the way planner/implementer handoffs do.
|
|
185
|
+
const result = spawnAgent({
|
|
186
|
+
prompt: reviewerHandoffPrompt(artifactPath, artifactKind, specPath !== undefined ? specPath : null, jobId),
|
|
187
|
+
cwd,
|
|
188
|
+
jobId,
|
|
189
|
+
maxPanesPerWindow: resolveMaxPanes(),
|
|
190
|
+
name,
|
|
191
|
+
});
|
|
192
|
+
if (result.status === 'not-in-tmux') {
|
|
193
|
+
throw new InputError({ error: 'not_in_tmux', message: result.message, next: 'Run inside a tmux session.' });
|
|
194
|
+
}
|
|
195
|
+
if (result.status === 'spawn-failed') {
|
|
196
|
+
throw new InputError({ error: 'spawn_failed', message: result.message, next: 'Check tmux is running and try again.' });
|
|
197
|
+
}
|
|
198
|
+
if (result.paneId !== undefined)
|
|
199
|
+
recordJobPane(jobId, result.paneId);
|
|
200
|
+
const reviewerPaneLabel = result.paneId !== undefined ? result.paneId : 'unknown';
|
|
201
|
+
appendEvent(jobId, { level: 'info', event: 'worker_started', message: `reviewer pane ${reviewerPaneLabel} spawned` });
|
|
202
|
+
return { job_id: jobId, follow_up: followUpResult(jobId) };
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// mode (root umbrella)
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
export function registerMode() {
|
|
209
|
+
return defineBranch({
|
|
210
|
+
name: 'mode',
|
|
211
|
+
rootEntry: {
|
|
212
|
+
concept: 'modes of operation: spec → plan → implement → review → debug. Workflow commands for the full agentic development lifecycle',
|
|
213
|
+
desc: 'spec, plan, implement, review, debug workflows',
|
|
214
|
+
useWhen: 'running any structured workflow phase — writing a spec, decomposing into a plan, handing off to an implementer or reviewer, or root-causing a bug. These are the "what to do next" commands; `crtr agent new` is the raw spawn primitive.',
|
|
215
|
+
},
|
|
216
|
+
help: {
|
|
217
|
+
name: 'mode',
|
|
218
|
+
summary: 'modes of operation: spec, plan, implement, review, debug',
|
|
219
|
+
model: 'Full agentic development lifecycle in one subtree. spec captures requirements; plan decomposes them into executable steps; planner/implementer/reviewer are the handoff-spawn leaves that delegate each phase to a fresh worker and return a job handle; debug root-causes failures with a reproduce-first workflow. Spawned workers register as jobs — monitor and collect at `crtr job`.',
|
|
220
|
+
children: [
|
|
221
|
+
{ name: 'spec', desc: 'create, read, list specifications', useWhen: 'capturing requirements before planning' },
|
|
222
|
+
{ name: 'plan', desc: 'create, read, list plans', useWhen: 'shaping or inspecting work' },
|
|
223
|
+
{ name: 'debug', desc: 'reproduce-first root-cause workflow', useWhen: 'a bug, test failure, or unexpected behavior needs root-causing' },
|
|
224
|
+
{ name: 'planner', desc: 'planning agent for an approved spec', useWhen: 'handing off spec → plan decomposition to a fresh agent' },
|
|
225
|
+
{ name: 'implementer', desc: 'implementation agent for an approved plan', useWhen: 'handing off plan → code implementation to a fresh agent' },
|
|
226
|
+
{ name: 'reviewer', desc: 'review agent for a plan or spec artifact', useWhen: 'launching a review of a plan or spec artifact' },
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
children: [registerSpec(), registerPlan(), registerDebug(), newPlanner, newImplementer, newReviewer],
|
|
230
|
+
});
|
|
231
|
+
}
|
package/dist/commands/pkg.js
CHANGED
|
@@ -1008,6 +1008,11 @@ const marketBranch = defineBranch({
|
|
|
1008
1008
|
export function registerPkg() {
|
|
1009
1009
|
return defineBranch({
|
|
1010
1010
|
name: 'pkg',
|
|
1011
|
+
rootEntry: {
|
|
1012
|
+
concept: 'plugins and marketplaces that supply skills',
|
|
1013
|
+
desc: 'manage plugins and marketplaces',
|
|
1014
|
+
useWhen: 'installing or browsing skill collections',
|
|
1015
|
+
},
|
|
1011
1016
|
help: {
|
|
1012
1017
|
name: 'pkg',
|
|
1013
1018
|
summary: 'manage plugins and plugin marketplaces',
|
package/dist/commands/plan.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const PLAN_NEW_GUIDE = "## Planning workflow\n\nBuild and save an implementation plan: a map another agent can execute without\nre-discovering context. Work through these phases before saving.\n\n### Phase 1: Understand\n\nBuild a full picture of the request and the code. Search for reusable\nfunctions, patterns, and existing implementations before proposing new ones.\n\nLaunch up to 3 Explore subagents IN PARALLEL (single message, multiple tool\ncalls). Use 1 agent for isolated, small-scope tasks; use more when scope is\nuncertain or multiple subsystems are involved. Give each a distinct focus so\nthey don't duplicate work.\n\n### Phase 2: Design\n\nDesign the implementation from Phase 1 findings. Default to launching at least\n1 Plan agent to validate your understanding and surface alternatives. Skip only\nfor trivially small tasks (typo fixes, single-line renames). Use up to 3 agents\nfor large refactors or architectural changes.\n\nIn the Plan agent prompt: include file paths, code-path traces, requirements,\nand constraints from Phase 1. Request a detailed implementation plan.\n\n### Phase 3: Review findings\n\nRead the critical files identified by agents. Confirm the plan aligns with the\nuser's request. Use AskUserQuestion ONLY to clarify requirements or choose\nbetween approaches \u2014 never to ask \"is this okay?\" or \"should I proceed?\".\n\n### Phase 4: Compose the plan body\n\nQuality bar \u2014 every item below is cheap to satisfy and saves the implementer\nfrom re-deciding:\n\n- Every decision pinned. No \"if X then Y\" branches, no \"investigate whether\u2026\",\n no deferred choices. If you don't know, find out or ask now.\n- No timelines, no fallbacks, no magic values, no \"for now\" shortcuts.\n- Where the plan creates a new interface, schema, or contract, write the actual\n shape, not \"design a Foo type.\"\n\nRequired sections:\n\n # Plan: <one-line title>\n\n ## Context\n <why this change is being made \u2014 the problem, what prompted it, intended outcome>\n\n ## Recommended approach\n <your chosen approach only. Concise enough to scan, detailed enough to execute.>\n\n ## Files to modify / create\n - `path/to/file.ts` \u2014 <what changes>\n\n ## Existing utilities to reuse\n - `functionName` from `path/to/file.ts:LL` \u2014 <why it fits>\n\n ## Verification\n <how to test end-to-end \u2014 run the code, run tests, etc.>\n\nFor plans touching 4+ files across distinct concerns, structure parallel tasks:\n\n ## Tasks\n - **Task 1**: <name>\n - Files: `a.ts`, `b.ts` (disjoint from other tasks)\n - Depends on: (none) | Task N\n - Integration: <shared types/APIs with exact shape>\n - Changes: <bullets>\n\nSkip the Tasks structure for small plans; it's noise when there's no\nparallelism to unlock.\n\n### Phase 5: Save\n\nRun `crtr
|
|
1
|
+
export declare const PLAN_NEW_GUIDE = "## Planning workflow\n\nBuild and save an implementation plan: a map another agent can execute without\nre-discovering context. Work through these phases before saving.\n\n### Phase 1: Understand\n\nBuild a full picture of the request and the code. Search for reusable\nfunctions, patterns, and existing implementations before proposing new ones.\n\nLaunch up to 3 Explore subagents IN PARALLEL (single message, multiple tool\ncalls). Use 1 agent for isolated, small-scope tasks; use more when scope is\nuncertain or multiple subsystems are involved. Give each a distinct focus so\nthey don't duplicate work.\n\n### Phase 2: Design\n\nDesign the implementation from Phase 1 findings. Default to launching at least\n1 Plan agent to validate your understanding and surface alternatives. Skip only\nfor trivially small tasks (typo fixes, single-line renames). Use up to 3 agents\nfor large refactors or architectural changes.\n\nIn the Plan agent prompt: include file paths, code-path traces, requirements,\nand constraints from Phase 1. Request a detailed implementation plan.\n\n### Phase 3: Review findings\n\nRead the critical files identified by agents. Confirm the plan aligns with the\nuser's request. Use AskUserQuestion ONLY to clarify requirements or choose\nbetween approaches \u2014 never to ask \"is this okay?\" or \"should I proceed?\".\n\n### Phase 4: Compose the plan body\n\nQuality bar \u2014 every item below is cheap to satisfy and saves the implementer\nfrom re-deciding:\n\n- Every decision pinned. No \"if X then Y\" branches, no \"investigate whether\u2026\",\n no deferred choices. If you don't know, find out or ask now.\n- No timelines, no fallbacks, no magic values, no \"for now\" shortcuts.\n- Where the plan creates a new interface, schema, or contract, write the actual\n shape, not \"design a Foo type.\"\n\nRequired sections:\n\n # Plan: <one-line title>\n\n ## Context\n <why this change is being made \u2014 the problem, what prompted it, intended outcome>\n\n ## Recommended approach\n <your chosen approach only. Concise enough to scan, detailed enough to execute.>\n\n ## Files to modify / create\n - `path/to/file.ts` \u2014 <what changes>\n\n ## Existing utilities to reuse\n - `functionName` from `path/to/file.ts:LL` \u2014 <why it fits>\n\n ## Verification\n <how to test end-to-end \u2014 run the code, run tests, etc.>\n\nFor plans touching 4+ files across distinct concerns, structure parallel tasks:\n\n ## Tasks\n - **Task 1**: <name>\n - Files: `a.ts`, `b.ts` (disjoint from other tasks)\n - Depends on: (none) | Task N\n - Integration: <shared types/APIs with exact shape>\n - Changes: <bullets>\n\nSkip the Tasks structure for small plans; it's noise when there's no\nparallelism to unlock.\n\n### Phase 5: Save\n\nRun `crtr mode plan new`:\n\n echo '<plan markdown>' | crtr mode plan new <kebab-case-name> [--spec <spec-name>]\n\n- NAME: short kebab-case slug. Nested names become subdirectories\n (e.g. `auth/jwt-refresh`).\n- Pipe the full plan markdown composed in Phase 4 on stdin.\n- `--spec` (optional): name of the spec this plan implements. Enables alignment\n check by the reviewer.\n\nOutput: `{path, follow_up}`. The `follow_up` field names the exact next call\n\u2014 run it.\n\n### Phase 6: Oversize check\n\nIf `follow_up` contains an oversize advisory (plan exceeds 200 lines), split\ninto a short index plan plus nested part plans, each under the threshold.\nRe-save. The implementer executes parts one at a time; long monolithic plans\nare under-decomposed.\n\n### Phase 7: Done\n\nAfter the reviewer approves the plan, your turn ends. Do not summarize in chat.\nFor a human gate, optionally put the plan in front of a person with `crtr\nhuman review` (anchored comments) and gate the handoff with `crtr human\napprove`. This complements \u2014 it does not replace \u2014 `crtr mode reviewer`.\nIf the user is ready to build, ask once whether to hand off; if yes, run:\n`crtr mode implementer` with the plan path.";
|
|
2
2
|
export declare const PLAN_SHOW_GUIDE = "";
|
|
3
3
|
import type { BranchDef } from '../core/command.js';
|
|
4
4
|
export declare function registerPlan(): BranchDef;
|
package/dist/commands/plan.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// `crtr
|
|
1
|
+
// `crtr mode plan` subtree — plan new / show / list handlers.
|
|
2
2
|
export const PLAN_NEW_GUIDE = `## Planning workflow
|
|
3
3
|
|
|
4
4
|
Build and save an implementation plan: a map another agent can execute without
|
|
@@ -74,9 +74,9 @@ parallelism to unlock.
|
|
|
74
74
|
|
|
75
75
|
### Phase 5: Save
|
|
76
76
|
|
|
77
|
-
Run \`crtr
|
|
77
|
+
Run \`crtr mode plan new\`:
|
|
78
78
|
|
|
79
|
-
echo '<plan markdown>' | crtr
|
|
79
|
+
echo '<plan markdown>' | crtr mode plan new <kebab-case-name> [--spec <spec-name>]
|
|
80
80
|
|
|
81
81
|
- NAME: short kebab-case slug. Nested names become subdirectories
|
|
82
82
|
(e.g. \`auth/jwt-refresh\`).
|
|
@@ -99,9 +99,9 @@ are under-decomposed.
|
|
|
99
99
|
After the reviewer approves the plan, your turn ends. Do not summarize in chat.
|
|
100
100
|
For a human gate, optionally put the plan in front of a person with \`crtr
|
|
101
101
|
human review\` (anchored comments) and gate the handoff with \`crtr human
|
|
102
|
-
approve\`. This complements — it does not replace — \`crtr
|
|
102
|
+
approve\`. This complements — it does not replace — \`crtr mode reviewer\`.
|
|
103
103
|
If the user is ready to build, ask once whether to hand off; if yes, run:
|
|
104
|
-
\`crtr
|
|
104
|
+
\`crtr mode implementer\` with the plan path.`;
|
|
105
105
|
export const PLAN_SHOW_GUIDE = '';
|
|
106
106
|
import { defineBranch, defineLeaf } from '../core/command.js';
|
|
107
107
|
import { saveArtifact, readArtifact, listArtifacts, OVERSIZE_WARN_LINES } from '../core/artifact.js';
|
|
@@ -110,7 +110,7 @@ export function registerPlan() {
|
|
|
110
110
|
const planNew = defineLeaf({
|
|
111
111
|
name: 'new',
|
|
112
112
|
help: {
|
|
113
|
-
name: '
|
|
113
|
+
name: 'mode plan new',
|
|
114
114
|
summary: 'draft a plan from intent and optional spec alignment',
|
|
115
115
|
guide: PLAN_NEW_GUIDE,
|
|
116
116
|
params: [
|
|
@@ -146,7 +146,7 @@ export function registerPlan() {
|
|
|
146
146
|
name: 'follow_up',
|
|
147
147
|
type: 'string',
|
|
148
148
|
required: true,
|
|
149
|
-
constraint: 'Recommended next call (reviewer spawn via `crtr
|
|
149
|
+
constraint: 'Recommended next call (reviewer spawn via `crtr mode reviewer`).',
|
|
150
150
|
},
|
|
151
151
|
],
|
|
152
152
|
outputKind: 'object',
|
|
@@ -163,7 +163,7 @@ export function registerPlan() {
|
|
|
163
163
|
if (spec !== undefined)
|
|
164
164
|
meta['spec'] = spec;
|
|
165
165
|
const { path, oversize, lineCount } = saveArtifact('plans', name, body, meta);
|
|
166
|
-
let follow_up = `Review it: crtr
|
|
166
|
+
let follow_up = `Review it: crtr mode reviewer ${path} --kind plan (returns {job_id}), then crtr job read result <job_id> --wait.`;
|
|
167
167
|
follow_up +=
|
|
168
168
|
` Optional human gate (complements, does not replace the agent reviewer): crtr human review --file ${path} for anchored comments, then gate handoff with crtr human approve --title "Approve this plan?".`;
|
|
169
169
|
if (oversize) {
|
|
@@ -176,7 +176,7 @@ export function registerPlan() {
|
|
|
176
176
|
const planShow = defineLeaf({
|
|
177
177
|
name: 'show',
|
|
178
178
|
help: {
|
|
179
|
-
name: '
|
|
179
|
+
name: 'mode plan show',
|
|
180
180
|
summary: 'read a plan artifact by name',
|
|
181
181
|
params: [
|
|
182
182
|
{
|
|
@@ -225,7 +225,7 @@ export function registerPlan() {
|
|
|
225
225
|
const planList = defineLeaf({
|
|
226
226
|
name: 'list',
|
|
227
227
|
help: {
|
|
228
|
-
name: '
|
|
228
|
+
name: 'mode plan list',
|
|
229
229
|
summary: 'paginated list of plan artifacts, sorted ascending by name',
|
|
230
230
|
params: [
|
|
231
231
|
{
|
|
@@ -295,7 +295,7 @@ export function registerPlan() {
|
|
|
295
295
|
return defineBranch({
|
|
296
296
|
name: 'plan',
|
|
297
297
|
help: {
|
|
298
|
-
name: '
|
|
298
|
+
name: 'mode plan',
|
|
299
299
|
summary: 'create and read plan artifacts',
|
|
300
300
|
model: 'Lifecycle: draft -> active -> handed-off.',
|
|
301
301
|
children: [
|
|
@@ -304,6 +304,19 @@ export function registerPlan() {
|
|
|
304
304
|
{ name: 'list', desc: 'enumerate plans', useWhen: 'discovering what plans exist' },
|
|
305
305
|
],
|
|
306
306
|
},
|
|
307
|
+
slash: {
|
|
308
|
+
name: 'plan',
|
|
309
|
+
description: 'Plan mode — decompose intent (and an optional spec) into an executable plan.',
|
|
310
|
+
argumentHint: '[what to plan]',
|
|
311
|
+
body: `You are entering **plan mode**: decompose work into an executable plan.
|
|
312
|
+
|
|
313
|
+
1. Run \`crtr mode plan new -h\` to load the planning workflow and output schema.
|
|
314
|
+
2. Follow it to draft the plan, then save by piping the markdown to \`crtr mode plan new <name>\` on stdin (pass \`--spec <path>\` if an approved spec exists).
|
|
315
|
+
|
|
316
|
+
The request: $ARGUMENTS
|
|
317
|
+
|
|
318
|
+
If no request was given, ask the user what to plan before starting.`,
|
|
319
|
+
},
|
|
307
320
|
children: [planNew, planShow, planList],
|
|
308
321
|
});
|
|
309
322
|
}
|
package/dist/commands/skill.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { writeFileSync } from 'node:fs';
|
|
6
6
|
import { defineBranch, defineLeaf } from '../core/command.js';
|
|
7
|
+
import { stateBlock } from '../core/help.js';
|
|
7
8
|
import { usage, general, notFound } from '../core/errors.js';
|
|
8
9
|
import { SCOPE_SKILL_PLUGIN, SKILL_ENTRY_FILE, SKILLS_DIR, SKILL_TYPES, isSkillType, skillConfigKey, } from '../types.js';
|
|
9
10
|
import { resolveSkill, listAllSkills, listInstalledPlugins, findPluginByName, parseSkillQualifier, listSkillSiblings, listSkillChildren, } from '../core/resolver.js';
|
|
@@ -583,6 +584,11 @@ const stateBranch = defineBranch({
|
|
|
583
584
|
},
|
|
584
585
|
children: [stateEnable, stateDisable],
|
|
585
586
|
});
|
|
587
|
+
/** The skill subtree's live state: the loaded-skills catalog as a self-named
|
|
588
|
+
* `<skills count="N">` element. The tag carries the label and the count is an
|
|
589
|
+
* attribute, so the body is the grouped tree alone — no "Loaded skills (N)"
|
|
590
|
+
* header to duplicate. Returns null (block omitted) when discovery fails or
|
|
591
|
+
* nothing is loaded. */
|
|
586
592
|
function buildSkillCatalog() {
|
|
587
593
|
let skills;
|
|
588
594
|
try {
|
|
@@ -614,10 +620,14 @@ function buildSkillCatalog() {
|
|
|
614
620
|
continue;
|
|
615
621
|
(scope === 'project' ? projectSources : userSources).push({ plugin, roots });
|
|
616
622
|
}
|
|
617
|
-
const
|
|
618
|
-
renderCatalogSection('Project', projectSources,
|
|
619
|
-
renderCatalogSection('User', userSources,
|
|
620
|
-
|
|
623
|
+
const body = [];
|
|
624
|
+
renderCatalogSection('Project', projectSources, body);
|
|
625
|
+
renderCatalogSection('User', userSources, body);
|
|
626
|
+
// renderCatalogSection leads each section with a blank separator; drop the
|
|
627
|
+
// leading one so the element body starts on its first real line.
|
|
628
|
+
while (body.length > 0 && body[0] === '')
|
|
629
|
+
body.shift();
|
|
630
|
+
return stateBlock('skills', { count: skills.length }, body.join('\n'));
|
|
621
631
|
}
|
|
622
632
|
function renderCatalogSection(label, sources, out) {
|
|
623
633
|
if (sources.length === 0)
|
|
@@ -685,6 +695,12 @@ function renderCatalogSection(label, sources, out) {
|
|
|
685
695
|
export function registerSkill() {
|
|
686
696
|
return defineBranch({
|
|
687
697
|
name: 'skill',
|
|
698
|
+
rootEntry: {
|
|
699
|
+
concept: 'a SKILL.md you read to adopt its workflow',
|
|
700
|
+
desc: 'find, read, author, and manage skills',
|
|
701
|
+
useWhen: 'a task matches a loaded skill — read it before improvising. `crtr skill read <name>` loads one by name from the catalog below; `crtr skill find` only when the name is not already listed. Names are crtr identifiers, not file paths — never cat or find SKILL.md off disk.',
|
|
702
|
+
dynamicState: buildSkillCatalog,
|
|
703
|
+
},
|
|
688
704
|
help: {
|
|
689
705
|
name: 'skill',
|
|
690
706
|
summary: 'discover, read, author, and manage skill state',
|
package/dist/commands/spec.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const SPEC_NEW_GUIDE = "## Spec workflow\n\nBuild and save a design + requirements spec: a document describing what to\nbuild, the shape of the solution, and the behaviors it must satisfy. A spec is\nupstream of a plan \u2014 it captures decisions, not implementation steps.\n\nAnti-pattern: do not fish for clarifications upfront. Draft a concrete spec\nfirst based on your investigation, then iterate. A specific draft the user can\nreact to converges faster than a list of questions in a vacuum.\n\n### Phase 1: Shape\n\nBuild a comprehensive picture of the problem and the relevant code. Surface\nexisting patterns, constraints, and prior decisions.\n\nLaunch up to 3 Explore subagents IN PARALLEL (single message, multiple tool\ncalls). Use 1 agent for narrow, well-scoped problems; use more when the spec\ntouches several subsystems or you need to compare existing implementations.\nQuality over quantity \u2014 3 agents maximum.\n\nAfter exploration, draft a high-level design: the shape of the solution, new or\nchanged pieces, the boundaries.\n\n### Phase 2: Requirements\n\nTranslate the shape into concrete behavioral requirements. Each requirement\nmust be:\n\n- Testable \u2014 has a clear pass/fail condition.\n- Behavior-focused \u2014 describes what the system does, not how.\n- Scoped \u2014 covers one observable behavior.\n\nGroup requirements by capability. Plain English is fine. For conditional or\nstateful behaviors, EARS templates sharpen phrasing:\n`When <trigger>, the system shall <behavior>` or\n`If <condition>, then the system shall <response>`.\n\nFor larger / multi-component designs, walk the design end-to-end: at each step\nfrom trigger to final state, verify preconditions, state, failure handling, and\nhandoffs between components are specified. Skip this for small self-contained\nspecs.\n\n### Phase 3: Deepen\n\nRead the critical files identified during Phase 1. Reconcile requirements\nagainst the shape \u2014 if a requirement reveals a gap in the design, refine the\ndesign before saving.\n\nUse AskUserQuestion ONLY to clarify requirements or choose between approaches.\nNever use it to ask \"is this spec okay?\" or \"should I save?\".\n\n### Phase 4: Compose the spec body\n\nRequired sections:\n\n # Spec: <one-line title>\n\n ## Context\n <the problem this spec addresses, what motivates it, and the intended outcome.\n Include relevant constraints \u2014 user goals, stakeholders, deadlines.>\n\n ## Design\n <the shape of the solution. Components, data flow, key decisions and why they\n were chosen. Reference existing code with `file_path:line_number`.>\n\n ## Requirements\n <grouped behavioral requirements. Each one testable. Plain English is fine.>\n\n ### <Capability A>\n - <one observable behavior>\n\n ### <Capability B>\n - ...\n\n ## Out of scope\n <things explicitly NOT covered, so the next reader knows where the edges are.>\n\n ## Open questions\n <anything you could not resolve. Empty if all decisions are pinned.>\n\n### Phase 5: Save\n\nRun `crtr
|
|
1
|
+
export declare const SPEC_NEW_GUIDE = "## Spec workflow\n\nBuild and save a design + requirements spec: a document describing what to\nbuild, the shape of the solution, and the behaviors it must satisfy. A spec is\nupstream of a plan \u2014 it captures decisions, not implementation steps.\n\nAnti-pattern: do not fish for clarifications upfront. Draft a concrete spec\nfirst based on your investigation, then iterate. A specific draft the user can\nreact to converges faster than a list of questions in a vacuum.\n\n### Phase 1: Shape\n\nBuild a comprehensive picture of the problem and the relevant code. Surface\nexisting patterns, constraints, and prior decisions.\n\nLaunch up to 3 Explore subagents IN PARALLEL (single message, multiple tool\ncalls). Use 1 agent for narrow, well-scoped problems; use more when the spec\ntouches several subsystems or you need to compare existing implementations.\nQuality over quantity \u2014 3 agents maximum.\n\nAfter exploration, draft a high-level design: the shape of the solution, new or\nchanged pieces, the boundaries.\n\n### Phase 2: Requirements\n\nTranslate the shape into concrete behavioral requirements. Each requirement\nmust be:\n\n- Testable \u2014 has a clear pass/fail condition.\n- Behavior-focused \u2014 describes what the system does, not how.\n- Scoped \u2014 covers one observable behavior.\n\nGroup requirements by capability. Plain English is fine. For conditional or\nstateful behaviors, EARS templates sharpen phrasing:\n`When <trigger>, the system shall <behavior>` or\n`If <condition>, then the system shall <response>`.\n\nFor larger / multi-component designs, walk the design end-to-end: at each step\nfrom trigger to final state, verify preconditions, state, failure handling, and\nhandoffs between components are specified. Skip this for small self-contained\nspecs.\n\n### Phase 3: Deepen\n\nRead the critical files identified during Phase 1. Reconcile requirements\nagainst the shape \u2014 if a requirement reveals a gap in the design, refine the\ndesign before saving.\n\nUse AskUserQuestion ONLY to clarify requirements or choose between approaches.\nNever use it to ask \"is this spec okay?\" or \"should I save?\".\n\n### Phase 4: Compose the spec body\n\nRequired sections:\n\n # Spec: <one-line title>\n\n ## Context\n <the problem this spec addresses, what motivates it, and the intended outcome.\n Include relevant constraints \u2014 user goals, stakeholders, deadlines.>\n\n ## Design\n <the shape of the solution. Components, data flow, key decisions and why they\n were chosen. Reference existing code with `file_path:line_number`.>\n\n ## Requirements\n <grouped behavioral requirements. Each one testable. Plain English is fine.>\n\n ### <Capability A>\n - <one observable behavior>\n\n ### <Capability B>\n - ...\n\n ## Out of scope\n <things explicitly NOT covered, so the next reader knows where the edges are.>\n\n ## Open questions\n <anything you could not resolve. Empty if all decisions are pinned.>\n\n### Phase 5: Save\n\nRun `crtr mode spec new`:\n\n echo '<spec markdown>' | crtr mode spec new <kebab-case-name>\n\n- NAME: short kebab-case slug. Nested names become subdirectories\n (e.g. `auth/refresh-tokens`).\n- Pipe the full spec markdown composed in Phase 4 on stdin.\n\nOutput: `{path, follow_up}`. The `follow_up` field names the exact next call\n\u2014 run it.\n\n### Phase 6: Done\n\nAfter the reviewer approves the spec, your turn ends. Do not summarize in chat.\nFor a human gate, optionally run `crtr human review` on the spec for anchored\ncomments and `crtr human approve` to gate the handoff \u2014 this complements, not\nreplaces, `crtr mode reviewer`.\nIf the user is ready to plan, ask once whether to hand off; if yes, follow the\n`follow_up` instructions from the save output.";
|
|
2
2
|
import type { BranchDef } from '../core/command.js';
|
|
3
3
|
export declare function registerSpec(): BranchDef;
|
package/dist/commands/spec.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// `crtr
|
|
1
|
+
// `crtr mode spec` subtree — spec new / show / list handlers.
|
|
2
2
|
export const SPEC_NEW_GUIDE = `## Spec workflow
|
|
3
3
|
|
|
4
4
|
Build and save a design + requirements spec: a document describing what to
|
|
@@ -81,9 +81,9 @@ Required sections:
|
|
|
81
81
|
|
|
82
82
|
### Phase 5: Save
|
|
83
83
|
|
|
84
|
-
Run \`crtr
|
|
84
|
+
Run \`crtr mode spec new\`:
|
|
85
85
|
|
|
86
|
-
echo '<spec markdown>' | crtr
|
|
86
|
+
echo '<spec markdown>' | crtr mode spec new <kebab-case-name>
|
|
87
87
|
|
|
88
88
|
- NAME: short kebab-case slug. Nested names become subdirectories
|
|
89
89
|
(e.g. \`auth/refresh-tokens\`).
|
|
@@ -97,7 +97,7 @@ Output: \`{path, follow_up}\`. The \`follow_up\` field names the exact next call
|
|
|
97
97
|
After the reviewer approves the spec, your turn ends. Do not summarize in chat.
|
|
98
98
|
For a human gate, optionally run \`crtr human review\` on the spec for anchored
|
|
99
99
|
comments and \`crtr human approve\` to gate the handoff — this complements, not
|
|
100
|
-
replaces, \`crtr
|
|
100
|
+
replaces, \`crtr mode reviewer\`.
|
|
101
101
|
If the user is ready to plan, ask once whether to hand off; if yes, follow the
|
|
102
102
|
\`follow_up\` instructions from the save output.`;
|
|
103
103
|
import { defineBranch, defineLeaf } from '../core/command.js';
|
|
@@ -107,7 +107,7 @@ export function registerSpec() {
|
|
|
107
107
|
const specNew = defineLeaf({
|
|
108
108
|
name: 'new',
|
|
109
109
|
help: {
|
|
110
|
-
name: '
|
|
110
|
+
name: 'mode spec new',
|
|
111
111
|
summary: 'draft a specification artifact from intent',
|
|
112
112
|
guide: SPEC_NEW_GUIDE,
|
|
113
113
|
params: [
|
|
@@ -136,7 +136,7 @@ export function registerSpec() {
|
|
|
136
136
|
name: 'follow_up',
|
|
137
137
|
type: 'string',
|
|
138
138
|
required: true,
|
|
139
|
-
constraint: 'Recommended next call (planner spawn via `crtr
|
|
139
|
+
constraint: 'Recommended next call (planner spawn via `crtr mode planner`).',
|
|
140
140
|
},
|
|
141
141
|
],
|
|
142
142
|
outputKind: 'object',
|
|
@@ -146,9 +146,9 @@ export function registerSpec() {
|
|
|
146
146
|
const name = input['name'];
|
|
147
147
|
const body = input['body'];
|
|
148
148
|
const { path, oversize, lineCount } = saveArtifact('specs', name, body);
|
|
149
|
-
let follow_up = `Plan it: crtr
|
|
149
|
+
let follow_up = `Plan it: crtr mode planner ${path} (returns {job_id}), then crtr job read result <job_id> --wait.`;
|
|
150
150
|
follow_up +=
|
|
151
|
-
` Optional human gate before planning (complements, does not replace \`crtr
|
|
151
|
+
` Optional human gate before planning (complements, does not replace \`crtr mode reviewer\`): crtr human review --file ${path} for anchored comments, then gate with crtr human approve --title "Approve this spec?".`;
|
|
152
152
|
if (oversize) {
|
|
153
153
|
follow_up +=
|
|
154
154
|
` OVERSIZE ADVISORY: this spec is ${lineCount} lines (> ${OVERSIZE_WARN_LINES}). Split into focused sub-specs before planning.`;
|
|
@@ -159,7 +159,7 @@ export function registerSpec() {
|
|
|
159
159
|
const specShow = defineLeaf({
|
|
160
160
|
name: 'show',
|
|
161
161
|
help: {
|
|
162
|
-
name: '
|
|
162
|
+
name: 'mode spec show',
|
|
163
163
|
summary: 'read a spec artifact by name',
|
|
164
164
|
params: [
|
|
165
165
|
{
|
|
@@ -202,7 +202,7 @@ export function registerSpec() {
|
|
|
202
202
|
const specList = defineLeaf({
|
|
203
203
|
name: 'list',
|
|
204
204
|
help: {
|
|
205
|
-
name: '
|
|
205
|
+
name: 'mode spec list',
|
|
206
206
|
summary: 'paginated list of spec artifacts, sorted ascending by name',
|
|
207
207
|
params: [
|
|
208
208
|
{
|
|
@@ -272,7 +272,7 @@ export function registerSpec() {
|
|
|
272
272
|
return defineBranch({
|
|
273
273
|
name: 'spec',
|
|
274
274
|
help: {
|
|
275
|
-
name: '
|
|
275
|
+
name: 'mode spec',
|
|
276
276
|
summary: 'create and read specification artifacts',
|
|
277
277
|
model: 'Lifecycle: draft -> approved. Approved specs drive plan creation.',
|
|
278
278
|
children: [
|
|
@@ -281,6 +281,19 @@ export function registerSpec() {
|
|
|
281
281
|
{ name: 'list', desc: 'enumerate specs', useWhen: 'discovering what specs exist' },
|
|
282
282
|
],
|
|
283
283
|
},
|
|
284
|
+
slash: {
|
|
285
|
+
name: 'spec',
|
|
286
|
+
description: 'Spec mode — turn intent into a specification artifact (spec-driven workflow).',
|
|
287
|
+
argumentHint: '[what to spec]',
|
|
288
|
+
body: `You are entering **spec mode**: turn intent into a written specification artifact.
|
|
289
|
+
|
|
290
|
+
1. Run \`crtr mode spec new -h\` to load the spec-authoring workflow and output schema.
|
|
291
|
+
2. Follow that workflow to draft the spec, then save it by piping the markdown to \`crtr mode spec new <name>\` on stdin.
|
|
292
|
+
|
|
293
|
+
The request: $ARGUMENTS
|
|
294
|
+
|
|
295
|
+
If no request was given, ask the user what to spec before starting.`,
|
|
296
|
+
},
|
|
284
297
|
children: [specNew, specShow, specList],
|
|
285
298
|
});
|
|
286
299
|
}
|
package/dist/commands/sys.js
CHANGED
|
@@ -697,6 +697,11 @@ const sysVersionLeaf = defineLeaf({
|
|
|
697
697
|
export function registerSys() {
|
|
698
698
|
return defineBranch({
|
|
699
699
|
name: 'sys',
|
|
700
|
+
rootEntry: {
|
|
701
|
+
concept: 'crtr configuration, diagnostics, and self-management',
|
|
702
|
+
desc: 'config, doctor, update, version',
|
|
703
|
+
useWhen: 'managing the crtr installation',
|
|
704
|
+
},
|
|
700
705
|
help: {
|
|
701
706
|
name: 'sys',
|
|
702
707
|
summary: 'crtr system configuration, diagnostics, and self-management',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Tests for the job subtree (monitoring) and agent
|
|
1
|
+
// Tests for the job subtree (monitoring) and agent/mode spawn leaves argv
|
|
2
2
|
// migration. Exercises parseArgv against each leaf's param schema directly —
|
|
3
3
|
// no subprocess, no tmux, no filesystem side-effects from the handler.
|
|
4
4
|
//
|
|
@@ -67,9 +67,9 @@ const failParams = [
|
|
|
67
67
|
{ kind: 'positional', name: 'job_id', type: 'string', required: true, constraint: '' },
|
|
68
68
|
];
|
|
69
69
|
// ---------------------------------------------------------------------------
|
|
70
|
-
// agent new
|
|
70
|
+
// agent new (formerly job start prompt)
|
|
71
71
|
// ---------------------------------------------------------------------------
|
|
72
|
-
describe('agent new
|
|
72
|
+
describe('agent new', () => {
|
|
73
73
|
// stdin is handled by readStdinRaw() which requires actual stdin — we only
|
|
74
74
|
// test the non-stdin flag parsing here.
|
|
75
75
|
test('--cwd flag parsed as string', async () => {
|
|
@@ -91,9 +91,9 @@ describe('agent new prompt', () => {
|
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
// ---------------------------------------------------------------------------
|
|
94
|
-
// agent
|
|
94
|
+
// agent fork (formerly job start fork)
|
|
95
95
|
// ---------------------------------------------------------------------------
|
|
96
|
-
describe('agent
|
|
96
|
+
describe('agent fork', () => {
|
|
97
97
|
test('no args parses cleanly', async () => {
|
|
98
98
|
const result = await parseArgv(startForkParams, []);
|
|
99
99
|
assert.equal(result['cwd'], undefined);
|
|
@@ -107,9 +107,9 @@ describe('agent new fork', () => {
|
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
// ---------------------------------------------------------------------------
|
|
110
|
-
//
|
|
110
|
+
// mode planner (formerly job start planner)
|
|
111
111
|
// ---------------------------------------------------------------------------
|
|
112
|
-
describe('
|
|
112
|
+
describe('mode planner', () => {
|
|
113
113
|
test('positional spec_path required', async () => {
|
|
114
114
|
await assert.rejects(() => parseArgv(startPlannerParams, []), (err) => { assert.match(err.message, /required parameter is missing/); return true; });
|
|
115
115
|
});
|
|
@@ -125,9 +125,9 @@ describe('agent new planner', () => {
|
|
|
125
125
|
});
|
|
126
126
|
});
|
|
127
127
|
// ---------------------------------------------------------------------------
|
|
128
|
-
//
|
|
128
|
+
// mode implementer (formerly job start implementer)
|
|
129
129
|
// ---------------------------------------------------------------------------
|
|
130
|
-
describe('
|
|
130
|
+
describe('mode implementer', () => {
|
|
131
131
|
test('positional plan_path required', async () => {
|
|
132
132
|
await assert.rejects(() => parseArgv(startImplementerParams, []), (err) => { assert.match(err.message, /required parameter is missing/); return true; });
|
|
133
133
|
});
|
|
@@ -137,9 +137,9 @@ describe('agent new implementer', () => {
|
|
|
137
137
|
});
|
|
138
138
|
});
|
|
139
139
|
// ---------------------------------------------------------------------------
|
|
140
|
-
//
|
|
140
|
+
// mode reviewer (formerly job start reviewer)
|
|
141
141
|
// ---------------------------------------------------------------------------
|
|
142
|
-
describe('
|
|
142
|
+
describe('mode reviewer', () => {
|
|
143
143
|
test('positional + --kind required', async () => {
|
|
144
144
|
await assert.rejects(() => parseArgv(startReviewerParams, ['/tmp/artifact.md']), (err) => { assert.match(err.message, /required parameter is missing/); return true; });
|
|
145
145
|
});
|