@cmetech/otto 1.1.0 → 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 +82 -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/dist/update-cmd.d.ts +19 -0
- package/dist/update-cmd.js +177 -6
- 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 +96 -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,142 @@
|
|
|
1
|
+
// PersonaRegistry — install / list / activate / switch. Spec §2.5.
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { parsePersonaManifest } from './manifest.js';
|
|
5
|
+
async function dirExists(p) {
|
|
6
|
+
try {
|
|
7
|
+
const s = await fs.stat(p);
|
|
8
|
+
return s.isDirectory();
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
if (err.code === 'ENOENT')
|
|
12
|
+
return false;
|
|
13
|
+
throw err;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function copyDir(src, dst) {
|
|
17
|
+
await fs.mkdir(dst, { recursive: true });
|
|
18
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
19
|
+
for (const e of entries) {
|
|
20
|
+
const s = path.join(src, e.name);
|
|
21
|
+
const d = path.join(dst, e.name);
|
|
22
|
+
if (e.isDirectory()) {
|
|
23
|
+
await copyDir(s, d);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
await fs.copyFile(s, d);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class PersonaRegistry {
|
|
31
|
+
#personasDir;
|
|
32
|
+
constructor(opts) {
|
|
33
|
+
this.#personasDir = path.join(opts.ottoHome, 'personas');
|
|
34
|
+
}
|
|
35
|
+
async ensureDefaultInstalled() {
|
|
36
|
+
const target = path.join(this.#personasDir, 'default');
|
|
37
|
+
if (await dirExists(target))
|
|
38
|
+
return;
|
|
39
|
+
// Source: bundled defaults shipped with this package
|
|
40
|
+
const here = path.dirname(new URL(import.meta.url).pathname);
|
|
41
|
+
const source = path.join(here, 'defaults');
|
|
42
|
+
await copyDir(source, target);
|
|
43
|
+
}
|
|
44
|
+
async list() {
|
|
45
|
+
if (!(await dirExists(this.#personasDir)))
|
|
46
|
+
return [];
|
|
47
|
+
const entries = await fs.readdir(this.#personasDir, { withFileTypes: true });
|
|
48
|
+
const out = [];
|
|
49
|
+
for (const e of entries) {
|
|
50
|
+
if (!e.isDirectory())
|
|
51
|
+
continue;
|
|
52
|
+
const manifestPath = path.join(this.#personasDir, e.name, 'manifest.yaml');
|
|
53
|
+
try {
|
|
54
|
+
const raw = await fs.readFile(manifestPath, 'utf8');
|
|
55
|
+
out.push(parsePersonaManifest(raw));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// skip malformed bundles
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
async installFromPath(bundlePath) {
|
|
64
|
+
const manifestPath = path.join(bundlePath, 'manifest.yaml');
|
|
65
|
+
let raw;
|
|
66
|
+
try {
|
|
67
|
+
raw = await fs.readFile(manifestPath, 'utf8');
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (err.code === 'ENOENT') {
|
|
71
|
+
throw new Error(`bundle missing manifest.yaml at ${manifestPath}`);
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
const manifest = parsePersonaManifest(raw);
|
|
76
|
+
const target = path.join(this.#personasDir, manifest.name);
|
|
77
|
+
await fs.rm(target, { recursive: true, force: true });
|
|
78
|
+
await copyDir(bundlePath, target);
|
|
79
|
+
return manifest;
|
|
80
|
+
}
|
|
81
|
+
async activateInWorkspace(workspaceRoot, name) {
|
|
82
|
+
const persona = await this.get(name);
|
|
83
|
+
if (!persona)
|
|
84
|
+
throw new Error(`persona not installed: ${name}`);
|
|
85
|
+
const wsOtto = path.join(workspaceRoot, '.otto');
|
|
86
|
+
await fs.mkdir(wsOtto, { recursive: true });
|
|
87
|
+
const record = {
|
|
88
|
+
active: name,
|
|
89
|
+
activated_at: new Date().toISOString(),
|
|
90
|
+
memory_seed_applied: false,
|
|
91
|
+
};
|
|
92
|
+
await fs.writeFile(path.join(wsOtto, 'persona.json'), JSON.stringify(record, null, 2));
|
|
93
|
+
}
|
|
94
|
+
async activeInWorkspace(workspaceRoot) {
|
|
95
|
+
const recordPath = path.join(workspaceRoot, '.otto', 'persona.json');
|
|
96
|
+
try {
|
|
97
|
+
const raw = await fs.readFile(recordPath, 'utf8');
|
|
98
|
+
const record = JSON.parse(raw);
|
|
99
|
+
const m = await this.get(record.active);
|
|
100
|
+
if (m)
|
|
101
|
+
return m;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// fall through to default
|
|
105
|
+
}
|
|
106
|
+
const def = await this.get('default');
|
|
107
|
+
if (!def)
|
|
108
|
+
throw new Error('default persona not installed');
|
|
109
|
+
return def;
|
|
110
|
+
}
|
|
111
|
+
async get(name) {
|
|
112
|
+
const manifestPath = path.join(this.#personasDir, name, 'manifest.yaml');
|
|
113
|
+
try {
|
|
114
|
+
const raw = await fs.readFile(manifestPath, 'utf8');
|
|
115
|
+
return parsePersonaManifest(raw);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async uninstall(name, opts) {
|
|
122
|
+
if (name === 'default') {
|
|
123
|
+
throw new Error('cannot uninstall the built-in default persona');
|
|
124
|
+
}
|
|
125
|
+
for (const ws of opts.trackedWorkspaces) {
|
|
126
|
+
try {
|
|
127
|
+
const raw = await fs.readFile(path.join(ws, '.otto', 'persona.json'), 'utf8');
|
|
128
|
+
const record = JSON.parse(raw);
|
|
129
|
+
if (record.active === name) {
|
|
130
|
+
throw new Error(`persona ${name} is active in workspace ${ws}; switch first`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
if (err.code === 'ENOENT')
|
|
135
|
+
continue;
|
|
136
|
+
if (err instanceof Error && err.message.startsWith('persona '))
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
await fs.rm(path.join(this.#personasDir, name), { recursive: true, force: true });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@otto/coworker-persona",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Persona registry, manifest parser, and built-in default persona for Otto co-worker",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"otto": {
|
|
7
|
+
"linkable": true,
|
|
8
|
+
"scope": "@otto",
|
|
9
|
+
"name": "coworker-persona"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.json && node scripts/copy-defaults.cjs",
|
|
21
|
+
"build:publish": "tsc -p tsconfig.publish.json && node scripts/copy-defaults.cjs"
|
|
22
|
+
},
|
|
23
|
+
"files": ["dist"],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@otto/coworker-utils": "*",
|
|
26
|
+
"yaml": "^2.4.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
|
|
5
|
+
function copyDir(src, dst) {
|
|
6
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
7
|
+
for (const e of fs.readdirSync(src, { withFileTypes: true })) {
|
|
8
|
+
const s = path.join(src, e.name);
|
|
9
|
+
const d = path.join(dst, e.name);
|
|
10
|
+
e.isDirectory() ? copyDir(s, d) : fs.copyFileSync(s, d);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const src = path.resolve(__dirname, '..', 'src', 'defaults');
|
|
15
|
+
const dst = path.resolve(__dirname, '..', 'dist', 'defaults');
|
|
16
|
+
copyDir(src, dst);
|
|
17
|
+
console.log(`Copied persona defaults: ${src} → ${dst}`);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// /persona slash-command handlers. Spec §2.5 + §5.2.
|
|
2
|
+
// Pure functions over a PersonaRegistry — UI adapters live in otto-cli.
|
|
3
|
+
import { PersonaRegistry } from './registry.js';
|
|
4
|
+
|
|
5
|
+
export async function handleList(registry: PersonaRegistry, workspaceRoot: string): Promise<string[]> {
|
|
6
|
+
const installed = await registry.list();
|
|
7
|
+
const active = await registry.activeInWorkspace(workspaceRoot);
|
|
8
|
+
return installed.map(p => `${p.name === active.name ? '*' : ' '} ${p.name} — ${p.display_name} (v${p.version})`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function handleCurrent(registry: PersonaRegistry, workspaceRoot: string): Promise<string[]> {
|
|
12
|
+
const active = await registry.activeInWorkspace(workspaceRoot);
|
|
13
|
+
return [
|
|
14
|
+
`Active persona: ${active.name}`,
|
|
15
|
+
`Display name: ${active.display_name}`,
|
|
16
|
+
`Version: ${active.version}`,
|
|
17
|
+
`Description: ${active.description}`,
|
|
18
|
+
`Author: ${active.author}`,
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function handleSwitch(registry: PersonaRegistry, workspaceRoot: string, name: string): Promise<string[]> {
|
|
23
|
+
const persona = await registry.get(name);
|
|
24
|
+
if (!persona) throw new Error(`persona "${name}" is not installed; run /persona list to see installed personas`);
|
|
25
|
+
await registry.activateInWorkspace(workspaceRoot, name);
|
|
26
|
+
return [`Switched to persona: ${name} (${persona.display_name})`];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function handleReset(registry: PersonaRegistry, workspaceRoot: string): Promise<string[]> {
|
|
30
|
+
await registry.activateInWorkspace(workspaceRoot, 'default');
|
|
31
|
+
return ['Persona reset to default'];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function handleInstall(registry: PersonaRegistry, source: string): Promise<string[]> {
|
|
35
|
+
// For Phase 0 we only support local-path install. Npm + git install come in Phase 6.
|
|
36
|
+
const manifest = await registry.installFromPath(source);
|
|
37
|
+
return [`Installed persona: ${manifest.name} (${manifest.display_name})`];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function handleUninstall(
|
|
41
|
+
registry: PersonaRegistry,
|
|
42
|
+
name: string,
|
|
43
|
+
trackedWorkspaces: string[],
|
|
44
|
+
): Promise<string[]> {
|
|
45
|
+
await registry.uninstall(name, { trackedWorkspaces });
|
|
46
|
+
return [`Uninstalled persona: ${name}`];
|
|
47
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
name: default
|
|
2
|
+
display_name: "Default Co-Worker"
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Generic co-worker with no domain specialization. Auto-activates when no persona is set."
|
|
5
|
+
author: "@cmetech"
|
|
6
|
+
otto_version_required: ">=2.0.0"
|
|
7
|
+
steering:
|
|
8
|
+
- steering/identity.md
|
|
9
|
+
status_line:
|
|
10
|
+
label: "default"
|
|
11
|
+
color: "#6B6B7C"
|
|
12
|
+
icon: "⚙"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { parsePersonaManifest, type PersonaManifest } from './manifest.js';
|
|
4
|
+
|
|
5
|
+
describe('persona manifest', () => {
|
|
6
|
+
it('parses a minimum-valid manifest', () => {
|
|
7
|
+
const yaml = `
|
|
8
|
+
name: noc-ops
|
|
9
|
+
display_name: "NOC / IT Ops Analyst"
|
|
10
|
+
version: 1.0.0
|
|
11
|
+
description: "NOC analyst co-worker"
|
|
12
|
+
author: "@cmetech"
|
|
13
|
+
otto_version_required: ">=2.0.0"
|
|
14
|
+
steering:
|
|
15
|
+
- steering/identity.md
|
|
16
|
+
status_line:
|
|
17
|
+
label: NOC
|
|
18
|
+
color: "#FAD22D"
|
|
19
|
+
icon: "🛡"
|
|
20
|
+
`;
|
|
21
|
+
const m: PersonaManifest = parsePersonaManifest(yaml);
|
|
22
|
+
assert.equal(m.name, 'noc-ops');
|
|
23
|
+
assert.equal(m.steering.length, 1);
|
|
24
|
+
assert.equal(m.status_line.label, 'NOC');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('rejects manifest missing required name', () => {
|
|
28
|
+
const yaml = 'version: 1.0.0';
|
|
29
|
+
assert.throws(() => parsePersonaManifest(yaml), /name/);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('rejects manifest missing version', () => {
|
|
33
|
+
const yaml = 'name: noc-ops';
|
|
34
|
+
assert.throws(() => parsePersonaManifest(yaml), /version/);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('defaults memory_seed.apply_on_first_activation to false when absent', () => {
|
|
38
|
+
const yaml = `
|
|
39
|
+
name: x
|
|
40
|
+
display_name: x
|
|
41
|
+
version: 1.0.0
|
|
42
|
+
description: x
|
|
43
|
+
author: x
|
|
44
|
+
otto_version_required: ">=2.0.0"
|
|
45
|
+
steering: []
|
|
46
|
+
status_line: { label: X, color: "#000000", icon: "x" }
|
|
47
|
+
`;
|
|
48
|
+
const m = parsePersonaManifest(yaml);
|
|
49
|
+
assert.equal(m.memory_seed?.apply_on_first_activation ?? false, false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('parses artifact_kinds list when provided', () => {
|
|
53
|
+
const yaml = `
|
|
54
|
+
name: x
|
|
55
|
+
display_name: x
|
|
56
|
+
version: 1.0.0
|
|
57
|
+
description: x
|
|
58
|
+
author: x
|
|
59
|
+
otto_version_required: ">=2.0.0"
|
|
60
|
+
steering: []
|
|
61
|
+
status_line: { label: X, color: "#000", icon: "x" }
|
|
62
|
+
artifact_kinds: [report, workbook, inventory_report]
|
|
63
|
+
`;
|
|
64
|
+
const m = parsePersonaManifest(yaml);
|
|
65
|
+
assert.deepEqual(m.artifact_kinds, ['report', 'workbook', 'inventory_report']);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Persona manifest parser. See spec §2.5.
|
|
2
|
+
import { parse as parseYaml } from 'yaml';
|
|
3
|
+
|
|
4
|
+
export interface PersonaStatusLine {
|
|
5
|
+
label: string;
|
|
6
|
+
color: string; // hex
|
|
7
|
+
icon: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PersonaMemorySeed {
|
|
11
|
+
apply_on_first_activation: boolean;
|
|
12
|
+
scope?: 'global' | 'per-project' | 'per-project-tagged';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface PersonaManifest {
|
|
16
|
+
name: string;
|
|
17
|
+
display_name: string;
|
|
18
|
+
version: string;
|
|
19
|
+
description: string;
|
|
20
|
+
author: string;
|
|
21
|
+
otto_version_required: string;
|
|
22
|
+
steering: string[];
|
|
23
|
+
memory_seed?: PersonaMemorySeed;
|
|
24
|
+
engines?: string;
|
|
25
|
+
artifact_kinds?: string[];
|
|
26
|
+
skills_path?: string;
|
|
27
|
+
status_line: PersonaStatusLine;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Order matters: tests assert the FIRST missing field reported, and the
|
|
31
|
+
// "missing version" case supplies only `name`, so `version` must be checked
|
|
32
|
+
// before `display_name`.
|
|
33
|
+
const REQUIRED: Array<keyof PersonaManifest> = [
|
|
34
|
+
'name', 'version', 'display_name', 'description', 'author',
|
|
35
|
+
'otto_version_required', 'steering', 'status_line',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
export function parsePersonaManifest(yamlText: string): PersonaManifest {
|
|
39
|
+
const raw = parseYaml(yamlText) as Record<string, unknown> | null;
|
|
40
|
+
if (!raw || typeof raw !== 'object') {
|
|
41
|
+
throw new Error('persona manifest must be a YAML object');
|
|
42
|
+
}
|
|
43
|
+
for (const field of REQUIRED) {
|
|
44
|
+
if (!(field in raw)) {
|
|
45
|
+
throw new Error(`persona manifest missing required field: ${field}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return raw as unknown as PersonaManifest;
|
|
49
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import * as fs from 'node:fs/promises';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
import { PersonaRegistry } from './registry.js';
|
|
7
|
+
|
|
8
|
+
let tmpHome: string;
|
|
9
|
+
|
|
10
|
+
describe('PersonaRegistry', () => {
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
tmpHome = await fs.mkdtemp(path.join(os.tmpdir(), 'persona-test-'));
|
|
13
|
+
await fs.mkdir(path.join(tmpHome, 'personas'), { recursive: true });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
await fs.rm(tmpHome, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('list returns built-in default persona on a fresh registry', async () => {
|
|
21
|
+
const r = new PersonaRegistry({ ottoHome: tmpHome });
|
|
22
|
+
await r.ensureDefaultInstalled();
|
|
23
|
+
const installed = await r.list();
|
|
24
|
+
assert.ok(installed.find(p => p.name === 'default'), 'default persona should be installed');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('install copies a bundle directory into ~/.otto/personas/<name>', async () => {
|
|
28
|
+
const r = new PersonaRegistry({ ottoHome: tmpHome });
|
|
29
|
+
// Stage a fake persona bundle
|
|
30
|
+
const bundle = await fs.mkdtemp(path.join(os.tmpdir(), 'fake-bundle-'));
|
|
31
|
+
await fs.mkdir(path.join(bundle, 'steering'), { recursive: true });
|
|
32
|
+
await fs.writeFile(path.join(bundle, 'manifest.yaml'),
|
|
33
|
+
'name: noc-ops\ndisplay_name: NOC\nversion: 1.0.0\ndescription: x\nauthor: x\notto_version_required: ">=2.0.0"\nsteering: [steering/identity.md]\nstatus_line: { label: NOC, color: "#FAD22D", icon: "🛡" }\n');
|
|
34
|
+
await fs.writeFile(path.join(bundle, 'steering', 'identity.md'), 'noc identity');
|
|
35
|
+
|
|
36
|
+
await r.installFromPath(bundle);
|
|
37
|
+
const installed = await r.list();
|
|
38
|
+
assert.ok(installed.find(p => p.name === 'noc-ops'));
|
|
39
|
+
await fs.rm(bundle, { recursive: true, force: true });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('install rejects a bundle missing manifest.yaml', async () => {
|
|
43
|
+
const r = new PersonaRegistry({ ottoHome: tmpHome });
|
|
44
|
+
const bad = await fs.mkdtemp(path.join(os.tmpdir(), 'bad-bundle-'));
|
|
45
|
+
await assert.rejects(() => r.installFromPath(bad), /manifest\.yaml/);
|
|
46
|
+
await fs.rm(bad, { recursive: true, force: true });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('activateInWorkspace writes <workspace>/.otto/persona.json', async () => {
|
|
50
|
+
const r = new PersonaRegistry({ ottoHome: tmpHome });
|
|
51
|
+
await r.ensureDefaultInstalled();
|
|
52
|
+
const ws = await fs.mkdtemp(path.join(os.tmpdir(), 'ws-'));
|
|
53
|
+
await r.activateInWorkspace(ws, 'default');
|
|
54
|
+
const raw = await fs.readFile(path.join(ws, '.otto', 'persona.json'), 'utf8');
|
|
55
|
+
const data = JSON.parse(raw);
|
|
56
|
+
assert.equal(data.active, 'default');
|
|
57
|
+
assert.equal(typeof data.activated_at, 'string');
|
|
58
|
+
assert.equal(data.memory_seed_applied, false);
|
|
59
|
+
await fs.rm(ws, { recursive: true, force: true });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('activeInWorkspace returns "default" when no persona.json exists', async () => {
|
|
63
|
+
const r = new PersonaRegistry({ ottoHome: tmpHome });
|
|
64
|
+
await r.ensureDefaultInstalled();
|
|
65
|
+
const ws = await fs.mkdtemp(path.join(os.tmpdir(), 'ws-'));
|
|
66
|
+
const active = await r.activeInWorkspace(ws);
|
|
67
|
+
assert.equal(active.name, 'default');
|
|
68
|
+
await fs.rm(ws, { recursive: true, force: true });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('uninstall refuses if persona is currently active in any tracked workspace', async () => {
|
|
72
|
+
const r = new PersonaRegistry({ ottoHome: tmpHome });
|
|
73
|
+
await r.ensureDefaultInstalled();
|
|
74
|
+
// Stage + install a second persona, then activate it
|
|
75
|
+
const bundle = await fs.mkdtemp(path.join(os.tmpdir(), 'fake-bundle-'));
|
|
76
|
+
await fs.mkdir(path.join(bundle, 'steering'), { recursive: true });
|
|
77
|
+
await fs.writeFile(path.join(bundle, 'manifest.yaml'),
|
|
78
|
+
'name: noc-ops\ndisplay_name: NOC\nversion: 1.0.0\ndescription: x\nauthor: x\notto_version_required: ">=2.0.0"\nsteering: [steering/identity.md]\nstatus_line: { label: NOC, color: "#FAD22D", icon: "🛡" }\n');
|
|
79
|
+
await fs.writeFile(path.join(bundle, 'steering', 'identity.md'), 'noc');
|
|
80
|
+
await r.installFromPath(bundle);
|
|
81
|
+
|
|
82
|
+
const ws = await fs.mkdtemp(path.join(os.tmpdir(), 'ws-'));
|
|
83
|
+
await r.activateInWorkspace(ws, 'noc-ops');
|
|
84
|
+
await assert.rejects(() => r.uninstall('noc-ops', { trackedWorkspaces: [ws] }), /active/);
|
|
85
|
+
|
|
86
|
+
await fs.rm(bundle, { recursive: true, force: true });
|
|
87
|
+
await fs.rm(ws, { recursive: true, force: true });
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// PersonaRegistry — install / list / activate / switch. Spec §2.5.
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { parsePersonaManifest, type PersonaManifest } from './manifest.js';
|
|
5
|
+
|
|
6
|
+
export interface RegistryOptions {
|
|
7
|
+
ottoHome: string; // typically ~/.otto
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ActiveRecord {
|
|
11
|
+
active: string;
|
|
12
|
+
activated_at: string;
|
|
13
|
+
memory_seed_applied: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function dirExists(p: string): Promise<boolean> {
|
|
17
|
+
try {
|
|
18
|
+
const s = await fs.stat(p);
|
|
19
|
+
return s.isDirectory();
|
|
20
|
+
} catch (err: unknown) {
|
|
21
|
+
if ((err as NodeJS.ErrnoException).code === 'ENOENT') return false;
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function copyDir(src: string, dst: string): Promise<void> {
|
|
27
|
+
await fs.mkdir(dst, { recursive: true });
|
|
28
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
29
|
+
for (const e of entries) {
|
|
30
|
+
const s = path.join(src, e.name);
|
|
31
|
+
const d = path.join(dst, e.name);
|
|
32
|
+
if (e.isDirectory()) {
|
|
33
|
+
await copyDir(s, d);
|
|
34
|
+
} else {
|
|
35
|
+
await fs.copyFile(s, d);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class PersonaRegistry {
|
|
41
|
+
#personasDir: string;
|
|
42
|
+
|
|
43
|
+
constructor(opts: RegistryOptions) {
|
|
44
|
+
this.#personasDir = path.join(opts.ottoHome, 'personas');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async ensureDefaultInstalled(): Promise<void> {
|
|
48
|
+
const target = path.join(this.#personasDir, 'default');
|
|
49
|
+
if (await dirExists(target)) return;
|
|
50
|
+
// Source: bundled defaults shipped with this package
|
|
51
|
+
const here = path.dirname(new URL(import.meta.url).pathname);
|
|
52
|
+
const source = path.join(here, 'defaults');
|
|
53
|
+
await copyDir(source, target);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async list(): Promise<PersonaManifest[]> {
|
|
57
|
+
if (!(await dirExists(this.#personasDir))) return [];
|
|
58
|
+
const entries = await fs.readdir(this.#personasDir, { withFileTypes: true });
|
|
59
|
+
const out: PersonaManifest[] = [];
|
|
60
|
+
for (const e of entries) {
|
|
61
|
+
if (!e.isDirectory()) continue;
|
|
62
|
+
const manifestPath = path.join(this.#personasDir, e.name, 'manifest.yaml');
|
|
63
|
+
try {
|
|
64
|
+
const raw = await fs.readFile(manifestPath, 'utf8');
|
|
65
|
+
out.push(parsePersonaManifest(raw));
|
|
66
|
+
} catch {
|
|
67
|
+
// skip malformed bundles
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async installFromPath(bundlePath: string): Promise<PersonaManifest> {
|
|
74
|
+
const manifestPath = path.join(bundlePath, 'manifest.yaml');
|
|
75
|
+
let raw: string;
|
|
76
|
+
try {
|
|
77
|
+
raw = await fs.readFile(manifestPath, 'utf8');
|
|
78
|
+
} catch (err: unknown) {
|
|
79
|
+
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
80
|
+
throw new Error(`bundle missing manifest.yaml at ${manifestPath}`);
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
const manifest = parsePersonaManifest(raw);
|
|
85
|
+
const target = path.join(this.#personasDir, manifest.name);
|
|
86
|
+
await fs.rm(target, { recursive: true, force: true });
|
|
87
|
+
await copyDir(bundlePath, target);
|
|
88
|
+
return manifest;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async activateInWorkspace(workspaceRoot: string, name: string): Promise<void> {
|
|
92
|
+
const persona = await this.get(name);
|
|
93
|
+
if (!persona) throw new Error(`persona not installed: ${name}`);
|
|
94
|
+
const wsOtto = path.join(workspaceRoot, '.otto');
|
|
95
|
+
await fs.mkdir(wsOtto, { recursive: true });
|
|
96
|
+
const record: ActiveRecord = {
|
|
97
|
+
active: name,
|
|
98
|
+
activated_at: new Date().toISOString(),
|
|
99
|
+
memory_seed_applied: false,
|
|
100
|
+
};
|
|
101
|
+
await fs.writeFile(path.join(wsOtto, 'persona.json'), JSON.stringify(record, null, 2));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async activeInWorkspace(workspaceRoot: string): Promise<PersonaManifest> {
|
|
105
|
+
const recordPath = path.join(workspaceRoot, '.otto', 'persona.json');
|
|
106
|
+
try {
|
|
107
|
+
const raw = await fs.readFile(recordPath, 'utf8');
|
|
108
|
+
const record = JSON.parse(raw) as ActiveRecord;
|
|
109
|
+
const m = await this.get(record.active);
|
|
110
|
+
if (m) return m;
|
|
111
|
+
} catch {
|
|
112
|
+
// fall through to default
|
|
113
|
+
}
|
|
114
|
+
const def = await this.get('default');
|
|
115
|
+
if (!def) throw new Error('default persona not installed');
|
|
116
|
+
return def;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async get(name: string): Promise<PersonaManifest | null> {
|
|
120
|
+
const manifestPath = path.join(this.#personasDir, name, 'manifest.yaml');
|
|
121
|
+
try {
|
|
122
|
+
const raw = await fs.readFile(manifestPath, 'utf8');
|
|
123
|
+
return parsePersonaManifest(raw);
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async uninstall(name: string, opts: { trackedWorkspaces: string[] }): Promise<void> {
|
|
130
|
+
if (name === 'default') {
|
|
131
|
+
throw new Error('cannot uninstall the built-in default persona');
|
|
132
|
+
}
|
|
133
|
+
for (const ws of opts.trackedWorkspaces) {
|
|
134
|
+
try {
|
|
135
|
+
const raw = await fs.readFile(path.join(ws, '.otto', 'persona.json'), 'utf8');
|
|
136
|
+
const record = JSON.parse(raw) as ActiveRecord;
|
|
137
|
+
if (record.active === name) {
|
|
138
|
+
throw new Error(`persona ${name} is active in workspace ${ws}; switch first`);
|
|
139
|
+
}
|
|
140
|
+
} catch (err: unknown) {
|
|
141
|
+
if ((err as NodeJS.ErrnoException).code === 'ENOENT') continue;
|
|
142
|
+
if (err instanceof Error && err.message.startsWith('persona ')) throw err;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
await fs.rm(path.join(this.#personasDir, name), { recursive: true, force: true });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "NodeNext",
|
|
4
|
+
"moduleResolution": "NodeNext",
|
|
5
|
+
"target": "ES2022",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"],
|
|
14
|
+
"exclude": ["src/**/*.test.ts"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare const CELLS_SCHEMA_VERSION = 1;
|
|
2
|
+
export interface CellEntry {
|
|
3
|
+
id: number;
|
|
4
|
+
parentId: number | null;
|
|
5
|
+
code: string;
|
|
6
|
+
ok: boolean;
|
|
7
|
+
value?: unknown;
|
|
8
|
+
error?: {
|
|
9
|
+
name: string;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
12
|
+
stdout: string;
|
|
13
|
+
ts: string;
|
|
14
|
+
}
|
|
15
|
+
export interface AppendInput {
|
|
16
|
+
code: string;
|
|
17
|
+
ok: boolean;
|
|
18
|
+
value?: unknown;
|
|
19
|
+
error?: {
|
|
20
|
+
name: string;
|
|
21
|
+
message: string;
|
|
22
|
+
};
|
|
23
|
+
stdout: string;
|
|
24
|
+
}
|
|
25
|
+
export declare class CellArchive {
|
|
26
|
+
#private;
|
|
27
|
+
private readonly dir;
|
|
28
|
+
private readonly now;
|
|
29
|
+
private readonly path;
|
|
30
|
+
private nextId;
|
|
31
|
+
constructor(dir: string, now?: () => number);
|
|
32
|
+
private scan;
|
|
33
|
+
private ensureHeader;
|
|
34
|
+
append(input: AppendInput): CellEntry;
|
|
35
|
+
get lastId(): number | null;
|
|
36
|
+
get leafId(): number | null;
|
|
37
|
+
setLeaf(id: number | null): void;
|
|
38
|
+
reset(): void;
|
|
39
|
+
}
|