@gajae-code/coding-agent 0.18.11 → 0.19.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 +760 -0
- package/dist/types/async/job-manager.d.ts +7 -3
- package/dist/types/async/result-formatting.d.ts +2 -0
- package/dist/types/autoresearch/git.d.ts +1 -1
- package/dist/types/capability/fs.d.ts +2 -2
- package/dist/types/capability/rule.d.ts +7 -2
- package/dist/types/cli/accounts-cli.d.ts +2 -2
- package/dist/types/cli/append-system-prompt.d.ts +6 -0
- package/dist/types/cli/args.d.ts +7 -2
- package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
- package/dist/types/cli/customize-doctor.d.ts +4 -3
- package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
- package/dist/types/cli/install-activation.d.ts +10 -4
- package/dist/types/cli/public-command-errors.d.ts +2 -0
- package/dist/types/cli/root-flags.d.ts +0 -12
- package/dist/types/cli/update-cli.d.ts +21 -3
- package/dist/types/cli-main.d.ts +0 -6
- package/dist/types/cli-ordinary.d.ts +2 -2
- package/dist/types/commands/accounts.d.ts +2 -1
- package/dist/types/commands/launch.d.ts +0 -6
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +13 -0
- package/dist/types/commit/changelog/index.d.ts +1 -0
- package/dist/types/commit/map-reduce/index.d.ts +4 -4
- package/dist/types/config/autorouting-tier-map.d.ts +53 -1
- package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
- package/dist/types/config/config-hot-reload.d.ts +44 -0
- package/dist/types/config/file-lock.d.ts +52 -3
- package/dist/types/config/model-discovery-manager.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +79 -9
- package/dist/types/config/model-registry.d.ts +72 -8
- package/dist/types/config/model-resolver.d.ts +7 -4
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/settings-schema.d.ts +57 -4
- package/dist/types/config/settings.d.ts +33 -1
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/customization/import.d.ts +2 -0
- package/dist/types/customization/types.d.ts +1 -1
- package/dist/types/dap/config.d.ts +8 -4
- package/dist/types/dap/session.d.ts +11 -2
- package/dist/types/debug/system-info.d.ts +6 -1
- package/dist/types/discovery/helpers.d.ts +7 -2
- package/dist/types/discovery/mcp-compat.d.ts +3 -1
- package/dist/types/edit/index.d.ts +5 -0
- package/dist/types/edit/modes/patch.d.ts +14 -0
- package/dist/types/edit/modes/replace.d.ts +3 -0
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +12 -0
- package/dist/types/export/ttsr.d.ts +19 -0
- package/dist/types/extensibility/extensions/types.d.ts +5 -1
- package/dist/types/extensibility/slash-commands.d.ts +1 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/content.d.ts +5 -6
- package/dist/types/internal-urls/types.d.ts +2 -0
- package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
- package/dist/types/main.d.ts +23 -18
- package/dist/types/migrate/action-planner.d.ts +10 -1
- package/dist/types/migrate/adapters/codex.d.ts +2 -1
- package/dist/types/migrate/adapters/index.d.ts +42 -1
- package/dist/types/migrate/executor.d.ts +3 -1
- package/dist/types/migrate/hook-module.d.ts +23 -0
- package/dist/types/migrate/report.d.ts +2 -1
- package/dist/types/migrate/types.d.ts +82 -3
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +43 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
- package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -0
- package/dist/types/modes/utils/host-status.d.ts +15 -11
- package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
- package/dist/types/runtime-mcp/manager.d.ts +7 -0
- package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
- package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
- package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
- package/dist/types/sdk/broker/broker-exit.d.ts +3 -3
- package/dist/types/sdk/broker/broker.d.ts +29 -2
- package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
- package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
- package/dist/types/sdk/broker/ensure.d.ts +141 -9
- package/dist/types/sdk/broker/hop.d.ts +45 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
- package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
- package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
- package/dist/types/sdk/broker/session-index.d.ts +6 -1
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
- package/dist/types/sdk/bus/index.d.ts +44 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
- package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
- package/dist/types/sdk/cli/session-cli.d.ts +0 -1
- package/dist/types/sdk/host/control/operations.d.ts +5 -1
- package/dist/types/sdk/host/events.d.ts +4 -0
- package/dist/types/sdk/host/host.d.ts +2 -1
- package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
- package/dist/types/sdk/host/session-runtime.d.ts +40 -8
- package/dist/types/sdk/host/surface-policy.d.ts +2 -1
- package/dist/types/sdk/lifecycle/service.d.ts +12 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
- package/dist/types/sdk/prompt-status.d.ts +11 -4
- package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
- package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +107 -9
- package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
- package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
- package/dist/types/session/messages.d.ts +5 -1
- package/dist/types/session/session-manager.d.ts +29 -3
- package/dist/types/session/startup-auth-config.d.ts +24 -2
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/session/terminal-abort.d.ts +3 -1
- package/dist/types/session-import/service.d.ts +1 -1
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +20 -4
- package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
- package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
- package/dist/types/ssh/ssh-executor.d.ts +8 -1
- package/dist/types/system-prompt.d.ts +3 -2
- package/dist/types/task/executor.d.ts +19 -2
- package/dist/types/task/role-body.d.ts +11 -0
- package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
- package/dist/types/tools/atomic-file-write.d.ts +9 -7
- package/dist/types/tools/awaiting-input.d.ts +28 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +17 -0
- package/dist/types/tools/bash-skill-urls.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +7 -0
- package/dist/types/tools/descriptors.d.ts +15 -2
- package/dist/types/tools/fetch.d.ts +7 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/irc.d.ts +5 -3
- package/dist/types/tools/python.d.ts +1 -1
- package/dist/types/tools/report-issue.d.ts +41 -0
- package/dist/types/tools/subagent.d.ts +23 -0
- package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/utils/file-mentions.d.ts +5 -0
- package/dist/types/utils/image-limits.d.ts +7 -0
- package/dist/types/utils/image-loading.d.ts +0 -1
- package/dist/types/utils/open.d.ts +0 -1
- package/dist/types/utils/pasted-image-loading.d.ts +1 -5
- package/dist/types/utils/prompt-suggestion.d.ts +2 -2
- package/dist/types/utils/shell-snapshot.d.ts +2 -0
- package/dist/types/utils/title-generator.d.ts +13 -3
- package/dist/types/web/scrapers/types.d.ts +8 -1
- package/dist/types/web/search/providers/codex.d.ts +6 -7
- package/dist/types/web/search/providers/insane.d.ts +7 -1
- package/dist/types/web/search/providers/searxng.d.ts +5 -0
- package/package.json +10 -9
- package/scripts/acp-conformance-agent.ts +12 -2
- package/scripts/build-binary.ts +2 -1
- package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
- package/scripts/generate-docs-index.ts +8 -1
- package/scripts/generate-sdk-operation-inventory.ts +38 -4
- package/src/async/job-manager.ts +356 -56
- package/src/async/result-formatting.ts +40 -0
- package/src/autoresearch/git.ts +9 -2
- package/src/autoresearch/prompts/prompt-setup.md +1 -1
- package/src/capability/fs.ts +58 -22
- package/src/capability/index.ts +2 -2
- package/src/capability/rule.ts +11 -2
- package/src/cli/accounts-cli.ts +176 -8
- package/src/cli/append-system-prompt.ts +12 -0
- package/src/cli/args.ts +11 -7
- package/src/cli/auth-broker-cli.ts +21 -11
- package/src/cli/customize-doctor-precedence.ts +31 -0
- package/src/cli/customize-doctor.ts +49 -39
- package/src/cli/doctor/install-repairs.ts +35 -5
- package/src/cli/fast-help.ts +1 -3
- package/src/cli/install-activation.ts +47 -4
- package/src/cli/migrate-cli.ts +19 -6
- package/src/cli/public-command-errors.ts +6 -0
- package/src/cli/root-flags.ts +2 -4
- package/src/cli/stats-cli.ts +9 -5
- package/src/cli/update-cli.ts +471 -13
- package/src/cli-ordinary.ts +7 -2
- package/src/commands/accounts.ts +10 -5
- package/src/commands/harness.ts +25 -15
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +277 -50
- package/src/commands/ultragoal.ts +16 -8
- package/src/commit/agentic/agent.ts +6 -6
- package/src/commit/analysis/conventional.ts +14 -5
- package/src/commit/changelog/index.ts +39 -1
- package/src/commit/map-reduce/index.ts +7 -7
- package/src/commit/message.ts +10 -3
- package/src/commit/pipeline.ts +12 -12
- package/src/config/autorouting-tier-map.ts +50 -1
- package/src/config/config-file.ts +106 -3
- package/src/config/config-hot-reload-trust.ts +148 -0
- package/src/config/config-hot-reload.ts +742 -0
- package/src/config/file-lock.ts +1615 -328
- package/src/config/keybindings.ts +154 -7
- package/src/config/model-bindings-applier.ts +85 -47
- package/src/config/model-discovery-manager.ts +10 -0
- package/src/config/model-profile-activation.ts +889 -148
- package/src/config/model-registry.ts +1706 -205
- package/src/config/model-resolver.ts +52 -22
- package/src/config/models-config-schema.ts +6 -0
- package/src/config/settings-schema.ts +73 -4
- package/src/config/settings.ts +460 -24
- package/src/coordinator-mcp/policy.ts +26 -1
- package/src/coordinator-mcp/server.ts +37 -2
- package/src/crash/sanitize.ts +7 -1
- package/src/customization/import.ts +279 -8
- package/src/customization/inventory.ts +24 -8
- package/src/customization/types.ts +1 -0
- package/src/dap/client.ts +3 -2
- package/src/dap/config.ts +31 -11
- package/src/dap/defaults.json +1 -1
- package/src/dap/session.ts +161 -36
- package/src/debug/crash-diagnostics.ts +89 -6
- package/src/debug/report-bundle.ts +90 -2
- package/src/debug/system-info.ts +8 -3
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
- package/src/discovery/agents-md.ts +1 -1
- package/src/discovery/builtin.ts +4 -3
- package/src/discovery/helpers.ts +25 -16
- package/src/discovery/mcp-compat.ts +120 -17
- package/src/edit/apply-patch/index.ts +12 -1
- package/src/edit/index.ts +204 -22
- package/src/edit/modes/patch.ts +214 -8
- package/src/edit/modes/replace.ts +136 -0
- package/src/eval/js/context-manager.ts +29 -3
- package/src/eval/js/worker-entry.ts +51 -18
- package/src/eval/py/executor.ts +14 -4
- package/src/eval/py/runner.py +144 -5
- package/src/eval/py/runtime.ts +44 -4
- package/src/exec/bash-executor.ts +81 -4
- package/src/exec/bash-shell-guardian.ts +4 -1
- package/src/exec/bash-shell-supervisor.ts +8 -4
- package/src/exec/isolated-shell.ts +5 -1
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/export/html/index.ts +86 -6
- package/src/export/ttsr.ts +176 -1
- package/src/extensibility/custom-tools/wrapper.ts +3 -1
- package/src/extensibility/extensions/loader.ts +10 -1
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
- package/src/extensibility/extensions/types.ts +5 -1
- package/src/extensibility/hooks/loader.ts +10 -2
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
- package/src/extensibility/plugins/marketplace/manager.ts +36 -10
- package/src/extensibility/slash-commands.ts +5 -0
- package/src/gjc-runtime/gc-runtime.ts +37 -7
- package/src/gjc-runtime/launch-tmux.ts +30 -15
- package/src/gjc-runtime/launch-worktree.test.ts +148 -0
- package/src/gjc-runtime/launch-worktree.ts +101 -14
- package/src/gjc-runtime/managed-owner-admission.ts +46 -2
- package/src/gjc-runtime/ralplan-current-final.ts +294 -0
- package/src/gjc-runtime/ralplan-runtime.ts +95 -1
- package/src/gjc-runtime/session-resolution.ts +38 -6
- package/src/gjc-runtime/session-state-lock.ts +59 -14
- package/src/gjc-runtime/state-runtime.ts +23 -19
- package/src/gjc-runtime/state-writer.ts +109 -5
- package/src/gjc-runtime/tmux-sessions.ts +93 -13
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
- package/src/gjc-runtime/ultragoal-guard.ts +9 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
- package/src/harness-control-plane/finalize.ts +11 -0
- package/src/harness-control-plane/receipt-spool.ts +105 -28
- package/src/harness-control-plane/storage.ts +124 -6
- package/src/hindsight/content.ts +116 -16
- package/src/hindsight/mental-models.ts +22 -10
- package/src/hindsight/state.ts +7 -4
- package/src/hooks/native-skill-hook.ts +17 -12
- package/src/internal-urls/artifact-protocol.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +23 -22
- package/src/internal-urls/local-protocol.ts +167 -8
- package/src/internal-urls/types.ts +2 -0
- package/src/lsp/client.ts +2 -1
- package/src/lsp/clients/biome-client.ts +2 -0
- package/src/lsp/clients/swiftlint-client.ts +2 -0
- package/src/lsp/index.ts +29 -0
- package/src/lsp/workspace-path.ts +23 -0
- package/src/main.ts +330 -92
- package/src/memories/index.ts +25 -2
- package/src/migrate/action-planner.ts +574 -2
- package/src/migrate/adapters/claude-code.ts +162 -5
- package/src/migrate/adapters/codex.ts +77 -7
- package/src/migrate/adapters/index.ts +258 -5
- package/src/migrate/adapters/opencode.ts +10 -1
- package/src/migrate/executor.ts +222 -7
- package/src/migrate/hook-module.ts +193 -0
- package/src/migrate/report.ts +63 -2
- package/src/migrate/types.ts +84 -4
- package/src/modes/DESIGN.md +51 -0
- package/src/modes/acp/acp-agent.ts +10 -1
- package/src/modes/acp/permission-mode.ts +18 -1
- package/src/modes/components/agent-dashboard.ts +20 -4
- package/src/modes/components/assistant-message.ts +55 -1
- package/src/modes/components/customization/customization-dashboard.ts +7 -0
- package/src/modes/components/customization/import-wizard.ts +29 -3
- package/src/modes/components/model-selector.ts +157 -103
- package/src/modes/components/settings-selector.ts +103 -37
- package/src/modes/components/welcome.ts +3 -1
- package/src/modes/controllers/command-controller.ts +300 -9
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +31 -1
- package/src/modes/controllers/goal-mode-controller.ts +79 -10
- package/src/modes/controllers/input-controller.ts +2 -0
- package/src/modes/controllers/plan-mode-controller.ts +66 -11
- package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
- package/src/modes/controllers/selector-controller.ts +76 -7
- package/src/modes/interactive-mode.ts +123 -4
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +5 -1
- package/src/modes/theme/theme.ts +15 -0
- package/src/modes/utils/host-status.ts +21 -10
- package/src/prompts/agent-fragments/browser-aside.md +2 -2
- package/src/prompts/system/irc-incoming.md +3 -1
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/report-issue.md +13 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/runtime-credential-selector.ts +2 -2
- package/src/runtime-mcp/content-limits.ts +7 -0
- package/src/runtime-mcp/manager-pool.test.ts +5 -4
- package/src/runtime-mcp/manager.ts +40 -3
- package/src/runtime-mcp/oauth-flow.ts +32 -6
- package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
- package/src/runtime-mcp/project-host-values.ts +12 -8
- package/src/runtime-mcp/render.ts +16 -1
- package/src/runtime-mcp/tool-bridge.ts +194 -21
- package/src/runtime-mcp/transports/stdio.ts +2 -1
- package/src/sdk/broker/broker-exit.ts +10 -5
- package/src/sdk/broker/broker.ts +484 -85
- package/src/sdk/broker/daemon-entry.ts +338 -116
- package/src/sdk/broker/doctor-restart.ts +2 -1
- package/src/sdk/broker/ensure.ts +1214 -268
- package/src/sdk/broker/hop.ts +286 -0
- package/src/sdk/broker/lifecycle-ledger.ts +38 -11
- package/src/sdk/broker/lifecycle.ts +102 -6
- package/src/sdk/broker/lock-host-identity.ts +29 -0
- package/src/sdk/broker/session-index.ts +91 -16
- package/src/sdk/broker/transport.ts +17 -1
- package/src/sdk/bus/chat-command-policy.ts +16 -3
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +460 -35
- package/src/sdk/bus/reconciliation-store.ts +201 -8
- package/src/sdk/bus/session-control.ts +2 -0
- package/src/sdk/bus/slack-daemon.ts +105 -9
- package/src/sdk/bus/telegram-daemon-contract.ts +9 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -2
- package/src/sdk/bus/threaded-render.ts +20 -1
- package/src/sdk/cli/session-cli.ts +23 -6
- package/src/sdk/host/control/dispatch.ts +13 -1
- package/src/sdk/host/control/operations.ts +10 -1
- package/src/sdk/host/events.ts +67 -4
- package/src/sdk/host/host.ts +8 -2
- package/src/sdk/host/prompt-image-upload.ts +438 -0
- package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
- package/src/sdk/host/session-runtime.test.ts +1737 -129
- package/src/sdk/host/session-runtime.ts +684 -83
- package/src/sdk/host/surface-policy.ts +4 -0
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
- package/src/sdk/host/websocket-transport.ts +19 -7
- package/src/sdk/lifecycle/service.ts +15 -1
- package/src/sdk/prompt-deadline-flush.ts +78 -5
- package/src/sdk/prompt-deadline-manager.ts +68 -14
- package/src/sdk/prompt-status.ts +6 -4
- package/src/sdk/protocol/adapter-validation.ts +81 -12
- package/src/sdk/protocol/operation-inventory.generated.json +261 -2
- package/src/sdk/protocol/operation-registry.ts +33 -3
- package/src/sdk/reconciliation-extensions.ts +1 -0
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +237 -97
- package/src/session/agent-session.ts +2726 -479
- package/src/session/history-storage.ts +182 -12
- package/src/session/internal/managed-session-scope.ts +173 -57
- package/src/session/internal/managed-session-storage.ts +85 -12
- package/src/session/internal/native-publish-outcome.ts +5 -1
- package/src/session/messages.ts +47 -6
- package/src/session/session-manager.ts +326 -19
- package/src/session/startup-auth-config.ts +116 -8
- package/src/session/streaming-output.ts +262 -4
- package/src/session/terminal-abort.ts +14 -2
- package/src/session-import/redact.ts +53 -2
- package/src/session-import/service.ts +1 -0
- package/src/skill-state/workflow-mutation-guard.ts +2145 -206
- package/src/slash-commands/builtin-registry.ts +21 -6
- package/src/slash-commands/helpers/credential-switch.ts +10 -5
- package/src/slash-commands/helpers/mcp.ts +11 -3
- package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
- package/src/ssh/ssh-executor.ts +9 -2
- package/src/stt/recorder.ts +26 -9
- package/src/system-prompt.ts +61 -16
- package/src/task/executor.ts +115 -20
- package/src/task/index.ts +389 -175
- package/src/task/role-body.ts +84 -0
- package/src/task/token-log-mode.test.ts +162 -0
- package/src/task/token-log.ts +91 -2
- package/src/tools/archive-reader.ts +650 -37
- package/src/tools/ask.ts +1 -0
- package/src/tools/ast-edit-write-path.ts +64 -0
- package/src/tools/ast-edit.ts +18 -1
- package/src/tools/ast-grep.ts +7 -2
- package/src/tools/atomic-file-write.ts +46 -15
- package/src/tools/auto-generated-guard.ts +15 -3
- package/src/tools/awaiting-input.ts +114 -0
- package/src/tools/bash-allowed-prefixes.ts +214 -6
- package/src/tools/bash-interactive.ts +14 -2
- package/src/tools/bash-skill-urls.ts +58 -5
- package/src/tools/bash.ts +113 -26
- package/src/tools/browser/tab-worker.ts +39 -13
- package/src/tools/browser.ts +17 -7
- package/src/tools/cron.ts +10 -1
- package/src/tools/debug.ts +16 -8
- package/src/tools/descriptors.test.ts +44 -0
- package/src/tools/descriptors.ts +50 -6
- package/src/tools/eval.ts +15 -0
- package/src/tools/fetch.ts +93 -4
- package/src/tools/gh.ts +27 -12
- package/src/tools/image-gen.ts +20 -6
- package/src/tools/index.ts +2 -0
- package/src/tools/irc.ts +5 -3
- package/src/tools/monitor.ts +9 -1
- package/src/tools/output-meta.ts +74 -5
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +26 -37
- package/src/tools/recipe/runner.ts +23 -12
- package/src/tools/render-utils.ts +2 -2
- package/src/tools/report-issue.ts +184 -0
- package/src/tools/search.ts +3 -2
- package/src/tools/ssh.ts +274 -8
- package/src/tools/subagent-render.ts +114 -12
- package/src/tools/subagent.ts +118 -5
- package/src/tools/tmux-self-injection-guard.ts +3301 -122
- package/src/tools/todo-write.ts +41 -3
- package/src/tools/tool-catalog.generated.ts +32 -4
- package/src/tools/ultragoal-ask-guard.ts +13 -1
- package/src/tools/write.ts +42 -3
- package/src/tui/tree-list.ts +15 -1
- package/src/utils/commit-message-generator.ts +12 -4
- package/src/utils/external-editor.ts +21 -7
- package/src/utils/file-mentions.ts +75 -7
- package/src/utils/git.ts +38 -7
- package/src/utils/image-limits.ts +8 -0
- package/src/utils/image-loading.ts +1 -1
- package/src/utils/open.ts +73 -4
- package/src/utils/pasted-image-loading.ts +9 -8
- package/src/utils/prompt-suggestion.ts +5 -4
- package/src/utils/shell-snapshot.ts +9 -1
- package/src/utils/title-generator.ts +72 -22
- package/src/web/scrapers/index.ts +3 -1
- package/src/web/scrapers/types.ts +8 -0
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/index.ts +2 -1
- package/src/web/search/providers/codex.ts +29 -32
- package/src/web/search/providers/insane.ts +91 -28
- package/src/web/search/providers/searxng.ts +62 -10
package/src/migrate/executor.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import * as fsSync from "node:fs";
|
|
10
10
|
import * as fs from "node:fs/promises";
|
|
11
11
|
import * as path from "node:path";
|
|
12
|
+
import { getTrustedHomeDir } from "@gajae-code/utils";
|
|
12
13
|
import { upsertMCPServer } from "../runtime-mcp/config-writer";
|
|
13
14
|
import type { MigrateAction } from "./types";
|
|
14
15
|
|
|
@@ -19,17 +20,23 @@ function trustedSkillDestinationBase(destination: string): string {
|
|
|
19
20
|
return path.basename(scopeRoot) === ".gjc" ? path.dirname(scopeRoot) : scopeRoot;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
async function ensureRealDirectoryPathNoFollow(
|
|
23
|
+
async function ensureRealDirectoryPathNoFollow(
|
|
24
|
+
directory: string,
|
|
25
|
+
trustedBase: string,
|
|
26
|
+
label = "skill destination",
|
|
27
|
+
createMissing = true,
|
|
28
|
+
mode?: number,
|
|
29
|
+
): Promise<void> {
|
|
23
30
|
const resolvedBase = path.resolve(trustedBase);
|
|
24
31
|
const resolvedDirectory = path.resolve(directory);
|
|
25
32
|
const relative = path.relative(resolvedBase, resolvedDirectory);
|
|
26
33
|
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
27
|
-
throw new Error(
|
|
34
|
+
throw new Error(`${label} escapes trusted base: ${resolvedDirectory}`);
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
const baseStat = await fs.stat(resolvedBase);
|
|
31
38
|
if (!baseStat.isDirectory()) {
|
|
32
|
-
throw new Error(
|
|
39
|
+
throw new Error(`${label} base is not a directory: ${resolvedBase}`);
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
let current = resolvedBase;
|
|
@@ -38,14 +45,17 @@ async function ensureRealDirectoryPathNoFollow(directory: string, trustedBase: s
|
|
|
38
45
|
try {
|
|
39
46
|
const stat = await fs.lstat(current);
|
|
40
47
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
41
|
-
throw new Error(
|
|
48
|
+
throw new Error(`${label} ancestor is not a real directory: ${current}`);
|
|
42
49
|
}
|
|
43
50
|
} catch (error) {
|
|
44
51
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
45
|
-
|
|
52
|
+
// A missing project `.gjc` is created by the MCP writer (mode 0o700).
|
|
53
|
+
// Do not mkdir here, or that mode is lost. An existing symlink still fails above.
|
|
54
|
+
if (!createMissing) return;
|
|
55
|
+
await fs.mkdir(current, mode === undefined ? undefined : { mode });
|
|
46
56
|
const stat = await fs.lstat(current);
|
|
47
57
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
48
|
-
throw new Error(
|
|
58
|
+
throw new Error(`${label} ancestor is not a real directory: ${current}`);
|
|
49
59
|
}
|
|
50
60
|
}
|
|
51
61
|
}
|
|
@@ -66,7 +76,161 @@ async function writeSkillFileNoFollow(destination: string, content: string): Pro
|
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
|
|
69
|
-
|
|
79
|
+
async function ensureInstructionDirectory(
|
|
80
|
+
directory: string,
|
|
81
|
+
project: boolean,
|
|
82
|
+
label = "instruction destination",
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
let trustedBase = path.dirname(directory);
|
|
85
|
+
if (!project) {
|
|
86
|
+
const home = path.resolve(getTrustedHomeDir());
|
|
87
|
+
const resolvedDirectory = path.resolve(directory);
|
|
88
|
+
const relative = path.relative(home, resolvedDirectory);
|
|
89
|
+
const isWithinHome =
|
|
90
|
+
relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
91
|
+
if (isWithinHome) {
|
|
92
|
+
trustedBase = home;
|
|
93
|
+
} else {
|
|
94
|
+
let current = path.dirname(resolvedDirectory);
|
|
95
|
+
while (true) {
|
|
96
|
+
try {
|
|
97
|
+
const stat = await fs.lstat(current);
|
|
98
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
99
|
+
throw new Error(`user ${label} ancestor is not a real directory: ${current}`);
|
|
100
|
+
}
|
|
101
|
+
trustedBase = current;
|
|
102
|
+
break;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
105
|
+
const parent = path.dirname(current);
|
|
106
|
+
if (parent === current) throw error;
|
|
107
|
+
current = parent;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
await ensureRealDirectoryPathNoFollow(directory, trustedBase, label, true, 0o700);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function writeInstructionFileNoFollow(destination: string, content: string, project: boolean): Promise<void> {
|
|
116
|
+
await ensureInstructionDirectory(path.dirname(destination), project);
|
|
117
|
+
const handle = await fs.open(
|
|
118
|
+
destination,
|
|
119
|
+
fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_TRUNC | fsSync.constants.O_NOFOLLOW,
|
|
120
|
+
0o666,
|
|
121
|
+
);
|
|
122
|
+
try {
|
|
123
|
+
await handle.writeFile(content, "utf-8");
|
|
124
|
+
} finally {
|
|
125
|
+
await handle.close();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function writeAgentFileNoFollow(
|
|
130
|
+
destination: string,
|
|
131
|
+
content: string,
|
|
132
|
+
project: boolean,
|
|
133
|
+
createOnly: boolean,
|
|
134
|
+
): Promise<void> {
|
|
135
|
+
const destinationDirectory = path.dirname(destination);
|
|
136
|
+
if (project) {
|
|
137
|
+
const agentsRoot = path.dirname(destinationDirectory);
|
|
138
|
+
const agentRoot = path.dirname(agentsRoot);
|
|
139
|
+
await ensureRealDirectoryPathNoFollow(agentRoot, path.dirname(agentRoot), "agent destination", true, 0o700);
|
|
140
|
+
await ensureRealDirectoryPathNoFollow(agentsRoot, agentRoot, "agent destination", true, 0o700);
|
|
141
|
+
await ensureRealDirectoryPathNoFollow(destinationDirectory, agentsRoot, "agent destination", true, 0o700);
|
|
142
|
+
} else {
|
|
143
|
+
await ensureInstructionDirectory(destinationDirectory, false, "agent destination");
|
|
144
|
+
}
|
|
145
|
+
const handle = await fs.open(
|
|
146
|
+
destination,
|
|
147
|
+
createOnly
|
|
148
|
+
? fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL | fsSync.constants.O_NOFOLLOW
|
|
149
|
+
: fsSync.constants.O_WRONLY |
|
|
150
|
+
fsSync.constants.O_CREAT |
|
|
151
|
+
fsSync.constants.O_TRUNC |
|
|
152
|
+
fsSync.constants.O_NOFOLLOW,
|
|
153
|
+
0o666,
|
|
154
|
+
);
|
|
155
|
+
try {
|
|
156
|
+
await handle.writeFile(content, "utf-8");
|
|
157
|
+
} finally {
|
|
158
|
+
await handle.close();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Generated hook modules live beside the agent's own hooks, so they use the same no-follow rules. */
|
|
163
|
+
async function writeHookModuleNoFollow(destination: string, content: string, createOnly: boolean): Promise<void> {
|
|
164
|
+
await ensureInstructionDirectory(path.dirname(destination), false, "hook destination");
|
|
165
|
+
const handle = await fs.open(
|
|
166
|
+
destination,
|
|
167
|
+
createOnly
|
|
168
|
+
? fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL | fsSync.constants.O_NOFOLLOW
|
|
169
|
+
: fsSync.constants.O_WRONLY |
|
|
170
|
+
fsSync.constants.O_CREAT |
|
|
171
|
+
fsSync.constants.O_TRUNC |
|
|
172
|
+
fsSync.constants.O_NOFOLLOW,
|
|
173
|
+
0o666,
|
|
174
|
+
);
|
|
175
|
+
try {
|
|
176
|
+
await handle.writeFile(content, "utf-8");
|
|
177
|
+
} finally {
|
|
178
|
+
await handle.close();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function writeCommandFileNoFollow(
|
|
183
|
+
destination: string,
|
|
184
|
+
content: string,
|
|
185
|
+
name: string,
|
|
186
|
+
project: boolean,
|
|
187
|
+
createOnly: boolean,
|
|
188
|
+
): Promise<void> {
|
|
189
|
+
const destinationDirectory = path.dirname(destination);
|
|
190
|
+
if (project) {
|
|
191
|
+
const nameParts = name.split(":");
|
|
192
|
+
let commandsRoot = destinationDirectory;
|
|
193
|
+
for (let index = 1; index < nameParts.length; index++) commandsRoot = path.dirname(commandsRoot);
|
|
194
|
+
const agentRoot = path.dirname(commandsRoot);
|
|
195
|
+
await ensureRealDirectoryPathNoFollow(
|
|
196
|
+
agentRoot,
|
|
197
|
+
path.dirname(agentRoot),
|
|
198
|
+
"slash command destination",
|
|
199
|
+
true,
|
|
200
|
+
0o700,
|
|
201
|
+
);
|
|
202
|
+
await ensureRealDirectoryPathNoFollow(commandsRoot, agentRoot, "slash command destination", true, 0o700);
|
|
203
|
+
await ensureRealDirectoryPathNoFollow(
|
|
204
|
+
destinationDirectory,
|
|
205
|
+
commandsRoot,
|
|
206
|
+
"slash command destination",
|
|
207
|
+
true,
|
|
208
|
+
0o700,
|
|
209
|
+
);
|
|
210
|
+
} else {
|
|
211
|
+
await ensureInstructionDirectory(destinationDirectory, false, "slash command destination");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const handle = await fs.open(
|
|
215
|
+
destination,
|
|
216
|
+
fsSync.constants.O_WRONLY |
|
|
217
|
+
fsSync.constants.O_CREAT |
|
|
218
|
+
fsSync.constants.O_TRUNC |
|
|
219
|
+
fsSync.constants.O_NOFOLLOW |
|
|
220
|
+
(createOnly ? fsSync.constants.O_EXCL : 0),
|
|
221
|
+
0o666,
|
|
222
|
+
);
|
|
223
|
+
try {
|
|
224
|
+
await handle.writeFile(content, "utf-8");
|
|
225
|
+
} finally {
|
|
226
|
+
await handle.close();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function executeActions(
|
|
231
|
+
actions: MigrateAction[],
|
|
232
|
+
options?: { project?: boolean },
|
|
233
|
+
): Promise<MigrateAction[]> {
|
|
70
234
|
const out: MigrateAction[] = [];
|
|
71
235
|
for (const action of actions) {
|
|
72
236
|
if (action.operation !== "create" && action.operation !== "update") {
|
|
@@ -75,6 +239,17 @@ export async function executeActions(actions: MigrateAction[]): Promise<MigrateA
|
|
|
75
239
|
}
|
|
76
240
|
try {
|
|
77
241
|
if (action.type === "mcp" && action.mcp && action.name && action.destination) {
|
|
242
|
+
// `project` is the migrate scope, not a guess from the directory name.
|
|
243
|
+
// A user agent dir may itself be named `.gjc`.
|
|
244
|
+
if (options?.project) {
|
|
245
|
+
const directory = path.dirname(action.destination);
|
|
246
|
+
await ensureRealDirectoryPathNoFollow(
|
|
247
|
+
directory,
|
|
248
|
+
path.dirname(path.resolve(directory)),
|
|
249
|
+
"mcp destination",
|
|
250
|
+
false,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
78
253
|
await upsertMCPServer(action.destination, action.name, action.mcp.config, {
|
|
79
254
|
force: action.operation === "update",
|
|
80
255
|
});
|
|
@@ -82,10 +257,50 @@ export async function executeActions(actions: MigrateAction[]): Promise<MigrateA
|
|
|
82
257
|
} else if (action.type === "skill" && action.skill && action.destination) {
|
|
83
258
|
await writeSkillFileNoFollow(action.destination, action.skill.content);
|
|
84
259
|
out.push(action);
|
|
260
|
+
} else if (action.type === "instruction" && action.instruction && action.destination) {
|
|
261
|
+
await writeInstructionFileNoFollow(
|
|
262
|
+
action.destination,
|
|
263
|
+
action.instruction.content,
|
|
264
|
+
options?.project ?? false,
|
|
265
|
+
);
|
|
266
|
+
out.push(action);
|
|
267
|
+
} else if (action.type === "hook" && action.hook && action.destination) {
|
|
268
|
+
await writeHookModuleNoFollow(action.destination, action.hook.content, action.operation === "create");
|
|
269
|
+
out.push(action);
|
|
270
|
+
} else if (action.type === "agent" && action.agent && action.destination) {
|
|
271
|
+
await writeAgentFileNoFollow(
|
|
272
|
+
action.destination,
|
|
273
|
+
action.agent.content,
|
|
274
|
+
options?.project ?? false,
|
|
275
|
+
action.operation === "create",
|
|
276
|
+
);
|
|
277
|
+
out.push(action);
|
|
278
|
+
} else if (action.type === "command" && action.command && action.destination && action.name) {
|
|
279
|
+
await writeCommandFileNoFollow(
|
|
280
|
+
action.destination,
|
|
281
|
+
action.command.content,
|
|
282
|
+
action.name,
|
|
283
|
+
options?.project ?? false,
|
|
284
|
+
action.operation === "create",
|
|
285
|
+
);
|
|
286
|
+
out.push(action);
|
|
85
287
|
} else {
|
|
86
288
|
out.push(action);
|
|
87
289
|
}
|
|
88
290
|
} catch (error) {
|
|
291
|
+
if (
|
|
292
|
+
(action.type === "command" || action.type === "agent") &&
|
|
293
|
+
action.operation === "create" &&
|
|
294
|
+
(error as NodeJS.ErrnoException).code === "EEXIST"
|
|
295
|
+
) {
|
|
296
|
+
out.push({
|
|
297
|
+
...action,
|
|
298
|
+
operation: "skip",
|
|
299
|
+
status: "skipped_exists",
|
|
300
|
+
reason: "destination appeared after planning; refusing to overwrite it",
|
|
301
|
+
});
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
89
304
|
out.push({
|
|
90
305
|
...action,
|
|
91
306
|
operation: "fail",
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render a gjc hook module from imported foreign hook commands.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code hooks are shell commands: the runtime writes a JSON payload to
|
|
5
|
+
* stdin and reads the exit code. `PreToolUse` blocks on exit 2 with stderr as the
|
|
6
|
+
* reason; every other event is observational. gjc hooks are TypeScript modules
|
|
7
|
+
* that subscribe to events in-process, so the importer reproduces the foreign
|
|
8
|
+
* contract rather than rewriting the commands.
|
|
9
|
+
*
|
|
10
|
+
* Only events whose semantics are unambiguous are translated. The payload keeps
|
|
11
|
+
* the foreign field names so an existing script that reads its JSON keeps working;
|
|
12
|
+
* fields this host cannot supply are omitted instead of guessed, which the module
|
|
13
|
+
* header states.
|
|
14
|
+
*/
|
|
15
|
+
import type { HookCandidate, MigrateSource } from "./types";
|
|
16
|
+
|
|
17
|
+
/** Foreign event -> gjc hook event. Blocking events are marked separately. */
|
|
18
|
+
const EVENT_MAP: Record<string, { event: string; blocking: boolean }> = {
|
|
19
|
+
PreToolUse: { event: "tool_call", blocking: true },
|
|
20
|
+
PostToolUse: { event: "tool_result", blocking: false },
|
|
21
|
+
SessionStart: { event: "session_start", blocking: false },
|
|
22
|
+
Stop: { event: "turn_end", blocking: false },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function isSupportedHookEvent(event: string): boolean {
|
|
26
|
+
return event in EVENT_MAP;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Foreign events this importer translates, for user-facing messages. */
|
|
30
|
+
export const SUPPORTED_HOOK_EVENTS: readonly string[] = Object.keys(EVENT_MAP);
|
|
31
|
+
|
|
32
|
+
interface RenderedHook {
|
|
33
|
+
event: string;
|
|
34
|
+
blocking: boolean;
|
|
35
|
+
matcher?: string;
|
|
36
|
+
command: string;
|
|
37
|
+
sourceEvent: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function constantName(event: string): string {
|
|
41
|
+
return event.replace(/[^a-z_]/g, "_").toUpperCase();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function renderHookLiteral(hook: RenderedHook): string {
|
|
45
|
+
const parts = [`event: ${JSON.stringify(hook.sourceEvent)}`, `command: ${JSON.stringify(hook.command)}`];
|
|
46
|
+
if (hook.matcher) parts.push(`matcher: ${JSON.stringify(hook.matcher)}`);
|
|
47
|
+
return `\t{ ${parts.join(", ")} },`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Build the module source. The result is deterministic for a given candidate
|
|
52
|
+
* list, so a dry-run report and a live write describe the same file.
|
|
53
|
+
*/
|
|
54
|
+
export function renderHookModule(source: MigrateSource, candidates: readonly HookCandidate[]): string {
|
|
55
|
+
const hooks: RenderedHook[] = [];
|
|
56
|
+
for (const candidate of candidates) {
|
|
57
|
+
const mapped = EVENT_MAP[candidate.event];
|
|
58
|
+
if (!mapped) continue;
|
|
59
|
+
hooks.push({
|
|
60
|
+
event: mapped.event,
|
|
61
|
+
blocking: mapped.blocking,
|
|
62
|
+
matcher: candidate.matcher,
|
|
63
|
+
command: candidate.command,
|
|
64
|
+
sourceEvent: candidate.event,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const byEvent = new Map<string, RenderedHook[]>();
|
|
68
|
+
for (const hook of hooks) {
|
|
69
|
+
const list = byEvent.get(hook.event) ?? [];
|
|
70
|
+
list.push(hook);
|
|
71
|
+
byEvent.set(hook.event, list);
|
|
72
|
+
}
|
|
73
|
+
const eventNames = [...byEvent.keys()].sort();
|
|
74
|
+
const blocks = eventNames
|
|
75
|
+
.map(event => {
|
|
76
|
+
const entries = (byEvent.get(event) ?? []).map(renderHookLiteral).join("\n");
|
|
77
|
+
return `const ${constantName(event)}_HOOKS: readonly ImportedHook[] = [\n${entries}\n];`;
|
|
78
|
+
})
|
|
79
|
+
.join("\n\n");
|
|
80
|
+
const registrations = eventNames
|
|
81
|
+
.map(event => {
|
|
82
|
+
const blocking = byEvent.get(event)?.some(hook => hook.blocking) ?? false;
|
|
83
|
+
const register = blocking ? "registerBlocking" : "registerObservational";
|
|
84
|
+
return `\t${register}(api, ${JSON.stringify(event)}, ${constantName(event)}_HOOKS);`;
|
|
85
|
+
})
|
|
86
|
+
.join("\n");
|
|
87
|
+
|
|
88
|
+
return `/**
|
|
89
|
+
* Generated by \`gjc migrate --from ${source}\`. Re-run with --force to regenerate.
|
|
90
|
+
*
|
|
91
|
+
* These hooks were shell commands in ${source}. Each command receives a JSON
|
|
92
|
+
* payload on stdin under the origin's field names; fields this runtime cannot
|
|
93
|
+
* supply (session id, transcript path) are omitted rather than invented.
|
|
94
|
+
* \`PreToolUse\` blocks the tool when the command exits 2 and uses its stderr as
|
|
95
|
+
* the reason, which is the origin contract; the other events only run the
|
|
96
|
+
* command.
|
|
97
|
+
*/
|
|
98
|
+
import { spawn } from "node:child_process";
|
|
99
|
+
|
|
100
|
+
interface ImportedHook {
|
|
101
|
+
event: string;
|
|
102
|
+
command: string;
|
|
103
|
+
matcher?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface HookCommandResult {
|
|
107
|
+
code: number;
|
|
108
|
+
stdout: string;
|
|
109
|
+
stderr: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface HookApi {
|
|
113
|
+
on(event: string, handler: (payload: unknown, context: { cwd?: string }) => unknown): void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
${blocks}
|
|
117
|
+
|
|
118
|
+
function matches(matcher: string | undefined, toolName: string | undefined): boolean {
|
|
119
|
+
if (!matcher || !matcher.trim()) return true;
|
|
120
|
+
if (!toolName) return false;
|
|
121
|
+
try {
|
|
122
|
+
return new RegExp(\`^(?:\${matcher})$\`).test(toolName);
|
|
123
|
+
} catch {
|
|
124
|
+
return matcher === toolName;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function shellCommand(command: string): string[] {
|
|
129
|
+
return process.platform === "win32"
|
|
130
|
+
? [process.env.ComSpec ?? "cmd.exe", "/d", "/s", "/c", command]
|
|
131
|
+
: ["/bin/bash", "-lc", command];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function runHook(hook: ImportedHook, payload: unknown): Promise<HookCommandResult> {
|
|
135
|
+
const [file, ...args] = shellCommand(hook.command);
|
|
136
|
+
const child = spawn(file, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
137
|
+
child.stdin?.end(JSON.stringify(payload));
|
|
138
|
+
let stdout = "";
|
|
139
|
+
let stderr = "";
|
|
140
|
+
child.stdout?.on("data", chunk => {
|
|
141
|
+
stdout += String(chunk);
|
|
142
|
+
});
|
|
143
|
+
child.stderr?.on("data", chunk => {
|
|
144
|
+
stderr += String(chunk);
|
|
145
|
+
});
|
|
146
|
+
const code = await new Promise<number>(resolve => {
|
|
147
|
+
child.on("error", () => resolve(1));
|
|
148
|
+
child.on("close", value => resolve(value ?? 1));
|
|
149
|
+
});
|
|
150
|
+
return { code, stdout, stderr };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function registerBlocking(api: HookApi, event: string, hooks: readonly ImportedHook[]): void {
|
|
154
|
+
api.on(event, async (payload, context) => {
|
|
155
|
+
const record = payload as { toolName?: string; input?: unknown };
|
|
156
|
+
for (const hook of hooks) {
|
|
157
|
+
if (!matches(hook.matcher, record?.toolName)) continue;
|
|
158
|
+
const result = await runHook(hook, {
|
|
159
|
+
hook_event_name: hook.event,
|
|
160
|
+
tool_name: record?.toolName,
|
|
161
|
+
tool_input: record?.input,
|
|
162
|
+
cwd: context?.cwd,
|
|
163
|
+
});
|
|
164
|
+
if (result.code === 2) {
|
|
165
|
+
return { block: true, reason: result.stderr.trim() || \`blocked by imported \${hook.event} hook\` };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return undefined;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function registerObservational(api: HookApi, event: string, hooks: readonly ImportedHook[]): void {
|
|
173
|
+
api.on(event, async (payload, context) => {
|
|
174
|
+
const record = payload as { toolName?: string; input?: unknown; turnIndex?: number };
|
|
175
|
+
for (const hook of hooks) {
|
|
176
|
+
if (event === "tool_result" && !matches(hook.matcher, record?.toolName)) continue;
|
|
177
|
+
await runHook(hook, {
|
|
178
|
+
hook_event_name: hook.event,
|
|
179
|
+
tool_name: record?.toolName,
|
|
180
|
+
tool_input: record?.input,
|
|
181
|
+
turn_index: record?.turnIndex,
|
|
182
|
+
cwd: context?.cwd,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return undefined;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export default (api: HookApi): void => {
|
|
190
|
+
${registrations}
|
|
191
|
+
};
|
|
192
|
+
`;
|
|
193
|
+
}
|
package/src/migrate/report.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import {
|
|
8
8
|
emptyStatusCounts,
|
|
9
9
|
isFailureStatus,
|
|
10
|
+
type ManualAction,
|
|
10
11
|
type MigrateAction,
|
|
11
12
|
type MigrateDestinations,
|
|
12
13
|
type MigrateReport,
|
|
@@ -20,6 +21,7 @@ export interface BuildReportInput {
|
|
|
20
21
|
warnings: MigrateWarning[];
|
|
21
22
|
sources: MigrateSource[];
|
|
22
23
|
destinations: MigrateDestinations;
|
|
24
|
+
manualActions: ManualAction[];
|
|
23
25
|
dryRun: boolean;
|
|
24
26
|
project: boolean;
|
|
25
27
|
force: boolean;
|
|
@@ -27,7 +29,15 @@ export interface BuildReportInput {
|
|
|
27
29
|
|
|
28
30
|
export function buildReport(input: BuildReportInput): MigrateReport {
|
|
29
31
|
const total = emptyStatusCounts();
|
|
30
|
-
const byType = {
|
|
32
|
+
const byType = {
|
|
33
|
+
mcp: emptyStatusCounts(),
|
|
34
|
+
skill: emptyStatusCounts(),
|
|
35
|
+
command: emptyStatusCounts(),
|
|
36
|
+
agent: emptyStatusCounts(),
|
|
37
|
+
hook: emptyStatusCounts(),
|
|
38
|
+
instruction: emptyStatusCounts(),
|
|
39
|
+
source: emptyStatusCounts(),
|
|
40
|
+
};
|
|
31
41
|
const bySource = {} as Record<MigrateSource, StatusCounts>;
|
|
32
42
|
for (const source of input.sources) bySource[source] = emptyStatusCounts();
|
|
33
43
|
|
|
@@ -47,6 +57,7 @@ export function buildReport(input: BuildReportInput): MigrateReport {
|
|
|
47
57
|
sources: input.sources,
|
|
48
58
|
destinations: input.destinations,
|
|
49
59
|
summary: { total, byType, bySource },
|
|
60
|
+
manualActions: input.manualActions,
|
|
50
61
|
actions: input.actions.map(action => ({
|
|
51
62
|
source: action.source,
|
|
52
63
|
type: action.type,
|
|
@@ -75,10 +86,52 @@ export function renderHuman(report: MigrateReport): string {
|
|
|
75
86
|
const mode = report.dryRun ? " (dry-run)" : "";
|
|
76
87
|
lines.push(`gjc migrate${mode}: ${report.ok ? "ok" : "completed with failures"}`);
|
|
77
88
|
lines.push(`Sources: ${report.sources.join(", ") || "none"}`);
|
|
78
|
-
lines.push(
|
|
89
|
+
lines.push(
|
|
90
|
+
`Destination: mcp=${report.destinations.mcpConfigPath} skills=${report.destinations.skillsDir} commands=${report.destinations.commandsDir} agents=${report.destinations.agentsDir} hooks=${report.destinations.hooksDir} instructions=${report.destinations.instructionsPath}`,
|
|
91
|
+
);
|
|
79
92
|
lines.push("");
|
|
80
93
|
lines.push(`MCP: ${summarizeCounts(report.summary.byType.mcp)}`);
|
|
81
94
|
lines.push(`Skills: ${summarizeCounts(report.summary.byType.skill)}`);
|
|
95
|
+
lines.push(`Commands: ${summarizeCounts(report.summary.byType.command)}`);
|
|
96
|
+
lines.push(`Agents: ${summarizeCounts(report.summary.byType.agent)}`);
|
|
97
|
+
lines.push(`Hooks: ${summarizeCounts(report.summary.byType.hook)}`);
|
|
98
|
+
lines.push(`Instructions: ${summarizeCounts(report.summary.byType.instruction)}`);
|
|
99
|
+
const commandActions = report.actions.filter(action => action.type === "command");
|
|
100
|
+
if (commandActions.length > 0) {
|
|
101
|
+
lines.push("Slash commands:");
|
|
102
|
+
for (const action of commandActions) {
|
|
103
|
+
lines.push(
|
|
104
|
+
` - [${action.source}] /${action.name ?? ""}: ${action.status}${action.reason ? ` (${action.reason})` : ""}`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const agentActions = report.actions.filter(action => action.type === "agent");
|
|
109
|
+
if (agentActions.length > 0) {
|
|
110
|
+
lines.push("Subagents:");
|
|
111
|
+
for (const action of agentActions) {
|
|
112
|
+
lines.push(
|
|
113
|
+
` - [${action.source}] ${action.name ?? ""}: ${action.status}${action.reason ? ` (${action.reason})` : ""}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const hookActions = report.actions.filter(action => action.type === "hook");
|
|
118
|
+
if (hookActions.length > 0) {
|
|
119
|
+
lines.push("Hooks:");
|
|
120
|
+
for (const action of hookActions) {
|
|
121
|
+
lines.push(
|
|
122
|
+
` - [${action.source}] ${action.name ?? ""}: ${action.status}${action.reason ? ` (${action.reason})` : ""}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const instructionActions = report.actions.filter(action => action.type === "instruction");
|
|
127
|
+
if (instructionActions.length > 0) {
|
|
128
|
+
lines.push("Instruction files:");
|
|
129
|
+
for (const action of instructionActions) {
|
|
130
|
+
lines.push(
|
|
131
|
+
` - [${action.source}] ${action.name ?? "AGENTS.md"}: ${action.status}${action.reason ? ` (${action.reason})` : ""}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
82
135
|
if (Object.values(report.summary.byType.source).some(n => n > 0)) {
|
|
83
136
|
lines.push(`Source: ${summarizeCounts(report.summary.byType.source)}`);
|
|
84
137
|
}
|
|
@@ -92,6 +145,14 @@ export function renderHuman(report: MigrateReport): string {
|
|
|
92
145
|
}
|
|
93
146
|
}
|
|
94
147
|
|
|
148
|
+
if (report.manualActions.length > 0) {
|
|
149
|
+
lines.push("");
|
|
150
|
+
lines.push("Manual actions:");
|
|
151
|
+
for (const m of report.manualActions) {
|
|
152
|
+
lines.push(` - [${m.source}] ${m.area}: ${m.detail}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
95
156
|
if (report.warnings.length > 0) {
|
|
96
157
|
lines.push("");
|
|
97
158
|
lines.push("Warnings:");
|