@entelligentsia/forgecli 0.7.10 → 0.9.0
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/CHANGELOG.md +127 -0
- package/dist/CHANGELOG-forge-plugin.md +70 -0
- package/dist/CHANGELOG-pi.md +63 -0
- package/dist/bin/argv.d.ts +2 -2
- package/dist/bin/argv.js +27 -0
- package/dist/bin/argv.js.map +1 -1
- package/dist/bin/config.d.ts +69 -0
- package/dist/bin/config.js +315 -0
- package/dist/bin/config.js.map +1 -0
- package/dist/bin/doctor.d.ts +1 -0
- package/dist/bin/doctor.js +12 -0
- package/dist/bin/doctor.js.map +1 -1
- package/dist/bin/env-defaults.d.ts +1 -0
- package/dist/bin/env-defaults.js +13 -0
- package/dist/bin/env-defaults.js.map +1 -0
- package/dist/bin/forge.js +16 -0
- package/dist/bin/forge.js.map +1 -1
- package/dist/bin/update-cli.d.ts +9 -0
- package/dist/bin/update-cli.js +120 -0
- package/dist/bin/update-cli.js.map +1 -0
- package/dist/extensions/forgecli/config-command.d.ts +8 -0
- package/dist/extensions/forgecli/config-command.js +66 -0
- package/dist/extensions/forgecli/config-command.js.map +1 -0
- package/dist/extensions/forgecli/config-layer.d.ts +38 -0
- package/dist/extensions/forgecli/config-layer.js +68 -0
- package/dist/extensions/forgecli/config-layer.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/component.d.ts +35 -0
- package/dist/extensions/forgecli/config-tui/component.js +236 -0
- package/dist/extensions/forgecli/config-tui/component.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/handler.d.ts +40 -0
- package/dist/extensions/forgecli/config-tui/handler.js +240 -0
- package/dist/extensions/forgecli/config-tui/handler.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/index.d.ts +5 -0
- package/dist/extensions/forgecli/config-tui/index.js +5 -0
- package/dist/extensions/forgecli/config-tui/index.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/keys.d.ts +26 -0
- package/dist/extensions/forgecli/config-tui/keys.js +33 -0
- package/dist/extensions/forgecli/config-tui/keys.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/plugin-config-reader.d.ts +23 -0
- package/dist/extensions/forgecli/config-tui/plugin-config-reader.js +58 -0
- package/dist/extensions/forgecli/config-tui/plugin-config-reader.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/advanced-menu.d.ts +7 -0
- package/dist/extensions/forgecli/config-tui/screens/advanced-menu.js +83 -0
- package/dist/extensions/forgecli/config-tui/screens/advanced-menu.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/confirm-quit.d.ts +11 -0
- package/dist/extensions/forgecli/config-tui/screens/confirm-quit.js +54 -0
- package/dist/extensions/forgecli/config-tui/screens/confirm-quit.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/override-editor.d.ts +11 -0
- package/dist/extensions/forgecli/config-tui/screens/override-editor.js +233 -0
- package/dist/extensions/forgecli/config-tui/screens/override-editor.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.d.ts +7 -0
- package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js +91 -0
- package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/overrides-list.d.ts +7 -0
- package/dist/extensions/forgecli/config-tui/screens/overrides-list.js +71 -0
- package/dist/extensions/forgecli/config-tui/screens/overrides-list.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/persona-editor.d.ts +10 -0
- package/dist/extensions/forgecli/config-tui/screens/persona-editor.js +182 -0
- package/dist/extensions/forgecli/config-tui/screens/persona-editor.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/persona-picker.d.ts +7 -0
- package/dist/extensions/forgecli/config-tui/screens/persona-picker.js +76 -0
- package/dist/extensions/forgecli/config-tui/screens/persona-picker.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/personas-list.d.ts +7 -0
- package/dist/extensions/forgecli/config-tui/screens/personas-list.js +98 -0
- package/dist/extensions/forgecli/config-tui/screens/personas-list.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/shared.d.ts +29 -0
- package/dist/extensions/forgecli/config-tui/screens/shared.js +100 -0
- package/dist/extensions/forgecli/config-tui/screens/shared.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/show-resolved.d.ts +23 -0
- package/dist/extensions/forgecli/config-tui/screens/show-resolved.js +128 -0
- package/dist/extensions/forgecli/config-tui/screens/show-resolved.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/tier-menu.d.ts +7 -0
- package/dist/extensions/forgecli/config-tui/screens/tier-menu.js +135 -0
- package/dist/extensions/forgecli/config-tui/screens/tier-menu.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/tier-picker.d.ts +9 -0
- package/dist/extensions/forgecli/config-tui/screens/tier-picker.js +122 -0
- package/dist/extensions/forgecli/config-tui/screens/tier-picker.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens/types.d.ts +24 -0
- package/dist/extensions/forgecli/config-tui/screens/types.js +5 -0
- package/dist/extensions/forgecli/config-tui/screens/types.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/screens.d.ts +24 -0
- package/dist/extensions/forgecli/config-tui/screens.js +78 -0
- package/dist/extensions/forgecli/config-tui/screens.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/state/buffer.d.ts +11 -0
- package/dist/extensions/forgecli/config-tui/state/buffer.js +91 -0
- package/dist/extensions/forgecli/config-tui/state/buffer.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/state/constants.d.ts +4 -0
- package/dist/extensions/forgecli/config-tui/state/constants.js +14 -0
- package/dist/extensions/forgecli/config-tui/state/constants.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/state/index.d.ts +6 -0
- package/dist/extensions/forgecli/config-tui/state/index.js +9 -0
- package/dist/extensions/forgecli/config-tui/state/index.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/state/init.d.ts +2 -0
- package/dist/extensions/forgecli/config-tui/state/init.js +30 -0
- package/dist/extensions/forgecli/config-tui/state/init.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/state/model.d.ts +192 -0
- package/dist/extensions/forgecli/config-tui/state/model.js +4 -0
- package/dist/extensions/forgecli/config-tui/state/model.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/state/reducer.d.ts +2 -0
- package/dist/extensions/forgecli/config-tui/state/reducer.js +212 -0
- package/dist/extensions/forgecli/config-tui/state/reducer.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/state/selectors.d.ts +91 -0
- package/dist/extensions/forgecli/config-tui/state/selectors.js +231 -0
- package/dist/extensions/forgecli/config-tui/state/selectors.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/state.d.ts +6 -0
- package/dist/extensions/forgecli/config-tui/state.js +11 -0
- package/dist/extensions/forgecli/config-tui/state.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/theme.d.ts +37 -0
- package/dist/extensions/forgecli/config-tui/theme.js +88 -0
- package/dist/extensions/forgecli/config-tui/theme.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/tier-meta.d.ts +28 -0
- package/dist/extensions/forgecli/config-tui/tier-meta.js +69 -0
- package/dist/extensions/forgecli/config-tui/tier-meta.js.map +1 -0
- package/dist/extensions/forgecli/config-writer.d.ts +16 -0
- package/dist/extensions/forgecli/config-writer.js +63 -0
- package/dist/extensions/forgecli/config-writer.js.map +1 -0
- package/dist/extensions/forgecli/fix-bug.js +85 -1
- package/dist/extensions/forgecli/fix-bug.js.map +1 -1
- package/dist/extensions/forgecli/forge-cli-schema.json +54 -0
- package/dist/extensions/forgecli/forge-commands.js +3 -8
- package/dist/extensions/forgecli/forge-commands.js.map +1 -1
- package/dist/extensions/forgecli/forge-subagent.d.ts +13 -0
- package/dist/extensions/forgecli/forge-subagent.js +19 -0
- package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
- package/dist/extensions/forgecli/index.js +19 -3
- package/dist/extensions/forgecli/index.js.map +1 -1
- package/dist/extensions/forgecli/input-router.d.ts +33 -0
- package/dist/extensions/forgecli/input-router.js +133 -0
- package/dist/extensions/forgecli/input-router.js.map +1 -0
- package/dist/extensions/forgecli/model-resolver.d.ts +32 -0
- package/dist/extensions/forgecli/model-resolver.js +65 -0
- package/dist/extensions/forgecli/model-resolver.js.map +1 -0
- package/dist/extensions/forgecli/model-validator.d.ts +29 -0
- package/dist/extensions/forgecli/model-validator.js +107 -0
- package/dist/extensions/forgecli/model-validator.js.map +1 -0
- package/dist/extensions/forgecli/run-sprint.js +59 -0
- package/dist/extensions/forgecli/run-sprint.js.map +1 -1
- package/dist/extensions/forgecli/run-task.js +93 -1
- package/dist/extensions/forgecli/run-task.js.map +1 -1
- package/dist/extensions/forgecli/thread-switcher.js +5 -2
- package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
- package/dist/extensions/forgecli/update-check.js +1 -1
- package/dist/extensions/forgecli/update-check.js.map +1 -1
- package/dist/extensions/forgecli/whats-new-widget.d.ts +5 -5
- package/dist/extensions/forgecli/whats-new-widget.js +16 -13
- package/dist/extensions/forgecli/whats-new-widget.js.map +1 -1
- package/dist/extensions/forgecli/whats-new.js +6 -5
- package/dist/extensions/forgecli/whats-new.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +27 -98
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +62 -132
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +25 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +17 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +8 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +17 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +8 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +63 -0
- package/node_modules/@earendil-works/pi-coding-agent/README.md +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli.js +6 -10
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +12 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +30 -15
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts +3 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +23 -13
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts +4 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +58 -38
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js +0 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js +3 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js +7 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +6 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +3 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.d.ts +7 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.js +60 -7
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/docs/packages.md +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +1 -3
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/package.json +6 -6
- package/node_modules/@earendil-works/pi-tui/package.json +2 -2
- package/node_modules/@protobufjs/fetch/CHANGELOG.md +8 -0
- package/node_modules/@protobufjs/fetch/index.d.ts +7 -7
- package/node_modules/@protobufjs/fetch/index.js +4 -7
- package/node_modules/@protobufjs/fetch/package.json +7 -5
- package/node_modules/@protobufjs/fetch/tests/data/file.txt +1 -0
- package/node_modules/@protobufjs/fetch/tests/index.js +150 -8
- package/node_modules/@protobufjs/fetch/util/fs.js +11 -0
- package/node_modules/@protobufjs/inquire/CHANGELOG.md +8 -0
- package/node_modules/@protobufjs/inquire/index.d.ts +1 -0
- package/node_modules/@protobufjs/inquire/index.js +1 -0
- package/node_modules/@protobufjs/inquire/package.json +1 -1
- package/node_modules/protobufjs/dist/light/protobuf.js +187 -153
- package/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
- package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
- package/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
- package/node_modules/protobufjs/dist/minimal/protobuf.js +14 -5
- package/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
- package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
- package/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
- package/node_modules/protobufjs/dist/protobuf.js +207 -173
- package/node_modules/protobufjs/dist/protobuf.js.map +1 -1
- package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
- package/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
- package/node_modules/protobufjs/package.json +6 -3
- package/node_modules/protobufjs/src/util/fs.js +11 -0
- package/node_modules/protobufjs/src/util/minimal.js +10 -2
- package/node_modules/protobufjs/src/util.js +1 -1
- package/node_modules/undici/README.md +14 -5
- package/node_modules/undici/docs/docs/api/Client.md +4 -2
- package/node_modules/undici/docs/docs/api/Dispatcher.md +62 -27
- package/node_modules/undici/docs/docs/api/GlobalInstallation.md +7 -5
- package/node_modules/undici/docs/docs/api/H2CClient.md +1 -1
- package/node_modules/undici/docs/docs/api/RedirectHandler.md +14 -9
- package/node_modules/undici/docs/docs/api/RetryAgent.md +0 -1
- package/node_modules/undici/docs/docs/api/RetryHandler.md +12 -14
- package/node_modules/undici/docs/docs/api/SnapshotAgent.md +23 -0
- package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +231 -0
- package/node_modules/undici/index.js +4 -2
- package/node_modules/undici/lib/api/api-connect.js +13 -11
- package/node_modules/undici/lib/api/api-pipeline.js +26 -13
- package/node_modules/undici/lib/api/api-request.js +45 -21
- package/node_modules/undici/lib/api/api-stream.js +81 -20
- package/node_modules/undici/lib/api/api-upgrade.js +21 -11
- package/node_modules/undici/lib/api/readable.js +3 -2
- package/node_modules/undici/lib/cache/memory-cache-store.js +1 -1
- package/node_modules/undici/lib/cache/sqlite-cache-store.js +6 -4
- package/node_modules/undici/lib/core/connect.js +17 -1
- package/node_modules/undici/lib/core/constants.js +1 -24
- package/node_modules/undici/lib/core/errors.js +2 -2
- package/node_modules/undici/lib/core/request.js +115 -18
- package/node_modules/undici/lib/core/socks5-client.js +24 -9
- package/node_modules/undici/lib/core/socks5-utils.js +32 -23
- package/node_modules/undici/lib/core/symbols.js +1 -0
- package/node_modules/undici/lib/core/util.js +70 -43
- package/node_modules/undici/lib/dispatcher/agent.js +47 -33
- package/node_modules/undici/lib/dispatcher/balanced-pool.js +21 -26
- package/node_modules/undici/lib/dispatcher/client-h1.js +98 -39
- package/node_modules/undici/lib/dispatcher/client-h2.js +603 -272
- package/node_modules/undici/lib/dispatcher/client.js +12 -5
- package/node_modules/undici/lib/dispatcher/dispatcher-base.js +24 -5
- package/node_modules/undici/lib/dispatcher/dispatcher.js +0 -4
- package/node_modules/undici/lib/dispatcher/dispatcher1-wrapper.js +107 -0
- package/node_modules/undici/lib/dispatcher/h2c-client.js +5 -5
- package/node_modules/undici/lib/dispatcher/pool-base.js +28 -10
- package/node_modules/undici/lib/dispatcher/pool.js +31 -6
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +38 -13
- package/node_modules/undici/lib/dispatcher/round-robin-pool.js +31 -9
- package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +95 -80
- package/node_modules/undici/lib/global.js +13 -1
- package/node_modules/undici/lib/handler/cache-handler.js +16 -8
- package/node_modules/undici/lib/handler/decorator-handler.js +1 -2
- package/node_modules/undici/lib/handler/redirect-handler.js +5 -51
- package/node_modules/undici/lib/handler/retry-handler.js +15 -2
- package/node_modules/undici/lib/interceptor/cache.js +30 -17
- package/node_modules/undici/lib/interceptor/decompress.js +28 -2
- package/node_modules/undici/lib/interceptor/dns.js +1 -1
- package/node_modules/undici/lib/interceptor/redirect.js +3 -3
- package/node_modules/undici/lib/llhttp/llhttp-wasm.js +1 -1
- package/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +1 -1
- package/node_modules/undici/lib/mock/mock-agent.js +8 -8
- package/node_modules/undici/lib/mock/mock-call-history.js +15 -15
- package/node_modules/undici/lib/mock/mock-utils.js +37 -22
- package/node_modules/undici/lib/mock/snapshot-agent.js +16 -6
- package/node_modules/undici/lib/mock/snapshot-recorder.js +38 -3
- package/node_modules/undici/lib/util/cache.js +8 -7
- package/node_modules/undici/lib/util/runtime-features.js +3 -34
- package/node_modules/undici/lib/web/cache/cache.js +6 -8
- package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +245 -150
- package/node_modules/undici/lib/web/fetch/body.js +3 -9
- package/node_modules/undici/lib/web/fetch/formdata-parser.js +17 -6
- package/node_modules/undici/lib/web/fetch/formdata.js +21 -2
- package/node_modules/undici/lib/web/fetch/index.js +214 -221
- package/node_modules/undici/lib/web/webidl/index.js +7 -9
- package/node_modules/undici/lib/web/websocket/frame.js +1 -7
- package/node_modules/undici/lib/web/websocket/permessage-deflate.js +13 -31
- package/node_modules/undici/lib/web/websocket/receiver.js +62 -22
- package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +11 -17
- package/node_modules/undici/lib/web/websocket/websocket.js +6 -1
- package/node_modules/undici/package.json +9 -9
- package/node_modules/undici/types/agent.d.ts +0 -2
- package/node_modules/undici/types/client.d.ts +25 -19
- package/node_modules/undici/types/dispatcher.d.ts +7 -27
- package/node_modules/undici/types/dispatcher1-wrapper.d.ts +7 -0
- package/node_modules/undici/types/formdata.d.ts +0 -6
- package/node_modules/undici/types/h2c-client.d.ts +6 -6
- package/node_modules/undici/types/header.d.ts +5 -0
- package/node_modules/undici/types/index.d.ts +3 -1
- package/node_modules/undici/types/interceptors.d.ts +1 -1
- package/node_modules/undici/types/pool.d.ts +0 -2
- package/node_modules/undici/types/proxy-agent.d.ts +2 -2
- package/node_modules/undici/types/round-robin-pool.d.ts +0 -2
- package/node_modules/undici/types/snapshot-agent.d.ts +4 -0
- package/node_modules/undici/types/socks5-proxy-agent.d.ts +2 -2
- package/node_modules/undici/types/webidl.d.ts +0 -1
- package/package.json +16 -9
- package/dist/extensions/forgecli/review-command.d.ts +0 -2
- package/dist/extensions/forgecli/review-command.js +0 -184
- package/dist/extensions/forgecli/review-command.js.map +0 -1
- package/dist/forge-payload/.tools/banners.cjs +0 -435
- package/dist/forge-payload/.tools/build-context-pack.cjs +0 -290
- package/dist/forge-payload/.tools/build-init-context.cjs +0 -322
- package/dist/forge-payload/.tools/build-overlay.cjs +0 -326
- package/dist/forge-payload/.tools/build-persona-pack.cjs +0 -226
- package/dist/forge-payload/.tools/collate.cjs +0 -1041
- package/dist/forge-payload/.tools/generation-manifest.cjs +0 -311
- package/dist/forge-payload/.tools/lib/forge-root.cjs +0 -59
- package/dist/forge-payload/.tools/lib/paths.cjs +0 -29
- package/dist/forge-payload/.tools/lib/pricing.cjs +0 -165
- package/dist/forge-payload/.tools/lib/project-root.cjs +0 -32
- package/dist/forge-payload/.tools/lib/result.js +0 -40
- package/dist/forge-payload/.tools/lib/store-facade.cjs +0 -162
- package/dist/forge-payload/.tools/lib/store-nlp.cjs +0 -250
- package/dist/forge-payload/.tools/lib/store-query-exec.cjs +0 -272
- package/dist/forge-payload/.tools/lib/validate.js +0 -141
- package/dist/forge-payload/.tools/manage-config.cjs +0 -340
- package/dist/forge-payload/.tools/manage-versions.cjs +0 -365
- package/dist/forge-payload/.tools/package.json +0 -3
- package/dist/forge-payload/.tools/parse-gates.cjs +0 -151
- package/dist/forge-payload/.tools/parse-verdict.cjs +0 -67
- package/dist/forge-payload/.tools/preflight-gate.cjs +0 -350
- package/dist/forge-payload/.tools/prompts/sprint-plan-prompt.md +0 -70
- package/dist/forge-payload/.tools/schemas/task-list.schema.json +0 -53
- package/dist/forge-payload/.tools/seed-store.cjs +0 -237
- package/dist/forge-payload/.tools/store-cli.cjs +0 -1226
- package/dist/forge-payload/.tools/store-query.cjs +0 -319
- package/dist/forge-payload/.tools/store.cjs +0 -315
- package/dist/forge-payload/.tools/substitute-placeholders.cjs +0 -625
- package/dist/forge-payload/.tools/validate-store.cjs +0 -593
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json +0 -24
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package-lock.json +0 -92
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package-lock.json +0 -31
- package/node_modules/undici/lib/handler/unwrap-handler.js +0 -100
- package/node_modules/undici/lib/handler/wrap-handler.js +0 -105
- package/node_modules/undici/lib/llhttp/.gitkeep +0 -0
- package/node_modules/undici/lib/util/promise.js +0 -28
- package/skills/refresh-kb-links/SKILL.md +0 -217
- package/skills/store-custodian/SKILL.md +0 -163
- package/skills/store-query-grammar/SKILL.md +0 -145
- package/skills/store-query-nlp/SKILL.md +0 -110
|
@@ -1,1226 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
// Forge tool: store-cli
|
|
5
|
-
// Deterministic store custodian CLI — wraps store.cjs facade.
|
|
6
|
-
// Enforces schema validation on write and status transition rules.
|
|
7
|
-
// Usage: node store-cli.cjs <command> <args>
|
|
8
|
-
|
|
9
|
-
let _store;
|
|
10
|
-
function _getStore() { return _store || (_store = require('./store.cjs')); }
|
|
11
|
-
let _projectRoot;
|
|
12
|
-
function _getProjectRoot() { return _projectRoot || (_projectRoot = require('./lib/project-root.cjs').findProjectRoot()); }
|
|
13
|
-
|
|
14
|
-
const _path = require('path');
|
|
15
|
-
|
|
16
|
-
// Path traversal guard — resolves the events directory and validates that
|
|
17
|
-
// sprintOrBugId doesn't escape it. Same pattern as store.cjs purgeEvents.
|
|
18
|
-
function _resolveEventsDir(sprintOrBugId) {
|
|
19
|
-
const root = _getProjectRoot();
|
|
20
|
-
const eventsBase = root
|
|
21
|
-
? _path.resolve(root, '.forge', 'store', 'events')
|
|
22
|
-
: _path.resolve('.forge', 'store', 'events');
|
|
23
|
-
const resolvedDir = _path.resolve(eventsBase, sprintOrBugId);
|
|
24
|
-
if (!resolvedDir.startsWith(eventsBase + _path.sep) && resolvedDir !== eventsBase) {
|
|
25
|
-
console.error(`Path traversal blocked: '${sprintOrBugId}' resolves outside events directory`);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
return resolvedDir;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
let _schemas;
|
|
32
|
-
function _getSchemas() {
|
|
33
|
-
if (_schemas) return _schemas;
|
|
34
|
-
const fs = require('fs');
|
|
35
|
-
const path = require('path');
|
|
36
|
-
|
|
37
|
-
const ENTITY_TYPES = ['sprint', 'task', 'bug', 'event', 'feature'];
|
|
38
|
-
|
|
39
|
-
const MINIMAL_REQUIRED = {
|
|
40
|
-
sprint: ['sprintId', 'title', 'status', 'taskIds', 'createdAt'],
|
|
41
|
-
task: ['taskId', 'sprintId', 'title', 'status', 'path'],
|
|
42
|
-
bug: ['bugId', 'title', 'severity', 'status', 'path', 'reportedAt'],
|
|
43
|
-
event: ['eventId', 'taskId', 'sprintId', 'role', 'action', 'phase', 'iteration', 'startTimestamp', 'endTimestamp', 'durationMinutes', 'model'],
|
|
44
|
-
feature: ['id', 'title', 'status', 'created_at']
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const schemas = {};
|
|
48
|
-
const projectDir = path.join('.forge', 'schemas');
|
|
49
|
-
const inTreeDir = path.join('forge', 'schemas');
|
|
50
|
-
const pluginDir = path.join(__dirname, '..', 'schemas');
|
|
51
|
-
|
|
52
|
-
const AUX_SCHEMAS = {
|
|
53
|
-
'event-sidecar': 'event-sidecar.schema.json',
|
|
54
|
-
'progress-entry': 'progress-entry.schema.json',
|
|
55
|
-
'collation-state': 'collation-state.schema.json',
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const allTypes = [...ENTITY_TYPES, ...Object.keys(AUX_SCHEMAS)];
|
|
59
|
-
for (const type of allTypes) {
|
|
60
|
-
const schemaFile = AUX_SCHEMAS[type] || `${type}.schema.json`;
|
|
61
|
-
let schema = null;
|
|
62
|
-
|
|
63
|
-
// 1. Try project-installed schemas first
|
|
64
|
-
const projectPath = path.join(projectDir, schemaFile);
|
|
65
|
-
try {
|
|
66
|
-
if (fs.existsSync(projectPath)) {
|
|
67
|
-
schema = JSON.parse(fs.readFileSync(projectPath, 'utf8'));
|
|
68
|
-
}
|
|
69
|
-
} catch (_) {}
|
|
70
|
-
|
|
71
|
-
// 2. Fall back to in-tree source schemas (development mode)
|
|
72
|
-
if (!schema) {
|
|
73
|
-
const inTreePath = path.join(inTreeDir, schemaFile);
|
|
74
|
-
try {
|
|
75
|
-
if (fs.existsSync(inTreePath)) {
|
|
76
|
-
schema = JSON.parse(fs.readFileSync(inTreePath, 'utf8'));
|
|
77
|
-
}
|
|
78
|
-
} catch (_) {}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// 3. Fall back to plugin-installed schemas (production mode)
|
|
82
|
-
// store-cli.cjs lives at $FORGE_ROOT/tools/, so __dirname/../schemas/
|
|
83
|
-
// resolves to $FORGE_ROOT/schemas/ — always correct for installed plugins.
|
|
84
|
-
if (!schema) {
|
|
85
|
-
const pluginPath = path.join(pluginDir, schemaFile);
|
|
86
|
-
try {
|
|
87
|
-
if (fs.existsSync(pluginPath)) {
|
|
88
|
-
schema = JSON.parse(fs.readFileSync(pluginPath, 'utf8'));
|
|
89
|
-
}
|
|
90
|
-
} catch (_) {}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (schema) {
|
|
94
|
-
schemas[type] = schema;
|
|
95
|
-
} else {
|
|
96
|
-
console.error(`WARN: schema file ${schemaFile} not found, using minimal fallback`);
|
|
97
|
-
schemas[type] = { type: 'object', required: MINIMAL_REQUIRED[type] || [], properties: {} };
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
_schemas = schemas;
|
|
102
|
-
return _schemas;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
// Schema loading — same resolution as validate-store.cjs
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
|
|
109
|
-
const ENTITY_TYPES = ['sprint', 'task', 'bug', 'event', 'feature'];
|
|
110
|
-
|
|
111
|
-
const MINIMAL_REQUIRED = {
|
|
112
|
-
sprint: ['sprintId', 'title', 'status', 'taskIds', 'createdAt'],
|
|
113
|
-
task: ['taskId', 'sprintId', 'title', 'status', 'path'],
|
|
114
|
-
bug: ['bugId', 'title', 'severity', 'status', 'path', 'reportedAt'],
|
|
115
|
-
event: ['eventId', 'taskId', 'sprintId', 'role', 'action', 'phase', 'iteration', 'startTimestamp', 'endTimestamp', 'durationMinutes', 'model'],
|
|
116
|
-
feature: ['id', 'title', 'status', 'created_at']
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// Shared validator + nullable-field set live in ./lib/validate.js so the
|
|
120
|
-
// write-boundary hook can reuse the exact same validation logic as tool writes.
|
|
121
|
-
const { validateRecord, NULLABLE_FIELDS } = require('./lib/validate.js');
|
|
122
|
-
|
|
123
|
-
// Valid phase keys for summaries (dot-delimited → underscore in JSON key)
|
|
124
|
-
const VALID_SUMMARY_PHASES = new Set(['plan', 'review_plan', 'implementation', 'code_review', 'validation']);
|
|
125
|
-
|
|
126
|
-
// Schema for a single phase summary (used by set-summary / set-bug-summary)
|
|
127
|
-
const PHASE_SUMMARY_SCHEMA = {
|
|
128
|
-
type: 'object',
|
|
129
|
-
required: ['objective', 'written_at'],
|
|
130
|
-
properties: {
|
|
131
|
-
objective: { type: 'string', maxLength: 280 },
|
|
132
|
-
key_changes: { type: 'array', items: { type: 'string', maxLength: 200 }, maxItems: 12 },
|
|
133
|
-
findings: { type: 'array', items: { type: 'string', maxLength: 200 }, maxItems: 12 },
|
|
134
|
-
verdict: { type: 'string', enum: ['approved', 'revision', 'n/a'] },
|
|
135
|
-
written_at: { type: 'string' },
|
|
136
|
-
artifact_ref:{ type: 'string' }
|
|
137
|
-
},
|
|
138
|
-
additionalProperties: false
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
// validateRecord imported from ./lib/validate.js above.
|
|
142
|
-
|
|
143
|
-
// ---------------------------------------------------------------------------
|
|
144
|
-
// Transition tables
|
|
145
|
-
// ---------------------------------------------------------------------------
|
|
146
|
-
|
|
147
|
-
const TASK_TRANSITIONS = {
|
|
148
|
-
draft: ['planned', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
|
|
149
|
-
planned: ['plan-approved', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
|
|
150
|
-
'plan-approved': ['implementing', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
|
|
151
|
-
implementing: ['implemented', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
|
|
152
|
-
implemented: ['review-approved', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
|
|
153
|
-
'review-approved': ['approved', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
|
|
154
|
-
approved: ['committed', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
|
|
155
|
-
// Terminal: committed, abandoned — no transitions out
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const SPRINT_TRANSITIONS = {
|
|
159
|
-
planning: ['active', 'blocked', 'partially-completed', 'abandoned'],
|
|
160
|
-
active: ['completed', 'blocked', 'partially-completed', 'abandoned'],
|
|
161
|
-
completed: ['retrospective-done', 'blocked', 'partially-completed', 'abandoned'],
|
|
162
|
-
// Terminal: retrospective-done, abandoned
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const BUG_TRANSITIONS = {
|
|
166
|
-
reported: ['triaged'],
|
|
167
|
-
triaged: ['in-progress'],
|
|
168
|
-
'in-progress': ['fixed'],
|
|
169
|
-
fixed: ['verified'],
|
|
170
|
-
// Terminal: verified
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const FEATURE_TRANSITIONS = {
|
|
174
|
-
draft: ['active'],
|
|
175
|
-
active: ['shipped', 'retired'],
|
|
176
|
-
// Terminal: shipped, retired
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const TRANSITION_MAP = {
|
|
180
|
-
task: TASK_TRANSITIONS,
|
|
181
|
-
sprint: SPRINT_TRANSITIONS,
|
|
182
|
-
bug: BUG_TRANSITIONS,
|
|
183
|
-
feature: FEATURE_TRANSITIONS
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const TERMINAL_STATES = new Set([
|
|
187
|
-
'committed', 'abandoned', // task
|
|
188
|
-
'retrospective-done', // sprint
|
|
189
|
-
'verified', // bug
|
|
190
|
-
'shipped', 'retired' // feature
|
|
191
|
-
]);
|
|
192
|
-
|
|
193
|
-
// Failed states that may be entered from any non-terminal state
|
|
194
|
-
const FAILED_STATES = new Set([
|
|
195
|
-
'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned', // task
|
|
196
|
-
'blocked', 'partially-completed' // sprint
|
|
197
|
-
]);
|
|
198
|
-
|
|
199
|
-
function isLegalTransition(entityType, field, currentValue, newValue) {
|
|
200
|
-
if (currentValue === newValue) return true; // no-op
|
|
201
|
-
|
|
202
|
-
const table = TRANSITION_MAP[entityType];
|
|
203
|
-
if (!table) return true; // no transition rules for this entity type
|
|
204
|
-
|
|
205
|
-
// Terminal states cannot transition out
|
|
206
|
-
if (TERMINAL_STATES.has(currentValue)) return false;
|
|
207
|
-
|
|
208
|
-
// Failed states may be entered from any non-terminal state
|
|
209
|
-
if (FAILED_STATES.has(newValue)) return true;
|
|
210
|
-
|
|
211
|
-
// Check the explicit transition table
|
|
212
|
-
const allowed = table[currentValue];
|
|
213
|
-
if (!allowed) return false; // current state not in table (unknown state)
|
|
214
|
-
|
|
215
|
-
return allowed.includes(newValue);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// ---------------------------------------------------------------------------
|
|
219
|
-
// Bug timestamp normalization helpers
|
|
220
|
-
// ---------------------------------------------------------------------------
|
|
221
|
-
|
|
222
|
-
// Returns true if the string is a date-only value (YYYY-MM-DD without time).
|
|
223
|
-
// These are rejected by the date-time format validator but agents commonly
|
|
224
|
-
// supply them for reportedAt/resolvedAt.
|
|
225
|
-
function _isDateOnly(ts) {
|
|
226
|
-
return typeof ts === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(ts);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Convert a date-only string (YYYY-MM-DD) into a full ISO datetime by
|
|
230
|
-
// appending the current time-of-day in UTC. The date portion is preserved
|
|
231
|
-
// from the input; only the time component is auto-populated.
|
|
232
|
-
function _dateOnlyToISO(dateStr) {
|
|
233
|
-
const now = new Date();
|
|
234
|
-
const timePart = now.toISOString().slice(10); // e.g. "T14:32:07.123Z"
|
|
235
|
-
return dateStr + timePart;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Normalize bug datetime fields before writing. When agents supply date-only
|
|
239
|
-
// values (YYYY-MM-DD) for reportedAt or resolvedAt, auto-populates the time
|
|
240
|
-
// component from the current time-of-day so the value passes date-time format
|
|
241
|
-
// validation. Full ISO datetimes are left untouched.
|
|
242
|
-
function _normalizeBugTimestamps(data) {
|
|
243
|
-
if (_isDateOnly(data.reportedAt)) data.reportedAt = _dateOnlyToISO(data.reportedAt);
|
|
244
|
-
if (_isDateOnly(data.resolvedAt)) data.resolvedAt = _dateOnlyToISO(data.resolvedAt);
|
|
245
|
-
return data;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// ---------------------------------------------------------------------------
|
|
249
|
-
// Model discovery
|
|
250
|
-
// ---------------------------------------------------------------------------
|
|
251
|
-
|
|
252
|
-
// Deterministic model discovery — probes environment variables in priority
|
|
253
|
-
// order to resolve the actual runtime model identifier. Returns "unknown"
|
|
254
|
-
// when no signal is available instead of guessing an Anthropic model name.
|
|
255
|
-
function discoverModel() {
|
|
256
|
-
const candidates = [
|
|
257
|
-
process.env.CLAUDE_CODE_SUBAGENT_MODEL,
|
|
258
|
-
process.env.ANTHROPIC_MODEL,
|
|
259
|
-
process.env.CLAUDE_MODEL,
|
|
260
|
-
];
|
|
261
|
-
for (const val of candidates) {
|
|
262
|
-
if (val && val.trim()) return val.trim();
|
|
263
|
-
}
|
|
264
|
-
return 'unknown';
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
module.exports = { isLegalTransition, validateRecord, TRANSITION_MAP, TERMINAL_STATES, FAILED_STATES, ENTITY_TYPES, MINIMAL_REQUIRED, NULLABLE_FIELDS, VALID_SUMMARY_PHASES, PHASE_SUMMARY_SCHEMA, _isDateOnly, _dateOnlyToISO, _normalizeBugTimestamps, discoverModel };
|
|
268
|
-
|
|
269
|
-
// ---------------------------------------------------------------------------
|
|
270
|
-
// CLI
|
|
271
|
-
// ---------------------------------------------------------------------------
|
|
272
|
-
if (require.main === module) {
|
|
273
|
-
|
|
274
|
-
process.on('uncaughtException', (error) => {
|
|
275
|
-
console.error('Fatal store-cli error:', error);
|
|
276
|
-
process.exit(1);
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
try {
|
|
280
|
-
const fs = require('fs');
|
|
281
|
-
const path = require('path');
|
|
282
|
-
const store = _getStore();
|
|
283
|
-
const schemas = _getSchemas();
|
|
284
|
-
|
|
285
|
-
const DRY_RUN = process.argv.includes('--dry-run');
|
|
286
|
-
const VERBOSE = process.argv.includes('--verbose');
|
|
287
|
-
|
|
288
|
-
// ---------------------------------------------------------------------------
|
|
289
|
-
// Entity ID field mapping
|
|
290
|
-
// ---------------------------------------------------------------------------
|
|
291
|
-
|
|
292
|
-
const ENTITY_ID_FIELD = {
|
|
293
|
-
sprint: 'sprintId',
|
|
294
|
-
task: 'taskId',
|
|
295
|
-
bug: 'bugId',
|
|
296
|
-
event: 'eventId',
|
|
297
|
-
feature: 'id'
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
// ---------------------------------------------------------------------------
|
|
301
|
-
// Store accessor mapping
|
|
302
|
-
// ---------------------------------------------------------------------------
|
|
303
|
-
|
|
304
|
-
function getEntity(entity, id) {
|
|
305
|
-
switch (entity) {
|
|
306
|
-
case 'sprint': return store.getSprint(id);
|
|
307
|
-
case 'task': return store.getTask(id);
|
|
308
|
-
case 'bug': return store.getBug(id);
|
|
309
|
-
case 'event': return store.getEvent(id, null); // needs sprintId separately
|
|
310
|
-
case 'feature': return store.getFeature(id);
|
|
311
|
-
default: return null;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function writeEntity(entity, data) {
|
|
316
|
-
switch (entity) {
|
|
317
|
-
case 'sprint': return store.writeSprint(data);
|
|
318
|
-
case 'task': return store.writeTask(data);
|
|
319
|
-
case 'bug': return store.writeBug(data);
|
|
320
|
-
case 'event': return store.writeEvent(data.sprintId, data);
|
|
321
|
-
case 'feature': return store.writeFeature(data);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function deleteEntity(entity, id) {
|
|
326
|
-
switch (entity) {
|
|
327
|
-
case 'sprint': return store.deleteSprint(id);
|
|
328
|
-
case 'task': return store.deleteTask(id);
|
|
329
|
-
case 'bug': return store.deleteBug(id);
|
|
330
|
-
case 'feature': return store.deleteFeature(id);
|
|
331
|
-
default:
|
|
332
|
-
console.error(`Unknown entity type: ${entity}`);
|
|
333
|
-
process.exit(1);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
function listEntities(entity, filter) {
|
|
338
|
-
switch (entity) {
|
|
339
|
-
case 'sprint': return store.listSprints(filter);
|
|
340
|
-
case 'task': return store.listTasks(filter);
|
|
341
|
-
case 'bug': return store.listBugs(filter);
|
|
342
|
-
case 'feature': return store.listFeatures(filter);
|
|
343
|
-
default:
|
|
344
|
-
console.error(`Unknown entity type: ${entity}`);
|
|
345
|
-
process.exit(1);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// ---------------------------------------------------------------------------
|
|
350
|
-
// Sidecar handling
|
|
351
|
-
// ---------------------------------------------------------------------------
|
|
352
|
-
|
|
353
|
-
// Canonical event schema token fields
|
|
354
|
-
const CANONICAL_TOKEN_FIELDS = [
|
|
355
|
-
'inputTokens', 'outputTokens', 'cacheReadTokens', 'cacheWriteTokens',
|
|
356
|
-
'estimatedCostUSD', 'model', 'durationMinutes', 'startTimestamp', 'endTimestamp',
|
|
357
|
-
'tokenSource'
|
|
358
|
-
];
|
|
359
|
-
|
|
360
|
-
// Accepted sidecar fields (includes aliases)
|
|
361
|
-
const SIDECAR_ACCEPTED_FIELDS = new Set([
|
|
362
|
-
'inputTokens', 'outputTokens', 'cacheReadTokens', 'cacheWriteTokens',
|
|
363
|
-
'estimatedCostUSD', 'model', 'cost', 'durationMinutes',
|
|
364
|
-
'startTimestamp', 'endTimestamp', 'cacheCreationTokens',
|
|
365
|
-
'tokenSource'
|
|
366
|
-
]);
|
|
367
|
-
|
|
368
|
-
// Alias mapping for sidecar → canonical event
|
|
369
|
-
const SIDECAR_ALIASES = {
|
|
370
|
-
'cacheCreationTokens': 'cacheWriteTokens',
|
|
371
|
-
'cost': 'estimatedCostUSD'
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
function resolveSidecarDir(sprintId) {
|
|
375
|
-
const storeRoot = store.impl.storeRoot;
|
|
376
|
-
return path.join(storeRoot, 'events', sprintId);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function sidecarPath(sprintId, eventId) {
|
|
380
|
-
return path.join(resolveSidecarDir(sprintId), `_${eventId}_usage.json`);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function canonicalEventPath(sprintId, eventId) {
|
|
384
|
-
return path.join(resolveSidecarDir(sprintId), `${eventId}.json`);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// ---------------------------------------------------------------------------
|
|
388
|
-
// CLI argument parsing
|
|
389
|
-
// ---------------------------------------------------------------------------
|
|
390
|
-
|
|
391
|
-
const args = process.argv.slice(2);
|
|
392
|
-
|
|
393
|
-
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
394
|
-
console.log(`Forge Store Custodian CLI
|
|
395
|
-
|
|
396
|
-
Usage: node store-cli.cjs <command> <args> [--dry-run]
|
|
397
|
-
|
|
398
|
-
Commands:
|
|
399
|
-
write <entity> '<json>' Write a full entity record
|
|
400
|
-
read <entity> <id> [--json] Read an entity record
|
|
401
|
-
list <entity> [key=value ...] List entities with optional filter
|
|
402
|
-
delete <entity> <id> Delete an entity record
|
|
403
|
-
update-status <entity> <id> <field> <value> [--force]
|
|
404
|
-
Update status/enum field with transition check
|
|
405
|
-
emit <sprintId> '<json>' [--sidecar] Write an event (or sidecar)
|
|
406
|
-
merge-sidecar <sprintId> <eventId> Merge sidecar into canonical event
|
|
407
|
-
record-usage <sprintId> <eventId> [flags] Write a token-usage sidecar
|
|
408
|
-
purge-events <sprintId> Delete all events for a sprint
|
|
409
|
-
progress <sprintOrBugId> <agentName> <bannerKey> <status> [detail]
|
|
410
|
-
Append a progress entry to the log
|
|
411
|
-
progress-clear <sprintOrBugId> Clear (truncate) the progress log
|
|
412
|
-
write-collation-state '<json>' Write COLLATION_STATE.json
|
|
413
|
-
validate <entity> '<json>' Validate against schema without writing
|
|
414
|
-
set-summary <taskId> <phase> <jsonFile> Set a phase summary on a task record
|
|
415
|
-
set-bug-summary <bugId> <phase> <jsonFile> Set a phase summary on a bug record
|
|
416
|
-
describe <entity> Print the JSON Schema for an entity
|
|
417
|
-
template <entity> Print a canonical sample record (required fields populated)
|
|
418
|
-
|
|
419
|
-
Entities: sprint, task, bug, event, feature
|
|
420
|
-
Phases (summaries): plan, review_plan, implementation, code_review, validation
|
|
421
|
-
|
|
422
|
-
Flags:
|
|
423
|
-
--dry-run Validate and preview without writing (applies to all write commands)
|
|
424
|
-
--force Bypass transition check on update-status (emits warning)
|
|
425
|
-
--json Output raw JSON on read (no pretty-print)
|
|
426
|
-
--sidecar Write as sidecar file on emit (ephemeral _-prefixed)
|
|
427
|
-
|
|
428
|
-
Exit codes: 0 on success, 1 on failure`);
|
|
429
|
-
process.exit(0);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const command = args[0];
|
|
433
|
-
|
|
434
|
-
// ---------------------------------------------------------------------------
|
|
435
|
-
// Command implementations
|
|
436
|
-
// ---------------------------------------------------------------------------
|
|
437
|
-
|
|
438
|
-
function cmdWrite() {
|
|
439
|
-
const entity = args[1];
|
|
440
|
-
const jsonStr = args[2];
|
|
441
|
-
|
|
442
|
-
if (!entity || !jsonStr) {
|
|
443
|
-
console.error('Usage: store-cli.cjs write <entity> \'<json>\'');
|
|
444
|
-
process.exit(1);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
if (!ENTITY_TYPES.includes(entity)) {
|
|
448
|
-
console.error(`Unknown entity type: ${entity}`);
|
|
449
|
-
process.exit(1);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
let data;
|
|
453
|
-
try {
|
|
454
|
-
data = JSON.parse(jsonStr);
|
|
455
|
-
} catch (e) {
|
|
456
|
-
console.error(`Invalid JSON: ${e.message}`);
|
|
457
|
-
process.exit(1);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// Auto-populate date-only YYYY-MM-DD values in bug datetime fields.
|
|
461
|
-
// Agents commonly supply date-only values for reportedAt/resolvedAt;
|
|
462
|
-
// this normalizes them to full ISO datetimes before schema validation.
|
|
463
|
-
if (entity === 'bug') {
|
|
464
|
-
_normalizeBugTimestamps(data);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const errors = validateRecord(data, schemas[entity], { entity });
|
|
468
|
-
if (errors.length > 0) {
|
|
469
|
-
for (const e of errors) console.error(e);
|
|
470
|
-
process.exit(1);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
if (DRY_RUN) {
|
|
474
|
-
console.log(`[dry-run] would write ${entity} ${data[ENTITY_ID_FIELD[entity]]}`);
|
|
475
|
-
} else {
|
|
476
|
-
writeEntity(entity, data);
|
|
477
|
-
}
|
|
478
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, entity, id: data[ENTITY_ID_FIELD[entity]], dryRun: DRY_RUN }));
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
function cmdRead() {
|
|
482
|
-
const entity = args[1];
|
|
483
|
-
const id = args[2];
|
|
484
|
-
const asJson = args.includes('--json');
|
|
485
|
-
|
|
486
|
-
if (!entity || !id) {
|
|
487
|
-
console.error('Usage: store-cli.cjs read <entity> <id> [--json]');
|
|
488
|
-
process.exit(1);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
if (!ENTITY_TYPES.includes(entity)) {
|
|
492
|
-
console.error(`Unknown entity type: ${entity}`);
|
|
493
|
-
process.exit(1);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// Events need sprintId for lookup — read by eventId with sprintId resolution
|
|
497
|
-
let record;
|
|
498
|
-
if (entity === 'event') {
|
|
499
|
-
// For events, try to find by scanning sprint directories
|
|
500
|
-
const sprints = store.listSprints();
|
|
501
|
-
record = null;
|
|
502
|
-
for (const sprint of sprints) {
|
|
503
|
-
if (!sprint) continue;
|
|
504
|
-
const found = store.getEvent(id, sprint.sprintId);
|
|
505
|
-
if (found) { record = found; break; }
|
|
506
|
-
}
|
|
507
|
-
} else {
|
|
508
|
-
record = getEntity(entity, id);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
if (!record) {
|
|
512
|
-
console.error(`Entity not found: ${entity} ${id}`);
|
|
513
|
-
process.exit(1);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if (asJson) {
|
|
517
|
-
console.log(JSON.stringify(record));
|
|
518
|
-
} else {
|
|
519
|
-
console.log(JSON.stringify(record, null, 2));
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
function cmdList() {
|
|
524
|
-
const entity = args[1];
|
|
525
|
-
|
|
526
|
-
if (!entity) {
|
|
527
|
-
console.error('Usage: store-cli.cjs list <entity> [key=value ...]');
|
|
528
|
-
process.exit(1);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
if (!ENTITY_TYPES.includes(entity)) {
|
|
532
|
-
console.error(`Unknown entity type: ${entity}`);
|
|
533
|
-
process.exit(1);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// Parse key=value filter pairs from remaining args
|
|
537
|
-
const filter = {};
|
|
538
|
-
for (let i = 2; i < args.length; i++) {
|
|
539
|
-
const eqIdx = args[i].indexOf('=');
|
|
540
|
-
if (eqIdx > 0) {
|
|
541
|
-
const key = args[i].slice(0, eqIdx);
|
|
542
|
-
const val = args[i].slice(eqIdx + 1);
|
|
543
|
-
// Try to parse numeric values
|
|
544
|
-
const num = Number(val);
|
|
545
|
-
filter[key] = (val !== '' && !isNaN(num) && val === String(num)) ? num : val;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
const records = listEntities(entity, Object.keys(filter).length > 0 ? filter : undefined);
|
|
550
|
-
console.log(JSON.stringify(records, null, 2));
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
function cmdDelete() {
|
|
554
|
-
const entity = args[1];
|
|
555
|
-
const id = args[2];
|
|
556
|
-
|
|
557
|
-
if (!entity || !id) {
|
|
558
|
-
console.error('Usage: store-cli.cjs delete <entity> <id>');
|
|
559
|
-
process.exit(1);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
if (!ENTITY_TYPES.includes(entity)) {
|
|
563
|
-
console.error(`Unknown entity type: ${entity}`);
|
|
564
|
-
process.exit(1);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
deleteEntity(entity, id);
|
|
568
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, deleted: `${entity}/${id}` }));
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
function cmdUpdateStatus() {
|
|
572
|
-
const entity = args[1];
|
|
573
|
-
const id = args[2];
|
|
574
|
-
const field = args[3];
|
|
575
|
-
const value = args[4];
|
|
576
|
-
const force = args.includes('--force');
|
|
577
|
-
|
|
578
|
-
if (!entity || !id || !field || !value) {
|
|
579
|
-
console.error('Usage: store-cli.cjs update-status <entity> <id> <field> <value> [--force]');
|
|
580
|
-
process.exit(1);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
if (!TRANSITION_MAP[entity]) {
|
|
584
|
-
console.error(`No transition rules for entity type: ${entity}`);
|
|
585
|
-
process.exit(1);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// Read current record
|
|
589
|
-
const record = getEntity(entity, id);
|
|
590
|
-
if (!record) {
|
|
591
|
-
console.error(`Entity not found: ${entity} ${id}`);
|
|
592
|
-
process.exit(1);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
const currentValue = record[field];
|
|
596
|
-
if (currentValue === undefined) {
|
|
597
|
-
console.error(`Field "${field}" not found on ${entity} ${id}`);
|
|
598
|
-
process.exit(1);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// Check transition legality
|
|
602
|
-
if (!force && !isLegalTransition(entity, field, currentValue, value)) {
|
|
603
|
-
console.error(`Illegal transition: ${entity} ${id} ${field}: ${currentValue} → ${value}`);
|
|
604
|
-
process.exit(1);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
if (force && !isLegalTransition(entity, field, currentValue, value)) {
|
|
608
|
-
console.error(`WARN: --force bypassing illegal transition: ${entity} ${id} ${field}: ${currentValue} → ${value}`);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// Apply update and write back
|
|
612
|
-
if (DRY_RUN) {
|
|
613
|
-
console.log(`[dry-run] would update ${entity} ${id} ${field}: ${currentValue} → ${value}`);
|
|
614
|
-
} else {
|
|
615
|
-
record[field] = value;
|
|
616
|
-
writeEntity(entity, record);
|
|
617
|
-
}
|
|
618
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, entity, id, field, from: currentValue, to: value, force, dryRun: DRY_RUN }));
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// ---------------------------------------------------------------------------
|
|
622
|
-
// Timestamp normalization helpers (#56)
|
|
623
|
-
// ---------------------------------------------------------------------------
|
|
624
|
-
|
|
625
|
-
// Returns true if the timestamp string has a zeroed time component (T00:00:00),
|
|
626
|
-
// which indicates the caller provided a date-only value instead of a real
|
|
627
|
-
// time-of-day. Midnight UTC is treated as "not set" for event timing purposes.
|
|
628
|
-
function _isZeroedTimestamp(ts) {
|
|
629
|
-
if (typeof ts !== 'string') return true; // null / missing → normalize
|
|
630
|
-
return /T00:00:00/.test(ts);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// Normalize event timestamps before writing. Replaces any zeroed or absent
|
|
634
|
-
// startTimestamp / endTimestamp with the current real time, then recomputes
|
|
635
|
-
// durationMinutes from the two timestamps so cost reports are accurate.
|
|
636
|
-
function _normalizeEventTimestamps(data) {
|
|
637
|
-
const now = new Date().toISOString();
|
|
638
|
-
|
|
639
|
-
if (_isZeroedTimestamp(data.startTimestamp)) data.startTimestamp = now;
|
|
640
|
-
if (_isZeroedTimestamp(data.endTimestamp)) data.endTimestamp = now;
|
|
641
|
-
|
|
642
|
-
// Recompute durationMinutes whenever both timestamps are present.
|
|
643
|
-
if (data.startTimestamp && data.endTimestamp) {
|
|
644
|
-
const diffMs = new Date(data.endTimestamp) - new Date(data.startTimestamp);
|
|
645
|
-
data.durationMinutes = Math.max(0, diffMs / 60000);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
return data;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
function cmdEmit() {
|
|
652
|
-
const sprintId = args[1];
|
|
653
|
-
const jsonStr = args[2];
|
|
654
|
-
const isSidecar = args.includes('--sidecar');
|
|
655
|
-
|
|
656
|
-
if (!sprintId || !jsonStr) {
|
|
657
|
-
console.error('Usage: store-cli.cjs emit <sprintId> \'<json>\' [--sidecar]');
|
|
658
|
-
process.exit(1);
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
let data;
|
|
662
|
-
try {
|
|
663
|
-
data = JSON.parse(jsonStr);
|
|
664
|
-
} catch (e) {
|
|
665
|
-
console.error(`Invalid JSON: ${e.message}`);
|
|
666
|
-
process.exit(1);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
if (isSidecar) {
|
|
670
|
-
// Write sidecar file — validate against the sidecar schema (eventId +
|
|
671
|
-
// optional token/cost fields). Full event-shape enforcement happens
|
|
672
|
-
// on merge into the canonical event.
|
|
673
|
-
const sidecarErrors = validateRecord(data, schemas['event-sidecar']);
|
|
674
|
-
if (sidecarErrors.length > 0) {
|
|
675
|
-
for (const e of sidecarErrors) console.error(e);
|
|
676
|
-
process.exit(1);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
const sidecarDir = resolveSidecarDir(sprintId);
|
|
680
|
-
const filePath = sidecarPath(sprintId, data.eventId);
|
|
681
|
-
|
|
682
|
-
if (DRY_RUN) {
|
|
683
|
-
console.log(`[dry-run] would write sidecar _${data.eventId}_usage.json`);
|
|
684
|
-
} else {
|
|
685
|
-
// Ensure directory exists
|
|
686
|
-
if (!fs.existsSync(sidecarDir)) {
|
|
687
|
-
fs.mkdirSync(sidecarDir, { recursive: true });
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
691
|
-
}
|
|
692
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, sidecar: true, eventId: data.eventId, sprintId, dryRun: DRY_RUN }));
|
|
693
|
-
} else {
|
|
694
|
-
// Normalize zeroed timestamps before validation so agents that provide
|
|
695
|
-
// date-only values (T00:00:00Z) get real time-of-day stamped in (#56).
|
|
696
|
-
_normalizeEventTimestamps(data);
|
|
697
|
-
|
|
698
|
-
// Auto-populate model from environment when missing or empty (FORGE-S12-T06).
|
|
699
|
-
// Callers who set model explicitly take priority — discoverModel() is only
|
|
700
|
-
// used as a fallback so we never silently record a wrong model name.
|
|
701
|
-
if (!data.model || !data.model.trim()) {
|
|
702
|
-
data.model = discoverModel();
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// Validate as event entity
|
|
706
|
-
const errors = validateRecord(data, schemas.event);
|
|
707
|
-
if (errors.length > 0) {
|
|
708
|
-
for (const e of errors) console.error(e);
|
|
709
|
-
process.exit(1);
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
if (DRY_RUN) {
|
|
713
|
-
console.log(`[dry-run] would emit event ${data.eventId}`);
|
|
714
|
-
} else {
|
|
715
|
-
store.writeEvent(sprintId, data);
|
|
716
|
-
}
|
|
717
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, event: true, eventId: data.eventId, sprintId, dryRun: DRY_RUN }));
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
function cmdMergeSidecar() {
|
|
722
|
-
const sprintId = args[1];
|
|
723
|
-
const eventId = args[2];
|
|
724
|
-
|
|
725
|
-
if (!sprintId || !eventId) {
|
|
726
|
-
console.error('Usage: store-cli.cjs merge-sidecar <sprintId> <eventId>');
|
|
727
|
-
process.exit(1);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// Read sidecar
|
|
731
|
-
const scPath = sidecarPath(sprintId, eventId);
|
|
732
|
-
if (!fs.existsSync(scPath)) {
|
|
733
|
-
console.error(`Sidecar not found: ${scPath}`);
|
|
734
|
-
process.exit(1);
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
let sidecar;
|
|
738
|
-
try {
|
|
739
|
-
sidecar = JSON.parse(fs.readFileSync(scPath, 'utf8'));
|
|
740
|
-
} catch (e) {
|
|
741
|
-
console.error(`Invalid sidecar JSON: ${e.message}`);
|
|
742
|
-
process.exit(1);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// Read canonical event
|
|
746
|
-
const canPath = canonicalEventPath(sprintId, eventId);
|
|
747
|
-
if (!fs.existsSync(canPath)) {
|
|
748
|
-
console.error(`Canonical event not found: ${canPath}`);
|
|
749
|
-
process.exit(1);
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
let event;
|
|
753
|
-
try {
|
|
754
|
-
event = JSON.parse(fs.readFileSync(canPath, 'utf8'));
|
|
755
|
-
} catch (e) {
|
|
756
|
-
console.error(`Invalid canonical event JSON: ${e.message}`);
|
|
757
|
-
process.exit(1);
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
// Merge token fields from sidecar into event
|
|
761
|
-
for (const [key, value] of Object.entries(sidecar)) {
|
|
762
|
-
// Resolve aliases
|
|
763
|
-
const canonicalKey = SIDECAR_ALIASES[key] || key;
|
|
764
|
-
if (CANONICAL_TOKEN_FIELDS.includes(canonicalKey)) {
|
|
765
|
-
event[canonicalKey] = value;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// Re-validate the merged canonical event against the event schema. Catches
|
|
770
|
-
// the case where a sidecar's token field is present but the canonical event
|
|
771
|
-
// was already malformed — we do not want to silently persist invalid data.
|
|
772
|
-
const mergedErrors = validateRecord(event, schemas.event);
|
|
773
|
-
if (mergedErrors.length > 0) {
|
|
774
|
-
console.error(`Merged event ${eventId} failed schema validation:`);
|
|
775
|
-
for (const e of mergedErrors) console.error(` ${e}`);
|
|
776
|
-
process.exit(1);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
if (DRY_RUN) {
|
|
780
|
-
console.log(`[dry-run] would merge sidecar into ${eventId} and delete sidecar`);
|
|
781
|
-
} else {
|
|
782
|
-
// Write updated event via facade (handles ghost-file logic)
|
|
783
|
-
store.writeEvent(sprintId, event);
|
|
784
|
-
|
|
785
|
-
// Delete sidecar
|
|
786
|
-
fs.unlinkSync(scPath);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, merged: true, eventId, sprintId, dryRun: DRY_RUN }));
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
function cmdRecordUsage() {
|
|
793
|
-
const sprintId = args[1];
|
|
794
|
-
const eventId = args[2];
|
|
795
|
-
|
|
796
|
-
if (!sprintId || !eventId) {
|
|
797
|
-
console.error('Usage: store-cli.cjs record-usage <sprintId> <eventId> [flags]');
|
|
798
|
-
console.error(' Flags:');
|
|
799
|
-
console.error(' --input-tokens <n> Input token count');
|
|
800
|
-
console.error(' --output-tokens <n> Output token count');
|
|
801
|
-
console.error(' --cache-read-tokens <n> Cache read token count');
|
|
802
|
-
console.error(' --cache-write-tokens <n> Cache write token count');
|
|
803
|
-
console.error(' --estimated-cost-usd <n> Estimated cost in USD');
|
|
804
|
-
console.error(' --token-source <src> reported | estimated');
|
|
805
|
-
console.error(' --model <model> Model identifier');
|
|
806
|
-
console.error(' --duration-minutes <n> Duration in minutes');
|
|
807
|
-
process.exit(1);
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
// Parse flag arguments from remaining args
|
|
811
|
-
const flagArgs = args.slice(3);
|
|
812
|
-
const sidecar = { eventId };
|
|
813
|
-
|
|
814
|
-
for (let i = 0; i < flagArgs.length; i++) {
|
|
815
|
-
const arg = flagArgs[i];
|
|
816
|
-
if (arg === '--input-tokens' && flagArgs[i + 1]) {
|
|
817
|
-
sidecar.inputTokens = parseInt(flagArgs[++i], 10);
|
|
818
|
-
} else if (arg === '--output-tokens' && flagArgs[i + 1]) {
|
|
819
|
-
sidecar.outputTokens = parseInt(flagArgs[++i], 10);
|
|
820
|
-
} else if (arg === '--cache-read-tokens' && flagArgs[i + 1]) {
|
|
821
|
-
sidecar.cacheReadTokens = parseInt(flagArgs[++i], 10);
|
|
822
|
-
} else if (arg === '--cache-write-tokens' && flagArgs[i + 1]) {
|
|
823
|
-
sidecar.cacheWriteTokens = parseInt(flagArgs[++i], 10);
|
|
824
|
-
} else if (arg === '--estimated-cost-usd' && flagArgs[i + 1]) {
|
|
825
|
-
sidecar.estimatedCostUSD = parseFloat(flagArgs[++i]);
|
|
826
|
-
} else if (arg === '--token-source' && flagArgs[i + 1]) {
|
|
827
|
-
sidecar.tokenSource = flagArgs[++i];
|
|
828
|
-
} else if (arg === '--model' && flagArgs[i + 1]) {
|
|
829
|
-
sidecar.model = flagArgs[++i];
|
|
830
|
-
} else if (arg === '--duration-minutes' && flagArgs[i + 1]) {
|
|
831
|
-
sidecar.durationMinutes = parseFloat(flagArgs[++i]);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
// Auto-populate model from environment when --model flag not provided (FORGE-S12-T06).
|
|
836
|
-
// An explicit --model flag takes priority — discoverModel() is only a fallback.
|
|
837
|
-
if (!sidecar.model) {
|
|
838
|
-
sidecar.model = discoverModel();
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
// Validate against sidecar schema
|
|
842
|
-
const sidecarErrors = validateRecord(sidecar, schemas['event-sidecar']);
|
|
843
|
-
if (sidecarErrors.length > 0) {
|
|
844
|
-
for (const e of sidecarErrors) console.error(e);
|
|
845
|
-
process.exit(1);
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
const sidecarDir = resolveSidecarDir(sprintId);
|
|
849
|
-
const filePath = sidecarPath(sprintId, eventId);
|
|
850
|
-
|
|
851
|
-
if (DRY_RUN) {
|
|
852
|
-
console.log(`[dry-run] would write sidecar _${eventId}_usage.json`);
|
|
853
|
-
} else {
|
|
854
|
-
if (!fs.existsSync(sidecarDir)) {
|
|
855
|
-
fs.mkdirSync(sidecarDir, { recursive: true });
|
|
856
|
-
}
|
|
857
|
-
fs.writeFileSync(filePath, JSON.stringify(sidecar, null, 2) + '\n', 'utf8');
|
|
858
|
-
}
|
|
859
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, sidecar: true, eventId, sprintId, dryRun: DRY_RUN }));
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
function cmdPurgeEvents() {
|
|
863
|
-
const sprintId = args[1];
|
|
864
|
-
|
|
865
|
-
if (!sprintId) {
|
|
866
|
-
console.error('Usage: store-cli.cjs purge-events <sprintId>');
|
|
867
|
-
process.exit(1);
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
const result = store.purgeEvents(sprintId, { dryRun: DRY_RUN });
|
|
871
|
-
if (DRY_RUN && !result.purged) {
|
|
872
|
-
console.log(`[dry-run] would purge ${result.fileCount} event(s) for ${sprintId}`);
|
|
873
|
-
}
|
|
874
|
-
if (VERBOSE) console.log(JSON.stringify(result, null, 2));
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
function cmdWriteCollationState() {
|
|
878
|
-
const jsonStr = args[1];
|
|
879
|
-
|
|
880
|
-
if (!jsonStr) {
|
|
881
|
-
console.error('Usage: store-cli.cjs write-collation-state \'<json>\'');
|
|
882
|
-
process.exit(1);
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
let data;
|
|
886
|
-
try {
|
|
887
|
-
data = JSON.parse(jsonStr);
|
|
888
|
-
} catch (e) {
|
|
889
|
-
console.error(`Invalid JSON: ${e.message}`);
|
|
890
|
-
process.exit(1);
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
const csErrors = validateRecord(data, schemas['collation-state']);
|
|
894
|
-
if (csErrors.length > 0) {
|
|
895
|
-
for (const e of csErrors) console.error(e);
|
|
896
|
-
process.exit(1);
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
if (DRY_RUN) {
|
|
900
|
-
console.log('[dry-run] would write COLLATION_STATE.json');
|
|
901
|
-
} else {
|
|
902
|
-
store.writeCollationState(data);
|
|
903
|
-
}
|
|
904
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, dryRun: DRY_RUN }));
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
function cmdProgress() {
|
|
908
|
-
const sprintOrBugId = args[1];
|
|
909
|
-
const agentName = args[2];
|
|
910
|
-
const bannerKey = args[3];
|
|
911
|
-
const status = args[4];
|
|
912
|
-
const detail = args.slice(5).join(' ');
|
|
913
|
-
|
|
914
|
-
if (!sprintOrBugId || !agentName || !bannerKey || !status) {
|
|
915
|
-
console.error('Usage: store-cli.cjs progress <sprintOrBugId> <agentName> <bannerKey> <status> [detail]');
|
|
916
|
-
console.error(' status: start | progress | done | error');
|
|
917
|
-
process.exit(1);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
const timestamp = new Date().toISOString();
|
|
921
|
-
|
|
922
|
-
const progressErrors = validateRecord(
|
|
923
|
-
{ timestamp, agentName, bannerKey, status, detail: detail || '' },
|
|
924
|
-
schemas['progress-entry']
|
|
925
|
-
);
|
|
926
|
-
if (progressErrors.length > 0) {
|
|
927
|
-
for (const e of progressErrors) console.error(e);
|
|
928
|
-
process.exit(1);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
const line = `${timestamp}|${agentName}|${bannerKey}|${status}|${detail}\n`;
|
|
932
|
-
|
|
933
|
-
const dir = _resolveEventsDir(sprintOrBugId);
|
|
934
|
-
if (!fs.existsSync(dir)) {
|
|
935
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
const logPath = path.join(dir, 'progress.log');
|
|
939
|
-
fs.appendFileSync(logPath, line, 'utf8');
|
|
940
|
-
|
|
941
|
-
// Emit human-readable summary to stdout
|
|
942
|
-
let banners;
|
|
943
|
-
try { banners = require('./banners.cjs'); } catch { banners = null; }
|
|
944
|
-
let emoji = bannerKey;
|
|
945
|
-
if (banners && typeof banners.mark === 'function') {
|
|
946
|
-
try { emoji = banners.mark(bannerKey); } catch { emoji = bannerKey; }
|
|
947
|
-
}
|
|
948
|
-
const summary = `${emoji} ${agentName} [${status}]${detail ? ' ' + detail : ''}`;
|
|
949
|
-
if (VERBOSE) process.stdout.write(summary + '\n');
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
function cmdProgressClear() {
|
|
953
|
-
const sprintOrBugId = args[1];
|
|
954
|
-
|
|
955
|
-
if (!sprintOrBugId) {
|
|
956
|
-
console.error('Usage: store-cli.cjs progress-clear <sprintOrBugId>');
|
|
957
|
-
process.exit(1);
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
const dir = _resolveEventsDir(sprintOrBugId);
|
|
961
|
-
if (!fs.existsSync(dir)) {
|
|
962
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
const logPath = path.join(dir, 'progress.log');
|
|
966
|
-
fs.writeFileSync(logPath, '', 'utf8');
|
|
967
|
-
if (VERBOSE) console.log(`Cleared ${logPath}`);
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
function cmdValidate() {
|
|
971
|
-
const entity = args[1];
|
|
972
|
-
const jsonStr = args[2];
|
|
973
|
-
|
|
974
|
-
if (!entity || !jsonStr) {
|
|
975
|
-
console.error('Usage: store-cli.cjs validate <entity> \'<json>\'');
|
|
976
|
-
process.exit(1);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
if (!ENTITY_TYPES.includes(entity)) {
|
|
980
|
-
console.error(`Unknown entity type: ${entity}`);
|
|
981
|
-
process.exit(1);
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
let data;
|
|
985
|
-
try {
|
|
986
|
-
data = JSON.parse(jsonStr);
|
|
987
|
-
} catch (e) {
|
|
988
|
-
console.error(`Invalid JSON: ${e.message}`);
|
|
989
|
-
process.exit(1);
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
const errors = validateRecord(data, schemas[entity], { entity });
|
|
993
|
-
if (errors.length > 0) {
|
|
994
|
-
for (const e of errors) console.error(e);
|
|
995
|
-
process.exit(1);
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, entity, valid: true }));
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
function _setSummaryOnEntity(entityKind, entityId, phase, summaryFilePath) {
|
|
1002
|
-
if (!VALID_SUMMARY_PHASES.has(phase)) {
|
|
1003
|
-
console.error(`Unknown phase "${phase}". Valid phases: ${[...VALID_SUMMARY_PHASES].join(', ')}`);
|
|
1004
|
-
process.exit(1);
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
// Read and validate summary JSON
|
|
1008
|
-
if (!fs.existsSync(summaryFilePath)) {
|
|
1009
|
-
console.error(`Summary file not found: ${summaryFilePath}`);
|
|
1010
|
-
process.exit(1);
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
let summary;
|
|
1014
|
-
try {
|
|
1015
|
-
summary = JSON.parse(fs.readFileSync(summaryFilePath, 'utf8'));
|
|
1016
|
-
} catch (e) {
|
|
1017
|
-
console.error(`Invalid JSON in summary file: ${e.message}`);
|
|
1018
|
-
process.exit(1);
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
const errors = validateRecord(summary, PHASE_SUMMARY_SCHEMA);
|
|
1022
|
-
if (errors.length > 0) {
|
|
1023
|
-
for (const e of errors) console.error(e);
|
|
1024
|
-
process.exit(1);
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
// Load entity
|
|
1028
|
-
const record = entityKind === 'task' ? store.getTask(entityId) : store.getBug(entityId);
|
|
1029
|
-
if (!record) {
|
|
1030
|
-
console.error(`${entityKind} not found: ${entityId}`);
|
|
1031
|
-
process.exit(1);
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
// Merge summary
|
|
1035
|
-
if (!record.summaries) record.summaries = {};
|
|
1036
|
-
record.summaries[phase] = summary;
|
|
1037
|
-
|
|
1038
|
-
// Atomic write: tmp + rename
|
|
1039
|
-
const entityDirKey = entityKind === 'task' ? 'tasks' : 'bugs';
|
|
1040
|
-
const idField = entityKind === 'task' ? record.taskId : record.bugId;
|
|
1041
|
-
const storeRoot = store.impl.storeRoot;
|
|
1042
|
-
const filePath = path.join(storeRoot, entityDirKey, `${idField}.json`);
|
|
1043
|
-
const tmpPath = filePath + '.tmp';
|
|
1044
|
-
|
|
1045
|
-
if (DRY_RUN) {
|
|
1046
|
-
console.log(`[dry-run] would set ${entityKind} ${entityId} summaries.${phase}`);
|
|
1047
|
-
} else {
|
|
1048
|
-
fs.writeFileSync(tmpPath, JSON.stringify(record, null, 2) + '\n', 'utf8');
|
|
1049
|
-
fs.renameSync(tmpPath, filePath);
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
if (VERBOSE) console.log(JSON.stringify({ ok: true, entityKind, id: entityId, phase, dryRun: DRY_RUN }));
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
function cmdSetSummary() {
|
|
1056
|
-
const taskId = args[1];
|
|
1057
|
-
const phase = args[2];
|
|
1058
|
-
const summaryFile = args[3];
|
|
1059
|
-
|
|
1060
|
-
if (!taskId || !phase || !summaryFile) {
|
|
1061
|
-
console.error('Usage: store-cli.cjs set-summary <taskId> <phase> <jsonFile>');
|
|
1062
|
-
process.exit(1);
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
_setSummaryOnEntity('task', taskId, phase, summaryFile);
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
function cmdSetBugSummary() {
|
|
1069
|
-
const bugId = args[1];
|
|
1070
|
-
const phase = args[2];
|
|
1071
|
-
const summaryFile = args[3];
|
|
1072
|
-
|
|
1073
|
-
if (!bugId || !phase || !summaryFile) {
|
|
1074
|
-
console.error('Usage: store-cli.cjs set-bug-summary <bugId> <phase> <jsonFile>');
|
|
1075
|
-
process.exit(1);
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
_setSummaryOnEntity('bug', bugId, phase, summaryFile);
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// ---------------------------------------------------------------------------
|
|
1082
|
-
// describe / template — schema introspection helpers (FORGE-BUG-029-friction)
|
|
1083
|
-
//
|
|
1084
|
-
// Reduce write→reject→retry friction. `describe <entity>` returns the raw
|
|
1085
|
-
// JSON Schema; `template <entity>` walks the schema and returns a canonical
|
|
1086
|
-
// sample record with required fields populated.
|
|
1087
|
-
// ---------------------------------------------------------------------------
|
|
1088
|
-
|
|
1089
|
-
const VALID_ENTITY_DESCRIBE = new Set(['sprint', 'task', 'bug', 'event', 'feature']);
|
|
1090
|
-
|
|
1091
|
-
function cmdDescribe() {
|
|
1092
|
-
const entity = args[1];
|
|
1093
|
-
if (!entity) {
|
|
1094
|
-
console.error('Usage: store-cli.cjs describe <entity>');
|
|
1095
|
-
console.error(`Entities: ${Array.from(VALID_ENTITY_DESCRIBE).join(', ')}`);
|
|
1096
|
-
process.exit(1);
|
|
1097
|
-
}
|
|
1098
|
-
if (!VALID_ENTITY_DESCRIBE.has(entity)) {
|
|
1099
|
-
console.error(`Unknown entity: ${entity}`);
|
|
1100
|
-
console.error(`Entities: ${Array.from(VALID_ENTITY_DESCRIBE).join(', ')}`);
|
|
1101
|
-
process.exit(1);
|
|
1102
|
-
}
|
|
1103
|
-
const schema = _getSchemas()[entity];
|
|
1104
|
-
if (!schema) {
|
|
1105
|
-
console.error(`Schema not found for entity: ${entity}`);
|
|
1106
|
-
process.exit(1);
|
|
1107
|
-
}
|
|
1108
|
-
console.log(JSON.stringify(schema, null, 2));
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
// Field-name → placeholder ID heuristics. Covers the common shapes without
|
|
1112
|
-
// inventing schema-specific knowledge in the generator itself.
|
|
1113
|
-
function _idPlaceholder(entity, fieldName) {
|
|
1114
|
-
if (fieldName === 'sprintId') return 'PROJECT-S01';
|
|
1115
|
-
if (fieldName === 'taskId') return 'PROJECT-S01-T01';
|
|
1116
|
-
if (fieldName === 'bugId') return 'PROJECT-BUG-001';
|
|
1117
|
-
if (fieldName === 'feature_id' || fieldName === 'featureId') return 'PROJECT-F01';
|
|
1118
|
-
if (fieldName === 'eventId') return '20260101T000000000Z_PROJECT-S01-T01_phase_start';
|
|
1119
|
-
if (entity === 'feature' && fieldName === 'id') return 'PROJECT-F01';
|
|
1120
|
-
return `<${fieldName}>`;
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
function _generateSample(schema, entity) {
|
|
1124
|
-
const sample = {};
|
|
1125
|
-
const required = schema.required || [];
|
|
1126
|
-
const properties = schema.properties || {};
|
|
1127
|
-
for (const field of required) {
|
|
1128
|
-
const def = properties[field] || {};
|
|
1129
|
-
sample[field] = _generateValue(def, field, entity);
|
|
1130
|
-
}
|
|
1131
|
-
return sample;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
function _generateValue(def, fieldName, entity) {
|
|
1135
|
-
// Resolve type — handle union (array of types) by picking the first non-null.
|
|
1136
|
-
let t = def.type;
|
|
1137
|
-
if (Array.isArray(t)) t = t.find((x) => x !== 'null') || t[0];
|
|
1138
|
-
|
|
1139
|
-
if (def.enum && Array.isArray(def.enum) && def.enum.length > 0) {
|
|
1140
|
-
return def.enum[0];
|
|
1141
|
-
}
|
|
1142
|
-
if (t === 'string') {
|
|
1143
|
-
if (def.format === 'date-time') return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
1144
|
-
if (/Id$|^id$/.test(fieldName) || fieldName === 'feature_id' || fieldName === 'eventId') {
|
|
1145
|
-
return _idPlaceholder(entity, fieldName);
|
|
1146
|
-
}
|
|
1147
|
-
if (fieldName === 'title') return 'Sample title';
|
|
1148
|
-
if (fieldName === 'description') return 'Sample description';
|
|
1149
|
-
if (fieldName === 'path') return `engineering/sprints/PROJECT-S01/${fieldName}-sample.md`;
|
|
1150
|
-
return `<${fieldName}>`;
|
|
1151
|
-
}
|
|
1152
|
-
if (t === 'integer' || t === 'number') return 0;
|
|
1153
|
-
if (t === 'boolean') return false;
|
|
1154
|
-
if (t === 'array') return [];
|
|
1155
|
-
if (t === 'object') return {};
|
|
1156
|
-
return null;
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
function cmdTemplate() {
|
|
1160
|
-
const entity = args[1];
|
|
1161
|
-
if (!entity) {
|
|
1162
|
-
console.error('Usage: store-cli.cjs template <entity>');
|
|
1163
|
-
console.error(`Entities: ${Array.from(VALID_ENTITY_DESCRIBE).join(', ')}`);
|
|
1164
|
-
process.exit(1);
|
|
1165
|
-
}
|
|
1166
|
-
if (!VALID_ENTITY_DESCRIBE.has(entity)) {
|
|
1167
|
-
console.error(`Unknown entity: ${entity}`);
|
|
1168
|
-
console.error(`Entities: ${Array.from(VALID_ENTITY_DESCRIBE).join(', ')}`);
|
|
1169
|
-
process.exit(1);
|
|
1170
|
-
}
|
|
1171
|
-
const schema = _getSchemas()[entity];
|
|
1172
|
-
if (!schema) {
|
|
1173
|
-
console.error(`Schema not found for entity: ${entity}`);
|
|
1174
|
-
process.exit(1);
|
|
1175
|
-
}
|
|
1176
|
-
const sample = _generateSample(schema, entity);
|
|
1177
|
-
console.log(JSON.stringify(sample, null, 2));
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
// ---------------------------------------------------------------------------
|
|
1181
|
-
// Command dispatch
|
|
1182
|
-
// ---------------------------------------------------------------------------
|
|
1183
|
-
|
|
1184
|
-
switch (command) {
|
|
1185
|
-
case 'write': cmdWrite(); break;
|
|
1186
|
-
case 'read': cmdRead(); break;
|
|
1187
|
-
case 'list': cmdList(); break;
|
|
1188
|
-
case 'delete': cmdDelete(); break;
|
|
1189
|
-
case 'update-status': cmdUpdateStatus(); break;
|
|
1190
|
-
case 'emit': cmdEmit(); break;
|
|
1191
|
-
case 'merge-sidecar': cmdMergeSidecar(); break;
|
|
1192
|
-
case 'record-usage': cmdRecordUsage(); break;
|
|
1193
|
-
case 'purge-events': cmdPurgeEvents(); break;
|
|
1194
|
-
case 'write-collation-state': cmdWriteCollationState(); break;
|
|
1195
|
-
case 'validate': cmdValidate(); break;
|
|
1196
|
-
case 'progress': cmdProgress(); break;
|
|
1197
|
-
case 'progress-clear': cmdProgressClear(); break;
|
|
1198
|
-
case 'set-summary': cmdSetSummary(); break;
|
|
1199
|
-
case 'set-bug-summary': cmdSetBugSummary(); break;
|
|
1200
|
-
case 'describe': cmdDescribe(); break;
|
|
1201
|
-
case 'template': cmdTemplate(); break;
|
|
1202
|
-
case 'query':
|
|
1203
|
-
case 'nlp':
|
|
1204
|
-
case 'schema': {
|
|
1205
|
-
// Delegate to store-query.cjs — query engine lives there
|
|
1206
|
-
const { spawnSync } = require('child_process');
|
|
1207
|
-
const queryBin = path.join(__dirname, 'store-query.cjs');
|
|
1208
|
-
const result = spawnSync(process.execPath, [queryBin, command, ...args.slice(1)], {
|
|
1209
|
-
stdio: 'inherit',
|
|
1210
|
-
cwd: process.cwd(),
|
|
1211
|
-
});
|
|
1212
|
-
process.exit(result.status ?? 1);
|
|
1213
|
-
break;
|
|
1214
|
-
}
|
|
1215
|
-
default:
|
|
1216
|
-
console.error(`Unknown command: ${command}`);
|
|
1217
|
-
console.error('Run with --help for usage information.');
|
|
1218
|
-
process.exit(1);
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
} catch (err) {
|
|
1222
|
-
console.error(err.message);
|
|
1223
|
-
process.exit(1);
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
} // end if (require.main === module)
|