@crouton-kit/crouter 0.3.43 → 0.3.45

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.
Files changed (41) hide show
  1. package/dist/builtin-memory/05-kinds/advisor/00-base.md +6 -0
  2. package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +26 -0
  3. package/dist/builtin-memory/05-kinds/design/00-base.md +8 -1
  4. package/dist/builtin-memory/05-kinds/developer/00-base.md +7 -0
  5. package/dist/builtin-memory/05-kinds/explore/00-base.md +7 -0
  6. package/dist/builtin-memory/05-kinds/general/00-base.md +5 -1
  7. package/dist/builtin-memory/05-kinds/plan/00-base.md +8 -1
  8. package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +8 -4
  9. package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +5 -1
  10. package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +6 -0
  11. package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +5 -1
  12. package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +6 -1
  13. package/dist/builtin-memory/05-kinds/product/00-base.md +4 -0
  14. package/dist/builtin-memory/05-kinds/review/00-base.md +6 -0
  15. package/dist/builtin-memory/05-kinds/spec/00-base.md +3 -0
  16. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/memory-slash-commands.test.ts +184 -0
  17. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +166 -0
  18. package/dist/clients/attach/attach-cmd.js +154 -154
  19. package/dist/commands/memory/delete.d.ts +1 -0
  20. package/dist/commands/memory/delete.js +77 -0
  21. package/dist/commands/memory/lint.js +10 -0
  22. package/dist/commands/memory/shared.js +1 -0
  23. package/dist/commands/memory/write.js +7 -1
  24. package/dist/commands/memory.js +3 -2
  25. package/dist/commands/node.js +46 -11
  26. package/dist/core/__tests__/review-model-floor.test.js +16 -0
  27. package/dist/core/memory-resolver.d.ts +6 -0
  28. package/dist/core/memory-resolver.js +1 -1
  29. package/dist/core/runtime/broker.js +13 -5
  30. package/dist/core/runtime/launch.d.ts +1 -0
  31. package/dist/core/runtime/launch.js +4 -3
  32. package/dist/core/runtime/spawn.d.ts +6 -3
  33. package/dist/core/runtime/stop-guard.js +19 -0
  34. package/dist/core/runtime/structured-output.d.ts +12 -0
  35. package/dist/core/runtime/structured-output.js +90 -0
  36. package/dist/core/substrate/schema.d.ts +15 -0
  37. package/dist/core/substrate/schema.js +9 -0
  38. package/dist/pi-extensions/canvas-structured-output.d.ts +9 -0
  39. package/dist/pi-extensions/canvas-structured-output.js +150 -0
  40. package/dist/prompts/review.js +14 -0
  41. package/package.json +1 -1
@@ -58,6 +58,21 @@ export interface SubstrateSchema {
58
58
  * than the node subject. Absent ⇒ no frontmatter trigger. Frontmatter key
59
59
  * `read-when`. */
60
60
  readWhen?: GatePredicate;
61
+ /** Opt-in: this doc is invocable as a pi slash command (`/<name>`, `/` in a
62
+ * nested name rendered as `:`). Default `false` — most docs are consulted,
63
+ * not invoked. Frontmatter key `slash`. */
64
+ slash: boolean;
65
+ /** The gap this doc exists to close — the observed agent failure that
66
+ * prompted it (CTO ruling 2026-07-03, `taste/document-substrate` §"Rationale
67
+ * is a frontmatter field, never delivered"). MAINTAINER-FACING ONLY: excluded
68
+ * from every delivered surface by construction — it is frontmatter, never
69
+ * body, so it never reaches a boot render, on-read injection, or `memory
70
+ * read` content output; only the raw file at `path` (or `memory read
71
+ * --frontmatter`) shows it. It is NOT read-routing (`whenAndWhyToRead` owns
72
+ * that) and NOT a content summary (`shortForm` owns that) — it answers
73
+ * "what observed failure made this doc exist," never "why obey it." Absent
74
+ * when the doc carries none. Frontmatter key `rationale`. */
75
+ rationale?: string;
61
76
  }
