@cmetech/otto 1.1.1 → 1.2.4
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/coworker/persona-commands.d.ts +1 -0
- package/dist/coworker/persona-commands.js +5 -0
- package/dist/coworker/persona-commands.test.d.ts +1 -0
- package/dist/coworker/persona-commands.test.js +45 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/_coworker-paths.js +8 -0
- package/dist/resources/extensions/coworker-artifacts/artifacts-command.js +31 -0
- package/dist/resources/extensions/coworker-artifacts/artifacts-singleton.js +17 -0
- package/dist/resources/extensions/coworker-artifacts/extension-manifest.json +13 -0
- package/dist/resources/extensions/coworker-artifacts/index.js +125 -0
- package/dist/resources/extensions/coworker-artifacts/list-tool.js +27 -0
- package/dist/resources/extensions/coworker-artifacts/open-tool.js +25 -0
- package/dist/resources/extensions/coworker-memory/extension-manifest.json +13 -0
- package/dist/resources/extensions/coworker-memory/index.js +219 -0
- package/dist/resources/extensions/coworker-memory/memorize-tool.js +10 -0
- package/dist/resources/extensions/coworker-memory/memory-command.js +157 -0
- package/dist/resources/extensions/coworker-memory/memory-singleton.js +55 -0
- package/dist/resources/extensions/coworker-memory/recall-tool.js +18 -0
- package/dist/resources/extensions/coworker-memory/session-hooks.js +45 -0
- package/dist/resources/extensions/coworker-scratchpad/attach-banners.js +53 -0
- package/dist/resources/extensions/coworker-scratchpad/extension-manifest.json +13 -0
- package/dist/resources/extensions/coworker-scratchpad/format-age.js +9 -0
- package/dist/resources/extensions/coworker-scratchpad/helpers.js +38 -0
- package/dist/resources/extensions/coworker-scratchpad/index.js +199 -0
- package/dist/resources/extensions/coworker-scratchpad/mime-bundle.js +20 -0
- package/dist/resources/extensions/coworker-scratchpad/scratchpad-tool.js +118 -0
- package/dist/resources/extensions/coworker-scratchpad/session-sidecar.js +60 -0
- package/dist/resources/extensions/coworker-scratchpad/sp-command.js +597 -0
- package/dist/resources/extensions/coworker-scratchpad/workspace-pointer.js +41 -0
- package/dist/resources/extensions/coworker-scratchpad/workspace-root.js +17 -0
- package/dist/resources/extensions/coworker-vault/audit-command.js +35 -0
- package/dist/resources/extensions/coworker-vault/connect-command.js +42 -0
- package/dist/resources/extensions/coworker-vault/datasource-command.js +50 -0
- package/dist/resources/extensions/coworker-vault/extension-manifest.json +12 -0
- package/dist/resources/extensions/coworker-vault/index.js +171 -0
- package/dist/resources/extensions/coworker-vault/test-helpers.js +86 -0
- package/dist/resources/extensions/coworker-vault/vault-singleton.js +24 -0
- package/dist/resources/extensions/otto/commands/release-notes/_data.js +71 -0
- package/dist/resources/extensions/otto/commands/release-notes/command.js +15 -4
- package/dist/resources/extensions/subagent/index.js +8 -1
- package/dist/resources/extensions/subagent/launch.js +37 -5
- package/dist/resources/extensions/subagent/run-store.js +1 -0
- package/dist/resources/extensions/workflow/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/workflow/bootstrap/register-hooks.js +10 -0
- package/dist/resources/extensions/workflow/persona-status.js +87 -0
- package/package.json +25 -10
- package/packages/contracts/package.json +1 -1
- package/packages/coworker-artifacts/dist/artifact-store.d.ts +25 -0
- package/packages/coworker-artifacts/dist/artifact-store.js +187 -0
- package/packages/coworker-artifacts/dist/dir-snapshot.d.ts +7 -0
- package/packages/coworker-artifacts/dist/dir-snapshot.js +54 -0
- package/packages/coworker-artifacts/dist/errors.d.ts +18 -0
- package/packages/coworker-artifacts/dist/errors.js +37 -0
- package/packages/coworker-artifacts/dist/index.d.ts +7 -0
- package/packages/coworker-artifacts/dist/index.js +7 -0
- package/packages/coworker-artifacts/dist/readme-renderer.d.ts +5 -0
- package/packages/coworker-artifacts/dist/readme-renderer.js +47 -0
- package/packages/coworker-artifacts/dist/resolve-uri.d.ts +3 -0
- package/packages/coworker-artifacts/dist/resolve-uri.js +29 -0
- package/packages/coworker-artifacts/dist/slug.d.ts +4 -0
- package/packages/coworker-artifacts/dist/slug.js +32 -0
- package/packages/coworker-artifacts/dist/types.d.ts +52 -0
- package/packages/coworker-artifacts/dist/types.js +1 -0
- package/packages/coworker-artifacts/package.json +20 -0
- package/packages/coworker-artifacts/src/artifact-store.test.ts +188 -0
- package/packages/coworker-artifacts/src/artifact-store.ts +206 -0
- package/packages/coworker-artifacts/src/artifacts-integration.test.ts +109 -0
- package/packages/coworker-artifacts/src/dir-snapshot.test.ts +71 -0
- package/packages/coworker-artifacts/src/dir-snapshot.ts +52 -0
- package/packages/coworker-artifacts/src/errors.test.ts +37 -0
- package/packages/coworker-artifacts/src/errors.ts +28 -0
- package/packages/coworker-artifacts/src/index.test.ts +22 -0
- package/packages/coworker-artifacts/src/index.ts +7 -0
- package/packages/coworker-artifacts/src/readme-renderer.test.ts +72 -0
- package/packages/coworker-artifacts/src/readme-renderer.ts +56 -0
- package/packages/coworker-artifacts/src/resolve-uri.test.ts +46 -0
- package/packages/coworker-artifacts/src/resolve-uri.ts +29 -0
- package/packages/coworker-artifacts/src/slug.test.ts +47 -0
- package/packages/coworker-artifacts/src/slug.ts +31 -0
- package/packages/coworker-artifacts/src/types.ts +61 -0
- package/packages/coworker-artifacts/tsconfig.json +15 -0
- package/packages/coworker-artifacts/tsconfig.publish.json +4 -0
- package/packages/coworker-memory/dist/context-injection.d.ts +9 -0
- package/packages/coworker-memory/dist/context-injection.js +41 -0
- package/packages/coworker-memory/dist/errors.d.ts +25 -0
- package/packages/coworker-memory/dist/errors.js +51 -0
- package/packages/coworker-memory/dist/index.d.ts +12 -0
- package/packages/coworker-memory/dist/index.js +12 -0
- package/packages/coworker-memory/dist/layer-a-store.d.ts +16 -0
- package/packages/coworker-memory/dist/layer-a-store.js +78 -0
- package/packages/coworker-memory/dist/local-sqlite-backend.d.ts +28 -0
- package/packages/coworker-memory/dist/local-sqlite-backend.js +167 -0
- package/packages/coworker-memory/dist/memory-backend.d.ts +14 -0
- package/packages/coworker-memory/dist/memory-backend.js +1 -0
- package/packages/coworker-memory/dist/memory-recorder.d.ts +50 -0
- package/packages/coworker-memory/dist/memory-recorder.js +69 -0
- package/packages/coworker-memory/dist/migrations/001-init.sql +38 -0
- package/packages/coworker-memory/dist/migrations/002-artifact-kind.sql +50 -0
- package/packages/coworker-memory/dist/paste-detector.d.ts +5 -0
- package/packages/coworker-memory/dist/paste-detector.js +14 -0
- package/packages/coworker-memory/dist/persona-seed.d.ts +10 -0
- package/packages/coworker-memory/dist/persona-seed.js +38 -0
- package/packages/coworker-memory/dist/recall-formatter.d.ts +2 -0
- package/packages/coworker-memory/dist/recall-formatter.js +14 -0
- package/packages/coworker-memory/dist/scope-resolver.d.ts +9 -0
- package/packages/coworker-memory/dist/scope-resolver.js +10 -0
- package/packages/coworker-memory/dist/types.d.ts +51 -0
- package/packages/coworker-memory/dist/types.js +2 -0
- package/packages/coworker-memory/dist/workspace-id.d.ts +3 -0
- package/packages/coworker-memory/dist/workspace-id.js +54 -0
- package/packages/coworker-memory/package.json +35 -0
- package/packages/coworker-memory/src/activator-integration.test.ts +141 -0
- package/packages/coworker-memory/src/context-injection.test.ts +72 -0
- package/packages/coworker-memory/src/context-injection.ts +57 -0
- package/packages/coworker-memory/src/errors.test.ts +45 -0
- package/packages/coworker-memory/src/errors.ts +42 -0
- package/packages/coworker-memory/src/index.test.ts +21 -0
- package/packages/coworker-memory/src/index.ts +12 -0
- package/packages/coworker-memory/src/layer-a-store.test.ts +85 -0
- package/packages/coworker-memory/src/layer-a-store.ts +88 -0
- package/packages/coworker-memory/src/local-sqlite-backend.test.ts +110 -0
- package/packages/coworker-memory/src/local-sqlite-backend.ts +185 -0
- package/packages/coworker-memory/src/memory-backend.ts +10 -0
- package/packages/coworker-memory/src/memory-integration.test.ts +89 -0
- package/packages/coworker-memory/src/memory-recorder.test.ts +101 -0
- package/packages/coworker-memory/src/memory-recorder.ts +95 -0
- package/packages/coworker-memory/src/migrations/001-init.sql +38 -0
- package/packages/coworker-memory/src/migrations/002-artifact-kind.sql +50 -0
- package/packages/coworker-memory/src/paste-detector.test.ts +23 -0
- package/packages/coworker-memory/src/paste-detector.ts +18 -0
- package/packages/coworker-memory/src/persona-seed.test.ts +57 -0
- package/packages/coworker-memory/src/persona-seed.ts +46 -0
- package/packages/coworker-memory/src/recall-formatter.test.ts +34 -0
- package/packages/coworker-memory/src/recall-formatter.ts +15 -0
- package/packages/coworker-memory/src/scope-resolver.test.ts +23 -0
- package/packages/coworker-memory/src/scope-resolver.ts +18 -0
- package/packages/coworker-memory/src/types.ts +61 -0
- package/packages/coworker-memory/src/workspace-id.test.ts +48 -0
- package/packages/coworker-memory/src/workspace-id.ts +56 -0
- package/packages/coworker-memory/tsconfig.json +15 -0
- package/packages/coworker-memory/tsconfig.publish.json +4 -0
- package/packages/coworker-persona/dist/commands.d.ts +7 -0
- package/packages/coworker-persona/dist/commands.js +35 -0
- package/packages/coworker-persona/dist/defaults/manifest.yaml +12 -0
- package/packages/coworker-persona/dist/defaults/steering/identity.md +3 -0
- package/packages/coworker-persona/dist/index.d.ts +3 -0
- package/packages/coworker-persona/dist/index.js +3 -0
- package/packages/coworker-persona/dist/manifest.d.ts +24 -0
- package/packages/coworker-persona/dist/manifest.js +21 -0
- package/packages/coworker-persona/dist/registry.d.ts +22 -0
- package/packages/coworker-persona/dist/registry.js +142 -0
- package/packages/coworker-persona/package.json +28 -0
- package/packages/coworker-persona/scripts/copy-defaults.cjs +17 -0
- package/packages/coworker-persona/src/commands.ts +47 -0
- package/packages/coworker-persona/src/defaults/manifest.yaml +12 -0
- package/packages/coworker-persona/src/defaults/steering/identity.md +3 -0
- package/packages/coworker-persona/src/index.ts +3 -0
- package/packages/coworker-persona/src/manifest.test.ts +67 -0
- package/packages/coworker-persona/src/manifest.ts +49 -0
- package/packages/coworker-persona/src/registry.test.ts +89 -0
- package/packages/coworker-persona/src/registry.ts +147 -0
- package/packages/coworker-persona/tsconfig.json +15 -0
- package/packages/coworker-persona/tsconfig.publish.json +4 -0
- package/packages/coworker-scratchpad/dist/cell-archive.d.ts +39 -0
- package/packages/coworker-scratchpad/dist/cell-archive.js +77 -0
- package/packages/coworker-scratchpad/dist/cell-tree.d.ts +14 -0
- package/packages/coworker-scratchpad/dist/cell-tree.js +72 -0
- package/packages/coworker-scratchpad/dist/child-process-runtime.d.ts +129 -0
- package/packages/coworker-scratchpad/dist/child-process-runtime.js +427 -0
- package/packages/coworker-scratchpad/dist/collector-registry.d.ts +12 -0
- package/packages/coworker-scratchpad/dist/collector-registry.js +29 -0
- package/packages/coworker-scratchpad/dist/detect-kind.d.ts +3 -0
- package/packages/coworker-scratchpad/dist/detect-kind.js +19 -0
- package/packages/coworker-scratchpad/dist/file-collector.d.ts +15 -0
- package/packages/coworker-scratchpad/dist/file-collector.js +99 -0
- package/packages/coworker-scratchpad/dist/index.d.ts +13 -0
- package/packages/coworker-scratchpad/dist/index.js +13 -0
- package/packages/coworker-scratchpad/dist/kernel-bindings.d.ts +49 -0
- package/packages/coworker-scratchpad/dist/kernel-bindings.js +220 -0
- package/packages/coworker-scratchpad/dist/kernel-entry.d.ts +1 -0
- package/packages/coworker-scratchpad/dist/kernel-entry.js +355 -0
- package/packages/coworker-scratchpad/dist/kernel-protocol.d.ts +171 -0
- package/packages/coworker-scratchpad/dist/kernel-protocol.js +48 -0
- package/packages/coworker-scratchpad/dist/kernel-spawn.d.ts +3 -0
- package/packages/coworker-scratchpad/dist/kernel-spawn.js +54 -0
- package/packages/coworker-scratchpad/dist/namespace-codec.d.ts +22 -0
- package/packages/coworker-scratchpad/dist/namespace-codec.js +61 -0
- package/packages/coworker-scratchpad/dist/scratchpad-lock.d.ts +24 -0
- package/packages/coworker-scratchpad/dist/scratchpad-lock.js +86 -0
- package/packages/coworker-scratchpad/dist/scratchpad-manager.d.ts +193 -0
- package/packages/coworker-scratchpad/dist/scratchpad-manager.js +866 -0
- package/packages/coworker-scratchpad/dist/staleness-banner.d.ts +12 -0
- package/packages/coworker-scratchpad/dist/staleness-banner.js +27 -0
- package/packages/coworker-scratchpad/package.json +31 -0
- package/packages/coworker-scratchpad/src/cell-archive.test.ts +150 -0
- package/packages/coworker-scratchpad/src/cell-archive.ts +97 -0
- package/packages/coworker-scratchpad/src/cell-tree.test.ts +105 -0
- package/packages/coworker-scratchpad/src/cell-tree.ts +90 -0
- package/packages/coworker-scratchpad/src/child-process-runtime.test.ts +413 -0
- package/packages/coworker-scratchpad/src/child-process-runtime.ts +493 -0
- package/packages/coworker-scratchpad/src/collector-registry.test.ts +69 -0
- package/packages/coworker-scratchpad/src/collector-registry.ts +33 -0
- package/packages/coworker-scratchpad/src/detect-kind.test.ts +33 -0
- package/packages/coworker-scratchpad/src/detect-kind.ts +22 -0
- package/packages/coworker-scratchpad/src/file-collector.test.ts +109 -0
- package/packages/coworker-scratchpad/src/file-collector.ts +114 -0
- package/packages/coworker-scratchpad/src/index.ts +74 -0
- package/packages/coworker-scratchpad/src/kernel-bindings.test.ts +188 -0
- package/packages/coworker-scratchpad/src/kernel-bindings.ts +279 -0
- package/packages/coworker-scratchpad/src/kernel-entry.test.ts +123 -0
- package/packages/coworker-scratchpad/src/kernel-entry.ts +390 -0
- package/packages/coworker-scratchpad/src/kernel-protocol.test.ts +105 -0
- package/packages/coworker-scratchpad/src/kernel-protocol.ts +230 -0
- package/packages/coworker-scratchpad/src/kernel-spawn.test.ts +60 -0
- package/packages/coworker-scratchpad/src/kernel-spawn.ts +54 -0
- package/packages/coworker-scratchpad/src/namespace-codec.test.ts +102 -0
- package/packages/coworker-scratchpad/src/namespace-codec.ts +90 -0
- package/packages/coworker-scratchpad/src/scratchpad-lock.test.ts +98 -0
- package/packages/coworker-scratchpad/src/scratchpad-lock.ts +102 -0
- package/packages/coworker-scratchpad/src/scratchpad-manager.test.ts +1343 -0
- package/packages/coworker-scratchpad/src/scratchpad-manager.ts +891 -0
- package/packages/coworker-scratchpad/src/staleness-banner.test.ts +53 -0
- package/packages/coworker-scratchpad/src/staleness-banner.ts +33 -0
- package/packages/coworker-scratchpad/src/vault-integration.test.ts +221 -0
- package/packages/coworker-scratchpad/tsconfig.json +15 -0
- package/packages/coworker-scratchpad/tsconfig.publish.json +4 -0
- package/packages/coworker-types/dist/artifacts.d.ts +31 -0
- package/packages/coworker-types/dist/artifacts.js +2 -0
- package/packages/coworker-types/dist/contracts.d.ts +32 -0
- package/packages/coworker-types/dist/contracts.js +1 -0
- package/packages/coworker-types/dist/index.d.ts +5 -0
- package/packages/coworker-types/dist/index.js +5 -0
- package/packages/coworker-types/dist/memory.d.ts +61 -0
- package/packages/coworker-types/dist/memory.js +3 -0
- package/packages/coworker-types/dist/scratchpad.d.ts +43 -0
- package/packages/coworker-types/dist/scratchpad.js +2 -0
- package/packages/coworker-types/dist/vault.d.ts +34 -0
- package/packages/coworker-types/dist/vault.js +2 -0
- package/packages/coworker-types/package.json +24 -0
- package/packages/coworker-types/src/artifacts.test.ts +52 -0
- package/packages/coworker-types/src/artifacts.ts +35 -0
- package/packages/coworker-types/src/contracts.test.ts +43 -0
- package/packages/coworker-types/src/contracts.ts +36 -0
- package/packages/coworker-types/src/index.ts +5 -0
- package/packages/coworker-types/src/memory.test.ts +50 -0
- package/packages/coworker-types/src/memory.ts +79 -0
- package/packages/coworker-types/src/scratchpad.test.ts +46 -0
- package/packages/coworker-types/src/scratchpad.ts +51 -0
- package/packages/coworker-types/src/smoke.test.ts +34 -0
- package/packages/coworker-types/src/vault.test.ts +49 -0
- package/packages/coworker-types/src/vault.ts +40 -0
- package/packages/coworker-types/tsconfig.json +15 -0
- package/packages/coworker-types/tsconfig.publish.json +4 -0
- package/packages/coworker-utils/dist/audit-log.d.ts +34 -0
- package/packages/coworker-utils/dist/audit-log.js +88 -0
- package/packages/coworker-utils/dist/index.d.ts +6 -0
- package/packages/coworker-utils/dist/index.js +6 -0
- package/packages/coworker-utils/dist/lease.d.ts +7 -0
- package/packages/coworker-utils/dist/lease.js +67 -0
- package/packages/coworker-utils/dist/logger.d.ts +13 -0
- package/packages/coworker-utils/dist/logger.js +26 -0
- package/packages/coworker-utils/dist/migration-runner.d.ts +7 -0
- package/packages/coworker-utils/dist/migration-runner.js +36 -0
- package/packages/coworker-utils/dist/ndjson-channel.d.ts +3 -0
- package/packages/coworker-utils/dist/ndjson-channel.js +38 -0
- package/packages/coworker-utils/dist/secret-scanner.d.ts +10 -0
- package/packages/coworker-utils/dist/secret-scanner.js +42 -0
- package/packages/coworker-utils/package.json +24 -0
- package/packages/coworker-utils/src/audit-log.test.ts +140 -0
- package/packages/coworker-utils/src/audit-log.ts +107 -0
- package/packages/coworker-utils/src/index.ts +6 -0
- package/packages/coworker-utils/src/lease.test.ts +64 -0
- package/packages/coworker-utils/src/lease.ts +76 -0
- package/packages/coworker-utils/src/logger.test.ts +50 -0
- package/packages/coworker-utils/src/logger.ts +45 -0
- package/packages/coworker-utils/src/migration-runner.test.ts +65 -0
- package/packages/coworker-utils/src/migration-runner.ts +50 -0
- package/packages/coworker-utils/src/ndjson-channel.test.ts +76 -0
- package/packages/coworker-utils/src/ndjson-channel.ts +41 -0
- package/packages/coworker-utils/src/secret-scanner.test.ts +61 -0
- package/packages/coworker-utils/src/secret-scanner.ts +56 -0
- package/packages/coworker-utils/tsconfig.json +15 -0
- package/packages/coworker-utils/tsconfig.publish.json +4 -0
- package/packages/coworker-vault/dist/data-vault.d.ts +41 -0
- package/packages/coworker-vault/dist/data-vault.js +223 -0
- package/packages/coworker-vault/dist/engine-registry.d.ts +34 -0
- package/packages/coworker-vault/dist/engine-registry.js +90 -0
- package/packages/coworker-vault/dist/engines/jira.yaml +17 -0
- package/packages/coworker-vault/dist/errors.d.ts +28 -0
- package/packages/coworker-vault/dist/errors.js +57 -0
- package/packages/coworker-vault/dist/index.d.ts +6 -0
- package/packages/coworker-vault/dist/index.js +6 -0
- package/packages/coworker-vault/dist/injector.d.ts +19 -0
- package/packages/coworker-vault/dist/injector.js +77 -0
- package/packages/coworker-vault/dist/types.d.ts +28 -0
- package/packages/coworker-vault/dist/types.js +1 -0
- package/packages/coworker-vault/dist/vault-keep.d.ts +4 -0
- package/packages/coworker-vault/dist/vault-keep.js +21 -0
- package/packages/coworker-vault/package.json +29 -0
- package/packages/coworker-vault/src/data-vault.test.ts +199 -0
- package/packages/coworker-vault/src/data-vault.ts +257 -0
- package/packages/coworker-vault/src/engine-registry.test.ts +120 -0
- package/packages/coworker-vault/src/engine-registry.ts +107 -0
- package/packages/coworker-vault/src/engines/jira.yaml +17 -0
- package/packages/coworker-vault/src/errors.test.ts +58 -0
- package/packages/coworker-vault/src/errors.ts +50 -0
- package/packages/coworker-vault/src/index.test.ts +24 -0
- package/packages/coworker-vault/src/index.ts +6 -0
- package/packages/coworker-vault/src/injector.test.ts +109 -0
- package/packages/coworker-vault/src/injector.ts +98 -0
- package/packages/coworker-vault/src/types.ts +33 -0
- package/packages/coworker-vault/src/vault-keep.test.ts +49 -0
- package/packages/coworker-vault/src/vault-keep.ts +31 -0
- package/packages/coworker-vault/tsconfig.json +15 -0
- package/packages/coworker-vault/tsconfig.publish.json +4 -0
- package/packages/daemon/package.json +3 -3
- package/packages/mcp-server/package.json +3 -3
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +6 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +22 -3
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +11 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts +47 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js +107 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts +19 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js +121 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +17 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +2 -2
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +22 -3
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +11 -0
- package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.regression.test.ts +129 -0
- package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.ts +117 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +18 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
- package/pkg/package.json +1 -1
- package/scripts/install.js +6 -5
- package/src/resources/extensions/_coworker-paths.test.ts +40 -0
- package/src/resources/extensions/_coworker-paths.ts +10 -0
- package/src/resources/extensions/coworker-artifacts/artifacts-command.test.ts +54 -0
- package/src/resources/extensions/coworker-artifacts/artifacts-command.ts +43 -0
- package/src/resources/extensions/coworker-artifacts/artifacts-singleton.test.ts +25 -0
- package/src/resources/extensions/coworker-artifacts/artifacts-singleton.ts +29 -0
- package/src/resources/extensions/coworker-artifacts/extension-manifest.json +13 -0
- package/src/resources/extensions/coworker-artifacts/index.test.ts +46 -0
- package/src/resources/extensions/coworker-artifacts/index.ts +154 -0
- package/src/resources/extensions/coworker-artifacts/list-tool.test.ts +29 -0
- package/src/resources/extensions/coworker-artifacts/list-tool.ts +53 -0
- package/src/resources/extensions/coworker-artifacts/open-tool.test.ts +30 -0
- package/src/resources/extensions/coworker-artifacts/open-tool.ts +43 -0
- package/src/resources/extensions/coworker-memory/extension-manifest.json +13 -0
- package/src/resources/extensions/coworker-memory/index.test.ts +137 -0
- package/src/resources/extensions/coworker-memory/index.ts +257 -0
- package/src/resources/extensions/coworker-memory/memorize-tool.test.ts +41 -0
- package/src/resources/extensions/coworker-memory/memorize-tool.ts +20 -0
- package/src/resources/extensions/coworker-memory/memory-command.test.ts +134 -0
- package/src/resources/extensions/coworker-memory/memory-command.ts +131 -0
- package/src/resources/extensions/coworker-memory/memory-singleton.test.ts +41 -0
- package/src/resources/extensions/coworker-memory/memory-singleton.ts +89 -0
- package/src/resources/extensions/coworker-memory/recall-tool.test.ts +50 -0
- package/src/resources/extensions/coworker-memory/recall-tool.ts +35 -0
- package/src/resources/extensions/coworker-memory/session-hooks.test.ts +77 -0
- package/src/resources/extensions/coworker-memory/session-hooks.ts +61 -0
- package/src/resources/extensions/coworker-scratchpad/attach-banners.test.ts +124 -0
- package/src/resources/extensions/coworker-scratchpad/attach-banners.ts +67 -0
- package/src/resources/extensions/coworker-scratchpad/extension-manifest.json +13 -0
- package/src/resources/extensions/coworker-scratchpad/format-age.test.ts +30 -0
- package/src/resources/extensions/coworker-scratchpad/format-age.ts +6 -0
- package/src/resources/extensions/coworker-scratchpad/helpers.test.ts +93 -0
- package/src/resources/extensions/coworker-scratchpad/helpers.ts +42 -0
- package/src/resources/extensions/coworker-scratchpad/index.test.ts +514 -0
- package/src/resources/extensions/coworker-scratchpad/index.ts +207 -0
- package/src/resources/extensions/coworker-scratchpad/mime-bundle.test.ts +61 -0
- package/src/resources/extensions/coworker-scratchpad/mime-bundle.ts +23 -0
- package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.test.ts +137 -0
- package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.ts +165 -0
- package/src/resources/extensions/coworker-scratchpad/session-sidecar.test.ts +133 -0
- package/src/resources/extensions/coworker-scratchpad/session-sidecar.ts +68 -0
- package/src/resources/extensions/coworker-scratchpad/sp-command.test.ts +836 -0
- package/src/resources/extensions/coworker-scratchpad/sp-command.ts +602 -0
- package/src/resources/extensions/coworker-scratchpad/workspace-pointer.test.ts +74 -0
- package/src/resources/extensions/coworker-scratchpad/workspace-pointer.ts +55 -0
- package/src/resources/extensions/coworker-scratchpad/workspace-root.test.ts +51 -0
- package/src/resources/extensions/coworker-scratchpad/workspace-root.ts +16 -0
- package/src/resources/extensions/coworker-vault/audit-command.test.ts +109 -0
- package/src/resources/extensions/coworker-vault/audit-command.ts +56 -0
- package/src/resources/extensions/coworker-vault/connect-command.test.ts +103 -0
- package/src/resources/extensions/coworker-vault/connect-command.ts +69 -0
- package/src/resources/extensions/coworker-vault/datasource-command.test.ts +80 -0
- package/src/resources/extensions/coworker-vault/datasource-command.ts +81 -0
- package/src/resources/extensions/coworker-vault/extension-manifest.json +12 -0
- package/src/resources/extensions/coworker-vault/index.test.ts +82 -0
- package/src/resources/extensions/coworker-vault/index.ts +181 -0
- package/src/resources/extensions/coworker-vault/test-helpers.ts +120 -0
- package/src/resources/extensions/coworker-vault/vault-singleton.test.ts +27 -0
- package/src/resources/extensions/coworker-vault/vault-singleton.ts +40 -0
- package/src/resources/extensions/otto/commands/release-notes/_data.ts +85 -0
- package/src/resources/extensions/otto/commands/release-notes/command.ts +16 -3
- package/src/resources/extensions/subagent/index.ts +9 -0
- package/src/resources/extensions/subagent/launch.test.ts +97 -0
- package/src/resources/extensions/subagent/launch.ts +42 -5
- package/src/resources/extensions/subagent/run-store.ts +3 -1
- package/src/resources/extensions/workflow/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/workflow/bootstrap/register-hooks.ts +10 -0
- package/src/resources/extensions/workflow/persona-status.ts +109 -0
- package/src/resources/extensions/workflow/tests/auto-recovery.test.ts +34 -0
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { ScratchpadBusyError, StalenessBanner } from '@otto/coworker-scratchpad';
|
|
4
|
+
import { LocalDataVault } from '@otto/coworker-vault';
|
|
5
|
+
import { validateName, readCellsJsonl, readPersistedLeaf } from './helpers.js';
|
|
6
|
+
import { projectTree, formatTreeText } from '@otto/coworker-scratchpad';
|
|
7
|
+
import { sessionSidecarPath, writeSessionSidecar, deleteSessionSidecar, readSessionSidecar } from './session-sidecar.js';
|
|
8
|
+
import { detectWorkspaceRoot } from './workspace-root.js';
|
|
9
|
+
import { workspaceHash, workspacePointerPath, writeWorkspacePointer } from './workspace-pointer.js';
|
|
10
|
+
import { showRecoveryNotesBanner, showDivergenceBanner, formatNoteLine } from './attach-banners.js';
|
|
11
|
+
import { formatRelativeAge } from './format-age.js';
|
|
12
|
+
const VERBS = ['list', 'new', 'attach', 'reset', 'view', 'remove', 'tree', 'fork', 'save', 'detach', 'clear-history', 'notes', 'evict', 'use', 'unuse'];
|
|
13
|
+
/**
|
|
14
|
+
* Phase 2 Task 16: module-level singleton so banner one-shot state survives
|
|
15
|
+
* across multiple /sp invocations within one process. Reset on /sp reset for
|
|
16
|
+
* the affected scratchpad (the kernel respawns ⇒ stale tracking restarts).
|
|
17
|
+
*
|
|
18
|
+
* Per-test isolation: tests create distinct scratchpad names (or new tmp
|
|
19
|
+
* roots) so the per-(scratchpad, session, ref) key set never collides across
|
|
20
|
+
* tests. No reset-all escape hatch is exposed.
|
|
21
|
+
*/
|
|
22
|
+
const stalenessBanner = new StalenessBanner();
|
|
23
|
+
function readBindingsFromMeta(metaPath) {
|
|
24
|
+
if (!existsSync(metaPath))
|
|
25
|
+
return [];
|
|
26
|
+
try {
|
|
27
|
+
const meta = JSON.parse(readFileSync(metaPath, 'utf8'));
|
|
28
|
+
return Array.isArray(meta.bindings) ? meta.bindings : [];
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function ensureCurrent(deps) {
|
|
35
|
+
let current = deps.getCurrentName();
|
|
36
|
+
if (!current) {
|
|
37
|
+
current = 'default';
|
|
38
|
+
deps.setCurrentName(current);
|
|
39
|
+
}
|
|
40
|
+
return current;
|
|
41
|
+
}
|
|
42
|
+
function listExistingScratchpads(root) {
|
|
43
|
+
if (!existsSync(root))
|
|
44
|
+
return [];
|
|
45
|
+
const names = [];
|
|
46
|
+
for (const entry of readdirSync(root)) {
|
|
47
|
+
const dir = join(root, entry);
|
|
48
|
+
try {
|
|
49
|
+
if (statSync(dir).isDirectory() && existsSync(join(dir, 'meta.json')))
|
|
50
|
+
names.push(entry);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// entry vanished -> skip
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return names.sort();
|
|
57
|
+
}
|
|
58
|
+
function formatCellSummary(rec) {
|
|
59
|
+
const head = rec.ok ? `cell ${rec.id} [ok]` : `cell ${rec.id} [err]`;
|
|
60
|
+
const value = rec.ok ? ` value=${JSON.stringify(rec.value)}` : ` error=${rec.error?.message ?? ''}`;
|
|
61
|
+
return `${head} ${rec.code.split('\n')[0].slice(0, 80)} ${value}`;
|
|
62
|
+
}
|
|
63
|
+
function persistWorkspacePointer(deps, name) {
|
|
64
|
+
const wsRoot = detectWorkspaceRoot(deps.getWorkspaceCwd());
|
|
65
|
+
const wsHash = workspaceHash(wsRoot);
|
|
66
|
+
const wsPath = workspacePointerPath(deps.rootDir(), wsHash);
|
|
67
|
+
const wsPayload = {
|
|
68
|
+
schema_version: 1,
|
|
69
|
+
workspace_hash: wsHash,
|
|
70
|
+
workspace_root: wsRoot,
|
|
71
|
+
last_session_id: deps.getSessionId(),
|
|
72
|
+
last_current_name: name,
|
|
73
|
+
last_attached_at: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
writeWorkspacePointer(wsPath, wsPayload);
|
|
76
|
+
}
|
|
77
|
+
function joinQuotedArg(parts, startIdx) {
|
|
78
|
+
if (startIdx >= parts.length)
|
|
79
|
+
return null;
|
|
80
|
+
const first = parts[startIdx];
|
|
81
|
+
if (!first)
|
|
82
|
+
return null;
|
|
83
|
+
if (!first.startsWith('"'))
|
|
84
|
+
return first;
|
|
85
|
+
// Quoted: walk forward until we find a part ending with "
|
|
86
|
+
if (first.length > 1 && first.endsWith('"')) {
|
|
87
|
+
return first.slice(1, -1); // single-token quoted reason
|
|
88
|
+
}
|
|
89
|
+
const collected = [first.slice(1)]; // strip opening quote
|
|
90
|
+
for (let i = startIdx + 1; i < parts.length; i++) {
|
|
91
|
+
const p = parts[i] ?? '';
|
|
92
|
+
if (p.endsWith('"')) {
|
|
93
|
+
collected.push(p.slice(0, -1));
|
|
94
|
+
return collected.join(' ');
|
|
95
|
+
}
|
|
96
|
+
collected.push(p);
|
|
97
|
+
}
|
|
98
|
+
return collected.join(' '); // no closing quote — take rest
|
|
99
|
+
}
|
|
100
|
+
export function registerSpCommand(pi, deps) {
|
|
101
|
+
pi.registerCommand('sp', {
|
|
102
|
+
description: 'Manage scratchpads: /sp [list|new|attach|reset|view|remove|tree|fork|save|detach|clear-history|notes|evict|use|unuse] [name]',
|
|
103
|
+
getArgumentCompletions: (prefix) => {
|
|
104
|
+
// Split on whitespace but preserve whether the prefix ends with a space
|
|
105
|
+
// (trailing space = user typed the verb and hit space, ready for name completion).
|
|
106
|
+
const trimmed = prefix.trimStart();
|
|
107
|
+
const parts = trimmed.split(/\s+/);
|
|
108
|
+
// If trailing space: user has finished typing the verb, want name completions.
|
|
109
|
+
const trailingSpace = prefix.endsWith(' ');
|
|
110
|
+
if (parts.length <= 1 && !trailingSpace) {
|
|
111
|
+
return VERBS.filter((v) => v.startsWith(parts[0] ?? '')).map((v) => ({ value: v, label: v }));
|
|
112
|
+
}
|
|
113
|
+
const verb = parts[0];
|
|
114
|
+
if (verb === 'attach' || verb === 'reset' || verb === 'view' || verb === 'remove') {
|
|
115
|
+
const namePrefix = trailingSpace && parts.length === 1 ? '' : (parts[1] ?? '');
|
|
116
|
+
return listExistingScratchpads(deps.rootDir())
|
|
117
|
+
.filter((n) => n.startsWith(namePrefix))
|
|
118
|
+
.map((n) => ({ value: `${verb} ${n}`, label: n }));
|
|
119
|
+
}
|
|
120
|
+
return [];
|
|
121
|
+
},
|
|
122
|
+
handler: async (args, ctx) => {
|
|
123
|
+
const trimmed = args.trim();
|
|
124
|
+
const parts = trimmed.length === 0 ? [] : trimmed.split(/\s+/);
|
|
125
|
+
const verb = parts[0] ?? 'list';
|
|
126
|
+
const name = parts[1];
|
|
127
|
+
try {
|
|
128
|
+
switch (verb) {
|
|
129
|
+
case 'list': {
|
|
130
|
+
const mgr = deps.getManager();
|
|
131
|
+
const live = mgr.list();
|
|
132
|
+
const liveByName = new Map(live.map((e) => [e.name, e]));
|
|
133
|
+
const onDisk = listExistingScratchpads(deps.rootDir());
|
|
134
|
+
const all = Array.from(new Set([...liveByName.keys(), ...onDisk])).sort();
|
|
135
|
+
const cur = deps.getCurrentName();
|
|
136
|
+
if (all.length === 0) {
|
|
137
|
+
ctx.ui.notify('No scratchpads yet. Use /sp new <name> to create one.', 'info');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Task D: render idle-age column for warm entries. Compute `now` once so
|
|
141
|
+
// every row shares the same baseline.
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
// Pad name column so the age column lines up. Cold entries have no age.
|
|
144
|
+
const maxNameLen = all.reduce((m, n) => Math.max(m, n.length), 0);
|
|
145
|
+
const lines = all.map((n) => {
|
|
146
|
+
const l = liveByName.get(n);
|
|
147
|
+
const state = l?.live ? '● live' : '○ cold';
|
|
148
|
+
const marker = n === cur ? ' (current)' : '';
|
|
149
|
+
let age = '';
|
|
150
|
+
if (l?.live) {
|
|
151
|
+
age = l.hasActiveCell ? 'active' : formatRelativeAge(now - l.lastUsedAt);
|
|
152
|
+
}
|
|
153
|
+
const namePadded = age ? n.padEnd(maxNameLen) : n;
|
|
154
|
+
const ageCol = age ? ` ${age}` : '';
|
|
155
|
+
// Phase 2 Task 16: binding count read from meta.json. Renders as
|
|
156
|
+
// `uses:N` after the age column so the existing live/cold and
|
|
157
|
+
// active/idle assertions stay intact. Hidden when 0 to avoid
|
|
158
|
+
// visual noise for scratchpads that aren't bound.
|
|
159
|
+
const bindingCount = readBindingsFromMeta(join(deps.rootDir(), n, 'meta.json')).length;
|
|
160
|
+
const bindingsCol = bindingCount > 0 ? ` uses:${bindingCount}` : '';
|
|
161
|
+
return ` ${state} ${namePadded}${ageCol}${bindingsCol}${marker}`;
|
|
162
|
+
});
|
|
163
|
+
ctx.ui.notify(['scratchpads:', ...lines].join('\n'), 'info');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
case 'new': {
|
|
167
|
+
if (!name) {
|
|
168
|
+
ctx.ui.notify('Usage: /sp new <name> [--use <engine:name>] ...', 'error');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
validateName(name);
|
|
172
|
+
// Phase 2 Task 16: parse --use flag. Repeatable: `--use jira:prod --use foo:bar`.
|
|
173
|
+
// Each ref is validated via LocalDataVault.parseRef before persisting; a single
|
|
174
|
+
// malformed ref aborts the whole create so we don't end up with a half-bound
|
|
175
|
+
// scratchpad on disk.
|
|
176
|
+
const bindings = [];
|
|
177
|
+
for (let i = 2; i < parts.length; i++) {
|
|
178
|
+
if (parts[i] === '--use') {
|
|
179
|
+
const ref = parts[i + 1];
|
|
180
|
+
if (!ref) {
|
|
181
|
+
ctx.ui.notify('Usage: /sp new <name> --use <engine:name>', 'error');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
LocalDataVault.parseRef(ref);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
ctx.ui.notify(err.message, 'error');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
bindings.push(ref);
|
|
192
|
+
i++; // skip ref token
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
await deps.getManager().create(name, bindings.length > 0 ? { bindings } : {});
|
|
196
|
+
deps.setCurrentName(name);
|
|
197
|
+
writeSessionSidecar(sessionSidecarPath(deps.rootDir(), deps.getSessionId()), {
|
|
198
|
+
schema_version: 1,
|
|
199
|
+
session_id: deps.getSessionId(),
|
|
200
|
+
current_name: name,
|
|
201
|
+
attached_at: new Date().toISOString(),
|
|
202
|
+
});
|
|
203
|
+
persistWorkspacePointer(deps, name);
|
|
204
|
+
const suffix = bindings.length > 0 ? ` (bindings: ${bindings.join(', ')})` : '';
|
|
205
|
+
ctx.ui.notify(`created scratchpad: ${name} (now current)${suffix}`, 'info');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
case 'use': {
|
|
209
|
+
const target = name;
|
|
210
|
+
const ref = parts[2];
|
|
211
|
+
if (!target || !ref) {
|
|
212
|
+
ctx.ui.notify('Usage: /sp use <name> <engine:name>', 'error');
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
validateName(target);
|
|
216
|
+
try {
|
|
217
|
+
LocalDataVault.parseRef(ref);
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
ctx.ui.notify(err.message, 'error');
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const { added } = await deps.getManager().addBinding(target, ref);
|
|
225
|
+
if (!added) {
|
|
226
|
+
ctx.ui.notify(`binding already present: ${ref} → ${target}`, 'info');
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
// Hint /sp reset because the live kernel was spawned without this
|
|
230
|
+
// env block; the binding only takes effect on the next respawn.
|
|
231
|
+
ctx.ui.notify(`binding added: ${ref} → ${target}. /sp reset to inject into the live kernel.`, 'info');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
ctx.ui.notify(err.message, 'error');
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
case 'unuse': {
|
|
240
|
+
const target = name;
|
|
241
|
+
const ref = parts[2];
|
|
242
|
+
if (!target || !ref) {
|
|
243
|
+
ctx.ui.notify('Usage: /sp unuse <name> <engine:name>', 'error');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
validateName(target);
|
|
247
|
+
try {
|
|
248
|
+
const { removed } = await deps.getManager().removeBinding(target, ref);
|
|
249
|
+
if (!removed) {
|
|
250
|
+
ctx.ui.notify(`binding not present: ${ref} → ${target}`, 'info');
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
ctx.ui.notify(`binding removed: ${ref} from ${target}. /sp reset to drop the env block from the live kernel.`, 'info');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
ctx.ui.notify(err.message, 'error');
|
|
258
|
+
}
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
case 'attach': {
|
|
262
|
+
if (!name) {
|
|
263
|
+
ctx.ui.notify('Usage: /sp attach <name> [--force-takeover] [--reason "<text>"]', 'error');
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
validateName(name);
|
|
267
|
+
// Slash-command path is strict: error on typo instead of silently auto-creating
|
|
268
|
+
// (the LLM-tool path via cw_scratchpad action=exec stays permissive).
|
|
269
|
+
const metaPath = join(deps.rootDir(), name, 'meta.json');
|
|
270
|
+
if (!existsSync(metaPath)) {
|
|
271
|
+
ctx.ui.notify(`scratchpad not found: ${name}. Use /sp new ${name} to create it.`, 'error');
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const forceFlag = parts.includes('--force-takeover');
|
|
275
|
+
const reasonIdx = parts.indexOf('--reason');
|
|
276
|
+
const reasonArg = reasonIdx >= 0 ? joinQuotedArg(parts, reasonIdx + 1) : null;
|
|
277
|
+
let attached = false;
|
|
278
|
+
// Phase 2 Task 16: capture the runtime returned by getOrAttach so we
|
|
279
|
+
// can use its spawnTime as the staleness banner's clock. Defensive
|
|
280
|
+
// typing because some tests return null from a stubbed manager.
|
|
281
|
+
let runtime = null;
|
|
282
|
+
try {
|
|
283
|
+
runtime = (await deps.getManager().getOrAttach(name));
|
|
284
|
+
attached = true;
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
if (!(err instanceof ScratchpadBusyError)) {
|
|
288
|
+
ctx.ui.notify(err.message, 'error');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const holder = err.holder;
|
|
292
|
+
const proceed = forceFlag || await ctx.ui.confirm('Force takeover?', `${name}: lock held by pid ${holder.pid} on host ${holder.host} (acquired ${holder.acquired_at}). Take it?`);
|
|
293
|
+
if (!proceed) {
|
|
294
|
+
ctx.ui.notify('cancelled', 'info');
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
let reason = reasonArg;
|
|
298
|
+
if (reason === null) {
|
|
299
|
+
const input = await ctx.ui.input('Takeover reason', 'why are you taking over?');
|
|
300
|
+
if (input === undefined) {
|
|
301
|
+
ctx.ui.notify('cancelled', 'info');
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
reason = input.trim() || '(no reason given)';
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
runtime = (await deps.getManager().getOrAttach(name, { forceTakeover: true, takeoverReason: reason }));
|
|
308
|
+
attached = true;
|
|
309
|
+
}
|
|
310
|
+
catch (retryErr) {
|
|
311
|
+
ctx.ui.notify(retryErr.message, 'error');
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (!attached)
|
|
316
|
+
return;
|
|
317
|
+
deps.setCurrentName(name);
|
|
318
|
+
writeSessionSidecar(sessionSidecarPath(deps.rootDir(), deps.getSessionId()), {
|
|
319
|
+
schema_version: 1,
|
|
320
|
+
session_id: deps.getSessionId(),
|
|
321
|
+
current_name: name,
|
|
322
|
+
attached_at: new Date().toISOString(),
|
|
323
|
+
});
|
|
324
|
+
persistWorkspacePointer(deps, name);
|
|
325
|
+
ctx.ui.notify(`attached to scratchpad: ${name}`, 'info');
|
|
326
|
+
// §2 + §4 banners (1g2):
|
|
327
|
+
const { markSeen } = showRecoveryNotesBanner(name, deps.rootDir(), ctx.ui);
|
|
328
|
+
if (markSeen) {
|
|
329
|
+
await deps.getManager().markRecoveryNotesSeen(name);
|
|
330
|
+
}
|
|
331
|
+
showDivergenceBanner(name, deps.rootDir(), ctx.ui);
|
|
332
|
+
// Phase 2 Task 16: staleness banner. If a vault is wired AND this
|
|
333
|
+
// scratchpad has bindings, check each ref's last-modified vs the
|
|
334
|
+
// kernel's spawnTime; emit one-shot warning if any are stale.
|
|
335
|
+
// Skipped silently when no vault is configured — the scratchpad
|
|
336
|
+
// extension still works without coworker-vault present.
|
|
337
|
+
//
|
|
338
|
+
// spawnTime source: prefer the runtime's stamped Date from
|
|
339
|
+
// ChildProcessRuntime.start() (Task 13). Fallback to meta.json's
|
|
340
|
+
// mtime — also rewritten on every attach by writeMeta — when the
|
|
341
|
+
// runtime is null (stubbed manager paths in tests) or missing the
|
|
342
|
+
// spawnTime field. Both anchors converge on the same user-visible
|
|
343
|
+
// semantic: "your creds changed since you started this kernel."
|
|
344
|
+
if (deps.getStalenessVault) {
|
|
345
|
+
const vault = deps.getStalenessVault();
|
|
346
|
+
const bindings = readBindingsFromMeta(join(deps.rootDir(), name, 'meta.json'));
|
|
347
|
+
if (vault && bindings.length > 0) {
|
|
348
|
+
try {
|
|
349
|
+
const spawnTime = runtime?.spawnTime instanceof Date && runtime.spawnTime.getTime() > 0
|
|
350
|
+
? runtime.spawnTime
|
|
351
|
+
: new Date(statSync(join(deps.rootDir(), name, 'meta.json')).mtime);
|
|
352
|
+
const banner = await stalenessBanner.check({
|
|
353
|
+
scratchpadName: name,
|
|
354
|
+
sessionId: deps.getSessionId(),
|
|
355
|
+
bindings,
|
|
356
|
+
spawnTime,
|
|
357
|
+
lookupLastModified: (ref) => vault.lookupLastModified(ref),
|
|
358
|
+
});
|
|
359
|
+
if (banner)
|
|
360
|
+
ctx.ui.notify(banner, 'warning');
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
// Banner emission must never block /sp attach — swallow errors.
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
case 'reset': {
|
|
370
|
+
const target = name ?? ensureCurrent(deps);
|
|
371
|
+
validateName(target);
|
|
372
|
+
const mgr = deps.getManager();
|
|
373
|
+
// Phase 2 Task 16: preserve bindings across reset. remove() wipes
|
|
374
|
+
// the dir, then create() rewrites a fresh meta.json — without this
|
|
375
|
+
// pre-read the new meta.bindings would be []. Resetting is "respawn
|
|
376
|
+
// the kernel with current config", so the binding list is part of
|
|
377
|
+
// config that must survive.
|
|
378
|
+
const preservedBindings = mgr.readBindings(target);
|
|
379
|
+
await mgr.remove(target);
|
|
380
|
+
await mgr.create(target, preservedBindings.length > 0 ? { bindings: preservedBindings } : {});
|
|
381
|
+
// Phase 2 Task 16: clear the staleness-banner one-shot state for
|
|
382
|
+
// this scratchpad — the new kernel has a fresh spawnTime, so old
|
|
383
|
+
// "stale" status is no longer relevant.
|
|
384
|
+
stalenessBanner.resetForRespawn(target);
|
|
385
|
+
// currentName preserved if it was the reset target; otherwise unchanged
|
|
386
|
+
ctx.ui.notify(`reset scratchpad: ${target}`, 'info');
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
case 'view': {
|
|
390
|
+
const target = name ?? ensureCurrent(deps);
|
|
391
|
+
validateName(target);
|
|
392
|
+
const { cells, total_cells } = readCellsJsonl(join(deps.rootDir(), target));
|
|
393
|
+
if (total_cells === 0) {
|
|
394
|
+
ctx.ui.notify(`${target}: no cells yet`, 'info');
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const tail = cells.slice(-10);
|
|
398
|
+
const lines = tail.map((c) => formatCellSummary(c));
|
|
399
|
+
ctx.ui.notify([`${target} (${total_cells} cells, last 10):`, ...lines].join('\n'), 'info');
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
case 'remove': {
|
|
403
|
+
if (!name) {
|
|
404
|
+
ctx.ui.notify('Usage: /sp remove <name> [--yes]', 'error');
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const force = parts.includes('--yes');
|
|
408
|
+
validateName(name);
|
|
409
|
+
if (name === deps.getCurrentName() && !force) {
|
|
410
|
+
const confirmed = await ctx.ui.confirm('Remove current scratchpad?', `${name} is your current scratchpad. Remove it? This deletes kernel.db, namespace.json, and the cell journal.`);
|
|
411
|
+
if (!confirmed) {
|
|
412
|
+
ctx.ui.notify('cancelled', 'info');
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const wasCurrent = name === deps.getCurrentName();
|
|
417
|
+
await deps.getManager().remove(name);
|
|
418
|
+
if (wasCurrent) {
|
|
419
|
+
deleteSessionSidecar(sessionSidecarPath(deps.rootDir(), deps.getSessionId()));
|
|
420
|
+
deps.setCurrentName(null);
|
|
421
|
+
}
|
|
422
|
+
ctx.ui.notify(`removed scratchpad: ${name}`, 'info');
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
case 'evict': {
|
|
426
|
+
if (!name) {
|
|
427
|
+
ctx.ui.notify('Usage: /sp evict <name> [--force]', 'error');
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const force = parts.includes('--force');
|
|
431
|
+
validateName(name);
|
|
432
|
+
try {
|
|
433
|
+
const { interrupted } = await deps.getManager().evict(name, { force });
|
|
434
|
+
const msg = interrupted
|
|
435
|
+
? `interrupted active cell and evicted ${name}`
|
|
436
|
+
: `evicted ${name} (still on disk; /sp attach ${name} to re-warm)`;
|
|
437
|
+
ctx.ui.notify(msg, 'info');
|
|
438
|
+
}
|
|
439
|
+
catch (e) {
|
|
440
|
+
ctx.ui.notify(e.message, 'error');
|
|
441
|
+
}
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
case 'tree': {
|
|
445
|
+
// Usage: /sp tree [<name>] [--to <id>]
|
|
446
|
+
const flagIdx = parts.indexOf('--to');
|
|
447
|
+
let target;
|
|
448
|
+
if (flagIdx === -1) {
|
|
449
|
+
target = name ?? ensureCurrent(deps);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
target = flagIdx === 1 ? ensureCurrent(deps) : parts[1];
|
|
453
|
+
const toId = Number(parts[flagIdx + 1]);
|
|
454
|
+
if (!Number.isInteger(toId) || toId <= 0) {
|
|
455
|
+
ctx.ui.notify('Usage: /sp tree [<name>] --to <id>', 'error');
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
validateName(target);
|
|
459
|
+
await deps.getManager().setLeaf(target, toId);
|
|
460
|
+
ctx.ui.notify(`set leaf of ${target} to cell ${toId}`, 'info');
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
validateName(target);
|
|
464
|
+
const { cells } = readCellsJsonl(join(deps.rootDir(), target));
|
|
465
|
+
if (cells.length === 0) {
|
|
466
|
+
ctx.ui.notify(`${target}: no cells yet`, 'info');
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const tree = projectTree(cells);
|
|
470
|
+
const leaf = readPersistedLeaf(join(deps.rootDir(), target, 'meta.json'));
|
|
471
|
+
ctx.ui.notify(`${target} cell tree:\n${formatTreeText(tree, leaf)}`, 'info');
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
case 'fork': {
|
|
475
|
+
// Usage: /sp fork <src> <dst>
|
|
476
|
+
if (parts.length < 3) {
|
|
477
|
+
ctx.ui.notify('Usage: /sp fork <src> <dst>', 'error');
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const src = parts[1];
|
|
481
|
+
const dst = parts[2];
|
|
482
|
+
validateName(src);
|
|
483
|
+
validateName(dst);
|
|
484
|
+
await deps.getManager().fork(src, dst);
|
|
485
|
+
ctx.ui.notify(`forked ${src} → ${dst}`, 'info');
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
case 'save': {
|
|
489
|
+
const target = name ?? deps.getCurrentName();
|
|
490
|
+
if (!target) {
|
|
491
|
+
ctx.ui.notify('Usage: /sp save [<name>] — no current scratchpad', 'error');
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
validateName(target);
|
|
495
|
+
await deps.getManager().save(target);
|
|
496
|
+
ctx.ui.notify(`saved ${target}`, 'info');
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
case 'detach': {
|
|
500
|
+
const target = deps.getCurrentName();
|
|
501
|
+
if (!target) {
|
|
502
|
+
ctx.ui.notify('not attached to any scratchpad', 'error');
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
await deps.getManager().detach(target, deps.getSessionId());
|
|
506
|
+
deleteSessionSidecar(sessionSidecarPath(deps.rootDir(), deps.getSessionId()));
|
|
507
|
+
deps.setCurrentName(null);
|
|
508
|
+
ctx.ui.notify(`detached from ${target}`, 'info');
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
case 'clear-history': {
|
|
512
|
+
const target = name ?? deps.getCurrentName();
|
|
513
|
+
if (!target) {
|
|
514
|
+
ctx.ui.notify('Usage: /sp clear-history [<name>] — no current scratchpad', 'error');
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
validateName(target);
|
|
518
|
+
const confirmed = await ctx.ui.confirm('Clear cell history?', `Clear cell history for ${target}? kernel.db + namespace.json are preserved.`);
|
|
519
|
+
if (!confirmed) {
|
|
520
|
+
ctx.ui.notify('cancelled', 'info');
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
await deps.getManager().clearHistory(target);
|
|
524
|
+
ctx.ui.notify(`cleared cell history for ${target}`, 'info');
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
case 'notes': {
|
|
528
|
+
const target = name ?? deps.getCurrentName();
|
|
529
|
+
if (!target) {
|
|
530
|
+
ctx.ui.notify('Usage: /sp notes [<name>] (no current scratchpad)', 'error');
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
validateName(target);
|
|
534
|
+
const metaPath = join(deps.rootDir(), target, 'meta.json');
|
|
535
|
+
if (!existsSync(metaPath)) {
|
|
536
|
+
ctx.ui.notify(`scratchpad not found: ${target}`, 'error');
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
let meta;
|
|
540
|
+
try {
|
|
541
|
+
meta = JSON.parse(readFileSync(metaPath, 'utf8'));
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
ctx.ui.notify(`${target}: meta.json unreadable`, 'error');
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
const notes = Array.isArray(meta.recovery_notes) ? meta.recovery_notes : [];
|
|
548
|
+
if (notes.length === 0) {
|
|
549
|
+
ctx.ui.notify(`no recovery notes for ${target}`, 'info');
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const lines = notes.map(formatNoteLine);
|
|
553
|
+
ctx.ui.notify(`${target} recovery notes (${notes.length}):\n${lines.join('\n')}`, 'info');
|
|
554
|
+
// Deliberately does NOT update recovery_notes_seen_at — re-view path is read-only.
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
default: {
|
|
558
|
+
ctx.ui.notify(`unknown verb: ${verb}. Try one of: ${VERBS.join(', ')}`, 'error');
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
catch (err) {
|
|
563
|
+
ctx.ui.notify(err.message, 'error');
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Phase 3 Task 18: exposes a session→scratchpad-name lookup for cross-pillar
|
|
570
|
+
* consumers (memory's MemoryRecorder uses this to derive the default Room).
|
|
571
|
+
*
|
|
572
|
+
* Reads the per-session sidecar written by /sp new and /sp attach. Returns
|
|
573
|
+
* null when no scratchpad is currently attached for `sessionId`, when
|
|
574
|
+
* `sessionId` is empty/undefined, or on any IO/parse error — callers must
|
|
575
|
+
* tolerate null (the caller-side fallback is "use workspace as Room").
|
|
576
|
+
*
|
|
577
|
+
* Decoupled from `SpDeps` so consumers outside the slash-command lifecycle
|
|
578
|
+
* (e.g. recorders constructed at extension activation) can call it without
|
|
579
|
+
* holding a reference to the scratchpad manager.
|
|
580
|
+
*
|
|
581
|
+
* Note on the sidecar reader's actual signature: `readSessionSidecar(path)`
|
|
582
|
+
* takes a fully-resolved path, not `{scratchpadsRoot, sessionId}`. We compose
|
|
583
|
+
* with `sessionSidecarPath(rootDir, sessionId)` to bridge.
|
|
584
|
+
*/
|
|
585
|
+
export function createCurrentScratchpadProvider(opts) {
|
|
586
|
+
return (sessionId) => {
|
|
587
|
+
if (!sessionId)
|
|
588
|
+
return null;
|
|
589
|
+
try {
|
|
590
|
+
const sidecar = readSessionSidecar(sessionSidecarPath(opts.scratchpadsRoot, sessionId));
|
|
591
|
+
return sidecar?.current_name ?? null;
|
|
592
|
+
}
|
|
593
|
+
catch {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
export const WORKSPACE_POINTER_STALE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
5
|
+
export function workspaceHash(workspaceRoot) {
|
|
6
|
+
return createHash('sha256').update(workspaceRoot).digest('hex').slice(0, 16);
|
|
7
|
+
}
|
|
8
|
+
export function workspacePointerPath(rootDir, hash) {
|
|
9
|
+
return join(rootDir, '_workspaces', `${hash}.json`);
|
|
10
|
+
}
|
|
11
|
+
export function readWorkspacePointer(path) {
|
|
12
|
+
if (!existsSync(path))
|
|
13
|
+
return null;
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
16
|
+
if (parsed.schema_version === 1 &&
|
|
17
|
+
typeof parsed.workspace_hash === 'string' &&
|
|
18
|
+
typeof parsed.workspace_root === 'string' &&
|
|
19
|
+
typeof parsed.last_session_id === 'string' &&
|
|
20
|
+
typeof parsed.last_current_name === 'string' &&
|
|
21
|
+
typeof parsed.last_attached_at === 'string') {
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function writeWorkspacePointer(path, payload) {
|
|
31
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
32
|
+
const tmp = `${path}.tmp`;
|
|
33
|
+
writeFileSync(tmp, JSON.stringify(payload, null, 2));
|
|
34
|
+
renameSync(tmp, path);
|
|
35
|
+
}
|
|
36
|
+
export function isPointerFresh(pointer, now) {
|
|
37
|
+
const attachedAt = Date.parse(pointer.last_attached_at);
|
|
38
|
+
if (Number.isNaN(attachedAt))
|
|
39
|
+
return false;
|
|
40
|
+
return (now - attachedAt) < WORKSPACE_POINTER_STALE_MS;
|
|
41
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
export function detectWorkspaceRoot(cwd) {
|
|
3
|
+
try {
|
|
4
|
+
const out = execSync('git rev-parse --show-toplevel', {
|
|
5
|
+
cwd,
|
|
6
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
7
|
+
encoding: 'utf8',
|
|
8
|
+
timeout: 500,
|
|
9
|
+
}).trim();
|
|
10
|
+
if (out)
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
/* not a git repo, git not installed, or timeout — fall through */
|
|
15
|
+
}
|
|
16
|
+
return cwd;
|
|
17
|
+
}
|