@crouton-kit/crouter 0.3.35 → 0.3.37
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/build-root.js +2 -6
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-personas/runtime-base.md +1 -1
- package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +4 -4
- package/dist/builtin-views/canvas/core.mjs +3 -2
- package/dist/clients/attach/__tests__/crtr-output-render.test.js +21 -205
- package/dist/clients/attach/attach-cmd.js +383 -386
- package/dist/clients/web/web-cmd.js +7 -7
- package/dist/commands/__tests__/human.test.js +1 -15
- package/dist/commands/attention.js +9 -6
- package/dist/commands/canvas-history/search.js +2 -2
- package/dist/commands/canvas-history/show.js +3 -3
- package/dist/commands/canvas-history.js +1 -1
- package/dist/commands/canvas-issue.js +1 -0
- package/dist/commands/canvas-snapshot.js +1 -0
- package/dist/commands/canvas.js +10 -11
- package/dist/commands/chord.js +1 -0
- package/dist/commands/human/prompts.js +0 -1
- package/dist/commands/human/queue.js +1 -0
- package/dist/commands/node-snapshot.js +1 -0
- package/dist/commands/node.js +12 -11
- package/dist/commands/push.d.ts +0 -1
- package/dist/commands/push.js +2 -228
- package/dist/commands/revive.js +2 -2
- package/dist/commands/{canvas-tmux-spread.js → surface-tmux-spread.js} +9 -6
- package/dist/commands/surface.js +3 -2
- package/dist/commands/sys/daemon.d.ts +2 -0
- package/dist/commands/{daemon.js → sys/daemon.js} +12 -12
- package/dist/commands/sys/doctor.js +1 -0
- package/dist/commands/sys.js +4 -3
- package/dist/core/__tests__/daemon-boot.test.js +1 -1
- package/dist/core/__tests__/daemon-wedge.test.d.ts +1 -0
- package/dist/core/__tests__/daemon-wedge.test.js +87 -0
- package/dist/core/__tests__/hearth-bootstrap.test.js +2 -2
- package/dist/core/__tests__/history-inbox.test.d.ts +1 -0
- package/dist/core/__tests__/history-inbox.test.js +105 -0
- package/dist/core/__tests__/kickoff.test.js +3 -3
- package/dist/core/__tests__/revive.test.js +39 -1
- package/dist/core/canvas/history.d.ts +1 -1
- package/dist/core/canvas/history.js +90 -3
- package/dist/core/canvas/paths.d.ts +6 -0
- package/dist/core/canvas/paths.js +9 -0
- package/dist/core/canvas/status-glyph.js +1 -0
- package/dist/core/fault-classifier.d.ts +2 -2
- package/dist/core/feed/inbox.d.ts +7 -12
- package/dist/core/feed/inbox.js +45 -29
- package/dist/core/hearth/providers/blaxel-bootstrap.js +1 -1
- package/dist/core/preview-registry.js +14 -112
- package/dist/core/render.js +1 -1
- package/dist/core/runtime/busy.d.ts +15 -0
- package/dist/core/runtime/busy.js +32 -1
- package/dist/core/runtime/close.d.ts +10 -0
- package/dist/core/runtime/close.js +18 -13
- package/dist/core/runtime/fault-recovery.js +4 -0
- package/dist/core/runtime/kickoff.js +5 -7
- package/dist/core/runtime/placement.js +1 -1
- package/dist/core/runtime/revive.js +9 -1
- package/dist/core/runtime/tmux.d.ts +2 -2
- package/dist/core/runtime/tmux.js +2 -2
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.d.ts +24 -0
- package/dist/daemon/crtrd.js +213 -5
- package/dist/daemon/manage.js +2 -2
- package/dist/pi-extensions/canvas-nav.js +2 -2
- package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
- package/dist/pi-extensions/canvas-stophook.js +59 -2
- package/dist/web-client/assets/{index-IAJVtuVe.js → index-MSKSWwV0.js} +1 -1
- package/dist/web-client/index.html +1 -1
- package/package.json +1 -1
- package/dist/commands/daemon.d.ts +0 -2
- /package/dist/commands/{canvas-tmux-spread.d.ts → surface-tmux-spread.d.ts} +0 -0
|
@@ -36,11 +36,11 @@ function openBrowser(url) {
|
|
|
36
36
|
}
|
|
37
37
|
const webServeLeaf = defineLeaf({
|
|
38
38
|
name: 'serve',
|
|
39
|
-
description: 'start the crouter web UI server — the
|
|
40
|
-
whenToUse: 'you want the crouter web UI
|
|
39
|
+
description: 'start the crouter web UI server — the browser alternative to a tmux pane for watching the canvas and driving headless nodes',
|
|
40
|
+
whenToUse: 'you want the crouter web UI in a browser instead of a tmux pane — watching the canvas and driving headless nodes from a page rather than a terminal surface. Binds 127.0.0.1 by default; pass --host to bind elsewhere, in which case --token is required so the page can authorize. Pass --dev only if you are iterating on the shell itself (Vite HMR) rather than just running it. The node is picked in the browser via the URL, not on the command line; the server runs until ctrl+c.',
|
|
41
41
|
help: {
|
|
42
42
|
name: 'web serve',
|
|
43
|
-
summary: 'start the
|
|
43
|
+
summary: 'start the crouter web UI server — the browser alternative to a tmux pane for watching the canvas and driving headless nodes',
|
|
44
44
|
params: [
|
|
45
45
|
{
|
|
46
46
|
kind: 'flag',
|
|
@@ -125,14 +125,14 @@ export function registerWeb() {
|
|
|
125
125
|
return defineBranch({
|
|
126
126
|
name: 'web',
|
|
127
127
|
rootEntry: {
|
|
128
|
-
concept: 'the crouter web UI server —
|
|
128
|
+
concept: 'the crouter web UI server — the browser alternative to a tmux pane for watching the canvas and driving headless nodes',
|
|
129
129
|
desc: 'serve the crouter web UI for the canvas and headless nodes',
|
|
130
|
-
useWhen: 'you want the crouter web UI in a browser instead of a tmux pane: watch the canvas and drive headless nodes
|
|
130
|
+
useWhen: 'you want the crouter web UI in a browser instead of a tmux pane: watch the canvas and drive headless nodes from a page. Binds 127.0.0.1 by default; a non-loopback bind requires a token so the page can authorize. The node is selected in the browser via the URL, not on the command line.',
|
|
131
131
|
},
|
|
132
132
|
help: {
|
|
133
133
|
name: 'web',
|
|
134
|
-
summary: 'serve the crouter web UI
|
|
135
|
-
model: 'The branch is a long-running browser surface
|
|
134
|
+
summary: 'serve the crouter web UI — the browser alternative to a tmux pane for the canvas and headless nodes',
|
|
135
|
+
model: 'The branch is a long-running browser surface, an alternative to a tmux pane. `serve` binds a host and port (loopback by default; --token required off-loopback) and runs until interrupted. The node is chosen in the browser via the URL, not on the command line; the server itself only hosts the surface and never spawns an engine.',
|
|
136
136
|
},
|
|
137
137
|
children: [webServeLeaf],
|
|
138
138
|
});
|
|
@@ -100,7 +100,7 @@ describe('human show: params', () => {
|
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
102
|
// ---------------------------------------------------------------------------
|
|
103
|
-
// human ask — --context-file (context-file kind, key=deckFile)
|
|
103
|
+
// human ask — --context-file (context-file kind, key=deckFile)
|
|
104
104
|
//
|
|
105
105
|
// NOTE: The argv parser hardcodes the CLI token as `--context-file` regardless
|
|
106
106
|
// of the param's `name` field. The `name` field ("deckFile") becomes the key
|
|
@@ -111,7 +111,6 @@ describe('human show: params', () => {
|
|
|
111
111
|
describe('human ask: params', () => {
|
|
112
112
|
const params = [
|
|
113
113
|
{ kind: 'context-file', name: 'deckFile', required: true, constraint: 'Humanloop deck JSON.' },
|
|
114
|
-
{ kind: 'flag', name: 'wait', type: 'bool', required: false, constraint: 'Presence-only.' },
|
|
115
114
|
];
|
|
116
115
|
test('--context-file parses JSON file and yields input.deckFile', async () => {
|
|
117
116
|
const deck = { interactions: [{ id: 'q', title: 'Go?', options: [{ id: 'yes', label: 'Yes' }] }] };
|
|
@@ -132,18 +131,6 @@ describe('human ask: params', () => {
|
|
|
132
131
|
writeFileSync(p, 'not json at all');
|
|
133
132
|
await assert.rejects(() => parseArgv(params, ['--context-file', p]), (err) => { assert.match(err.message, /not valid JSON/); return true; });
|
|
134
133
|
});
|
|
135
|
-
test('--wait absent → false', async () => {
|
|
136
|
-
const deck = { interactions: [] };
|
|
137
|
-
const path = tmpJson(deck);
|
|
138
|
-
const result = await parseArgv(params, ['--context-file', path]);
|
|
139
|
-
assert.equal(result['wait'], false);
|
|
140
|
-
});
|
|
141
|
-
test('--wait present → true', async () => {
|
|
142
|
-
const deck = { interactions: [] };
|
|
143
|
-
const path = tmpJson(deck);
|
|
144
|
-
const result = await parseArgv(params, ['--context-file', path, '--wait']);
|
|
145
|
-
assert.equal(result['wait'], true);
|
|
146
|
-
});
|
|
147
134
|
});
|
|
148
135
|
// ---------------------------------------------------------------------------
|
|
149
136
|
// human ask — shorthand question + strict --context-file deck
|
|
@@ -159,7 +146,6 @@ describe('human ask: shorthand', () => {
|
|
|
159
146
|
test('parses a positional question into input.question', async () => {
|
|
160
147
|
const result = await parseArgv(humanAsk.help.params ?? [], ['Should we ship today?']);
|
|
161
148
|
assert.equal(result['question'], 'Should we ship today?');
|
|
162
|
-
assert.equal(result['wait'], false);
|
|
163
149
|
});
|
|
164
150
|
test('parses --context-file into input.deck while keeping the strict deck path', async () => {
|
|
165
151
|
const deck = {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// `crtr canvas attention` — aggregate pending human asks across the canvas.
|
|
2
2
|
//
|
|
3
3
|
// Pending asks are stored per-cwd by humanloop. This subtree surfaces two
|
|
4
|
-
// views:
|
|
5
|
-
// count — a single integer (stdout.count is
|
|
4
|
+
// public views:
|
|
5
|
+
// count — a single integer (stdout.count is machine-parseable)
|
|
6
6
|
// list — itemised entries with cwd, node id, and per-cwd ask count
|
|
7
|
+
// `map` (hidden) is the batched per-node form the attach viewer's nav chrome
|
|
8
|
+
// polls; it is not an agent-facing decision.
|
|
7
9
|
//
|
|
8
10
|
// All three scope modes share the same underlying helpers in attention.ts:
|
|
9
11
|
// --node <id> → countAsks(id) (one cwd)
|
|
@@ -21,7 +23,7 @@ import { countAsks, pendingAsksForView, asksAcrossCanvas, asksForNodes, } from '
|
|
|
21
23
|
const attentionCount = defineLeaf({
|
|
22
24
|
name: 'count',
|
|
23
25
|
description: 'total pending ask count (machine-parseable stdout.count)',
|
|
24
|
-
whenToUse: '
|
|
26
|
+
whenToUse: 'a lightweight summary when you just need the number, not which cwds are blocked — scope it to a node or a sub-DAG, or default to canvas-wide. Use `list` when you need to act on where to go answer.',
|
|
25
27
|
help: {
|
|
26
28
|
name: 'canvas attention count',
|
|
27
29
|
// stdout.count is parsed directly by the nav chrome — keep the contract.
|
|
@@ -93,7 +95,7 @@ const attentionCount = defineLeaf({
|
|
|
93
95
|
const attentionList = defineLeaf({
|
|
94
96
|
name: 'list',
|
|
95
97
|
description: 'itemised list of cwds with pending asks',
|
|
96
|
-
whenToUse: 'finding which agents are blocked waiting on a human — an itemised list of the cwds with pending asks, oldest first, so you know where to go answer. Scope it to a sub-DAG or list canvas-wide. Use `canvas attention count` instead when
|
|
98
|
+
whenToUse: 'finding which agents are blocked waiting on a human — an itemised list of the cwds with pending asks, oldest first, so you know where to go answer. Scope it to a sub-DAG or list canvas-wide. Use `canvas attention count` instead when you just need the number.',
|
|
97
99
|
help: {
|
|
98
100
|
name: 'canvas attention list',
|
|
99
101
|
summary: 'list nodes with pending human asks, grouped by cwd, oldest first',
|
|
@@ -145,6 +147,7 @@ const attentionMap = defineLeaf({
|
|
|
145
147
|
name: 'map',
|
|
146
148
|
description: 'per-node ask counts for a visible set, batched in one pass',
|
|
147
149
|
whenToUse: 'labelling every node in a UI with its pending-ask count in one batched pass — the form nav chrome polls (one process, one JSON blob) instead of N count shell-outs',
|
|
150
|
+
tier: 'hidden',
|
|
148
151
|
help: {
|
|
149
152
|
name: 'canvas attention map',
|
|
150
153
|
summary: 'per-node pending-ask counts for a visible set of nodes in ONE pass — the batched form the nav chrome polls (one process, one JSON blob) instead of N count shell-outs',
|
|
@@ -211,11 +214,11 @@ const attentionMap = defineLeaf({
|
|
|
211
214
|
export const attentionBranch = defineBranch({
|
|
212
215
|
name: 'attention',
|
|
213
216
|
description: 'count/list pending human asks across the graph',
|
|
214
|
-
whenToUse: 'checking whether any agent on the canvas is blocked waiting on a human, and where: count the pending asks
|
|
217
|
+
whenToUse: 'checking whether any agent on the canvas is blocked waiting on a human, and where: count the pending asks or list the cwds that have them. Scope it to a node or a sub-DAG, or go canvas-wide. Use `canvas dashboard` instead for the graph SHAPE, or `node inspect list` for a plain node roster',
|
|
215
218
|
help: {
|
|
216
219
|
name: 'canvas attention',
|
|
217
220
|
summary: 'aggregate pending human asks across the canvas',
|
|
218
|
-
model: '
|
|
221
|
+
model: '`count` returns a single integer (stdout.count is machine-parseable); `list` returns itemised entries — the cwds with pending asks, oldest first. Scope with --node (one node) or --view (sub-DAG) — default is canvas-wide.',
|
|
219
222
|
},
|
|
220
223
|
children: [attentionCount, attentionList, attentionMap],
|
|
221
224
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineLeaf } from '../../core/command.js';
|
|
2
2
|
import { usage } from '../../core/errors.js';
|
|
3
3
|
import { buildCorpus, } from '../../core/canvas/index.js';
|
|
4
|
-
const TYPES = ['report', 'doc', 'roadmap', 'meta'];
|
|
4
|
+
const TYPES = ['report', 'doc', 'roadmap', 'meta', 'inbox'];
|
|
5
5
|
const NODE_KINDS = ['advisor', 'developer', 'spec', 'design', 'review', 'explore', 'plan', 'general'];
|
|
6
6
|
const STATUSES = ['active', 'idle', 'done', 'dead', 'canceled'];
|
|
7
7
|
const SORTS = ['relevance', 'recency', 'oldest'];
|
|
@@ -86,7 +86,7 @@ export const searchLeaf = defineLeaf({
|
|
|
86
86
|
{ kind: 'flag', name: 'all-cwds', type: 'bool', required: false, constraint: 'Search every cwd on the canvas. Mutually exclusive with --cwd.' },
|
|
87
87
|
{ kind: 'flag', name: 'under', type: 'string', required: false, constraint: 'Restrict to a node and its subscription descendants (one initiative / sub-DAG).' },
|
|
88
88
|
{ kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Restrict to specific node ids. Comma-separated for several.' },
|
|
89
|
-
{ kind: 'flag', name: 'type', type: 'string', required: false, constraint: `Corpus to search; comma-separated for several. One of: ${TYPES.join(', ')}. report = push history; doc = context artifacts; roadmap = roadmap.md specifically; meta = node name/description/kind. Default:
|
|
89
|
+
{ kind: 'flag', name: 'type', type: 'string', required: false, constraint: `Corpus to search; comma-separated for several. One of: ${TYPES.join(', ')}. report = push history; doc = context artifacts; roadmap = roadmap.md specifically; meta = node name/description/kind; inbox = raw inbox.jsonl history, cursor-independent and including full un-clipped direct-message bodies (diagnostic, not curated work). Default: report, doc, roadmap, meta — inbox is opt-in only, pass --type inbox explicitly.` },
|
|
90
90
|
{ kind: 'flag', name: 'report-kind', type: 'enum', choices: ['final', 'update'], required: false, constraint: 'Narrow reports to outcome summaries (final) or progress (update). Implies --type report.' },
|
|
91
91
|
{ kind: 'flag', name: 'kind', type: 'string', required: false, constraint: `Node kind; comma-separated for several. One of: ${NODE_KINDS.join(', ')}.` },
|
|
92
92
|
{ kind: 'flag', name: 'status', type: 'string', required: false, constraint: `Node lifecycle status; comma-separated for several. One of: ${STATUSES.join(', ')} (e.g. done = completed work only).` },
|
|
@@ -2,7 +2,7 @@ import { defineLeaf } from '../../core/command.js';
|
|
|
2
2
|
import { notFound } from '../../core/errors.js';
|
|
3
3
|
import { getNode, nodeArtifacts } from '../../core/canvas/index.js';
|
|
4
4
|
import { statSync } from 'node:fs';
|
|
5
|
-
const TYPES = ['report', 'doc', 'roadmap'];
|
|
5
|
+
const TYPES = ['report', 'doc', 'roadmap', 'inbox'];
|
|
6
6
|
export const showLeaf = defineLeaf({
|
|
7
7
|
name: 'show',
|
|
8
8
|
description: 'enumerate one node\'s artifacts as refs',
|
|
@@ -12,12 +12,12 @@ export const showLeaf = defineLeaf({
|
|
|
12
12
|
summary: 'list one node\'s artifacts (reports + context docs) as refs ready for `canvas history read`',
|
|
13
13
|
params: [
|
|
14
14
|
{ kind: 'positional', name: 'node-id', required: true, constraint: 'The node whose artifacts to list.' },
|
|
15
|
-
{ kind: 'flag', name: 'type', type: 'enum', choices: [...TYPES], required: false, constraint: 'Narrow to one corpus: report | doc | roadmap. Default:
|
|
15
|
+
{ kind: 'flag', name: 'type', type: 'enum', choices: [...TYPES], required: false, constraint: 'Narrow to one corpus: report | doc | roadmap | inbox. Default: report, doc, roadmap (inbox is opt-in only — the node\'s raw inbox.jsonl history, cursor-independent and including full un-clipped direct-message bodies; a diagnostic surface, not curated past work).' },
|
|
16
16
|
{ kind: 'flag', name: 'sort', type: 'enum', choices: ['recency', 'oldest'], required: false, constraint: 'Order by artifact timestamp. Default recency.' },
|
|
17
17
|
],
|
|
18
18
|
output: [
|
|
19
19
|
{ name: 'node', type: 'string', required: true, constraint: 'Node name + id.' },
|
|
20
|
-
{ name: 'artifacts', type: 'object[]', required: true, constraint: 'Each: {ref, source, ts, detail}. source = report:<kind> | doc | roadmap. detail = report title / doc heading + size. ref passes verbatim to `canvas history read`.' },
|
|
20
|
+
{ name: 'artifacts', type: 'object[]', required: true, constraint: 'Each: {ref, source, ts, detail}. source = report:<kind> | doc | roadmap | inbox. detail = report title / doc heading + size (inbox: `[kind] label`, no size). ref passes verbatim to `canvas history read`.' },
|
|
21
21
|
{ name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands.' },
|
|
22
22
|
],
|
|
23
23
|
outputKind: 'object',
|
|
@@ -23,7 +23,7 @@ export const historyBranch = defineBranch({
|
|
|
23
23
|
help: {
|
|
24
24
|
name: 'canvas history',
|
|
25
25
|
summary: 'search and recall the canvas\'s record of past work in this cwd',
|
|
26
|
-
model: 'The accumulated record of every node that ran in a cwd and the artifacts it left — its reports (final/update outcome summaries) and context docs (specs, designs, roadmaps, findings). `search` finds by content (ranked, filtered, sorted; omit the query to browse by recency); `read` pulls one hit\'s full body by its <node-id>:<relpath> ref; `show` lists everything one node left. This is the episodic record of what was DONE — distinct from `crtr memory`, which is curated semantic knowledge. To reopen a node you found, use `canvas revive`; for its place in the graph, `node inspect show`.',
|
|
26
|
+
model: 'The accumulated record of every node that ran in a cwd and the artifacts it left — its reports (final/update outcome summaries) and context docs (specs, designs, roadmaps, findings). `search` finds by content (ranked, filtered, sorted; omit the query to browse by recency); `read` pulls one hit\'s full body by its <node-id>:<relpath> ref; `show` lists everything one node left. This is the episodic record of what was DONE — distinct from `crtr memory`, which is curated semantic knowledge. A node\'s raw inbox history (`--type inbox`) is a separate, opt-in diagnostic corpus — the full cursor-independent inbox.jsonl for one node, including un-clipped direct-message bodies; reach for it only when the curated reports/docs above don\'t already answer the question. To reopen a node you found, use `canvas revive`; for its place in the graph, `node inspect show`.',
|
|
27
27
|
dynamicState: corpusBlock,
|
|
28
28
|
},
|
|
29
29
|
children: [searchLeaf, readLeaf, showLeaf],
|
|
@@ -66,6 +66,7 @@ export const issueNewLeaf = defineLeaf({
|
|
|
66
66
|
name: 'new',
|
|
67
67
|
description: 'capture a todo/idea/bug note into a GitHub issue (or a .todos/ markdown note)',
|
|
68
68
|
whenToUse: 'the alt+c \u2192 i chord runs this inside a tmux popup; rarely run by hand',
|
|
69
|
+
tier: 'hidden',
|
|
69
70
|
help: {
|
|
70
71
|
name: 'canvas issue new',
|
|
71
72
|
summary: 'Capture a free-text note as a GitHub issue labeled with its type (idea|bug). Reads the note from the popup TTY, auto-generates the title via a headless pi call, and files it with `gh issue create` when the cwd is a GitHub repo, else writes `<git-root>/.todos/<slug>.md`. Bound to the alt+c \u2192 i prefix chord.',
|
|
@@ -5,6 +5,7 @@ export const canvasSnapshotLeaf = defineLeaf({
|
|
|
5
5
|
name: 'snapshot',
|
|
6
6
|
description: 'emit the browser canvas roster as structured data',
|
|
7
7
|
whenToUse: 'you need a machine-readable roster of every canvas node for a browser or local tool; use dashboard for the human-readable tree',
|
|
8
|
+
tier: 'hidden',
|
|
8
9
|
help: {
|
|
9
10
|
name: 'canvas snapshot',
|
|
10
11
|
summary: 'machine-readable snapshot of the whole canvas roster',
|
package/dist/commands/canvas.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
// `crtr canvas` — observe and supervise the whole graph.
|
|
2
2
|
//
|
|
3
|
-
// Where `node` operates on one node and `push
|
|
4
|
-
//
|
|
3
|
+
// Where `node` operates on one node and `push` is a node's own spine write
|
|
4
|
+
// verb, `canvas` is the bird's-eye / supervisor surface over the entire canvas:
|
|
5
5
|
// render the subscription forest (`dashboard`), see who is blocked on a human
|
|
6
|
-
// (`attention`), bring a node back (`revive`)
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// (`attention`), and bring a node back (`revive`). It assembles leaves/branches
|
|
7
|
+
// the sibling command files own, so each piece declares its own help one level
|
|
8
|
+
// down.
|
|
9
9
|
import { defineBranch } from '../core/command.js';
|
|
10
10
|
import { dashboardLeaf } from './dashboard.js';
|
|
11
11
|
import { canvasSnapshotLeaf } from './canvas-snapshot.js';
|
|
12
12
|
import { browseLeaf } from './canvas-browse.js';
|
|
13
13
|
import { reviveLeaf } from './revive.js';
|
|
14
14
|
import { attentionBranch } from './attention.js';
|
|
15
|
-
import { daemonBranch } from './daemon.js';
|
|
16
15
|
import { chordLeaf } from './chord.js';
|
|
17
|
-
import { tmuxSpreadLeaf } from './canvas-tmux-spread.js';
|
|
18
16
|
import { canvasPruneLeaf } from './canvas-prune.js';
|
|
19
17
|
import { canvasRebuildIndexLeaf } from './canvas-rebuild-index.js';
|
|
20
18
|
import { historyBranch } from './canvas-history.js';
|
|
@@ -23,6 +21,7 @@ export const issueBranch = defineBranch({
|
|
|
23
21
|
name: 'issue',
|
|
24
22
|
description: 'capture a quick todo/idea/bug into a GitHub issue (or a .todos/ note)',
|
|
25
23
|
whenToUse: 'the alt+c \u2192 i chord uses this; capture a passing thought without leaving your pane',
|
|
24
|
+
tier: 'hidden',
|
|
26
25
|
help: {
|
|
27
26
|
name: 'canvas issue',
|
|
28
27
|
summary: 'capture a quick todo/idea/bug into a GitHub issue',
|
|
@@ -34,15 +33,15 @@ export function registerCanvas() {
|
|
|
34
33
|
return defineBranch({
|
|
35
34
|
name: 'canvas',
|
|
36
35
|
rootEntry: {
|
|
37
|
-
concept: 'the whole agent graph at a glance — render it, see who is blocked, revive a node
|
|
36
|
+
concept: 'the whole agent graph at a glance — render it, see who is blocked, revive a node',
|
|
38
37
|
desc: 'bird\'s-eye view and supervision of the entire canvas',
|
|
39
|
-
useWhen: 'surveying the full graph, finding blocked agents, or
|
|
38
|
+
useWhen: 'surveying the full graph, finding blocked agents, or reviving a node',
|
|
40
39
|
},
|
|
41
40
|
help: {
|
|
42
41
|
name: 'canvas',
|
|
43
42
|
summary: 'observe and supervise the whole agent graph',
|
|
44
|
-
model: 'Canvas-wide operations, distinct from per-node work (`node`) and a node\'s own spine
|
|
43
|
+
model: 'Canvas-wide operations, distinct from per-node work (`node`) and a node\'s own spine write verb (`push`). `dashboard` renders the subscription forest as a tree; `browse` opens an interactive full-screen navigator (tabs/tree/search) over the whole canvas and resumes the chosen node; `attention` aggregates pending human asks across the graph; `revive` reopens a window for a done/idle/dead/canceled node; `history` searches and recalls the content record (reports + context docs) of past work in a cwd; `prune` bounds growth by deleting terminal nodes past a TTL.',
|
|
45
44
|
},
|
|
46
|
-
children: [dashboardLeaf, canvasSnapshotLeaf, browseLeaf, attentionBranch, reviveLeaf, historyBranch,
|
|
45
|
+
children: [dashboardLeaf, canvasSnapshotLeaf, browseLeaf, attentionBranch, reviveLeaf, historyBranch, chordLeaf, issueBranch, canvasPruneLeaf, canvasRebuildIndexLeaf],
|
|
47
46
|
});
|
|
48
47
|
}
|
package/dist/commands/chord.js
CHANGED
|
@@ -154,6 +154,7 @@ export const chordLeaf = defineLeaf({
|
|
|
154
154
|
name: 'chord',
|
|
155
155
|
description: 'tmux prefix-menu dispatcher (bound by alt+c; not run by hand)',
|
|
156
156
|
whenToUse: 'never directly — the alt+c menu routes node-relative actions through it',
|
|
157
|
+
tier: 'hidden',
|
|
157
158
|
help: {
|
|
158
159
|
name: 'canvas chord',
|
|
159
160
|
summary: 'tmux prefix-menu dispatcher — resolve the node in --pane, interpolate --run against it, and exec `crtr <argv>`. Bound by the alt+c menu; not meant to be run by hand.',
|
|
@@ -40,7 +40,6 @@ export const humanAsk = defineLeaf({
|
|
|
40
40
|
params: [
|
|
41
41
|
{ kind: 'positional', name: 'question', type: 'string', required: false, constraint: 'Bare-question shorthand for a simple open-text ask. Mutually exclusive with `--context-file`.' },
|
|
42
42
|
{ kind: 'context-file', name: 'deck', required: false, constraint: 'Strict complex-ask deck JSON. Use this when you need multiple interactions, options, or other deck structure. Mutually exclusive with the question shorthand.', shape: DECK_SCHEMA_HINT },
|
|
43
|
-
{ kind: 'flag', name: 'wait', type: 'bool', required: false, constraint: 'Accepted for symmetry with the job contract; the kickoff never blocks.' },
|
|
44
43
|
],
|
|
45
44
|
output: [
|
|
46
45
|
{ name: 'job_id', type: 'string', required: true, constraint: 'Node id of this human interaction. Its answer is pushed to your inbox when the human responds.' },
|
|
@@ -520,6 +520,7 @@ export const humanResolve = defineLeaf({
|
|
|
520
520
|
name: 'resolve',
|
|
521
521
|
description: 'resolve one humanloop deck from stdin answers',
|
|
522
522
|
whenToUse: 'a browser or local tool is submitting answers for a pending human interaction; this writes through the crouter/humanloop finalize path',
|
|
523
|
+
tier: 'hidden',
|
|
523
524
|
help: {
|
|
524
525
|
name: 'human resolve',
|
|
525
526
|
summary: 'write deck answers atomically and deliver them back to the asking node',
|
|
@@ -63,6 +63,7 @@ export const nodeSnapshotLeaf = defineLeaf({
|
|
|
63
63
|
name: 'snapshot',
|
|
64
64
|
description: 'read a dormant node session snapshot without launching its broker',
|
|
65
65
|
whenToUse: 'you need a read-only broker-compatible snapshot of a dormant node; it copies the session JSONL before parsing and never revives the node',
|
|
66
|
+
tier: 'hidden',
|
|
66
67
|
help: {
|
|
67
68
|
name: 'node inspect snapshot',
|
|
68
69
|
summary: 'read-only dormant node snapshot in the broker welcome shape',
|
package/dist/commands/node.js
CHANGED
|
@@ -400,15 +400,15 @@ const nodeShow = defineLeaf({
|
|
|
400
400
|
},
|
|
401
401
|
});
|
|
402
402
|
// ---------------------------------------------------------------------------
|
|
403
|
-
// node inspect — read the graph (list, show,
|
|
403
|
+
// node inspect — read the graph (list, show, context)
|
|
404
404
|
// ---------------------------------------------------------------------------
|
|
405
405
|
const nodeInspect = defineBranch({
|
|
406
406
|
name: 'inspect',
|
|
407
|
-
description: 'read the graph (list, show,
|
|
408
|
-
whenToUse: 'reading the graph: enumerate nodes on the canvas (`list`), inspect one and its spine neighbors (`show`), or
|
|
407
|
+
description: 'read the graph (list, show, context)',
|
|
408
|
+
whenToUse: 'reading the graph: enumerate nodes on the canvas (`list`), inspect one and its spine neighbors (`show`), or browse on-disk context dirs with `node inspect context` while reading a node. Use `canvas dashboard` instead to render the tree SHAPE, or `canvas attention` to find which nodes are blocked on a human',
|
|
409
409
|
help: {
|
|
410
410
|
name: 'node inspect',
|
|
411
|
-
summary: 'read the canvas graph — enumerate nodes, inspect one, or
|
|
411
|
+
summary: 'read the canvas graph — enumerate nodes, inspect one, or browse on-disk context dirs for a node',
|
|
412
412
|
},
|
|
413
413
|
children: [nodeList, nodeShow, nodeSnapshotLeaf, nodeContextLeaf],
|
|
414
414
|
});
|
|
@@ -962,10 +962,10 @@ function resolveMsgTarget(input) {
|
|
|
962
962
|
const nodeMsg = defineLeaf({
|
|
963
963
|
name: 'msg',
|
|
964
964
|
description: 'message any node now, or arm a trigger that messages/revives it later',
|
|
965
|
-
whenToUse: 'you want to address a specific node directly — steer it mid-flight, hand it a correction, ping it, pass it new information, or schedule any of that for later — regardless of subscriptions. Default delivers immediately and revives a dormant target; `--at`/`--every` arm a clock trigger instead of acting now; `--fresh` revives with no inbox entry; `--self --until`
|
|
965
|
+
whenToUse: 'you want to address a specific node directly — steer it mid-flight, hand it a correction, ping it, pass it new information, or schedule any of that for later — regardless of subscriptions. Default delivers immediately and revives a dormant target; `--at`/`--every` arm a clock trigger instead of acting now; `--fresh` revives with no inbox entry; `--self --until` binds a deadline that changes your task policy if no push arrives by then; `--when` arms a daemon-evaluated predicate for an external condition (CI, a deploy) you cannot otherwise be pushed for. Use `node subscribe` instead to wire ONGOING push delivery rather than a one-off, `push` to report UP your own spine, and `node new` to schedule the BIRTH of a node rather than message an existing one',
|
|
966
966
|
help: {
|
|
967
967
|
name: 'node msg',
|
|
968
|
-
summary: 'message a node now, or arm a trigger (clock or predicate) that messages/revives it later. No trigger flag → immediate inbox delivery (or immediate fresh revive with --fresh). --at/--every → clock trigger. --self --until → self-only deadline
|
|
968
|
+
summary: 'message a node now, or arm a trigger (clock or predicate) that messages/revives it later. No trigger flag → immediate inbox delivery (or immediate fresh revive with --fresh). --at/--every → clock trigger. --self --until → self-only deadline: fires whichever comes first, an inbox message or this time, and that outcome is what should change your next action. --when → daemon-evaluated predicate, fires once on exit 0',
|
|
969
969
|
params: [
|
|
970
970
|
{ kind: 'stdin', name: 'body', required: false, constraint: 'Message body. Positional or stdin. Required for a plain or clock-triggered message and for --until. Forbidden with --fresh. Optional with --when — if omitted, the predicate\'s captured stdout becomes the body when it fires.' },
|
|
971
971
|
{ kind: 'flag', name: 'to', type: 'string', required: false, constraint: 'Target an existing node by id. Exactly one of --to/--self is required.' },
|
|
@@ -975,7 +975,7 @@ const nodeMsg = defineLeaf({
|
|
|
975
975
|
{ kind: 'flag', name: 'at', type: 'string', required: false, constraint: 'Arm a one-shot clock trigger at this future time instead of acting now — a duration ("5m","1h30m"), a zoned ISO ("2026-06-07T09:00:00Z"), or a bare ISO ("2026-06-07T09:00", host-local or in --tz). Required unless --every is given (the cadence then sets the first fire). Mutually exclusive with --when.' },
|
|
976
976
|
{ kind: 'flag', name: 'every', type: 'string', required: false, constraint: 'Arm a recurring clock trigger on this cadence — a duration ("6h","30m") or a 5-field cron / @alias ("0 9 * * *","@daily"). Combine with --at to anchor the first fire, then recur normally. Min cadence 60s. With --when, this instead sets the PREDICATE EVALUATION cadence (required), not a repeating action.' },
|
|
977
977
|
{ kind: 'flag', name: 'when', type: 'string', required: false, constraint: 'Arm a daemon-evaluated bash predicate instead of a clock trigger: exit 0 fires the message once, non-zero leaves it armed for the next evaluation. Requires --every (eval cadence) and --timeout (wall-clock bound). Mutually exclusive with --at, --fresh, and --until.' },
|
|
978
|
-
{ kind: 'flag', name: 'until', type: 'string', required: false, constraint: 'Self-only: bind a deadline
|
|
978
|
+
{ kind: 'flag', name: 'until', type: 'string', required: false, constraint: 'Self-only: bind a deadline that decides your next action — you wake on the first inbox message, or at this time if none arrives (whichever fires first cancels the other), and the body is the policy to follow in the no-message case. One-shot, urgent tier, ≤ 1 per node (a new --until replaces any prior). Mutually exclusive with --at, --every, --when, --fresh, --tier, and --timeout. Requires --self and a body.' },
|
|
979
979
|
{ kind: 'flag', name: 'timeout', type: 'string', required: false, constraint: 'Wall-clock bound for a --when predicate — a duration or absolute time after which the trigger is consumed and the owner gets one urgent notice. Required with --when; rejected otherwise.' },
|
|
980
980
|
{ kind: 'flag', name: 'tz', type: 'string', required: false, constraint: 'IANA zone for a calendar --every, a bare-ISO --at, or a bare-ISO --until (default: host-local). Valid only alongside --at/--every/--until; rejected with --when, and rejected when acting immediately (no trigger flags) since none of those have anything to parse it against.' },
|
|
981
981
|
],
|
|
@@ -1225,8 +1225,9 @@ const nodeMsg = defineLeaf({
|
|
|
1225
1225
|
}
|
|
1226
1226
|
const tier = (hasTier ? tierRaw : 'normal');
|
|
1227
1227
|
const from = process.env['CRTR_NODE_ID'] ?? 'human';
|
|
1228
|
-
// A
|
|
1229
|
-
//
|
|
1228
|
+
// A body over the preview bound spills to a ref file; appendInbox clips the
|
|
1229
|
+
// inline preview and the receiver dereferences the ref for the full text,
|
|
1230
|
+
// same as any other report.
|
|
1230
1231
|
appendInbox(targetId, { from, tier, kind: 'message', label: body.split('\n')[0].slice(0, 120), data: { body: body } });
|
|
1231
1232
|
// A direct message wakes any node: if the target has no live window
|
|
1232
1233
|
// (done/dead/idle-released), revive it so its inbox-watcher delivers this.
|
|
@@ -1510,7 +1511,7 @@ const nodeYield = defineLeaf({
|
|
|
1510
1511
|
const nodeTriggersList = defineLeaf({
|
|
1511
1512
|
name: 'list',
|
|
1512
1513
|
description: 'list pending triggers for a scope (default self)',
|
|
1513
|
-
whenToUse: '
|
|
1514
|
+
whenToUse: 'seeing what triggers (clock or predicate) are currently armed — your own, another node\'s, or the whole canvas. Defaults to your own triggers, with a --node or --canvas scope for anything wider',
|
|
1514
1515
|
help: {
|
|
1515
1516
|
name: 'node triggers list',
|
|
1516
1517
|
summary: 'list pending triggers (clock and predicate, incl. deferred spawns) for a scope — id, owner, target, action/mode, type, next fire/eval, cadence, timeout, label, predicate diagnostics. Fired one-shots are gone; a recurrence shows its NEXT fire/eval, not past slots',
|
|
@@ -1602,7 +1603,7 @@ const nodeTriggersList = defineLeaf({
|
|
|
1602
1603
|
const nodeTriggersCancel = defineLeaf({
|
|
1603
1604
|
name: 'cancel',
|
|
1604
1605
|
description: 'cancel a pending trigger by id (idempotent)',
|
|
1605
|
-
whenToUse: 'a wait you no longer need — a poll whose goal is met, a deferred spawn you reconsidered, a deadline you are replacing. Idempotent: canceling an already-fired or already-canceled id is a no-op.
|
|
1606
|
+
whenToUse: 'a wait you no longer need — a poll whose goal is met, a deferred spawn you reconsidered, a deadline you are replacing. Idempotent: canceling an already-fired or already-canceled id is a no-op.',
|
|
1606
1607
|
help: {
|
|
1607
1608
|
name: 'node triggers cancel',
|
|
1608
1609
|
summary: 'cancel a pending trigger by id — it never fires/evaluates again and leaves the list. Idempotent (canceling an already-gone id is a no-op, not an error)',
|
package/dist/commands/push.d.ts
CHANGED
|
@@ -2,4 +2,3 @@ import type { BranchDef } from '../core/command.js';
|
|
|
2
2
|
import { InputError } from '../core/io.js';
|
|
3
3
|
export declare function mapFinalizeTransitionRaceError(nodeStatus: string | null | undefined, err: unknown): InputError | null;
|
|
4
4
|
export declare function registerPush(): BranchDef;
|
|
5
|
-
export declare function registerFeed(): BranchDef;
|