@north-light/crouter 0.3.220 → 0.3.222
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/api/client.d.ts +29 -0
- package/dist/api/client.js +44 -0
- package/dist/api/dto/chat-inventory.d.ts +56 -0
- package/dist/api/dto/chat-inventory.js +11 -0
- package/dist/api/dto/human-requests.d.ts +88 -0
- package/dist/api/dto/human-requests.js +4 -0
- package/dist/api/dto/human.d.ts +3 -0
- package/dist/api/dto/profiles.d.ts +19 -5
- package/dist/api/dto/profiles.js +2 -1
- package/dist/api/dto/reviews.d.ts +2 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/routes.d.ts +8 -0
- package/dist/api/routes.js +11 -0
- package/dist/build-root.d.ts +2 -6
- package/dist/build-root.js +51 -4
- package/dist/builtin-memory/00-runtime-base/00-authoring.md +31 -0
- package/dist/builtin-memory/00-runtime-base/01-escalation.md +14 -0
- package/dist/builtin-memory/{insights/listen.md → 00-runtime-base/02-insight-capture.md} +1 -0
- package/dist/builtin-memory/02-turn-lifecycle/00-ending-a-turn.md +27 -0
- package/dist/builtin-memory/{02-lifecycle/01-resident.md → 02-turn-lifecycle/02-resident.md} +5 -0
- package/dist/builtin-memory/04-base-worker.md +4 -8
- package/dist/builtin-memory/04-orchestration-kernel.md +1 -1
- package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/advisor/advice-contract.md +1 -0
- package/dist/builtin-memory/05-kinds/design/00-base.md +2 -1
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +2 -1
- package/dist/builtin-memory/05-kinds/design/design-contract.md +19 -0
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/general/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/00-base.md +2 -1
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +2 -1
- package/dist/builtin-memory/05-kinds/plan/plan-contract.md +28 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/lens-contract.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +1 -0
- package/dist/builtin-memory/05-kinds/review/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/review/security-findings.md +1 -0
- package/dist/builtin-memory/05-kinds/spec/00-base.md +4 -3
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/spec/requirements.md +1 -0
- package/dist/builtin-memory/design/guide.md +35 -0
- package/dist/builtin-memory/design/roadmap.md +21 -0
- package/dist/builtin-memory/insights/capture.md +1 -1
- package/dist/builtin-memory/internal/agent-shaping.md +3 -1
- package/dist/builtin-memory/internal/memory-loading.md +4 -0
- package/dist/builtin-memory/internal/plugins.md +10 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/plan/roadmap.md +6 -22
- package/dist/builtin-memory/spec/guide.md +23 -6
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +34 -16
- package/dist/clients/attach/__tests__/ref-autocomplete.test.js +1 -1
- package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -1
- package/dist/clients/attach/overlays/file-review.js +2 -2
- package/dist/clients/attach/render/markdown-source.js +106 -1
- package/dist/clients/attach/session/file-links.d.ts +13 -4
- package/dist/clients/attach/session/file-links.js +54 -58
- package/dist/clients/attach/session/keys.d.ts +1 -1
- package/dist/clients/attach/session/profile-files.js +1 -1
- package/dist/clients/attach/viewer.js +698 -696
- package/dist/clients/inbox/controller.js +1 -1
- package/dist/clients/inbox/resolve.d.ts +1 -0
- package/dist/clients/inbox/review/launch.d.ts +8 -4
- package/dist/clients/inbox/review/launch.js +55 -5
- package/dist/clients/inbox/review/review-client.d.ts +1 -0
- package/dist/clients/inbox/review/review-client.js +7 -1
- package/dist/clients/inbox/review-adapter.d.ts +1 -8
- package/dist/clients/inbox/review-adapter.js +4 -52
- package/dist/commands/__tests__/human.test.js +2 -2
- package/dist/commands/human/request.d.ts +2 -0
- package/dist/commands/human/request.js +281 -0
- package/dist/commands/human.js +5 -2
- package/dist/commands/memory/lint.js +2 -1
- package/dist/commands/memory/read.js +1 -0
- package/dist/commands/memory.js +1 -1
- package/dist/commands/pkg/market-manage.js +165 -75
- package/dist/commands/pkg/plugin-inspect.js +19 -2
- package/dist/commands/pkg/plugin-manage.d.ts +8 -3
- package/dist/commands/pkg/plugin-manage.js +72 -24
- package/dist/commands/profile/default.js +6 -10
- package/dist/commands/profile/list.js +5 -3
- package/dist/commands/profile/new.js +21 -8
- package/dist/commands/profile/project.js +25 -19
- package/dist/commands/profile/show.js +3 -3
- package/dist/commands/surface-inbox.js +1 -0
- package/dist/commands/sys/__tests__/migrate.test.js +16 -5
- package/dist/commands/sys/config.js +2 -2
- package/dist/commands/sys/doctor.js +87 -5
- package/dist/commands/sys/migrate.js +38 -19
- package/dist/commands/sys/setup-core.js +1 -1
- package/dist/commands/sys/sync-project-guidance.js +1 -1
- package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +7 -4
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +1 -1
- package/dist/core/__tests__/fixtures/c5-command-boundary-ext.js +24 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +24 -18
- package/dist/core/__tests__/fixtures/fake-engine.js +8 -1
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +71 -0
- package/dist/core/__tests__/human-action-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/human-action-delivery.test.js +140 -0
- package/dist/core/__tests__/human-actions.test.d.ts +1 -0
- package/dist/core/__tests__/human-actions.test.js +116 -0
- package/dist/core/__tests__/inline-memory-refs.test.js +36 -2
- package/dist/core/__tests__/profile-project-memory-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +217 -0
- package/dist/core/__tests__/prospective-inventory-capability-parity.test.d.ts +1 -0
- package/dist/core/__tests__/prospective-inventory-capability-parity.test.js +91 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.d.ts +1 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +127 -0
- package/dist/core/__tests__/seam/prospective-inventory-stdout.test.d.ts +1 -0
- package/dist/core/__tests__/seam/prospective-inventory-stdout.test.js +31 -0
- package/dist/core/__tests__/serial/broker-sdk-wiring.test.js +102 -2
- package/dist/core/bootstrap.js +6 -0
- package/dist/core/canvas/browse/app.js +5 -2
- package/dist/core/canvas/browse/model.d.ts +25 -15
- package/dist/core/canvas/browse/model.js +86 -65
- package/dist/core/canvas/db.js +23 -0
- package/dist/core/canvas/human-deliveries.d.ts +53 -0
- package/dist/core/canvas/human-deliveries.js +75 -0
- package/dist/core/canvas/render-source.d.ts +6 -0
- package/dist/core/canvas/render-source.js +7 -1
- package/dist/core/canvas/render.js +10 -2
- package/dist/core/command-hooks/artifact.d.ts +10 -0
- package/dist/core/command-hooks/artifact.js +129 -0
- package/dist/core/command-hooks/catalog.d.ts +14 -0
- package/dist/core/command-hooks/catalog.js +38 -0
- package/dist/core/command-hooks/compose.d.ts +15 -0
- package/dist/core/command-hooks/compose.js +99 -0
- package/dist/core/command-hooks/discovery.d.ts +87 -0
- package/dist/core/command-hooks/discovery.js +174 -0
- package/dist/core/command-hooks/help.d.ts +5 -0
- package/dist/core/command-hooks/help.js +18 -0
- package/dist/core/command-hooks/index.d.ts +6 -0
- package/dist/core/command-hooks/index.js +6 -0
- package/dist/core/command-hooks/report.d.ts +23 -0
- package/dist/core/command-hooks/report.js +19 -0
- package/dist/core/command-hooks/schema.d.ts +27 -0
- package/dist/core/command-hooks/schema.js +68 -0
- package/dist/core/command-hooks/transport/exec-invoke.d.ts +22 -0
- package/dist/core/command-hooks/transport/exec-invoke.js +274 -0
- package/dist/core/command-plugins/presence.d.ts +2 -0
- package/dist/core/command-plugins/presence.js +17 -0
- package/dist/core/command-plugins/transport/exec-invoke.d.ts +5 -0
- package/dist/core/command-plugins/transport/exec-invoke.js +58 -5
- package/dist/core/command.d.ts +8 -1
- package/dist/core/command.js +12 -10
- package/dist/core/config.d.ts +13 -1
- package/dist/core/config.js +51 -1
- package/dist/core/feed/inbox.d.ts +6 -0
- package/dist/core/feed/inbox.js +9 -1
- package/dist/core/help.d.ts +7 -1
- package/dist/core/human/action-binding.d.ts +21 -0
- package/dist/core/human/action-binding.js +40 -0
- package/dist/core/human/completion.d.ts +38 -0
- package/dist/core/human/completion.js +27 -0
- package/dist/core/human/convention.d.ts +2 -0
- package/dist/core/human/convention.js +2 -0
- package/dist/core/human/tickets.d.ts +25 -6
- package/dist/core/human/tickets.js +19 -13
- package/dist/core/human/types.d.ts +5 -0
- package/dist/core/human-actions.d.ts +25 -0
- package/dist/core/human-actions.js +101 -0
- package/dist/core/io.d.ts +9 -1
- package/dist/core/io.js +44 -2
- package/dist/core/memory/inline-ref-inventory.d.ts +2 -1
- package/dist/core/memory/inline-ref-inventory.js +15 -8
- package/dist/core/memory-resolver.d.ts +13 -1
- package/dist/core/memory-resolver.js +26 -20
- package/dist/core/profiles/manifest.d.ts +13 -2
- package/dist/core/profiles/manifest.js +84 -18
- package/dist/core/profiles/select.d.ts +2 -0
- package/dist/core/profiles/select.js +29 -12
- package/dist/core/render.js +11 -0
- package/dist/core/runtime/advertised-command-invocation.d.ts +20 -0
- package/dist/core/runtime/advertised-command-invocation.js +233 -0
- package/dist/core/runtime/bearings.js +1 -1
- package/dist/core/runtime/broker/event-projection.js +7 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +1 -1
- package/dist/core/runtime/broker/frame-dispatch.js +13 -11
- package/dist/core/runtime/broker/read-ops.d.ts +4 -0
- package/dist/core/runtime/broker/read-ops.js +6 -2
- package/dist/core/runtime/broker-extension-render.js +1 -1
- package/dist/core/runtime/broker-inventory.d.ts +4 -0
- package/dist/core/runtime/broker-inventory.js +116 -0
- package/dist/core/runtime/broker-persona-guidance.js +1 -1
- package/dist/core/runtime/broker-protocol.d.ts +9 -2
- package/dist/core/runtime/broker.js +10 -1
- package/dist/core/runtime/chat-inventory-rows.d.ts +8 -0
- package/dist/core/runtime/chat-inventory-rows.js +105 -0
- package/dist/core/runtime/command-surface.d.ts +38 -0
- package/dist/core/runtime/command-surface.js +117 -0
- package/dist/core/runtime/launch-target.d.ts +25 -0
- package/dist/core/runtime/launch-target.js +54 -0
- package/dist/core/runtime/node-read.js +5 -0
- package/dist/core/runtime/persona.js +3 -3
- package/dist/core/runtime/prospective-inventory-cli.d.ts +1 -0
- package/dist/core/runtime/prospective-inventory-cli.js +61 -0
- package/dist/core/runtime/prospective-inventory.d.ts +10 -0
- package/dist/core/runtime/prospective-inventory.js +88 -0
- package/dist/core/runtime/spawn.d.ts +3 -1
- package/dist/core/runtime/spawn.js +5 -3
- package/dist/core/scope.d.ts +26 -1
- package/dist/core/scope.js +52 -12
- package/dist/core/substrate/on-read.d.ts +7 -1
- package/dist/core/substrate/on-read.js +30 -32
- package/dist/core/substrate/render-node.d.ts +3 -2
- package/dist/core/substrate/render-node.js +3 -2
- package/dist/core/substrate/render.js +65 -24
- package/dist/core/substrate/schema.d.ts +16 -2
- package/dist/core/substrate/schema.js +14 -5
- package/dist/core/user-settings.d.ts +4 -0
- package/dist/core/user-settings.js +1 -0
- package/dist/daemon/api/__tests__/profile-launch-gates.test.js +56 -7
- package/dist/daemon/api/handlers/chat-inventory.d.ts +2 -0
- package/dist/daemon/api/handlers/chat-inventory.js +25 -0
- package/dist/daemon/api/handlers/human-requests.d.ts +2 -0
- package/dist/daemon/api/handlers/human-requests.js +409 -0
- package/dist/daemon/api/handlers/human.js +3 -0
- package/dist/daemon/api/handlers/inbox.js +3 -0
- package/dist/daemon/api/handlers/nodes.d.ts +1 -3
- package/dist/daemon/api/handlers/nodes.js +11 -46
- package/dist/daemon/api/handlers/profiles.js +7 -1
- package/dist/daemon/api/handlers/prospective-chat-inventory.d.ts +2 -0
- package/dist/daemon/api/handlers/prospective-chat-inventory.js +59 -0
- package/dist/daemon/api/handlers/reviews.js +10 -2
- package/dist/daemon/api/map.d.ts +2 -1
- package/dist/daemon/api/map.js +3 -2
- package/dist/daemon/api/server.js +6 -0
- package/dist/daemon/crtrd.js +6 -0
- package/dist/daemon/human/deliver-action.d.ts +16 -0
- package/dist/daemon/human/deliver-action.js +168 -0
- package/dist/daemon/human/finish.d.ts +8 -5
- package/dist/daemon/human/finish.js +45 -6
- package/dist/daemon/human/sweep.js +4 -1
- package/dist/daemon/reconcilers/human-delivery-lane.d.ts +10 -0
- package/dist/daemon/reconcilers/human-delivery-lane.js +41 -0
- package/dist/daemon/review/finish.d.ts +8 -3
- package/dist/daemon/review/finish.js +19 -1
- package/dist/hook-authoring.d.ts +75 -0
- package/dist/hook-authoring.js +358 -0
- package/dist/hook-process.d.ts +7 -0
- package/dist/hook-process.js +34 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/migrations/002-profile-project-memory.d.ts +2 -0
- package/dist/migrations/002-profile-project-memory.js +71 -0
- package/dist/migrations/profile-manifests.d.ts +30 -0
- package/dist/migrations/profile-manifests.js +70 -0
- package/dist/migrations/registry.js +10 -5
- package/dist/migrations/types.d.ts +28 -1
- package/dist/migrations/types.js +15 -9
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +21 -4
- package/dist/pi-extensions/canvas-structured-output.js +85 -2
- package/dist/types.d.ts +23 -6
- package/dist/types.js +1 -0
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/dist/builtin-memory/00-runtime-base.md +0 -55
- package/dist/builtin-memory/design.md +0 -55
- package/dist/clients/attach/__tests__/file-review-focus.test.js +0 -49
- /package/dist/builtin-memory/{02-lifecycle/00-terminal.md → 02-turn-lifecycle/01-terminal.md} +0 -0
- /package/dist/{clients/attach/__tests__/file-review-focus.test.d.ts → core/__tests__/fixtures/memory-slash-live-probe.d.ts} +0 -0
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
// `crtr profile default`
|
|
2
|
-
// node boots in THIS directory, use THIS profile" and stops the startup
|
|
3
|
-
// chooser from asking. It is directory-scoped, not manifest state: independent
|
|
4
|
-
// of a profile's project purview (`crtr profile project`). The startup menu's
|
|
5
|
-
// `d` action writes the same pin; these verbs are the explicit surface for it.
|
|
1
|
+
// `crtr profile default` manages the per-cwd profile-selection pin.
|
|
6
2
|
import { defineLeaf, defineBranch } from '../../core/command.js';
|
|
7
3
|
import { InputError } from '../../core/io.js';
|
|
8
4
|
import { loadProfileManifest } from '../../core/profiles/manifest.js';
|
|
@@ -11,7 +7,7 @@ import { getDefaultProfileId, setDefaultProfileId, clearDefaultProfile, resolveB
|
|
|
11
7
|
const setLeaf = defineLeaf({
|
|
12
8
|
name: 'set',
|
|
13
9
|
description: 'pin a profile as the default for the current directory',
|
|
14
|
-
whenToUse: 'you want crtr to stop asking which profile to use here and always default to a chosen one — the command form of the startup menu\u2019s `d` action. The profile must already cover this directory (add
|
|
10
|
+
whenToUse: 'you want crtr to stop asking which profile to use here and always default to a chosen one — the command form of the startup menu\u2019s `d` action. The profile must already cover this directory (add its purview relationship with `profile project add` and required `--memory` first if not).',
|
|
15
11
|
help: {
|
|
16
12
|
name: 'profile default set',
|
|
17
13
|
summary: "pin a profile as the current directory's default, so the startup chooser stops asking",
|
|
@@ -20,7 +16,7 @@ const setLeaf = defineLeaf({
|
|
|
20
16
|
kind: 'positional',
|
|
21
17
|
name: 'profile',
|
|
22
18
|
required: true,
|
|
23
|
-
constraint: 'Exact profile id, or a unique manifest name. Must already cover the current directory (be at/above/below it in its project purview).',
|
|
19
|
+
constraint: 'Exact profile id, or a unique manifest name. Must already cover the current directory (be at/above/below it in its project purview); this pin does not change automatic memory disclosure.',
|
|
24
20
|
},
|
|
25
21
|
],
|
|
26
22
|
output: [
|
|
@@ -40,7 +36,7 @@ const setLeaf = defineLeaf({
|
|
|
40
36
|
throw new InputError({
|
|
41
37
|
error: 'not_covered',
|
|
42
38
|
message: `profile "${entry.manifest.name}" does not cover ${resolved}, so a default pinned here would be ignored.`,
|
|
43
|
-
next: `Widen its purview first: \`crtr profile project add ${entry.profileId} --dir ${resolved}
|
|
39
|
+
next: `Widen its purview and choose its automatic memory disclosure cap first: \`crtr profile project add ${entry.profileId} --dir ${resolved} --memory <none|name|preview|content>\`, then re-run this.`,
|
|
44
40
|
});
|
|
45
41
|
}
|
|
46
42
|
setDefaultProfileId(cwd, entry.profileId);
|
|
@@ -133,11 +129,11 @@ const showLeaf = defineLeaf({
|
|
|
133
129
|
export const defaultBranch = defineBranch({
|
|
134
130
|
name: 'default',
|
|
135
131
|
description: "manage this directory's default-profile pin",
|
|
136
|
-
whenToUse: "setting, clearing, or inspecting which profile a directory defaults to at startup — so crtr stops asking. Directory-scoped, independent of
|
|
132
|
+
whenToUse: "setting, clearing, or inspecting which profile a directory defaults to at startup — so crtr stops asking. Directory-scoped, independent of project purview and automatic memory disclosure.",
|
|
137
133
|
help: {
|
|
138
134
|
name: 'profile default',
|
|
139
135
|
summary: "the per-directory default profile — pin one so the startup chooser stops asking",
|
|
140
|
-
model: "A pin binds the CURRENT directory to a profile: launches here auto-pick it with no prompt (`crtr --pick-profile` reopens the chooser once). `set` pins a profile (which must already cover this dir), `clear` removes the pin, `show` reports it. This is directory-scoped selection,
|
|
136
|
+
model: "A pin binds the CURRENT directory to a profile: launches here auto-pick it with no prompt (`crtr --pick-profile` reopens the chooser once). `set` pins a profile (which must already cover this dir), `clear` removes the pin, `show` reports it. This is directory-scoped selection, independent of `crtr profile project`: project `path` values govern purview and each `memory` value caps automatic disclosure. The startup menu's `d` action writes the same pin.",
|
|
141
137
|
},
|
|
142
138
|
children: [setLeaf, clearLeaf, showLeaf],
|
|
143
139
|
});
|
|
@@ -3,15 +3,16 @@ import { emitLine } from '../../core/io.js';
|
|
|
3
3
|
import { listProfiles } from '../../core/profiles/manifest.js';
|
|
4
4
|
export const listLeaf = defineLeaf({
|
|
5
5
|
name: 'list',
|
|
6
|
-
description: 'inventory every profile',
|
|
7
|
-
whenToUse: 'browse every profile that exists — one line each with its id, name, project
|
|
6
|
+
description: 'inventory every profile and its project relationships',
|
|
7
|
+
whenToUse: 'browse every profile that exists — one line each with its id, name, explicit project relationships, home directory, paused state, and last-used time. Reach for `profile show` once you have picked one, for its full manifest.',
|
|
8
8
|
help: {
|
|
9
9
|
name: 'profile list',
|
|
10
10
|
summary: 'every profile as JSONL, one line each, sorted by name then profile_id',
|
|
11
11
|
output: [
|
|
12
12
|
{ name: 'profile_id', type: 'string', required: true, constraint: 'Stable directory id.' },
|
|
13
13
|
{ name: 'name', type: 'string', required: true, constraint: 'Manifest name.' },
|
|
14
|
-
{ name: 'projects_count', type: 'int', required: true, constraint: 'Number of project
|
|
14
|
+
{ name: 'projects_count', type: 'int', required: true, constraint: 'Number of `{path, memory}` project relationships on the manifest.' },
|
|
15
|
+
{ name: 'projects', type: 'ProfileProject[]', required: true, constraint: 'Explicit `{path, memory}` relationships in manifest order. `path` defines profile purview; `memory` caps automatic boot and workspace-open disclosure.' },
|
|
15
16
|
{ name: 'home', type: 'string | null', required: true, constraint: 'Directory the profile\u2019s nodes are pinned to, or null when it owns no projects.' },
|
|
16
17
|
{ name: 'paused_at', type: 'string | null', required: true, constraint: 'ISO 8601 pause timestamp, or null when the profile is active.' },
|
|
17
18
|
{ name: 'default_kind', type: 'string | null', required: true, constraint: 'Stored default node kind, or null when creates default to general.' },
|
|
@@ -30,6 +31,7 @@ export const listLeaf = defineLeaf({
|
|
|
30
31
|
profile_id: profileId,
|
|
31
32
|
name: manifest.name,
|
|
32
33
|
projects_count: manifest.projects.length,
|
|
34
|
+
projects: manifest.projects,
|
|
33
35
|
home: manifest.home,
|
|
34
36
|
paused_at: manifest.paused_at,
|
|
35
37
|
default_kind: manifest.default_kind ?? null,
|
|
@@ -2,6 +2,7 @@ import { defineLeaf } from '../../core/command.js';
|
|
|
2
2
|
import { usage } from '../../core/errors.js';
|
|
3
3
|
import { assertInstalledKind } from '../../core/config.js';
|
|
4
4
|
import { createProfile, ensureRootProfile, profileRoot } from '../../core/profiles/manifest.js';
|
|
5
|
+
import { PROFILE_PROJECT_MEMORY_VALUES } from '../../api/dto/profiles.js';
|
|
5
6
|
import { resolveMetaPairs } from './meta.js';
|
|
6
7
|
export const newLeaf = defineLeaf({
|
|
7
8
|
name: 'new',
|
|
@@ -23,14 +24,22 @@ export const newLeaf = defineLeaf({
|
|
|
23
24
|
name: 'root',
|
|
24
25
|
type: 'bool',
|
|
25
26
|
required: false,
|
|
26
|
-
constraint: 'Provision the canonical stable root profile (fixed id `root-00000000`, name `root`) — the fallback default identity for a home — instead of a fresh named one. Idempotent: re-running returns the existing root. Node-free (no `node new --root` needed). Mutually exclusive with `--name`/`--project`/`--default-kind`/`--meta`.',
|
|
27
|
+
constraint: 'Provision the canonical stable root profile (fixed id `root-00000000`, name `root`) — the fallback default identity for a home — instead of a fresh named one. Idempotent: re-running returns the existing root. Node-free (no `node new --root` needed). Mutually exclusive with `--name`/`--project`/`--memory`/`--default-kind`/`--meta`.',
|
|
27
28
|
},
|
|
28
29
|
{
|
|
29
30
|
kind: 'flag',
|
|
30
31
|
name: 'project',
|
|
31
32
|
type: 'path',
|
|
32
33
|
required: false,
|
|
33
|
-
constraint: "One initial project directory. Must already exist and be a directory; resolved to its absolute real path before storing.
|
|
34
|
+
constraint: "One initial project directory. Must already exist and be a directory; resolved to its absolute real path before storing. Creates a purview relationship and becomes the profile's home — where its nodes are pinned. Requires paired `--memory`; singular — add more with `crtr profile project add` after creation.",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
kind: 'flag',
|
|
38
|
+
name: 'memory',
|
|
39
|
+
type: 'enum',
|
|
40
|
+
choices: [...PROFILE_PROJECT_MEMORY_VALUES],
|
|
41
|
+
required: false,
|
|
42
|
+
constraint: 'Required with `--project`; invalid without it. Maximum automatic memory disclosure from this project at boot and workspace-open: `none`, `name`, `preview`, or `content`. Does not change purview or explicit `crtr memory read` access.',
|
|
34
43
|
},
|
|
35
44
|
{
|
|
36
45
|
kind: 'flag',
|
|
@@ -52,7 +61,7 @@ export const newLeaf = defineLeaf({
|
|
|
52
61
|
{ name: 'profile_id', type: 'string', required: true, constraint: 'Stable directory id (`<slug>-<shortid>`). Pass this, or the name, to every other `profile` verb and to `--profile` elsewhere.' },
|
|
53
62
|
{ name: 'name', type: 'string', required: true, constraint: 'The manifest name as given.' },
|
|
54
63
|
{ name: 'path', type: 'string', required: true, constraint: 'Absolute path to the profile directory.' },
|
|
55
|
-
{ name: 'projects', type: '
|
|
64
|
+
{ name: 'projects', type: 'ProfileProject[]', required: true, constraint: 'Explicit `{path, memory}` project relationships in manifest order. `path` defines purview; `memory` caps automatic boot and workspace-open disclosure. Empty when --project was omitted.' },
|
|
56
65
|
{ name: 'home', type: 'string | null', required: true, constraint: 'Directory the profile\u2019s nodes are pinned to — the initial project, or null when none was given.' },
|
|
57
66
|
{ name: 'default_kind', type: 'string | null', required: true, constraint: 'Stored default persona kind, or null when node creates default to general.' },
|
|
58
67
|
{ name: 'metadata', type: 'object', required: true, constraint: 'Stored metadata map; empty when --meta was omitted.' },
|
|
@@ -65,12 +74,13 @@ export const newLeaf = defineLeaf({
|
|
|
65
74
|
run: async (input) => {
|
|
66
75
|
const name = input['name'];
|
|
67
76
|
const project = input['project'];
|
|
77
|
+
const memory = input['memory'];
|
|
68
78
|
const defaultKind = input['defaultKind'];
|
|
69
79
|
const metadata = resolveMetaPairs(input['meta'], 'meta');
|
|
70
80
|
const root = input['root'] === true;
|
|
71
81
|
if (root) {
|
|
72
|
-
if (name !== undefined || project !== undefined || defaultKind !== undefined || Object.keys(metadata).length > 0) {
|
|
73
|
-
throw usage('`--root` provisions the canonical stable root profile and takes no `--name`/`--project`/`--default-kind`/`--meta`.');
|
|
82
|
+
if (name !== undefined || project !== undefined || memory !== undefined || defaultKind !== undefined || Object.keys(metadata).length > 0) {
|
|
83
|
+
throw usage('`--root` provisions the canonical stable root profile and takes no `--name`/`--project`/`--memory`/`--default-kind`/`--meta`.');
|
|
74
84
|
}
|
|
75
85
|
const { profileId, manifest } = ensureRootProfile();
|
|
76
86
|
return {
|
|
@@ -82,14 +92,17 @@ export const newLeaf = defineLeaf({
|
|
|
82
92
|
default_kind: manifest.default_kind ?? null,
|
|
83
93
|
metadata: manifest.metadata ?? {},
|
|
84
94
|
created_at: manifest.created_at,
|
|
85
|
-
follow_up: `Root profile ensured. Spawn nodes under it with \`--profile ${profileId}\`, or widen its purview with \`crtr profile project add ${profileId} --dir <path>\`.`,
|
|
95
|
+
follow_up: `Root profile ensured. Spawn nodes under it with \`--profile ${profileId}\`, or widen its purview and choose its automatic memory disclosure cap with \`crtr profile project add ${profileId} --dir <path> --memory <none|name|preview|content>\`.`,
|
|
86
96
|
};
|
|
87
97
|
}
|
|
88
98
|
if (name === undefined)
|
|
89
99
|
throw usage('provide `--name <name>` to create a profile, or `--root` to provision the stable root profile.');
|
|
100
|
+
if ((project === undefined) !== (memory === undefined)) {
|
|
101
|
+
throw usage('`--project` and `--memory` must be supplied together: `--project` establishes purview and `--memory` sets its automatic boot and workspace-open disclosure cap.');
|
|
102
|
+
}
|
|
90
103
|
if (defaultKind !== undefined)
|
|
91
104
|
assertInstalledKind(defaultKind);
|
|
92
|
-
const { profileId, manifest } = createProfile(name, project !== undefined ? [project] : [], {
|
|
105
|
+
const { profileId, manifest } = createProfile(name, project !== undefined && memory !== undefined ? [{ path: project, memory }] : [], {
|
|
93
106
|
defaultKind,
|
|
94
107
|
...(Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
95
108
|
});
|
|
@@ -102,7 +115,7 @@ export const newLeaf = defineLeaf({
|
|
|
102
115
|
default_kind: manifest.default_kind ?? null,
|
|
103
116
|
metadata: manifest.metadata ?? {},
|
|
104
117
|
created_at: manifest.created_at,
|
|
105
|
-
follow_up: `Show it with \`crtr profile show ${profileId}\`, add
|
|
118
|
+
follow_up: `Show it with \`crtr profile show ${profileId}\`, add purview and choose its automatic memory disclosure cap with \`crtr profile project add ${profileId} --dir <path> --memory <none|name|preview|content>\`, or list every profile with \`crtr profile list\`.`,
|
|
106
119
|
};
|
|
107
120
|
},
|
|
108
121
|
});
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
// `crtr profile project`
|
|
2
|
-
// profile's `projects` list is what it can SEE (memory + config resolution),
|
|
3
|
-
// and its `home` — one of those dirs — is where its nodes actually run. These
|
|
4
|
-
// two verbs widen or narrow that list after creation and re-point the home;
|
|
5
|
-
// `crtr profile show` reads both back.
|
|
1
|
+
// `crtr profile project` manages purview relationships and their automatic-memory caps.
|
|
6
2
|
import { defineLeaf, defineBranch } from '../../core/command.js';
|
|
7
3
|
import { addProfileProject, removeProfileProject, loadProfileManifest } from '../../core/profiles/manifest.js';
|
|
4
|
+
import { PROFILE_PROJECT_MEMORY_VALUES } from '../../api/dto/profiles.js';
|
|
8
5
|
const addLeaf = defineLeaf({
|
|
9
6
|
name: 'add',
|
|
10
|
-
description: "add
|
|
11
|
-
whenToUse: "a profile needs
|
|
7
|
+
description: "add or update a project's purview relationship",
|
|
8
|
+
whenToUse: "a profile needs purview over another project directory, or its automatic memory disclosure cap needs changing — extend or update the relationship after creation. Also the way to re-point the profile's home, with --home.",
|
|
12
9
|
help: {
|
|
13
10
|
name: 'profile project add',
|
|
14
|
-
summary: "
|
|
11
|
+
summary: "add or update a project relationship in a profile's manifest, deduped by real path",
|
|
15
12
|
params: [
|
|
16
13
|
{
|
|
17
14
|
kind: 'positional',
|
|
@@ -24,7 +21,15 @@ const addLeaf = defineLeaf({
|
|
|
24
21
|
name: 'dir',
|
|
25
22
|
type: 'path',
|
|
26
23
|
required: true,
|
|
27
|
-
constraint: 'Directory to add. Must exist and be a directory; resolved to its absolute real path before storing. A path already on the manifest
|
|
24
|
+
constraint: 'Directory to add. Must exist and be a directory; resolved to its absolute real path before storing. A path already on the manifest keeps its position and updates its `memory` value.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
kind: 'flag',
|
|
28
|
+
name: 'memory',
|
|
29
|
+
type: 'enum',
|
|
30
|
+
choices: [...PROFILE_PROJECT_MEMORY_VALUES],
|
|
31
|
+
required: true,
|
|
32
|
+
constraint: 'Maximum automatic memory disclosure from this project at boot and workspace-open: `none`, `name`, `preview`, or `content`. Does not change the profile’s purview or explicit `crtr memory read` access.',
|
|
28
33
|
},
|
|
29
34
|
{
|
|
30
35
|
kind: 'flag',
|
|
@@ -36,16 +41,17 @@ const addLeaf = defineLeaf({
|
|
|
36
41
|
],
|
|
37
42
|
output: [
|
|
38
43
|
{ name: 'profile_id', type: 'string', required: true, constraint: 'Stable directory id.' },
|
|
39
|
-
{ name: 'projects', type: '
|
|
44
|
+
{ name: 'projects', type: 'ProfileProject[]', required: true, constraint: 'The updated `{path, memory}` relationships in manifest order. `path` defines purview; `memory` caps automatic boot and workspace-open disclosure.' },
|
|
40
45
|
{ name: 'home', type: 'string | null', required: true, constraint: "The profile's home directory after the change." },
|
|
41
46
|
{ name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands.' },
|
|
42
47
|
],
|
|
43
48
|
outputKind: 'object',
|
|
44
|
-
effects: ["Appends
|
|
49
|
+
effects: ["Appends a new project relationship or updates an existing relationship’s `memory` in the profile manifest, and rewrites `home` when --home is given. Purview follows `path`; `memory` caps automatic boot and workspace-open disclosure. Invalidates the process scope cache."],
|
|
45
50
|
},
|
|
46
51
|
run: async (input) => {
|
|
47
52
|
const { profileId } = loadProfileManifest(input['profile']);
|
|
48
53
|
const { manifest } = addProfileProject(profileId, input['dir'], {
|
|
54
|
+
memory: input['memory'],
|
|
49
55
|
home: input['home'] === true,
|
|
50
56
|
});
|
|
51
57
|
return {
|
|
@@ -58,7 +64,7 @@ const addLeaf = defineLeaf({
|
|
|
58
64
|
});
|
|
59
65
|
const removeLeaf = defineLeaf({
|
|
60
66
|
name: 'remove',
|
|
61
|
-
description: "remove a project
|
|
67
|
+
description: "remove a project relationship from a profile's purview",
|
|
62
68
|
whenToUse: "a profile no longer needs purview over one of its listed project directories — narrow it back. Works even if the directory itself was since deleted.",
|
|
63
69
|
help: {
|
|
64
70
|
name: 'profile project remove',
|
|
@@ -75,17 +81,17 @@ const removeLeaf = defineLeaf({
|
|
|
75
81
|
name: 'dir',
|
|
76
82
|
type: 'path',
|
|
77
83
|
required: true,
|
|
78
|
-
constraint: 'Directory to remove, matched
|
|
84
|
+
constraint: 'Directory to remove, matched against the stored project `path`. Does not require the directory to still exist on disk. Removing the home dir re-points the home at the first remaining project.',
|
|
79
85
|
},
|
|
80
86
|
],
|
|
81
87
|
output: [
|
|
82
88
|
{ name: 'profile_id', type: 'string', required: true, constraint: 'Stable directory id.' },
|
|
83
|
-
{ name: 'projects', type: '
|
|
89
|
+
{ name: 'projects', type: 'ProfileProject[]', required: true, constraint: 'The updated `{path, memory}` relationships in manifest order.' },
|
|
84
90
|
{ name: 'home', type: 'string | null', required: true, constraint: "The profile's home directory after the change." },
|
|
85
91
|
{ name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands.' },
|
|
86
92
|
],
|
|
87
93
|
outputKind: 'object',
|
|
88
|
-
effects: ["Removes
|
|
94
|
+
effects: ["Removes a project relationship from the profile manifest, re-pointing `home` when that entry was it. This narrows purview and removes its automatic memory disclosure cap. Invalidates the process scope cache."],
|
|
89
95
|
},
|
|
90
96
|
run: async (input) => {
|
|
91
97
|
const { profileId } = loadProfileManifest(input['profile']);
|
|
@@ -100,12 +106,12 @@ const removeLeaf = defineLeaf({
|
|
|
100
106
|
});
|
|
101
107
|
export const projectBranch = defineBranch({
|
|
102
108
|
name: 'project',
|
|
103
|
-
description: "
|
|
104
|
-
whenToUse: "changing
|
|
109
|
+
description: "manage a profile's project purview and automatic memory disclosure",
|
|
110
|
+
whenToUse: "changing a profile’s project purview or one relationship’s automatic memory disclosure cap after creation — add, update, or remove one.",
|
|
105
111
|
help: {
|
|
106
112
|
name: 'profile project',
|
|
107
|
-
summary: "manage
|
|
108
|
-
model: "
|
|
113
|
+
summary: "manage project purview and automatic memory disclosure caps",
|
|
114
|
+
model: "Each `{path, memory}` relationship widens purview: the profile resolves memory and config from `path`. Its `memory` value separately caps only automatic boot and workspace-open delivery; explicit reads remain available through purview. `add` appends a new real path or updates an existing relationship’s cap without moving it, and can re-point `home`; `remove` narrows purview and re-points `home` when needed. Purview and automatic disclosure are both independent of a per-cwd default pin (`crtr profile default`).",
|
|
109
115
|
},
|
|
110
116
|
children: [addLeaf, removeLeaf],
|
|
111
117
|
});
|
|
@@ -21,7 +21,7 @@ export const showLeaf = defineLeaf({
|
|
|
21
21
|
output: [
|
|
22
22
|
{ name: 'profile_id', type: 'string', required: true, constraint: 'Stable directory id.' },
|
|
23
23
|
{ name: 'name', type: 'string', required: true, constraint: 'Manifest name.' },
|
|
24
|
-
{ name: 'projects', type: '
|
|
24
|
+
{ name: 'projects', type: 'ProfileProject[]', required: true, constraint: 'Explicit `{path, memory}` relationships in manifest order. `path` defines profile purview; `memory` caps automatic boot and workspace-open disclosure without limiting explicit reads.' },
|
|
25
25
|
{ name: 'home', type: 'string | null', required: true, constraint: 'Where a node under this profile runs when its create names no directory at all — an explicit --cwd and the directory a root is launched from both win over it. Defaults to the first project; re-point it with `profile project add --home`. Null when the profile owns no projects (the root profile).' },
|
|
26
26
|
{ name: 'paused_at', type: 'string | null', required: true, constraint: 'ISO 8601 pause timestamp, or null when the profile is active.' },
|
|
27
27
|
{ name: 'default_kind', type: 'string | null', required: true, constraint: 'Stored default node kind, or null when creates default to general.' },
|
|
@@ -53,8 +53,8 @@ export const showLeaf = defineLeaf({
|
|
|
53
53
|
config_path: configPath,
|
|
54
54
|
config_exists: existsSync(configPath),
|
|
55
55
|
follow_up: manifest.paused_at === null
|
|
56
|
-
? `
|
|
57
|
-
: `
|
|
56
|
+
? `Manage its purview and automatic memory disclosure with \`crtr profile project add ${profileId} --dir <path> --memory <none|name|preview|content>\`, pause it with \`crtr profile pause ${profileId}\`, or set its default kind with \`crtr profile kind ${profileId} <kind>\`.`
|
|
57
|
+
: `Manage its purview and automatic memory disclosure with \`crtr profile project add ${profileId} --dir <path> --memory <none|name|preview|content>\`, or resume it with \`crtr profile resume ${profileId}\`.`,
|
|
58
58
|
};
|
|
59
59
|
},
|
|
60
60
|
});
|
|
@@ -51,6 +51,7 @@ export const surfaceInboxLeaf = defineLeaf({
|
|
|
51
51
|
description: 'human inbox toggle dispatcher (bound to the inbox-toggle key; not run by hand)',
|
|
52
52
|
whenToUse: 'never directly — the inbox-toggle keybinding routes the user’s own request through it. An agent that wants a decision uses crtr human send; how the user reaches their inbox is theirs.',
|
|
53
53
|
tier: 'hidden',
|
|
54
|
+
hookEligible: false,
|
|
54
55
|
help: {
|
|
55
56
|
name: 'surface inbox',
|
|
56
57
|
summary: 'deliver the human inbox toggle to a live viewer',
|
|
@@ -25,16 +25,17 @@ function write(path, content) {
|
|
|
25
25
|
writeFileSync(path, content);
|
|
26
26
|
}
|
|
27
27
|
function manifest(name, projects) {
|
|
28
|
+
const first = projects[0];
|
|
28
29
|
return JSON.stringify({
|
|
29
30
|
schema_version: 1,
|
|
30
31
|
name,
|
|
31
32
|
projects,
|
|
32
|
-
home:
|
|
33
|
+
home: (typeof first === 'string' ? first : first?.path) ?? null,
|
|
33
34
|
created_at: '2026-01-01T00:00:00.000Z',
|
|
34
35
|
last_used_at: null,
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
|
-
test('sys migrate
|
|
38
|
+
test('sys migrate converges profile manifests, then profile-aware refs and every descendant project/plugin store', () => {
|
|
38
39
|
const home = mkdtempSync(join(tmpdir(), 'crtr-sys-migrate-'));
|
|
39
40
|
const userRoot = join(home, '.crouter');
|
|
40
41
|
const rootProfile = join(userRoot, 'profiles', 'root-00000000');
|
|
@@ -52,8 +53,11 @@ test('sys migrate covers profile-aware refs and every descendant project/plugin
|
|
|
52
53
|
const pluginDoc = join(pluginRoot, 'memory', 'plugin.md');
|
|
53
54
|
const embeddedPluginDoc = join(embeddedPluginRoot, 'memory', 'plugin.md');
|
|
54
55
|
try {
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
// Root's string entries migrate to `preview`; every other profile's to
|
|
57
|
+
// `content`. The applet manifest is mixed: its already-current entry must
|
|
58
|
+
// survive untouched while the string beside it converts.
|
|
59
|
+
write(join(rootProfile, 'profile.json'), manifest('root', [appletProject]));
|
|
60
|
+
write(join(appletProfile, 'profile.json'), manifest('applet-inactive', [{ path: appletProject, memory: 'name' }, nestedOnlyProject]));
|
|
57
61
|
write(userDoc, `${OLD_DOC}\nSee [[user/scoped]].\n`);
|
|
58
62
|
write(join(userRoot, 'memory', 'scoped', 'INDEX.md'), CURRENT_DOC);
|
|
59
63
|
write(rootDoc, `${OLD_DOC}\nSee [[shared]].\n`);
|
|
@@ -78,8 +82,15 @@ test('sys migrate covers profile-aware refs and every descendant project/plugin
|
|
|
78
82
|
const result = spawnSync(process.execPath, ['--conditions=crtr-src', '--import', TSX_LOADER, CLI, 'sys', 'migrate', '--json'], { cwd: home, env, encoding: 'utf8' });
|
|
79
83
|
assert.equal(result.status, 0, result.stderr);
|
|
80
84
|
const output = JSON.parse(result.stdout);
|
|
85
|
+
assert.equal(output.profiles, 2);
|
|
81
86
|
assert.equal(output.stores, 7);
|
|
82
|
-
assert.equal(output.changed,
|
|
87
|
+
assert.equal(output.changed, 9);
|
|
88
|
+
const readProjects = (dir) => JSON.parse(readFileSync(join(dir, 'profile.json'), 'utf8')).projects;
|
|
89
|
+
assert.deepEqual(readProjects(rootProfile), [{ path: appletProject, memory: 'preview' }]);
|
|
90
|
+
assert.deepEqual(readProjects(appletProfile), [
|
|
91
|
+
{ path: appletProject, memory: 'name' },
|
|
92
|
+
{ path: nestedOnlyProject, memory: 'content' },
|
|
93
|
+
]);
|
|
83
94
|
for (const path of [userDoc, rootDoc, appletDoc, projectDoc, deepDoc, pluginDoc, embeddedPluginDoc]) {
|
|
84
95
|
const migrated = readFileSync(path, 'utf8');
|
|
85
96
|
assert.doesNotMatch(migrated, /system-prompt-visibility|file-read-visibility/);
|
|
@@ -181,7 +181,7 @@ const configSet = defineLeaf({
|
|
|
181
181
|
name: 'sys config set',
|
|
182
182
|
summary: 'write a config value by dotted key',
|
|
183
183
|
params: [
|
|
184
|
-
{ kind: 'positional', name: 'key', type: 'string', required: true, constraint: `Dotted key path. User-scope scalar settings: ${SCALAR_SETTING_HELP}. Also supported: working_gerunds and whip_messages (non-empty JSON string arrays), brokerThresholds.warning and brokerThresholds.automaticReviveCap (user scope; integer >= 1), modelLadders.defaultProvider, and modelLadders.<anthropic|openai>.<ultra|strong|medium|light>. Keybindings are edited only through sys settings.` },
|
|
184
|
+
{ kind: 'positional', name: 'key', type: 'string', required: true, constraint: `Dotted key path. User-scope scalar settings: ${SCALAR_SETTING_HELP}. Also supported: working_gerunds and whip_messages (non-empty JSON string arrays), brokerThresholds.warning and brokerThresholds.automaticReviveCap (user scope; integer >= 1), modelLadders.defaultProvider, and modelLadders.<anthropic|openai>.<ultra|strong|medium|light>. Keybindings are edited only through sys settings. humanActions is an object map edited directly in ~/.crouter/config.json or <repo>/.crouter/config.json.` },
|
|
185
185
|
{ kind: 'flag', name: 'value', type: 'string', required: true, constraint: 'value VALUE — string, required. working_gerunds and whip_messages accept JSON arrays; other values are stored as-is if quoted and coerced to number or boolean when unambiguous.' },
|
|
186
186
|
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Scope to write to. Default: user. Project scope is supported only for modelLadders.' },
|
|
187
187
|
],
|
|
@@ -261,7 +261,7 @@ export const configBranch = defineBranch({
|
|
|
261
261
|
whenToUse: 'inspecting crtr settings, changing supported scalar values, or locating config.json. Keybinding changes belong in sys settings.',
|
|
262
262
|
help: {
|
|
263
263
|
name: 'sys config',
|
|
264
|
-
summary: 'read and write crtr configuration, including model ladders',
|
|
264
|
+
summary: 'read and write crtr configuration, including model ladders; edit humanActions maps directly in ~/.crouter/config.json or <repo>/.crouter/config.json',
|
|
265
265
|
},
|
|
266
266
|
children: [configGet, configSet, configPath],
|
|
267
267
|
});
|
|
@@ -13,11 +13,14 @@ import { readFault } from '../../core/runtime/fault.js';
|
|
|
13
13
|
import { detectNerdFont, nerdFontInstallCommand, NERD_FONT_SELECT_HINT } from '../../core/runtime/nerd-font.js';
|
|
14
14
|
import { detectPackageManager } from './setup-core.js';
|
|
15
15
|
import { validateEffectiveCommandPlugins } from '../../core/command-plugins/discovery.js';
|
|
16
|
+
import { discoverHookRegistry } from '../../core/command-hooks/discovery.js';
|
|
16
17
|
import { resolveBinContributions } from '../../core/runtime/bin-contributions.js';
|
|
18
|
+
import { inspectHumanActions } from '../../core/human-actions.js';
|
|
17
19
|
import { buildBrokerEnv, resolvePathExecutable } from '../../core/runtime/spawn-env.js';
|
|
18
20
|
import { listAllPlugins } from '../../core/resolver.js';
|
|
19
|
-
import { SUBTREE_NAMES, coreCommandPaths } from '../../build-root.js';
|
|
21
|
+
import { SUBTREE_NAMES, coreCommandPaths, coreHookCatalog } from '../../build-root.js';
|
|
20
22
|
import { processGeneration, processLocatedGeneration, runtimeManifest, selectedGeneration } from './shared.js';
|
|
23
|
+
import { CONFIG_FILE } from '../../types.js';
|
|
21
24
|
function pass(scope, name, message) {
|
|
22
25
|
return { scope, name, status: 'pass', message };
|
|
23
26
|
}
|
|
@@ -141,6 +144,34 @@ async function runCommandPluginChecks(scopes) {
|
|
|
141
144
|
}
|
|
142
145
|
return results;
|
|
143
146
|
}
|
|
147
|
+
/** Statically inspect the complete effective hook registry. This shares the
|
|
148
|
+
* runtime target catalog but never enters hook execution, preserving doctor as
|
|
149
|
+
* the recovery path for broken hooks. */
|
|
150
|
+
async function runHookPluginChecks(scopes) {
|
|
151
|
+
const inScope = new Set(scopes);
|
|
152
|
+
const registry = discoverHookRegistry(await coreHookCatalog());
|
|
153
|
+
const results = [];
|
|
154
|
+
for (const validation of registry.validations) {
|
|
155
|
+
const plugin = validation.plugin;
|
|
156
|
+
if (!inScope.has(plugin.scope))
|
|
157
|
+
continue;
|
|
158
|
+
const issues = registry.issues.filter((issue) => issue.plugin === plugin.name && issue.scope === plugin.scope);
|
|
159
|
+
if (issues.length === 0) {
|
|
160
|
+
const declarations = validation.hooks.map((hook) => `${hook.target} [${hook.phase} ${hook.op}]`).join('; ');
|
|
161
|
+
results.push(pass(plugin.scope, `plugin:${plugin.name}:hooks`, `effective hook declaration(s): ${declarations}`));
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
issues.forEach((issue, index) => {
|
|
165
|
+
const source = validation.manifestPath ?? join(plugin.root, '.crouter-plugin', 'plugin.json');
|
|
166
|
+
const detail = issue.path !== undefined ? `${source}:${issue.path}` : source;
|
|
167
|
+
results.push(failCheck(plugin.scope, `plugin:${plugin.name}:hooks[${index}]`, `${issue.code} at ${detail}: ${issue.message} (received ${issue.received}, expected ${issue.expected})`, {
|
|
168
|
+
kind: 'human_action',
|
|
169
|
+
description: `${issue.next} Inspect with \`crtr pkg plugin show ${plugin.name}\`; then disable (\`crtr pkg plugin disable ${plugin.name} --scope ${plugin.scope}\`), update (\`crtr pkg plugin update --name ${plugin.name}\`), or remove (\`crtr pkg plugin remove ${plugin.name}\`) the plugin.`,
|
|
170
|
+
}));
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return results;
|
|
174
|
+
}
|
|
144
175
|
/**
|
|
145
176
|
* Report every bare-binary contribution (`bin` in a plugin manifest or a scope
|
|
146
177
|
* `config.json`) that failed to reach a node's PATH, plus the ones that did.
|
|
@@ -169,6 +200,55 @@ function runBinContributionChecks(scopes) {
|
|
|
169
200
|
}
|
|
170
201
|
return results;
|
|
171
202
|
}
|
|
203
|
+
/** Validate every scope-configured completion action without executing it. */
|
|
204
|
+
function runHumanActionChecks(scopes) {
|
|
205
|
+
const inScope = new Set(scopes);
|
|
206
|
+
const userConfig = join(scopeRoot('user'), CONFIG_FILE);
|
|
207
|
+
const results = [];
|
|
208
|
+
const winningOrigins = new Map();
|
|
209
|
+
for (const action of inspectHumanActions(process.cwd())) {
|
|
210
|
+
if ('kind' in action) {
|
|
211
|
+
if (action.kind === 'unknown_action')
|
|
212
|
+
continue;
|
|
213
|
+
const scope = action.configOrigin === userConfig ? 'user' : 'project';
|
|
214
|
+
const block = action.block === true;
|
|
215
|
+
const winningOrigin = block ? undefined : winningOrigins.get(action.name);
|
|
216
|
+
if (!block && winningOrigin === undefined)
|
|
217
|
+
winningOrigins.set(action.name, action.configOrigin);
|
|
218
|
+
if (!inScope.has(scope))
|
|
219
|
+
continue;
|
|
220
|
+
const checkName = block ? `humanActions:block@${action.configOrigin}` : `humanActions:${action.name}@${action.configOrigin}`;
|
|
221
|
+
const remediation = {
|
|
222
|
+
kind: 'human_action',
|
|
223
|
+
description: block
|
|
224
|
+
? `Fix or remove the \`humanActions\` block in ${action.configOrigin}. Actions run argv directly without a shell.`
|
|
225
|
+
: `Fix or remove the \`humanActions.${action.name}\` declaration in ${action.configOrigin}. Actions run argv directly without a shell.`,
|
|
226
|
+
};
|
|
227
|
+
if (winningOrigin !== undefined) {
|
|
228
|
+
results.push({
|
|
229
|
+
scope,
|
|
230
|
+
name: checkName,
|
|
231
|
+
status: 'pass',
|
|
232
|
+
message: `warning: shadowed by ${winningOrigin}; ${action.configOrigin}: ${action.reason}`,
|
|
233
|
+
remediation,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
results.push(failCheck(scope, checkName, `${action.configOrigin}: ${action.reason}`, remediation));
|
|
238
|
+
}
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const scope = action.configOrigin === userConfig ? 'user' : 'project';
|
|
242
|
+
const winningOrigin = winningOrigins.get(action.name);
|
|
243
|
+
if (winningOrigin === undefined)
|
|
244
|
+
winningOrigins.set(action.name, action.configOrigin);
|
|
245
|
+
if (!inScope.has(scope))
|
|
246
|
+
continue;
|
|
247
|
+
const shadowedPrefix = winningOrigin === undefined ? '' : `shadowed by ${winningOrigin}; `;
|
|
248
|
+
results.push(pass(scope, `humanActions:${action.name}@${action.configOrigin}`, `${shadowedPrefix}${action.configOrigin} → ${action.argv[0]} (cwd ${action.cwd})`));
|
|
249
|
+
}
|
|
250
|
+
return results;
|
|
251
|
+
}
|
|
172
252
|
/** Check each enabled plugin's advisory PATH requirements against the exact
|
|
173
253
|
* broker environment a node launched from this cwd/profile receives. */
|
|
174
254
|
function runRequiresChecks(scopes) {
|
|
@@ -467,7 +547,7 @@ function runChecksForScope(scope, opts) {
|
|
|
467
547
|
export const sysDoctorLeaf = defineLeaf({
|
|
468
548
|
name: 'doctor',
|
|
469
549
|
description: 'diagnose installation health',
|
|
470
|
-
whenToUse: 'something in your crtr install looks off and you want it diagnosed — a plugin or marketplace manifest is missing, a config entry points at a directory that no longer exists, or a plugin command
|
|
550
|
+
whenToUse: 'something in your crtr install looks off and you want it diagnosed — a plugin or marketplace manifest is missing, a config entry points at a directory that no longer exists, or a plugin command/hook declaration is malformed. Reports each problem with a structured remediation, and can apply the safe repairs for you.',
|
|
471
551
|
help: {
|
|
472
552
|
name: 'sys doctor',
|
|
473
553
|
summary: 'diagnose runtime integrity, missing manifests, and broken config entries',
|
|
@@ -477,13 +557,13 @@ export const sysDoctorLeaf = defineLeaf({
|
|
|
477
557
|
{ kind: 'flag', name: 'remote', type: 'bool', required: false, constraint: 'Check git remotes with ls-remote (slow — makes network calls).' },
|
|
478
558
|
],
|
|
479
559
|
output: [
|
|
480
|
-
{ name: 'checks', type: 'object[]', required: true, constraint: 'Each: {scope, name, status, message, fixed?, remediation?}. status: pass | fail. scope may be user | project | canvas. bin:<name> checks report bare-binary contributions (a `bin` block in a plugin manifest or a scope config.json): a pass names the contributor and the resolved target, a fail names an unsafe/reserved command name, malformed target declaration, missing/non-executable/escaping target, or same-precedence name collision that installed nothing; their remediation is always a human_action. requires:<name> checks report each enabled plugin manifest PATH requirement: a pass names the plugin and resolved executable path; a fail names the plugin and carries its declared install hint as remediation. Runtime checks validate selected and process generation manifests, sealing/containment, and full SHA-256 inventories. terminal:nerd-font (user scope) reports whether a Nerd Font is installed for the viewer\'s icon glyphs; its remediation is always a human_action carrying the platform install command, never auto-fixed. Plugin
|
|
560
|
+
{ name: 'checks', type: 'object[]', required: true, constraint: 'Each: {scope, name, status, message, fixed?, remediation?}. status: pass | fail. scope may be user | project | canvas. bin:<name> checks report bare-binary contributions (a `bin` block in a plugin manifest or a scope config.json): a pass names the contributor and the resolved target, a fail names an unsafe/reserved command name, malformed target declaration, missing/non-executable/escaping target, or same-precedence name collision that installed nothing; their remediation is always a human_action. humanActions:<name>@<config-origin> checks validate each user/project scope completion-action declaration: action-name and argv shape, cwd directory, argv[0] regular-file existence, and execute permission; they never execute an action. A malformed effective declaration fails; a malformed shadowed declaration is a pass-level warning that names the winning and shadowed origins. requires:<name> checks report each enabled plugin manifest PATH requirement: a pass names the plugin and resolved executable path; a fail names the plugin and carries its declared install hint as remediation. Runtime checks validate selected and process generation manifests, sealing/containment, and full SHA-256 inventories. terminal:nerd-font (user scope) reports whether a Nerd Font is installed for the viewer\'s icon glyphs; its remediation is always a human_action carrying the platform install command, never auto-fixed. Plugin checks appear as plugin:<name>:commands and plugin:<name>:hooks; both are validated statically and never execute plugin code. Hook failures identify manifest/path/target/collision problems and carry disable/update/remove/show remediation; healthy hooks list effective target, phase, and op. remediation (when present) is {kind, description, ...payload} where kind is remove_config_key | rm_path | human_action; fault human_action remediations include the canonical logs command. Sorted by scope then name.' },
|
|
481
561
|
{ name: 'ok', type: 'boolean', required: true, constraint: 'True when no unresolved fail checks remain.' },
|
|
482
562
|
],
|
|
483
563
|
outputKind: 'object',
|
|
484
564
|
effects: [
|
|
485
|
-
'Read-only unless --fix is passed. Runtime verification reads and hashes every selected/process generation file; plugin command manifests, bare-binary (`bin`) declarations, and advisory `requires` PATH checks never execute plugin or contributed binaries.',
|
|
486
|
-
'With --fix: applies each non-pass check\'s `remediation` — removes stale config entries and deletes dangling plugin/marketplace directories; canvas fault findings and plugin command findings stay read-only (--fix never chmods or rewrites third-party plugin content).',
|
|
565
|
+
'Read-only unless --fix is passed. Runtime verification reads and hashes every selected/process generation file; plugin command and hook manifests, bare-binary (`bin`) declarations, humanActions declarations, and advisory `requires` PATH checks never execute plugin or contributed binaries.',
|
|
566
|
+
'With --fix: applies each non-pass check\'s `remediation` — removes stale config entries and deletes dangling plugin/marketplace directories; canvas fault findings and plugin command/hook findings stay read-only (--fix never chmods or rewrites third-party plugin content).',
|
|
487
567
|
'Each non-pass result carries a structured `remediation` describing the fix action (absolute paths, exact config keys, or a human_action with its command) so callers can apply it directly without --fix.',
|
|
488
568
|
],
|
|
489
569
|
},
|
|
@@ -500,7 +580,9 @@ export const sysDoctorLeaf = defineLeaf({
|
|
|
500
580
|
if (scopes.includes('user'))
|
|
501
581
|
allResults.push(runNerdFontCheck());
|
|
502
582
|
allResults.push(...await runCommandPluginChecks(scopes));
|
|
583
|
+
allResults.push(...await runHookPluginChecks(scopes));
|
|
503
584
|
allResults.push(...runBinContributionChecks(scopes));
|
|
585
|
+
allResults.push(...runHumanActionChecks(scopes));
|
|
504
586
|
allResults.push(...runRequiresChecks(scopes));
|
|
505
587
|
allResults.push(...await runFaultChecks());
|
|
506
588
|
const processLocation = processGeneration();
|