@north-light/crouter 0.3.227 → 0.3.229
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 +5 -5
- package/dist/api/client.js +3 -3
- package/dist/api/dto/broker-ops.d.ts +8 -0
- package/dist/api/dto/memory.d.ts +8 -7
- package/dist/api/dto/memory.js +2 -2
- package/dist/builtin-memory/04-base-worker.md +1 -1
- package/dist/builtin-memory/insights/init.md +5 -5
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/agent-shaping.md +11 -11
- package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +6 -6
- package/dist/builtin-memory/internal/plugins.md +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +50 -37
- package/dist/clients/attach/__tests__/pane-tag-successor.test.js +56 -0
- package/dist/clients/attach/__tests__/profile-files.test.js +21 -0
- package/dist/clients/attach/chrome/inbox-strip.js +11 -7
- package/dist/clients/attach/session/pane-tag.d.ts +28 -2
- package/dist/clients/attach/session/pane-tag.js +48 -22
- package/dist/clients/attach/session/profile-files.d.ts +6 -0
- package/dist/clients/attach/session/profile-files.js +44 -11
- package/dist/clients/attach/slash/dispatch.js +4 -7
- package/dist/clients/attach/viewer.js +588 -596
- package/dist/commands/__tests__/surface-reload-target.test.js +21 -0
- package/dist/commands/api-client.d.ts +3 -7
- package/dist/commands/api-client.js +7 -12
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +292 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +146 -0
- package/dist/commands/memory/delete.js +51 -24
- package/dist/commands/memory/edit.js +50 -10
- package/dist/commands/memory/find.js +105 -56
- package/dist/commands/memory/history.js +51 -31
- package/dist/commands/memory/lint.d.ts +0 -9
- package/dist/commands/memory/lint.js +78 -322
- package/dist/commands/memory/list.d.ts +12 -4
- package/dist/commands/memory/list.js +64 -25
- package/dist/commands/memory/move.js +103 -73
- package/dist/commands/memory/origin.js +35 -5
- package/dist/commands/memory/read.d.ts +4 -0
- package/dist/commands/memory/read.js +132 -80
- package/dist/commands/memory/shared.d.ts +115 -25
- package/dist/commands/memory/shared.js +331 -74
- package/dist/commands/memory/write.js +72 -21
- package/dist/commands/memory.js +2 -2
- package/dist/commands/node/create.d.ts +1 -1
- package/dist/commands/node/create.js +1 -1
- package/dist/commands/node/inspect.js +1 -1
- package/dist/commands/node/lifecycle.js +5 -5
- package/dist/commands/pkg/market-manage.js +25 -24
- package/dist/commands/pkg/plugin-manage.d.ts +22 -6
- package/dist/commands/pkg/plugin-manage.js +118 -25
- package/dist/commands/pkg/shared.d.ts +8 -0
- package/dist/commands/pkg/shared.js +23 -20
- package/dist/commands/surface-reload.d.ts +5 -0
- package/dist/commands/surface-reload.js +9 -1
- package/dist/commands/sys/__tests__/migrate.test.js +1140 -22
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +218 -0
- package/dist/commands/sys/migrate.js +126 -140
- package/dist/commands/sys/panels/profiles-panel.d.ts +66 -0
- package/dist/commands/sys/panels/profiles-panel.js +599 -0
- package/dist/commands/sys/settings-shell.d.ts +4 -2
- package/dist/commands/sys/settings-shell.js +35 -4
- package/dist/commands/sys/settings.js +21 -5
- package/dist/commands/sys/sync-deps.d.ts +2 -0
- package/dist/commands/sys/sync-deps.js +26 -15
- package/dist/commands/sys/sync-project-guidance.js +228 -145
- package/dist/commands/sys/sync-skills.js +28 -17
- package/dist/commands/sys/update.js +11 -3
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +15 -2
- package/dist/core/__tests__/config-change-delta.test.js +106 -0
- package/dist/core/__tests__/context-intro.test.js +13 -9
- package/dist/core/__tests__/daemon-boot.test.js +22 -16
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +5 -1
- package/dist/core/__tests__/human-deliver.test.js +19 -0
- package/dist/core/__tests__/inline-memory-refs.test.js +50 -46
- package/dist/core/__tests__/{serial → integration}/broker-fork-seam.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-sdk-wiring.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-snapshot-history.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/command-plugins.test.js +94 -3
- package/dist/core/__tests__/{serial → integration}/deferred-no-wake.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/flagship-lifecycle.test.js +13 -27
- package/dist/core/__tests__/{serial → integration}/host-teardown-process-group.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/human-deliver-e2e.test.js +1 -1
- package/dist/core/__tests__/{live-mutation-verbs.test.js → integration/live-mutation-verbs.test.js} +22 -33
- package/dist/core/__tests__/{serial → integration}/live-mutation.test.js +21 -41
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/refresh-stall-recycle.test.js +1 -1
- package/dist/core/__tests__/integration/revive.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/revive.test.js +38 -19
- package/dist/core/__tests__/integration/spawn-root.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/spawn-root.test.js +104 -1
- package/dist/core/__tests__/integration/subscription-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/subscription-delivery.test.js +1 -1
- package/dist/core/__tests__/integration/tmux-surface.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/tmux-surface.test.js +1 -1
- package/dist/core/__tests__/integration/worktree-land.test.d.ts +1 -0
- package/dist/core/__tests__/integration/worktree-land.test.js +400 -0
- package/dist/core/__tests__/integration/worktree-reap.test.d.ts +1 -0
- package/dist/core/__tests__/{serial/worktree.test.js → integration/worktree-reap.test.js} +6 -338
- package/dist/core/__tests__/kickoff.test.js +16 -5
- package/dist/core/__tests__/memory-resolver-precedence.test.js +122 -91
- package/dist/core/__tests__/nested-store-discovery.test.js +5 -3
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +39 -27
- package/dist/core/__tests__/on-read-nested-store.test.js +19 -13
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +138 -44
- package/dist/core/__tests__/repository-association.test.d.ts +1 -0
- package/dist/core/__tests__/repository-association.test.js +153 -0
- package/dist/core/__tests__/repository-root-identity.test.d.ts +1 -0
- package/dist/core/__tests__/repository-root-identity.test.js +219 -0
- package/dist/core/__tests__/review-model-floor.test.js +12 -4
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +27 -11
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.d.ts +1 -0
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +40 -0
- package/dist/core/canvas/canvas.d.ts +3 -0
- package/dist/core/canvas/canvas.js +30 -10
- package/dist/core/canvas/db.js +28 -1
- package/dist/core/canvas/paths.d.ts +4 -6
- package/dist/core/canvas/paths.js +8 -6
- package/dist/core/canvas/render-source.js +2 -2
- package/dist/core/canvas/types.d.ts +9 -13
- package/dist/core/config.d.ts +3 -3
- package/dist/core/config.js +3 -3
- package/dist/core/exclusive-lock.d.ts +2 -0
- package/dist/core/exclusive-lock.js +21 -0
- package/dist/core/git.d.ts +0 -1
- package/dist/core/git.js +0 -3
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +1 -0
- package/dist/core/human/feedback-companion.js +3 -0
- package/dist/core/human/scan.js +8 -1
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +2 -1
- package/dist/core/memory/doc-link-grammar.js +2 -2
- package/dist/core/memory/history.d.ts +24 -0
- package/dist/core/memory/history.js +66 -1
- package/dist/core/memory/identity.d.ts +65 -0
- package/dist/core/memory/identity.js +185 -0
- package/dist/core/memory/inline-ref-guidance.d.ts +1 -1
- package/dist/core/memory/inline-ref-guidance.js +1 -1
- package/dist/core/memory/inline-ref-inventory.d.ts +3 -10
- package/dist/core/memory/inline-ref-inventory.js +30 -62
- package/dist/core/memory/lint.d.ts +129 -0
- package/dist/core/memory/lint.js +517 -0
- package/dist/core/memory/project-namespace.d.ts +31 -0
- package/dist/core/memory/project-namespace.js +85 -0
- package/dist/core/memory/repository-association.d.ts +31 -0
- package/dist/core/memory/repository-association.js +129 -0
- package/dist/core/memory/tree.d.ts +39 -0
- package/dist/core/memory/tree.js +93 -0
- package/dist/core/memory-resolver.d.ts +193 -90
- package/dist/core/memory-resolver.js +461 -375
- package/dist/core/nested-stores.js +6 -13
- package/dist/core/profiles/select.d.ts +4 -1
- package/dist/core/profiles/select.js +108 -48
- package/dist/core/review/__tests__/capture-origin.test.js +2 -2
- package/dist/core/review/__tests__/stage-identity.test.js +2 -2
- package/dist/core/review/companion.js +11 -2
- package/dist/core/runtime/bearings.d.ts +2 -2
- package/dist/core/runtime/bearings.js +3 -3
- package/dist/core/runtime/broker/daemon-ops.d.ts +2 -2
- package/dist/core/runtime/broker/rebind.js +5 -0
- package/dist/core/runtime/broker-extension-render.d.ts +7 -3
- package/dist/core/runtime/broker-extension-render.js +10 -5
- package/dist/core/runtime/broker-persona-guidance.d.ts +19 -5
- package/dist/core/runtime/broker-persona-guidance.js +122 -27
- package/dist/core/runtime/deliver-live.d.ts +16 -4
- package/dist/core/runtime/deliver-live.js +29 -15
- package/dist/core/runtime/kickoff.d.ts +3 -3
- package/dist/core/runtime/kickoff.js +8 -16
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/lifecycle.js +2 -3
- package/dist/core/runtime/nodes.d.ts +3 -4
- package/dist/core/runtime/nodes.js +3 -4
- package/dist/core/runtime/persona.d.ts +8 -12
- package/dist/core/runtime/persona.js +24 -96
- package/dist/core/runtime/promote.d.ts +2 -2
- package/dist/core/runtime/promote.js +8 -21
- package/dist/core/runtime/revive.js +20 -17
- package/dist/core/runtime/spawn.js +18 -7
- package/dist/core/runtime/tmux-bindings.js +2 -3
- package/dist/core/runtime/warm-pool.js +3 -4
- package/dist/core/scope.js +2 -0
- package/dist/core/self-update.d.ts +0 -2
- package/dist/core/self-update.js +2 -35
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.js +28 -0
- package/dist/core/substrate/index.d.ts +2 -2
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/injected-store.d.ts +43 -27
- package/dist/core/substrate/injected-store.js +208 -104
- package/dist/core/substrate/listings.d.ts +19 -12
- package/dist/core/substrate/listings.js +75 -52
- package/dist/core/substrate/on-read-node.d.ts +4 -7
- package/dist/core/substrate/on-read-node.js +6 -8
- package/dist/core/substrate/on-read.d.ts +22 -25
- package/dist/core/substrate/on-read.js +103 -147
- package/dist/core/substrate/render-node.d.ts +4 -7
- package/dist/core/substrate/render-node.js +5 -7
- package/dist/core/substrate/render.d.ts +21 -3
- package/dist/core/substrate/render.js +291 -223
- package/dist/core/substrate/schema.d.ts +1 -13
- package/dist/core/substrate/schema.js +5 -40
- package/dist/core/substrate/session-cache.d.ts +14 -4
- package/dist/core/substrate/session-cache.js +40 -22
- package/dist/core/substrate/surface-match.d.ts +9 -7
- package/dist/core/substrate/surface-match.js +26 -25
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +30 -0
- package/dist/daemon/__tests__/helpers/source-daemon.js +174 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.d.ts +1 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.js +97 -0
- package/dist/daemon/api/__tests__/bridge-heartbeat.test.js +21 -5
- package/dist/daemon/api/handlers/broker-ops.js +21 -16
- package/dist/daemon/api/handlers/memory.js +2 -0
- package/dist/daemon/crtrd.js +2 -0
- package/dist/daemon/human/finish.js +8 -1
- package/dist/daemon/manage.d.ts +0 -1
- package/dist/daemon/manage.js +7 -16
- package/dist/daemon/startup-policy.d.ts +1 -0
- package/dist/daemon/startup-policy.js +1 -0
- package/dist/migrations/001-surfaces-frontmatter.js +21 -109
- package/dist/migrations/002-profile-project-memory.js +1 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
- package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
- package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
- package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
- package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
- package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
- package/dist/migrations/__tests__/activation.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation.test.js +149 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
- package/dist/migrations/activation.d.ts +16 -0
- package/dist/migrations/activation.js +78 -0
- package/dist/migrations/convergent.d.ts +14 -3
- package/dist/migrations/convergent.js +21 -10
- package/dist/migrations/corpus.d.ts +78 -0
- package/dist/migrations/corpus.js +497 -0
- package/dist/migrations/frontmatter-splice.d.ts +15 -0
- package/dist/migrations/frontmatter-splice.js +176 -0
- package/dist/migrations/registry.d.ts +6 -1
- package/dist/migrations/registry.js +7 -2
- package/dist/migrations/runner.d.ts +41 -0
- package/dist/migrations/runner.js +81 -0
- package/dist/migrations/types.d.ts +148 -9
- package/dist/migrations/types.js +9 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
- package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
- package/dist/pi-extensions/canvas-context-intro.js +50 -46
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
- package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
- package/dist/pi-extensions/canvas-stophook.js +6 -13
- package/dist/shared/generated-context.d.ts +0 -3
- package/dist/shared/generated-context.js +0 -57
- package/dist/shared/tool-groups.js +2 -3
- package/dist/types.d.ts +8 -11
- package/dist/types.js +4 -31
- package/package.json +5 -4
- package/runtime.lock.json +2 -2
- package/dist/builtin-memory/05-kinds/design/00-base.md +0 -17
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +0 -15
- package/dist/builtin-memory/05-kinds/design/design-contract.md +0 -19
- package/dist/builtin-memory/05-kinds/developer/00-base.md +0 -17
- package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +0 -15
- package/dist/builtin-memory/05-kinds/plan/00-base.md +0 -17
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +0 -17
- package/dist/builtin-memory/05-kinds/plan/plan-contract.md +0 -28
- package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +0 -15
- package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +0 -15
- package/dist/builtin-memory/05-kinds/plan/reviewers/lens-contract.md +0 -13
- package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +0 -17
- package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +0 -17
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +0 -17
- package/dist/builtin-memory/05-kinds/spec/00-base.md +0 -17
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +0 -15
- package/dist/builtin-memory/05-kinds/spec/requirements.md +0 -15
- package/dist/builtin-memory/design/guide.md +0 -35
- package/dist/builtin-memory/design/roadmap.md +0 -21
- package/dist/builtin-memory/development.md +0 -113
- package/dist/builtin-memory/plan/roadmap.md +0 -25
- package/dist/builtin-memory/spec/guide.md +0 -53
- package/dist/builtin-memory/spec/requirements.md +0 -29
- package/dist/builtin-memory/spec/roadmap.md +0 -36
- package/dist/builtin-memory/testing.md +0 -39
- /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
- /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
- /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
- /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
- /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
- /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
- /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
- /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
|
@@ -3,22 +3,41 @@ import { resolve as resolvePath } from 'node:path';
|
|
|
3
3
|
import { defineLeaf } from '../../core/command.js';
|
|
4
4
|
import { CrtrError, notFound, usage } from '../../core/errors.js';
|
|
5
5
|
import { readText } from '../../core/fs-utils.js';
|
|
6
|
-
import {
|
|
6
|
+
import { loadMemoryStoreView, resolveMemoryCandidates } from '../../core/memory-resolver.js';
|
|
7
7
|
import { appendHistoryRecord, buildHistoryRecord, historyLogPathFor } from '../../core/memory/history.js';
|
|
8
|
-
import {
|
|
8
|
+
import { MEMORY_WRITE_SCOPES, pruneEmptyParents, requireMountedStore, selectMutationDoc, resolveReadSelector, selectorParam, } from './shared.js';
|
|
9
|
+
/** Every physical document owning that exact canonical identity — in the target
|
|
10
|
+
* view, or inside the one store `--dir` selected — or null when nothing
|
|
11
|
+
* answers to the name. */
|
|
12
|
+
function targetCandidates(rawName, scope, storeView) {
|
|
13
|
+
if (storeView !== null)
|
|
14
|
+
return storeView.candidates(rawName);
|
|
15
|
+
try {
|
|
16
|
+
return resolveMemoryCandidates(rawName, {
|
|
17
|
+
includeDescendants: true,
|
|
18
|
+
...(scope !== undefined ? { scope } : {}),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
if (e instanceof CrtrError && e.code === 'not_found')
|
|
23
|
+
return null;
|
|
24
|
+
throw e; // usage propagates with its own recovery guidance
|
|
25
|
+
}
|
|
26
|
+
}
|
|
9
27
|
export const deleteLeaf = defineLeaf({
|
|
10
28
|
name: 'delete',
|
|
11
29
|
description: 'delete a memory document by name',
|
|
12
|
-
whenToUse: 'a stored document is wrong, obsolete, or superseded and should be removed — the sanctioned way to delete one, so you never rm the markdown off disk. Resolves the name exactly as `read` does
|
|
30
|
+
whenToUse: 'a stored document is wrong, obsolete, or superseded and should be removed — the sanctioned way to delete one, so you never rm the markdown off disk. Resolves the full canonical name exactly as `read` does and removes that one file: if you can read it, you can delete it. Refuses docs you do not own — builtin docs (shipped with the package) and installed-plugin docs (managed by `crtr pkg`). If two physical files in the selected store share the name, it selects the first physical path in stable lexical order and recovers the collision.',
|
|
13
31
|
help: {
|
|
14
32
|
name: 'memory delete',
|
|
15
|
-
summary: 'resolve
|
|
33
|
+
summary: 'resolve an exact canonical name and remove that document from its store',
|
|
16
34
|
params: [
|
|
17
|
-
{ kind: 'positional', name: 'name', required: true, constraint: '
|
|
18
|
-
|
|
35
|
+
{ kind: 'positional', name: 'name', required: true, constraint: 'Full canonical name of the document — a project document is `<namespace>/<local name>`. Resolution is exact, as `read` resolves it: a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail. A canonical directory’s own document is deleted at the directory name; its members are untouched and stay reachable. If two physical files in the selected store own the name, delete selects the first path in stable lexical order and reports it. Builtin and installed-plugin documents are read-only and refuse to delete, as does the repository-root document, which declares the immutable namespace for every document in the repository; a nested store’s root guide is ordinary and may be deleted.' },
|
|
36
|
+
selectorParam('scope-read', { choices: [...MEMORY_WRITE_SCOPES] }, 'builtin is not a choice — builtin documents ship with the package and cannot be deleted.'),
|
|
37
|
+
selectorParam('dir', {}, 'Deletes from that one store, including a non-winning duplicate no target view resolves to.'),
|
|
19
38
|
],
|
|
20
39
|
output: [
|
|
21
|
-
{ name: 'name', type: 'string', required: true, constraint: '
|
|
40
|
+
{ name: 'name', type: 'string', required: true, constraint: 'Canonical name of the deleted document.' },
|
|
22
41
|
{ name: 'scope', type: 'string', required: true, constraint: 'Scope the document was deleted from: node, user, project, or profile.' },
|
|
23
42
|
{ name: 'path', type: 'string', required: true, constraint: 'Absolute path of the file that was removed.' },
|
|
24
43
|
{ name: 'deleted', type: 'boolean', required: true, constraint: 'Always true on success — the file was removed. A missing document fails with not_found instead.' },
|
|
@@ -27,39 +46,47 @@ export const deleteLeaf = defineLeaf({
|
|
|
27
46
|
],
|
|
28
47
|
outputKind: 'object',
|
|
29
48
|
effects: [
|
|
30
|
-
'Permanently removes
|
|
31
|
-
'Appends a `delete` record carrying the document’s full final text to
|
|
49
|
+
'Permanently removes the resolved file from its store, and prunes any parent directories it leaves empty. Irreversible. Refuses the repository-root document: it declares the immutable `namespace:` every document in this repository composes under, so removing it would unmount the whole repository.',
|
|
50
|
+
'Appends a `delete` record carrying the document’s full final text to `.history/<physical path>.jsonl` in the same store. The log itself is never removed.',
|
|
32
51
|
]
|
|
33
52
|
},
|
|
34
53
|
run: async (input) => {
|
|
35
54
|
const nameRaw = input['name'];
|
|
36
55
|
const scopeArg = input['scope'];
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
throw e; // ambiguous / usage propagate with their own recovery guidance
|
|
56
|
+
const dirArg = input['dir'];
|
|
57
|
+
const selector = resolveReadSelector({ scope: scopeArg, dir: dirArg });
|
|
58
|
+
const storeView = selector.store === null ? null : loadMemoryStoreView(requireMountedStore(selector.store), true);
|
|
59
|
+
const set = targetCandidates(nameRaw, selector.scope, storeView);
|
|
60
|
+
if (set === null) {
|
|
61
|
+
throw notFound(`memory document not found: ${nameRaw}`, {
|
|
62
|
+
memory: nameRaw,
|
|
63
|
+
next: 'A delete takes the full canonical name (a project document is <namespace>/<local name>), and a directory’s own document answers at the directory name. Run `crtr memory find <leaf>` to look it up, or pass --dir to reach one exact project store.',
|
|
64
|
+
});
|
|
49
65
|
}
|
|
66
|
+
const selected = selectMutationDoc(set);
|
|
67
|
+
const doc = selected.doc;
|
|
50
68
|
if (doc.plugin !== undefined) {
|
|
51
69
|
throw usage(`${doc.name} belongs to the installed plugin "${doc.plugin}" — it is not a scope doc and cannot be deleted here. Manage plugin docs with \`crtr pkg\`. To override just this doc, write a same-named doc at a writable scope (\`crtr memory write ${doc.name} ...\`).`, { memory: doc.name, plugin: doc.plugin });
|
|
52
70
|
}
|
|
53
71
|
if (doc.scope === 'builtin') {
|
|
54
72
|
throw usage(`${doc.name} is a builtin document shipped with the package (read-only) — it cannot be deleted. Override it with a same-named doc at a writable scope instead (\`crtr memory write ${doc.name} ...\`).`, { memory: doc.name, scope: 'builtin' });
|
|
55
73
|
}
|
|
74
|
+
// Only the repository-root document declares the namespace and is
|
|
75
|
+
// immutable through the memory commands.
|
|
76
|
+
if (doc.localName === '' && doc.store.ownerRelativePath === '') {
|
|
77
|
+
throw usage(`${doc.name} is the repository-root document at ${doc.path}, which declares the namespace every document in this repository composes under — deleting it would unmount the whole repository, so it cannot be deleted.`, {
|
|
78
|
+
memory: doc.name,
|
|
79
|
+
path: doc.path,
|
|
80
|
+
next: `Delete the documents under \`${doc.name}/\` individually, or revise the repository-root document with \`crtr memory edit ${doc.name} --rationale "<why>"\`.`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
56
83
|
const before = readText(doc.path);
|
|
57
84
|
const logPath = historyLogPathFor(doc.root, doc.path);
|
|
58
85
|
rmSync(doc.path);
|
|
59
|
-
pruneEmptyParents(resolvePath(doc.path), doc.
|
|
86
|
+
pruneEmptyParents(resolvePath(doc.path), doc.physicalRelativePath.split('/').length);
|
|
60
87
|
// The log is the audit trail and outlives the doc: the tombstone carries the
|
|
61
88
|
// full final text, and a later `write` of the same name keeps appending to
|
|
62
|
-
// this same log — one log per
|
|
89
|
+
// this same log — one log per physical path over its whole life.
|
|
63
90
|
appendHistoryRecord(logPath, buildHistoryRecord({ op: 'delete', before, after: '' }));
|
|
64
91
|
return {
|
|
65
92
|
name: doc.name,
|
|
@@ -67,7 +94,7 @@ export const deleteLeaf = defineLeaf({
|
|
|
67
94
|
path: doc.path,
|
|
68
95
|
deleted: true,
|
|
69
96
|
log_path: logPath,
|
|
70
|
-
follow_up: `Removed. Its revision history survives — read it with \`crtr memory history ${doc.name}\`, or browse what remains with \`crtr memory list\`.`,
|
|
97
|
+
follow_up: `Removed${selected.collisionPaths.length > 0 ? ` the first physical path in stable lexical order (${doc.path}) to recover a same-store collision.` : '.'} Its revision history survives — read it with \`crtr memory history ${doc.name}\`, or browse what remains with \`crtr memory list\`.`,
|
|
71
98
|
};
|
|
72
99
|
},
|
|
73
100
|
});
|
|
@@ -2,9 +2,9 @@ import { defineLeaf } from '../../core/command.js';
|
|
|
2
2
|
import { CrtrError, notFound, usage } from '../../core/errors.js';
|
|
3
3
|
import { parseFrontmatterGeneric } from '../../core/frontmatter.js';
|
|
4
4
|
import { readText, writeText } from '../../core/fs-utils.js';
|
|
5
|
-
import {
|
|
5
|
+
import { loadMemoryStoreView, resolveMemoryCandidates, } from '../../core/memory-resolver.js';
|
|
6
6
|
import { appendHistoryRecord, buildHistoryRecord, historyLogPathFor, readHistoryRecords, } from '../../core/memory/history.js';
|
|
7
|
-
import { DOC_RATIONALE_CONSTRAINT, GUIDE_DOC_LINKS, GUIDE_PREDICATE_VOCABULARY, GUIDE_ROUTING_LINE, GUIDE_SURFACES,
|
|
7
|
+
import { DOC_RATIONALE_CONSTRAINT, GUIDE_CANONICAL_NAMES, GUIDE_DOC_LINKS, GUIDE_PREDICATE_VOCABULARY, GUIDE_ROUTING_LINE, GUIDE_SURFACES, coerceGate, coerceSurface, memoryExtensionCatalogForDoc, memoryExtensionFieldCatalogHelp, overlayParam, parseRequestedExtensionChanges, applyExtensionChanges, literalBodySegment, serializeMemoryDocLiteral, selectorParam, requireLocalNameInStore, requireUnambiguousDoc, resolveReadSelector, } from './shared.js';
|
|
8
8
|
/** Frontmatter fields an overlay flag cannot express removing. `kind` and the
|
|
9
9
|
* routing line are the document's contract (a doc without them is malformed);
|
|
10
10
|
* `origin` and `last-updated` are runtime provenance. */
|
|
@@ -25,10 +25,11 @@ export const editLeaf = defineLeaf({
|
|
|
25
25
|
GUIDE_ROUTING_LINE + '\n\n' +
|
|
26
26
|
GUIDE_SURFACES + '\n\n' +
|
|
27
27
|
GUIDE_PREDICATE_VOCABULARY + '\n\n' +
|
|
28
|
+
GUIDE_CANONICAL_NAMES + '\n\n' +
|
|
28
29
|
GUIDE_DOC_LINKS + '\n\n' +
|
|
29
30
|
'Create a document that does not exist yet with `crtr memory write`. Read the revisions back with `crtr memory history`.',
|
|
30
31
|
params: [
|
|
31
|
-
{ kind: 'positional', name: 'name', required: true, constraint: '
|
|
32
|
+
{ kind: 'positional', name: 'name', required: true, constraint: 'Full canonical identity, resolved exactly in the target view or exact project store. A project document requires its complete `<namespace>/<local name>`; a directory document answers at the directory name, never with a trailing `/INDEX`. Same-store physical collisions are refused with both paths; `--dir` selects one exact project store. Builtin and installed-plugin docs are read-only corpora and are refused.' },
|
|
32
33
|
{ kind: 'flag', name: 'rationale', type: 'string', required: true, constraint: 'Why THIS REVISION is happening \u2014 the audit-trail entry, required on every edit. Recorded in the document\u2019s revision history, NEVER written into the document. To change the doc\u2019s standing `rationale` frontmatter field instead, use --doc-rationale.' },
|
|
33
34
|
{ kind: 'flag', name: 'verbatim', type: 'bool', required: false, constraint: 'Declares the piped body a verbatim payload from the principal \u2014 their own text, relayed unchanged \u2014 rather than agent-authored prose. Pure provenance recorded in the history record; the save path is identical either way, because there is only one save path and it never transforms.' },
|
|
34
35
|
overlayParam('kind'),
|
|
@@ -42,7 +43,9 @@ export const editLeaf = defineLeaf({
|
|
|
42
43
|
{ kind: 'flag', name: 'extension', type: 'string', required: false, repeatable: true, constraint: 'Set one declared plugin field as `extensions.<plugin>.<field>=VALUE`. The path is required in full; booleans accept only true or false, numbers require finite numeric syntax, and strings/enums preserve the literal text after the first =. Every requested field validates before the document is revised.' },
|
|
43
44
|
{ kind: 'flag', name: 'unset-extension', type: 'string', required: false, repeatable: true, constraint: 'Remove one explicitly persisted declared field by its full `extensions.<plugin>.<field>` path. A removed explicit value lets its declaration default apply only in structured output.' },
|
|
44
45
|
{ kind: 'flag', name: 'unset', type: 'enum', choices: [...CLEARABLE_FIELDS], required: false, repeatable: true, constraint: 'Remove a frontmatter field an overlay flag cannot express removing. One field per occurrence. `kind`, `when-and-why-to-read`, `origin`, and `last-updated` are never clearable.' },
|
|
45
|
-
|
|
46
|
+
selectorParam('scope-read', {}, 'Edit resolves the address winner and refuses same-store physical collisions.'),
|
|
47
|
+
selectorParam('dir'),
|
|
48
|
+
selectorParam('profile'),
|
|
46
49
|
{ kind: 'stdin', name: 'body', required: false, constraint: 'Full replacement body (markdown, no frontmatter), saved byte for byte. Piped on stdin only \u2014 this leaf already claims the one positional for NAME. Absent or empty stdin leaves the existing body untouched, which is how a frontmatter-only revision is made.' },
|
|
47
50
|
],
|
|
48
51
|
output: [
|
|
@@ -58,7 +61,7 @@ export const editLeaf = defineLeaf({
|
|
|
58
61
|
outputKind: 'object',
|
|
59
62
|
dynamicState: () => memoryExtensionFieldCatalogHelp('edit'),
|
|
60
63
|
effects: [
|
|
61
|
-
'Rewrites
|
|
64
|
+
'Rewrites the selected physical memory document and appends one `edit` record to its mirrored `.history` sidecar.'
|
|
62
65
|
],
|
|
63
66
|
},
|
|
64
67
|
run: async (input) => {
|
|
@@ -67,12 +70,27 @@ export const editLeaf = defineLeaf({
|
|
|
67
70
|
const rationale = input['rationale'];
|
|
68
71
|
const verbatim = input['verbatim'] === true;
|
|
69
72
|
const stdinBody = input['body'] ?? '';
|
|
73
|
+
const dirArg = input['dir'];
|
|
74
|
+
const profileArg = input['profile'];
|
|
75
|
+
const selector = resolveReadSelector({ scope: scopeArg, dir: dirArg, profile: profileArg });
|
|
70
76
|
let doc;
|
|
71
77
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
const set = selector.store === null
|
|
79
|
+
? resolveMemoryCandidates(nameRaw, {
|
|
80
|
+
includeDescendants: true,
|
|
81
|
+
...(selector.scope === undefined ? {} : { scope: selector.scope }),
|
|
82
|
+
})
|
|
83
|
+
: (() => {
|
|
84
|
+
requireLocalNameInStore(selector.store, nameRaw);
|
|
85
|
+
return loadMemoryStoreView(selector.store, true).candidates(nameRaw);
|
|
86
|
+
})();
|
|
87
|
+
if (set === null) {
|
|
88
|
+
throw notFound(`memory document not found: ${nameRaw}`, {
|
|
89
|
+
memory: nameRaw,
|
|
90
|
+
next: `\`edit\` revises a document that already exists. Create it with \`crtr memory write ${nameRaw} ...\`, or run \`crtr memory find <query>\` if it exists under another name.`,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
doc = requireUnambiguousDoc(set);
|
|
76
94
|
}
|
|
77
95
|
catch (e) {
|
|
78
96
|
if (e instanceof CrtrError && e.code === 'not_found') {
|
|
@@ -81,7 +99,7 @@ export const editLeaf = defineLeaf({
|
|
|
81
99
|
next: `\`edit\` revises a document that already exists. Create it with \`crtr memory write ${nameRaw} ...\`, or run \`crtr memory find <query>\` if it exists under another name.`,
|
|
82
100
|
});
|
|
83
101
|
}
|
|
84
|
-
throw e;
|
|
102
|
+
throw e;
|
|
85
103
|
}
|
|
86
104
|
if (doc.plugin !== undefined) {
|
|
87
105
|
throw usage(`${doc.name} belongs to the installed plugin "${doc.plugin}" \u2014 a read-only corpus managed by \`crtr pkg\`, not editable here. To override just this doc, write a same-named doc at a writable scope (\`crtr memory write ${doc.name} ...\`).`, { memory: doc.name, plugin: doc.plugin });
|
|
@@ -94,6 +112,28 @@ export const editLeaf = defineLeaf({
|
|
|
94
112
|
const parsed = parseFrontmatterGeneric(before);
|
|
95
113
|
const previous = { ...(parsed.data ?? {}) };
|
|
96
114
|
const frontmatter = { ...previous };
|
|
115
|
+
if (doc.scope === 'project' && doc.localName === '') {
|
|
116
|
+
if (doc.store.ownerRelativePath !== '' && Object.prototype.hasOwnProperty.call(previous, 'namespace')) {
|
|
117
|
+
throw usage(`a nested store's INDEX.md carries no namespace: — the repository at ${doc.store.repositoryRoot ?? doc.root} declares it`, {
|
|
118
|
+
memory: doc.name,
|
|
119
|
+
path: doc.path,
|
|
120
|
+
next: `Run \`crtr memory lint --dir ${doc.store.ownerDir ?? doc.root}\` or \`crtr sys migrate --dir ${doc.store.repositoryRoot ?? doc.root}\`.`,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (doc.store.ownerRelativePath === '' && previous['namespace'] !== doc.store.repositoryNamespace) {
|
|
124
|
+
throw usage(`the repository namespace on ${doc.path} is immutable; it must remain \`${doc.store.repositoryNamespace}\``, {
|
|
125
|
+
memory: doc.name,
|
|
126
|
+
path: doc.path,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (previous['name'] !== undefined) {
|
|
130
|
+
throw usage(`the project root INDEX at ${doc.path} cannot carry a \`name:\` field`, {
|
|
131
|
+
memory: doc.name,
|
|
132
|
+
path: doc.path,
|
|
133
|
+
next: `Run \`crtr sys migrate --dir ${doc.store.repositoryRoot ?? doc.store.ownerDir ?? doc.root}\`.`,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
97
137
|
const setIf = (key, value) => {
|
|
98
138
|
if (value !== undefined)
|
|
99
139
|
frontmatter[key] = value;
|
|
@@ -1,81 +1,99 @@
|
|
|
1
1
|
import { defineLeaf } from '../../core/command.js';
|
|
2
2
|
import { usage } from '../../core/errors.js';
|
|
3
|
-
import { listAllMemoryDocs } from '../../core/memory-resolver.js';
|
|
3
|
+
import { listAllMemoryDocs, loadMemoryStoreView } from '../../core/memory-resolver.js';
|
|
4
4
|
import { parseSubstrateDoc } from '../../core/substrate/schema.js';
|
|
5
5
|
import { paginate } from '../../core/pagination.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
path: d.path,
|
|
13
|
-
routing: d.whenAndWhyToRead,
|
|
14
|
-
shortForm: d.shortForm,
|
|
15
|
-
loadBody: () => d.body,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
/** The candidate set: every substrate memory document (native + plugin, supplied
|
|
19
|
-
* by listAllMemoryDocs), optionally narrowed to one kind and/or one scope.
|
|
20
|
-
* Within that explicit narrowing find searches EVERYTHING — it never applies
|
|
21
|
-
* gate or visibility-rung filtering (design §11#3); --kind/--scope are caller
|
|
22
|
-
* intent, not a visibility policy.
|
|
6
|
+
import { candidateRow } from './read.js';
|
|
7
|
+
import { MEMORY_KINDS, registerMemoryListingExposure, requireMountedStore, resolveReadSelector, selectorParam, } from './shared.js';
|
|
8
|
+
/** The search space over one corpus: every substrate memory document, narrowed
|
|
9
|
+
* to one kind when asked. Within that narrowing find searches EVERYTHING — it
|
|
10
|
+
* never applies gate or visibility-rung filtering; --kind/--scope/--dir are
|
|
11
|
+
* caller intent, not a visibility policy.
|
|
23
12
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
function
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
13
|
+
* A target view is deduped by canonical name, first in source order wins, so a
|
|
14
|
+
* hit names the document a read of that name returns. An exact store (`--dir`)
|
|
15
|
+
* is not deduped: naming a store means seeing what it physically holds. */
|
|
16
|
+
function searchUnits(kindFilter, docs, dedup) {
|
|
17
|
+
const byName = new Map();
|
|
18
|
+
for (const doc of docs) {
|
|
19
|
+
const bucket = byName.get(doc.name);
|
|
20
|
+
if (bucket !== undefined)
|
|
21
|
+
bucket.push(doc);
|
|
22
|
+
else
|
|
23
|
+
byName.set(doc.name, [doc]);
|
|
24
|
+
}
|
|
25
|
+
const seen = new Set();
|
|
36
26
|
const units = [];
|
|
37
|
-
for (const doc of
|
|
27
|
+
for (const doc of docs) {
|
|
38
28
|
const sub = parseSubstrateDoc(doc);
|
|
39
29
|
if (sub === null)
|
|
40
30
|
continue;
|
|
41
31
|
if (kindFilter !== undefined && sub.kind !== kindFilter)
|
|
42
32
|
continue;
|
|
43
|
-
|
|
33
|
+
if (dedup) {
|
|
34
|
+
if (seen.has(sub.name))
|
|
35
|
+
continue;
|
|
36
|
+
seen.add(sub.name);
|
|
37
|
+
}
|
|
38
|
+
units.push({
|
|
39
|
+
name: sub.name,
|
|
40
|
+
localName: doc.localName,
|
|
41
|
+
kind: sub.kind,
|
|
42
|
+
scope: sub.scope,
|
|
43
|
+
path: sub.path,
|
|
44
|
+
storeRoot: doc.root,
|
|
45
|
+
physicalRelativePath: doc.physicalRelativePath,
|
|
46
|
+
routing: sub.whenAndWhyToRead,
|
|
47
|
+
shortForm: sub.shortForm,
|
|
48
|
+
candidates: byName.get(doc.name) ?? [doc],
|
|
49
|
+
loadBody: () => sub.body,
|
|
50
|
+
});
|
|
44
51
|
}
|
|
45
52
|
return units;
|
|
46
53
|
}
|
|
54
|
+
/** The physical location fields --paths adds to any hit shape. */
|
|
55
|
+
function pathFields(u) {
|
|
56
|
+
return {
|
|
57
|
+
path: u.path,
|
|
58
|
+
store_root: u.storeRoot,
|
|
59
|
+
physical_relative_path: u.physicalRelativePath,
|
|
60
|
+
...(u.candidates.length > 1 ? { candidates: u.candidates.map((c) => candidateRow(c, u.candidates[0])) } : {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
47
63
|
export const findLeaf = defineLeaf({
|
|
48
64
|
name: 'find',
|
|
49
65
|
description: 'relevance search across memory documents',
|
|
50
|
-
whenToUse: '
|
|
66
|
+
whenToUse: 'the discovery surface when you do NOT know a document\u2019s canonical name — ranks documents by relevance over the canonical name, the store-local name, the physical path, the read-routing line, and short-form (add --body to also weigh body text), and every hit reports the canonical name `crtr memory read` takes. Searches every scope by default regardless of any visibility gate; --scope narrows to one class of store, --dir to one exact store. Reach for `crtr memory list` instead when you want the whole inventory rather than a topic match, and --grep when you need an exact regex or literal-string match across document bodies.',
|
|
51
67
|
help: {
|
|
52
68
|
name: 'memory find',
|
|
53
|
-
summary: 'bounded relevance search across memory documents, weighted over name/routing-line/short-form (and body with --body)',
|
|
69
|
+
summary: 'bounded relevance search across memory documents, weighted over name/local name/physical path/routing-line/short-form (and body with --body)',
|
|
54
70
|
params: [
|
|
55
|
-
{ kind: 'positional', name: 'query', required: true, constraint: 'With ranked search (default): whitespace-separated terms, matched case-insensitively and weighted over
|
|
71
|
+
{ kind: 'positional', name: 'query', required: true, constraint: 'With ranked search (default): whitespace-separated terms, matched case-insensitively and weighted over the document\u2019s names (canonical, store-local, and physical path), the read-routing line, and short-form (plus body with --body); documents matching more/stronger fields rank higher. By default returns at most 10 hits scoring at least half the top score. With --grep: an ECMAScript regex applied to each document body line.' },
|
|
56
72
|
{ kind: 'flag', name: 'kind', type: 'enum', choices: [...MEMORY_KINDS], required: false, constraint: 'Filter to a single kind. Default: all kinds.' },
|
|
57
|
-
|
|
73
|
+
selectorParam('scope-read', {}, 'Default: every resolved scope, builtin included.'),
|
|
74
|
+
selectorParam('dir', {}, 'Searches that store alone, and searches it physically: two documents sharing one canonical name are both searched and both reported, where the default view searches only the winner.'),
|
|
58
75
|
{ kind: 'flag', name: 'grep', type: 'bool', required: false, constraint: 'Treat the query as an ECMAScript regex and match it against document bodies, instead of weighted relevance ranking. Mutually exclusive with --body.' },
|
|
59
76
|
{ kind: 'flag', name: 'body', type: 'bool', required: false, constraint: 'Also weigh document body text in the relevance ranking (in addition to name/when/why/short-form). Ignored under --grep, which always scans bodies.' },
|
|
60
77
|
{ kind: 'flag', name: 'limit', type: 'int', required: false, constraint: 'Maximum hits to return per page after relevance filtering. Default 10, hard max 100.' },
|
|
61
78
|
{ kind: 'flag', name: 'cursor', type: 'string', required: false, constraint: 'Opaque token from next_cursor. Omit on first call.' },
|
|
62
79
|
{ kind: 'flag', name: 'min-score', type: 'int', required: false, constraint: 'Ranked mode only. Minimum score to return, replacing the default adaptive floor of half the top score. Must be at least 1.' },
|
|
63
|
-
{ kind: 'flag', name: 'paths', type: 'bool', required: false, constraint: 'Include absolute
|
|
80
|
+
{ kind: 'flag', name: 'paths', type: 'bool', required: false, constraint: 'Include each hit\u2019s physical location — absolute path, store root, store-relative path, and every other physical document sharing its canonical name. Off by default; the canonical name is sufficient for `crtr memory read <name>`.' },
|
|
64
81
|
],
|
|
65
82
|
output: [
|
|
66
83
|
{ name: 'query', type: 'string', required: true, constraint: 'Echo of the input query.' },
|
|
67
84
|
{ name: 'matches', type: 'integer', required: true, constraint: 'Total matches before the default relevance floor and limit are applied (ranked mode) or total matching lines (grep mode) — across all pages, not just this one.' },
|
|
68
|
-
{ name: 'hits', type: 'object[]', required: true, constraint: 'Ranked mode — each: {name, kind, scope, score, short_form}, sorted by score descending. Grep mode — each: {name, scope, line, text}, sorted by name then line. With --paths, each shape also
|
|
85
|
+
{ name: 'hits', type: 'object[]', required: true, constraint: 'Ranked mode — each: {name, kind, scope, score, short_form}, sorted by score descending. Grep mode — each: {name, scope, line, text}, sorted by name then line. `name` is always the canonical name, whatever field matched. With --paths, each shape also carries path, store_root, physical_relative_path, and candidates when several physical documents share the name. Paginated via --limit/--cursor.' },
|
|
69
86
|
{ name: 'next_cursor', type: 'string | null', required: true, constraint: 'Opaque token for the next page; null means no more items.' },
|
|
70
87
|
{ name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands for reading a hit in full or refining the search.' },
|
|
71
88
|
],
|
|
72
89
|
outputKind: 'object',
|
|
73
|
-
effects: ['
|
|
90
|
+
effects: ['Records this search page as listing context for the current node without marking any matched document as loaded.'],
|
|
74
91
|
},
|
|
75
92
|
run: async (input) => {
|
|
76
93
|
const query = input['query'];
|
|
77
94
|
const kindFilter = input['kind'];
|
|
78
|
-
const
|
|
95
|
+
const dirArg = input['dir'];
|
|
96
|
+
const selector = resolveReadSelector({ scope: input['scope'], dir: dirArg });
|
|
79
97
|
const grep = input['grep'];
|
|
80
98
|
const weighBody = input['body'];
|
|
81
99
|
const limitArg = input['limit'];
|
|
@@ -93,7 +111,21 @@ export const findLeaf = defineLeaf({
|
|
|
93
111
|
if (minScore !== undefined && minScore < 1)
|
|
94
112
|
throw usage('--min-score must be at least 1.');
|
|
95
113
|
const limit = limitArg ?? 10;
|
|
96
|
-
const
|
|
114
|
+
const listingIdentity = `listing:memory-find:${JSON.stringify({
|
|
115
|
+
query,
|
|
116
|
+
kind: kindFilter ?? null,
|
|
117
|
+
scope: selector.scope ?? null,
|
|
118
|
+
dir: dirArg ?? null,
|
|
119
|
+
grep: grep === true,
|
|
120
|
+
body: weighBody === true,
|
|
121
|
+
limit,
|
|
122
|
+
cursor: cursor ?? null,
|
|
123
|
+
minScore: minScore ?? null,
|
|
124
|
+
paths: includePaths === true,
|
|
125
|
+
})}`;
|
|
126
|
+
const units = selector.store !== null
|
|
127
|
+
? searchUnits(kindFilter, loadMemoryStoreView(requireMountedStore(selector.store), false).docs, false)
|
|
128
|
+
: searchUnits(kindFilter, listAllMemoryDocs(selector.scope, false, true), true);
|
|
97
129
|
// --- grep mode: regex over every unit's body, one row per matching line ---
|
|
98
130
|
if (grep) {
|
|
99
131
|
let regex;
|
|
@@ -108,25 +140,32 @@ export const findLeaf = defineLeaf({
|
|
|
108
140
|
const lines = u.loadBody().split('\n');
|
|
109
141
|
lines.forEach((text, idx) => {
|
|
110
142
|
if (regex.test(text))
|
|
111
|
-
matches.push({ name: u.name, scope: u.scope,
|
|
143
|
+
matches.push({ name: u.name, scope: u.scope, unit: u, line: idx + 1, text });
|
|
112
144
|
});
|
|
113
145
|
}
|
|
146
|
+
// Physical path is part of the identity: under --dir two documents can
|
|
147
|
+
// share one canonical name, and pagination needs unique ascending keys.
|
|
114
148
|
matches.sort((a, b) => {
|
|
115
149
|
const nc = a.name.localeCompare(b.name);
|
|
116
150
|
if (nc !== 0)
|
|
117
151
|
return nc;
|
|
118
152
|
const sc = a.scope.localeCompare(b.scope);
|
|
119
|
-
|
|
153
|
+
if (sc !== 0)
|
|
154
|
+
return sc;
|
|
155
|
+
const pc = a.unit.physicalRelativePath.localeCompare(b.unit.physicalRelativePath);
|
|
156
|
+
return pc !== 0 ? pc : a.line - b.line;
|
|
120
157
|
});
|
|
121
|
-
const keyOf = (m) => `${m.name}:${m.scope}:${String(m.line).padStart(7, '0')}`;
|
|
158
|
+
const keyOf = (m) => `${m.name}:${m.scope}:${m.unit.physicalRelativePath}:${String(m.line).padStart(7, '0')}`;
|
|
122
159
|
const page = paginate(matches, { limit, cursor }, { defaultLimit: 10, maxLimit: 100, keyOf, total: 'omit' });
|
|
123
|
-
|
|
160
|
+
const output = {
|
|
124
161
|
query,
|
|
125
162
|
matches: matches.length,
|
|
126
|
-
hits: page.items.map(({
|
|
163
|
+
hits: page.items.map(({ unit, ...match }) => (includePaths ? { ...match, ...pathFields(unit) } : match)),
|
|
127
164
|
next_cursor: page.next_cursor,
|
|
128
165
|
follow_up: 'Read a hit with `crtr memory read <name>`. Run `crtr memory find -h` for filters and verbosity.',
|
|
129
166
|
};
|
|
167
|
+
registerMemoryListingExposure(listingIdentity);
|
|
168
|
+
return output;
|
|
130
169
|
}
|
|
131
170
|
// --- ranked mode: weighted relevance over name/routing-line/short-form(+body) ---
|
|
132
171
|
const terms = query
|
|
@@ -137,13 +176,19 @@ export const findLeaf = defineLeaf({
|
|
|
137
176
|
throw usage('query must contain at least one non-whitespace term');
|
|
138
177
|
const hits = [];
|
|
139
178
|
for (const u of units) {
|
|
179
|
+
// One name weight over three spellings of the same document: an agent
|
|
180
|
+
// searching a store-local name or a physical path is searching for the
|
|
181
|
+
// identity, and counting each spelling separately would inflate the band
|
|
182
|
+
// the adaptive floor is calibrated against.
|
|
140
183
|
const nameLc = u.name.toLowerCase();
|
|
184
|
+
const localLc = u.localName.toLowerCase();
|
|
185
|
+
const physLc = u.physicalRelativePath.toLowerCase();
|
|
141
186
|
const routingLc = u.routing.toLowerCase();
|
|
142
187
|
const shortLc = u.shortForm.toLowerCase();
|
|
143
188
|
const bodyLc = weighBody ? u.loadBody().toLowerCase() : null;
|
|
144
189
|
let score = 0;
|
|
145
190
|
for (const term of terms) {
|
|
146
|
-
if (nameLc.includes(term))
|
|
191
|
+
if (nameLc.includes(term) || localLc.includes(term) || physLc.includes(term))
|
|
147
192
|
score += 10;
|
|
148
193
|
if (routingLc.includes(term))
|
|
149
194
|
score += 5;
|
|
@@ -156,18 +201,20 @@ export const findLeaf = defineLeaf({
|
|
|
156
201
|
hits.push({ unit: u, score });
|
|
157
202
|
}
|
|
158
203
|
// Sort comparator MUST match keyOf exactly (score desc, then scope asc,
|
|
159
|
-
// then name asc
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
// own keys, and paginate() would silently skip a later item.
|
|
204
|
+
// then name asc, then physical path asc — which is unique even under --dir,
|
|
205
|
+
// where two documents can share one canonical name). Sorting equal-score
|
|
206
|
+
// hits by one order while the cursor key uses another would let the array
|
|
207
|
+
// disagree with its own keys, and paginate() would silently skip an item.
|
|
164
208
|
hits.sort((a, b) => {
|
|
165
209
|
if (b.score !== a.score)
|
|
166
210
|
return b.score - a.score;
|
|
167
211
|
const scopeCmp = a.unit.scope.localeCompare(b.unit.scope);
|
|
168
212
|
if (scopeCmp !== 0)
|
|
169
213
|
return scopeCmp;
|
|
170
|
-
|
|
214
|
+
const nameCmp = a.unit.name.localeCompare(b.unit.name);
|
|
215
|
+
if (nameCmp !== 0)
|
|
216
|
+
return nameCmp;
|
|
217
|
+
return a.unit.physicalRelativePath.localeCompare(b.unit.physicalRelativePath);
|
|
171
218
|
});
|
|
172
219
|
const adaptiveFloor = hits.length === 0 ? 1 : Math.max(1, Math.ceil(hits[0].score / 2));
|
|
173
220
|
const floor = minScore ?? adaptiveFloor;
|
|
@@ -175,9 +222,9 @@ export const findLeaf = defineLeaf({
|
|
|
175
222
|
// relevant is already sorted score-desc/scope-asc/name-asc, which is
|
|
176
223
|
// exactly ascending order by this key (higher score → smaller key),
|
|
177
224
|
// satisfying paginate's ascending-key precondition.
|
|
178
|
-
const keyOf = (h) => `${String(1_000_000 - h.score).padStart(7, '0')}:${h.unit.scope}:${h.unit.name}`;
|
|
225
|
+
const keyOf = (h) => `${String(1_000_000 - h.score).padStart(7, '0')}:${h.unit.scope}:${h.unit.name}:${h.unit.physicalRelativePath}`;
|
|
179
226
|
const page = paginate(relevant, { limit, cursor }, { defaultLimit: 10, maxLimit: 100, keyOf, total: 'omit' });
|
|
180
|
-
|
|
227
|
+
const output = {
|
|
181
228
|
query,
|
|
182
229
|
matches: hits.length,
|
|
183
230
|
hits: page.items.map((h) => ({
|
|
@@ -186,10 +233,12 @@ export const findLeaf = defineLeaf({
|
|
|
186
233
|
scope: h.unit.scope,
|
|
187
234
|
score: h.score,
|
|
188
235
|
short_form: h.unit.shortForm,
|
|
189
|
-
...(includePaths ?
|
|
236
|
+
...(includePaths ? pathFields(h.unit) : {}),
|
|
190
237
|
})),
|
|
191
238
|
next_cursor: page.next_cursor,
|
|
192
239
|
follow_up: 'Read a hit with `crtr memory read <name>`. Run `crtr memory find -h` for filters and verbosity.',
|
|
193
240
|
};
|
|
241
|
+
registerMemoryListingExposure(listingIdentity);
|
|
242
|
+
return output;
|
|
194
243
|
},
|
|
195
244
|
});
|