@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,21 +1,38 @@
|
|
|
1
1
|
---
|
|
2
2
|
kind: knowledge
|
|
3
|
-
when-and-why-to-read: When eliciting or writing a specification, this knowledge should be read because
|
|
4
|
-
short-form:
|
|
5
|
-
rationale:
|
|
3
|
+
when-and-why-to-read: When exploring, eliciting, or writing a specification, this knowledge should be read because the outcome has to be developed with the user before it is pinned down, and downstream design and planning then need it settled without avoidable questions or ceremony.
|
|
4
|
+
short-form: Explore openly with the user, converge on what they chose, then write a right-sized behavioral contract a downstream reader can use without guessing.
|
|
5
|
+
rationale: >-
|
|
6
|
+
Specification quality and elicitation guidance lived inside an always-loaded spec persona while the lightweight /spec command had almost none, leaving agents to choose between a vague one-shot and a fixed discovery workflow. Spec writers also promoted plausible nice-to-haves into requirements without asking, silently expanding the requested work. Every remaining lever then pointed at convergence — one interpretation reflected back, questions minimized, elicitation stopped as soon as no answer would change the contract — so the agent transcribed the request instead of developing it. The exploration is about the solution — challenging the user's premise is available when something genuinely does not fit, never a required move.
|
|
6
7
|
---
|
|
7
8
|
|
|
8
9
|
# Writing a specification
|
|
9
10
|
|
|
10
11
|
A specification settles **what outcome and behavior are required**. It is not an architecture document or an implementation plan. Its depth follows the stakes and unresolved intent: a small reversible change may need a few paragraphs; a consequential product surface may need collaborative discovery and separate design.
|
|
11
12
|
|
|
13
|
+
## Explore before you converge
|
|
14
|
+
|
|
15
|
+
Take the request at face value and put the openness into what it could be. Understand what the user is trying to achieve and why now, then develop the possibilities with them: a specification is the output of a finished exploration, and the first shape anyone thinks of is rarely the best one available.
|
|
16
|
+
|
|
17
|
+
Develop a few genuinely different directions rather than enumerating shallow variants, and push each one several steps — what changes, what that makes possible next, what it looks like once it exists. Moves that open a direction: remove a constraint everyone assumed; change who or what is served; do materially less than asked and see what survives; ask what happens if nothing changes at all. Where the surrounding system or prior art would feed the thinking, read for it while you think, not as a validation pass afterward.
|
|
18
|
+
|
|
19
|
+
Bring these to the user as live options, in plain language, with what each buys and closes off. Do not open with objections, feasibility verdicts, or a recommendation, and do not pre-reject an unusual but coherent direction — nothing is committed until the user picks, so divergence is free. If something in the request genuinely does not fit what they are trying to achieve, say so once; questioning their premise is not the job.
|
|
20
|
+
|
|
21
|
+
Converge when the user has chosen among live options and what remains is detail.
|
|
22
|
+
|
|
12
23
|
## Elicit without interrogating
|
|
13
24
|
|
|
14
|
-
Investigate before asking. Read the request, relevant code and documents, and already-settled decisions first. A fact available from the project is not a question for the user.
|
|
25
|
+
Investigate before asking. Read the request, relevant code and documents, and already-settled decisions first. A fact available from the project is not a question for the user; intent never is such a fact.
|
|
26
|
+
|
|
27
|
+
Reflect a concrete interpretation back so the user can confirm or correct it. Resolve the uncertainty whose answer could most change behavior, scope, or acceptance. When a decision really belongs to the user, give them a focused question with a proposed default or concrete options; use one decision or one small coherent set rather than a questionnaire.
|
|
28
|
+
|
|
29
|
+
Spend attention where judgment is load-bearing, not where detail is merely available. Keep settled points moving and fold each answer into the specification as current truth. Once converged, stop eliciting when another answer would not materially change the behavioral contract. Explicit approval is warranted when the user is co-authoring the document or the remaining decision is consequential; ordinary reversible work does not need a ritual approval loop.
|
|
30
|
+
|
|
31
|
+
## Commit only what the user chose
|
|
15
32
|
|
|
16
|
-
|
|
33
|
+
Exploration is unbounded; the document is not. What you explored and the user did not choose stays out — speculative features, future extensibility, adjacent cleanup, and other merely plausible additions are not requirements just because they came up. The bar is not smallness for its own sake: nothing enters the specification without the user's assent.
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
When something seems likely desirable but is not explicitly or implicitly required by the request, ask the user whether to include it through `crtr human` before finishing the specification (`crtr human send -h`), wait for their answer, and make the resulting boundary explicit. Do not hide the addition in an assumption, recommendation, or optional requirement.
|
|
19
36
|
|
|
20
37
|
## The finished specification
|
|
21
38
|
|
|
@@ -164,7 +164,7 @@ function selectedProfileProjects(): string[] {
|
|
|
164
164
|
const profileId = process.env["CRTR_PROFILE_ID"] ?? "";
|
|
165
165
|
if (profileId === "") return [];
|
|
166
166
|
try {
|
|
167
|
-
return loadProfileManifest(profileId).manifest.projects;
|
|
167
|
+
return loadProfileManifest(profileId).manifest.projects.map((project) => project.path);
|
|
168
168
|
} catch {
|
|
169
169
|
// A deleted or invalid selected profile must not hide cwd-local commands.
|
|
170
170
|
return [];
|
|
@@ -4,6 +4,7 @@ import { promisify } from "node:util";
|
|
|
4
4
|
import { readFileSync, existsSync, realpathSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
|
+
import type { ChatCommandMetadata } from "../../../../core/runtime/command-surface.js";
|
|
7
8
|
|
|
8
9
|
const exec = promisify(execFile);
|
|
9
10
|
|
|
@@ -149,6 +150,11 @@ export default async function (pi: ExtensionAPI) {
|
|
|
149
150
|
const invocation = `Run \`crtr ${path.join(" ")} -h\` (pass args to run the real command)`;
|
|
150
151
|
pi.registerCommand(name, {
|
|
151
152
|
description: node.description ? `${node.description} — ${invocation}` : invocation,
|
|
153
|
+
// Chat-capable: the handler's outcome is the command's output placed into
|
|
154
|
+
// the conversation as a displayed message. The transient status line is
|
|
155
|
+
// decoration a surface without one simply does not show.
|
|
156
|
+
gateway: true as const,
|
|
157
|
+
argumentHint: "[args]",
|
|
152
158
|
handler: async (args, ctx) => {
|
|
153
159
|
const extra = (args ?? "").trim();
|
|
154
160
|
// No args -> show help. With args -> run the real command.
|
|
@@ -165,7 +171,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
165
171
|
{ triggerTurn: TRIGGER_TURN },
|
|
166
172
|
);
|
|
167
173
|
},
|
|
168
|
-
});
|
|
174
|
+
} as Parameters<ExtensionAPI["registerCommand"]>[1] & ChatCommandMetadata);
|
|
169
175
|
};
|
|
170
176
|
|
|
171
177
|
const filters = loadFilters();
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type DeterministicCommandExpansion,
|
|
7
7
|
} from "../../../core/runtime/command-expansion.js";
|
|
8
8
|
import { expandShellBlocks, hasShellBlocks, DEFAULT_SHELL_TIMEOUT_MS } from "../../../core/runtime/shell-expansion.js";
|
|
9
|
+
import type { ChatCommandMetadata } from "../../../core/runtime/command-surface.js";
|
|
9
10
|
import { piShellRunner } from "./pi-shell-runner.js";
|
|
10
11
|
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
@@ -43,10 +44,9 @@ export interface SlashDoc {
|
|
|
43
44
|
description: string;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
// `memory list` already chose
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
const resolvedMemoryPaths = new Map<string, string>();
|
|
47
|
+
// `memory list` already chose each winning document. Keep its canonical body
|
|
48
|
+
// for disclosure classification and its path for the existing execution read.
|
|
49
|
+
const resolvedMemoryDocs = new Map<string, { path: string; canonicalBody: string }>();
|
|
50
50
|
|
|
51
51
|
async function defaultListMemoryDocs(): Promise<MemoryListItem[]> {
|
|
52
52
|
const snapshot = createMemoryDocSnapshot();
|
|
@@ -67,15 +67,24 @@ async function defaultListMemoryDocs(): Promise<MemoryListItem[]> {
|
|
|
67
67
|
return item.slash === true ? [item.name] : [];
|
|
68
68
|
});
|
|
69
69
|
const resolved = snapshot.resolve(names);
|
|
70
|
-
|
|
71
|
-
for (const [name, doc] of resolved)
|
|
70
|
+
resolvedMemoryDocs.clear();
|
|
71
|
+
for (const [name, doc] of resolved) {
|
|
72
|
+
resolvedMemoryDocs.set(name, { path: doc.path, canonicalBody: doc.body });
|
|
73
|
+
}
|
|
72
74
|
return items;
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
function defaultReadMemoryDoc(name: string): string {
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
return
|
|
77
|
+
function defaultReadMemoryDoc(name: string): { canonicalBody: string; executionBody: string } {
|
|
78
|
+
const resolved = resolvedMemoryDocs.get(name);
|
|
79
|
+
if (resolved === undefined) throw new Error(`memory slash document was not resolved: ${name}`);
|
|
80
|
+
return {
|
|
81
|
+
canonicalBody: resolved.canonicalBody,
|
|
82
|
+
executionBody: readMemoryDocContent(resolved.path),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function hasNodeRelativeExpansion(body: string): boolean {
|
|
87
|
+
return body.includes("$CRTR_CONTEXT_DIR") || body.includes("$CRTR_NODE_ID");
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
/** `/`-joined path-derived doc name -> the registered slash-command name. */
|
|
@@ -109,24 +118,33 @@ export default async function (pi: ExtensionAPI) {
|
|
|
109
118
|
const docs = await discoverSlashDocs();
|
|
110
119
|
for (const doc of docs) {
|
|
111
120
|
let expansion: DeterministicCommandExpansion | undefined;
|
|
121
|
+
let discloseExpansion = false;
|
|
112
122
|
let loadError: unknown;
|
|
113
123
|
try {
|
|
124
|
+
const body = defaultReadMemoryDoc(doc.name);
|
|
114
125
|
expansion = {
|
|
115
126
|
kind: "memory-slash",
|
|
116
127
|
commandName: doc.commandName,
|
|
117
|
-
body:
|
|
128
|
+
body: body.executionBody,
|
|
118
129
|
};
|
|
130
|
+
discloseExpansion = !hasNodeRelativeExpansion(body.canonicalBody);
|
|
119
131
|
} catch (err) {
|
|
120
132
|
loadError = err;
|
|
121
133
|
}
|
|
122
134
|
|
|
123
135
|
// pi preserves unknown registration fields on the resolved command object.
|
|
124
|
-
// The broker discloses
|
|
125
|
-
//
|
|
126
|
-
//
|
|
136
|
+
// The broker discloses deterministic expansions through get_commands. A
|
|
137
|
+
// node-relative body still executes through the private expansion below,
|
|
138
|
+
// but cannot promise a concrete payload before the future node exists.
|
|
127
139
|
pi.registerCommand(doc.commandName, {
|
|
128
140
|
description: doc.description,
|
|
129
|
-
|
|
141
|
+
// Chat-capable: the handler's whole outcome is a conversation turn
|
|
142
|
+
// carrying the expanded document, which any surface hosting the
|
|
143
|
+
// conversation receives. Only when the expansion loaded — without it the
|
|
144
|
+
// sole outcome is a terminal-only error notify, so it stays undisclosed.
|
|
145
|
+
...(expansion === undefined
|
|
146
|
+
? {}
|
|
147
|
+
: { gateway: true as const, ...(discloseExpansion ? { expansion } : {}) }),
|
|
130
148
|
handler: async (args, ctx) => {
|
|
131
149
|
if (expansion === undefined) {
|
|
132
150
|
ctx.ui.notify(
|
|
@@ -157,6 +175,6 @@ export default async function (pi: ExtensionAPI) {
|
|
|
157
175
|
{ triggerTurn: true },
|
|
158
176
|
);
|
|
159
177
|
},
|
|
160
|
-
} as Parameters<ExtensionAPI["registerCommand"]>[1] & { expansion?: DeterministicCommandExpansion });
|
|
178
|
+
} as Parameters<ExtensionAPI["registerCommand"]>[1] & { expansion?: DeterministicCommandExpansion } & ChatCommandMetadata);
|
|
161
179
|
}
|
|
162
180
|
}
|
|
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
|
|
|
2
2
|
import test from 'node:test';
|
|
3
3
|
import { findRefCompletionContext, RefAwareAutocompleteProvider } from '../input/ref-autocomplete.js';
|
|
4
4
|
function ref(name, shortForm = `${name} docs`) {
|
|
5
|
-
return { name, kind: 'knowledge', scope: 'user', shortForm };
|
|
5
|
+
return { name, kind: 'knowledge', scope: 'user', shortForm, gatewayVisible: true };
|
|
6
6
|
}
|
|
7
7
|
/** The wrapped provider; ref handling must never reach it in a ref context. */
|
|
8
8
|
const inertDelegate = {
|
|
@@ -47,7 +47,7 @@ test('Enter-confirming a non-leading ref completion accepts the token and return
|
|
|
47
47
|
editor.onSubmit = (text) => {
|
|
48
48
|
submitted = text;
|
|
49
49
|
};
|
|
50
|
-
const refs = [{ name: 'dev', kind: 'knowledge', scope: 'user', shortForm: 'dev docs' }];
|
|
50
|
+
const refs = [{ name: 'dev', kind: 'knowledge', scope: 'user', shortForm: 'dev docs', gatewayVisible: true }];
|
|
51
51
|
const delegate = {
|
|
52
52
|
async getSuggestions() {
|
|
53
53
|
return null;
|
|
@@ -2,7 +2,7 @@ import { homedir } from 'node:os';
|
|
|
2
2
|
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
3
3
|
import { Input, SelectList, getKeybindings, } from '@earendil-works/pi-tui';
|
|
4
4
|
import { getSelectListTheme } from '@earendil-works/pi-coding-agent';
|
|
5
|
-
import {
|
|
5
|
+
import { openReviewWindow } from '../../inbox/review/launch.js';
|
|
6
6
|
import { createReviewClient, ReviewTerminalError, terminalRefusalNotice } from '../../inbox/review/review-client.js';
|
|
7
7
|
import { checkReviewableFile, extractFilePaths } from '#core/human/visible-paths';
|
|
8
8
|
const MAX_VISIBLE_ROWS = 12;
|
|
@@ -123,6 +123,6 @@ export async function launchFileReview(host, file, opts) {
|
|
|
123
123
|
opts.onNotice(err instanceof ReviewTerminalError ? terminalRefusalNotice(err) : err instanceof Error ? err.message : String(err));
|
|
124
124
|
return;
|
|
125
125
|
}
|
|
126
|
-
|
|
126
|
+
openReviewWindow(host, review);
|
|
127
127
|
}
|
|
128
128
|
export { extractFilePaths };
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
// GLYPHS. Private-use `U+XXXX` references become their glyphs (see
|
|
23
23
|
// glyph-codepoints.ts), so an agent that cannot type a Nerd Font character can
|
|
24
24
|
// still put one on screen.
|
|
25
|
+
import { pathToFileURL } from 'node:url';
|
|
25
26
|
import { expandGlyphCodepoints } from './glyph-codepoints.js';
|
|
26
27
|
const ATX_HEADING = /^(\s{0,3})(#{3,6})(?:[ \t]+|$)(.*?)([ \t]+#+[ \t]*)?$/;
|
|
27
28
|
const FENCE_OPEN = /^\s{0,3}(`{3,}|~{3,})/;
|
|
@@ -126,7 +127,111 @@ export function styleAttachMarkdownSource(markdown) {
|
|
|
126
127
|
* message keeps `styleAttachMarkdownSource` alone — the `U+XXXX` escape hatch
|
|
127
128
|
* exists for agents that cannot type a Nerd Font character. */
|
|
128
129
|
export function styleAttachAgentMarkdown(markdown) {
|
|
129
|
-
return expandGlyphCodepoints(styleAttachMarkdownSource(markdown));
|
|
130
|
+
return expandGlyphCodepoints(styleAttachMarkdownSource(rewriteAbsoluteFileLinks(markdown)));
|
|
131
|
+
}
|
|
132
|
+
/** Give iTerm a hidden fragment so its Semantic History action receives the local target. */
|
|
133
|
+
function rewriteAbsoluteFileLinks(markdown) {
|
|
134
|
+
let fence;
|
|
135
|
+
return markdown.split('\n').map((line) => {
|
|
136
|
+
const fenceMatch = FENCE_OPEN.exec(line);
|
|
137
|
+
if (fence !== undefined) {
|
|
138
|
+
if (fenceMatch?.[1][0] === fence)
|
|
139
|
+
fence = undefined;
|
|
140
|
+
return line;
|
|
141
|
+
}
|
|
142
|
+
if (fenceMatch) {
|
|
143
|
+
fence = fenceMatch[1][0];
|
|
144
|
+
return line;
|
|
145
|
+
}
|
|
146
|
+
return rewriteInlineFileLinks(line);
|
|
147
|
+
}).join('\n');
|
|
148
|
+
}
|
|
149
|
+
function rewriteInlineFileLinks(line) {
|
|
150
|
+
let result = '';
|
|
151
|
+
let rest = line;
|
|
152
|
+
while (rest !== '') {
|
|
153
|
+
const opening = rest.match(/`+/);
|
|
154
|
+
const prose = opening === null ? rest : rest.slice(0, opening.index);
|
|
155
|
+
result += rewriteProseFileLinks(prose);
|
|
156
|
+
if (opening === null)
|
|
157
|
+
break;
|
|
158
|
+
const delimiter = opening[0];
|
|
159
|
+
const end = rest.indexOf(delimiter, prose.length + delimiter.length);
|
|
160
|
+
if (end === -1)
|
|
161
|
+
return result + rest.slice(prose.length);
|
|
162
|
+
result += rest.slice(prose.length, end + delimiter.length);
|
|
163
|
+
rest = rest.slice(end + delimiter.length);
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
function rewriteProseFileLinks(prose) {
|
|
168
|
+
let result = '';
|
|
169
|
+
let index = 0;
|
|
170
|
+
while (index < prose.length) {
|
|
171
|
+
const start = prose.indexOf('[', index);
|
|
172
|
+
if (start === -1)
|
|
173
|
+
return result + prose.slice(index);
|
|
174
|
+
const link = parseLocalMarkdownLink(prose, start);
|
|
175
|
+
if (link === undefined) {
|
|
176
|
+
result += prose.slice(index, start + 1);
|
|
177
|
+
index = start + 1;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
result += prose.slice(index, start) + `[${link.label}](${pathToFileURL(link.path).href}#crtr-file-open)`;
|
|
181
|
+
index = link.end;
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
function parseLocalMarkdownLink(line, start) {
|
|
186
|
+
const labelEnd = closingDelimiter(line, start + 1, ']');
|
|
187
|
+
if (labelEnd === undefined || line[labelEnd + 1] !== '(')
|
|
188
|
+
return undefined;
|
|
189
|
+
const destinationStart = labelEnd + 2;
|
|
190
|
+
if (line[destinationStart] === '<') {
|
|
191
|
+
const destinationEnd = closingDelimiter(line, destinationStart + 1, '>');
|
|
192
|
+
if (destinationEnd === undefined || line[destinationEnd + 1] !== ')')
|
|
193
|
+
return undefined;
|
|
194
|
+
const path = localPathFromDestination(line.slice(destinationStart + 1, destinationEnd));
|
|
195
|
+
return path === undefined ? undefined : { label: line.slice(start + 1, labelEnd), path, end: destinationEnd + 2 };
|
|
196
|
+
}
|
|
197
|
+
let depth = 0;
|
|
198
|
+
for (let index = destinationStart; index < line.length; index++) {
|
|
199
|
+
if (line[index] === '\\') {
|
|
200
|
+
index++;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (line[index] === '(') {
|
|
204
|
+
depth++;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (line[index] !== ')' || depth-- > 0)
|
|
208
|
+
continue;
|
|
209
|
+
const path = localPathFromDestination(line.slice(destinationStart, index));
|
|
210
|
+
return path === undefined ? undefined : { label: line.slice(start + 1, labelEnd), path, end: index + 1 };
|
|
211
|
+
}
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
function closingDelimiter(text, start, delimiter) {
|
|
215
|
+
for (let index = start; index < text.length; index++) {
|
|
216
|
+
if (text[index] === '\\') {
|
|
217
|
+
index++;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (text[index] === delimiter)
|
|
221
|
+
return index;
|
|
222
|
+
}
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
function localPathFromDestination(destination) {
|
|
226
|
+
const unescaped = destination.replace(/\\([!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~-])/g, '$1');
|
|
227
|
+
let path = unescaped;
|
|
228
|
+
try {
|
|
229
|
+
path = decodeURIComponent(unescaped);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// A literal percent is legal in a path but not in URI decoding.
|
|
233
|
+
}
|
|
234
|
+
return path.startsWith('/') ? path : undefined;
|
|
130
235
|
}
|
|
131
236
|
/** Copy a pi message for terminal rendering and decorate only Markdown-bearing
|
|
132
237
|
* text/thinking blocks. The broker's source event is never mutated. */
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export
|
|
3
|
-
|
|
1
|
+
/** A local file requested through iTerm Semantic History. */
|
|
2
|
+
export interface FileOpenInput {
|
|
3
|
+
file: string | undefined;
|
|
4
|
+
remaining: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Strip the private iTerm Semantic History input and return its local path.
|
|
8
|
+
* iTerm expands `\\1` as a shell-escaped word, so decode that representation
|
|
9
|
+
* without ever evaluating it as shell input.
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractFileOpenInput(data: string): FileOpenInput | undefined;
|
|
12
|
+
export type LinkedFileOpenResult = 'opened' | 'missing' | 'failed';
|
|
4
13
|
/** Replace this local viewer pane with Neovim, then reattach it when Neovim exits. */
|
|
5
14
|
export declare function openLinkedFileInPane(opts: {
|
|
6
|
-
|
|
15
|
+
file: string;
|
|
7
16
|
pane: string;
|
|
8
17
|
cwd: string;
|
|
9
18
|
nodeId: string;
|
|
@@ -1,72 +1,68 @@
|
|
|
1
|
-
// session/file-links.ts —
|
|
1
|
+
// session/file-links.ts — iTerm Semantic History file-open bridge.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// and OSC-8 boundaries have all already been composed there.
|
|
3
|
+
// iTerm sends the clicked path through a private input sequence. The viewer
|
|
4
|
+
// validates it, replaces its pane with Neovim, then reattaches after exit.
|
|
6
5
|
import { existsSync } from 'node:fs';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { walkRow } from '../ansi-cells.js';
|
|
9
6
|
import { respawnPaneInBackground, shellQuote, viewerSplitEnv } from '../../../core/runtime/placement-tmux.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
const FILE_OPEN_PREFIX = '\x1b_crtr-file-open;';
|
|
8
|
+
const STRING_TERMINATOR = '\x1b\\';
|
|
9
|
+
/**
|
|
10
|
+
* Strip the private iTerm Semantic History input and return its local path.
|
|
11
|
+
* iTerm expands `\\1` as a shell-escaped word, so decode that representation
|
|
12
|
+
* without ever evaluating it as shell input.
|
|
13
|
+
*/
|
|
14
|
+
export function extractFileOpenInput(data) {
|
|
15
|
+
const start = data.indexOf(FILE_OPEN_PREFIX);
|
|
16
|
+
if (start === -1)
|
|
13
17
|
return undefined;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return scanned.uri;
|
|
21
|
-
}
|
|
22
|
-
return undefined;
|
|
18
|
+
const pathStart = start + FILE_OPEN_PREFIX.length;
|
|
19
|
+
const end = data.indexOf(STRING_TERMINATOR, pathStart);
|
|
20
|
+
if (end === -1)
|
|
21
|
+
return undefined;
|
|
22
|
+
const file = decodeShellEscapedPath(data.slice(pathStart, end));
|
|
23
|
+
return { file, remaining: data.slice(0, start) + data.slice(end + STRING_TERMINATOR.length) };
|
|
23
24
|
}
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
walkRow(line, {
|
|
28
|
-
onEscape: (escape) => {
|
|
29
|
-
const osc8 = parseOsc8(escape);
|
|
30
|
-
if (osc8 !== null)
|
|
31
|
-
activeUri = osc8 === '' ? undefined : osc8;
|
|
32
|
-
},
|
|
33
|
-
onGlyph: (glyph) => {
|
|
34
|
-
if (uri !== undefined || hitColumn === undefined || activeUri === undefined || glyph.width === 0)
|
|
35
|
-
return;
|
|
36
|
-
const cell = hitColumn - 1;
|
|
37
|
-
if (cell >= glyph.cell && cell < glyph.cell + glyph.width)
|
|
38
|
-
uri = activeUri;
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
return { activeUri, uri };
|
|
25
|
+
function decodeShellEscapedPath(value) {
|
|
26
|
+
const decoded = value.startsWith("'") ? decodeSingleQuotedPath(value) : decodeBackslashPath(value);
|
|
27
|
+
return decoded !== undefined && decoded.startsWith('/') && !/[\x00-\x1f\x7f]/.test(decoded) ? decoded : undefined;
|
|
42
28
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
29
|
+
function decodeSingleQuotedPath(value) {
|
|
30
|
+
let result = '';
|
|
31
|
+
let index = 1;
|
|
32
|
+
while (true) {
|
|
33
|
+
const end = value.indexOf("'", index);
|
|
34
|
+
if (end === -1)
|
|
35
|
+
return undefined;
|
|
36
|
+
result += value.slice(index, end);
|
|
37
|
+
index = end + 1;
|
|
38
|
+
if (index === value.length)
|
|
39
|
+
return result;
|
|
40
|
+
if (value.slice(index, index + 3) !== "\\''")
|
|
41
|
+
return undefined;
|
|
42
|
+
result += "'";
|
|
43
|
+
index += 3;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function decodeBackslashPath(value) {
|
|
47
|
+
let result = '';
|
|
48
|
+
for (let index = 0; index < value.length; index++) {
|
|
49
|
+
if (value[index] !== '\\') {
|
|
50
|
+
result += value[index];
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const escaped = value[++index];
|
|
54
|
+
if (escaped === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
result += escaped;
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
53
59
|
}
|
|
54
60
|
/** Replace this local viewer pane with Neovim, then reattach it when Neovim exits. */
|
|
55
61
|
export function openLinkedFileInPane(opts) {
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
const url = new URL(opts.uri);
|
|
59
|
-
if (url.protocol !== 'file:')
|
|
60
|
-
return 'invalid';
|
|
61
|
-
file = fileURLToPath(url);
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return 'invalid';
|
|
65
|
-
}
|
|
66
|
-
if (!existsSync(file))
|
|
62
|
+
if (!existsSync(opts.file))
|
|
67
63
|
return 'missing';
|
|
68
64
|
const attach = `crtr surface attach to ${shellQuote(opts.nodeId)}${opts.observer ? ' --observer' : ''}`;
|
|
69
|
-
const command = `nvim ${shellQuote(file)} || :; exec ${attach}`;
|
|
65
|
+
const command = `nvim ${shellQuote(opts.file)} || :; exec ${attach}`;
|
|
70
66
|
opts.beforeRespawn();
|
|
71
67
|
return respawnPaneInBackground({ pane: opts.pane, cwd: opts.cwd, env: viewerSplitEnv(), command }) ? 'opened' : 'failed';
|
|
72
68
|
}
|
|
@@ -31,7 +31,7 @@ export interface KeyHooks {
|
|
|
31
31
|
toggleInboxStrip: () => void;
|
|
32
32
|
cycleModelLadder: (direction: 'forward' | 'backward') => void;
|
|
33
33
|
inspectLoadedCommand: () => void;
|
|
34
|
-
/** The file-review chord —
|
|
34
|
+
/** The file-review chord — pick a transcript file and open its review window. */
|
|
35
35
|
openFileReview: () => void;
|
|
36
36
|
/** Search the current node's local profile project files. */
|
|
37
37
|
openProfileFiles: () => void;
|
|
@@ -50,7 +50,7 @@ function profileSearchRoots(profileId) {
|
|
|
50
50
|
return { projects: [], memory: null };
|
|
51
51
|
try {
|
|
52
52
|
const { profileId: resolvedId, manifest } = loadProfileManifest(profileId);
|
|
53
|
-
return { projects: manifest.projects, memory: profileMemoryDir(resolvedId) };
|
|
53
|
+
return { projects: manifest.projects.map((project) => project.path), memory: profileMemoryDir(resolvedId) };
|
|
54
54
|
}
|
|
55
55
|
catch {
|
|
56
56
|
return { projects: [], memory: null };
|