62
77
  /** A fully-resolved substrate document: the parsed schema PLUS the resolver's
63
78
  * path-derived identity and body. This single object flows through the whole
@@ -94,6 +94,8 @@ export function parseSubstrateFrontmatter(fm) {
94
94
  gate: parseGate(fm.gate),
95
95
  appliesTo: parseAppliesTo(fm['applies-to']),
96
96
  readWhen: parseGate(fm['read-when']),
97
+ slash: fm.slash === true,
98
+ rationale: parseRationale(fm['rationale']),
97
99
  };
98
100
  }
99
101
  /** Parse a resolved MemoryDoc into a fully-typed SubstrateDoc (schema + the
@@ -121,6 +123,13 @@ export function previewLine(doc) {
121
123
  function strField(v) {
122
124
  return typeof v === 'string' ? v : '';
123
125
  }
126
+ /** Resolve an optional `rationale` field: a non-empty string is carried as-is
127
+ * (multi-line YAML block scalars parse to a plain string, so no special
128
+ * handling is needed); anything else (absent, blank, non-string) yields
129
+ * `undefined` — the doc simply carries no rationale. */
130
+ function parseRationale(v) {
131
+ return typeof v === 'string' && v.trim() !== '' ? v : undefined;
132
+ }
124
133
  /** Resolve a visibility field to a ladder rung, falling back to the neutral
125
134
  * floor when absent/invalid. */
