automagik-forge 0.1.13 → 0.1.14
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/README.md +143 -447
- package/dist/linux-x64/automagik-forge-mcp.zip +0 -0
- package/{npx-cli/automagik-forge-0.0.55.tgz → dist/linux-x64/automagik-forge.zip} +0 -0
- package/package.json +13 -23
- package/.cargo/config.toml +0 -13
- package/.claude/commands/commit.md +0 -376
- package/.claude/commands/prompt.md +0 -871
- package/.env.example +0 -20
- package/.github/actions/setup-node/action.yml +0 -29
- package/.github/images/automagik-logo.png +0 -0
- package/.github/workflows/pre-release.yml +0 -470
- package/.github/workflows/publish.yml +0 -145
- package/.github/workflows/test.yml +0 -63
- package/.mcp.json +0 -57
- package/AGENT.md +0 -40
- package/CLAUDE.md +0 -40
- package/CODE-OF-CONDUCT.md +0 -89
- package/Cargo.toml +0 -19
- package/Dockerfile +0 -43
- package/LICENSE +0 -201
- package/Makefile +0 -97
- package/backend/.sqlx/query-01b7e2bac1261d8be3d03c03df3e5220590da6c31c77f161074fc62752d63881.json +0 -12
- package/backend/.sqlx/query-03f2b02ba6dc5ea2b3cf6b1004caea0ad6bcc10ebd63f441d321a389f026e263.json +0 -12
- package/backend/.sqlx/query-0923b77d137a29fc54d399a873ff15fc4af894490bc65a4d344a7575cb0d8643.json +0 -12
- package/backend/.sqlx/query-0f808bcdb63c5f180836e448dd64c435c51758b2fc54a52ce9e67495b1ab200e.json +0 -68
- package/backend/.sqlx/query-1268afe9ca849daa6722e3df7ca8e9e61f0d37052e782bb5452ab8e1018d9b63.json +0 -12
- package/backend/.sqlx/query-1b082630a9622f8667ee7a9aba2c2d3176019a68c6bb83d33008594821415a57.json +0 -12
- package/backend/.sqlx/query-1c7b06ba1e112abf6b945a2ff08a0b40ec23f3738c2e7399f067b558cf8d490e.json +0 -12
- package/backend/.sqlx/query-1f619f01f46859a64ded531dd0ef61abacfe62e758abe7030a6aa745140b95ca.json +0 -104
- package/backend/.sqlx/query-1fca1ce14b4b20205364cd1f1f45ebe1d2e30cd745e59e189d56487b5639dfbb.json +0 -12
- package/backend/.sqlx/query-212828320e8d871ab9d83705a040b23bcf0393dc7252177fc539a74657f578ef.json +0 -32
- package/backend/.sqlx/query-290ce5c152be8d36e58ff42570f9157beb07ab9e77a03ec6fc30b4f56f9b8f6b.json +0 -56
- package/backend/.sqlx/query-2b471d2c2e8ffbe0cd42d2a91b814c0d79f9d09200f147e3cea33ba4ce673c8a.json +0 -68
- package/backend/.sqlx/query-354a48c705bb9bb2048c1b7f10fcb714e23f9db82b7a4ea6932486197b2ede6a.json +0 -92
- package/backend/.sqlx/query-36c9e3dd10648e94b949db5c91a774ecb1e10a899ef95da74066eccedca4d8b2.json +0 -12
- package/backend/.sqlx/query-36e4ba7bbd81b402d5a20b6005755eafbb174c8dda442081823406ac32809a94.json +0 -56
- package/backend/.sqlx/query-3a5b3c98a55ca183ab20c74708e3d7e579dda37972c059e7515c4ceee4bd8dd3.json +0 -62
- package/backend/.sqlx/query-3d0a1cabf2a52e9d90cdfd29c509ca89aeb448d0c1d2446c65cd43db40735e86.json +0 -62
- package/backend/.sqlx/query-3d6bd16fbce59efe30b7f67ea342e0e4ea6d1432389c02468ad79f1f742d4031.json +0 -56
- package/backend/.sqlx/query-4049ca413b285a05aca6b25385e9c8185575f01e9069e4e8581aa45d713f612f.json +0 -32
- package/backend/.sqlx/query-412bacd3477d86369082e90f52240407abce436cb81292d42b2dbe1e5c18eea1.json +0 -104
- package/backend/.sqlx/query-417a8b1ff4e51de82aea0159a3b97932224dc325b23476cb84153d690227fd8b.json +0 -62
- package/backend/.sqlx/query-461cc1b0bb6fd909afc9dd2246e8526b3771cfbb0b22ae4b5d17b51af587b9e2.json +0 -56
- package/backend/.sqlx/query-58408c7a8cdeeda0bef359f1f9bd91299a339dc2b191462fc58c9736a56d5227.json +0 -92
- package/backend/.sqlx/query-5a886026d75d515c01f347cc203c8d99dd04c61dc468e2e4c5aa548436d13834.json +0 -62
- package/backend/.sqlx/query-5b902137b11022d2e1a5c4f6a9c83fec1a856c6a710aff831abd2382ede76b43.json +0 -12
- package/backend/.sqlx/query-5ed1238e52e59bb5f76c0f153fd99a14093f7ce2585bf9843585608f17ec575b.json +0 -104
- package/backend/.sqlx/query-6e8b860b14decfc2227dc57213f38442943d3fbef5c8418fd6b634c6e0f5e2ea.json +0 -104
- package/backend/.sqlx/query-6ec414276994c4ccb2433eaa5b1b342168557d17ddf5a52dac84cb1b59b9de8f.json +0 -68
- package/backend/.sqlx/query-6ecfa16d0cf825aacf233544b5baf151e9adfdca26c226ad71020d291fd802d5.json +0 -62
- package/backend/.sqlx/query-72509d252c39fce77520aa816cb2acbc1fb35dc2605e7be893610599b2427f2e.json +0 -62
- package/backend/.sqlx/query-75239b2da188f749707d77f3c1544332ca70db3d6d6743b2601dc0d167536437.json +0 -62
- package/backend/.sqlx/query-83d10e29f8478aff33434f9ac67068e013b888b953a2657e2bb72a6f619d04f2.json +0 -50
- package/backend/.sqlx/query-8610803360ea18b9b9d078a6981ea56abfbfe84e6354fc1d5ae4c622e01410ed.json +0 -68
- package/backend/.sqlx/query-86d03eb70eef39c59296416867f2ee66c9f7cd8b7f961fbda2f89fc0a1c442c2.json +0 -12
- package/backend/.sqlx/query-87d0feb5a6b442bad9c60068ea7569599cc6fc91a0e2692ecb42e93b03201b9d.json +0 -68
- package/backend/.sqlx/query-8a67b3b3337248f06a57bdf8a908f7ef23177431eaed82dc08c94c3e5944340e.json +0 -12
- package/backend/.sqlx/query-8f01ebd64bdcde6a090479f14810d73ba23020e76fd70854ac57f2da251702c3.json +0 -12
- package/backend/.sqlx/query-90fd607fcb2dca72239ff25e618e21e174b195991eaa33722cbf5f76da84cfab.json +0 -62
- package/backend/.sqlx/query-92e8bdbcd80c5ff3db7a35cf79492048803ef305cbdef0d0a1fe5dc881ca8c71.json +0 -104
- package/backend/.sqlx/query-93a1605f90e9672dad29b472b6ad85fa9a55ea3ffa5abcb8724b09d61be254ca.json +0 -20
- package/backend/.sqlx/query-9472c8fb477958167f5fae40b85ac44252468c5226b2cdd7770f027332eed6d7.json +0 -104
- package/backend/.sqlx/query-96036c4f9e0f48bdc5a4a4588f0c5f288ac7aaa5425cac40fc33f337e1a351f2.json +0 -56
- package/backend/.sqlx/query-9edb2c01e91fd0f0fe7b56e988c7ae0393150f50be3f419a981e035c0121dfc7.json +0 -104
- package/backend/.sqlx/query-a157cf00616f703bfba21927f1eb1c9eec2a81c02da15f66efdba0b6c375de1b.json +0 -26
- package/backend/.sqlx/query-a31fff84f3b8e532fd1160447d89d700f06ae08821fee00c9a5b60492b05259c.json +0 -62
- package/backend/.sqlx/query-a5ba908419fb3e456bdd2daca41ba06cc3212ffffb8520fc7dbbcc8b60ada314.json +0 -12
- package/backend/.sqlx/query-a6d2961718dbc3b1a925e549f49a159c561bef58c105529275f274b27e2eba5b.json +0 -104
- package/backend/.sqlx/query-a9e93d5b09b29faf66e387e4d7596a792d81e75c4d3726e83c2963e8d7c9b56f.json +0 -104
- package/backend/.sqlx/query-ac5247c8d7fb86e4650c4b0eb9420031614c831b7b085083bac20c1af314c538.json +0 -12
- package/backend/.sqlx/query-afef9467be74c411c4cb119a8b2b1aea53049877dfc30cc60b486134ba4b4c9f.json +0 -68
- package/backend/.sqlx/query-b2b2c6b4d0b1a347b5c4cb63c3a46a265d4db53be9554989a814b069d0af82f2.json +0 -62
- package/backend/.sqlx/query-c50d2ff0b12e5bcc81e371089ee2d007e233e7db93aefba4fef08e7aa68f5ab7.json +0 -20
- package/backend/.sqlx/query-c614e6056b244ca07f1b9d44e7edc9d5819225c6f8d9e077070c6e518a17f50b.json +0 -12
- package/backend/.sqlx/query-c67259be8bf4ee0cfd32167b2aa3b7fe9192809181a8171bf1c2d6df731967ae.json +0 -12
- package/backend/.sqlx/query-d2d0a1b985ebbca6a2b3e882a221a219f3199890fa640afc946ef1a792d6d8de.json +0 -12
- package/backend/.sqlx/query-d30aa5786757f32bf2b9c5fe51a45e506c71c28c5994e430d9b0546adb15ffa2.json +0 -20
- package/backend/.sqlx/query-d3b9ea1de1576af71b312924ce7f4ea8ae5dbe2ac138ea3b4470f2d5cd734846.json +0 -12
- package/backend/.sqlx/query-ed8456646fa69ddd412441955f06ff22bfb790f29466450735e0b8bb1bc4ec94.json +0 -12
- package/backend/Cargo.toml +0 -71
- package/backend/build.rs +0 -32
- package/backend/migrations/20250617183714_init.sql +0 -44
- package/backend/migrations/20250620212427_execution_processes.sql +0 -25
- package/backend/migrations/20250620214100_remove_stdout_stderr_from_task_attempts.sql +0 -28
- package/backend/migrations/20250621120000_relate_activities_to_execution_processes.sql +0 -23
- package/backend/migrations/20250623120000_executor_sessions.sql +0 -17
- package/backend/migrations/20250623130000_add_executor_type_to_execution_processes.sql +0 -4
- package/backend/migrations/20250625000000_add_dev_script_to_projects.sql +0 -4
- package/backend/migrations/20250701000000_add_branch_to_task_attempts.sql +0 -2
- package/backend/migrations/20250701000001_add_pr_tracking_to_task_attempts.sql +0 -5
- package/backend/migrations/20250701120000_add_assistant_message_to_executor_sessions.sql +0 -2
- package/backend/migrations/20250708000000_add_base_branch_to_task_attempts.sql +0 -2
- package/backend/migrations/20250709000000_add_worktree_deleted_flag.sql +0 -2
- package/backend/migrations/20250710000000_add_setup_completion.sql +0 -3
- package/backend/migrations/20250715154859_add_task_templates.sql +0 -25
- package/backend/migrations/20250716143725_add_default_templates.sql +0 -174
- package/backend/migrations/20250716161432_update_executor_names_to_kebab_case.sql +0 -20
- package/backend/migrations/20250716170000_add_parent_task_to_tasks.sql +0 -7
- package/backend/migrations/20250717000000_drop_task_attempt_activities.sql +0 -9
- package/backend/migrations/20250719000000_add_cleanup_script_to_projects.sql +0 -2
- package/backend/migrations/20250720000000_add_cleanupscript_to_process_type_constraint.sql +0 -25
- package/backend/migrations/20250723000000_add_wish_to_tasks.sql +0 -7
- package/backend/migrations/20250724000000_remove_unique_wish_constraint.sql +0 -5
- package/backend/scripts/toast-notification.ps1 +0 -23
- package/backend/sounds/abstract-sound1.wav +0 -0
- package/backend/sounds/abstract-sound2.wav +0 -0
- package/backend/sounds/abstract-sound3.wav +0 -0
- package/backend/sounds/abstract-sound4.wav +0 -0
- package/backend/sounds/cow-mooing.wav +0 -0
- package/backend/sounds/phone-vibration.wav +0 -0
- package/backend/sounds/rooster.wav +0 -0
- package/backend/src/app_state.rs +0 -218
- package/backend/src/bin/generate_types.rs +0 -189
- package/backend/src/bin/mcp_task_server.rs +0 -191
- package/backend/src/execution_monitor.rs +0 -1193
- package/backend/src/executor.rs +0 -1053
- package/backend/src/executors/amp.rs +0 -697
- package/backend/src/executors/ccr.rs +0 -91
- package/backend/src/executors/charm_opencode.rs +0 -113
- package/backend/src/executors/claude.rs +0 -887
- package/backend/src/executors/cleanup_script.rs +0 -124
- package/backend/src/executors/dev_server.rs +0 -53
- package/backend/src/executors/echo.rs +0 -79
- package/backend/src/executors/gemini/config.rs +0 -67
- package/backend/src/executors/gemini/streaming.rs +0 -363
- package/backend/src/executors/gemini.rs +0 -765
- package/backend/src/executors/mod.rs +0 -23
- package/backend/src/executors/opencode_ai.rs +0 -113
- package/backend/src/executors/setup_script.rs +0 -130
- package/backend/src/executors/sst_opencode/filter.rs +0 -184
- package/backend/src/executors/sst_opencode/tools.rs +0 -139
- package/backend/src/executors/sst_opencode.rs +0 -756
- package/backend/src/lib.rs +0 -45
- package/backend/src/main.rs +0 -324
- package/backend/src/mcp/mod.rs +0 -1
- package/backend/src/mcp/task_server.rs +0 -850
- package/backend/src/middleware/mod.rs +0 -3
- package/backend/src/middleware/model_loaders.rs +0 -242
- package/backend/src/models/api_response.rs +0 -36
- package/backend/src/models/config.rs +0 -375
- package/backend/src/models/execution_process.rs +0 -430
- package/backend/src/models/executor_session.rs +0 -225
- package/backend/src/models/mod.rs +0 -12
- package/backend/src/models/project.rs +0 -356
- package/backend/src/models/task.rs +0 -345
- package/backend/src/models/task_attempt.rs +0 -1214
- package/backend/src/models/task_template.rs +0 -146
- package/backend/src/openapi.rs +0 -93
- package/backend/src/routes/auth.rs +0 -297
- package/backend/src/routes/config.rs +0 -385
- package/backend/src/routes/filesystem.rs +0 -228
- package/backend/src/routes/health.rs +0 -16
- package/backend/src/routes/mod.rs +0 -9
- package/backend/src/routes/projects.rs +0 -562
- package/backend/src/routes/stream.rs +0 -244
- package/backend/src/routes/task_attempts.rs +0 -1172
- package/backend/src/routes/task_templates.rs +0 -229
- package/backend/src/routes/tasks.rs +0 -353
- package/backend/src/services/analytics.rs +0 -216
- package/backend/src/services/git_service.rs +0 -1321
- package/backend/src/services/github_service.rs +0 -307
- package/backend/src/services/mod.rs +0 -13
- package/backend/src/services/notification_service.rs +0 -263
- package/backend/src/services/pr_monitor.rs +0 -214
- package/backend/src/services/process_service.rs +0 -940
- package/backend/src/utils/path.rs +0 -96
- package/backend/src/utils/shell.rs +0 -19
- package/backend/src/utils/text.rs +0 -24
- package/backend/src/utils/worktree_manager.rs +0 -578
- package/backend/src/utils.rs +0 -125
- package/backend/test.db +0 -0
- package/build-npm-package.sh +0 -61
- package/dev_assets_seed/config.json +0 -19
- package/frontend/.eslintrc.json +0 -25
- package/frontend/.prettierrc.json +0 -8
- package/frontend/components.json +0 -17
- package/frontend/index.html +0 -19
- package/frontend/package-lock.json +0 -7321
- package/frontend/package.json +0 -61
- package/frontend/postcss.config.js +0 -6
- package/frontend/public/android-chrome-192x192.png +0 -0
- package/frontend/public/android-chrome-512x512.png +0 -0
- package/frontend/public/apple-touch-icon.png +0 -0
- package/frontend/public/automagik-forge-logo-dark.svg +0 -3
- package/frontend/public/automagik-forge-logo.svg +0 -3
- package/frontend/public/automagik-forge-screenshot-overview.png +0 -0
- package/frontend/public/favicon-16x16.png +0 -0
- package/frontend/public/favicon-32x32.png +0 -0
- package/frontend/public/favicon.ico +0 -0
- package/frontend/public/site.webmanifest +0 -1
- package/frontend/public/viba-kanban-favicon.png +0 -0
- package/frontend/src/App.tsx +0 -157
- package/frontend/src/components/DisclaimerDialog.tsx +0 -106
- package/frontend/src/components/GitHubLoginDialog.tsx +0 -314
- package/frontend/src/components/OnboardingDialog.tsx +0 -185
- package/frontend/src/components/PrivacyOptInDialog.tsx +0 -130
- package/frontend/src/components/ProvidePatDialog.tsx +0 -98
- package/frontend/src/components/TaskTemplateManager.tsx +0 -336
- package/frontend/src/components/config-provider.tsx +0 -119
- package/frontend/src/components/context/TaskDetailsContextProvider.tsx +0 -470
- package/frontend/src/components/context/taskDetailsContext.ts +0 -125
- package/frontend/src/components/keyboard-shortcuts-demo.tsx +0 -35
- package/frontend/src/components/layout/navbar.tsx +0 -86
- package/frontend/src/components/logo.tsx +0 -44
- package/frontend/src/components/projects/ProjectCard.tsx +0 -155
- package/frontend/src/components/projects/project-detail.tsx +0 -251
- package/frontend/src/components/projects/project-form-fields.tsx +0 -238
- package/frontend/src/components/projects/project-form.tsx +0 -301
- package/frontend/src/components/projects/project-list.tsx +0 -200
- package/frontend/src/components/projects/projects-page.tsx +0 -20
- package/frontend/src/components/tasks/BranchSelector.tsx +0 -169
- package/frontend/src/components/tasks/DeleteFileConfirmationDialog.tsx +0 -94
- package/frontend/src/components/tasks/EditorSelectionDialog.tsx +0 -119
- package/frontend/src/components/tasks/TaskCard.tsx +0 -154
- package/frontend/src/components/tasks/TaskDetails/CollapsibleToolbar.tsx +0 -33
- package/frontend/src/components/tasks/TaskDetails/DiffCard.tsx +0 -109
- package/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx +0 -135
- package/frontend/src/components/tasks/TaskDetails/DiffFile.tsx +0 -296
- package/frontend/src/components/tasks/TaskDetails/DiffTab.tsx +0 -32
- package/frontend/src/components/tasks/TaskDetails/DisplayConversationEntry.tsx +0 -392
- package/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx +0 -256
- package/frontend/src/components/tasks/TaskDetails/LogsTab/ConversationEntry.tsx +0 -56
- package/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx +0 -92
- package/frontend/src/components/tasks/TaskDetails/LogsTab/Prompt.tsx +0 -22
- package/frontend/src/components/tasks/TaskDetails/LogsTab/SetupScriptRunning.tsx +0 -49
- package/frontend/src/components/tasks/TaskDetails/LogsTab.tsx +0 -186
- package/frontend/src/components/tasks/TaskDetails/ProcessesTab.tsx +0 -288
- package/frontend/src/components/tasks/TaskDetails/RelatedTasksTab.tsx +0 -216
- package/frontend/src/components/tasks/TaskDetails/TabNavigation.tsx +0 -93
- package/frontend/src/components/tasks/TaskDetailsHeader.tsx +0 -169
- package/frontend/src/components/tasks/TaskDetailsPanel.tsx +0 -126
- package/frontend/src/components/tasks/TaskDetailsToolbar.tsx +0 -302
- package/frontend/src/components/tasks/TaskFollowUpSection.tsx +0 -130
- package/frontend/src/components/tasks/TaskFormDialog.tsx +0 -400
- package/frontend/src/components/tasks/TaskKanbanBoard.tsx +0 -180
- package/frontend/src/components/tasks/Toolbar/CreateAttempt.tsx +0 -259
- package/frontend/src/components/tasks/Toolbar/CreatePRDialog.tsx +0 -243
- package/frontend/src/components/tasks/Toolbar/CurrentAttempt.tsx +0 -899
- package/frontend/src/components/tasks/index.ts +0 -2
- package/frontend/src/components/theme-provider.tsx +0 -82
- package/frontend/src/components/theme-toggle.tsx +0 -36
- package/frontend/src/components/ui/alert.tsx +0 -59
- package/frontend/src/components/ui/auto-expanding-textarea.tsx +0 -70
- package/frontend/src/components/ui/badge.tsx +0 -36
- package/frontend/src/components/ui/button.tsx +0 -56
- package/frontend/src/components/ui/card.tsx +0 -86
- package/frontend/src/components/ui/checkbox.tsx +0 -44
- package/frontend/src/components/ui/chip.tsx +0 -25
- package/frontend/src/components/ui/dialog.tsx +0 -124
- package/frontend/src/components/ui/dropdown-menu.tsx +0 -198
- package/frontend/src/components/ui/file-search-textarea.tsx +0 -292
- package/frontend/src/components/ui/folder-picker.tsx +0 -279
- package/frontend/src/components/ui/input.tsx +0 -25
- package/frontend/src/components/ui/label.tsx +0 -24
- package/frontend/src/components/ui/loader.tsx +0 -26
- package/frontend/src/components/ui/markdown-renderer.tsx +0 -75
- package/frontend/src/components/ui/select.tsx +0 -160
- package/frontend/src/components/ui/separator.tsx +0 -31
- package/frontend/src/components/ui/shadcn-io/kanban/index.tsx +0 -185
- package/frontend/src/components/ui/table.tsx +0 -117
- package/frontend/src/components/ui/tabs.tsx +0 -53
- package/frontend/src/components/ui/textarea.tsx +0 -22
- package/frontend/src/components/ui/tooltip.tsx +0 -28
- package/frontend/src/hooks/useNormalizedConversation.ts +0 -440
- package/frontend/src/index.css +0 -225
- package/frontend/src/lib/api.ts +0 -630
- package/frontend/src/lib/keyboard-shortcuts.ts +0 -266
- package/frontend/src/lib/responsive-config.ts +0 -70
- package/frontend/src/lib/types.ts +0 -39
- package/frontend/src/lib/utils.ts +0 -10
- package/frontend/src/main.tsx +0 -50
- package/frontend/src/pages/McpServers.tsx +0 -418
- package/frontend/src/pages/Settings.tsx +0 -610
- package/frontend/src/pages/project-tasks.tsx +0 -575
- package/frontend/src/pages/projects.tsx +0 -18
- package/frontend/src/vite-env.d.ts +0 -1
- package/frontend/tailwind.config.js +0 -125
- package/frontend/tsconfig.json +0 -26
- package/frontend/tsconfig.node.json +0 -10
- package/frontend/vite.config.ts +0 -33
- package/npx-cli/README.md +0 -159
- package/npx-cli/automagik-forge-0.1.0.tgz +0 -0
- package/npx-cli/automagik-forge-0.1.10.tgz +0 -0
- package/npx-cli/package.json +0 -17
- package/npx-cli/vibe-kanban-0.0.55.tgz +0 -0
- package/pnpm-workspace.yaml +0 -2
- package/rust-toolchain.toml +0 -11
- package/rustfmt.toml +0 -3
- package/scripts/load-env.js +0 -43
- package/scripts/mcp_test.js +0 -374
- package/scripts/prepare-db.js +0 -45
- package/scripts/setup-dev-environment.js +0 -274
- package/scripts/start-mcp-sse.js +0 -70
- package/scripts/test-debug.js +0 -32
- package/scripts/test-mcp-sse.js +0 -138
- package/scripts/test-simple.js +0 -44
- package/scripts/test-wish-final.js +0 -179
- package/scripts/test-wish-system.js +0 -221
- package/shared/types.ts +0 -182
- package/test-npm-package.sh +0 -42
- /package/{npx-cli/bin → bin}/cli.js +0 -0
@@ -1,23 +0,0 @@
|
|
1
|
-
pub mod amp;
|
2
|
-
pub mod ccr;
|
3
|
-
pub mod charm_opencode;
|
4
|
-
pub mod claude;
|
5
|
-
pub mod cleanup_script;
|
6
|
-
pub mod dev_server;
|
7
|
-
pub mod echo;
|
8
|
-
pub mod gemini;
|
9
|
-
pub mod opencode_ai;
|
10
|
-
pub mod setup_script;
|
11
|
-
pub mod sst_opencode;
|
12
|
-
|
13
|
-
pub use amp::AmpExecutor;
|
14
|
-
pub use ccr::CCRExecutor;
|
15
|
-
pub use charm_opencode::CharmOpencodeExecutor;
|
16
|
-
pub use claude::ClaudeExecutor;
|
17
|
-
pub use cleanup_script::CleanupScriptExecutor;
|
18
|
-
pub use dev_server::DevServerExecutor;
|
19
|
-
pub use echo::EchoExecutor;
|
20
|
-
pub use gemini::GeminiExecutor;
|
21
|
-
pub use opencode_ai::OpencodeAiExecutor;
|
22
|
-
pub use setup_script::SetupScriptExecutor;
|
23
|
-
pub use sst_opencode::SstOpencodeExecutor;
|
@@ -1,113 +0,0 @@
|
|
1
|
-
use async_trait::async_trait;
|
2
|
-
use command_group::{AsyncCommandGroup, AsyncGroupChild};
|
3
|
-
use uuid::Uuid;
|
4
|
-
|
5
|
-
use crate::{
|
6
|
-
executor::{Executor, ExecutorError},
|
7
|
-
models::task::Task,
|
8
|
-
utils::shell::get_shell_command,
|
9
|
-
};
|
10
|
-
|
11
|
-
/// An executor that uses OpenCode to process tasks
|
12
|
-
pub struct OpencodeAiExecutor;
|
13
|
-
|
14
|
-
#[async_trait]
|
15
|
-
impl Executor for OpencodeAiExecutor {
|
16
|
-
async fn spawn(
|
17
|
-
&self,
|
18
|
-
pool: &sqlx::SqlitePool,
|
19
|
-
task_id: Uuid,
|
20
|
-
worktree_path: &str,
|
21
|
-
) -> Result<AsyncGroupChild, ExecutorError> {
|
22
|
-
// Get the task to fetch its description
|
23
|
-
let task = Task::find_by_id(pool, task_id)
|
24
|
-
.await?
|
25
|
-
.ok_or(ExecutorError::TaskNotFound)?;
|
26
|
-
|
27
|
-
use std::process::Stdio;
|
28
|
-
|
29
|
-
use tokio::process::Command;
|
30
|
-
|
31
|
-
let prompt = if let Some(task_description) = task.description {
|
32
|
-
format!(
|
33
|
-
r#"project_id: {}
|
34
|
-
|
35
|
-
Task title: {}
|
36
|
-
Task description: {}"#,
|
37
|
-
task.project_id, task.title, task_description
|
38
|
-
)
|
39
|
-
} else {
|
40
|
-
format!(
|
41
|
-
r#"project_id: {}
|
42
|
-
|
43
|
-
Task title: {}"#,
|
44
|
-
task.project_id, task.title
|
45
|
-
)
|
46
|
-
};
|
47
|
-
|
48
|
-
// Use shell command for cross-platform compatibility
|
49
|
-
let (shell_cmd, shell_arg) = get_shell_command();
|
50
|
-
let opencode_command = format!(
|
51
|
-
"opencode -p \"{}\" --output-format=json",
|
52
|
-
prompt.replace('"', "\\\"")
|
53
|
-
);
|
54
|
-
|
55
|
-
let mut command = Command::new(shell_cmd);
|
56
|
-
command
|
57
|
-
.kill_on_drop(true)
|
58
|
-
.stdout(Stdio::piped())
|
59
|
-
.stderr(Stdio::piped())
|
60
|
-
.current_dir(worktree_path)
|
61
|
-
.arg(shell_arg)
|
62
|
-
.arg(opencode_command);
|
63
|
-
|
64
|
-
let child = command
|
65
|
-
.group_spawn() // Create new process group so we can kill entire tree
|
66
|
-
.map_err(|e| {
|
67
|
-
crate::executor::SpawnContext::from_command(&command, "OpenCode AI")
|
68
|
-
.with_task(task_id, Some(task.title.clone()))
|
69
|
-
.with_context("OpenCode AI CLI execution for new task")
|
70
|
-
.spawn_error(e)
|
71
|
-
})?;
|
72
|
-
|
73
|
-
Ok(child)
|
74
|
-
}
|
75
|
-
|
76
|
-
async fn spawn_followup(
|
77
|
-
&self,
|
78
|
-
_pool: &sqlx::SqlitePool,
|
79
|
-
_task_id: Uuid,
|
80
|
-
_session_id: &str,
|
81
|
-
prompt: &str,
|
82
|
-
worktree_path: &str,
|
83
|
-
) -> Result<AsyncGroupChild, ExecutorError> {
|
84
|
-
use std::process::Stdio;
|
85
|
-
|
86
|
-
use tokio::process::Command;
|
87
|
-
|
88
|
-
// CharmOpencode doesn't support session-based followup, so we ignore session_id
|
89
|
-
// and just run with the new prompt
|
90
|
-
let (shell_cmd, shell_arg) = get_shell_command();
|
91
|
-
let opencode_command = format!(
|
92
|
-
"opencode -p \"{}\" --output-format=json",
|
93
|
-
prompt.replace('"', "\\\"")
|
94
|
-
);
|
95
|
-
|
96
|
-
let mut command = Command::new(shell_cmd);
|
97
|
-
command
|
98
|
-
.kill_on_drop(true)
|
99
|
-
.stdout(Stdio::piped())
|
100
|
-
.stderr(Stdio::piped())
|
101
|
-
.current_dir(worktree_path)
|
102
|
-
.arg(shell_arg)
|
103
|
-
.arg(&opencode_command);
|
104
|
-
|
105
|
-
let child = command.group_spawn().map_err(|e| {
|
106
|
-
crate::executor::SpawnContext::from_command(&command, "OpenCode AI")
|
107
|
-
.with_context("OpenCode AI CLI followup execution")
|
108
|
-
.spawn_error(e)
|
109
|
-
})?;
|
110
|
-
|
111
|
-
Ok(child)
|
112
|
-
}
|
113
|
-
}
|
@@ -1,130 +0,0 @@
|
|
1
|
-
use async_trait::async_trait;
|
2
|
-
use command_group::{AsyncCommandGroup, AsyncGroupChild};
|
3
|
-
use tokio::process::Command;
|
4
|
-
use uuid::Uuid;
|
5
|
-
|
6
|
-
use crate::{
|
7
|
-
executor::{Executor, ExecutorError},
|
8
|
-
models::{project::Project, task::Task},
|
9
|
-
utils::shell::get_shell_command,
|
10
|
-
};
|
11
|
-
|
12
|
-
/// Executor for running project setup scripts
|
13
|
-
pub struct SetupScriptExecutor {
|
14
|
-
pub script: String,
|
15
|
-
}
|
16
|
-
|
17
|
-
impl SetupScriptExecutor {
|
18
|
-
pub fn new(script: String) -> Self {
|
19
|
-
Self { script }
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
#[async_trait]
|
24
|
-
impl Executor for SetupScriptExecutor {
|
25
|
-
async fn spawn(
|
26
|
-
&self,
|
27
|
-
pool: &sqlx::SqlitePool,
|
28
|
-
task_id: Uuid,
|
29
|
-
worktree_path: &str,
|
30
|
-
) -> Result<AsyncGroupChild, ExecutorError> {
|
31
|
-
// Validate the task and project exist
|
32
|
-
let task = Task::find_by_id(pool, task_id)
|
33
|
-
.await?
|
34
|
-
.ok_or(ExecutorError::TaskNotFound)?;
|
35
|
-
|
36
|
-
let _project = Project::find_by_id(pool, task.project_id)
|
37
|
-
.await?
|
38
|
-
.ok_or(ExecutorError::TaskNotFound)?; // Reuse TaskNotFound for simplicity
|
39
|
-
|
40
|
-
let (shell_cmd, shell_arg) = get_shell_command();
|
41
|
-
let mut command = Command::new(shell_cmd);
|
42
|
-
command
|
43
|
-
.kill_on_drop(true)
|
44
|
-
.stdout(std::process::Stdio::piped())
|
45
|
-
.stderr(std::process::Stdio::piped())
|
46
|
-
.arg(shell_arg)
|
47
|
-
.arg(&self.script)
|
48
|
-
.current_dir(worktree_path);
|
49
|
-
|
50
|
-
let child = command.group_spawn().map_err(|e| {
|
51
|
-
crate::executor::SpawnContext::from_command(&command, "SetupScript")
|
52
|
-
.with_task(task_id, Some(task.title.clone()))
|
53
|
-
.with_context("Setup script execution")
|
54
|
-
.spawn_error(e)
|
55
|
-
})?;
|
56
|
-
|
57
|
-
Ok(child)
|
58
|
-
}
|
59
|
-
|
60
|
-
/// Normalize setup script logs into a readable format
|
61
|
-
fn normalize_logs(
|
62
|
-
&self,
|
63
|
-
logs: &str,
|
64
|
-
_worktree_path: &str,
|
65
|
-
) -> Result<crate::executor::NormalizedConversation, String> {
|
66
|
-
let mut entries = Vec::new();
|
67
|
-
|
68
|
-
// Add script command as first entry
|
69
|
-
entries.push(crate::executor::NormalizedEntry {
|
70
|
-
timestamp: None,
|
71
|
-
entry_type: crate::executor::NormalizedEntryType::SystemMessage,
|
72
|
-
content: format!("Executing setup script:\n{}", self.script),
|
73
|
-
metadata: None,
|
74
|
-
});
|
75
|
-
|
76
|
-
// Process the logs - split by lines and create entries
|
77
|
-
if !logs.trim().is_empty() {
|
78
|
-
let lines: Vec<&str> = logs.lines().collect();
|
79
|
-
let mut current_chunk = String::new();
|
80
|
-
|
81
|
-
for line in lines {
|
82
|
-
current_chunk.push_str(line);
|
83
|
-
current_chunk.push('\n');
|
84
|
-
|
85
|
-
// Create entry for every 10 lines or when we encounter an error-like line
|
86
|
-
if current_chunk.lines().count() >= 10
|
87
|
-
|| line.to_lowercase().contains("error")
|
88
|
-
|| line.to_lowercase().contains("failed")
|
89
|
-
|| line.to_lowercase().contains("exception")
|
90
|
-
{
|
91
|
-
let entry_type = if line.to_lowercase().contains("error")
|
92
|
-
|| line.to_lowercase().contains("failed")
|
93
|
-
|| line.to_lowercase().contains("exception")
|
94
|
-
{
|
95
|
-
crate::executor::NormalizedEntryType::ErrorMessage
|
96
|
-
} else {
|
97
|
-
crate::executor::NormalizedEntryType::SystemMessage
|
98
|
-
};
|
99
|
-
|
100
|
-
entries.push(crate::executor::NormalizedEntry {
|
101
|
-
timestamp: Some(chrono::Utc::now().to_rfc3339()),
|
102
|
-
entry_type,
|
103
|
-
content: current_chunk.trim().to_string(),
|
104
|
-
metadata: None,
|
105
|
-
});
|
106
|
-
|
107
|
-
current_chunk.clear();
|
108
|
-
}
|
109
|
-
}
|
110
|
-
|
111
|
-
// Add any remaining content
|
112
|
-
if !current_chunk.trim().is_empty() {
|
113
|
-
entries.push(crate::executor::NormalizedEntry {
|
114
|
-
timestamp: Some(chrono::Utc::now().to_rfc3339()),
|
115
|
-
entry_type: crate::executor::NormalizedEntryType::SystemMessage,
|
116
|
-
content: current_chunk.trim().to_string(),
|
117
|
-
metadata: None,
|
118
|
-
});
|
119
|
-
}
|
120
|
-
}
|
121
|
-
|
122
|
-
Ok(crate::executor::NormalizedConversation {
|
123
|
-
entries,
|
124
|
-
session_id: None,
|
125
|
-
executor_type: "setup-script".to_string(),
|
126
|
-
prompt: Some(self.script.clone()),
|
127
|
-
summary: None,
|
128
|
-
})
|
129
|
-
}
|
130
|
-
}
|
@@ -1,184 +0,0 @@
|
|
1
|
-
use lazy_static::lazy_static;
|
2
|
-
use regex::Regex;
|
3
|
-
|
4
|
-
lazy_static! {
|
5
|
-
static ref OPENCODE_LOG_REGEX: Regex = Regex::new(r"^(INFO|DEBUG|WARN|ERROR)\s+.*").unwrap();
|
6
|
-
static ref SESSION_ID_REGEX: Regex = Regex::new(r".*\b(id|session|sessionID)=([^ ]+)").unwrap();
|
7
|
-
static ref TOOL_USAGE_REGEX: Regex = Regex::new(r"^\|\s*([a-zA-Z]+)\s*(.*)").unwrap();
|
8
|
-
static ref NPM_WARN_REGEX: Regex = Regex::new(r"^npm warn .*").unwrap();
|
9
|
-
}
|
10
|
-
|
11
|
-
/// Filter for OpenCode stderr output
|
12
|
-
pub struct OpenCodeFilter;
|
13
|
-
|
14
|
-
impl OpenCodeFilter {
|
15
|
-
/// Check if a line should be skipped as noise
|
16
|
-
pub fn is_noise(line: &str) -> bool {
|
17
|
-
let trimmed = line.trim();
|
18
|
-
|
19
|
-
// Empty lines are noise
|
20
|
-
if trimmed.is_empty() {
|
21
|
-
return true;
|
22
|
-
}
|
23
|
-
|
24
|
-
// Strip ANSI escape codes for analysis
|
25
|
-
let cleaned = Self::strip_ansi_codes(trimmed);
|
26
|
-
let cleaned_trim = cleaned.trim();
|
27
|
-
|
28
|
-
// Skip tool calls - they are NOT noise
|
29
|
-
if TOOL_USAGE_REGEX.is_match(cleaned_trim) {
|
30
|
-
return false;
|
31
|
-
}
|
32
|
-
|
33
|
-
// OpenCode log lines are noise (includes session logs)
|
34
|
-
if is_opencode_log_line(cleaned_trim) {
|
35
|
-
return true;
|
36
|
-
}
|
37
|
-
|
38
|
-
if NPM_WARN_REGEX.is_match(cleaned_trim) {
|
39
|
-
return true;
|
40
|
-
}
|
41
|
-
|
42
|
-
// Spinner glyphs
|
43
|
-
if cleaned_trim.len() == 1 && "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏".contains(cleaned_trim) {
|
44
|
-
return true;
|
45
|
-
}
|
46
|
-
|
47
|
-
// Banner lines containing block glyphs (Unicode Block Elements range)
|
48
|
-
if cleaned_trim
|
49
|
-
.chars()
|
50
|
-
.any(|c| ('\u{2580}'..='\u{259F}').contains(&c))
|
51
|
-
{
|
52
|
-
return true;
|
53
|
-
}
|
54
|
-
|
55
|
-
// UI/stats frames using Box Drawing glyphs (U+2500-257F)
|
56
|
-
if cleaned_trim
|
57
|
-
.chars()
|
58
|
-
.any(|c| ('\u{2500}'..='\u{257F}').contains(&c))
|
59
|
-
{
|
60
|
-
return true;
|
61
|
-
}
|
62
|
-
|
63
|
-
// Model banner (@ with spaces)
|
64
|
-
if cleaned_trim.starts_with("@ ") {
|
65
|
-
return true;
|
66
|
-
}
|
67
|
-
|
68
|
-
// Share link
|
69
|
-
if cleaned_trim.starts_with("~") && cleaned_trim.contains("https://opencode.ai/s/") {
|
70
|
-
return true;
|
71
|
-
}
|
72
|
-
|
73
|
-
// Everything else (assistant messages) is NOT noise
|
74
|
-
false
|
75
|
-
}
|
76
|
-
|
77
|
-
pub fn is_stderr(_line: &str) -> bool {
|
78
|
-
false
|
79
|
-
}
|
80
|
-
|
81
|
-
/// Strip ANSI escape codes from text (conservative)
|
82
|
-
pub fn strip_ansi_codes(text: &str) -> String {
|
83
|
-
// Handle both unicode escape sequences and raw ANSI codes
|
84
|
-
let result = text.replace("\\u001b", "\x1b");
|
85
|
-
|
86
|
-
let mut cleaned = String::new();
|
87
|
-
let mut chars = result.chars().peekable();
|
88
|
-
|
89
|
-
while let Some(ch) = chars.next() {
|
90
|
-
if ch == '\x1b' {
|
91
|
-
// Skip ANSI escape sequence
|
92
|
-
if chars.peek() == Some(&'[') {
|
93
|
-
chars.next(); // consume '['
|
94
|
-
// Skip until we find a letter (end of ANSI sequence)
|
95
|
-
for next_ch in chars.by_ref() {
|
96
|
-
if next_ch.is_ascii_alphabetic() {
|
97
|
-
break;
|
98
|
-
}
|
99
|
-
}
|
100
|
-
}
|
101
|
-
} else {
|
102
|
-
cleaned.push(ch);
|
103
|
-
}
|
104
|
-
}
|
105
|
-
|
106
|
-
cleaned
|
107
|
-
}
|
108
|
-
}
|
109
|
-
|
110
|
-
/// Detect if a line is an OpenCode log line format using regex
|
111
|
-
pub fn is_opencode_log_line(line: &str) -> bool {
|
112
|
-
OPENCODE_LOG_REGEX.is_match(line)
|
113
|
-
}
|
114
|
-
|
115
|
-
/// Parse session_id from OpenCode log lines
|
116
|
-
pub fn parse_session_id_from_line(line: &str) -> Option<String> {
|
117
|
-
// Only apply to OpenCode log lines
|
118
|
-
if !is_opencode_log_line(line) {
|
119
|
-
return None;
|
120
|
-
}
|
121
|
-
|
122
|
-
// Try regex for session ID extraction from service=session logs
|
123
|
-
if let Some(captures) = SESSION_ID_REGEX.captures(line) {
|
124
|
-
if let Some(id) = captures.get(2) {
|
125
|
-
return Some(id.as_str().to_string());
|
126
|
-
}
|
127
|
-
}
|
128
|
-
|
129
|
-
None
|
130
|
-
}
|
131
|
-
|
132
|
-
/// Get the tool usage regex for parsing tool patterns
|
133
|
-
pub fn tool_usage_regex() -> &'static Regex {
|
134
|
-
&TOOL_USAGE_REGEX
|
135
|
-
}
|
136
|
-
|
137
|
-
#[cfg(test)]
|
138
|
-
mod tests {
|
139
|
-
#[test]
|
140
|
-
fn test_session_id_extraction() {
|
141
|
-
use crate::executors::sst_opencode::filter::parse_session_id_from_line;
|
142
|
-
|
143
|
-
// Test session ID extraction from session= format (only works on OpenCode log lines)
|
144
|
-
assert_eq!(
|
145
|
-
parse_session_id_from_line("INFO session=ses_abc123 starting"),
|
146
|
-
Some("ses_abc123".to_string())
|
147
|
-
);
|
148
|
-
|
149
|
-
assert_eq!(
|
150
|
-
parse_session_id_from_line("DEBUG id=debug_id process"),
|
151
|
-
Some("debug_id".to_string())
|
152
|
-
);
|
153
|
-
|
154
|
-
// Test lines without log prefix (should return None)
|
155
|
-
assert_eq!(
|
156
|
-
parse_session_id_from_line("session=simple_id chatting"),
|
157
|
-
None
|
158
|
-
);
|
159
|
-
|
160
|
-
// Test no session ID
|
161
|
-
assert_eq!(parse_session_id_from_line("No session here"), None);
|
162
|
-
assert_eq!(parse_session_id_from_line(""), None);
|
163
|
-
}
|
164
|
-
|
165
|
-
#[test]
|
166
|
-
fn test_ansi_code_stripping() {
|
167
|
-
use crate::executors::sst_opencode::filter::OpenCodeFilter;
|
168
|
-
|
169
|
-
// Test ANSI escape sequence removal
|
170
|
-
let ansi_text = "\x1b[31mRed text\x1b[0m normal text";
|
171
|
-
let cleaned = OpenCodeFilter::strip_ansi_codes(ansi_text);
|
172
|
-
assert_eq!(cleaned, "Red text normal text");
|
173
|
-
|
174
|
-
// Test unicode escape sequences
|
175
|
-
let unicode_ansi = "Text with \\u001b[32mgreen\\u001b[0m color";
|
176
|
-
let cleaned = OpenCodeFilter::strip_ansi_codes(unicode_ansi);
|
177
|
-
assert_eq!(cleaned, "Text with green color");
|
178
|
-
|
179
|
-
// Test text without ANSI codes (unchanged)
|
180
|
-
let plain_text = "Regular text without codes";
|
181
|
-
let cleaned = OpenCodeFilter::strip_ansi_codes(plain_text);
|
182
|
-
assert_eq!(cleaned, plain_text);
|
183
|
-
}
|
184
|
-
}
|
@@ -1,139 +0,0 @@
|
|
1
|
-
use serde_json::{json, Value};
|
2
|
-
|
3
|
-
use crate::utils::path::make_path_relative;
|
4
|
-
|
5
|
-
/// Normalize tool names to match frontend expectations for purple box styling
|
6
|
-
pub fn normalize_tool_name(tool_name: &str) -> String {
|
7
|
-
match tool_name {
|
8
|
-
"Todo" => "todowrite".to_string(), // Generic TODO tool → todowrite
|
9
|
-
"TodoWrite" => "todowrite".to_string(),
|
10
|
-
"TodoRead" => "todoread".to_string(),
|
11
|
-
_ => tool_name.to_string(),
|
12
|
-
}
|
13
|
-
}
|
14
|
-
|
15
|
-
/// Helper function to determine action type for tool usage
|
16
|
-
pub fn determine_action_type(tool_name: &str, input: &Value, worktree_path: &str) -> Value {
|
17
|
-
match tool_name.to_lowercase().as_str() {
|
18
|
-
"read" => {
|
19
|
-
if let Some(file_path) = input.get("filePath").and_then(|p| p.as_str()) {
|
20
|
-
json!({
|
21
|
-
"action": "file_read",
|
22
|
-
"path": make_path_relative(file_path, worktree_path)
|
23
|
-
})
|
24
|
-
} else {
|
25
|
-
json!({"action": "other", "description": "File read operation"})
|
26
|
-
}
|
27
|
-
}
|
28
|
-
"write" | "edit" => {
|
29
|
-
if let Some(file_path) = input.get("filePath").and_then(|p| p.as_str()) {
|
30
|
-
json!({
|
31
|
-
"action": "file_write",
|
32
|
-
"path": make_path_relative(file_path, worktree_path)
|
33
|
-
})
|
34
|
-
} else {
|
35
|
-
json!({"action": "other", "description": "File write operation"})
|
36
|
-
}
|
37
|
-
}
|
38
|
-
"bash" => {
|
39
|
-
if let Some(command) = input.get("command").and_then(|c| c.as_str()) {
|
40
|
-
json!({"action": "command_run", "command": command})
|
41
|
-
} else {
|
42
|
-
json!({"action": "other", "description": "Command execution"})
|
43
|
-
}
|
44
|
-
}
|
45
|
-
"grep" => {
|
46
|
-
if let Some(pattern) = input.get("pattern").and_then(|p| p.as_str()) {
|
47
|
-
json!({"action": "search", "query": pattern})
|
48
|
-
} else {
|
49
|
-
json!({"action": "other", "description": "Search operation"})
|
50
|
-
}
|
51
|
-
}
|
52
|
-
"todowrite" | "todoread" => {
|
53
|
-
json!({"action": "other", "description": "TODO list management"})
|
54
|
-
}
|
55
|
-
_ => json!({"action": "other", "description": format!("Tool: {}", tool_name)}),
|
56
|
-
}
|
57
|
-
}
|
58
|
-
|
59
|
-
/// Helper function to generate concise content for tool usage
|
60
|
-
pub fn generate_tool_content(tool_name: &str, input: &Value, worktree_path: &str) -> String {
|
61
|
-
match tool_name.to_lowercase().as_str() {
|
62
|
-
"read" => {
|
63
|
-
if let Some(file_path) = input.get("filePath").and_then(|p| p.as_str()) {
|
64
|
-
format!("`{}`", make_path_relative(file_path, worktree_path))
|
65
|
-
} else {
|
66
|
-
"Read file".to_string()
|
67
|
-
}
|
68
|
-
}
|
69
|
-
"write" | "edit" => {
|
70
|
-
if let Some(file_path) = input.get("filePath").and_then(|p| p.as_str()) {
|
71
|
-
format!("`{}`", make_path_relative(file_path, worktree_path))
|
72
|
-
} else {
|
73
|
-
"Write file".to_string()
|
74
|
-
}
|
75
|
-
}
|
76
|
-
"bash" => {
|
77
|
-
if let Some(command) = input.get("command").and_then(|c| c.as_str()) {
|
78
|
-
format!("`{}`", command)
|
79
|
-
} else {
|
80
|
-
"Execute command".to_string()
|
81
|
-
}
|
82
|
-
}
|
83
|
-
"todowrite" | "todoread" => generate_todo_content(input),
|
84
|
-
_ => format!("`{}`", tool_name),
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
|
-
/// Generate formatted content for TODO tools
|
89
|
-
fn generate_todo_content(input: &Value) -> String {
|
90
|
-
// Extract todo list from input to show actual todos
|
91
|
-
if let Some(todos) = input.get("todos").and_then(|t| t.as_array()) {
|
92
|
-
let mut todo_items = Vec::new();
|
93
|
-
for todo in todos {
|
94
|
-
if let Some(content) = todo.get("content").and_then(|c| c.as_str()) {
|
95
|
-
let status = todo
|
96
|
-
.get("status")
|
97
|
-
.and_then(|s| s.as_str())
|
98
|
-
.unwrap_or("pending");
|
99
|
-
let status_emoji = match status {
|
100
|
-
"completed" => "✅",
|
101
|
-
"in_progress" => "🔄",
|
102
|
-
"pending" | "todo" => "⏳",
|
103
|
-
_ => "📝",
|
104
|
-
};
|
105
|
-
let priority = todo
|
106
|
-
.get("priority")
|
107
|
-
.and_then(|p| p.as_str())
|
108
|
-
.unwrap_or("medium");
|
109
|
-
todo_items.push(format!("{} {} ({})", status_emoji, content, priority));
|
110
|
-
}
|
111
|
-
}
|
112
|
-
if !todo_items.is_empty() {
|
113
|
-
format!("TODO List:\n{}", todo_items.join("\n"))
|
114
|
-
} else {
|
115
|
-
"Managing TODO list".to_string()
|
116
|
-
}
|
117
|
-
} else {
|
118
|
-
"Managing TODO list".to_string()
|
119
|
-
}
|
120
|
-
}
|
121
|
-
|
122
|
-
#[cfg(test)]
|
123
|
-
mod tests {
|
124
|
-
#[test]
|
125
|
-
fn test_normalize_tool_name() {
|
126
|
-
use crate::executors::sst_opencode::tools::normalize_tool_name;
|
127
|
-
|
128
|
-
// Test TODO tool normalization
|
129
|
-
assert_eq!(normalize_tool_name("Todo"), "todowrite");
|
130
|
-
assert_eq!(normalize_tool_name("TodoWrite"), "todowrite");
|
131
|
-
assert_eq!(normalize_tool_name("TodoRead"), "todoread");
|
132
|
-
|
133
|
-
// Test other tools remain unchanged
|
134
|
-
assert_eq!(normalize_tool_name("Read"), "Read");
|
135
|
-
assert_eq!(normalize_tool_name("Write"), "Write");
|
136
|
-
assert_eq!(normalize_tool_name("bash"), "bash");
|
137
|
-
assert_eq!(normalize_tool_name("SomeOtherTool"), "SomeOtherTool");
|
138
|
-
}
|
139
|
-
}
|