@crouton-kit/crouter 0.3.3 → 0.3.8
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/README.md +2 -2
- package/dist/builtin-skills/skills/crouter-development/marketplaces/SKILL.md +1 -1
- package/dist/builtin-skills/skills/crouter-development/plugins/SKILL.md +3 -3
- package/dist/cli.js +6 -6
- package/dist/commands/__tests__/skill.test.js +24 -28
- package/dist/commands/{flow.d.ts → agent.d.ts} +1 -1
- package/dist/commands/agent.js +384 -0
- package/dist/commands/debug.d.ts +1 -1
- package/dist/commands/debug.js +7 -7
- package/dist/commands/job.js +54 -379
- package/dist/commands/plan.d.ts +1 -1
- package/dist/commands/plan.js +11 -11
- package/dist/commands/skill.js +114 -107
- package/dist/commands/spec.d.ts +1 -1
- package/dist/commands/spec.js +11 -11
- package/dist/core/__tests__/job.test.js +38 -74
- package/dist/core/__tests__/jobs.test.d.ts +1 -0
- package/dist/core/__tests__/jobs.test.js +66 -0
- package/dist/core/__tests__/resolver.test.d.ts +1 -0
- package/dist/core/__tests__/resolver.test.js +113 -0
- package/dist/core/config.js +20 -2
- package/dist/core/jobs.d.ts +26 -12
- package/dist/core/jobs.js +151 -42
- package/dist/core/resolver.d.ts +1 -2
- package/dist/core/resolver.js +60 -46
- package/dist/core/spawn.d.ts +26 -3
- package/dist/core/spawn.js +144 -11
- package/dist/prompts/agent.d.ts +3 -3
- package/dist/prompts/agent.js +20 -18
- package/dist/prompts/debug.js +14 -7
- package/dist/prompts/skill.js +16 -16
- package/dist/types.d.ts +1 -1
- package/dist/types.js +2 -2
- package/package.json +2 -2
- package/dist/commands/flow.js +0 -24
package/dist/core/spawn.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Tmux pane spawning machinery for crtr job subtree.
|
|
2
2
|
//
|
|
3
3
|
// Kept: spawnAgent (fire-and-forget new pane), spawnAndDetach (detach + kill originating pane),
|
|
4
|
-
// shellQuote, isInTmux, countPanesInCurrentWindow.
|
|
4
|
+
// shellQuote, isInTmux, countPanesInCurrentWindow, findWindowWithSpace.
|
|
5
5
|
//
|
|
6
6
|
// Removed: createSession, submitToSession, awaitSession, waitForResult,
|
|
7
7
|
// sessionDirForId, writeSessionMeta, readSessionMeta — all superseded
|
|
@@ -28,6 +28,118 @@ export function countPanesInCurrentWindow() {
|
|
|
28
28
|
return 0;
|
|
29
29
|
return result.stdout.split('\n').filter((line) => line.trim() !== '').length;
|
|
30
30
|
}
|
|
31
|
+
function listWindowsInCurrentSession() {
|
|
32
|
+
const result = spawnSync('tmux', ['list-windows', '-F', '#{window_id} #{window_panes} #{window_active}'], { encoding: 'utf8' });
|
|
33
|
+
if (result.status !== 0)
|
|
34
|
+
return [];
|
|
35
|
+
return result.stdout
|
|
36
|
+
.split('\n')
|
|
37
|
+
.filter((line) => line.trim() !== '')
|
|
38
|
+
.map((line) => {
|
|
39
|
+
const [id, count, active] = line.split(' ');
|
|
40
|
+
return {
|
|
41
|
+
windowId: id,
|
|
42
|
+
paneCount: Number.parseInt(count, 10),
|
|
43
|
+
isActive: active === '1',
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Map of window_id → list of pane TTYs (basename, e.g. `ttys008`) for every
|
|
49
|
+
* pane in the current tmux session. Used as the bridge between tmux's pane
|
|
50
|
+
* model and the system process table for foreground-command lookup.
|
|
51
|
+
*
|
|
52
|
+
* tmux's `#{pane_current_command}` is unreliable on macOS because the Claude
|
|
53
|
+
* Code CLI sets `process.title` to its version (e.g. `2.1.143`), which is what
|
|
54
|
+
* tmux then reports. Going through the TTY + `ps` gives us the real binary
|
|
55
|
+
* name (`claude`) from the kernel.
|
|
56
|
+
*/
|
|
57
|
+
function paneTtysByWindow() {
|
|
58
|
+
const result = spawnSync('tmux', ['list-panes', '-s', '-F', '#{window_id} #{pane_tty}'], { encoding: 'utf8' });
|
|
59
|
+
const out = new Map();
|
|
60
|
+
if (result.status !== 0)
|
|
61
|
+
return out;
|
|
62
|
+
for (const line of result.stdout.split('\n')) {
|
|
63
|
+
if (line.trim() === '')
|
|
64
|
+
continue;
|
|
65
|
+
const idx = line.indexOf(' ');
|
|
66
|
+
if (idx === -1)
|
|
67
|
+
continue;
|
|
68
|
+
const windowId = line.slice(0, idx);
|
|
69
|
+
const tty = line.slice(idx + 1);
|
|
70
|
+
const ttyBase = tty.startsWith('/dev/') ? tty.slice(5) : tty;
|
|
71
|
+
const existing = out.get(windowId);
|
|
72
|
+
if (existing === undefined) {
|
|
73
|
+
out.set(windowId, [ttyBase]);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
existing.push(ttyBase);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Map of tty basename → set of foreground process `comm` names on that tty.
|
|
83
|
+
* A process is "foreground" if its STAT field includes `+` (member of the
|
|
84
|
+
* terminal's foreground process group). Built from one `ps -axo ...` call.
|
|
85
|
+
*/
|
|
86
|
+
function foregroundCommsByTty() {
|
|
87
|
+
const result = spawnSync('ps', ['-axo', 'stat=,comm=,tty='], { encoding: 'utf8' });
|
|
88
|
+
const out = new Map();
|
|
89
|
+
if (result.status !== 0)
|
|
90
|
+
return out;
|
|
91
|
+
for (const line of result.stdout.split('\n')) {
|
|
92
|
+
if (line.trim() === '')
|
|
93
|
+
continue;
|
|
94
|
+
const m = line.match(/^(\S+)\s+(.+?)\s+(\S+)\s*$/);
|
|
95
|
+
if (m === null)
|
|
96
|
+
continue;
|
|
97
|
+
const [, stat, comm, tty] = m;
|
|
98
|
+
if (!stat.includes('+'))
|
|
99
|
+
continue;
|
|
100
|
+
if (tty === '??' || tty === '?')
|
|
101
|
+
continue;
|
|
102
|
+
const existing = out.get(tty);
|
|
103
|
+
if (existing === undefined) {
|
|
104
|
+
out.set(tty, new Set([comm.trim()]));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
existing.add(comm.trim());
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Find a window in the current tmux session with fewer than `maxPanesPerWindow`
|
|
114
|
+
* panes AND where every existing pane has `claude` as a foreground process.
|
|
115
|
+
* Prefers the active window so the spawned pane is visible to the user;
|
|
116
|
+
* otherwise falls back to the first other eligible window. Returns the tmux
|
|
117
|
+
* window id (e.g. `@5`) to pass via `-t`, or null if no window qualifies.
|
|
118
|
+
*
|
|
119
|
+
* Windows holding non-agent panes (dashboards, log tails, idle shells, editors,
|
|
120
|
+
* REPLs, etc.) are skipped so spawning never disrupts those workflows. A pane
|
|
121
|
+
* qualifies as long as `claude` is among its foreground commands — co-resident
|
|
122
|
+
* helpers like `caffeinate` don't disqualify it.
|
|
123
|
+
*/
|
|
124
|
+
export function findWindowWithSpace(maxPanesPerWindow) {
|
|
125
|
+
const windows = listWindowsInCurrentSession();
|
|
126
|
+
const ttysByWindow = paneTtysByWindow();
|
|
127
|
+
const fgByTty = foregroundCommsByTty();
|
|
128
|
+
const isClaudeOnly = (windowId) => {
|
|
129
|
+
const ttys = ttysByWindow.get(windowId);
|
|
130
|
+
if (ttys === undefined || ttys.length === 0)
|
|
131
|
+
return false;
|
|
132
|
+
return ttys.every((tty) => fgByTty.get(tty)?.has('claude') === true);
|
|
133
|
+
};
|
|
134
|
+
const eligible = windows.filter((w) => w.paneCount < maxPanesPerWindow && isClaudeOnly(w.windowId));
|
|
135
|
+
const active = eligible.find((w) => w.isActive);
|
|
136
|
+
if (active !== undefined)
|
|
137
|
+
return active.windowId;
|
|
138
|
+
const first = eligible[0];
|
|
139
|
+
if (first === undefined)
|
|
140
|
+
return null;
|
|
141
|
+
return first.windowId;
|
|
142
|
+
}
|
|
31
143
|
/**
|
|
32
144
|
* Schedule a kill-pane on the *current* tmux pane after `delaySeconds`, detached
|
|
33
145
|
* so the caller can return normally before the pane dies. No-op outside tmux
|
|
@@ -75,9 +187,15 @@ export function spawnAndDetach(opts) {
|
|
|
75
187
|
message: 'handoff requires tmux (TMUX env var not set)',
|
|
76
188
|
};
|
|
77
189
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
190
|
+
const buildClaudeInner = () => {
|
|
191
|
+
const parts = ['claude'];
|
|
192
|
+
if (opts.name !== undefined && opts.name !== '') {
|
|
193
|
+
parts.push('-n', shellQuote(opts.name));
|
|
194
|
+
}
|
|
195
|
+
parts.push('--dangerously-skip-permissions', shellQuote(opts.prompt));
|
|
196
|
+
return parts.join(' ');
|
|
197
|
+
};
|
|
198
|
+
const inner = opts.command !== undefined ? opts.command : buildClaudeInner();
|
|
81
199
|
const useFailGuard = opts.failGuard !== false;
|
|
82
200
|
const fullCmd = useFailGuard ? wrapperCmd(inner, opts.jobId) : inner;
|
|
83
201
|
const splitArgs = [];
|
|
@@ -117,9 +235,14 @@ export function spawnAndDetach(opts) {
|
|
|
117
235
|
};
|
|
118
236
|
}
|
|
119
237
|
/**
|
|
120
|
-
* Async sibling spawn. Launches a claude session in a
|
|
121
|
-
*
|
|
122
|
-
* with the pane id; the parent stays alive.
|
|
238
|
+
* Async sibling spawn. Launches a claude session in a tmux pane, progressively
|
|
239
|
+
* filling existing windows up to `maxPanesPerWindow` before creating a new
|
|
240
|
+
* window. Returns immediately with the pane id; the parent stays alive.
|
|
241
|
+
*
|
|
242
|
+
* Placement order:
|
|
243
|
+
* 1. Current window, if it has space.
|
|
244
|
+
* 2. Any other window in the session with space.
|
|
245
|
+
* 3. New window (every existing window at capacity).
|
|
123
246
|
*
|
|
124
247
|
* If `fork` is set, uses `claude --resume <id> --fork-session`.
|
|
125
248
|
*/
|
|
@@ -131,17 +254,21 @@ export function spawnAgent(opts) {
|
|
|
131
254
|
};
|
|
132
255
|
}
|
|
133
256
|
const claudeParts = ['claude'];
|
|
257
|
+
if (opts.name !== undefined && opts.name !== '') {
|
|
258
|
+
claudeParts.push('-n', shellQuote(opts.name));
|
|
259
|
+
}
|
|
134
260
|
if (opts.fork !== undefined) {
|
|
135
261
|
claudeParts.push('--resume', opts.fork.sessionId, '--fork-session');
|
|
136
262
|
}
|
|
137
263
|
claudeParts.push('--dangerously-skip-permissions', shellQuote(opts.prompt));
|
|
138
264
|
const claudeCmd = claudeParts.join(' ');
|
|
139
265
|
const fullCmd = wrapperCmd(claudeCmd, opts.jobId);
|
|
140
|
-
const
|
|
141
|
-
const placement =
|
|
266
|
+
const targetWindow = findWindowWithSpace(opts.maxPanesPerWindow);
|
|
267
|
+
const placement = targetWindow === null ? 'new-window' : 'split-window';
|
|
142
268
|
const tmuxArgs = [placement];
|
|
143
|
-
if (
|
|
144
|
-
tmuxArgs.push('-h');
|
|
269
|
+
if (placement === 'split-window') {
|
|
270
|
+
tmuxArgs.push('-h', '-t', targetWindow);
|
|
271
|
+
}
|
|
145
272
|
tmuxArgs.push('-P', '-F', '#{pane_id}', '-c', opts.cwd, '-e', `CRTR_JOB_ID=${opts.jobId}`, fullCmd);
|
|
146
273
|
const split = spawnSync('tmux', tmuxArgs, { encoding: 'utf8' });
|
|
147
274
|
if (split.status !== 0) {
|
|
@@ -150,6 +277,12 @@ export function spawnAgent(opts) {
|
|
|
150
277
|
return { status: 'spawn-failed', message: msg };
|
|
151
278
|
}
|
|
152
279
|
const paneId = split.stdout.trim();
|
|
280
|
+
// Re-balance the target window's panes evenly so the new pane doesn't end up
|
|
281
|
+
// half the size of its siblings. -t <pane_id> resolves to the window it lives
|
|
282
|
+
// in for both placements (split + new-window).
|
|
283
|
+
spawnSync('tmux', ['select-layout', '-t', paneId, 'even-horizontal'], {
|
|
284
|
+
encoding: 'utf8',
|
|
285
|
+
});
|
|
153
286
|
return {
|
|
154
287
|
status: 'spawned',
|
|
155
288
|
paneId,
|
package/dist/prompts/agent.d.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* First user message for a spec → plan handoff.
|
|
3
3
|
*
|
|
4
4
|
* Thin prompt: the worker discovers the full planning workflow by running
|
|
5
|
-
* `crtr
|
|
6
|
-
* embedding the planPrompt() blob here and keeps the prompt in sync
|
|
7
|
-
* live CLI without any coupling.
|
|
5
|
+
* `crtr agent plan new -h`, then saves the plan via `crtr agent plan new`. This
|
|
6
|
+
* avoids embedding the planPrompt() blob here and keeps the prompt in sync
|
|
7
|
+
* with the live CLI without any coupling.
|
|
8
8
|
*/
|
|
9
9
|
export declare function planHandoffPrompt(specPath: string, jobId: string): string;
|
|
10
10
|
/**
|
package/dist/prompts/agent.js
CHANGED
|
@@ -3,30 +3,30 @@ import { planReviewPrompt, specReviewPrompt } from './review.js';
|
|
|
3
3
|
* First user message for a spec → plan handoff.
|
|
4
4
|
*
|
|
5
5
|
* Thin prompt: the worker discovers the full planning workflow by running
|
|
6
|
-
* `crtr
|
|
7
|
-
* embedding the planPrompt() blob here and keeps the prompt in sync
|
|
8
|
-
* live CLI without any coupling.
|
|
6
|
+
* `crtr agent plan new -h`, then saves the plan via `crtr agent plan new`. This
|
|
7
|
+
* avoids embedding the planPrompt() blob here and keeps the prompt in sync
|
|
8
|
+
* with the live CLI without any coupling.
|
|
9
9
|
*/
|
|
10
10
|
export function planHandoffPrompt(specPath, jobId) {
|
|
11
11
|
return `You were launched in a new tmux pane to turn an approved spec into a plan.
|
|
12
12
|
|
|
13
13
|
**Spec:** ${specPath}
|
|
14
14
|
|
|
15
|
-
1. Run \`crtr
|
|
15
|
+
1. Run \`crtr agent plan new -h\` to load the planning workflow and output schema.
|
|
16
16
|
2. Read the spec end-to-end.
|
|
17
|
-
3. Follow the workflow from step 1 and save the plan by passing the plan markdown to \`crtr
|
|
18
|
-
4. When done, submit
|
|
17
|
+
3. Follow the workflow from step 1 and save the plan by passing the plan markdown to \`crtr agent plan new\` on stdin.
|
|
18
|
+
4. When done, submit a short markdown report on stdin:
|
|
19
19
|
|
|
20
20
|
\`\`\`bash
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
crtr job submit ${jobId} <<'MD'
|
|
22
|
+
Plan saved at <path>. <one-line summary of the plan's shape>.
|
|
23
|
+
MD
|
|
23
24
|
\`\`\`
|
|
24
25
|
|
|
25
|
-
If you cannot complete the plan,
|
|
26
|
+
If you cannot complete the plan, submit a failure with a reason:
|
|
26
27
|
|
|
27
28
|
\`\`\`bash
|
|
28
|
-
|
|
29
|
-
crtr job submit ${jobId} --context-file /tmp/crtr-result-${jobId}.json
|
|
29
|
+
crtr job submit ${jobId} --status failed --reason "<why>"
|
|
30
30
|
\`\`\`
|
|
31
31
|
|
|
32
32
|
Begin now.`;
|
|
@@ -111,17 +111,19 @@ would be slower.
|
|
|
111
111
|
|
|
112
112
|
## Phase 6: Report and submit
|
|
113
113
|
|
|
114
|
-
When all tasks complete and verification passes, submit
|
|
114
|
+
When all tasks complete and verification passes, submit a markdown report on stdin:
|
|
115
115
|
|
|
116
116
|
\`\`\`bash
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
crtr job submit ${jobId} <<'MD'
|
|
118
|
+
**Summary:** <one-line summary of files touched>
|
|
119
|
+
|
|
120
|
+
<optional further notes — verification output, surprises, callouts>
|
|
121
|
+
MD
|
|
119
122
|
\`\`\`
|
|
120
123
|
|
|
121
|
-
If implementation fails,
|
|
124
|
+
If implementation fails, submit a failure with a reason:
|
|
122
125
|
\`\`\`bash
|
|
123
|
-
|
|
124
|
-
crtr job submit ${jobId} --context-file /tmp/crtr-result-${jobId}.json
|
|
126
|
+
crtr job submit ${jobId} --status failed --reason "<why>"
|
|
125
127
|
\`\`\`
|
|
126
128
|
|
|
127
129
|
## Guardrails (apply to you AND your subagents)
|
|
@@ -144,7 +146,7 @@ export function reviewerHandoffPrompt(artifactPath, artifactKind, specPath, jobI
|
|
|
144
146
|
const reviewBody = artifactKind === 'spec'
|
|
145
147
|
? specReviewPrompt(artifactPath)
|
|
146
148
|
: planReviewPrompt(artifactPath, specPath);
|
|
147
|
-
const patched = reviewBody.replace('__CRTR_SUBMIT_INSTRUCTION__', `the submit command injected below. The \`--kill-pane\` flag closes this reviewer pane after submission — keep it, do not drop it.\n\n\`\`\`bash\
|
|
149
|
+
const patched = reviewBody.replace('__CRTR_SUBMIT_INSTRUCTION__', `the submit command injected below. Pipe your full review markdown on stdin. The \`--kill-pane\` flag closes this reviewer pane after submission — keep it, do not drop it.\n\n\`\`\`bash\ncrtr job submit ${jobId} --kill-pane <<'MD'\n<your full review markdown — the same Status/Issues/Recommendations block you composed above>\nMD\n\`\`\`\n\nIf the artifact is too malformed to review, submit a failure instead:\n\n\`\`\`bash\ncrtr job submit ${jobId} --status failed --reason "<why>" --kill-pane\n\`\`\``);
|
|
148
150
|
return `${patched}
|
|
149
151
|
|
|
150
152
|
After calling \`crtr job submit\`, your turn ends and the pane closes itself. Do NOT chat or summarize after submission.`;
|
package/dist/prompts/debug.js
CHANGED
|
@@ -19,18 +19,25 @@ Rules — follow exactly:
|
|
|
19
19
|
|
|
20
20
|
Submit exactly one of the following via \`crtr job submit\`, then your turn ends — do not chat:
|
|
21
21
|
|
|
22
|
-
Reproduced:
|
|
22
|
+
Reproduced (markdown body on stdin):
|
|
23
23
|
\`\`\`bash
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
crtr job submit ${jobId} <<'MD'
|
|
25
|
+
**Reproduces:** yes
|
|
26
|
+
|
|
27
|
+
**Test path:** <path>
|
|
28
|
+
|
|
29
|
+
**Test command:** \`<exact cmd>\`
|
|
30
|
+
|
|
31
|
+
**Failure output:**
|
|
32
|
+
\`\`\`
|
|
33
|
+
<pasted failing output>
|
|
34
|
+
\`\`\`
|
|
35
|
+
MD
|
|
28
36
|
\`\`\`
|
|
29
37
|
|
|
30
38
|
Gave up (no faithful repro achievable):
|
|
31
39
|
\`\`\`bash
|
|
32
|
-
|
|
33
|
-
crtr job submit ${jobId} --context-file /tmp/crtr-result-${jobId}.json
|
|
40
|
+
crtr job submit ${jobId} --status failed --reason "<why a faithful repro was not achievable>"
|
|
34
41
|
\`\`\`
|
|
35
42
|
|
|
36
43
|
Begin now.`;
|
package/dist/prompts/skill.js
CHANGED
|
@@ -111,7 +111,7 @@ are for large, complicated, or unintuitive systems only.
|
|
|
111
111
|
## 2. Scope + name
|
|
112
112
|
|
|
113
113
|
- **Scope**: \`project\` by default. \`user\` only if cross-repo.
|
|
114
|
-
- **Name**: kebab-case. Confirm no collision: \`crtr skill read
|
|
114
|
+
- **Name**: kebab-case. Confirm no collision: \`crtr skill read <name> --no-body\` (returns \`.path\`, \`.scope\`, \`.plugin\`).
|
|
115
115
|
|
|
116
116
|
## 3. Parallel exploration
|
|
117
117
|
|
|
@@ -178,7 +178,7 @@ Non-obvious coupling. Looks-broken-but-isn't. Past footguns.
|
|
|
178
178
|
\`\`\`
|
|
179
179
|
|
|
180
180
|
**No \`## Related\` for within-plugin siblings** — the CLI auto-appends a
|
|
181
|
-
\`## Neighbors\` section on \`crtr skill read
|
|
181
|
+
\`## Neighbors\` section on \`crtr skill read <name>\`. Add a manual \`## Related\`
|
|
182
182
|
only for cross-plugin or distant refs.
|
|
183
183
|
|
|
184
184
|
**Density rules:**
|
|
@@ -195,8 +195,8 @@ only for cross-plugin or distant refs.
|
|
|
195
195
|
Output is JSON; \`.content\` has the body, \`.path\` has the location:
|
|
196
196
|
|
|
197
197
|
\`\`\`
|
|
198
|
-
crtr skill read
|
|
199
|
-
crtr skill read
|
|
198
|
+
crtr skill read <name> --no-body
|
|
199
|
+
crtr skill read <name>
|
|
200
200
|
crtr skill find search "<keyword>" # confirm description triggers discovery
|
|
201
201
|
\`\`\`
|
|
202
202
|
|
|
@@ -246,7 +246,7 @@ PR over many small ones for refactors here, because review churn dominates"*
|
|
|
246
246
|
|
|
247
247
|
- **Scope**: \`user\` for cross-project methodology. \`project\` for repo-specific.
|
|
248
248
|
- **Name**: kebab-case, verb-or-noun-phrase. Not "guide-to-X".
|
|
249
|
-
- Check \`crtr skill read
|
|
249
|
+
- Check \`crtr skill read <name> --no-body\` (returns \`.path\`, \`.scope\`, \`.plugin\`).
|
|
250
250
|
|
|
251
251
|
## 3. Scaffold
|
|
252
252
|
|
|
@@ -297,7 +297,7 @@ to read the whole thing for value, you've buried the judgment.
|
|
|
297
297
|
\`\`\`
|
|
298
298
|
|
|
299
299
|
**No \`## Related\` for within-plugin siblings** — the CLI auto-appends a
|
|
300
|
-
\`## Neighbors\` section on \`crtr skill read
|
|
300
|
+
\`## Neighbors\` section on \`crtr skill read <name>\`. Add a manual \`## Related\`
|
|
301
301
|
only for cross-plugin or distant refs.
|
|
302
302
|
|
|
303
303
|
## 6. Progressive disclosure
|
|
@@ -319,8 +319,8 @@ loads supporting files only when needed.
|
|
|
319
319
|
Output is JSON; \`.content\` has the body, \`.path\` has the location:
|
|
320
320
|
|
|
321
321
|
\`\`\`
|
|
322
|
-
crtr skill read
|
|
323
|
-
crtr skill read
|
|
322
|
+
crtr skill read <name> --no-body
|
|
323
|
+
crtr skill read <name>
|
|
324
324
|
crtr skill find search "<keyword>"
|
|
325
325
|
\`\`\`
|
|
326
326
|
|
|
@@ -410,8 +410,8 @@ Or invent your own. Stay tight — no padding.
|
|
|
410
410
|
Output is JSON; \`.content\` has the body, \`.path\` has the location:
|
|
411
411
|
|
|
412
412
|
\`\`\`
|
|
413
|
-
crtr skill read
|
|
414
|
-
crtr skill read
|
|
413
|
+
crtr skill read <name> --no-body
|
|
414
|
+
crtr skill read <name>
|
|
415
415
|
crtr skill find search "<keyword>"
|
|
416
416
|
\`\`\`
|
|
417
417
|
|
|
@@ -464,7 +464,7 @@ field/flag/code values** — pull verbatim from source.
|
|
|
464
464
|
|
|
465
465
|
- **Scope**: \`user\` for cross-project facts. \`project\` for repo-specific.
|
|
466
466
|
- **Name**: noun-phrase. \`http-status-codes\` not \`learn-http-status\`.
|
|
467
|
-
- Check \`crtr skill read
|
|
467
|
+
- Check \`crtr skill read <name> --no-body\` (returns \`.path\`, \`.scope\`, \`.plugin\`).
|
|
468
468
|
|
|
469
469
|
## 3. Scaffold
|
|
470
470
|
|
|
@@ -525,8 +525,8 @@ SKILL.md links to siblings (\`see [full-table.md](full-table.md)\`).
|
|
|
525
525
|
Output is JSON; \`.content\` has the body, \`.path\` has the location:
|
|
526
526
|
|
|
527
527
|
\`\`\`
|
|
528
|
-
crtr skill read
|
|
529
|
-
crtr skill read
|
|
528
|
+
crtr skill read <name> --no-body
|
|
529
|
+
crtr skill read <name>
|
|
530
530
|
crtr skill find search "<keyword>"
|
|
531
531
|
\`\`\`
|
|
532
532
|
|
|
@@ -577,7 +577,7 @@ push to \\\`main\\\`, wait for green CI, click promote"* is a runbook step.
|
|
|
577
577
|
|
|
578
578
|
- **Scope**: \`project\` for repo-specific procedures. \`user\` for cross-project.
|
|
579
579
|
- **Name**: verb-phrase. \`deploy-to-prod\` not \`production-deployment-guide\`.
|
|
580
|
-
- Check \`crtr skill read
|
|
580
|
+
- Check \`crtr skill read <name> --no-body\` (returns \`.path\`, \`.scope\`, \`.plugin\`).
|
|
581
581
|
|
|
582
582
|
## 3. Scaffold
|
|
583
583
|
|
|
@@ -632,8 +632,8 @@ crtr skill author scaffold <name> --type runbook --scope <user|project> --descri
|
|
|
632
632
|
Output is JSON; \`.content\` has the body, \`.path\` has the location:
|
|
633
633
|
|
|
634
634
|
\`\`\`
|
|
635
|
-
crtr skill read
|
|
636
|
-
crtr skill read
|
|
635
|
+
crtr skill read <name> --no-body
|
|
636
|
+
crtr skill read <name>
|
|
637
637
|
crtr skill find search "<keyword>"
|
|
638
638
|
\`\`\`
|
|
639
639
|
|
package/dist/types.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare const ExitCode: {
|
|
|
8
8
|
readonly NETWORK: 5;
|
|
9
9
|
};
|
|
10
10
|
export type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode];
|
|
11
|
-
export declare const SCHEMA_VERSION =
|
|
11
|
+
export declare const SCHEMA_VERSION = 2;
|
|
12
12
|
export interface OwnerRef {
|
|
13
13
|
name?: string;
|
|
14
14
|
email?: string;
|
package/dist/types.js
CHANGED
|
@@ -6,7 +6,7 @@ export const ExitCode = {
|
|
|
6
6
|
AMBIGUOUS: 4,
|
|
7
7
|
NETWORK: 5,
|
|
8
8
|
};
|
|
9
|
-
export const SCHEMA_VERSION =
|
|
9
|
+
export const SCHEMA_VERSION = 2;
|
|
10
10
|
export const SKILL_TYPES = ['playbook', 'primer', 'reference', 'runbook', 'freeform'];
|
|
11
11
|
export function isSkillType(v) {
|
|
12
12
|
return typeof v === 'string' && SKILL_TYPES.includes(v);
|
|
@@ -36,7 +36,7 @@ export function defaultScopeConfig() {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
export function skillConfigKey(plugin, name) {
|
|
39
|
-
return `${plugin}
|
|
39
|
+
return `${plugin}/${name}`;
|
|
40
40
|
}
|
|
41
41
|
export function defaultScopeState() {
|
|
42
42
|
return { marketplaces: {}, plugins: {} };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crouton-kit/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "crtr — fast access to skills, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@crouton-kit/humanloop": "^0.3.
|
|
38
|
+
"@crouton-kit/humanloop": "^0.3.11",
|
|
39
39
|
"commander": "^13.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
package/dist/commands/flow.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// `crtr flow` umbrella — groups the spec → plan → debug development process.
|
|
2
|
-
// registerSpec/registerPlan are unchanged (each still defineBranch{name}); they
|
|
3
|
-
// nest under `flow` here instead of registering at root. registerDebug is a
|
|
4
|
-
// leaf — `crtr flow debug` spawns directly and `-h` prints FLOW_DEBUG_GUIDE.
|
|
5
|
-
import { defineBranch } from '../core/command.js';
|
|
6
|
-
import { registerSpec } from './spec.js';
|
|
7
|
-
import { registerPlan } from './plan.js';
|
|
8
|
-
import { registerDebug } from './debug.js';
|
|
9
|
-
export function registerFlow() {
|
|
10
|
-
return defineBranch({
|
|
11
|
-
name: 'flow',
|
|
12
|
-
help: {
|
|
13
|
-
name: 'flow',
|
|
14
|
-
summary: 'the spec → plan → debug development process',
|
|
15
|
-
model: 'spec captures requirements; plan decomposes them; debug root-causes failures reproduce-first.',
|
|
16
|
-
children: [
|
|
17
|
-
{ name: 'spec', desc: 'create, read, list specifications', useWhen: 'capturing requirements before planning' },
|
|
18
|
-
{ name: 'plan', desc: 'create, read, list plans', useWhen: 'shaping or inspecting work' },
|
|
19
|
-
{ name: 'debug', desc: 'reproduce-first root-cause workflow', useWhen: 'a bug, test failure, or unexpected behavior needs root-causing' },
|
|
20
|
-
],
|
|
21
|
-
},
|
|
22
|
-
children: [registerSpec(), registerPlan(), registerDebug()],
|
|
23
|
-
});
|
|
24
|
-
}
|