opencode-repos 0.3.0 → 0.3.2
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/index.ts +25 -11
- package/package.json +27 -27
- package/src/__tests__/git.test.ts +33 -2
- package/src/git.ts +32 -5
- package/.sisyphus/boulder.json +0 -8
- package/.sisyphus/notepads/opencode-repos/decisions.md +0 -15
- package/.sisyphus/notepads/opencode-repos/learnings.md +0 -384
- package/.sisyphus/plans/opencode-repos.md +0 -987
- package/.tmux-sessionizer +0 -8
- package/TODO.md +0 -3
- package/oh-my-opencode/.github/FUNDING.yml +0 -15
- package/oh-my-opencode/.github/ISSUE_TEMPLATE/bug_report.yml +0 -129
- package/oh-my-opencode/.github/ISSUE_TEMPLATE/config.yml +0 -8
- package/oh-my-opencode/.github/ISSUE_TEMPLATE/feature_request.yml +0 -100
- package/oh-my-opencode/.github/ISSUE_TEMPLATE/general.yml +0 -83
- package/oh-my-opencode/.github/assets/google.jpg +0 -0
- package/oh-my-opencode/.github/assets/hero.jpg +0 -0
- package/oh-my-opencode/.github/assets/indent.jpg +0 -0
- package/oh-my-opencode/.github/assets/microsoft.jpg +0 -0
- package/oh-my-opencode/.github/assets/omo.png +0 -0
- package/oh-my-opencode/.github/assets/orchestrator-atlas.png +0 -0
- package/oh-my-opencode/.github/assets/sisyphus.png +0 -0
- package/oh-my-opencode/.github/assets/sisyphuslabs.png +0 -0
- package/oh-my-opencode/.github/pull_request_template.md +0 -34
- package/oh-my-opencode/.github/workflows/ci.yml +0 -138
- package/oh-my-opencode/.github/workflows/cla.yml +0 -41
- package/oh-my-opencode/.github/workflows/lint-workflows.yml +0 -22
- package/oh-my-opencode/.github/workflows/publish.yml +0 -165
- package/oh-my-opencode/.github/workflows/sisyphus-agent.yml +0 -500
- package/oh-my-opencode/.opencode/background-tasks.json +0 -27
- package/oh-my-opencode/.opencode/command/get-unpublished-changes.md +0 -84
- package/oh-my-opencode/.opencode/command/omomomo.md +0 -37
- package/oh-my-opencode/.opencode/command/publish.md +0 -257
- package/oh-my-opencode/AGENTS.md +0 -179
- package/oh-my-opencode/CLA.md +0 -58
- package/oh-my-opencode/CONTRIBUTING.md +0 -268
- package/oh-my-opencode/LICENSE.md +0 -82
- package/oh-my-opencode/README.ja.md +0 -370
- package/oh-my-opencode/README.md +0 -376
- package/oh-my-opencode/README.zh-cn.md +0 -380
- package/oh-my-opencode/assets/oh-my-opencode.schema.json +0 -2171
- package/oh-my-opencode/bin/oh-my-opencode.js +0 -80
- package/oh-my-opencode/bin/platform.js +0 -38
- package/oh-my-opencode/bin/platform.test.ts +0 -148
- package/oh-my-opencode/bun.lock +0 -314
- package/oh-my-opencode/bunfig.toml +0 -2
- package/oh-my-opencode/docs/category-skill-guide.md +0 -200
- package/oh-my-opencode/docs/cli-guide.md +0 -272
- package/oh-my-opencode/docs/configurations.md +0 -654
- package/oh-my-opencode/docs/features.md +0 -550
- package/oh-my-opencode/docs/guide/installation.md +0 -288
- package/oh-my-opencode/docs/guide/overview.md +0 -97
- package/oh-my-opencode/docs/guide/understanding-orchestration-system.md +0 -445
- package/oh-my-opencode/docs/orchestration-guide.md +0 -152
- package/oh-my-opencode/docs/ultrawork-manifesto.md +0 -197
- package/oh-my-opencode/package.json +0 -89
- package/oh-my-opencode/packages/darwin-arm64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/darwin-arm64/package.json +0 -22
- package/oh-my-opencode/packages/darwin-x64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/darwin-x64/package.json +0 -22
- package/oh-my-opencode/packages/linux-arm64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/linux-arm64/package.json +0 -25
- package/oh-my-opencode/packages/linux-arm64-musl/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/linux-arm64-musl/package.json +0 -25
- package/oh-my-opencode/packages/linux-x64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/linux-x64/package.json +0 -25
- package/oh-my-opencode/packages/linux-x64-musl/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/linux-x64-musl/package.json +0 -25
- package/oh-my-opencode/packages/windows-x64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/windows-x64/package.json +0 -22
- package/oh-my-opencode/postinstall.mjs +0 -43
- package/oh-my-opencode/script/build-binaries.ts +0 -103
- package/oh-my-opencode/script/build-schema.ts +0 -28
- package/oh-my-opencode/script/generate-changelog.ts +0 -92
- package/oh-my-opencode/script/publish.ts +0 -344
- package/oh-my-opencode/signatures/cla.json +0 -676
- package/oh-my-opencode/src/agents/AGENTS.md +0 -67
- package/oh-my-opencode/src/agents/atlas.ts +0 -1383
- package/oh-my-opencode/src/agents/dynamic-agent-prompt-builder.ts +0 -400
- package/oh-my-opencode/src/agents/explore.ts +0 -122
- package/oh-my-opencode/src/agents/index.ts +0 -13
- package/oh-my-opencode/src/agents/librarian.ts +0 -326
- package/oh-my-opencode/src/agents/metis.ts +0 -315
- package/oh-my-opencode/src/agents/momus.test.ts +0 -57
- package/oh-my-opencode/src/agents/momus.ts +0 -444
- package/oh-my-opencode/src/agents/multimodal-looker.ts +0 -56
- package/oh-my-opencode/src/agents/oracle.ts +0 -122
- package/oh-my-opencode/src/agents/prometheus-prompt.test.ts +0 -22
- package/oh-my-opencode/src/agents/prometheus-prompt.ts +0 -1196
- package/oh-my-opencode/src/agents/sisyphus-junior.test.ts +0 -232
- package/oh-my-opencode/src/agents/sisyphus-junior.ts +0 -134
- package/oh-my-opencode/src/agents/sisyphus.ts +0 -633
- package/oh-my-opencode/src/agents/types.ts +0 -80
- package/oh-my-opencode/src/agents/utils.test.ts +0 -311
- package/oh-my-opencode/src/agents/utils.ts +0 -240
- package/oh-my-opencode/src/cli/AGENTS.md +0 -91
- package/oh-my-opencode/src/cli/config-manager.test.ts +0 -364
- package/oh-my-opencode/src/cli/config-manager.ts +0 -641
- package/oh-my-opencode/src/cli/doctor/checks/auth.test.ts +0 -114
- package/oh-my-opencode/src/cli/doctor/checks/auth.ts +0 -115
- package/oh-my-opencode/src/cli/doctor/checks/config.test.ts +0 -103
- package/oh-my-opencode/src/cli/doctor/checks/config.ts +0 -123
- package/oh-my-opencode/src/cli/doctor/checks/dependencies.test.ts +0 -152
- package/oh-my-opencode/src/cli/doctor/checks/dependencies.ts +0 -163
- package/oh-my-opencode/src/cli/doctor/checks/gh.test.ts +0 -151
- package/oh-my-opencode/src/cli/doctor/checks/gh.ts +0 -171
- package/oh-my-opencode/src/cli/doctor/checks/index.ts +0 -34
- package/oh-my-opencode/src/cli/doctor/checks/lsp.test.ts +0 -134
- package/oh-my-opencode/src/cli/doctor/checks/lsp.ts +0 -77
- package/oh-my-opencode/src/cli/doctor/checks/mcp.test.ts +0 -115
- package/oh-my-opencode/src/cli/doctor/checks/mcp.ts +0 -128
- package/oh-my-opencode/src/cli/doctor/checks/opencode.test.ts +0 -227
- package/oh-my-opencode/src/cli/doctor/checks/opencode.ts +0 -178
- package/oh-my-opencode/src/cli/doctor/checks/plugin.test.ts +0 -109
- package/oh-my-opencode/src/cli/doctor/checks/plugin.ts +0 -124
- package/oh-my-opencode/src/cli/doctor/checks/version.test.ts +0 -148
- package/oh-my-opencode/src/cli/doctor/checks/version.ts +0 -135
- package/oh-my-opencode/src/cli/doctor/constants.ts +0 -72
- package/oh-my-opencode/src/cli/doctor/formatter.test.ts +0 -218
- package/oh-my-opencode/src/cli/doctor/formatter.ts +0 -140
- package/oh-my-opencode/src/cli/doctor/index.ts +0 -11
- package/oh-my-opencode/src/cli/doctor/runner.test.ts +0 -153
- package/oh-my-opencode/src/cli/doctor/runner.ts +0 -132
- package/oh-my-opencode/src/cli/doctor/types.ts +0 -113
- package/oh-my-opencode/src/cli/get-local-version/formatter.ts +0 -66
- package/oh-my-opencode/src/cli/get-local-version/index.ts +0 -106
- package/oh-my-opencode/src/cli/get-local-version/types.ts +0 -14
- package/oh-my-opencode/src/cli/index.ts +0 -153
- package/oh-my-opencode/src/cli/install.ts +0 -523
- package/oh-my-opencode/src/cli/model-fallback.ts +0 -246
- package/oh-my-opencode/src/cli/run/completion.test.ts +0 -170
- package/oh-my-opencode/src/cli/run/completion.ts +0 -79
- package/oh-my-opencode/src/cli/run/events.test.ts +0 -155
- package/oh-my-opencode/src/cli/run/events.ts +0 -325
- package/oh-my-opencode/src/cli/run/index.ts +0 -2
- package/oh-my-opencode/src/cli/run/runner.ts +0 -159
- package/oh-my-opencode/src/cli/run/types.ts +0 -76
- package/oh-my-opencode/src/cli/types.ts +0 -40
- package/oh-my-opencode/src/config/index.ts +0 -26
- package/oh-my-opencode/src/config/schema.test.ts +0 -444
- package/oh-my-opencode/src/config/schema.ts +0 -339
- package/oh-my-opencode/src/features/AGENTS.md +0 -77
- package/oh-my-opencode/src/features/background-agent/concurrency.test.ts +0 -418
- package/oh-my-opencode/src/features/background-agent/concurrency.ts +0 -137
- package/oh-my-opencode/src/features/background-agent/index.ts +0 -3
- package/oh-my-opencode/src/features/background-agent/manager.test.ts +0 -1928
- package/oh-my-opencode/src/features/background-agent/manager.ts +0 -1335
- package/oh-my-opencode/src/features/background-agent/types.ts +0 -66
- package/oh-my-opencode/src/features/boulder-state/constants.ts +0 -13
- package/oh-my-opencode/src/features/boulder-state/index.ts +0 -3
- package/oh-my-opencode/src/features/boulder-state/storage.test.ts +0 -250
- package/oh-my-opencode/src/features/boulder-state/storage.ts +0 -150
- package/oh-my-opencode/src/features/boulder-state/types.ts +0 -26
- package/oh-my-opencode/src/features/builtin-commands/commands.ts +0 -89
- package/oh-my-opencode/src/features/builtin-commands/index.ts +0 -2
- package/oh-my-opencode/src/features/builtin-commands/templates/init-deep.ts +0 -300
- package/oh-my-opencode/src/features/builtin-commands/templates/ralph-loop.ts +0 -38
- package/oh-my-opencode/src/features/builtin-commands/templates/refactor.ts +0 -619
- package/oh-my-opencode/src/features/builtin-commands/templates/start-work.ts +0 -72
- package/oh-my-opencode/src/features/builtin-commands/types.ts +0 -9
- package/oh-my-opencode/src/features/builtin-skills/frontend-ui-ux/SKILL.md +0 -78
- package/oh-my-opencode/src/features/builtin-skills/git-master/SKILL.md +0 -1105
- package/oh-my-opencode/src/features/builtin-skills/index.ts +0 -2
- package/oh-my-opencode/src/features/builtin-skills/skills.ts +0 -1203
- package/oh-my-opencode/src/features/builtin-skills/types.ts +0 -16
- package/oh-my-opencode/src/features/claude-code-agent-loader/index.ts +0 -2
- package/oh-my-opencode/src/features/claude-code-agent-loader/loader.ts +0 -90
- package/oh-my-opencode/src/features/claude-code-agent-loader/types.ts +0 -17
- package/oh-my-opencode/src/features/claude-code-command-loader/index.ts +0 -2
- package/oh-my-opencode/src/features/claude-code-command-loader/loader.ts +0 -144
- package/oh-my-opencode/src/features/claude-code-command-loader/types.ts +0 -46
- package/oh-my-opencode/src/features/claude-code-mcp-loader/env-expander.ts +0 -27
- package/oh-my-opencode/src/features/claude-code-mcp-loader/index.ts +0 -11
- package/oh-my-opencode/src/features/claude-code-mcp-loader/loader.test.ts +0 -162
- package/oh-my-opencode/src/features/claude-code-mcp-loader/loader.ts +0 -113
- package/oh-my-opencode/src/features/claude-code-mcp-loader/transformer.ts +0 -53
- package/oh-my-opencode/src/features/claude-code-mcp-loader/types.ts +0 -42
- package/oh-my-opencode/src/features/claude-code-plugin-loader/index.ts +0 -3
- package/oh-my-opencode/src/features/claude-code-plugin-loader/loader.ts +0 -486
- package/oh-my-opencode/src/features/claude-code-plugin-loader/types.ts +0 -210
- package/oh-my-opencode/src/features/claude-code-session-state/index.ts +0 -1
- package/oh-my-opencode/src/features/claude-code-session-state/state.test.ts +0 -126
- package/oh-my-opencode/src/features/claude-code-session-state/state.ts +0 -37
- package/oh-my-opencode/src/features/context-injector/collector.test.ts +0 -330
- package/oh-my-opencode/src/features/context-injector/collector.ts +0 -85
- package/oh-my-opencode/src/features/context-injector/index.ts +0 -14
- package/oh-my-opencode/src/features/context-injector/injector.test.ts +0 -122
- package/oh-my-opencode/src/features/context-injector/injector.ts +0 -167
- package/oh-my-opencode/src/features/context-injector/types.ts +0 -91
- package/oh-my-opencode/src/features/hook-message-injector/constants.ts +0 -6
- package/oh-my-opencode/src/features/hook-message-injector/index.ts +0 -4
- package/oh-my-opencode/src/features/hook-message-injector/injector.ts +0 -195
- package/oh-my-opencode/src/features/hook-message-injector/types.ts +0 -47
- package/oh-my-opencode/src/features/opencode-skill-loader/async-loader.test.ts +0 -448
- package/oh-my-opencode/src/features/opencode-skill-loader/async-loader.ts +0 -180
- package/oh-my-opencode/src/features/opencode-skill-loader/blocking.test.ts +0 -210
- package/oh-my-opencode/src/features/opencode-skill-loader/blocking.ts +0 -62
- package/oh-my-opencode/src/features/opencode-skill-loader/discover-worker.ts +0 -59
- package/oh-my-opencode/src/features/opencode-skill-loader/index.ts +0 -4
- package/oh-my-opencode/src/features/opencode-skill-loader/loader.test.ts +0 -273
- package/oh-my-opencode/src/features/opencode-skill-loader/loader.ts +0 -259
- package/oh-my-opencode/src/features/opencode-skill-loader/merger.ts +0 -267
- package/oh-my-opencode/src/features/opencode-skill-loader/skill-content.test.ts +0 -267
- package/oh-my-opencode/src/features/opencode-skill-loader/skill-content.ts +0 -206
- package/oh-my-opencode/src/features/opencode-skill-loader/types.ts +0 -38
- package/oh-my-opencode/src/features/skill-mcp-manager/env-cleaner.test.ts +0 -201
- package/oh-my-opencode/src/features/skill-mcp-manager/env-cleaner.ts +0 -27
- package/oh-my-opencode/src/features/skill-mcp-manager/index.ts +0 -2
- package/oh-my-opencode/src/features/skill-mcp-manager/manager.test.ts +0 -611
- package/oh-my-opencode/src/features/skill-mcp-manager/manager.ts +0 -520
- package/oh-my-opencode/src/features/skill-mcp-manager/types.ts +0 -14
- package/oh-my-opencode/src/features/task-toast-manager/index.ts +0 -2
- package/oh-my-opencode/src/features/task-toast-manager/manager.test.ts +0 -249
- package/oh-my-opencode/src/features/task-toast-manager/manager.ts +0 -215
- package/oh-my-opencode/src/features/task-toast-manager/types.ts +0 -24
- package/oh-my-opencode/src/hooks/AGENTS.md +0 -73
- package/oh-my-opencode/src/hooks/agent-usage-reminder/constants.ts +0 -54
- package/oh-my-opencode/src/hooks/agent-usage-reminder/index.ts +0 -109
- package/oh-my-opencode/src/hooks/agent-usage-reminder/storage.ts +0 -42
- package/oh-my-opencode/src/hooks/agent-usage-reminder/types.ts +0 -6
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/executor.test.ts +0 -307
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/executor.ts +0 -485
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/index.ts +0 -151
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/parser.ts +0 -201
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.test.ts +0 -33
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.ts +0 -184
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/pruning-types.ts +0 -44
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/storage.test.ts +0 -77
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/storage.ts +0 -250
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/types.ts +0 -42
- package/oh-my-opencode/src/hooks/atlas/index.test.ts +0 -953
- package/oh-my-opencode/src/hooks/atlas/index.ts +0 -771
- package/oh-my-opencode/src/hooks/auto-slash-command/constants.ts +0 -12
- package/oh-my-opencode/src/hooks/auto-slash-command/detector.test.ts +0 -296
- package/oh-my-opencode/src/hooks/auto-slash-command/detector.ts +0 -65
- package/oh-my-opencode/src/hooks/auto-slash-command/executor.ts +0 -205
- package/oh-my-opencode/src/hooks/auto-slash-command/index.test.ts +0 -254
- package/oh-my-opencode/src/hooks/auto-slash-command/index.ts +0 -89
- package/oh-my-opencode/src/hooks/auto-slash-command/types.ts +0 -23
- package/oh-my-opencode/src/hooks/auto-update-checker/cache.ts +0 -93
- package/oh-my-opencode/src/hooks/auto-update-checker/checker.test.ts +0 -24
- package/oh-my-opencode/src/hooks/auto-update-checker/checker.ts +0 -284
- package/oh-my-opencode/src/hooks/auto-update-checker/constants.ts +0 -64
- package/oh-my-opencode/src/hooks/auto-update-checker/index.test.ts +0 -254
- package/oh-my-opencode/src/hooks/auto-update-checker/index.ts +0 -260
- package/oh-my-opencode/src/hooks/auto-update-checker/types.ts +0 -29
- package/oh-my-opencode/src/hooks/background-compaction/index.ts +0 -87
- package/oh-my-opencode/src/hooks/background-notification/index.ts +0 -28
- package/oh-my-opencode/src/hooks/background-notification/types.ts +0 -5
- package/oh-my-opencode/src/hooks/claude-code-hooks/AGENTS.md +0 -70
- package/oh-my-opencode/src/hooks/claude-code-hooks/config-loader.ts +0 -107
- package/oh-my-opencode/src/hooks/claude-code-hooks/config.ts +0 -103
- package/oh-my-opencode/src/hooks/claude-code-hooks/index.ts +0 -401
- package/oh-my-opencode/src/hooks/claude-code-hooks/plugin-config.ts +0 -12
- package/oh-my-opencode/src/hooks/claude-code-hooks/post-tool-use.ts +0 -199
- package/oh-my-opencode/src/hooks/claude-code-hooks/pre-compact.ts +0 -109
- package/oh-my-opencode/src/hooks/claude-code-hooks/pre-tool-use.ts +0 -172
- package/oh-my-opencode/src/hooks/claude-code-hooks/stop.ts +0 -118
- package/oh-my-opencode/src/hooks/claude-code-hooks/todo.ts +0 -76
- package/oh-my-opencode/src/hooks/claude-code-hooks/tool-input-cache.ts +0 -47
- package/oh-my-opencode/src/hooks/claude-code-hooks/transcript.ts +0 -252
- package/oh-my-opencode/src/hooks/claude-code-hooks/types.ts +0 -204
- package/oh-my-opencode/src/hooks/claude-code-hooks/user-prompt-submit.ts +0 -117
- package/oh-my-opencode/src/hooks/comment-checker/cli.test.ts +0 -68
- package/oh-my-opencode/src/hooks/comment-checker/cli.ts +0 -221
- package/oh-my-opencode/src/hooks/comment-checker/downloader.ts +0 -196
- package/oh-my-opencode/src/hooks/comment-checker/index.ts +0 -171
- package/oh-my-opencode/src/hooks/comment-checker/types.ts +0 -33
- package/oh-my-opencode/src/hooks/compaction-context-injector/index.ts +0 -61
- package/oh-my-opencode/src/hooks/context-window-monitor.ts +0 -99
- package/oh-my-opencode/src/hooks/delegate-task-retry/index.test.ts +0 -119
- package/oh-my-opencode/src/hooks/delegate-task-retry/index.ts +0 -136
- package/oh-my-opencode/src/hooks/directory-agents-injector/constants.ts +0 -9
- package/oh-my-opencode/src/hooks/directory-agents-injector/index.ts +0 -182
- package/oh-my-opencode/src/hooks/directory-agents-injector/storage.ts +0 -48
- package/oh-my-opencode/src/hooks/directory-agents-injector/types.ts +0 -5
- package/oh-my-opencode/src/hooks/directory-readme-injector/constants.ts +0 -9
- package/oh-my-opencode/src/hooks/directory-readme-injector/index.ts +0 -177
- package/oh-my-opencode/src/hooks/directory-readme-injector/storage.ts +0 -48
- package/oh-my-opencode/src/hooks/directory-readme-injector/types.ts +0 -5
- package/oh-my-opencode/src/hooks/edit-error-recovery/index.test.ts +0 -126
- package/oh-my-opencode/src/hooks/edit-error-recovery/index.ts +0 -57
- package/oh-my-opencode/src/hooks/empty-task-response-detector.ts +0 -27
- package/oh-my-opencode/src/hooks/index.ts +0 -32
- package/oh-my-opencode/src/hooks/interactive-bash-session/constants.ts +0 -15
- package/oh-my-opencode/src/hooks/interactive-bash-session/index.ts +0 -262
- package/oh-my-opencode/src/hooks/interactive-bash-session/storage.ts +0 -59
- package/oh-my-opencode/src/hooks/interactive-bash-session/types.ts +0 -11
- package/oh-my-opencode/src/hooks/keyword-detector/constants.ts +0 -300
- package/oh-my-opencode/src/hooks/keyword-detector/detector.ts +0 -52
- package/oh-my-opencode/src/hooks/keyword-detector/index.test.ts +0 -529
- package/oh-my-opencode/src/hooks/keyword-detector/index.ts +0 -100
- package/oh-my-opencode/src/hooks/keyword-detector/types.ts +0 -4
- package/oh-my-opencode/src/hooks/non-interactive-env/constants.ts +0 -70
- package/oh-my-opencode/src/hooks/non-interactive-env/detector.ts +0 -19
- package/oh-my-opencode/src/hooks/non-interactive-env/index.test.ts +0 -323
- package/oh-my-opencode/src/hooks/non-interactive-env/index.ts +0 -63
- package/oh-my-opencode/src/hooks/non-interactive-env/types.ts +0 -3
- package/oh-my-opencode/src/hooks/prometheus-md-only/constants.ts +0 -32
- package/oh-my-opencode/src/hooks/prometheus-md-only/index.test.ts +0 -488
- package/oh-my-opencode/src/hooks/prometheus-md-only/index.ts +0 -136
- package/oh-my-opencode/src/hooks/ralph-loop/constants.ts +0 -5
- package/oh-my-opencode/src/hooks/ralph-loop/index.test.ts +0 -835
- package/oh-my-opencode/src/hooks/ralph-loop/index.ts +0 -417
- package/oh-my-opencode/src/hooks/ralph-loop/storage.ts +0 -115
- package/oh-my-opencode/src/hooks/ralph-loop/types.ts +0 -19
- package/oh-my-opencode/src/hooks/rules-injector/constants.ts +0 -30
- package/oh-my-opencode/src/hooks/rules-injector/finder.test.ts +0 -381
- package/oh-my-opencode/src/hooks/rules-injector/finder.ts +0 -263
- package/oh-my-opencode/src/hooks/rules-injector/index.ts +0 -223
- package/oh-my-opencode/src/hooks/rules-injector/matcher.ts +0 -63
- package/oh-my-opencode/src/hooks/rules-injector/parser.test.ts +0 -226
- package/oh-my-opencode/src/hooks/rules-injector/parser.ts +0 -211
- package/oh-my-opencode/src/hooks/rules-injector/storage.ts +0 -59
- package/oh-my-opencode/src/hooks/rules-injector/types.ts +0 -57
- package/oh-my-opencode/src/hooks/session-notification-utils.ts +0 -140
- package/oh-my-opencode/src/hooks/session-notification.test.ts +0 -361
- package/oh-my-opencode/src/hooks/session-notification.ts +0 -330
- package/oh-my-opencode/src/hooks/session-recovery/constants.ts +0 -10
- package/oh-my-opencode/src/hooks/session-recovery/index.test.ts +0 -223
- package/oh-my-opencode/src/hooks/session-recovery/index.ts +0 -435
- package/oh-my-opencode/src/hooks/session-recovery/storage.ts +0 -390
- package/oh-my-opencode/src/hooks/session-recovery/types.ts +0 -98
- package/oh-my-opencode/src/hooks/start-work/index.test.ts +0 -402
- package/oh-my-opencode/src/hooks/start-work/index.ts +0 -242
- package/oh-my-opencode/src/hooks/task-resume-info/index.ts +0 -36
- package/oh-my-opencode/src/hooks/think-mode/detector.ts +0 -57
- package/oh-my-opencode/src/hooks/think-mode/index.test.ts +0 -353
- package/oh-my-opencode/src/hooks/think-mode/index.ts +0 -89
- package/oh-my-opencode/src/hooks/think-mode/switcher.test.ts +0 -461
- package/oh-my-opencode/src/hooks/think-mode/switcher.ts +0 -222
- package/oh-my-opencode/src/hooks/think-mode/types.ts +0 -21
- package/oh-my-opencode/src/hooks/thinking-block-validator/index.ts +0 -171
- package/oh-my-opencode/src/hooks/todo-continuation-enforcer.test.ts +0 -876
- package/oh-my-opencode/src/hooks/todo-continuation-enforcer.ts +0 -480
- package/oh-my-opencode/src/hooks/tool-output-truncator.test.ts +0 -168
- package/oh-my-opencode/src/hooks/tool-output-truncator.ts +0 -61
- package/oh-my-opencode/src/index.ts +0 -589
- package/oh-my-opencode/src/mcp/AGENTS.md +0 -70
- package/oh-my-opencode/src/mcp/context7.ts +0 -6
- package/oh-my-opencode/src/mcp/grep-app.ts +0 -6
- package/oh-my-opencode/src/mcp/index.test.ts +0 -86
- package/oh-my-opencode/src/mcp/index.ts +0 -32
- package/oh-my-opencode/src/mcp/types.ts +0 -9
- package/oh-my-opencode/src/mcp/websearch.ts +0 -10
- package/oh-my-opencode/src/plugin-config.test.ts +0 -119
- package/oh-my-opencode/src/plugin-config.ts +0 -135
- package/oh-my-opencode/src/plugin-handlers/config-handler.test.ts +0 -103
- package/oh-my-opencode/src/plugin-handlers/config-handler.ts +0 -399
- package/oh-my-opencode/src/plugin-handlers/index.ts +0 -1
- package/oh-my-opencode/src/plugin-state.ts +0 -30
- package/oh-my-opencode/src/shared/AGENTS.md +0 -63
- package/oh-my-opencode/src/shared/agent-tool-restrictions.ts +0 -44
- package/oh-my-opencode/src/shared/agent-variant.test.ts +0 -83
- package/oh-my-opencode/src/shared/agent-variant.ts +0 -40
- package/oh-my-opencode/src/shared/claude-config-dir.test.ts +0 -60
- package/oh-my-opencode/src/shared/claude-config-dir.ts +0 -11
- package/oh-my-opencode/src/shared/command-executor.ts +0 -225
- package/oh-my-opencode/src/shared/config-errors.ts +0 -18
- package/oh-my-opencode/src/shared/config-path.ts +0 -47
- package/oh-my-opencode/src/shared/data-path.ts +0 -22
- package/oh-my-opencode/src/shared/deep-merge.test.ts +0 -336
- package/oh-my-opencode/src/shared/deep-merge.ts +0 -53
- package/oh-my-opencode/src/shared/dynamic-truncator.ts +0 -193
- package/oh-my-opencode/src/shared/external-plugin-detector.test.ts +0 -133
- package/oh-my-opencode/src/shared/external-plugin-detector.ts +0 -132
- package/oh-my-opencode/src/shared/file-reference-resolver.ts +0 -85
- package/oh-my-opencode/src/shared/file-utils.ts +0 -40
- package/oh-my-opencode/src/shared/first-message-variant.test.ts +0 -32
- package/oh-my-opencode/src/shared/first-message-variant.ts +0 -28
- package/oh-my-opencode/src/shared/frontmatter.test.ts +0 -262
- package/oh-my-opencode/src/shared/frontmatter.ts +0 -31
- package/oh-my-opencode/src/shared/hook-disabled.ts +0 -22
- package/oh-my-opencode/src/shared/index.ts +0 -29
- package/oh-my-opencode/src/shared/jsonc-parser.test.ts +0 -266
- package/oh-my-opencode/src/shared/jsonc-parser.ts +0 -66
- package/oh-my-opencode/src/shared/logger.ts +0 -20
- package/oh-my-opencode/src/shared/migration.test.ts +0 -602
- package/oh-my-opencode/src/shared/migration.ts +0 -191
- package/oh-my-opencode/src/shared/model-resolver.test.ts +0 -101
- package/oh-my-opencode/src/shared/model-resolver.ts +0 -35
- package/oh-my-opencode/src/shared/model-sanitizer.ts +0 -12
- package/oh-my-opencode/src/shared/opencode-config-dir.test.ts +0 -318
- package/oh-my-opencode/src/shared/opencode-config-dir.ts +0 -142
- package/oh-my-opencode/src/shared/opencode-version.test.ts +0 -223
- package/oh-my-opencode/src/shared/opencode-version.ts +0 -72
- package/oh-my-opencode/src/shared/pattern-matcher.ts +0 -29
- package/oh-my-opencode/src/shared/permission-compat.test.ts +0 -134
- package/oh-my-opencode/src/shared/permission-compat.ts +0 -77
- package/oh-my-opencode/src/shared/session-cursor.test.ts +0 -66
- package/oh-my-opencode/src/shared/session-cursor.ts +0 -85
- package/oh-my-opencode/src/shared/shell-env.test.ts +0 -278
- package/oh-my-opencode/src/shared/shell-env.ts +0 -111
- package/oh-my-opencode/src/shared/snake-case.ts +0 -49
- package/oh-my-opencode/src/shared/system-directive.ts +0 -40
- package/oh-my-opencode/src/shared/tool-name.ts +0 -26
- package/oh-my-opencode/src/shared/zip-extractor.ts +0 -83
- package/oh-my-opencode/src/tools/AGENTS.md +0 -74
- package/oh-my-opencode/src/tools/ast-grep/cli.ts +0 -230
- package/oh-my-opencode/src/tools/ast-grep/constants.ts +0 -261
- package/oh-my-opencode/src/tools/ast-grep/downloader.ts +0 -128
- package/oh-my-opencode/src/tools/ast-grep/index.ts +0 -13
- package/oh-my-opencode/src/tools/ast-grep/tools.ts +0 -112
- package/oh-my-opencode/src/tools/ast-grep/types.ts +0 -61
- package/oh-my-opencode/src/tools/ast-grep/utils.ts +0 -102
- package/oh-my-opencode/src/tools/background-task/constants.ts +0 -7
- package/oh-my-opencode/src/tools/background-task/index.ts +0 -7
- package/oh-my-opencode/src/tools/background-task/tools.ts +0 -479
- package/oh-my-opencode/src/tools/background-task/types.ts +0 -16
- package/oh-my-opencode/src/tools/call-omo-agent/constants.ts +0 -7
- package/oh-my-opencode/src/tools/call-omo-agent/index.ts +0 -3
- package/oh-my-opencode/src/tools/call-omo-agent/tools.ts +0 -338
- package/oh-my-opencode/src/tools/call-omo-agent/types.ts +0 -27
- package/oh-my-opencode/src/tools/delegate-task/constants.ts +0 -205
- package/oh-my-opencode/src/tools/delegate-task/index.ts +0 -3
- package/oh-my-opencode/src/tools/delegate-task/tools.test.ts +0 -1575
- package/oh-my-opencode/src/tools/delegate-task/tools.ts +0 -885
- package/oh-my-opencode/src/tools/delegate-task/types.ts +0 -9
- package/oh-my-opencode/src/tools/glob/cli.test.ts +0 -158
- package/oh-my-opencode/src/tools/glob/cli.ts +0 -191
- package/oh-my-opencode/src/tools/glob/constants.ts +0 -12
- package/oh-my-opencode/src/tools/glob/index.ts +0 -3
- package/oh-my-opencode/src/tools/glob/tools.ts +0 -41
- package/oh-my-opencode/src/tools/glob/types.ts +0 -22
- package/oh-my-opencode/src/tools/glob/utils.ts +0 -26
- package/oh-my-opencode/src/tools/grep/cli.ts +0 -229
- package/oh-my-opencode/src/tools/grep/constants.ts +0 -127
- package/oh-my-opencode/src/tools/grep/downloader.test.ts +0 -103
- package/oh-my-opencode/src/tools/grep/downloader.ts +0 -145
- package/oh-my-opencode/src/tools/grep/index.ts +0 -3
- package/oh-my-opencode/src/tools/grep/tools.ts +0 -40
- package/oh-my-opencode/src/tools/grep/types.ts +0 -39
- package/oh-my-opencode/src/tools/grep/utils.ts +0 -53
- package/oh-my-opencode/src/tools/index.ts +0 -72
- package/oh-my-opencode/src/tools/interactive-bash/constants.ts +0 -18
- package/oh-my-opencode/src/tools/interactive-bash/index.ts +0 -4
- package/oh-my-opencode/src/tools/interactive-bash/tools.ts +0 -126
- package/oh-my-opencode/src/tools/interactive-bash/utils.ts +0 -71
- package/oh-my-opencode/src/tools/look-at/constants.ts +0 -3
- package/oh-my-opencode/src/tools/look-at/index.ts +0 -3
- package/oh-my-opencode/src/tools/look-at/tools.test.ts +0 -73
- package/oh-my-opencode/src/tools/look-at/tools.ts +0 -173
- package/oh-my-opencode/src/tools/look-at/types.ts +0 -4
- package/oh-my-opencode/src/tools/lsp/client.ts +0 -596
- package/oh-my-opencode/src/tools/lsp/config.test.ts +0 -130
- package/oh-my-opencode/src/tools/lsp/config.ts +0 -285
- package/oh-my-opencode/src/tools/lsp/constants.ts +0 -390
- package/oh-my-opencode/src/tools/lsp/index.ts +0 -7
- package/oh-my-opencode/src/tools/lsp/tools.ts +0 -261
- package/oh-my-opencode/src/tools/lsp/types.ts +0 -124
- package/oh-my-opencode/src/tools/lsp/utils.ts +0 -406
- package/oh-my-opencode/src/tools/session-manager/constants.ts +0 -97
- package/oh-my-opencode/src/tools/session-manager/index.ts +0 -3
- package/oh-my-opencode/src/tools/session-manager/storage.test.ts +0 -315
- package/oh-my-opencode/src/tools/session-manager/storage.ts +0 -238
- package/oh-my-opencode/src/tools/session-manager/tools.test.ts +0 -124
- package/oh-my-opencode/src/tools/session-manager/tools.ts +0 -146
- package/oh-my-opencode/src/tools/session-manager/types.ts +0 -99
- package/oh-my-opencode/src/tools/session-manager/utils.test.ts +0 -160
- package/oh-my-opencode/src/tools/session-manager/utils.ts +0 -199
- package/oh-my-opencode/src/tools/skill/constants.ts +0 -8
- package/oh-my-opencode/src/tools/skill/index.ts +0 -3
- package/oh-my-opencode/src/tools/skill/tools.test.ts +0 -239
- package/oh-my-opencode/src/tools/skill/tools.ts +0 -200
- package/oh-my-opencode/src/tools/skill/types.ts +0 -31
- package/oh-my-opencode/src/tools/skill-mcp/constants.ts +0 -3
- package/oh-my-opencode/src/tools/skill-mcp/index.ts +0 -3
- package/oh-my-opencode/src/tools/skill-mcp/tools.test.ts +0 -215
- package/oh-my-opencode/src/tools/skill-mcp/tools.ts +0 -172
- package/oh-my-opencode/src/tools/skill-mcp/types.ts +0 -8
- package/oh-my-opencode/src/tools/slashcommand/index.ts +0 -2
- package/oh-my-opencode/src/tools/slashcommand/tools.ts +0 -252
- package/oh-my-opencode/src/tools/slashcommand/types.ts +0 -28
- package/oh-my-opencode/test-setup.ts +0 -6
- package/oh-my-opencode/tsconfig.json +0 -20
|
@@ -1,953 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"
|
|
2
|
-
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
|
3
|
-
import { join } from "node:path"
|
|
4
|
-
import { tmpdir } from "node:os"
|
|
5
|
-
import { createAtlasHook } from "./index"
|
|
6
|
-
import {
|
|
7
|
-
writeBoulderState,
|
|
8
|
-
clearBoulderState,
|
|
9
|
-
readBoulderState,
|
|
10
|
-
} from "../../features/boulder-state"
|
|
11
|
-
import type { BoulderState } from "../../features/boulder-state"
|
|
12
|
-
|
|
13
|
-
import { MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
|
14
|
-
|
|
15
|
-
describe("atlas hook", () => {
|
|
16
|
-
const TEST_DIR = join(tmpdir(), "atlas-test-" + Date.now())
|
|
17
|
-
const SISYPHUS_DIR = join(TEST_DIR, ".sisyphus")
|
|
18
|
-
|
|
19
|
-
function createMockPluginInput(overrides?: { promptMock?: ReturnType<typeof mock> }) {
|
|
20
|
-
const promptMock = overrides?.promptMock ?? mock(() => Promise.resolve())
|
|
21
|
-
return {
|
|
22
|
-
directory: TEST_DIR,
|
|
23
|
-
client: {
|
|
24
|
-
session: {
|
|
25
|
-
prompt: promptMock,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
_promptMock: promptMock,
|
|
29
|
-
} as unknown as Parameters<typeof createAtlasHook>[0] & { _promptMock: ReturnType<typeof mock> }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function setupMessageStorage(sessionID: string, agent: string): void {
|
|
33
|
-
const messageDir = join(MESSAGE_STORAGE, sessionID)
|
|
34
|
-
if (!existsSync(messageDir)) {
|
|
35
|
-
mkdirSync(messageDir, { recursive: true })
|
|
36
|
-
}
|
|
37
|
-
const messageData = {
|
|
38
|
-
agent,
|
|
39
|
-
model: { providerID: "anthropic", modelID: "claude-opus-4-5" },
|
|
40
|
-
}
|
|
41
|
-
writeFileSync(join(messageDir, "msg_test001.json"), JSON.stringify(messageData))
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function cleanupMessageStorage(sessionID: string): void {
|
|
45
|
-
const messageDir = join(MESSAGE_STORAGE, sessionID)
|
|
46
|
-
if (existsSync(messageDir)) {
|
|
47
|
-
rmSync(messageDir, { recursive: true, force: true })
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
if (!existsSync(TEST_DIR)) {
|
|
53
|
-
mkdirSync(TEST_DIR, { recursive: true })
|
|
54
|
-
}
|
|
55
|
-
if (!existsSync(SISYPHUS_DIR)) {
|
|
56
|
-
mkdirSync(SISYPHUS_DIR, { recursive: true })
|
|
57
|
-
}
|
|
58
|
-
clearBoulderState(TEST_DIR)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
afterEach(() => {
|
|
62
|
-
clearBoulderState(TEST_DIR)
|
|
63
|
-
if (existsSync(TEST_DIR)) {
|
|
64
|
-
rmSync(TEST_DIR, { recursive: true, force: true })
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
describe("tool.execute.after handler", () => {
|
|
69
|
-
test("should ignore non-delegate_task tools", async () => {
|
|
70
|
-
// #given - hook and non-delegate_task tool
|
|
71
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
72
|
-
const output = {
|
|
73
|
-
title: "Test Tool",
|
|
74
|
-
output: "Original output",
|
|
75
|
-
metadata: {},
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// #when
|
|
79
|
-
await hook["tool.execute.after"](
|
|
80
|
-
{ tool: "other_tool", sessionID: "session-123" },
|
|
81
|
-
output
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
// #then - output unchanged
|
|
85
|
-
expect(output.output).toBe("Original output")
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
test("should not transform when caller is not Atlas", async () => {
|
|
89
|
-
// #given - boulder state exists but caller agent in message storage is not Atlas
|
|
90
|
-
const sessionID = "session-non-orchestrator-test"
|
|
91
|
-
setupMessageStorage(sessionID, "other-agent")
|
|
92
|
-
|
|
93
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
94
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1")
|
|
95
|
-
|
|
96
|
-
const state: BoulderState = {
|
|
97
|
-
active_plan: planPath,
|
|
98
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
99
|
-
session_ids: ["session-1"],
|
|
100
|
-
plan_name: "test-plan",
|
|
101
|
-
}
|
|
102
|
-
writeBoulderState(TEST_DIR, state)
|
|
103
|
-
|
|
104
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
105
|
-
const output = {
|
|
106
|
-
title: "Sisyphus Task",
|
|
107
|
-
output: "Task completed successfully",
|
|
108
|
-
metadata: {},
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// #when
|
|
112
|
-
await hook["tool.execute.after"](
|
|
113
|
-
{ tool: "delegate_task", sessionID },
|
|
114
|
-
output
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
// #then - output unchanged because caller is not orchestrator
|
|
118
|
-
expect(output.output).toBe("Task completed successfully")
|
|
119
|
-
|
|
120
|
-
cleanupMessageStorage(sessionID)
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
test("should append standalone verification when no boulder state but caller is Atlas", async () => {
|
|
124
|
-
// #given - no boulder state, but caller is Atlas
|
|
125
|
-
const sessionID = "session-no-boulder-test"
|
|
126
|
-
setupMessageStorage(sessionID, "Atlas")
|
|
127
|
-
|
|
128
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
129
|
-
const output = {
|
|
130
|
-
title: "Sisyphus Task",
|
|
131
|
-
output: "Task completed successfully",
|
|
132
|
-
metadata: {},
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// #when
|
|
136
|
-
await hook["tool.execute.after"](
|
|
137
|
-
{ tool: "delegate_task", sessionID },
|
|
138
|
-
output
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
// #then - standalone verification reminder appended
|
|
142
|
-
expect(output.output).toContain("Task completed successfully")
|
|
143
|
-
expect(output.output).toContain("MANDATORY:")
|
|
144
|
-
expect(output.output).toContain("delegate_task(resume=")
|
|
145
|
-
|
|
146
|
-
cleanupMessageStorage(sessionID)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
test("should transform output when caller is Atlas with boulder state", async () => {
|
|
150
|
-
// #given - Atlas caller with boulder state
|
|
151
|
-
const sessionID = "session-transform-test"
|
|
152
|
-
setupMessageStorage(sessionID, "Atlas")
|
|
153
|
-
|
|
154
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
155
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1\n- [x] Task 2")
|
|
156
|
-
|
|
157
|
-
const state: BoulderState = {
|
|
158
|
-
active_plan: planPath,
|
|
159
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
160
|
-
session_ids: ["session-1"],
|
|
161
|
-
plan_name: "test-plan",
|
|
162
|
-
}
|
|
163
|
-
writeBoulderState(TEST_DIR, state)
|
|
164
|
-
|
|
165
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
166
|
-
const output = {
|
|
167
|
-
title: "Sisyphus Task",
|
|
168
|
-
output: "Task completed successfully",
|
|
169
|
-
metadata: {},
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// #when
|
|
173
|
-
await hook["tool.execute.after"](
|
|
174
|
-
{ tool: "delegate_task", sessionID },
|
|
175
|
-
output
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
// #then - output should be transformed (original output preserved for debugging)
|
|
179
|
-
expect(output.output).toContain("Task completed successfully")
|
|
180
|
-
expect(output.output).toContain("SUBAGENT WORK COMPLETED")
|
|
181
|
-
expect(output.output).toContain("test-plan")
|
|
182
|
-
expect(output.output).toContain("LIE")
|
|
183
|
-
expect(output.output).toContain("delegate_task(resume=")
|
|
184
|
-
|
|
185
|
-
cleanupMessageStorage(sessionID)
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
test("should still transform when plan is complete (shows progress)", async () => {
|
|
189
|
-
// #given - boulder state with complete plan, Atlas caller
|
|
190
|
-
const sessionID = "session-complete-plan-test"
|
|
191
|
-
setupMessageStorage(sessionID, "Atlas")
|
|
192
|
-
|
|
193
|
-
const planPath = join(TEST_DIR, "complete-plan.md")
|
|
194
|
-
writeFileSync(planPath, "# Plan\n- [x] Task 1\n- [x] Task 2")
|
|
195
|
-
|
|
196
|
-
const state: BoulderState = {
|
|
197
|
-
active_plan: planPath,
|
|
198
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
199
|
-
session_ids: ["session-1"],
|
|
200
|
-
plan_name: "complete-plan",
|
|
201
|
-
}
|
|
202
|
-
writeBoulderState(TEST_DIR, state)
|
|
203
|
-
|
|
204
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
205
|
-
const output = {
|
|
206
|
-
title: "Sisyphus Task",
|
|
207
|
-
output: "Original output",
|
|
208
|
-
metadata: {},
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// #when
|
|
212
|
-
await hook["tool.execute.after"](
|
|
213
|
-
{ tool: "delegate_task", sessionID },
|
|
214
|
-
output
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
// #then - output transformed even when complete (shows 2/2 done)
|
|
218
|
-
expect(output.output).toContain("SUBAGENT WORK COMPLETED")
|
|
219
|
-
expect(output.output).toContain("2/2 done")
|
|
220
|
-
expect(output.output).toContain("0 remaining")
|
|
221
|
-
|
|
222
|
-
cleanupMessageStorage(sessionID)
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
test("should append session ID to boulder state if not present", async () => {
|
|
226
|
-
// #given - boulder state without session-append-test, Atlas caller
|
|
227
|
-
const sessionID = "session-append-test"
|
|
228
|
-
setupMessageStorage(sessionID, "Atlas")
|
|
229
|
-
|
|
230
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
231
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1")
|
|
232
|
-
|
|
233
|
-
const state: BoulderState = {
|
|
234
|
-
active_plan: planPath,
|
|
235
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
236
|
-
session_ids: ["session-1"],
|
|
237
|
-
plan_name: "test-plan",
|
|
238
|
-
}
|
|
239
|
-
writeBoulderState(TEST_DIR, state)
|
|
240
|
-
|
|
241
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
242
|
-
const output = {
|
|
243
|
-
title: "Sisyphus Task",
|
|
244
|
-
output: "Task output",
|
|
245
|
-
metadata: {},
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// #when
|
|
249
|
-
await hook["tool.execute.after"](
|
|
250
|
-
{ tool: "delegate_task", sessionID },
|
|
251
|
-
output
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
// #then - sessionID should be appended
|
|
255
|
-
const updatedState = readBoulderState(TEST_DIR)
|
|
256
|
-
expect(updatedState?.session_ids).toContain(sessionID)
|
|
257
|
-
|
|
258
|
-
cleanupMessageStorage(sessionID)
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
test("should not duplicate existing session ID", async () => {
|
|
262
|
-
// #given - boulder state already has session-dup-test, Atlas caller
|
|
263
|
-
const sessionID = "session-dup-test"
|
|
264
|
-
setupMessageStorage(sessionID, "Atlas")
|
|
265
|
-
|
|
266
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
267
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1")
|
|
268
|
-
|
|
269
|
-
const state: BoulderState = {
|
|
270
|
-
active_plan: planPath,
|
|
271
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
272
|
-
session_ids: [sessionID],
|
|
273
|
-
plan_name: "test-plan",
|
|
274
|
-
}
|
|
275
|
-
writeBoulderState(TEST_DIR, state)
|
|
276
|
-
|
|
277
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
278
|
-
const output = {
|
|
279
|
-
title: "Sisyphus Task",
|
|
280
|
-
output: "Task output",
|
|
281
|
-
metadata: {},
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// #when
|
|
285
|
-
await hook["tool.execute.after"](
|
|
286
|
-
{ tool: "delegate_task", sessionID },
|
|
287
|
-
output
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
// #then - should still have only one sessionID
|
|
291
|
-
const updatedState = readBoulderState(TEST_DIR)
|
|
292
|
-
const count = updatedState?.session_ids.filter((id) => id === sessionID).length
|
|
293
|
-
expect(count).toBe(1)
|
|
294
|
-
|
|
295
|
-
cleanupMessageStorage(sessionID)
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
test("should include boulder.json path and notepad path in transformed output", async () => {
|
|
299
|
-
// #given - boulder state, Atlas caller
|
|
300
|
-
const sessionID = "session-path-test"
|
|
301
|
-
setupMessageStorage(sessionID, "Atlas")
|
|
302
|
-
|
|
303
|
-
const planPath = join(TEST_DIR, "my-feature.md")
|
|
304
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1\n- [ ] Task 2\n- [x] Task 3")
|
|
305
|
-
|
|
306
|
-
const state: BoulderState = {
|
|
307
|
-
active_plan: planPath,
|
|
308
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
309
|
-
session_ids: ["session-1"],
|
|
310
|
-
plan_name: "my-feature",
|
|
311
|
-
}
|
|
312
|
-
writeBoulderState(TEST_DIR, state)
|
|
313
|
-
|
|
314
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
315
|
-
const output = {
|
|
316
|
-
title: "Sisyphus Task",
|
|
317
|
-
output: "Task completed",
|
|
318
|
-
metadata: {},
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// #when
|
|
322
|
-
await hook["tool.execute.after"](
|
|
323
|
-
{ tool: "delegate_task", sessionID },
|
|
324
|
-
output
|
|
325
|
-
)
|
|
326
|
-
|
|
327
|
-
// #then - output should contain plan name and progress
|
|
328
|
-
expect(output.output).toContain("my-feature")
|
|
329
|
-
expect(output.output).toContain("1/3 done")
|
|
330
|
-
expect(output.output).toContain("2 remaining")
|
|
331
|
-
|
|
332
|
-
cleanupMessageStorage(sessionID)
|
|
333
|
-
})
|
|
334
|
-
|
|
335
|
-
test("should include resume and checkbox instructions in reminder", async () => {
|
|
336
|
-
// #given - boulder state, Atlas caller
|
|
337
|
-
const sessionID = "session-resume-test"
|
|
338
|
-
setupMessageStorage(sessionID, "Atlas")
|
|
339
|
-
|
|
340
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
341
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1")
|
|
342
|
-
|
|
343
|
-
const state: BoulderState = {
|
|
344
|
-
active_plan: planPath,
|
|
345
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
346
|
-
session_ids: ["session-1"],
|
|
347
|
-
plan_name: "test-plan",
|
|
348
|
-
}
|
|
349
|
-
writeBoulderState(TEST_DIR, state)
|
|
350
|
-
|
|
351
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
352
|
-
const output = {
|
|
353
|
-
title: "Sisyphus Task",
|
|
354
|
-
output: "Task completed",
|
|
355
|
-
metadata: {},
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// #when
|
|
359
|
-
await hook["tool.execute.after"](
|
|
360
|
-
{ tool: "delegate_task", sessionID },
|
|
361
|
-
output
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
// #then - should include resume instructions and verification
|
|
365
|
-
expect(output.output).toContain("delegate_task(resume=")
|
|
366
|
-
expect(output.output).toContain("[x]")
|
|
367
|
-
expect(output.output).toContain("MANDATORY:")
|
|
368
|
-
|
|
369
|
-
cleanupMessageStorage(sessionID)
|
|
370
|
-
})
|
|
371
|
-
|
|
372
|
-
describe("Write/Edit tool direct work reminder", () => {
|
|
373
|
-
const ORCHESTRATOR_SESSION = "orchestrator-write-test"
|
|
374
|
-
|
|
375
|
-
beforeEach(() => {
|
|
376
|
-
setupMessageStorage(ORCHESTRATOR_SESSION, "Atlas")
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
afterEach(() => {
|
|
380
|
-
cleanupMessageStorage(ORCHESTRATOR_SESSION)
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
test("should append delegation reminder when orchestrator writes outside .sisyphus/", async () => {
|
|
384
|
-
// #given
|
|
385
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
386
|
-
const output = {
|
|
387
|
-
title: "Write",
|
|
388
|
-
output: "File written successfully",
|
|
389
|
-
metadata: { filePath: "/path/to/code.ts" },
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// #when
|
|
393
|
-
await hook["tool.execute.after"](
|
|
394
|
-
{ tool: "Write", sessionID: ORCHESTRATOR_SESSION },
|
|
395
|
-
output
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
// #then
|
|
399
|
-
expect(output.output).toContain("DELEGATION REQUIRED")
|
|
400
|
-
expect(output.output).toContain("ORCHESTRATOR, not an IMPLEMENTER")
|
|
401
|
-
expect(output.output).toContain("delegate_task")
|
|
402
|
-
})
|
|
403
|
-
|
|
404
|
-
test("should append delegation reminder when orchestrator edits outside .sisyphus/", async () => {
|
|
405
|
-
// #given
|
|
406
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
407
|
-
const output = {
|
|
408
|
-
title: "Edit",
|
|
409
|
-
output: "File edited successfully",
|
|
410
|
-
metadata: { filePath: "/src/components/button.tsx" },
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// #when
|
|
414
|
-
await hook["tool.execute.after"](
|
|
415
|
-
{ tool: "Edit", sessionID: ORCHESTRATOR_SESSION },
|
|
416
|
-
output
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
// #then
|
|
420
|
-
expect(output.output).toContain("DELEGATION REQUIRED")
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
test("should NOT append reminder when orchestrator writes inside .sisyphus/", async () => {
|
|
424
|
-
// #given
|
|
425
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
426
|
-
const originalOutput = "File written successfully"
|
|
427
|
-
const output = {
|
|
428
|
-
title: "Write",
|
|
429
|
-
output: originalOutput,
|
|
430
|
-
metadata: { filePath: "/project/.sisyphus/plans/work-plan.md" },
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// #when
|
|
434
|
-
await hook["tool.execute.after"](
|
|
435
|
-
{ tool: "Write", sessionID: ORCHESTRATOR_SESSION },
|
|
436
|
-
output
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
// #then
|
|
440
|
-
expect(output.output).toBe(originalOutput)
|
|
441
|
-
expect(output.output).not.toContain("DELEGATION REQUIRED")
|
|
442
|
-
})
|
|
443
|
-
|
|
444
|
-
test("should NOT append reminder when non-orchestrator writes outside .sisyphus/", async () => {
|
|
445
|
-
// #given
|
|
446
|
-
const nonOrchestratorSession = "non-orchestrator-session"
|
|
447
|
-
setupMessageStorage(nonOrchestratorSession, "Sisyphus-Junior")
|
|
448
|
-
|
|
449
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
450
|
-
const originalOutput = "File written successfully"
|
|
451
|
-
const output = {
|
|
452
|
-
title: "Write",
|
|
453
|
-
output: originalOutput,
|
|
454
|
-
metadata: { filePath: "/path/to/code.ts" },
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// #when
|
|
458
|
-
await hook["tool.execute.after"](
|
|
459
|
-
{ tool: "Write", sessionID: nonOrchestratorSession },
|
|
460
|
-
output
|
|
461
|
-
)
|
|
462
|
-
|
|
463
|
-
// #then
|
|
464
|
-
expect(output.output).toBe(originalOutput)
|
|
465
|
-
expect(output.output).not.toContain("DELEGATION REQUIRED")
|
|
466
|
-
|
|
467
|
-
cleanupMessageStorage(nonOrchestratorSession)
|
|
468
|
-
})
|
|
469
|
-
|
|
470
|
-
test("should NOT append reminder for read-only tools", async () => {
|
|
471
|
-
// #given
|
|
472
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
473
|
-
const originalOutput = "File content"
|
|
474
|
-
const output = {
|
|
475
|
-
title: "Read",
|
|
476
|
-
output: originalOutput,
|
|
477
|
-
metadata: { filePath: "/path/to/code.ts" },
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// #when
|
|
481
|
-
await hook["tool.execute.after"](
|
|
482
|
-
{ tool: "Read", sessionID: ORCHESTRATOR_SESSION },
|
|
483
|
-
output
|
|
484
|
-
)
|
|
485
|
-
|
|
486
|
-
// #then
|
|
487
|
-
expect(output.output).toBe(originalOutput)
|
|
488
|
-
})
|
|
489
|
-
|
|
490
|
-
test("should handle missing filePath gracefully", async () => {
|
|
491
|
-
// #given
|
|
492
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
493
|
-
const originalOutput = "File written successfully"
|
|
494
|
-
const output = {
|
|
495
|
-
title: "Write",
|
|
496
|
-
output: originalOutput,
|
|
497
|
-
metadata: {},
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// #when
|
|
501
|
-
await hook["tool.execute.after"](
|
|
502
|
-
{ tool: "Write", sessionID: ORCHESTRATOR_SESSION },
|
|
503
|
-
output
|
|
504
|
-
)
|
|
505
|
-
|
|
506
|
-
// #then
|
|
507
|
-
expect(output.output).toBe(originalOutput)
|
|
508
|
-
})
|
|
509
|
-
|
|
510
|
-
describe("cross-platform path validation (Windows support)", () => {
|
|
511
|
-
test("should NOT append reminder when orchestrator writes inside .sisyphus\\ (Windows backslash)", async () => {
|
|
512
|
-
// #given
|
|
513
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
514
|
-
const originalOutput = "File written successfully"
|
|
515
|
-
const output = {
|
|
516
|
-
title: "Write",
|
|
517
|
-
output: originalOutput,
|
|
518
|
-
metadata: { filePath: ".sisyphus\\plans\\work-plan.md" },
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// #when
|
|
522
|
-
await hook["tool.execute.after"](
|
|
523
|
-
{ tool: "Write", sessionID: ORCHESTRATOR_SESSION },
|
|
524
|
-
output
|
|
525
|
-
)
|
|
526
|
-
|
|
527
|
-
// #then
|
|
528
|
-
expect(output.output).toBe(originalOutput)
|
|
529
|
-
expect(output.output).not.toContain("DELEGATION REQUIRED")
|
|
530
|
-
})
|
|
531
|
-
|
|
532
|
-
test("should NOT append reminder when orchestrator writes inside .sisyphus with mixed separators", async () => {
|
|
533
|
-
// #given
|
|
534
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
535
|
-
const originalOutput = "File written successfully"
|
|
536
|
-
const output = {
|
|
537
|
-
title: "Write",
|
|
538
|
-
output: originalOutput,
|
|
539
|
-
metadata: { filePath: ".sisyphus\\plans/work-plan.md" },
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// #when
|
|
543
|
-
await hook["tool.execute.after"](
|
|
544
|
-
{ tool: "Write", sessionID: ORCHESTRATOR_SESSION },
|
|
545
|
-
output
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
// #then
|
|
549
|
-
expect(output.output).toBe(originalOutput)
|
|
550
|
-
expect(output.output).not.toContain("DELEGATION REQUIRED")
|
|
551
|
-
})
|
|
552
|
-
|
|
553
|
-
test("should NOT append reminder for absolute Windows path inside .sisyphus\\", async () => {
|
|
554
|
-
// #given
|
|
555
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
556
|
-
const originalOutput = "File written successfully"
|
|
557
|
-
const output = {
|
|
558
|
-
title: "Write",
|
|
559
|
-
output: originalOutput,
|
|
560
|
-
metadata: { filePath: "C:\\Users\\test\\project\\.sisyphus\\plans\\x.md" },
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// #when
|
|
564
|
-
await hook["tool.execute.after"](
|
|
565
|
-
{ tool: "Write", sessionID: ORCHESTRATOR_SESSION },
|
|
566
|
-
output
|
|
567
|
-
)
|
|
568
|
-
|
|
569
|
-
// #then
|
|
570
|
-
expect(output.output).toBe(originalOutput)
|
|
571
|
-
expect(output.output).not.toContain("DELEGATION REQUIRED")
|
|
572
|
-
})
|
|
573
|
-
|
|
574
|
-
test("should append reminder for Windows path outside .sisyphus\\", async () => {
|
|
575
|
-
// #given
|
|
576
|
-
const hook = createAtlasHook(createMockPluginInput())
|
|
577
|
-
const output = {
|
|
578
|
-
title: "Write",
|
|
579
|
-
output: "File written successfully",
|
|
580
|
-
metadata: { filePath: "C:\\Users\\test\\project\\src\\code.ts" },
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// #when
|
|
584
|
-
await hook["tool.execute.after"](
|
|
585
|
-
{ tool: "Write", sessionID: ORCHESTRATOR_SESSION },
|
|
586
|
-
output
|
|
587
|
-
)
|
|
588
|
-
|
|
589
|
-
// #then
|
|
590
|
-
expect(output.output).toContain("DELEGATION REQUIRED")
|
|
591
|
-
})
|
|
592
|
-
})
|
|
593
|
-
})
|
|
594
|
-
})
|
|
595
|
-
|
|
596
|
-
describe("session.idle handler (boulder continuation)", () => {
|
|
597
|
-
const MAIN_SESSION_ID = "main-session-123"
|
|
598
|
-
|
|
599
|
-
beforeEach(() => {
|
|
600
|
-
mock.module("../../features/claude-code-session-state", () => ({
|
|
601
|
-
getMainSessionID: () => MAIN_SESSION_ID,
|
|
602
|
-
subagentSessions: new Set<string>(),
|
|
603
|
-
}))
|
|
604
|
-
setupMessageStorage(MAIN_SESSION_ID, "Atlas")
|
|
605
|
-
})
|
|
606
|
-
|
|
607
|
-
afterEach(() => {
|
|
608
|
-
cleanupMessageStorage(MAIN_SESSION_ID)
|
|
609
|
-
})
|
|
610
|
-
|
|
611
|
-
test("should inject continuation when boulder has incomplete tasks", async () => {
|
|
612
|
-
// #given - boulder state with incomplete plan
|
|
613
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
614
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1\n- [x] Task 2\n- [ ] Task 3")
|
|
615
|
-
|
|
616
|
-
const state: BoulderState = {
|
|
617
|
-
active_plan: planPath,
|
|
618
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
619
|
-
session_ids: [MAIN_SESSION_ID],
|
|
620
|
-
plan_name: "test-plan",
|
|
621
|
-
}
|
|
622
|
-
writeBoulderState(TEST_DIR, state)
|
|
623
|
-
|
|
624
|
-
const mockInput = createMockPluginInput()
|
|
625
|
-
const hook = createAtlasHook(mockInput)
|
|
626
|
-
|
|
627
|
-
// #when
|
|
628
|
-
await hook.handler({
|
|
629
|
-
event: {
|
|
630
|
-
type: "session.idle",
|
|
631
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
632
|
-
},
|
|
633
|
-
})
|
|
634
|
-
|
|
635
|
-
// #then - should call prompt with continuation
|
|
636
|
-
expect(mockInput._promptMock).toHaveBeenCalled()
|
|
637
|
-
const callArgs = mockInput._promptMock.mock.calls[0][0]
|
|
638
|
-
expect(callArgs.path.id).toBe(MAIN_SESSION_ID)
|
|
639
|
-
expect(callArgs.body.parts[0].text).toContain("BOULDER CONTINUATION")
|
|
640
|
-
expect(callArgs.body.parts[0].text).toContain("2 remaining")
|
|
641
|
-
})
|
|
642
|
-
|
|
643
|
-
test("should not inject when no boulder state exists", async () => {
|
|
644
|
-
// #given - no boulder state
|
|
645
|
-
const mockInput = createMockPluginInput()
|
|
646
|
-
const hook = createAtlasHook(mockInput)
|
|
647
|
-
|
|
648
|
-
// #when
|
|
649
|
-
await hook.handler({
|
|
650
|
-
event: {
|
|
651
|
-
type: "session.idle",
|
|
652
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
653
|
-
},
|
|
654
|
-
})
|
|
655
|
-
|
|
656
|
-
// #then - should not call prompt
|
|
657
|
-
expect(mockInput._promptMock).not.toHaveBeenCalled()
|
|
658
|
-
})
|
|
659
|
-
|
|
660
|
-
test("should not inject when boulder plan is complete", async () => {
|
|
661
|
-
// #given - boulder state with complete plan
|
|
662
|
-
const planPath = join(TEST_DIR, "complete-plan.md")
|
|
663
|
-
writeFileSync(planPath, "# Plan\n- [x] Task 1\n- [x] Task 2")
|
|
664
|
-
|
|
665
|
-
const state: BoulderState = {
|
|
666
|
-
active_plan: planPath,
|
|
667
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
668
|
-
session_ids: [MAIN_SESSION_ID],
|
|
669
|
-
plan_name: "complete-plan",
|
|
670
|
-
}
|
|
671
|
-
writeBoulderState(TEST_DIR, state)
|
|
672
|
-
|
|
673
|
-
const mockInput = createMockPluginInput()
|
|
674
|
-
const hook = createAtlasHook(mockInput)
|
|
675
|
-
|
|
676
|
-
// #when
|
|
677
|
-
await hook.handler({
|
|
678
|
-
event: {
|
|
679
|
-
type: "session.idle",
|
|
680
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
681
|
-
},
|
|
682
|
-
})
|
|
683
|
-
|
|
684
|
-
// #then - should not call prompt
|
|
685
|
-
expect(mockInput._promptMock).not.toHaveBeenCalled()
|
|
686
|
-
})
|
|
687
|
-
|
|
688
|
-
test("should skip when abort error occurred before idle", async () => {
|
|
689
|
-
// #given - boulder state with incomplete plan
|
|
690
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
691
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1")
|
|
692
|
-
|
|
693
|
-
const state: BoulderState = {
|
|
694
|
-
active_plan: planPath,
|
|
695
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
696
|
-
session_ids: [MAIN_SESSION_ID],
|
|
697
|
-
plan_name: "test-plan",
|
|
698
|
-
}
|
|
699
|
-
writeBoulderState(TEST_DIR, state)
|
|
700
|
-
|
|
701
|
-
const mockInput = createMockPluginInput()
|
|
702
|
-
const hook = createAtlasHook(mockInput)
|
|
703
|
-
|
|
704
|
-
// #when - send abort error then idle
|
|
705
|
-
await hook.handler({
|
|
706
|
-
event: {
|
|
707
|
-
type: "session.error",
|
|
708
|
-
properties: {
|
|
709
|
-
sessionID: MAIN_SESSION_ID,
|
|
710
|
-
error: { name: "AbortError", message: "aborted" },
|
|
711
|
-
},
|
|
712
|
-
},
|
|
713
|
-
})
|
|
714
|
-
await hook.handler({
|
|
715
|
-
event: {
|
|
716
|
-
type: "session.idle",
|
|
717
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
718
|
-
},
|
|
719
|
-
})
|
|
720
|
-
|
|
721
|
-
// #then - should not call prompt
|
|
722
|
-
expect(mockInput._promptMock).not.toHaveBeenCalled()
|
|
723
|
-
})
|
|
724
|
-
|
|
725
|
-
test("should skip when background tasks are running", async () => {
|
|
726
|
-
// #given - boulder state with incomplete plan
|
|
727
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
728
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1")
|
|
729
|
-
|
|
730
|
-
const state: BoulderState = {
|
|
731
|
-
active_plan: planPath,
|
|
732
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
733
|
-
session_ids: [MAIN_SESSION_ID],
|
|
734
|
-
plan_name: "test-plan",
|
|
735
|
-
}
|
|
736
|
-
writeBoulderState(TEST_DIR, state)
|
|
737
|
-
|
|
738
|
-
const mockBackgroundManager = {
|
|
739
|
-
getTasksByParentSession: () => [{ status: "running" }],
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
const mockInput = createMockPluginInput()
|
|
743
|
-
const hook = createAtlasHook(mockInput, {
|
|
744
|
-
directory: TEST_DIR,
|
|
745
|
-
backgroundManager: mockBackgroundManager as any,
|
|
746
|
-
})
|
|
747
|
-
|
|
748
|
-
// #when
|
|
749
|
-
await hook.handler({
|
|
750
|
-
event: {
|
|
751
|
-
type: "session.idle",
|
|
752
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
753
|
-
},
|
|
754
|
-
})
|
|
755
|
-
|
|
756
|
-
// #then - should not call prompt
|
|
757
|
-
expect(mockInput._promptMock).not.toHaveBeenCalled()
|
|
758
|
-
})
|
|
759
|
-
|
|
760
|
-
test("should clear abort state on message.updated", async () => {
|
|
761
|
-
// #given - boulder with incomplete plan
|
|
762
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
763
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1")
|
|
764
|
-
|
|
765
|
-
const state: BoulderState = {
|
|
766
|
-
active_plan: planPath,
|
|
767
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
768
|
-
session_ids: [MAIN_SESSION_ID],
|
|
769
|
-
plan_name: "test-plan",
|
|
770
|
-
}
|
|
771
|
-
writeBoulderState(TEST_DIR, state)
|
|
772
|
-
|
|
773
|
-
const mockInput = createMockPluginInput()
|
|
774
|
-
const hook = createAtlasHook(mockInput)
|
|
775
|
-
|
|
776
|
-
// #when - abort error, then message update, then idle
|
|
777
|
-
await hook.handler({
|
|
778
|
-
event: {
|
|
779
|
-
type: "session.error",
|
|
780
|
-
properties: {
|
|
781
|
-
sessionID: MAIN_SESSION_ID,
|
|
782
|
-
error: { name: "AbortError" },
|
|
783
|
-
},
|
|
784
|
-
},
|
|
785
|
-
})
|
|
786
|
-
await hook.handler({
|
|
787
|
-
event: {
|
|
788
|
-
type: "message.updated",
|
|
789
|
-
properties: { info: { sessionID: MAIN_SESSION_ID, role: "user" } },
|
|
790
|
-
},
|
|
791
|
-
})
|
|
792
|
-
await hook.handler({
|
|
793
|
-
event: {
|
|
794
|
-
type: "session.idle",
|
|
795
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
796
|
-
},
|
|
797
|
-
})
|
|
798
|
-
|
|
799
|
-
// #then - should call prompt because abort state was cleared
|
|
800
|
-
expect(mockInput._promptMock).toHaveBeenCalled()
|
|
801
|
-
})
|
|
802
|
-
|
|
803
|
-
test("should include plan progress in continuation prompt", async () => {
|
|
804
|
-
// #given - boulder state with specific progress
|
|
805
|
-
const planPath = join(TEST_DIR, "progress-plan.md")
|
|
806
|
-
writeFileSync(planPath, "# Plan\n- [x] Task 1\n- [x] Task 2\n- [ ] Task 3\n- [ ] Task 4")
|
|
807
|
-
|
|
808
|
-
const state: BoulderState = {
|
|
809
|
-
active_plan: planPath,
|
|
810
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
811
|
-
session_ids: [MAIN_SESSION_ID],
|
|
812
|
-
plan_name: "progress-plan",
|
|
813
|
-
}
|
|
814
|
-
writeBoulderState(TEST_DIR, state)
|
|
815
|
-
|
|
816
|
-
const mockInput = createMockPluginInput()
|
|
817
|
-
const hook = createAtlasHook(mockInput)
|
|
818
|
-
|
|
819
|
-
// #when
|
|
820
|
-
await hook.handler({
|
|
821
|
-
event: {
|
|
822
|
-
type: "session.idle",
|
|
823
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
824
|
-
},
|
|
825
|
-
})
|
|
826
|
-
|
|
827
|
-
// #then - should include progress
|
|
828
|
-
const callArgs = mockInput._promptMock.mock.calls[0][0]
|
|
829
|
-
expect(callArgs.body.parts[0].text).toContain("2/4 completed")
|
|
830
|
-
expect(callArgs.body.parts[0].text).toContain("2 remaining")
|
|
831
|
-
})
|
|
832
|
-
|
|
833
|
-
test("should not inject when last agent is not Atlas", async () => {
|
|
834
|
-
// #given - boulder state with incomplete plan, but last agent is NOT Atlas
|
|
835
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
836
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1\n- [ ] Task 2")
|
|
837
|
-
|
|
838
|
-
const state: BoulderState = {
|
|
839
|
-
active_plan: planPath,
|
|
840
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
841
|
-
session_ids: [MAIN_SESSION_ID],
|
|
842
|
-
plan_name: "test-plan",
|
|
843
|
-
}
|
|
844
|
-
writeBoulderState(TEST_DIR, state)
|
|
845
|
-
|
|
846
|
-
// #given - last agent is NOT Atlas
|
|
847
|
-
cleanupMessageStorage(MAIN_SESSION_ID)
|
|
848
|
-
setupMessageStorage(MAIN_SESSION_ID, "Sisyphus")
|
|
849
|
-
|
|
850
|
-
const mockInput = createMockPluginInput()
|
|
851
|
-
const hook = createAtlasHook(mockInput)
|
|
852
|
-
|
|
853
|
-
// #when
|
|
854
|
-
await hook.handler({
|
|
855
|
-
event: {
|
|
856
|
-
type: "session.idle",
|
|
857
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
858
|
-
},
|
|
859
|
-
})
|
|
860
|
-
|
|
861
|
-
// #then - should NOT call prompt because agent is not Atlas
|
|
862
|
-
expect(mockInput._promptMock).not.toHaveBeenCalled()
|
|
863
|
-
})
|
|
864
|
-
|
|
865
|
-
test("should debounce rapid continuation injections (prevent infinite loop)", async () => {
|
|
866
|
-
// #given - boulder state with incomplete plan
|
|
867
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
868
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1\n- [ ] Task 2")
|
|
869
|
-
|
|
870
|
-
const state: BoulderState = {
|
|
871
|
-
active_plan: planPath,
|
|
872
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
873
|
-
session_ids: [MAIN_SESSION_ID],
|
|
874
|
-
plan_name: "test-plan",
|
|
875
|
-
}
|
|
876
|
-
writeBoulderState(TEST_DIR, state)
|
|
877
|
-
|
|
878
|
-
const mockInput = createMockPluginInput()
|
|
879
|
-
const hook = createAtlasHook(mockInput)
|
|
880
|
-
|
|
881
|
-
// #when - fire multiple idle events in rapid succession (simulating infinite loop bug)
|
|
882
|
-
await hook.handler({
|
|
883
|
-
event: {
|
|
884
|
-
type: "session.idle",
|
|
885
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
886
|
-
},
|
|
887
|
-
})
|
|
888
|
-
await hook.handler({
|
|
889
|
-
event: {
|
|
890
|
-
type: "session.idle",
|
|
891
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
892
|
-
},
|
|
893
|
-
})
|
|
894
|
-
await hook.handler({
|
|
895
|
-
event: {
|
|
896
|
-
type: "session.idle",
|
|
897
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
898
|
-
},
|
|
899
|
-
})
|
|
900
|
-
|
|
901
|
-
// #then - should only call prompt ONCE due to debouncing
|
|
902
|
-
expect(mockInput._promptMock).toHaveBeenCalledTimes(1)
|
|
903
|
-
})
|
|
904
|
-
|
|
905
|
-
test("should cleanup on session.deleted", async () => {
|
|
906
|
-
// #given - boulder state
|
|
907
|
-
const planPath = join(TEST_DIR, "test-plan.md")
|
|
908
|
-
writeFileSync(planPath, "# Plan\n- [ ] Task 1")
|
|
909
|
-
|
|
910
|
-
const state: BoulderState = {
|
|
911
|
-
active_plan: planPath,
|
|
912
|
-
started_at: "2026-01-02T10:00:00Z",
|
|
913
|
-
session_ids: [MAIN_SESSION_ID],
|
|
914
|
-
plan_name: "test-plan",
|
|
915
|
-
}
|
|
916
|
-
writeBoulderState(TEST_DIR, state)
|
|
917
|
-
|
|
918
|
-
const mockInput = createMockPluginInput()
|
|
919
|
-
const hook = createAtlasHook(mockInput)
|
|
920
|
-
|
|
921
|
-
// #when - create abort state then delete
|
|
922
|
-
await hook.handler({
|
|
923
|
-
event: {
|
|
924
|
-
type: "session.error",
|
|
925
|
-
properties: {
|
|
926
|
-
sessionID: MAIN_SESSION_ID,
|
|
927
|
-
error: { name: "AbortError" },
|
|
928
|
-
},
|
|
929
|
-
},
|
|
930
|
-
})
|
|
931
|
-
await hook.handler({
|
|
932
|
-
event: {
|
|
933
|
-
type: "session.deleted",
|
|
934
|
-
properties: { info: { id: MAIN_SESSION_ID } },
|
|
935
|
-
},
|
|
936
|
-
})
|
|
937
|
-
|
|
938
|
-
// Re-create boulder after deletion
|
|
939
|
-
writeBoulderState(TEST_DIR, state)
|
|
940
|
-
|
|
941
|
-
// Trigger idle - should inject because state was cleaned up
|
|
942
|
-
await hook.handler({
|
|
943
|
-
event: {
|
|
944
|
-
type: "session.idle",
|
|
945
|
-
properties: { sessionID: MAIN_SESSION_ID },
|
|
946
|
-
},
|
|
947
|
-
})
|
|
948
|
-
|
|
949
|
-
// #then - should call prompt because session state was cleaned
|
|
950
|
-
expect(mockInput._promptMock).toHaveBeenCalled()
|
|
951
|
-
})
|
|
952
|
-
})
|
|
953
|
-
})
|