126
135
  function parseRung(v, fallback) {
@@ -0,0 +1,9 @@
1
+ type PiEvents = 'before_agent_start' | 'agent_start';
2
+ interface PiLike {
3
+ on: (event: PiEvents, handler: (event: any, ctx: any) => void | Promise<void>) => void;
4
+ registerTool: (definition: Record<string, unknown>) => void;
5
+ getActiveTools?: () => string[];
6
+ setActiveTools?: (tools: string[]) => void;
7
+ }
8
+ export declare function registerCanvasStructuredOutput(pi: PiLike): void;
9
+ export default registerCanvasStructuredOutput;
@@ -0,0 +1,150 @@
1
+ // canvas-structured-output.ts — dynamic `submit` tool for schema-shaped node results.
2
+ //
3
+ // Loaded into every canvas node. Inert unless nodes/<id>/output-schema.json exists.
4
+ import { mkdirSync, writeFileSync } from 'node:fs';
5
+ import { dirname } from 'node:path';
6
+ import { getNode } from '../core/canvas/index.js';
7
+ import { pushFinal, pushUpdate } from '../core/feed/feed.js';
8
+ import { outputResultPath, readOutputRequest, removeOutputSchema, } from '../core/runtime/structured-output.js';
9
+ function reportBody(resultPath, json) {
10
+ return `Structured output submitted.\n\nResult path: ${resultPath}\n\n\`\`\`json\n${json}\n\`\`\`\n`;
11
+ }
12
+ function activateTool(pi) {
13
+ try {
14
+ const current = pi.getActiveTools?.() ?? [];
15
+ if (!current.includes('submit'))
16
+ pi.setActiveTools?.([...current, 'submit']);
17
+ }
18
+ catch {
19
+ /* best-effort; registration refresh normally activates new extension tools */
20
+ }
21
+ }
22
+ function retireTool(pi) {
23
+ try {
24
+ const current = pi.getActiveTools?.() ?? [];
25
+ pi.setActiveTools?.(current.filter((name) => name !== 'submit'));
26
+ }
27
+ catch {
28
+ /* best-effort; the execute handler is inert when no schema file exists */
29
+ }
30
+ }
31
+ function toolDescription(request) {
32
+ if (request.mode === 'terminal') {
33
+ return 'Submit your final structured result. Calling this ends your run: the result is recorded and a final report is pushed automatically — do NOT call `crtr push final` yourself.';
34
+ }
35
+ return 'Submit the requested one-off structured result. Calling this records the result, pushes an update report automatically, frees you from the structured-output gate, and lets you continue working.';
36
+ }
37
+ export function registerCanvasStructuredOutput(pi) {
38
+ const nodeId = process.env['CRTR_NODE_ID'];
39
+ if (nodeId === undefined || nodeId.trim() === '')
40
+ return;
41
+ let registeredKey = null;
42
+ let active = false;
43
+ const ensureSubmitTool = (activate) => {
44
+ const request = readOutputRequest(nodeId);
45
+ if (request === null) {
46
+ if (active) {
47
+ active = false;
48
+ retireTool(pi);
49
+ }
50
+ return;
51
+ }
52
+ const key = JSON.stringify(request);
53
+ if (registeredKey !== key) {
54
+ pi.registerTool({
55
+ name: 'submit',
56
+ label: 'Submit',
57
+ description: toolDescription(request),
58
+ promptSnippet: request.mode === 'terminal'
59
+ ? 'Submit the final structured result and end this node'
60
+ : 'Submit the requested one-off structured result',
61
+ promptGuidelines: request.mode === 'terminal'
62
+ ? [
63
+ 'Use submit as your final action when the required structured result is ready.',
64
+ 'After calling submit, do not call crtr push final yourself and do not emit another assistant response.',
65
+ ]
66
+ : [
67
+ 'Use submit to answer the pending one-off structured-output request before stopping or going dormant.',
68
+ 'After one-off submit succeeds, continue with any remaining work normally.',
69
+ ],
70
+ parameters: request.schema,
71
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
72
+ const current = readOutputRequest(nodeId);
73
+ if (current === null) {
74
+ return {
75
+ content: [{ type: 'text', text: 'No structured output is currently requested.' }],
76
+ details: { submitted: false, reason: 'no_request' },
77
+ };
78
+ }
79
+ if (JSON.stringify(current) !== key) {
80
+ registeredKey = null;
81
+ ensureSubmitTool(true);
82
+ return {
83
+ content: [{ type: 'text', text: 'The structured-output request changed while you were working. Call submit again using the current schema.' }],
84
+ details: { submitted: false, reason: 'schema_replaced' },
85
+ };
86
+ }
87
+ const resultPath = outputResultPath(nodeId);
88
+ const json = JSON.stringify(params, null, 2);
89
+ mkdirSync(dirname(resultPath), { recursive: true });
90
+ writeFileSync(resultPath, `${json}\n`, 'utf8');
91
+ const body = reportBody(resultPath, json);
92
+ if (current.mode === 'terminal') {
93
+ try {
94
+ await pushFinal(nodeId, body);
95
+ }
96
+ catch (err) {
97
+ const status = getNode(nodeId)?.status;
98
+ const alreadyFinalized = status === 'done' || status === 'dead' || status === 'canceled' ||
99
+ (err instanceof Error && err.message.startsWith("illegal lifecycle transition: 'finalize' from status='"));
100
+ if (!alreadyFinalized)
101
+ throw err;
102
+ }
103
+ removeOutputSchema(nodeId);
104
+ active = false;
105
+ retireTool(pi);
106
+ try {
107
+ ctx?.shutdown?.();
108
+ }
109
+ catch { /* ignore */ }
110
+ return {
111
+ content: [{ type: 'text', text: `Structured output submitted and finalized: ${resultPath}` }],
112
+ details: { submitted: true, mode: current.mode, resultPath, result: params },
113
+ terminate: true,
114
+ };
115
+ }
116
+ await pushUpdate(nodeId, body);
117
+ removeOutputSchema(nodeId);
118
+ active = false;
119
+ retireTool(pi);
120
+ return {
121
+ content: [{ type: 'text', text: `Structured output submitted: ${resultPath}` }],
122
+ details: { submitted: true, mode: current.mode, resultPath, result: params },
123
+ };
124
+ },
125
+ });
126
+ registeredKey = key;
127
+ }
128
+ if (activate && !active) {
129
+ active = true;
130
+ activateTool(pi);
131
+ }
132
+ };
133
+ try {
134
+ ensureSubmitTool(false);
135
+ }
136
+ catch { /* malformed files are enforced by the stop-guard */ }
137
+ pi.on('before_agent_start', () => {
138
+ try {
139
+ ensureSubmitTool(true);
140
+ }
141
+ catch { /* keep the node alive; stop-guard will reprompt */ }
142
+ });
143
+ pi.on('agent_start', () => {
144
+ try {
145
+ ensureSubmitTool(true);
146
+ }
147
+ catch { /* keep the node alive; stop-guard will reprompt */ }
148
+ });
149
+ }
150
+ export default registerCanvasStructuredOutput;
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Builds the guidance delivered to spec/plan reviewer agents.
3
+ *
4
+ * Why this exists (CTO, 2026-07-03): reviewer agents without calibration
5
+ * drift to rubber-stamping or nitpicking, so the decision tables force a
6
+ * real verdict — but the deeper point is that this reviews AGENT-written
7
+ * work, not generic review. Agents have a specific failure profile: a check
8
+ * earns a place here only when it targets a mistake agents actually make
9
+ * (unrequested features, placeholder sections, noise comments), and a check
10
+ * for something agents reliably get right (e.g. arithmetic/logic
11
+ * correctness) is pure noise. Maintain the checklists empirically: add a
12
+ * check when we observe agents failing at something, cut one that never
13
+ * catches anything.
14
+ */
1
15
  const SUBMIT_INSTRUCTIONS = `## Delivering your review
2
16
 
3
17
  When your review is complete, deliver your verdict as instructed by
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crouton-kit/crouter",
3
- "version": "0.3.43",
3
+ "version": "0.3.45",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",