@crouton-kit/crouter 0.3.19 → 0.3.21
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/builtin-memory/crouter-development/marketplaces.md +2 -4
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +3 -5
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +3 -6
- package/dist/builtin-memory/crouter-development/personas.md +2 -6
- package/dist/builtin-memory/crouter-development/plugins.md +2 -4
- package/dist/builtin-memory/design.md +2 -4
- package/dist/builtin-memory/development.md +3 -3
- package/dist/builtin-memory/planning.md +3 -4
- package/dist/builtin-memory/spec.md +3 -10
- package/dist/builtin-personas/design/PERSONA.md +1 -0
- package/dist/builtin-personas/design/orchestrator.md +1 -0
- package/dist/builtin-personas/developer/PERSONA.md +1 -0
- package/dist/builtin-personas/developer/orchestrator.md +1 -0
- package/dist/builtin-personas/explore/PERSONA.md +1 -0
- package/dist/builtin-personas/explore/orchestrator.md +4 -0
- package/dist/builtin-personas/general/PERSONA.md +1 -0
- package/dist/builtin-personas/general/orchestrator.md +4 -0
- package/dist/builtin-personas/orchestration-kernel.md +1 -1
- package/dist/builtin-personas/plan/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/orchestrator.md +1 -0
- package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -0
- package/dist/builtin-personas/review/PERSONA.md +1 -0
- package/dist/builtin-personas/review/orchestrator.md +4 -0
- package/dist/builtin-personas/spec/PERSONA.md +1 -0
- package/dist/builtin-personas/spec/orchestrator.md +1 -0
- package/dist/commands/human/queue.js +13 -2
- package/dist/commands/memory/__tests__/lint-schema.test.d.ts +1 -0
- package/dist/commands/memory/__tests__/lint-schema.test.js +29 -0
- package/dist/commands/memory/find.js +10 -15
- package/dist/commands/memory/lint.d.ts +7 -0
- package/dist/commands/memory/lint.js +10 -1
- package/dist/commands/memory/shared.js +1 -2
- package/dist/commands/memory/write.js +6 -9
- package/dist/commands/node.js +51 -26
- package/dist/commands/push.js +10 -15
- package/dist/commands/tmux-spread.js +16 -1
- package/dist/commands/view-cycle.js +9 -5
- package/dist/commands/view-run.js +3 -2
- package/dist/core/__tests__/broker-attach-limits.test.d.ts +1 -0
- package/dist/core/__tests__/broker-attach-limits.test.js +157 -0
- package/dist/core/__tests__/broker-attach-stream.test.d.ts +1 -0
- package/dist/core/__tests__/broker-attach-stream.test.js +125 -0
- package/dist/core/__tests__/broker-crash-teardown.test.d.ts +1 -0
- package/dist/core/__tests__/broker-crash-teardown.test.js +116 -0
- package/dist/core/__tests__/broker-dialogs.test.d.ts +1 -0
- package/dist/core/__tests__/broker-dialogs.test.js +126 -0
- package/dist/core/__tests__/broker-dormant-wake.test.d.ts +1 -0
- package/dist/core/__tests__/broker-dormant-wake.test.js +51 -0
- package/dist/core/__tests__/broker-lifecycle.test.js +14 -604
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.d.ts +1 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +102 -0
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +2 -36
- package/dist/core/__tests__/cascade-close.test.js +3 -2
- package/dist/core/__tests__/context-intro.test.js +23 -27
- package/dist/core/__tests__/detach-focus.test.js +2 -2
- package/dist/core/__tests__/helpers/broker-clients.d.ts +43 -0
- package/dist/core/__tests__/helpers/broker-clients.js +178 -0
- package/dist/core/__tests__/live-mutation-verbs.test.d.ts +1 -0
- package/dist/core/__tests__/live-mutation-verbs.test.js +175 -0
- package/dist/core/__tests__/live-mutation.test.js +4 -147
- package/dist/core/__tests__/memory.test.js +4 -4
- package/dist/core/__tests__/subscription-delivery.test.js +3 -3
- package/dist/core/bootstrap.js +18 -14
- package/dist/core/canvas/canvas.js +1 -1
- package/dist/core/canvas/types.d.ts +6 -0
- package/dist/core/help.d.ts +2 -2
- package/dist/core/help.js +1 -1
- package/dist/core/render.d.ts +4 -3
- package/dist/core/render.js +38 -41
- package/dist/core/resolver.js +1 -1
- package/dist/core/runtime/bearings.d.ts +2 -2
- package/dist/core/runtime/bearings.js +2 -2
- package/dist/core/runtime/launch.d.ts +9 -2
- package/dist/core/runtime/launch.js +20 -3
- package/dist/core/runtime/nodes.d.ts +4 -0
- package/dist/core/runtime/nodes.js +1 -0
- package/dist/core/runtime/promote.js +3 -0
- package/dist/core/runtime/reset.js +3 -2
- package/dist/core/runtime/spawn.d.ts +4 -0
- package/dist/core/runtime/spawn.js +2 -1
- package/dist/core/substrate/render.d.ts +1 -2
- package/dist/core/substrate/render.js +9 -12
- package/dist/core/substrate/schema.d.ts +18 -13
- package/dist/core/substrate/schema.js +12 -11
- package/dist/pi-extensions/canvas-context-intro.js +5 -3
- package/package.json +1 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: skill
|
|
3
|
-
when: When a
|
|
4
|
-
|
|
5
|
-
symlink-based install, auto-bump CI, dual-publishing. Use when creating a
|
|
6
|
-
marketplace or contributing plugins to one.
|
|
3
|
+
when-and-why-to-read: When creating a crtr marketplace or contributing plugins
|
|
4
|
+
to one, this skill should be read.
|
|
7
5
|
short-form: How to author a crtr marketplace — marketplace.json index, plugin
|
|
8
6
|
entries, symlink-based install, auto-bump CI, dual-publishing. Use when
|
|
9
7
|
creating a marketplace or contributing plugins to one.
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: skill
|
|
3
|
-
when: When
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
what to put in it, the identity/deliverable/boundary/report shape, and the
|
|
7
|
-
voice to use. Use when writing or revising a <kind>/PERSONA.md.
|
|
3
|
+
when-and-why-to-read: When writing or revising a base persona prompt (a
|
|
4
|
+
<kind>/PERSONA.md, the system prompt for a single-window worker node), this
|
|
5
|
+
skill should be read.
|
|
8
6
|
short-form: How to write a base persona prompt (the mode=base PERSONA.md) — the
|
|
9
7
|
system prompt for a single-window worker node. Covers what a base persona is
|
|
10
8
|
for, what to put in it, the identity/deliverable/boundary/report shape, and
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: skill
|
|
3
|
-
when: When
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
belongs in the per-kind body, naming the child pipeline, the roadmapSkill
|
|
7
|
-
pointer, and the @include rule. Use when writing or revising a
|
|
8
|
-
<kind>/orchestrator.md.
|
|
3
|
+
when-and-why-to-read: When writing or revising an orchestrator persona prompt (a
|
|
4
|
+
<kind>/orchestrator.md, the system prompt for a resident coordinator node),
|
|
5
|
+
this skill should be read.
|
|
9
6
|
short-form: How to write an orchestrator persona prompt (orchestrator.md) — the
|
|
10
7
|
system prompt for a resident coordinator node. Covers the kernel-vs-kind
|
|
11
8
|
split, what belongs in the per-kind body, naming the child pipeline, the
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: skill
|
|
3
|
-
when: When a
|
|
4
|
-
|
|
5
|
-
PERSONA.md/orchestrator files, the frontmatter contract (incl. whenToUse),
|
|
6
|
-
nested sub-personas, scope resolution and overrides, and how to write the
|
|
7
|
-
prose. Use when adding a new `--kind`, overriding a builtin agent, or
|
|
8
|
-
debugging persona resolution.
|
|
3
|
+
when-and-why-to-read: When adding a new `--kind`, overriding a builtin agent, or
|
|
4
|
+
debugging persona resolution, this skill should be read.
|
|
9
5
|
short-form: How to define a custom node kind (persona) for crtr — the
|
|
10
6
|
PERSONA.md/orchestrator files, the frontmatter contract (incl. whenToUse),
|
|
11
7
|
nested sub-personas, scope resolution and overrides, and how to write the
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: skill
|
|
3
|
-
when: When a
|
|
4
|
-
|
|
5
|
-
scopes, install mechanics, versioning. Use when creating a new plugin,
|
|
6
|
-
packaging skills for distribution, or debugging install/resolution.
|
|
3
|
+
when-and-why-to-read: When creating a crtr plugin, packaging skills for
|
|
4
|
+
distribution, or debugging install/resolution, this skill should be read.
|
|
7
5
|
short-form: How to author a crtr plugin — plugin.json manifest, directory
|
|
8
6
|
layout, scopes, install mechanics, versioning. Use when creating a new plugin,
|
|
9
7
|
packaging skills for distribution, or debugging install/resolution.
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: skill
|
|
3
|
-
when: When shaping a design roadmap or producing an
|
|
4
|
-
|
|
5
|
-
top-down vs bottom-up, and how to decompose a large design into composable
|
|
6
|
-
sub-designs.
|
|
3
|
+
when-and-why-to-read: When shaping a design roadmap or producing an
|
|
4
|
+
architecture/interface design, this skill should be read.
|
|
7
5
|
short-form: Use when shaping a design roadmap or producing an
|
|
8
6
|
architecture/interface design — covers what a design deliverable is, the
|
|
9
7
|
design-artifact shape, when to go top-down vs bottom-up, and how to decompose
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: skill
|
|
3
|
-
when: When shaping or reshaping a build roadmap
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
when-and-why-to-read: When shaping or reshaping a build roadmap — choosing a
|
|
4
|
+
development style, selecting a phase skeleton, or setting exit criteria for a
|
|
5
|
+
software goal — this skill should be read.
|
|
6
6
|
short-form: Use when shaping or reshaping a build roadmap — choosing a
|
|
7
7
|
development style, selecting a phase skeleton, or setting exit criteria for a
|
|
8
8
|
software goal.
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: skill
|
|
3
|
-
when: When shaping a planning roadmap, deciding plan structure,
|
|
4
|
-
plan-review specialists before declaring a plan ready
|
|
5
|
-
|
|
6
|
-
out plan-review specialists before declaring a plan ready.
|
|
3
|
+
when-and-why-to-read: When shaping a planning roadmap, deciding plan structure,
|
|
4
|
+
or fanning out plan-review specialists before declaring a plan ready, this
|
|
5
|
+
skill should be read.
|
|
7
6
|
short-form: Use when shaping a planning roadmap, deciding plan structure, or
|
|
8
7
|
fanning out plan-review specialists before declaring a plan ready.
|
|
9
8
|
system-prompt-visibility: name
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: skill
|
|
3
|
-
when: When running a specification effort, shaping a spec
|
|
4
|
-
how to stage design and requirements work
|
|
5
|
-
|
|
6
|
-
node, the isolation principle behind the design/requirements split, and what a
|
|
7
|
-
finished spec contains
|
|
8
|
-
why: Use when running a specification effort, shaping a spec roadmap, or
|
|
9
|
-
deciding how to stage design and requirements work. Covers the three-stage
|
|
10
|
-
shape→design→requirements methodology, when to delegate design to a child
|
|
11
|
-
node, the isolation principle behind the design/requirements split, and what a
|
|
12
|
-
finished spec contains.
|
|
3
|
+
when-and-why-to-read: When running a specification effort, shaping a spec
|
|
4
|
+
roadmap, or deciding how to stage design and requirements work, this skill
|
|
5
|
+
should be read.
|
|
13
6
|
short-form: Use when running a specification effort, shaping a spec roadmap, or
|
|
14
7
|
deciding how to stage design and requirements work. Covers the three-stage
|
|
15
8
|
shape→design→requirements methodology, when to delegate design to a child
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: Architect a solution — produce one design document an implementer can build from without re-deciding anything left open.
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a design agent. Given a bounded design task — a component, subsystem, or interaction surface — you produce one design document an implementer can build from without re-deciding anything you left open. That, not emitting a document, is the bar for done.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
roadmapSkill: design
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a **design orchestrator** — you own a design effort too large for one agent and deliver one coherent design by decomposing it, delegating each sub-design to a `design` child, and integrating what returns into a unified artifact.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: Implement a change — make the feature or fix genuinely work against its acceptance criteria, not merely compile.
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are an implementation agent. Your job is to **implement this feature or change** so the goal it serves is genuinely met — not to emit a diff that compiles and stop.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
roadmapSkill: development
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a **developer orchestrator** — a senior engineer who owns a feature-sized goal and delivers it by driving specialist children, never by writing the code yourself. Your children are `explore` (to map), `spec` (to specify), `plan` (to decompose), `developer` (to implement), and `review` (to validate). Keep them pointed at the right work with the right context, integrate what they return, and advance the goal phase by phase until it is genuinely done.
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
model: haiku
|
|
3
|
+
---
|
|
4
|
+
|
|
1
5
|
You are an **exploration orchestrator** — you own a research question too large for one window, and you answer it by fanning out scouts and synthesising what they find. You do not read the whole codebase yourself; that is exactly the context exhaustion you exist to avoid.
|
|
2
6
|
|
|
3
7
|
Decompose the surface — by subsystem, directory, layer, or sub-question — into areas small enough for one `explore` scout to map well, and delegate each a sharp, self-contained question. Then integrate what they return into one coherent answer: the architecture, the call paths, where things live, with the `file:line` evidence preserved. The question is answered only when every sub-question is — a gap a scout left open is a gap you fill with another scout, not a guess. Reconcile contradictions the same way: when two scouts disagree, spawn a follow-up to settle it rather than picking the answer you like. Your deliverable is the synthesis, not a pile of child transcripts.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: Anything else — the catch-all worker for a task that does not fit a specialist kind.
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a general-purpose worker — the catch-all for work that doesn't fit a specialist kind. Your job is to complete whatever task is handed to you, and "done" means the **goal actually met**, whatever it was, not an artifact emitted in its direction.
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
model: opus
|
|
3
|
+
---
|
|
4
|
+
|
|
1
5
|
You are a **general orchestrator** — the manager for goals that don't belong to a single specialty. You have no lens of your own; your entire edge is decomposition and routing — reading a goal, breaking it into units, and sending each to the most specific kind that fits.
|
|
2
6
|
|
|
3
7
|
That routing is the discipline. When a whole goal is squarely a build, a research sweep, a spec, or a review, you are not its best owner — hand it to that specialist (created as an orchestrator if it's large) and let their completion expertise carry it. You keep the goals that are genuinely mixed or hard to classify, and you guarantee them done by making sure each routed unit lands with a kind that owns its outcome — never by quietly grinding a specialist's work yourself because routing it felt like overhead.
|
|
@@ -52,7 +52,7 @@ Separate from the roadmap (your live plan and state) you have a persistent docum
|
|
|
52
52
|
|
|
53
53
|
**Reading.** At boot, skills and preferences surface in your system prompt automatically (`## Skills`, `## Preferences`). References surface in your `<crtr-context>` block (`## References`). To browse the full inventory: `crtr memory list`. To search by topic: `crtr memory find <query>`. To load a document by name: `crtr memory read <name>`.
|
|
54
54
|
|
|
55
|
-
**Writing.** Use `crtr memory write` to create or update a document. Every document carries `kind
|
|
55
|
+
**Writing.** Use `crtr memory write` to create or update a document. Every document carries `kind` and `when-and-why-to-read` in its frontmatter, plus a body. `when-and-why-to-read` is ONE read-routing sentence — "When <circumstance>, this <kind> should be read <because <payoff>>." — that tells a future reader when to open the doc and why the read is worth it; it is read-routing, never a justification of the content. It becomes the preview line verbatim. The `kind` governs which section it surfaces in at boot and how it loads:
|
|
56
56
|
|
|
57
57
|
- `skill` — a workflow or methodology to adopt. Surfaces by name in `## Skills`; load with `crtr memory read`.
|
|
58
58
|
- `preference` — how you should work. Surfaces as a `###` routing line in `## Preferences` at boot (default `system-prompt-visibility: preview`).
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: Break work into steps — turn a spec or design into a concrete, phased, parallelizable plan with every decision resolved.
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a planning agent. Given a spec, design, or requirement, you produce a concrete, navigable plan an implementer builds from without guessing — every decision resolved, not a document that defers the hard calls to the build. A plan that is 80% right costs more than no plan, because agents build the wrong thing confidently.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
roadmapSkill: planning
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a **plan orchestrator** — you own a planning effort end-to-end and deliver one coherent, implementation-ready plan. Planning is the sharpest test of owning a goal: a plan's flaws are invisible until implementation makes them expensive, so a flaw you resolve here is orders of magnitude cheaper than the same flaw caught in the diff. You both write plans directly and decompose large ones; read `crtr memory read planning` for the decomposition thresholds, plan shapes, task templates, and exit-criteria patterns before you shape the roadmap.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: proposed files/modules/abstractions fit the system's existing decomposition; flags new units that duplicate existing ones or cross layer boundaries
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are an **architecture-fit reviewer**. Given a plan, verify that the files, modules, and abstractions it proposes fit the system's existing decomposition rather than cutting across it.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: nullability mismatches, type conflicts across parts, hidden N+1s, over-fetching, missing error boundaries, leaky abstractions; owns file-level conflicts between parts
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a **code-smells / design reviewer**. Given a plan, find the design flaws that would ship if it were implemented as written — before any code makes them expensive.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: the plan honors the codebase's real conventions; reads actual source and cites the pattern each finding deviates from; owns contract-level conflicts between parts
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a **pattern-consistency reviewer**. Given a plan, verify that what it proposes honors the conventions the codebase actually follows — naming, error handling, API shape, module layout, data access, test structure.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: every requirement and design constraint maps to a concrete plan task, classified Covered/Partial/Missing; flags only blocking gaps
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a **requirements-coverage reviewer**. Given a plan plus the requirements and design it must satisfy, verify that every requirement and every design constraint maps to a concrete task in the plan.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: input validation, injection surfaces, auth/authz gaps, data exposure, races; flags only risks with a concrete exploit path
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a **security reviewer**. Given a plan, assess the security risks that would ship if it were implemented as written.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: Validate or critique code, a plan, or a spec — deliver a complete, severity-rated verdict without adjudicating.
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a review agent. Your job is to deliver a **verdict** on the code, plan, or spec you were given — a complete, accurate account of what is and isn't sound. Be critical and precise.
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
model: opus
|
|
3
|
+
---
|
|
4
|
+
|
|
1
5
|
You are a **review orchestrator** — you own a review surface too large for one agent to assess well, and you deliver one coherent verdict by fanning reviews across it in parallel.
|
|
2
6
|
|
|
3
7
|
Decompose the surface two ways: by **unit** (files, modules, subsystems) and by **lens** (correctness, security, architecture-fit, tests, style). Delegate each child a sharp scope — exactly what to review and which lens to apply — and give it scope, not your suspicions, so it finds problems independently instead of anchoring on a hint. Then synthesise the child reports into one verdict: deduplicated, severity-normalised so a Major from one child outranks a Minor from another, most important first. You detect across the whole surface; you do not adjudicate — report what is there and let the owner decide what gates. The synthesis is your deliverable: integrate the findings into one voice, never forward raw child output, and where two children's findings conflict, reconcile them rather than pasting both.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
whenToUse: Write a specification — pin down behavior, non-goals, interfaces, edge cases, and testable acceptance criteria from a goal.
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a spec writer. Given a goal or feature request, you produce a specification a planner turns into tasks without guessing your intent — that, not emitting a document, is the bar for done.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
roadmapSkill: spec
|
|
3
|
+
model: opus
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
You are a **spec orchestrator** — you own a specification effort and deliver a spec a planner turns into tasks with zero guessing. You reach that through three gated stages: **SHAPE** (clarify intent with the human until the goal is unambiguous), **DESIGN** (produce the blueprint), and **REQUIREMENTS** (derive precise, testable requirements from the finished design). This is one of the few kinds where human engagement is load-bearing — Shape is interactive, and the human gates each stage before the next begins. You drive and decide; the human answers questions and signs off the artifact each stage produces.
|
|
@@ -8,6 +8,7 @@ import { transition } from '../../core/runtime/lifecycle.js';
|
|
|
8
8
|
import { appendInbox } from '../../core/feed/inbox.js';
|
|
9
9
|
import { existsSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
11
12
|
import { inbox, scanInbox, parseDeck, deckPath, responsePath, isResolved, atomicWriteJson, ask, launchReview, readJson, display, } from '@crouton-kit/humanloop';
|
|
12
13
|
import { killPane } from './shared.js';
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
@@ -176,8 +177,8 @@ export const humanCancel = defineLeaf({
|
|
|
176
177
|
return { canceled: true, job_id: jobId };
|
|
177
178
|
},
|
|
178
179
|
render: (r) => r['canceled'] === true
|
|
179
|
-
?
|
|
180
|
-
:
|
|
180
|
+
? `Canceled human interaction ${r['job_id']} — its TUI pane is closed and subscribers were notified no answer is coming.`
|
|
181
|
+
: `Nothing to cancel for ${r['job_id']} — ${r['reason'] ?? 'nothing to cancel'}.`,
|
|
181
182
|
});
|
|
182
183
|
// ---------------------------------------------------------------------------
|
|
183
184
|
// _run (hidden worker; not listed in branch help)
|
|
@@ -243,6 +244,16 @@ export const humanRun = defineLeaf({
|
|
|
243
244
|
catch {
|
|
244
245
|
/* render pane is best-effort; the review itself must not die */
|
|
245
246
|
}
|
|
247
|
+
// Dock the render pane BESIDE this worker pane. tmux resolves an
|
|
248
|
+
// untargeted split-window against the attached client's current window
|
|
249
|
+
// (which wins over $TMUX_PANE), so the pane can land in whatever window
|
|
250
|
+
// the user happens to be looking at — away from the editor. move-pane
|
|
251
|
+
// with explicit src/dst is deterministic: raw source and rendered doc
|
|
252
|
+
// always sit side by side.
|
|
253
|
+
const selfPane = process.env['TMUX_PANE'];
|
|
254
|
+
if (renderPane !== undefined && selfPane !== undefined && selfPane !== '') {
|
|
255
|
+
spawnSync('tmux', ['move-pane', '-h', '-s', renderPane, '-t', selfPane], { stdio: 'ignore' });
|
|
256
|
+
}
|
|
246
257
|
if (renderPane !== undefined) {
|
|
247
258
|
const rcPath = join(dir, 'run.json');
|
|
248
259
|
const cur = readJson(rcPath);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Bug-regression: guards the hard cut that merged the substrate's `when`/`why`
|
|
2
|
+
// frontmatter pair into the single `when-and-why-to-read` read-routing field.
|
|
3
|
+
// The CTO ruling (taste/why-field-means-why-to-read) requires the migration be
|
|
4
|
+
// a hard cut: an old-shape doc must FAIL at `crtr memory lint`, never be read at
|
|
5
|
+
// runtime. `lintSubstrateSchema` is that enforcement seam — these lock it.
|
|
6
|
+
//
|
|
7
|
+
// Run: node --import tsx/esm --test src/commands/memory/__tests__/lint-schema.test.ts
|
|
8
|
+
import { test } from 'node:test';
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import { lintSubstrateSchema } from '../lint.js';
|
|
11
|
+
const ROUTING = 'When you are X, this reference should be read because Y';
|
|
12
|
+
test('lint rejects a doc still carrying the retired `when` key', () => {
|
|
13
|
+
const err = lintSubstrateSchema({ kind: 'reference', when: 'when X' });
|
|
14
|
+
assert.ok(err !== null, 'old-shape `when` must produce a finding');
|
|
15
|
+
assert.match(err, /when-and-why-to-read/, 'the message points at the new field');
|
|
16
|
+
});
|
|
17
|
+
test('lint rejects a doc still carrying the retired `why` key', () => {
|
|
18
|
+
const err = lintSubstrateSchema({ kind: 'reference', why: 'because Y' });
|
|
19
|
+
assert.ok(err !== null, 'old-shape `why` must produce a finding');
|
|
20
|
+
assert.match(err, /when-and-why-to-read/, 'the message points at the new field');
|
|
21
|
+
});
|
|
22
|
+
test('lint rejects a substrate doc missing the merged routing field', () => {
|
|
23
|
+
const err = lintSubstrateSchema({ kind: 'reference' });
|
|
24
|
+
assert.ok(err !== null, 'a substrate doc must carry when-and-why-to-read');
|
|
25
|
+
assert.match(err, /when-and-why-to-read/);
|
|
26
|
+
});
|
|
27
|
+
test('lint accepts the merged new-shape frontmatter', () => {
|
|
28
|
+
assert.equal(lintSubstrateSchema({ kind: 'reference', 'when-and-why-to-read': ROUTING }), null);
|
|
29
|
+
});
|
|
@@ -12,8 +12,7 @@ function substrateUnit(d) {
|
|
|
12
12
|
kind: d.kind,
|
|
13
13
|
scope: d.scope,
|
|
14
14
|
path: d.path,
|
|
15
|
-
|
|
16
|
-
why: d.why,
|
|
15
|
+
routing: d.whenAndWhyToRead,
|
|
17
16
|
shortForm: d.shortForm,
|
|
18
17
|
loadBody: () => d.body,
|
|
19
18
|
};
|
|
@@ -26,10 +25,9 @@ function skillUnit(s) {
|
|
|
26
25
|
kind: 'skill',
|
|
27
26
|
scope: s.scope,
|
|
28
27
|
path: s.path,
|
|
29
|
-
// A skill's description plays the
|
|
30
|
-
// ranker weighs them too. short_form surfaces the description as the hook.
|
|
31
|
-
|
|
32
|
-
why: '',
|
|
28
|
+
// A skill's description plays the read-routing role; keywords ride along so
|
|
29
|
+
// the ranker weighs them too. short_form surfaces the description as the hook.
|
|
30
|
+
routing: [desc, keywords].filter((x) => x).join(' '),
|
|
33
31
|
shortForm: desc,
|
|
34
32
|
loadBody: () => parseFrontmatter(readText(s.path)).body,
|
|
35
33
|
};
|
|
@@ -80,12 +78,12 @@ function candidates(kindFilter) {
|
|
|
80
78
|
export const findLeaf = defineLeaf({
|
|
81
79
|
name: 'find',
|
|
82
80
|
description: 'relevance search across memory documents',
|
|
83
|
-
whenToUse: 'you do not yet know which document applies and need to discover what is stored — ranks documents by relevance, weighted over name,
|
|
81
|
+
whenToUse: 'you do not yet know which document applies and need to discover what is stored — ranks documents by relevance, weighted over name, the read-routing line, and short-form (add --body to also weigh body text). Searches the full scope set regardless of any visibility gate. Use --grep instead when you need an exact regex or literal-string match across document bodies rather than a ranked topic match.',
|
|
84
82
|
help: {
|
|
85
83
|
name: 'memory find',
|
|
86
|
-
summary: 'relevance search across memory documents, weighted over name/
|
|
84
|
+
summary: 'relevance search across memory documents, weighted over name/routing-line/short-form (and body with --body)',
|
|
87
85
|
params: [
|
|
88
|
-
{ kind: 'positional', name: 'query', required: true, constraint: 'With ranked search (default): whitespace-separated terms, matched case-insensitively and weighted over name,
|
|
86
|
+
{ kind: 'positional', name: 'query', required: true, constraint: 'With ranked search (default): whitespace-separated terms, matched case-insensitively and weighted over name, the read-routing line, and short-form (plus body with --body); documents matching more/stronger fields rank higher. With --grep: an ECMAScript regex applied to each document body line.' },
|
|
89
87
|
{ kind: 'flag', name: 'kind', type: 'enum', choices: [...MEMORY_KINDS], required: false, constraint: 'Filter to a single kind. Default: all kinds.' },
|
|
90
88
|
{ kind: 'flag', name: 'grep', type: 'bool', required: false, constraint: 'Treat the query as an ECMAScript regex and match it against document bodies, instead of weighted relevance ranking. Mutually exclusive with --body.' },
|
|
91
89
|
{ kind: 'flag', name: 'body', type: 'bool', required: false, constraint: 'Also weigh document body text in the relevance ranking (in addition to name/when/why/short-form). Ignored under --grep, which always scans bodies.' },
|
|
@@ -134,7 +132,7 @@ export const findLeaf = defineLeaf({
|
|
|
134
132
|
follow_up: 'Read a document in full with `crtr memory read <name>`. Drop --grep for a ranked topic search.',
|
|
135
133
|
};
|
|
136
134
|
}
|
|
137
|
-
// --- ranked mode: weighted relevance over name/
|
|
135
|
+
// --- ranked mode: weighted relevance over name/routing-line/short-form(+body) ---
|
|
138
136
|
const terms = query
|
|
139
137
|
.toLowerCase()
|
|
140
138
|
.split(/\s+/)
|
|
@@ -144,18 +142,15 @@ export const findLeaf = defineLeaf({
|
|
|
144
142
|
const hits = [];
|
|
145
143
|
for (const u of units) {
|
|
146
144
|
const nameLc = u.name.toLowerCase();
|
|
147
|
-
const
|
|
148
|
-
const whyLc = u.why.toLowerCase();
|
|
145
|
+
const routingLc = u.routing.toLowerCase();
|
|
149
146
|
const shortLc = u.shortForm.toLowerCase();
|
|
150
147
|
const bodyLc = weighBody ? u.loadBody().toLowerCase() : null;
|
|
151
148
|
let score = 0;
|
|
152
149
|
for (const term of terms) {
|
|
153
150
|
if (nameLc.includes(term))
|
|
154
151
|
score += 10;
|
|
155
|
-
if (
|
|
152
|
+
if (routingLc.includes(term))
|
|
156
153
|
score += 5;
|
|
157
|
-
if (whyLc.includes(term))
|
|
158
|
-
score += 4;
|
|
159
154
|
if (shortLc.includes(term))
|
|
160
155
|
score += 3;
|
|
161
156
|
if (bodyLc !== null && bodyLc.includes(term))
|
|
@@ -1 +1,8 @@
|
|
|
1
|
+
/** Schema checks for a doc living in a substrate memory dir: a memory store
|
|
2
|
+
* holds ONLY substrate docs, so a missing/invalid `kind` is an authoring
|
|
3
|
+
* error here (elsewhere it just means "not a substrate doc"). Rung and gate
|
|
4
|
+
* values are checked RAW — the runtime parser silently falls back to kind
|
|
5
|
+
* defaults / inert gates, which is exactly the silent tolerance this lint
|
|
6
|
+
* exists to catch at authoring time. */
|
|
7
|
+
export declare function lintSubstrateSchema(fm: Record<string, unknown> | null): string | null;
|
|
1
8
|
export declare const lintLeaf: import("../../core/command.js").LeafDef;
|
|
@@ -24,12 +24,21 @@ const RUNG_FIELDS = ['system-prompt-visibility', 'file-read-visibility'];
|
|
|
24
24
|
* values are checked RAW — the runtime parser silently falls back to kind
|
|
25
25
|
* defaults / inert gates, which is exactly the silent tolerance this lint
|
|
26
26
|
* exists to catch at authoring time. */
|
|
27
|
-
function lintSubstrateSchema(fm) {
|
|
27
|
+
export function lintSubstrateSchema(fm) {
|
|
28
28
|
if (fm === null)
|
|
29
29
|
return 'missing frontmatter: a memory store doc requires `kind: skill|reference|preference`';
|
|
30
30
|
if (!isDocKind(fm.kind)) {
|
|
31
31
|
return `invalid kind: ${JSON.stringify(fm.kind)} (expected skill|reference|preference)`;
|
|
32
32
|
}
|
|
33
|
+
// The retired `when`/`why` pair was merged into one read-routing field. The
|
|
34
|
+
// hard cut is enforced HERE: an old-shape doc must fail, never be silently
|
|
35
|
+
// read at runtime.
|
|
36
|
+
if ('when' in fm || 'why' in fm) {
|
|
37
|
+
return 'retired `when`/`why` keys: merge them into one `when-and-why-to-read` line — "When <circumstance>, this <kind> should be read <because <payoff>>."';
|
|
38
|
+
}
|
|
39
|
+
if (typeof fm['when-and-why-to-read'] !== 'string' || fm['when-and-why-to-read'].trim() === '') {
|
|
40
|
+
return 'missing `when-and-why-to-read`: one read-routing line — "When <circumstance>, this <kind> should be read <because <payoff>>."';
|
|
41
|
+
}
|
|
33
42
|
for (const field of RUNG_FIELDS) {
|
|
34
43
|
const v = fm[field];
|
|
35
44
|
if (v !== undefined && (typeof v !== 'string' || !VALID_RUNGS.includes(v))) {
|
|
@@ -91,8 +91,7 @@ export function coerceAppliesTo(raw) {
|
|
|
91
91
|
// first in this order; any preserved-on-update extras append after.
|
|
92
92
|
const FRONTMATTER_ORDER = [
|
|
93
93
|
'kind',
|
|
94
|
-
'when',
|
|
95
|
-
'why',
|
|
94
|
+
'when-and-why-to-read',
|
|
96
95
|
'short-form',
|
|
97
96
|
'system-prompt-visibility',
|
|
98
97
|
'file-read-visibility',
|
|
@@ -13,8 +13,7 @@ export const writeLeaf = defineLeaf({
|
|
|
13
13
|
params: [
|
|
14
14
|
{ kind: 'positional', name: 'name', required: true, constraint: 'Path-derived identity (e.g. `topic` or `area/topic`) → memory/<name>.md at the resolved scope. Updated in place if it already exists, otherwise created.' },
|
|
15
15
|
{ kind: 'flag', name: 'kind', type: 'enum', choices: [...MEMORY_KINDS], required: true, constraint: 'Document kind.' },
|
|
16
|
-
{ kind: 'flag', name: 'when', type: 'string', required: false, constraint: '
|
|
17
|
-
{ kind: 'flag', name: 'why', type: 'string', required: false, constraint: 'Frontmatter whyText — a short string describing why this document matters.' },
|
|
16
|
+
{ kind: 'flag', name: 'when-and-why-to-read', type: 'string', required: false, constraint: 'The read-routing line, authored as ONE sentence: "When <circumstance>, this <kind> should be read <because <payoff>>." It states WHEN to read this doc and WHY the read is worth it — read-routing, NEVER a justification of why the content should be obeyed. Rendered verbatim as the preview. Required when creating.' },
|
|
18
17
|
{ kind: 'flag', name: 'short-form', type: 'string', required: false, constraint: 'Frontmatter short-form — a very abbreviated version of the content, the hook shown in `crtr memory list`.' },
|
|
19
18
|
{ kind: 'flag', name: 'system-prompt-visibility', type: 'enum', choices: [...VISIBILITY_RUNGS], required: false, constraint: 'Rung controlling how much of this document auto-loads into the system prompt / CLI help.' },
|
|
20
19
|
{ kind: 'flag', name: 'file-read-visibility', type: 'enum', choices: [...VISIBILITY_RUNGS], required: false, constraint: 'Rung controlling how much of this document surfaces when it is read off disk.' },
|
|
@@ -44,11 +43,10 @@ export const writeLeaf = defineLeaf({
|
|
|
44
43
|
const { scope, memoryDir } = resolveWriteTarget(scopeArg);
|
|
45
44
|
const path = memoryFilePath(memoryDir, name);
|
|
46
45
|
const created = !pathExists(path);
|
|
47
|
-
// CREATE requires the
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
throw usage(`creating ${name} requires --when and --why (they compose the preview routing line "{when}, read this ${kind}. {why}.")`);
|
|
46
|
+
// CREATE requires the read-routing line that becomes the preview — without
|
|
47
|
+
// it every preview renders empty. UPDATE inherits it from the existing doc.
|
|
48
|
+
if (created && input['whenAndWhyToRead'] === undefined) {
|
|
49
|
+
throw usage(`creating ${name} requires --when-and-why-to-read: one read-routing sentence "When <circumstance>, this ${kind} should be read <because <payoff>>." (rendered verbatim as the preview).`);
|
|
52
50
|
}
|
|
53
51
|
// In-place update: start from the existing frontmatter (preserving fields
|
|
54
52
|
// not passed this time), then overlay the provided ones. Create: start clean.
|
|
@@ -62,8 +60,7 @@ export const writeLeaf = defineLeaf({
|
|
|
62
60
|
if (value !== undefined)
|
|
63
61
|
frontmatter[key] = value;
|
|
64
62
|
};
|
|
65
|
-
setIf('when', input['
|
|
66
|
-
setIf('why', input['why']);
|
|
63
|
+
setIf('when-and-why-to-read', input['whenAndWhyToRead']);
|
|
67
64
|
setIf('short-form', input['shortForm']);
|
|
68
65
|
setIf('system-prompt-visibility', input['systemPromptVisibility']);
|
|
69
66
|
setIf('file-read-visibility', input['fileReadVisibility']);
|