@crouton-kit/crouter 0.3.13 → 0.3.15
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.d.ts +8 -0
- package/dist/build-root.js +30 -0
- package/dist/builtin-personas/design/base.md +3 -7
- package/dist/builtin-personas/design/orchestrator.md +4 -3
- package/dist/builtin-personas/developer/base.md +3 -7
- package/dist/builtin-personas/developer/orchestrator.md +5 -4
- package/dist/builtin-personas/explore/base.md +3 -7
- package/dist/builtin-personas/explore/orchestrator.md +1 -5
- package/dist/builtin-personas/general/base.md +2 -4
- package/dist/builtin-personas/general/orchestrator.md +2 -4
- package/dist/builtin-personas/lifecycle/resident.md +2 -0
- package/dist/builtin-personas/lifecycle/terminal.md +6 -0
- package/dist/builtin-personas/orchestration-kernel.md +42 -3
- package/dist/builtin-personas/plan/base.md +3 -5
- package/dist/builtin-personas/plan/orchestrator.md +5 -4
- package/dist/builtin-personas/plan/reviewers/architecture-fit/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/code-smells/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/security/base.md +9 -0
- package/dist/builtin-personas/review/base.md +3 -5
- package/dist/builtin-personas/review/orchestrator.md +2 -6
- package/dist/builtin-personas/runtime-base.md +3 -19
- package/dist/builtin-personas/spec/base.md +3 -5
- package/dist/builtin-personas/spec/orchestrator.md +4 -3
- package/dist/builtin-personas/spine/has-manager.md +10 -0
- package/dist/builtin-personas/spine/no-manager.md +2 -0
- package/dist/builtin-skills/skills/crouter-development/personas/SKILL.md +96 -0
- package/dist/builtin-skills/skills/crouter-development/personas/base-prompt/SKILL.md +49 -0
- package/dist/builtin-skills/skills/crouter-development/personas/orchestrator-prompt/SKILL.md +49 -0
- package/dist/builtin-skills/skills/planning/SKILL.md +1 -1
- package/dist/builtin-skills/skills/spec/SKILL.md +2 -2
- package/dist/cli.js +6 -29
- package/dist/commands/__tests__/human.test.js +73 -2
- package/dist/commands/attention.js +76 -7
- package/dist/commands/canvas-prune.d.ts +2 -0
- package/dist/commands/canvas-prune.js +66 -0
- package/dist/commands/canvas.js +5 -8
- package/dist/commands/chord.d.ts +2 -0
- package/dist/commands/chord.js +143 -0
- package/dist/commands/daemon.js +8 -5
- package/dist/commands/dashboard.js +2 -0
- package/dist/commands/human/prompts.js +28 -27
- package/dist/commands/human/queue.d.ts +1 -0
- package/dist/commands/human/queue.js +105 -2
- package/dist/commands/human/shared.d.ts +28 -18
- package/dist/commands/human/shared.js +53 -60
- package/dist/commands/human.js +6 -14
- package/dist/commands/node.d.ts +11 -0
- package/dist/commands/node.js +381 -87
- package/dist/commands/pkg/market-inspect.js +6 -4
- package/dist/commands/pkg/market-manage.js +10 -6
- package/dist/commands/pkg/market.js +2 -4
- package/dist/commands/pkg/plugin-inspect.js +6 -4
- package/dist/commands/pkg/plugin-manage.js +12 -7
- package/dist/commands/pkg/plugin.js +2 -4
- package/dist/commands/pkg.js +0 -4
- package/dist/commands/push.js +178 -15
- package/dist/commands/revive.js +5 -3
- package/dist/commands/skill/author.js +6 -4
- package/dist/commands/skill/find.js +8 -5
- package/dist/commands/skill/read.js +2 -0
- package/dist/commands/skill/state.js +6 -4
- package/dist/commands/skill.js +0 -6
- package/dist/commands/sys/config.js +21 -7
- package/dist/commands/sys/doctor.js +2 -0
- package/dist/commands/sys/update.js +4 -0
- package/dist/commands/sys.js +0 -6
- package/dist/commands/tmux-spread.d.ts +2 -0
- package/dist/commands/tmux-spread.js +130 -0
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +25 -0
- package/dist/core/__tests__/child-followup.test.d.ts +1 -0
- package/dist/core/__tests__/child-followup.test.js +83 -0
- package/dist/core/__tests__/close.test.d.ts +1 -0
- package/dist/core/__tests__/close.test.js +148 -0
- package/dist/core/__tests__/context-intro.test.d.ts +1 -0
- package/dist/core/__tests__/context-intro.test.js +196 -0
- package/dist/core/__tests__/daemon-boot.test.d.ts +1 -0
- package/dist/core/__tests__/daemon-boot.test.js +93 -0
- package/dist/core/__tests__/daemon-liveness.test.d.ts +1 -0
- package/dist/core/__tests__/daemon-liveness.test.js +223 -0
- package/dist/core/__tests__/focuses.test.d.ts +1 -0
- package/dist/core/__tests__/focuses.test.js +259 -0
- package/dist/core/__tests__/fork.test.d.ts +1 -0
- package/dist/core/__tests__/fork.test.js +91 -0
- package/dist/core/__tests__/home-session.test.d.ts +1 -0
- package/dist/core/__tests__/home-session.test.js +153 -0
- package/dist/core/__tests__/human-cancel-guard.test.d.ts +1 -0
- package/dist/core/__tests__/human-cancel-guard.test.js +49 -0
- package/dist/core/__tests__/keystone.test.d.ts +1 -0
- package/dist/core/__tests__/keystone.test.js +185 -0
- package/dist/core/__tests__/kickoff.test.d.ts +1 -0
- package/dist/core/__tests__/kickoff.test.js +89 -0
- package/dist/core/__tests__/lifecycle.test.d.ts +1 -0
- package/dist/core/__tests__/lifecycle.test.js +178 -0
- package/dist/core/__tests__/listing-completeness.test.d.ts +1 -0
- package/dist/core/__tests__/listing-completeness.test.js +31 -0
- package/dist/core/__tests__/memory.test.d.ts +1 -0
- package/dist/core/__tests__/memory.test.js +152 -0
- package/dist/core/__tests__/migration.test.d.ts +1 -0
- package/dist/core/__tests__/migration.test.js +238 -0
- package/dist/core/__tests__/pane-column.test.d.ts +1 -0
- package/dist/core/__tests__/pane-column.test.js +153 -0
- package/dist/core/__tests__/passive-subscription.test.d.ts +1 -0
- package/dist/core/__tests__/passive-subscription.test.js +164 -0
- package/dist/core/__tests__/persona-compose.test.d.ts +1 -0
- package/dist/core/__tests__/persona-compose.test.js +53 -0
- package/dist/core/__tests__/persona-subkind.test.d.ts +1 -0
- package/dist/core/__tests__/persona-subkind.test.js +62 -0
- package/dist/core/__tests__/persona.test.d.ts +1 -0
- package/dist/core/__tests__/persona.test.js +107 -0
- package/dist/core/__tests__/placement-focus.test.d.ts +1 -0
- package/dist/core/__tests__/placement-focus.test.js +244 -0
- package/dist/core/__tests__/placement-reconcile.test.d.ts +1 -0
- package/dist/core/__tests__/placement-reconcile.test.js +212 -0
- package/dist/core/__tests__/placement-revive.test.d.ts +1 -0
- package/dist/core/__tests__/placement-revive.test.js +238 -0
- package/dist/core/__tests__/placement-teardown.test.d.ts +1 -0
- package/dist/core/__tests__/placement-teardown.test.js +183 -0
- package/dist/core/__tests__/prune.test.d.ts +1 -0
- package/dist/core/__tests__/prune.test.js +116 -0
- package/dist/core/__tests__/push-final-guard.test.d.ts +1 -0
- package/dist/core/__tests__/push-final-guard.test.js +71 -0
- package/dist/core/__tests__/relaunch.test.d.ts +1 -0
- package/dist/core/__tests__/relaunch.test.js +328 -0
- package/dist/core/__tests__/reset.test.js +26 -7
- package/dist/core/__tests__/revive.test.d.ts +1 -0
- package/dist/core/__tests__/revive.test.js +217 -0
- package/dist/core/__tests__/spawn-root.test.d.ts +1 -0
- package/dist/core/__tests__/spawn-root.test.js +73 -0
- package/dist/core/__tests__/steer-note.test.d.ts +1 -0
- package/dist/core/__tests__/steer-note.test.js +39 -0
- package/dist/core/__tests__/stop-guard.test.d.ts +1 -0
- package/dist/core/__tests__/stop-guard.test.js +82 -0
- package/dist/core/__tests__/subcommand-tier.test.d.ts +1 -0
- package/dist/core/__tests__/subcommand-tier.test.js +99 -0
- package/dist/core/__tests__/tmux-surface.test.d.ts +1 -0
- package/dist/core/__tests__/tmux-surface.test.js +106 -0
- package/dist/core/__tests__/unknown-path.test.js +8 -2
- package/dist/core/canvas/attention.d.ts +10 -0
- package/dist/core/canvas/attention.js +40 -0
- package/dist/core/canvas/canvas.d.ts +66 -7
- package/dist/core/canvas/canvas.js +209 -21
- package/dist/core/canvas/db.d.ts +8 -0
- package/dist/core/canvas/db.js +206 -4
- package/dist/core/canvas/focuses.d.ts +22 -0
- package/dist/core/canvas/focuses.js +80 -0
- package/dist/core/canvas/index.d.ts +3 -0
- package/dist/core/canvas/index.js +3 -0
- package/dist/core/canvas/labels.d.ts +27 -0
- package/dist/core/canvas/labels.js +36 -0
- package/dist/core/canvas/paths.d.ts +4 -0
- package/dist/core/canvas/paths.js +6 -0
- package/dist/core/canvas/render.js +25 -10
- package/dist/core/canvas/telemetry.d.ts +14 -0
- package/dist/core/canvas/telemetry.js +35 -0
- package/dist/core/canvas/types.d.ts +115 -12
- package/dist/core/command.d.ts +25 -1
- package/dist/core/command.js +48 -7
- package/dist/core/config.js +36 -2
- package/dist/core/feed/feed.js +14 -12
- package/dist/core/feed/inbox.d.ts +3 -1
- package/dist/core/feed/inbox.js +45 -5
- package/dist/core/feed/passive.d.ts +17 -0
- package/dist/core/feed/passive.js +92 -0
- package/dist/core/help.d.ts +59 -13
- package/dist/core/help.js +73 -28
- package/dist/core/personas/index.d.ts +1 -1
- package/dist/core/personas/index.js +1 -1
- package/dist/core/personas/loader.d.ts +40 -1
- package/dist/core/personas/loader.js +63 -1
- package/dist/core/personas/resolve.d.ts +13 -6
- package/dist/core/personas/resolve.js +46 -34
- package/dist/core/runtime/bearings.d.ts +20 -0
- package/dist/core/runtime/bearings.js +92 -0
- package/dist/core/runtime/close.d.ts +14 -0
- package/dist/core/runtime/close.js +151 -0
- package/dist/core/runtime/demote.d.ts +14 -0
- package/dist/core/runtime/demote.js +120 -0
- package/dist/core/runtime/front-door.js +1 -1
- package/dist/core/runtime/kickoff.d.ts +32 -6
- package/dist/core/runtime/kickoff.js +111 -37
- package/dist/core/runtime/launch.d.ts +29 -6
- package/dist/core/runtime/launch.js +85 -13
- package/dist/core/runtime/lifecycle.d.ts +13 -0
- package/dist/core/runtime/lifecycle.js +86 -0
- package/dist/core/runtime/memory.d.ts +43 -0
- package/dist/core/runtime/memory.js +165 -0
- package/dist/core/runtime/naming.d.ts +22 -0
- package/dist/core/runtime/naming.js +166 -0
- package/dist/core/runtime/nodes.d.ts +32 -1
- package/dist/core/runtime/nodes.js +60 -10
- package/dist/core/runtime/persona.d.ts +25 -0
- package/dist/core/runtime/persona.js +139 -0
- package/dist/core/runtime/placement.d.ts +287 -0
- package/dist/core/runtime/placement.js +663 -0
- package/dist/core/runtime/presence.d.ts +7 -32
- package/dist/core/runtime/presence.js +90 -110
- package/dist/core/runtime/promote.d.ts +18 -7
- package/dist/core/runtime/promote.js +70 -65
- package/dist/core/runtime/reset.d.ts +47 -4
- package/dist/core/runtime/reset.js +223 -52
- package/dist/core/runtime/revive.d.ts +26 -2
- package/dist/core/runtime/revive.js +166 -39
- package/dist/core/runtime/roadmap.d.ts +5 -4
- package/dist/core/runtime/roadmap.js +9 -16
- package/dist/core/runtime/spawn.d.ts +20 -5
- package/dist/core/runtime/spawn.js +169 -44
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +18 -8
- package/dist/core/runtime/tmux.d.ts +106 -21
- package/dist/core/runtime/tmux.js +249 -45
- package/dist/core/spawn.js +15 -0
- package/dist/daemon/crtrd.d.ts +12 -1
- package/dist/daemon/crtrd.js +152 -34
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +266 -0
- package/dist/pi-extensions/canvas-commands.d.ts +34 -0
- package/dist/pi-extensions/canvas-commands.js +103 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +70 -0
- package/dist/pi-extensions/canvas-context-intro.js +164 -0
- package/dist/pi-extensions/canvas-goal-capture.d.ts +21 -0
- package/dist/pi-extensions/canvas-goal-capture.js +67 -0
- package/dist/pi-extensions/canvas-inbox-watcher.js +11 -0
- package/dist/pi-extensions/canvas-nav.d.ts +12 -4
- package/dist/pi-extensions/canvas-nav.js +586 -262
- package/dist/pi-extensions/canvas-passive-context.d.ts +32 -0
- package/dist/pi-extensions/canvas-passive-context.js +114 -0
- package/dist/pi-extensions/canvas-stophook.d.ts +16 -0
- package/dist/pi-extensions/canvas-stophook.js +344 -228
- package/dist/types.d.ts +28 -0
- package/dist/types.js +16 -0
- package/package.json +1 -1
|
@@ -8,6 +8,8 @@ import { resolveScopeArg, projectScopeRoot } from '../../core/scope.js';
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
const marketList = defineLeaf({
|
|
10
10
|
name: 'list',
|
|
11
|
+
description: 'list registered marketplaces',
|
|
12
|
+
whenToUse: 'listing which marketplaces are registered, with their git URL, ref, and scope',
|
|
11
13
|
help: {
|
|
12
14
|
name: 'pkg market inspect list',
|
|
13
15
|
summary: 'list registered marketplaces',
|
|
@@ -76,6 +78,8 @@ const marketList = defineLeaf({
|
|
|
76
78
|
// ---------------------------------------------------------------------------
|
|
77
79
|
const marketBrowse = defineLeaf({
|
|
78
80
|
name: 'browse',
|
|
81
|
+
description: 'list plugins available in a marketplace',
|
|
82
|
+
whenToUse: 'exploring what a marketplace offers so you can decide before installing — lists every plugin in a marketplace index with its description, keywords, version, and whether it is already installed. Reach for this to pick which plugin to pull, then install it by name with `pkg market manage install`',
|
|
79
83
|
help: {
|
|
80
84
|
name: 'pkg market inspect browse',
|
|
81
85
|
summary: 'list plugins available in a marketplace',
|
|
@@ -145,13 +149,11 @@ const marketBrowse = defineLeaf({
|
|
|
145
149
|
});
|
|
146
150
|
export const marketInspectBranch = defineBranch({
|
|
147
151
|
name: 'inspect',
|
|
152
|
+
description: 'list or browse marketplaces',
|
|
153
|
+
whenToUse: 'reading marketplace metadata to decide before you install — list registered marketplaces, or browse the plugins available in one marketplace. Read-only; switch to `pkg market manage` to add a marketplace or install a plugin from it',
|
|
148
154
|
help: {
|
|
149
155
|
name: 'pkg market inspect',
|
|
150
156
|
summary: 'read marketplace metadata without modifying state',
|
|
151
|
-
children: [
|
|
152
|
-
{ name: 'list', desc: 'list registered marketplaces', useWhen: 'seeing which marketplaces are configured' },
|
|
153
|
-
{ name: 'browse', desc: 'list plugins available in a marketplace', useWhen: 'exploring what a marketplace offers before installing' },
|
|
154
|
-
],
|
|
155
157
|
},
|
|
156
158
|
children: [marketList, marketBrowse],
|
|
157
159
|
});
|
|
@@ -14,6 +14,8 @@ import { resolveInstallScope } from './shared.js';
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
const marketAdd = defineLeaf({
|
|
16
16
|
name: 'add',
|
|
17
|
+
description: 'add a marketplace by git URL',
|
|
18
|
+
whenToUse: 'registering a new marketplace by git URL so its plugins become installable by name',
|
|
17
19
|
help: {
|
|
18
20
|
name: 'pkg market manage add',
|
|
19
21
|
summary: 'add a marketplace by git URL',
|
|
@@ -74,6 +76,8 @@ const marketAdd = defineLeaf({
|
|
|
74
76
|
// ---------------------------------------------------------------------------
|
|
75
77
|
const marketRemove = defineLeaf({
|
|
76
78
|
name: 'remove',
|
|
79
|
+
description: 'remove a marketplace',
|
|
80
|
+
whenToUse: 'unregistering a marketplace: deletes it and any plugins installed from it',
|
|
77
81
|
help: {
|
|
78
82
|
name: 'pkg market manage remove',
|
|
79
83
|
summary: 'remove a marketplace and its directory',
|
|
@@ -148,6 +152,8 @@ const marketRemove = defineLeaf({
|
|
|
148
152
|
// ---------------------------------------------------------------------------
|
|
149
153
|
const marketUpdate = defineLeaf({
|
|
150
154
|
name: 'update',
|
|
155
|
+
description: 'pull latest marketplace index',
|
|
156
|
+
whenToUse: 'refreshing the plugin index of one named marketplace from git, or of all registered marketplaces when no name is given',
|
|
151
157
|
help: {
|
|
152
158
|
name: 'pkg market manage update',
|
|
153
159
|
summary: 'git pull updates for one or all registered marketplaces',
|
|
@@ -234,6 +240,8 @@ const marketUpdate = defineLeaf({
|
|
|
234
240
|
// ---------------------------------------------------------------------------
|
|
235
241
|
const marketInstall = defineLeaf({
|
|
236
242
|
name: 'install',
|
|
243
|
+
description: 'install a plugin from a marketplace',
|
|
244
|
+
whenToUse: 'installing a plugin by name from an already-registered marketplace, e.g. `pkg market manage install --marketplace <m> --plugin <p>`',
|
|
237
245
|
help: {
|
|
238
246
|
name: 'pkg market manage install',
|
|
239
247
|
summary: 'install a plugin from an added marketplace by plugin name',
|
|
@@ -302,15 +310,11 @@ const marketInstall = defineLeaf({
|
|
|
302
310
|
});
|
|
303
311
|
export const marketManageBranch = defineBranch({
|
|
304
312
|
name: 'manage',
|
|
313
|
+
description: 'add, remove, update, install',
|
|
314
|
+
whenToUse: 'changing marketplace state: add or remove a marketplace, refresh its index, or install a plugin from it',
|
|
305
315
|
help: {
|
|
306
316
|
name: 'pkg market manage',
|
|
307
317
|
summary: 'add, remove, update, or install from marketplaces',
|
|
308
|
-
children: [
|
|
309
|
-
{ name: 'add', desc: 'add a marketplace by git URL', useWhen: 'registering a new marketplace source' },
|
|
310
|
-
{ name: 'remove', desc: 'remove a marketplace', useWhen: 'unregistering a marketplace' },
|
|
311
|
-
{ name: 'update', desc: 'pull latest marketplace index', useWhen: 'refreshing the marketplace plugin list' },
|
|
312
|
-
{ name: 'install', desc: 'install a plugin from a marketplace', useWhen: 'adding a plugin sourced from a registered marketplace' },
|
|
313
|
-
],
|
|
314
318
|
},
|
|
315
319
|
children: [marketAdd, marketRemove, marketUpdate, marketInstall],
|
|
316
320
|
});
|
|
@@ -3,14 +3,12 @@ import { marketManageBranch } from './market-manage.js';
|
|
|
3
3
|
import { marketInspectBranch } from './market-inspect.js';
|
|
4
4
|
export const marketBranch = defineBranch({
|
|
5
5
|
name: 'market',
|
|
6
|
+
description: 'manage marketplace sources and install from them',
|
|
7
|
+
whenToUse: 'using curated plugin collections instead of raw git URLs — register a marketplace, browse its index of plugins, then install them by name. Use `pkg plugin` instead when you already have a specific git URL or local plugin to install directly, or when inspecting and toggling plugins that are already installed',
|
|
6
8
|
help: {
|
|
7
9
|
name: 'pkg market',
|
|
8
10
|
summary: 'manage and browse plugin marketplaces',
|
|
9
11
|
model: 'Marketplaces are git repos containing a .crouter-marketplace/marketplace.json index of plugins.',
|
|
10
|
-
children: [
|
|
11
|
-
{ name: 'manage', desc: 'add, remove, update, install', useWhen: 'changing marketplace or marketplace-sourced plugin state' },
|
|
12
|
-
{ name: 'inspect', desc: 'list or browse marketplaces', useWhen: 'reading marketplace metadata' },
|
|
13
|
-
],
|
|
14
12
|
},
|
|
15
13
|
children: [marketManageBranch, marketInspectBranch],
|
|
16
14
|
});
|
|
@@ -8,6 +8,8 @@ import { resolveScopeArg, projectScopeRoot } from '../../core/scope.js';
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
const pluginList = defineLeaf({
|
|
10
10
|
name: 'list',
|
|
11
|
+
description: 'paginated list of installed plugins',
|
|
12
|
+
whenToUse: 'enumerating which plugins are installed across user and project scope, optionally including disabled ones',
|
|
11
13
|
help: {
|
|
12
14
|
name: 'pkg plugin inspect list',
|
|
13
15
|
summary: 'paginated list of installed plugins',
|
|
@@ -76,6 +78,8 @@ const pluginList = defineLeaf({
|
|
|
76
78
|
// ---------------------------------------------------------------------------
|
|
77
79
|
const pluginShow = defineLeaf({
|
|
78
80
|
name: 'show',
|
|
81
|
+
description: 'read plugin manifest and skill inventory',
|
|
82
|
+
whenToUse: 'reading one installed plugin in detail to decide before you enable, disable, or remove it — returns the full manifest, the skills it provides, its scope, and whether it is currently enabled. Use `pkg plugin inspect list` instead to enumerate plugins rather than drill into one',
|
|
79
83
|
help: {
|
|
80
84
|
name: 'pkg plugin inspect show',
|
|
81
85
|
summary: 'read plugin manifest and metadata by name',
|
|
@@ -130,13 +134,11 @@ const pluginShow = defineLeaf({
|
|
|
130
134
|
});
|
|
131
135
|
export const pluginInspectBranch = defineBranch({
|
|
132
136
|
name: 'inspect',
|
|
137
|
+
description: 'list or show installed plugins',
|
|
138
|
+
whenToUse: 'reading installed plugin metadata to decide before you change anything — list what is installed, or show the manifest and skills of one plugin. Read-only; switch to `pkg plugin manage` to actually install, enable, disable, or remove',
|
|
133
139
|
help: {
|
|
134
140
|
name: 'pkg plugin inspect',
|
|
135
141
|
summary: 'read plugin metadata without modifying state',
|
|
136
|
-
children: [
|
|
137
|
-
{ name: 'list', desc: 'paginated list of installed plugins', useWhen: 'enumerating what plugins are installed' },
|
|
138
|
-
{ name: 'show', desc: 'read plugin manifest and skill inventory', useWhen: 'inspecting a specific plugin\'s details' },
|
|
139
|
-
],
|
|
140
142
|
},
|
|
141
143
|
children: [pluginList, pluginShow],
|
|
142
144
|
});
|
|
@@ -14,6 +14,8 @@ import { isGitUrl, setPluginEnabled, resolveInstallScope } from './shared.js';
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
const pluginInstall = defineLeaf({
|
|
16
16
|
name: 'install',
|
|
17
|
+
description: 'install from a git URL',
|
|
18
|
+
whenToUse: 'installing a plugin directly from a git URL into a scope, e.g. `pkg plugin manage install https://github.com/org/repo`',
|
|
17
19
|
help: {
|
|
18
20
|
name: 'pkg plugin manage install',
|
|
19
21
|
summary: 'install a plugin from a git URL into the given scope',
|
|
@@ -86,6 +88,8 @@ const pluginInstall = defineLeaf({
|
|
|
86
88
|
// ---------------------------------------------------------------------------
|
|
87
89
|
const pluginRemove = defineLeaf({
|
|
88
90
|
name: 'remove',
|
|
91
|
+
description: 'remove plugin and directory',
|
|
92
|
+
whenToUse: 'uninstalling a plugin: deletes its directory and removes it from config',
|
|
89
93
|
help: {
|
|
90
94
|
name: 'pkg plugin manage remove',
|
|
91
95
|
summary: 'remove a plugin and its directory from the given scope',
|
|
@@ -141,6 +145,8 @@ const pluginRemove = defineLeaf({
|
|
|
141
145
|
// ---------------------------------------------------------------------------
|
|
142
146
|
const pluginEnable = defineLeaf({
|
|
143
147
|
name: 'enable',
|
|
148
|
+
description: 'enable a plugin',
|
|
149
|
+
whenToUse: 'reactivating a plugin that was previously disabled so its skills resolve again',
|
|
144
150
|
help: {
|
|
145
151
|
name: 'pkg plugin manage enable',
|
|
146
152
|
summary: 'enable a plugin in the given scope',
|
|
@@ -160,6 +166,8 @@ const pluginEnable = defineLeaf({
|
|
|
160
166
|
});
|
|
161
167
|
const pluginDisable = defineLeaf({
|
|
162
168
|
name: 'disable',
|
|
169
|
+
description: 'disable without removing',
|
|
170
|
+
whenToUse: 'hiding a plugin from resolution without deleting it, so you can re-enable it later',
|
|
163
171
|
help: {
|
|
164
172
|
name: 'pkg plugin manage disable',
|
|
165
173
|
summary: 'disable a plugin (keeps files, hides from resolution)',
|
|
@@ -183,6 +191,8 @@ const pluginDisable = defineLeaf({
|
|
|
183
191
|
// ---------------------------------------------------------------------------
|
|
184
192
|
const pluginUpdate = defineLeaf({
|
|
185
193
|
name: 'update',
|
|
194
|
+
description: 'pull latest from git',
|
|
195
|
+
whenToUse: 'pulling the latest git version of one named plugin, or of all installed plugins when no name is given',
|
|
186
196
|
help: {
|
|
187
197
|
name: 'pkg plugin manage update',
|
|
188
198
|
summary: 'git pull updates for one or all installed plugins',
|
|
@@ -279,16 +289,11 @@ const pluginUpdate = defineLeaf({
|
|
|
279
289
|
});
|
|
280
290
|
export const pluginManageBranch = defineBranch({
|
|
281
291
|
name: 'manage',
|
|
292
|
+
description: 'install, remove, enable, disable, update',
|
|
293
|
+
whenToUse: 'changing plugin state: install, remove, enable, disable, or update installed plugins',
|
|
282
294
|
help: {
|
|
283
295
|
name: 'pkg plugin manage',
|
|
284
296
|
summary: 'install, remove, enable, disable, or update plugins',
|
|
285
|
-
children: [
|
|
286
|
-
{ name: 'install', desc: 'install from a git URL', useWhen: 'adding a new plugin' },
|
|
287
|
-
{ name: 'remove', desc: 'remove plugin and directory', useWhen: 'uninstalling a plugin' },
|
|
288
|
-
{ name: 'enable', desc: 'enable a plugin', useWhen: 'activating a disabled plugin' },
|
|
289
|
-
{ name: 'disable', desc: 'disable without removing', useWhen: 'temporarily hiding a plugin' },
|
|
290
|
-
{ name: 'update', desc: 'pull latest from git', useWhen: 'updating a plugin to its latest version' },
|
|
291
|
-
],
|
|
292
297
|
},
|
|
293
298
|
children: [pluginInstall, pluginRemove, pluginEnable, pluginDisable, pluginUpdate],
|
|
294
299
|
});
|
|
@@ -3,14 +3,12 @@ import { pluginManageBranch } from './plugin-manage.js';
|
|
|
3
3
|
import { pluginInspectBranch } from './plugin-inspect.js';
|
|
4
4
|
export const pluginBranch = defineBranch({
|
|
5
5
|
name: 'plugin',
|
|
6
|
+
description: 'install and manage plugins',
|
|
7
|
+
whenToUse: 'working with individual plugins directly — installing one from a git URL, inspecting or showing what a plugin provides, or enabling, disabling, removing, and updating an installed plugin. Use `pkg market` instead when you want a curated collection: browsing a marketplace index and installing plugins by name from it rather than handling raw git URLs yourself',
|
|
6
8
|
help: {
|
|
7
9
|
name: 'pkg plugin',
|
|
8
10
|
summary: 'install and manage plugins that extend crtr with skills',
|
|
9
11
|
model: 'Plugins are git repos or local directories containing a .crouter-plugin/plugin.json manifest and a skills/ directory.',
|
|
10
|
-
children: [
|
|
11
|
-
{ name: 'manage', desc: 'install, remove, enable, disable, update', useWhen: 'changing plugin state' },
|
|
12
|
-
{ name: 'inspect', desc: 'list or show installed plugins', useWhen: 'reading plugin metadata' },
|
|
13
|
-
],
|
|
14
12
|
},
|
|
15
13
|
children: [pluginManageBranch, pluginInspectBranch],
|
|
16
14
|
});
|
package/dist/commands/pkg.js
CHANGED
|
@@ -15,10 +15,6 @@ export function registerPkg() {
|
|
|
15
15
|
help: {
|
|
16
16
|
name: 'pkg',
|
|
17
17
|
summary: 'manage plugins and plugin marketplaces',
|
|
18
|
-
children: [
|
|
19
|
-
{ name: 'plugin', desc: 'install and manage plugins', useWhen: 'working with individual plugins directly' },
|
|
20
|
-
{ name: 'market', desc: 'manage marketplace sources and install from them', useWhen: 'using curated plugin collections' },
|
|
21
|
-
],
|
|
22
18
|
},
|
|
23
19
|
children: [pluginBranch, marketBranch],
|
|
24
20
|
});
|
package/dist/commands/push.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// `crtr push` + `crtr feed` — the one verb up the graph, and its read side.
|
|
2
2
|
//
|
|
3
|
-
// crtr push update [body] — routine progress
|
|
3
|
+
// crtr push update [body] — routine progress to subscribers
|
|
4
4
|
// crtr push urgent [body] — force-wake subscribers
|
|
5
5
|
// crtr push final [body] — finish: write result, mark node done, close window
|
|
6
6
|
// crtr feed read — drain the caller's (or a named) inbox into a digest
|
|
7
|
+
// crtr feed peek — live state of the nodes below you, cursor untouched
|
|
7
8
|
//
|
|
8
9
|
// "push" is THE verb a node uses to talk to its managers; the tier is the
|
|
9
10
|
// subcommand. The caller's node is resolved from CRTR_NODE_ID (injected by the
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
import { defineBranch, defineLeaf } from '../core/command.js';
|
|
13
14
|
import { InputError, readStdinRaw } from '../core/io.js';
|
|
14
15
|
import { push } from '../core/feed/feed.js';
|
|
16
|
+
import { getNode, subscribersOf, subscriptionsOf, fullName } from '../core/canvas/index.js';
|
|
15
17
|
import { readInboxSince, readCursor, writeCursor, coalesce, } from '../core/feed/inbox.js';
|
|
16
18
|
function requireCallerNode() {
|
|
17
19
|
const id = process.env['CRTR_NODE_ID'];
|
|
@@ -29,14 +31,24 @@ const TIER_BLURB = {
|
|
|
29
31
|
urgent: 'force-wake subscribers (inbox tier urgent)',
|
|
30
32
|
final: 'finish: write the canonical result, mark the node done, close its window',
|
|
31
33
|
};
|
|
34
|
+
const TIER_WHENTOUSE = {
|
|
35
|
+
update: 'you have routine progress worth surfacing to your managers but nothing that needs them to act right now — a checkpoint, a finished sub-step, a status note while you keep working, a heads-up on a decision you made. Fans a lightweight pointer to every subscriber without forcing a wake. Nothing is pushed automatically — your managers see only what you push explicitly, so reach for this whenever you want a progress signal to reach them. Use push urgent instead when a manager must see it immediately, push final when the work is actually done.',
|
|
36
|
+
urgent: 'something your managers must see and act on immediately — you are blocked and need a decision, you hit an error that derails the plan, a discovery changes the scope, or a child reported something that has to travel further up the chain now. Same report mechanism as push update, but it force-wakes every subscriber instead of waiting for them to drain their feed on their own time. Use push update instead for progress that can wait, push final when you are handing back a finished result rather than raising an alarm.',
|
|
37
|
+
final: 'the work this node was spawned to do is complete and you are ready to hand back the canonical result — this writes that result, marks the node done, and closes its window, so it is the LAST thing you do here, not a progress note. Any node finishes this way. Use push update or push urgent instead while work is still in flight, and do not reach for this on a node working directly with the user: it has no manager to report up to and would close mid-conversation (the guard blocks it unless you pass --force after the user confirms).',
|
|
38
|
+
};
|
|
32
39
|
function makeTierLeaf(tier) {
|
|
33
40
|
return defineLeaf({
|
|
34
41
|
name: tier,
|
|
42
|
+
description: TIER_BLURB[tier],
|
|
43
|
+
whenToUse: TIER_WHENTOUSE[tier],
|
|
35
44
|
help: {
|
|
36
45
|
name: `push ${tier}`,
|
|
37
46
|
summary: TIER_BLURB[tier],
|
|
38
47
|
params: [
|
|
39
|
-
{ kind: 'stdin', name: 'body', required: true, constraint:
|
|
48
|
+
{ kind: 'stdin', name: 'body', required: true, constraint: `Report body (markdown). Positional or stdin — use stdin/heredoc for large bodies (\`crtr push ${tier} <<'EOF' … EOF\`).` },
|
|
49
|
+
...(tier === 'final'
|
|
50
|
+
? [{ kind: 'flag', name: 'force', type: 'bool', required: false, default: false, constraint: 'Override the guard that blocks `push final` on a human-attended node (a resident with no one to report up to). Only pass this after the user explicitly confirms they want this node finished.' }]
|
|
51
|
+
: []),
|
|
40
52
|
],
|
|
41
53
|
output: [
|
|
42
54
|
{ name: 'report_path', type: 'string', required: true, constraint: 'Path of the written report.' },
|
|
@@ -58,6 +70,38 @@ function makeTierLeaf(tier) {
|
|
|
58
70
|
if (body === '') {
|
|
59
71
|
throw new InputError({ error: 'missing_body', message: 'no report body', field: 'body', next: 'Pass the body as an argument or on stdin.' });
|
|
60
72
|
}
|
|
73
|
+
if (tier === 'final') {
|
|
74
|
+
const node = getNode(nodeId);
|
|
75
|
+
// A 2nd `push final` in one turn (or finalizing an already dead/canceled
|
|
76
|
+
// node) is an illegal finalize-from-terminal: the underlying transition()
|
|
77
|
+
// throws a raw Error that surfaces as an `internal` "crtr bug" on stderr.
|
|
78
|
+
// Catch it here as a CLEAN user-facing error — the node is simply already
|
|
79
|
+
// finished, so there is nothing to do.
|
|
80
|
+
if (node !== null && node.status !== 'active' && node.status !== 'idle') {
|
|
81
|
+
throw new InputError({
|
|
82
|
+
error: 'already_finalized',
|
|
83
|
+
message: `This node is already ${node.status} — \`push final\` finishes a node once and cannot finalize it again.`,
|
|
84
|
+
next: 'Nothing to do here: the node is already finished. Do not push again from this node.',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// A RESIDENT node with no subscribers is human-driven and has no one to
|
|
88
|
+
// submit a canonical result to: `push final` fans to subscribers, and a
|
|
89
|
+
// resident root conversation has none. Finishing it would close its window
|
|
90
|
+
// mid-conversation. Block that unless the user confirms (--force). Keyed on
|
|
91
|
+
// lifecycle, NOT subscriber-count alone: a TERMINAL node with no subscribers
|
|
92
|
+
// was deliberately terminalized to owe a final — it self-completes here
|
|
93
|
+
// (records the result, reaps) rather than being blocked for lack of a recipient.
|
|
94
|
+
if (input['force'] !== true) {
|
|
95
|
+
const noRecipient = node !== null && node.lifecycle === 'resident' && subscribersOf(nodeId).length === 0;
|
|
96
|
+
if (noRecipient) {
|
|
97
|
+
throw new InputError({
|
|
98
|
+
error: 'no_final_recipient',
|
|
99
|
+
message: 'This node is working directly with the user — it has no manager to submit a final result to. `push final` would close its window mid-conversation.',
|
|
100
|
+
next: 'You almost certainly do NOT need to finish here — just keep working with the user. If the user has explicitly asked you to finish and close this node, confirm with them first, then rerun with `crtr push final --force "<result>"`.',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
61
105
|
const result = await push(nodeId, { kind: tier, body });
|
|
62
106
|
return { report_path: result.reportPath, delivered_to: result.deliveredTo, status: tier === 'final' ? 'done' : 'active' };
|
|
63
107
|
},
|
|
@@ -77,18 +121,21 @@ function makeTierLeaf(tier) {
|
|
|
77
121
|
// ---------------------------------------------------------------------------
|
|
78
122
|
const feedReadLeaf = defineLeaf({
|
|
79
123
|
name: 'read',
|
|
124
|
+
description: 'drain unread pointers into a digest',
|
|
125
|
+
whenToUse: 'you want to PROACTIVELY poll what the nodes you subscribe to — your children and anyone you follow — have reported before the watcher wakes you, draining the unread pointers in your inbox into one coalesced digest. NOTE: when a subscriber push wakes you, that wake message already IS this digest (the watcher drains your inbox to wake you), so don\'t re-run feed read to "open" it — dereference the refs in the digest you already have. Reach for read to poll before the next wake, to inspect a worker inbox via --node, or with --all to re-read the whole history (full message bodies included) after the cursor has advanced.',
|
|
80
126
|
help: {
|
|
81
127
|
name: 'feed read',
|
|
82
128
|
summary: 'drain unread inbox pointers for the caller (or a named node) into a compact digest',
|
|
83
129
|
params: [
|
|
84
130
|
{ kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Node whose inbox to read. Defaults to CRTR_NODE_ID. Use to inspect a worker\'s inbox as an orchestrator.' },
|
|
85
|
-
{ kind: 'flag', name: 'all', type: 'bool', required: false, default: false, constraint: 'Ignore the cursor and return everything from the start.' },
|
|
131
|
+
{ kind: 'flag', name: 'all', type: 'bool', required: false, default: false, constraint: 'Ignore the cursor and return everything from the start — use to re-read history the wake already drained.' },
|
|
86
132
|
],
|
|
87
133
|
output: [
|
|
88
134
|
{ name: 'node_id', type: 'string', required: true, constraint: 'Node whose inbox was read.' },
|
|
89
135
|
{ name: 'digest', type: 'string', required: true, constraint: 'Coalesced digest — paste directly into a prompt.' },
|
|
90
136
|
{ name: 'entries', type: 'object[]', required: true, constraint: 'Raw InboxEntry objects.' },
|
|
91
137
|
{ name: 'cursor', type: 'string', required: true, constraint: 'New cursor ISO written after draining.' },
|
|
138
|
+
{ name: 'inbox_total', type: 'number', required: true, constraint: 'Total entries in the inbox (read + unread). Distinguishes a never-used inbox from one already drained at wake.' },
|
|
92
139
|
],
|
|
93
140
|
outputKind: 'object',
|
|
94
141
|
effects: ['Advances nodes/<nodeId>/inbox.jsonl.cursor.', 'Read-only on inbox.jsonl itself.'],
|
|
@@ -99,6 +146,7 @@ const feedReadLeaf = defineLeaf({
|
|
|
99
146
|
: requireCallerNode();
|
|
100
147
|
const cursor = input['all'] === true ? undefined : readCursor(nodeId);
|
|
101
148
|
const entries = readInboxSince(nodeId, cursor);
|
|
149
|
+
const total = readInboxSince(nodeId, undefined).length;
|
|
102
150
|
const newCursor = entries.length > 0 ? entries[entries.length - 1].ts : cursor ?? new Date().toISOString();
|
|
103
151
|
writeCursor(nodeId, newCursor);
|
|
104
152
|
return {
|
|
@@ -106,17 +154,138 @@ const feedReadLeaf = defineLeaf({
|
|
|
106
154
|
digest: coalesce(entries),
|
|
107
155
|
entries: entries,
|
|
108
156
|
cursor: newCursor,
|
|
157
|
+
inbox_total: total,
|
|
109
158
|
};
|
|
110
159
|
},
|
|
111
160
|
render: (r) => {
|
|
112
161
|
const n = Array.isArray(r['entries']) ? r['entries'].length : 0;
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
162
|
+
const rawDigest = typeof r['digest'] === 'string' ? r['digest'] : '';
|
|
163
|
+
if (n > 0 && rawDigest.trim() !== '') {
|
|
164
|
+
return `<feed node="${r['node_id']}" unread="${n}">\n${rawDigest}\n</feed>`;
|
|
165
|
+
}
|
|
166
|
+
// Empty drain has two distinct causes — say which, honestly. An inbox that
|
|
167
|
+
// already holds entries was drained when the watcher woke you (the wake
|
|
168
|
+
// message WAS that digest); a never-used inbox simply has nothing yet.
|
|
169
|
+
const total = typeof r['inbox_total'] === 'number' ? r['inbox_total'] : 0;
|
|
170
|
+
const digest = total > 0
|
|
171
|
+
? 'Nothing unread — but your inbox is not empty. The watcher drains your inbox to wake you, so the entries you already saw in your wake message are the same ones you would read here; that is why this is empty. Re-read the whole history (full message bodies included) with `crtr feed read --all`, or dereference the refs from the wake digest you already have.'
|
|
172
|
+
: 'Inbox empty — nothing has arrived from your subscriptions yet. Expected while workers run: a worker that has not pushed yet leaves no pointer, and it will wake you the moment it does. The wake is automatic — just continue your own work or end your turn. (Reach for `crtr feed peek` only if you suspect a worker died, not to confirm a live one.)';
|
|
116
173
|
return `<feed node="${r['node_id']}" unread="${n}">\n${digest}\n</feed>`;
|
|
117
174
|
},
|
|
118
175
|
});
|
|
119
176
|
// ---------------------------------------------------------------------------
|
|
177
|
+
// feed peek — live state of the nodes below you, without draining anything
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
const STATUS_GLYPH = {
|
|
180
|
+
active: '●', idle: '○', done: '✓', dead: '✗', canceled: '⊘',
|
|
181
|
+
};
|
|
182
|
+
/** Coarse "Nm ago" age from an ISO timestamp — enough to read staleness at a glance. */
|
|
183
|
+
function fmtAge(iso) {
|
|
184
|
+
const ms = Date.now() - new Date(iso).getTime();
|
|
185
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
186
|
+
return 'just now';
|
|
187
|
+
const s = Math.floor(ms / 1000);
|
|
188
|
+
if (s < 60)
|
|
189
|
+
return `${s}s ago`;
|
|
190
|
+
const m = Math.floor(s / 60);
|
|
191
|
+
if (m < 60)
|
|
192
|
+
return `${m}m ago`;
|
|
193
|
+
const h = Math.floor(m / 60);
|
|
194
|
+
if (h < 24)
|
|
195
|
+
return `${h}h ago`;
|
|
196
|
+
return `${Math.floor(h / 24)}d ago`;
|
|
197
|
+
}
|
|
198
|
+
const feedPeekLeaf = defineLeaf({
|
|
199
|
+
name: 'peek',
|
|
200
|
+
description: 'live state of the nodes below you, without draining anything',
|
|
201
|
+
whenToUse: 'you are about to end a turn and want to confirm your workers are running before you chill — peek shows every node you subscribe to (the workers below you) with its live status (working/idle/done/dead), how long it has run, its cycle count, and whether it has pushed yet, plus a one-line verdict on whether it is safe to yield. Non-destructive: it never advances your inbox cursor, so a later `feed read` still delivers undrained reports. Reach for it exactly when the feed reads empty but you have outstanding children — that empty feed is EXPECTED (a worker that has not pushed yet contributes no inbox pointer); peek confirms those workers are alive and running async so you can stop and wait instead of polling.',
|
|
202
|
+
help: {
|
|
203
|
+
name: 'feed peek',
|
|
204
|
+
summary: 'show the live state of every node you subscribe to (the workers below you) with a yield-or-not verdict; never drains the inbox',
|
|
205
|
+
params: [
|
|
206
|
+
{ kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Node whose subscriptions to peek. Defaults to CRTR_NODE_ID. Use to inspect a worker\'s downstream as an orchestrator.' },
|
|
207
|
+
],
|
|
208
|
+
output: [
|
|
209
|
+
{ name: 'node_id', type: 'string', required: true, constraint: 'Node that was peeked.' },
|
|
210
|
+
{ name: 'unread', type: 'number', required: true, constraint: 'Inbox pointers not yet drained by `feed read`.' },
|
|
211
|
+
{ name: 'children', type: 'object[]', required: true, constraint: 'One row per subscription: {node_id, kind, name, status, active, spawned, cycles, last_push}.' },
|
|
212
|
+
],
|
|
213
|
+
outputKind: 'object',
|
|
214
|
+
effects: ['Read-only: reads canvas.db edges + node metas + inbox.jsonl.', 'Does NOT advance the inbox cursor — peek leaves the feed intact for a later `feed read`.'],
|
|
215
|
+
},
|
|
216
|
+
run: async (input) => {
|
|
217
|
+
const nodeId = typeof input['node'] === 'string' && input['node'].trim() !== ''
|
|
218
|
+
? input['node'].trim()
|
|
219
|
+
: requireCallerNode();
|
|
220
|
+
// Read the WHOLE inbox to find each child's last push, but with no cursor
|
|
221
|
+
// write — peek is non-destructive by contract. `unread` is computed from the
|
|
222
|
+
// persisted cursor so peek can tell you a `feed read` would deliver something.
|
|
223
|
+
const all = readInboxSince(nodeId, undefined);
|
|
224
|
+
const unread = readInboxSince(nodeId, readCursor(nodeId)).length;
|
|
225
|
+
const children = subscriptionsOf(nodeId).map((s) => {
|
|
226
|
+
const n = getNode(s.node_id);
|
|
227
|
+
const fromMe = all.filter((e) => e.from === s.node_id);
|
|
228
|
+
const last = fromMe.length > 0 ? fromMe[fromMe.length - 1] : undefined;
|
|
229
|
+
return {
|
|
230
|
+
node_id: s.node_id,
|
|
231
|
+
kind: n?.kind ?? '?',
|
|
232
|
+
name: n !== null ? fullName(n) : s.node_id,
|
|
233
|
+
status: n?.status ?? 'dead',
|
|
234
|
+
active: s.active,
|
|
235
|
+
spawned: n?.created ?? s.created,
|
|
236
|
+
cycles: n?.cycles ?? 0,
|
|
237
|
+
last_push: last !== undefined
|
|
238
|
+
? { kind: last.kind, ts: last.ts, ref: last.ref ?? null, label: last.label }
|
|
239
|
+
: null,
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
return { node_id: nodeId, unread, children };
|
|
243
|
+
},
|
|
244
|
+
render: (r) => {
|
|
245
|
+
const id = r['node_id'];
|
|
246
|
+
const unread = r['unread'] ?? 0;
|
|
247
|
+
const kids = r['children'] ?? [];
|
|
248
|
+
if (kids.length === 0) {
|
|
249
|
+
const tail = unread > 0
|
|
250
|
+
? `${unread} unread report${unread === 1 ? '' : 's'} sit in your inbox — run \`crtr feed read\` to absorb ${unread === 1 ? 'it' : 'them'}.`
|
|
251
|
+
: 'If you spawned workers, they have finished and detached, or you never subscribed. Nothing will wake you.';
|
|
252
|
+
return `<peek node="${id}" subscriptions="0" unread="${unread}" verdict="empty">\nNo nodes below you. ${tail}\n</peek>`;
|
|
253
|
+
}
|
|
254
|
+
const working = kids.filter((k) => k.status === 'active' || k.status === 'idle');
|
|
255
|
+
const liveWaking = working.filter((k) => k.active); // active sub to a live node = it will wake me
|
|
256
|
+
const done = kids.filter((k) => k.status === 'done' || k.status === 'canceled');
|
|
257
|
+
const dead = kids.filter((k) => k.status === 'dead');
|
|
258
|
+
let verdict;
|
|
259
|
+
let line;
|
|
260
|
+
if (dead.length > 0) {
|
|
261
|
+
verdict = 'attention';
|
|
262
|
+
line = `\u26a0 ${dead.length} below you ${dead.length === 1 ? 'is' : 'are'} dead and will NOT wake you. Inspect with \`crtr node inspect show <id>\`, then re-delegate or proceed without ${dead.length === 1 ? 'it' : 'them'}.`;
|
|
263
|
+
}
|
|
264
|
+
else if (liveWaking.length > 0) {
|
|
265
|
+
verdict = 'working';
|
|
266
|
+
line = `Safe to yield \u2014 ${liveWaking.length} worker${liveWaking.length === 1 ? '' : 's'} running async will wake you on the next push. Nothing to do now; end your turn and chill.`;
|
|
267
|
+
}
|
|
268
|
+
else if (unread > 0) {
|
|
269
|
+
verdict = 'ready';
|
|
270
|
+
line = `Nothing still running, but ${unread} unread report${unread === 1 ? '' : 's'} \u2014 run \`crtr feed read\` to absorb ${unread === 1 ? 'it' : 'them'}, then continue or finish.`;
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
verdict = 'idle';
|
|
274
|
+
line = 'Everything below you has finished and been drained \u2014 nothing is running. Continue your own work, or `crtr push final` to finish.';
|
|
275
|
+
}
|
|
276
|
+
const rows = kids.map((k) => {
|
|
277
|
+
const sub = k.active ? '' : ' (passive)';
|
|
278
|
+
const push = k.last_push !== null
|
|
279
|
+
? `pushed ${fmtAge(k.last_push.ts)} [${k.last_push.kind}]${k.last_push.ref !== null ? ` ref:${k.last_push.ref}` : ''}`
|
|
280
|
+
: 'no push yet';
|
|
281
|
+
const glyph = STATUS_GLYPH[k.status] ?? '?';
|
|
282
|
+
return ` ${glyph} ${k.node_id} ${k.kind} ${k.name}${sub} \u00b7 ${k.status} \u00b7 spawned ${fmtAge(k.spawned)} \u00b7 cyc ${k.cycles} \u00b7 ${push}`;
|
|
283
|
+
}).join('\n');
|
|
284
|
+
const attrs = `node="${id}" subscriptions="${kids.length}" working="${working.length}" done="${done.length}" dead="${dead.length}" unread="${unread}" verdict="${verdict}"`;
|
|
285
|
+
return `<peek ${attrs}>\n${line}\n\n${rows}\n</peek>`;
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
120
289
|
// Registration
|
|
121
290
|
// ---------------------------------------------------------------------------
|
|
122
291
|
export function registerPush() {
|
|
@@ -130,12 +299,7 @@ export function registerPush() {
|
|
|
130
299
|
help: {
|
|
131
300
|
name: 'push',
|
|
132
301
|
summary: 'push a report to your subscribers',
|
|
133
|
-
model: 'A push writes a markdown report to the node\'s reports/ history and fans a lightweight pointer to every subscriber\'s inbox (not the content — they dereference lazily).
|
|
134
|
-
children: [
|
|
135
|
-
{ name: 'update', desc: TIER_BLURB.update, useWhen: 'sharing routine progress explicitly' },
|
|
136
|
-
{ name: 'urgent', desc: TIER_BLURB.urgent, useWhen: 'something your managers must see now' },
|
|
137
|
-
{ name: 'final', desc: TIER_BLURB.final, useWhen: 'the work is done — this finishes the node' },
|
|
138
|
-
],
|
|
302
|
+
model: 'A push writes a markdown report to the node\'s reports/ history and fans a lightweight pointer to every subscriber\'s inbox (not the content — they dereference lazily). Nothing is pushed automatically — the feed contains only what a node pushes explicitly, so push whenever you want a manager to see something. Pipe large bodies via stdin/heredoc (`crtr push <tier> <<\'EOF\' … EOF`). `push final` is how ANY node finishes: write the canonical result, mark done, close the window.',
|
|
139
303
|
},
|
|
140
304
|
children: [makeTierLeaf('update'), makeTierLeaf('urgent'), makeTierLeaf('final')],
|
|
141
305
|
});
|
|
@@ -151,9 +315,8 @@ export function registerFeed() {
|
|
|
151
315
|
help: {
|
|
152
316
|
name: 'feed',
|
|
153
317
|
summary: 'read the per-node inbox feed',
|
|
154
|
-
model: 'Each node has an inbox.jsonl that accumulates ~30-token pointers from publishers it subscribes to.
|
|
155
|
-
children: [{ name: 'read', desc: 'drain unread pointers into a digest', useWhen: 'checking what your subscriptions pushed' }],
|
|
318
|
+
model: 'Each node has an inbox.jsonl that accumulates ~30-token pointers from publishers it subscribes to. The watcher drains this inbox to wake you, so the wake message you receive ALREADY IS the coalesced digest \u2014 dereference the reports that matter by reading their ref paths (a push carries a ref; a direct message inlines its full body). `feed read` is for PROACTIVELY polling before a wake (it advances the cursor); after a wake it reads empty because the cursor already moved \u2014 use `--all` to re-read history. An empty feed is normal while workers run \u2014 a worker that has not pushed leaves no pointer \u2014 so use `feed peek` to see the live state of the nodes below you (and whether to yield) without draining anything.',
|
|
156
319
|
},
|
|
157
|
-
children: [feedReadLeaf],
|
|
320
|
+
children: [feedReadLeaf, feedPeekLeaf],
|
|
158
321
|
});
|
|
159
322
|
}
|
package/dist/commands/revive.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Bypasses the daemon: directly opens a fresh tmux window for a node that is
|
|
4
4
|
// done, idle, or dead. Default behavior resumes the saved pi conversation
|
|
5
|
-
// (--
|
|
5
|
+
// (--session <id>); pass --fresh to start a clean pi session against the context dir.
|
|
6
6
|
import { defineLeaf } from '../core/command.js';
|
|
7
7
|
import { InputError } from '../core/io.js';
|
|
8
8
|
import { reviveNode } from '../core/runtime/revive.js';
|
|
@@ -12,6 +12,8 @@ import { getNode } from '../core/canvas/index.js';
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
export const reviveLeaf = defineLeaf({
|
|
14
14
|
name: 'revive',
|
|
15
|
+
description: 'reopen a window for a done/idle/dead/canceled node',
|
|
16
|
+
whenToUse: 'you want to bring a dormant node back yourself — reopen a window for one that is done, idle, dead, or canceled: resume a node you closed with `node close`, reopen a finished worker for a follow-up, or restart a crashed one now instead of waiting. Resumes the saved conversation by default; pass `--fresh` to restart clean against its context dir. You rarely need this for crashes — the daemon auto-revives those; reach for it to bring a node back on demand, or to revive a canceled node the daemon will never touch on its own',
|
|
15
17
|
help: {
|
|
16
18
|
name: 'canvas revive',
|
|
17
19
|
summary: 'open a fresh tmux window for a node, optionally resuming its saved pi conversation',
|
|
@@ -28,13 +30,13 @@ export const reviveLeaf = defineLeaf({
|
|
|
28
30
|
type: 'bool',
|
|
29
31
|
required: false,
|
|
30
32
|
default: false,
|
|
31
|
-
constraint: 'When set, start a clean pi session (no --
|
|
33
|
+
constraint: 'When set, start a clean pi session (no --session). Default: resume the saved conversation.',
|
|
32
34
|
},
|
|
33
35
|
],
|
|
34
36
|
output: [
|
|
35
37
|
{ name: 'window', type: 'string', required: false, constraint: 'New tmux window id.' },
|
|
36
38
|
{ name: 'session', type: 'string', required: true, constraint: 'Tmux session the node was placed in.' },
|
|
37
|
-
{ name: 'resumed', type: 'boolean', required: true, constraint: 'True when pi was told to --
|
|
39
|
+
{ name: 'resumed', type: 'boolean', required: true, constraint: 'True when pi was told to --session the saved conversation.' },
|
|
38
40
|
],
|
|
39
41
|
outputKind: 'object',
|
|
40
42
|
effects: [
|
|
@@ -12,6 +12,8 @@ import { skillCreatePrompt, skillTemplatePrompt } from '../../prompts/skill.js';
|
|
|
12
12
|
import { VALID_TYPES, resolveWriteScope } from './shared.js';
|
|
13
13
|
export const authorGuide = defineLeaf({
|
|
14
14
|
name: 'guide',
|
|
15
|
+
description: 'load authoring workflow + skeleton for a type',
|
|
16
|
+
whenToUse: 'writing a new skill and you want the authoring workflow plus the template skeleton — call it once with no --type for the template picker, then again with --type for the full workflow and skeleton of that type.',
|
|
15
17
|
help: {
|
|
16
18
|
name: 'skill author guide',
|
|
17
19
|
summary: 'load the skill authoring workflow — two stages: omit type to pick one, pass type for its full skeleton',
|
|
@@ -40,6 +42,8 @@ export const authorGuide = defineLeaf({
|
|
|
40
42
|
});
|
|
41
43
|
export const authorScaffold = defineLeaf({
|
|
42
44
|
name: 'scaffold',
|
|
45
|
+
description: 'create an empty SKILL.md stub',
|
|
46
|
+
whenToUse: 'creating the empty SKILL.md stub at a `<plugin>/<skill>` qualifier before you fill in content — it writes the frontmatter and file, then points you at the authoring guide.',
|
|
43
47
|
help: {
|
|
44
48
|
name: 'skill author scaffold',
|
|
45
49
|
summary: 'create an empty SKILL.md stub at the given qualifier',
|
|
@@ -135,13 +139,11 @@ export const authorScaffold = defineLeaf({
|
|
|
135
139
|
});
|
|
136
140
|
export const authorBranch = defineBranch({
|
|
137
141
|
name: 'author',
|
|
142
|
+
description: 'create and scaffold skills',
|
|
143
|
+
whenToUse: 'you have a reusable workflow, methodology, or hard-won convention worth capturing so future agents adopt it instead of re-deriving it — author carries you from picking a template through scaffolding the file. Reach for it when a task just taught you a repeatable procedure, when the same guidance keeps getting re-explained across sessions, or when the house conventions for a tool deserve to be written down once. Start with `crtr skill author guide` for the template picker and authoring workflow; use `crtr skill author scaffold` to stub the SKILL.md file.',
|
|
138
144
|
help: {
|
|
139
145
|
name: 'skill author',
|
|
140
146
|
summary: 'create and scaffold new skills',
|
|
141
|
-
children: [
|
|
142
|
-
{ name: 'guide', desc: 'load authoring workflow + skeleton for a type', useWhen: 'writing a new skill and need the template and instructions' },
|
|
143
|
-
{ name: 'scaffold', desc: 'create an empty SKILL.md stub', useWhen: 'initializing the file before writing content' },
|
|
144
|
-
],
|
|
145
147
|
},
|
|
146
148
|
children: [authorGuide, authorScaffold],
|
|
147
149
|
});
|
|
@@ -8,6 +8,8 @@ import { paginate } from '../../core/pagination.js';
|
|
|
8
8
|
import { walkFiles, readText } from '../../core/fs-utils.js';
|
|
9
9
|
export const findList = defineLeaf({
|
|
10
10
|
name: 'list',
|
|
11
|
+
description: 'paginated list of installed skills',
|
|
12
|
+
whenToUse: 'browse the whole catalog of installed skills, paginated, when you want to see everything that is available rather than hunt for a particular topic — supports --scope, --plugin, and --full filters to narrow or enrich the listing. Use `crtr skill find search` instead when you already have a topic or keyword in mind.',
|
|
11
13
|
help: {
|
|
12
14
|
name: 'skill find list',
|
|
13
15
|
summary: 'paginated list of installed skills',
|
|
@@ -92,6 +94,8 @@ export const findList = defineLeaf({
|
|
|
92
94
|
});
|
|
93
95
|
export const findSearch = defineLeaf({
|
|
94
96
|
name: 'search',
|
|
97
|
+
description: 'keyword search across name/description/keywords',
|
|
98
|
+
whenToUse: 'you have a topic, keyword, or problem in mind but not the exact skill name — search ranks installed skills by how well your terms match their name, description, and keywords (add --search-body to also look inside SKILL.md bodies). Reach for it to answer questions like is there a skill for writing tests, anything on prompt design, do we have a debugging workflow. Use `crtr skill find list` instead to browse the whole catalog when you have no particular topic in mind, and `crtr skill find grep` when you need an exact regex or literal-string match across skill bodies rather than a ranked topic match.',
|
|
95
99
|
help: {
|
|
96
100
|
name: 'skill find search',
|
|
97
101
|
summary: 'search skills by name, description, and keywords',
|
|
@@ -177,6 +181,8 @@ export const findSearch = defineLeaf({
|
|
|
177
181
|
});
|
|
178
182
|
export const findGrep = defineLeaf({
|
|
179
183
|
name: 'grep',
|
|
184
|
+
description: 'regex search across SKILL.md bodies',
|
|
185
|
+
whenToUse: 'you need to find which skills contain a specific literal string or regex pattern in their SKILL.md body — an exact text match, not a ranked topic search: locating every skill that mentions a command name, a flag, or an exact phrase. Use `crtr skill find search` instead when you want skills matched by topic or keyword rather than exact body text.',
|
|
180
186
|
help: {
|
|
181
187
|
name: 'skill find grep',
|
|
182
188
|
summary: 'search skill file contents for a regex pattern',
|
|
@@ -241,14 +247,11 @@ export const findGrep = defineLeaf({
|
|
|
241
247
|
});
|
|
242
248
|
export const findBranch = defineBranch({
|
|
243
249
|
name: 'find',
|
|
250
|
+
description: 'list, search, or grep skills',
|
|
251
|
+
whenToUse: 'you do not yet know which skill applies and need to discover what is installed — list to browse the whole catalog, search to rank skills by topic or keyword, grep to match exact text inside skill bodies.',
|
|
244
252
|
help: {
|
|
245
253
|
name: 'skill find',
|
|
246
254
|
summary: 'discover skills by listing, keyword search, or body grep',
|
|
247
|
-
children: [
|
|
248
|
-
{ name: 'list', desc: 'paginated list of installed skills', useWhen: 'enumerating all available skills' },
|
|
249
|
-
{ name: 'search', desc: 'keyword search across name/description/keywords', useWhen: 'looking for skills matching a topic' },
|
|
250
|
-
{ name: 'grep', desc: 'regex search across SKILL.md bodies', useWhen: 'finding skills containing specific text or patterns' },
|
|
251
|
-
],
|
|
252
255
|
},
|
|
253
256
|
children: [findList, findSearch, findGrep],
|
|
254
257
|
});
|