@xopcai/xopc 0.0.122 → 0.0.124
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 +28 -11
- package/README.zh-CN.md +28 -11
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CK3s5cJo.js → agent-avatar-dicebear-B5uSVAyJ.js} +1 -1
- package/dist/gateway/static/root/assets/agents-BjIxt67Z.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CWsNHtTs.js → app-management-settings-panel-CYCONLIL.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-C4b8y6iZ.js → appearance-settings-Clw3ryKg.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-QICTeGjX.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-DAtNW3Ia.js → archive-plugin-CXUGRp2i.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Dm-RY54y.js → attachment-load-Cmiub3Wg.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-BKHewSVT.js → automation-suggestion-card-CU0-4nQ-.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-Car-jtiz.js +1 -0
- package/dist/gateway/static/root/assets/{binary-plugins-BG6Xnk71.js → binary-plugins-0Izz-N3p.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-DfndiO3h.js → block-editor-C1Kv0IkJ.js} +28 -28
- package/dist/gateway/static/root/assets/browser-settings-page-CMeBJzlS.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-Cx93GLdw.js → capability-presets-api-BmQTM9Il.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DZEJEoCF.js +1 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-DdPTtEs8.js → channel-recipient-api-BeA2aVor.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-kkW-xFau.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-BvaQvO5F.js +2 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-D835EHJd.js → directory-picker-path-field-2rjzUu57.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-CCa1F7Fg.js → dreaming-settings-DnHO686d.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-W2ITCPSA.js → extension-debug-page-BGLRuWVN.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DrvUytvn.js → extension-page-D978ePp9.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-qM8aLT5u.js → extension-settings-page-B6Ih4Swx.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-ovkvM8KQ.js → fetch-BU5S0Sn7.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BB3biuD0.js → gateway-config-api-BIPGwrl1.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-CMEA3r5u.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-CanQ8UcR.js → gateway-startup-retry-Cd0Zz30N.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-create-dialog-CNqNdEkQ.js → goal-create-dialog-B-KHKeXN.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-BxrSoWdj.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-BBOX6mUl.js → goals-config-api-B95KdGMj.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-BP1kfvWv.js → goals-page-BTJzIl28.js} +2 -2
- package/dist/gateway/static/root/assets/{goals-settings-Bh3A7OZV.js → goals-settings-BJyEuLLm.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-wkOjYNQT.js +1 -0
- package/dist/gateway/static/root/assets/index-BLaXr6Ev.css +1 -0
- package/dist/gateway/static/root/assets/index-c_9TVr_C.js +191 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B4TKJHla.js → keyboard-shortcuts-settings-BT8T4DyC.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-CrxBJUgI.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-DcFtdLci.js → markdown-split-Dv66bO2b.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-B6SmeLVB.js → media-plugins-BhIQU5yS.js} +1 -1
- package/dist/gateway/static/root/assets/messages-O6TAtdFC.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-DOOpz6y1.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-Br16pK9v.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-PSJAi0ZC.js +2 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-C29O19s7.js → preview-open-alternatives-DGzet1d7.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CIGGgKuw.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-B2grhsBt.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-CXMORxBD.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-B4ad9GfB.js → save-bar-controls-Cqgb-jqa.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-DdYncfLR.js → schedule-field-DVZcI8Yt.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-BMTrDE-7.js → schema-form-Bj7sOxC8.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-Bi2wkBzU.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-B1L3G4IA.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-MwTW2eK4.js → settings-collapsible-section-BmXn-8N6.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-Dg6LBbcr.js → settings-form-section-BnqYMWHR.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-BfRSHJmO.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-layout-BX4T-OLF.js → settings-page-layout-CdWZU1hd.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-Ce1pcFtR.js → setup-status-panel-CWtPM7RK.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-DWE-dUFh.js → share-preview-page-IB1v8i86.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-ngbsBgij.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BIe442Gv.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-BfchZRVa.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-Dd6Y35Cm.js → system-settings-panel-BxQ384RC.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-Cx6XV5A2.js → typed-models-lib-BkcpvFYO.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-D4sMbmy7.js → use-channel-catalog-Cd0FaEQ4.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-Bx-hjMQc.js → user-profile-settings-panel-DQ7NnRSY.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-D7b_vZnv.js → utils-YKhVf7Ka.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-C5Ov8Uk_.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel--TdohF5N.js → workflow-run-setup-panel-BYD9BtOq.js} +2 -2
- package/dist/gateway/static/root/assets/{workflow-start-dialog-B0lavx_o.js → workflow-start-dialog-BNJVCWoS.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BYF29ANm.js +18 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +6 -1
- package/dist/src/agent/agent-manager.js +28 -12
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
- package/dist/src/agent/embedded/xopc-tools-bridge.js +11 -7
- package/dist/src/agent/embedded/xopc-tools-bridge.js.map +1 -1
- package/dist/src/agent/lifecycle/progress.js +3 -1
- package/dist/src/agent/lifecycle/progress.js.map +1 -1
- package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +2 -2
- package/dist/src/agent/lifecycle/timeout-wrapper.js +4 -4
- package/dist/src/agent/lifecycle/timeout-wrapper.js.map +1 -1
- package/dist/src/agent/memory/summary-generator.d.ts +1 -1
- package/dist/src/agent/memory/summary-generator.js +1 -2
- package/dist/src/agent/memory/summary-generator.js.map +1 -1
- package/dist/src/agent/middleware/index.d.ts +1 -1
- package/dist/src/agent/middleware/self-verify.d.ts +44 -14
- package/dist/src/agent/middleware/self-verify.js +214 -44
- package/dist/src/agent/middleware/self-verify.js.map +1 -1
- package/dist/src/agent/orchestration/agent-event-handler.js +57 -15
- package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
- package/dist/src/agent/prompt/sections/tooling.js +18 -13
- package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
- package/dist/src/agent/prompt/system-reminder.js +2 -2
- package/dist/src/agent/prompt/system-reminder.js.map +1 -1
- package/dist/src/agent/sandbox/sanitize-env-vars.d.ts +2 -2
- package/dist/src/agent/sandbox/sanitize-env-vars.js +2 -2
- package/dist/src/agent/sandbox/sanitize-env-vars.js.map +1 -1
- package/dist/src/agent/sandbox/types.d.ts +2 -2
- package/dist/src/agent/sandbox/types.js.map +1 -1
- package/dist/src/agent/service/btw-query.js +1 -1
- package/dist/src/agent/service/btw-query.js.map +1 -1
- package/dist/src/agent/service/process-direct-one-shot.js +6 -2
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +12 -7
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.js +2 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/skill-tool-gating.js +1 -1
- package/dist/src/agent/skills/skill-tool-gating.js.map +1 -1
- package/dist/src/agent/starter-agents.js +9 -9
- package/dist/src/agent/starter-agents.js.map +1 -1
- package/dist/src/agent/tools/apply-patch.d.ts +25 -0
- package/dist/src/agent/tools/apply-patch.js +256 -0
- package/dist/src/agent/tools/apply-patch.js.map +1 -0
- package/dist/src/agent/tools/delegate-tool.d.ts +1 -1
- package/dist/src/agent/tools/delegate-tool.js +3 -3
- package/dist/src/agent/tools/delegate-tool.js.map +1 -1
- package/dist/src/agent/tools/exec-command.d.ts +30 -0
- package/dist/src/agent/tools/exec-command.js +224 -0
- package/dist/src/agent/tools/exec-command.js.map +1 -0
- package/dist/src/agent/tools/execute-code-tool.js +7 -4
- package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +3 -3
- package/dist/src/agent/tools/factory.js +8 -9
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/goal-tool.d.ts +5 -1
- package/dist/src/agent/tools/goal-tool.js +8 -2
- package/dist/src/agent/tools/goal-tool.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +4 -3
- package/dist/src/agent/tools/index.js +5 -4
- package/dist/src/agent/tools/skills-tools.d.ts +1 -1
- package/dist/src/agent/tools/skills-tools.js.map +1 -1
- package/dist/src/agent/tools/update-plan-tool.d.ts +11 -0
- package/dist/src/agent/tools/update-plan-tool.js +72 -0
- package/dist/src/agent/tools/update-plan-tool.js.map +1 -0
- package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.js +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/release-check.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/release-check.js +1 -1
- package/dist/src/agent/workflow/builtins/release-check.js.map +1 -1
- package/dist/src/agent/workflow/step-labels.js +7 -3
- package/dist/src/agent/workflow/step-labels.js.map +1 -1
- package/dist/src/chat-commands/agent-edit.js +1 -1
- package/dist/src/chat-commands/agent-edit.js.map +1 -1
- package/dist/src/chat-commands/builtins/goal.js +4 -1
- package/dist/src/chat-commands/builtins/goal.js.map +1 -1
- package/dist/src/chat-commands/builtins/review.d.ts +1 -0
- package/dist/src/chat-commands/builtins/review.js +167 -0
- package/dist/src/chat-commands/builtins/review.js.map +1 -0
- package/dist/src/chat-commands/index.d.ts +1 -0
- package/dist/src/chat-commands/index.js +3 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +3 -2
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/mapper.d.ts +6 -0
- package/dist/src/gateway/chat-stream/mapper.js +149 -6
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/chat-stream/protocol.d.ts +51 -1
- package/dist/src/gateway/hono/routes/commands-skills.js +21 -0
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +1 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/projects.js +136 -14
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/hono/routes/search.d.ts +3 -0
- package/dist/src/gateway/hono/routes/search.js +47 -0
- package/dist/src/gateway/hono/routes/search.js.map +1 -0
- package/dist/src/gateway/security/dangerous-tools.js +1 -1
- package/dist/src/gateway/security/dangerous-tools.js.map +1 -1
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/projects/index.d.ts +3 -0
- package/dist/src/projects/index.js +4 -1
- package/dist/src/projects/project-agent-suggestion.d.ts +14 -0
- package/dist/src/projects/project-agent-suggestion.js +19 -0
- package/dist/src/projects/project-agent-suggestion.js.map +1 -0
- package/dist/src/projects/project-kind.d.ts +13 -0
- package/dist/src/projects/project-kind.js +147 -0
- package/dist/src/projects/project-kind.js.map +1 -0
- package/dist/src/projects/project-service.d.ts +15 -0
- package/dist/src/projects/project-service.js +96 -3
- package/dist/src/projects/project-service.js.map +1 -1
- package/dist/src/projects/project-store.js +32 -8
- package/dist/src/projects/project-store.js.map +1 -1
- package/dist/src/projects/types.d.ts +4 -1
- package/dist/src/projects/workspace-project.d.ts +22 -0
- package/dist/src/projects/workspace-project.js +143 -0
- package/dist/src/projects/workspace-project.js.map +1 -0
- package/dist/src/review/review-git.d.ts +48 -0
- package/dist/src/review/review-git.js +333 -0
- package/dist/src/review/review-git.js.map +1 -0
- package/dist/src/review/review-types.d.ts +22 -0
- package/dist/src/review/review-types.js +29 -0
- package/dist/src/review/review-types.js.map +1 -0
- package/dist/src/session/session-context-for-llm.js +151 -0
- package/dist/src/session/session-context-for-llm.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/021_projects_fts.sql +21 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +8 -1
- package/dist/src/tui/backends/embedded-backend.js +60 -1
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +9 -1
- package/dist/src/tui/backends/gateway-sse-backend.js +29 -0
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/components/assistant-message.js +35 -0
- package/dist/src/tui/components/assistant-message.js.map +1 -1
- package/dist/src/tui/tui-agent-events.js +25 -0
- package/dist/src/tui/tui-agent-events.js.map +1 -1
- package/dist/src/tui/tui-backend.d.ts +20 -0
- package/dist/src/tui/tui-commands.d.ts +1 -0
- package/dist/src/tui/tui-commands.js +110 -4
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-picker-overlay.d.ts +2 -0
- package/dist/src/tui/tui-picker-overlay.js +153 -1
- package/dist/src/tui/tui-picker-overlay.js.map +1 -1
- package/dist/src/tui/tui.js +36 -2
- package/dist/src/tui/tui.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.js +1 -1
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-SsQrj9B-.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-CYb6fame.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-DpOOq1nk.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-9nXxgh4u.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-D528_YFy.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-Ck-3h5lI.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-DL-fAqE_.js +0 -2
- package/dist/gateway/static/root/assets/gateway-settings-mmmnIH27.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-Ceu5zrN6.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-YoYgKlfX.js +0 -1
- package/dist/gateway/static/root/assets/index-DdqSO9R2.js +0 -191
- package/dist/gateway/static/root/assets/index-jGB-2nI-.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-BgVk5DvZ.js +0 -2
- package/dist/gateway/static/root/assets/messages-B5O4OIEo.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-BU0x8PGP.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-D6JYVP4I.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-mqRJYnJS.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-DOTgzoeh.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DGUIb4y5.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-DVKKUj3u.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BVbzUWxW.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DrNNoSqE.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-CMDqgeMR.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-aKSraR8l.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-CS-FU_B-.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-BaF3u0LA.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-CkhbRt1q.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-CZR05Bu_.js +0 -18
- package/dist/src/agent/tools/edit.d.ts +0 -14
- package/dist/src/agent/tools/edit.js +0 -132
- package/dist/src/agent/tools/edit.js.map +0 -1
- package/dist/src/agent/tools/shell.d.ts +0 -15
- package/dist/src/agent/tools/shell.js +0 -158
- package/dist/src/agent/tools/shell.js.map +0 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Config } from '../config/schema.js';
|
|
2
|
+
import { type ProjectKindInference } from './project-kind.js';
|
|
3
|
+
export declare const CODING_PROJECT_AGENT_ID = "coder";
|
|
4
|
+
export declare function suggestProjectDefaultAgentId(input: {
|
|
5
|
+
config: Config;
|
|
6
|
+
kind: ProjectKindInference['kind'];
|
|
7
|
+
}): string | undefined;
|
|
8
|
+
export declare function inferSuggestedProjectDefaultAgentId(input: {
|
|
9
|
+
config: Config;
|
|
10
|
+
name?: string | null;
|
|
11
|
+
description?: string | null;
|
|
12
|
+
workspaceRoot?: string | null;
|
|
13
|
+
projectKind?: string | null;
|
|
14
|
+
}): string | undefined;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { agentExists, init_resolve_route } from "../routing/resolve-route.js";
|
|
2
|
+
import { inferProjectKind } from "./project-kind.js";
|
|
3
|
+
//#region src/projects/project-agent-suggestion.ts
|
|
4
|
+
init_resolve_route();
|
|
5
|
+
const CODING_PROJECT_AGENT_ID = "coder";
|
|
6
|
+
function suggestProjectDefaultAgentId(input) {
|
|
7
|
+
if (input.kind === "coding" && agentExists("coder", input.config)) return CODING_PROJECT_AGENT_ID;
|
|
8
|
+
}
|
|
9
|
+
function inferSuggestedProjectDefaultAgentId(input) {
|
|
10
|
+
const inferred = inferProjectKind(input);
|
|
11
|
+
return suggestProjectDefaultAgentId({
|
|
12
|
+
config: input.config,
|
|
13
|
+
kind: inferred.kind
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { CODING_PROJECT_AGENT_ID, inferSuggestedProjectDefaultAgentId, suggestProjectDefaultAgentId };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=project-agent-suggestion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-agent-suggestion.js","names":[],"sources":["../../../src/projects/project-agent-suggestion.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { agentExists } from '../routing/resolve-route.js';\nimport { inferProjectKind, type ProjectKindInference } from './project-kind.js';\n\nexport const CODING_PROJECT_AGENT_ID = 'coder';\n\nexport function suggestProjectDefaultAgentId(input: {\n config: Config;\n kind: ProjectKindInference['kind'];\n}): string | undefined {\n if (input.kind === 'coding' && agentExists(CODING_PROJECT_AGENT_ID, input.config)) {\n return CODING_PROJECT_AGENT_ID;\n }\n return undefined;\n}\n\nexport function inferSuggestedProjectDefaultAgentId(input: {\n config: Config;\n name?: string | null;\n description?: string | null;\n workspaceRoot?: string | null;\n projectKind?: string | null;\n}): string | undefined {\n const inferred = inferProjectKind(input);\n return suggestProjectDefaultAgentId({ config: input.config, kind: inferred.kind });\n}\n"],"mappings":";;;oBAC0D;AAG1D,MAAa,0BAA0B;AAEvC,SAAgB,6BAA6B,OAGtB;AACrB,KAAI,MAAM,SAAS,YAAY,YAAA,SAAqC,MAAM,OAAO,CAC/E,QAAO;;AAKX,SAAgB,oCAAoC,OAM7B;CACrB,MAAM,WAAW,iBAAiB,MAAM;AACxC,QAAO,6BAA6B;EAAE,QAAQ,MAAM;EAAQ,MAAM,SAAS;EAAM,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type ProjectKind = 'coding' | 'general' | 'unknown';
|
|
2
|
+
export type ProjectKindOverride = 'auto' | 'coding' | 'general';
|
|
3
|
+
export type ProjectKindInference = {
|
|
4
|
+
kind: ProjectKind;
|
|
5
|
+
confidence: number;
|
|
6
|
+
reasons: string[];
|
|
7
|
+
};
|
|
8
|
+
export declare function inferProjectKind(input: {
|
|
9
|
+
name?: string | null;
|
|
10
|
+
description?: string | null;
|
|
11
|
+
workspaceRoot?: string | null;
|
|
12
|
+
projectKind?: string | null;
|
|
13
|
+
}): ProjectKindInference;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { canonicalWorkspacePath } from "./workspace-project.js";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
4
|
+
//#region src/projects/project-kind.ts
|
|
5
|
+
const STRONG_CODE_MARKERS = [
|
|
6
|
+
".git",
|
|
7
|
+
"package.json",
|
|
8
|
+
"pnpm-lock.yaml",
|
|
9
|
+
"yarn.lock",
|
|
10
|
+
"package-lock.json",
|
|
11
|
+
"tsconfig.json",
|
|
12
|
+
"go.mod",
|
|
13
|
+
"Cargo.toml",
|
|
14
|
+
"pyproject.toml",
|
|
15
|
+
"requirements.txt",
|
|
16
|
+
"pom.xml",
|
|
17
|
+
"build.gradle",
|
|
18
|
+
"Makefile",
|
|
19
|
+
"Dockerfile",
|
|
20
|
+
"deno.json",
|
|
21
|
+
"deno.jsonc",
|
|
22
|
+
"composer.json",
|
|
23
|
+
"Gemfile"
|
|
24
|
+
];
|
|
25
|
+
const WEAK_CODE_DIRECTORIES = [
|
|
26
|
+
"src",
|
|
27
|
+
"app",
|
|
28
|
+
"lib",
|
|
29
|
+
"tests",
|
|
30
|
+
"test",
|
|
31
|
+
"__tests__",
|
|
32
|
+
".github"
|
|
33
|
+
];
|
|
34
|
+
const CODE_EXTENSIONS = new Set([
|
|
35
|
+
".c",
|
|
36
|
+
".cc",
|
|
37
|
+
".cpp",
|
|
38
|
+
".cs",
|
|
39
|
+
".css",
|
|
40
|
+
".go",
|
|
41
|
+
".h",
|
|
42
|
+
".hpp",
|
|
43
|
+
".html",
|
|
44
|
+
".java",
|
|
45
|
+
".js",
|
|
46
|
+
".jsx",
|
|
47
|
+
".kt",
|
|
48
|
+
".mjs",
|
|
49
|
+
".php",
|
|
50
|
+
".py",
|
|
51
|
+
".rb",
|
|
52
|
+
".rs",
|
|
53
|
+
".sh",
|
|
54
|
+
".swift",
|
|
55
|
+
".ts",
|
|
56
|
+
".tsx"
|
|
57
|
+
]);
|
|
58
|
+
function normalizeOverride(value) {
|
|
59
|
+
const normalized = value?.trim().toLowerCase();
|
|
60
|
+
return normalized === "coding" || normalized === "general" ? normalized : "auto";
|
|
61
|
+
}
|
|
62
|
+
function hasSourceExtension(name) {
|
|
63
|
+
const lower = name.toLowerCase();
|
|
64
|
+
for (const extension of CODE_EXTENSIONS) if (lower.endsWith(extension)) return true;
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function readDirectoryNames(pathValue) {
|
|
68
|
+
try {
|
|
69
|
+
return readdirSync(pathValue).slice(0, 200);
|
|
70
|
+
} catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function directoryExists(pathValue) {
|
|
75
|
+
try {
|
|
76
|
+
return statSync(pathValue).isDirectory();
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function inferProjectKind(input) {
|
|
82
|
+
const override = normalizeOverride(input.projectKind);
|
|
83
|
+
if (override === "coding") return {
|
|
84
|
+
kind: "coding",
|
|
85
|
+
confidence: 1,
|
|
86
|
+
reasons: ["selected coding project type"]
|
|
87
|
+
};
|
|
88
|
+
if (override === "general") return {
|
|
89
|
+
kind: "general",
|
|
90
|
+
confidence: 1,
|
|
91
|
+
reasons: ["selected general project type"]
|
|
92
|
+
};
|
|
93
|
+
const workspaceRoot = canonicalWorkspacePath(input.workspaceRoot);
|
|
94
|
+
const reasons = [];
|
|
95
|
+
let score = 0;
|
|
96
|
+
if (workspaceRoot && directoryExists(workspaceRoot)) {
|
|
97
|
+
for (const marker of STRONG_CODE_MARKERS) if (existsSync(join(workspaceRoot, marker))) {
|
|
98
|
+
score += 4;
|
|
99
|
+
reasons.push(`found ${marker}`);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
for (const dir of WEAK_CODE_DIRECTORIES) if (directoryExists(join(workspaceRoot, dir))) {
|
|
103
|
+
score += 1;
|
|
104
|
+
reasons.push(`found ${dir}/`);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
if (readDirectoryNames(workspaceRoot).some(hasSourceExtension)) {
|
|
108
|
+
score += 2;
|
|
109
|
+
reasons.push("found source files");
|
|
110
|
+
} else for (const dir of [
|
|
111
|
+
"src",
|
|
112
|
+
"app",
|
|
113
|
+
"lib"
|
|
114
|
+
]) {
|
|
115
|
+
const childDir = join(workspaceRoot, dir);
|
|
116
|
+
if (directoryExists(childDir) && readDirectoryNames(childDir).some(hasSourceExtension)) {
|
|
117
|
+
score += 2;
|
|
118
|
+
reasons.push(`found source files in ${dir}/`);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (score >= 4) return {
|
|
124
|
+
kind: "coding",
|
|
125
|
+
confidence: .9,
|
|
126
|
+
reasons
|
|
127
|
+
};
|
|
128
|
+
if (score >= 3) return {
|
|
129
|
+
kind: "coding",
|
|
130
|
+
confidence: .75,
|
|
131
|
+
reasons
|
|
132
|
+
};
|
|
133
|
+
if (workspaceRoot) return {
|
|
134
|
+
kind: "general",
|
|
135
|
+
confidence: .55,
|
|
136
|
+
reasons
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
kind: "unknown",
|
|
140
|
+
confidence: .25,
|
|
141
|
+
reasons
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
export { inferProjectKind };
|
|
146
|
+
|
|
147
|
+
//# sourceMappingURL=project-kind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-kind.js","names":[],"sources":["../../../src/projects/project-kind.ts"],"sourcesContent":["import { existsSync, readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { canonicalWorkspacePath } from './workspace-project.js';\n\nexport type ProjectKind = 'coding' | 'general' | 'unknown';\nexport type ProjectKindOverride = 'auto' | 'coding' | 'general';\n\nexport type ProjectKindInference = {\n kind: ProjectKind;\n confidence: number;\n reasons: string[];\n};\n\nconst STRONG_CODE_MARKERS = [\n '.git',\n 'package.json',\n 'pnpm-lock.yaml',\n 'yarn.lock',\n 'package-lock.json',\n 'tsconfig.json',\n 'go.mod',\n 'Cargo.toml',\n 'pyproject.toml',\n 'requirements.txt',\n 'pom.xml',\n 'build.gradle',\n 'Makefile',\n 'Dockerfile',\n 'deno.json',\n 'deno.jsonc',\n 'composer.json',\n 'Gemfile',\n] as const;\n\nconst WEAK_CODE_DIRECTORIES = ['src', 'app', 'lib', 'tests', 'test', '__tests__', '.github'] as const;\nconst CODE_EXTENSIONS = new Set([\n '.c',\n '.cc',\n '.cpp',\n '.cs',\n '.css',\n '.go',\n '.h',\n '.hpp',\n '.html',\n '.java',\n '.js',\n '.jsx',\n '.kt',\n '.mjs',\n '.php',\n '.py',\n '.rb',\n '.rs',\n '.sh',\n '.swift',\n '.ts',\n '.tsx',\n]);\n\nfunction normalizeOverride(value: string | null | undefined): ProjectKindOverride {\n const normalized = value?.trim().toLowerCase();\n return normalized === 'coding' || normalized === 'general' ? normalized : 'auto';\n}\n\nfunction hasSourceExtension(name: string): boolean {\n const lower = name.toLowerCase();\n for (const extension of CODE_EXTENSIONS) {\n if (lower.endsWith(extension)) return true;\n }\n return false;\n}\n\nfunction readDirectoryNames(pathValue: string): string[] {\n try {\n return readdirSync(pathValue).slice(0, 200);\n } catch {\n return [];\n }\n}\n\nfunction directoryExists(pathValue: string): boolean {\n try {\n return statSync(pathValue).isDirectory();\n } catch {\n return false;\n }\n}\n\nexport function inferProjectKind(input: {\n name?: string | null;\n description?: string | null;\n workspaceRoot?: string | null;\n projectKind?: string | null;\n}): ProjectKindInference {\n const override = normalizeOverride(input.projectKind);\n if (override === 'coding') return { kind: 'coding', confidence: 1, reasons: ['selected coding project type'] };\n if (override === 'general') return { kind: 'general', confidence: 1, reasons: ['selected general project type'] };\n\n const workspaceRoot = canonicalWorkspacePath(input.workspaceRoot);\n const reasons: string[] = [];\n let score = 0;\n\n if (workspaceRoot && directoryExists(workspaceRoot)) {\n for (const marker of STRONG_CODE_MARKERS) {\n if (existsSync(join(workspaceRoot, marker))) {\n score += 4;\n reasons.push(`found ${marker}`);\n break;\n }\n }\n\n for (const dir of WEAK_CODE_DIRECTORIES) {\n if (directoryExists(join(workspaceRoot, dir))) {\n score += 1;\n reasons.push(`found ${dir}/`);\n break;\n }\n }\n\n if (readDirectoryNames(workspaceRoot).some(hasSourceExtension)) {\n score += 2;\n reasons.push('found source files');\n } else {\n for (const dir of ['src', 'app', 'lib']) {\n const childDir = join(workspaceRoot, dir);\n if (directoryExists(childDir) && readDirectoryNames(childDir).some(hasSourceExtension)) {\n score += 2;\n reasons.push(`found source files in ${dir}/`);\n break;\n }\n }\n }\n }\n\n if (score >= 4) return { kind: 'coding', confidence: 0.9, reasons };\n if (score >= 3) return { kind: 'coding', confidence: 0.75, reasons };\n if (workspaceRoot) return { kind: 'general', confidence: 0.55, reasons };\n return { kind: 'unknown', confidence: 0.25, reasons };\n}\n"],"mappings":";;;;AAcA,MAAM,sBAAsB;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,wBAAwB;CAAC;CAAO;CAAO;CAAO;CAAS;CAAQ;CAAa;CAAU;AAC5F,MAAM,kBAAkB,IAAI,IAAI;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAS,kBAAkB,OAAuD;CAChF,MAAM,aAAa,OAAO,MAAM,CAAC,aAAa;AAC9C,QAAO,eAAe,YAAY,eAAe,YAAY,aAAa;;AAG5E,SAAS,mBAAmB,MAAuB;CACjD,MAAM,QAAQ,KAAK,aAAa;AAChC,MAAK,MAAM,aAAa,gBACtB,KAAI,MAAM,SAAS,UAAU,CAAE,QAAO;AAExC,QAAO;;AAGT,SAAS,mBAAmB,WAA6B;AACvD,KAAI;AACF,SAAO,YAAY,UAAU,CAAC,MAAM,GAAG,IAAI;SACrC;AACN,SAAO,EAAE;;;AAIb,SAAS,gBAAgB,WAA4B;AACnD,KAAI;AACF,SAAO,SAAS,UAAU,CAAC,aAAa;SAClC;AACN,SAAO;;;AAIX,SAAgB,iBAAiB,OAKR;CACvB,MAAM,WAAW,kBAAkB,MAAM,YAAY;AACrD,KAAI,aAAa,SAAU,QAAO;EAAE,MAAM;EAAU,YAAY;EAAG,SAAS,CAAC,+BAA+B;EAAE;AAC9G,KAAI,aAAa,UAAW,QAAO;EAAE,MAAM;EAAW,YAAY;EAAG,SAAS,CAAC,gCAAgC;EAAE;CAEjH,MAAM,gBAAgB,uBAAuB,MAAM,cAAc;CACjE,MAAM,UAAoB,EAAE;CAC5B,IAAI,QAAQ;AAEZ,KAAI,iBAAiB,gBAAgB,cAAc,EAAE;AACnD,OAAK,MAAM,UAAU,oBACnB,KAAI,WAAW,KAAK,eAAe,OAAO,CAAC,EAAE;AAC3C,YAAS;AACT,WAAQ,KAAK,SAAS,SAAS;AAC/B;;AAIJ,OAAK,MAAM,OAAO,sBAChB,KAAI,gBAAgB,KAAK,eAAe,IAAI,CAAC,EAAE;AAC7C,YAAS;AACT,WAAQ,KAAK,SAAS,IAAI,GAAG;AAC7B;;AAIJ,MAAI,mBAAmB,cAAc,CAAC,KAAK,mBAAmB,EAAE;AAC9D,YAAS;AACT,WAAQ,KAAK,qBAAqB;QAElC,MAAK,MAAM,OAAO;GAAC;GAAO;GAAO;GAAM,EAAE;GACvC,MAAM,WAAW,KAAK,eAAe,IAAI;AACzC,OAAI,gBAAgB,SAAS,IAAI,mBAAmB,SAAS,CAAC,KAAK,mBAAmB,EAAE;AACtF,aAAS;AACT,YAAQ,KAAK,yBAAyB,IAAI,GAAG;AAC7C;;;;AAMR,KAAI,SAAS,EAAG,QAAO;EAAE,MAAM;EAAU,YAAY;EAAK;EAAS;AACnE,KAAI,SAAS,EAAG,QAAO;EAAE,MAAM;EAAU,YAAY;EAAM;EAAS;AACpE,KAAI,cAAe,QAAO;EAAE,MAAM;EAAW,YAAY;EAAM;EAAS;AACxE,QAAO;EAAE,MAAM;EAAW,YAAY;EAAM;EAAS"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ProjectStore } from './project-store.js';
|
|
2
2
|
import type { CreateProjectInput, Project, ProjectListQuery, ProjectListResult, ProjectWithDetails, UpdateProjectInput } from './types.js';
|
|
3
|
+
import { type WorkspaceProjectMatch } from './workspace-project.js';
|
|
3
4
|
export type ProjectSuggestion = {
|
|
4
5
|
projectId: string;
|
|
5
6
|
projectName: string;
|
|
@@ -9,10 +10,24 @@ export type ProjectSuggestion = {
|
|
|
9
10
|
export declare class ProjectService {
|
|
10
11
|
private readonly store;
|
|
11
12
|
constructor(store?: ProjectStore);
|
|
13
|
+
private listAllProjects;
|
|
12
14
|
create(input: CreateProjectInput): Project;
|
|
13
15
|
get(id: string): Project | null;
|
|
14
16
|
getBySlug(slug: string): Project | null;
|
|
15
17
|
list(query?: ProjectListQuery): ProjectListResult;
|
|
18
|
+
findByWorkspaceRoot(workspaceRoot: string, options?: {
|
|
19
|
+
excludeProjectId?: string;
|
|
20
|
+
}): Project | null;
|
|
21
|
+
resolveForWorkspacePath(workspacePath: string): {
|
|
22
|
+
project: Project;
|
|
23
|
+
reason: 'exact' | 'contained';
|
|
24
|
+
} | null;
|
|
25
|
+
resolveOrCreateForWorkspacePath(input: {
|
|
26
|
+
workspacePath: string;
|
|
27
|
+
agentId?: string;
|
|
28
|
+
defaultAgentId?: string;
|
|
29
|
+
autoCreate?: boolean;
|
|
30
|
+
}): WorkspaceProjectMatch | null;
|
|
16
31
|
update(id: string, input: UpdateProjectInput): Project;
|
|
17
32
|
delete(id: string): void;
|
|
18
33
|
getWithDetails(id: string): ProjectWithDetails | null;
|
|
@@ -3,16 +3,44 @@ import "../storage/sqlite/index.js";
|
|
|
3
3
|
import { ProjectStore } from "./project-store.js";
|
|
4
4
|
import { bindGoalToProject, listProjectGoalIds, unbindGoalFromProject } from "./goal-bind.js";
|
|
5
5
|
import { bindSessionToProject, listProjectSessionKeys, unbindSessionFromProject } from "./session-bind.js";
|
|
6
|
+
import { ProjectWorkspaceConflictError, ProjectWorkspaceMissingError, canonicalWorkspacePath, ensureWorkspaceDirectory, inferProjectNameFromWorkspaceRoot, isPathSameOrInsideWorkspace, isSafeAutoCreateWorkspaceRoot, resolveWorkspaceProjectRoot, workspaceDirectoryExists } from "./workspace-project.js";
|
|
6
7
|
//#region src/projects/project-service.ts
|
|
7
8
|
var ProjectService = class {
|
|
8
9
|
constructor(store = new ProjectStore()) {
|
|
9
10
|
this.store = store;
|
|
10
11
|
}
|
|
12
|
+
listAllProjects() {
|
|
13
|
+
const items = [];
|
|
14
|
+
let offset = 0;
|
|
15
|
+
const limit = 500;
|
|
16
|
+
while (true) {
|
|
17
|
+
const page = this.list({
|
|
18
|
+
limit,
|
|
19
|
+
offset
|
|
20
|
+
});
|
|
21
|
+
items.push(...page.items);
|
|
22
|
+
if (!page.hasMore || page.items.length === 0) break;
|
|
23
|
+
offset += page.items.length;
|
|
24
|
+
}
|
|
25
|
+
return items;
|
|
26
|
+
}
|
|
11
27
|
create(input) {
|
|
12
|
-
const
|
|
28
|
+
const rawWorkspaceRoot = input.workspaceRoot?.trim();
|
|
29
|
+
const workspaceRoot = rawWorkspaceRoot ? canonicalWorkspacePath(rawWorkspaceRoot) : void 0;
|
|
30
|
+
if (rawWorkspaceRoot && !workspaceRoot) throw new Error("Invalid workspace root");
|
|
31
|
+
if (workspaceRoot) {
|
|
32
|
+
const existing = this.findByWorkspaceRoot(workspaceRoot);
|
|
33
|
+
if (existing) throw new ProjectWorkspaceConflictError(existing);
|
|
34
|
+
if (!workspaceDirectoryExists(workspaceRoot)) if (input.createWorkspaceRoot === true) ensureWorkspaceDirectory(workspaceRoot);
|
|
35
|
+
else throw new ProjectWorkspaceMissingError(workspaceRoot);
|
|
36
|
+
}
|
|
37
|
+
const name = input.name?.trim() || inferProjectNameFromWorkspaceRoot(workspaceRoot) || "";
|
|
38
|
+
const slug = input.slug?.trim() || this.store.generateSlug(name);
|
|
13
39
|
return this.store.create({
|
|
14
40
|
...input,
|
|
15
|
-
|
|
41
|
+
name,
|
|
42
|
+
slug,
|
|
43
|
+
workspaceRoot
|
|
16
44
|
});
|
|
17
45
|
}
|
|
18
46
|
get(id) {
|
|
@@ -24,8 +52,73 @@ var ProjectService = class {
|
|
|
24
52
|
list(query) {
|
|
25
53
|
return this.store.list(query);
|
|
26
54
|
}
|
|
55
|
+
findByWorkspaceRoot(workspaceRoot, options = {}) {
|
|
56
|
+
const target = canonicalWorkspacePath(workspaceRoot);
|
|
57
|
+
if (!target) return null;
|
|
58
|
+
for (const project of this.listAllProjects()) {
|
|
59
|
+
if (options.excludeProjectId && project.id === options.excludeProjectId) continue;
|
|
60
|
+
const projectRoot = canonicalWorkspacePath(project.workspaceRoot);
|
|
61
|
+
if (projectRoot && isPathSameOrInsideWorkspace(projectRoot, target) && isPathSameOrInsideWorkspace(target, projectRoot)) return project;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
resolveForWorkspacePath(workspacePath) {
|
|
66
|
+
const target = canonicalWorkspacePath(workspacePath);
|
|
67
|
+
if (!target) return null;
|
|
68
|
+
let best = null;
|
|
69
|
+
for (const project of this.listAllProjects()) {
|
|
70
|
+
const root = canonicalWorkspacePath(project.workspaceRoot);
|
|
71
|
+
if (!root || !isPathSameOrInsideWorkspace(root, target)) continue;
|
|
72
|
+
const reason = isPathSameOrInsideWorkspace(root, target) && isPathSameOrInsideWorkspace(target, root) ? "exact" : "contained";
|
|
73
|
+
if (!best || root.length > best.root.length) best = {
|
|
74
|
+
project,
|
|
75
|
+
root,
|
|
76
|
+
reason
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return best ? {
|
|
80
|
+
project: best.project,
|
|
81
|
+
reason: best.reason
|
|
82
|
+
} : null;
|
|
83
|
+
}
|
|
84
|
+
resolveOrCreateForWorkspacePath(input) {
|
|
85
|
+
const existing = this.resolveForWorkspacePath(input.workspacePath);
|
|
86
|
+
if (existing) return {
|
|
87
|
+
project: existing.project,
|
|
88
|
+
reason: existing.reason,
|
|
89
|
+
created: false
|
|
90
|
+
};
|
|
91
|
+
if (!input.autoCreate) return null;
|
|
92
|
+
const workspaceRoot = resolveWorkspaceProjectRoot(input.workspacePath) ?? canonicalWorkspacePath(input.workspacePath);
|
|
93
|
+
if (!workspaceRoot || !isSafeAutoCreateWorkspaceRoot(workspaceRoot)) return null;
|
|
94
|
+
const rootExisting = this.resolveForWorkspacePath(workspaceRoot);
|
|
95
|
+
if (rootExisting) return {
|
|
96
|
+
project: rootExisting.project,
|
|
97
|
+
reason: rootExisting.reason,
|
|
98
|
+
created: false
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
project: this.create({
|
|
102
|
+
name: inferProjectNameFromWorkspaceRoot(workspaceRoot) ?? void 0,
|
|
103
|
+
workspaceRoot,
|
|
104
|
+
defaultAgentId: Object.hasOwn(input, "defaultAgentId") ? input.defaultAgentId : input.agentId
|
|
105
|
+
}),
|
|
106
|
+
reason: "auto_created",
|
|
107
|
+
created: true
|
|
108
|
+
};
|
|
109
|
+
}
|
|
27
110
|
update(id, input) {
|
|
28
|
-
|
|
111
|
+
const patch = { ...input };
|
|
112
|
+
if (input.workspaceRoot !== void 0 && input.workspaceRoot !== null && input.workspaceRoot.trim()) {
|
|
113
|
+
const workspaceRoot = canonicalWorkspacePath(input.workspaceRoot);
|
|
114
|
+
if (!workspaceRoot) throw new Error("Invalid workspace root");
|
|
115
|
+
const existing = this.findByWorkspaceRoot(workspaceRoot, { excludeProjectId: id });
|
|
116
|
+
if (existing) throw new ProjectWorkspaceConflictError(existing);
|
|
117
|
+
if (!workspaceDirectoryExists(workspaceRoot)) if (input.createWorkspaceRoot === true) ensureWorkspaceDirectory(workspaceRoot);
|
|
118
|
+
else throw new ProjectWorkspaceMissingError(workspaceRoot);
|
|
119
|
+
patch.workspaceRoot = workspaceRoot;
|
|
120
|
+
}
|
|
121
|
+
return this.store.update(id, patch);
|
|
29
122
|
}
|
|
30
123
|
delete(id) {
|
|
31
124
|
this.store.delete(id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-service.js","names":[],"sources":["../../../src/projects/project-service.ts"],"sourcesContent":["import { getSessionMetadata } from '../storage/sqlite/index.js';\nimport { ProjectStore } from './project-store.js';\nimport { bindGoalToProject, listProjectGoalIds, unbindGoalFromProject } from './goal-bind.js';\nimport { bindSessionToProject, listProjectSessionKeys, unbindSessionFromProject } from './session-bind.js';\nimport type { CreateProjectInput, Project, ProjectListQuery, ProjectListResult, ProjectWithDetails, UpdateProjectInput } from './types.js';\n\nexport type ProjectSuggestion = {\n projectId: string;\n projectName: string;\n score: number;\n reason: string;\n};\n\nexport class ProjectService {\n constructor(private readonly store = new ProjectStore()) {}\n\n create(input: CreateProjectInput): Project {\n const slug = input.slug?.trim() || this.store.generateSlug(input.name);\n return this.store.create({ ...input, slug });\n }\n\n get(id: string): Project | null {\n return this.store.get(id);\n }\n\n getBySlug(slug: string): Project | null {\n return this.store.findBySlug(slug);\n }\n\n list(query?: ProjectListQuery): ProjectListResult {\n return this.store.list(query);\n }\n\n update(id: string, input: UpdateProjectInput): Project {\n return this.store.update(id, input);\n }\n\n delete(id: string): void {\n this.store.delete(id);\n }\n\n getWithDetails(id: string): ProjectWithDetails | null {\n return this.store.getWithDetails(id);\n }\n\n attachSession(sessionKey: string, projectId: string): void {\n bindSessionToProject(sessionKey, projectId);\n }\n\n detachSession(sessionKey: string): void {\n unbindSessionFromProject(sessionKey);\n }\n\n listSessionKeys(projectId: string, limit?: number, offset?: number): string[] {\n return listProjectSessionKeys(projectId, limit, offset);\n }\n\n attachGoal(goalId: string, projectId: string): void {\n bindGoalToProject(goalId, projectId);\n }\n\n detachGoal(goalId: string): void {\n unbindGoalFromProject(goalId);\n }\n\n listGoalIds(projectId: string, limit?: number, offset?: number): string[] {\n return listProjectGoalIds(projectId, limit, offset);\n }\n\n suggestProjectsForSession(sessionKey: string): ProjectSuggestion[] {\n const session = getSessionMetadata(sessionKey);\n if (!session) return [];\n const projects = this.list({ status: 'active', limit: 500 }).items;\n const haystack = [session.key, session.name, session.routing?.agentId, session.sourceChannel, ...(session.tags ?? [])]\n .filter(Boolean)\n .join(' ')\n .toLowerCase();\n const suggestions: ProjectSuggestion[] = [];\n for (const project of projects) {\n let score = 0;\n const reasons: string[] = [];\n if (haystack.includes(project.slug.toLowerCase())) {\n score += 10;\n reasons.push('session metadata matches project slug');\n }\n for (const token of project.name.toLowerCase().split(/\\s+/).filter((t) => t.length >= 3)) {\n if (haystack.includes(token)) {\n score += 3;\n reasons.push(`matches ${token}`);\n break;\n }\n }\n if (score > 0) {\n suggestions.push({ projectId: project.id, projectName: project.name, score, reason: reasons.join(', ') });\n }\n }\n return suggestions.sort((a, b) => b.score - a.score).slice(0, 5);\n }\n}\n"],"mappings":";;;;;;AAaA,IAAa,iBAAb,MAA4B;CAC1B,YAAY,QAAyB,IAAI,cAAc,EAAE;AAA5B,OAAA,QAAA;;CAE7B,OAAO,OAAoC;EACzC,MAAM,OAAO,MAAM,MAAM,MAAM,IAAI,KAAK,MAAM,aAAa,MAAM,KAAK;AACtE,SAAO,KAAK,MAAM,OAAO;GAAE,GAAG;GAAO;GAAM,CAAC;;CAG9C,IAAI,IAA4B;AAC9B,SAAO,KAAK,MAAM,IAAI,GAAG;;CAG3B,UAAU,MAA8B;AACtC,SAAO,KAAK,MAAM,WAAW,KAAK;;CAGpC,KAAK,OAA6C;AAChD,SAAO,KAAK,MAAM,KAAK,MAAM;;CAG/B,OAAO,IAAY,OAAoC;AACrD,SAAO,KAAK,MAAM,OAAO,IAAI,MAAM;;CAGrC,OAAO,IAAkB;AACvB,OAAK,MAAM,OAAO,GAAG;;CAGvB,eAAe,IAAuC;AACpD,SAAO,KAAK,MAAM,eAAe,GAAG;;CAGtC,cAAc,YAAoB,WAAyB;AACzD,uBAAqB,YAAY,UAAU;;CAG7C,cAAc,YAA0B;AACtC,2BAAyB,WAAW;;CAGtC,gBAAgB,WAAmB,OAAgB,QAA2B;AAC5E,SAAO,uBAAuB,WAAW,OAAO,OAAO;;CAGzD,WAAW,QAAgB,WAAyB;AAClD,oBAAkB,QAAQ,UAAU;;CAGtC,WAAW,QAAsB;AAC/B,wBAAsB,OAAO;;CAG/B,YAAY,WAAmB,OAAgB,QAA2B;AACxE,SAAO,mBAAmB,WAAW,OAAO,OAAO;;CAGrD,0BAA0B,YAAyC;EACjE,MAAM,UAAU,mBAAmB,WAAW;AAC9C,MAAI,CAAC,QAAS,QAAO,EAAE;EACvB,MAAM,WAAW,KAAK,KAAK;GAAE,QAAQ;GAAU,OAAO;GAAK,CAAC,CAAC;EAC7D,MAAM,WAAW;GAAC,QAAQ;GAAK,QAAQ;GAAM,QAAQ,SAAS;GAAS,QAAQ;GAAe,GAAI,QAAQ,QAAQ,EAAE;GAAE,CACnH,OAAO,QAAQ,CACf,KAAK,IAAI,CACT,aAAa;EAChB,MAAM,cAAmC,EAAE;AAC3C,OAAK,MAAM,WAAW,UAAU;GAC9B,IAAI,QAAQ;GACZ,MAAM,UAAoB,EAAE;AAC5B,OAAI,SAAS,SAAS,QAAQ,KAAK,aAAa,CAAC,EAAE;AACjD,aAAS;AACT,YAAQ,KAAK,wCAAwC;;AAEvD,QAAK,MAAM,SAAS,QAAQ,KAAK,aAAa,CAAC,MAAM,MAAM,CAAC,QAAQ,MAAM,EAAE,UAAU,EAAE,CACtF,KAAI,SAAS,SAAS,MAAM,EAAE;AAC5B,aAAS;AACT,YAAQ,KAAK,WAAW,QAAQ;AAChC;;AAGJ,OAAI,QAAQ,EACV,aAAY,KAAK;IAAE,WAAW,QAAQ;IAAI,aAAa,QAAQ;IAAM;IAAO,QAAQ,QAAQ,KAAK,KAAK;IAAE,CAAC;;AAG7G,SAAO,YAAY,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG,EAAE"}
|
|
1
|
+
{"version":3,"file":"project-service.js","names":[],"sources":["../../../src/projects/project-service.ts"],"sourcesContent":["import { getSessionMetadata } from '../storage/sqlite/index.js';\nimport { ProjectStore } from './project-store.js';\nimport { bindGoalToProject, listProjectGoalIds, unbindGoalFromProject } from './goal-bind.js';\nimport { bindSessionToProject, listProjectSessionKeys, unbindSessionFromProject } from './session-bind.js';\nimport type { CreateProjectInput, Project, ProjectListQuery, ProjectListResult, ProjectWithDetails, UpdateProjectInput } from './types.js';\nimport {\n canonicalWorkspacePath,\n ensureWorkspaceDirectory,\n inferProjectNameFromWorkspaceRoot,\n isPathSameOrInsideWorkspace,\n isSafeAutoCreateWorkspaceRoot,\n ProjectWorkspaceConflictError,\n ProjectWorkspaceMissingError,\n resolveWorkspaceProjectRoot,\n workspaceDirectoryExists,\n type WorkspaceProjectMatch,\n} from './workspace-project.js';\n\nexport type ProjectSuggestion = {\n projectId: string;\n projectName: string;\n score: number;\n reason: string;\n};\n\nexport class ProjectService {\n constructor(private readonly store = new ProjectStore()) {}\n\n private listAllProjects(): Project[] {\n const items: Project[] = [];\n let offset = 0;\n const limit = 500;\n while (true) {\n const page = this.list({ limit, offset });\n items.push(...page.items);\n if (!page.hasMore || page.items.length === 0) break;\n offset += page.items.length;\n }\n return items;\n }\n\n create(input: CreateProjectInput): Project {\n const rawWorkspaceRoot = input.workspaceRoot?.trim();\n const workspaceRoot = rawWorkspaceRoot ? canonicalWorkspacePath(rawWorkspaceRoot) : undefined;\n if (rawWorkspaceRoot && !workspaceRoot) {\n throw new Error('Invalid workspace root');\n }\n if (workspaceRoot) {\n const existing = this.findByWorkspaceRoot(workspaceRoot);\n if (existing) throw new ProjectWorkspaceConflictError(existing);\n if (!workspaceDirectoryExists(workspaceRoot)) {\n if (input.createWorkspaceRoot === true) {\n ensureWorkspaceDirectory(workspaceRoot);\n } else {\n throw new ProjectWorkspaceMissingError(workspaceRoot);\n }\n }\n }\n const name = input.name?.trim() || inferProjectNameFromWorkspaceRoot(workspaceRoot) || '';\n const slug = input.slug?.trim() || this.store.generateSlug(name);\n return this.store.create({ ...input, name, slug, workspaceRoot });\n }\n\n get(id: string): Project | null {\n return this.store.get(id);\n }\n\n getBySlug(slug: string): Project | null {\n return this.store.findBySlug(slug);\n }\n\n list(query?: ProjectListQuery): ProjectListResult {\n return this.store.list(query);\n }\n\n findByWorkspaceRoot(workspaceRoot: string, options: { excludeProjectId?: string } = {}): Project | null {\n const target = canonicalWorkspacePath(workspaceRoot);\n if (!target) return null;\n for (const project of this.listAllProjects()) {\n if (options.excludeProjectId && project.id === options.excludeProjectId) continue;\n const projectRoot = canonicalWorkspacePath(project.workspaceRoot);\n if (projectRoot && isPathSameOrInsideWorkspace(projectRoot, target) && isPathSameOrInsideWorkspace(target, projectRoot)) {\n return project;\n }\n }\n return null;\n }\n\n resolveForWorkspacePath(workspacePath: string): { project: Project; reason: 'exact' | 'contained' } | null {\n const target = canonicalWorkspacePath(workspacePath);\n if (!target) return null;\n let best: { project: Project; root: string; reason: 'exact' | 'contained' } | null = null;\n for (const project of this.listAllProjects()) {\n const root = canonicalWorkspacePath(project.workspaceRoot);\n if (!root || !isPathSameOrInsideWorkspace(root, target)) continue;\n const reason = isPathSameOrInsideWorkspace(root, target) && isPathSameOrInsideWorkspace(target, root) ? 'exact' : 'contained';\n if (!best || root.length > best.root.length) {\n best = { project, root, reason };\n }\n }\n return best ? { project: best.project, reason: best.reason } : null;\n }\n\n resolveOrCreateForWorkspacePath(input: {\n workspacePath: string;\n agentId?: string;\n defaultAgentId?: string;\n autoCreate?: boolean;\n }): WorkspaceProjectMatch | null {\n const existing = this.resolveForWorkspacePath(input.workspacePath);\n if (existing) return { project: existing.project, reason: existing.reason, created: false };\n if (!input.autoCreate) return null;\n const workspaceRoot = resolveWorkspaceProjectRoot(input.workspacePath) ?? canonicalWorkspacePath(input.workspacePath);\n if (!workspaceRoot || !isSafeAutoCreateWorkspaceRoot(workspaceRoot)) return null;\n const rootExisting = this.resolveForWorkspacePath(workspaceRoot);\n if (rootExisting) return { project: rootExisting.project, reason: rootExisting.reason, created: false };\n const project = this.create({\n name: inferProjectNameFromWorkspaceRoot(workspaceRoot) ?? undefined,\n workspaceRoot,\n defaultAgentId: Object.hasOwn(input, 'defaultAgentId') ? input.defaultAgentId : input.agentId,\n });\n return { project, reason: 'auto_created', created: true };\n }\n\n update(id: string, input: UpdateProjectInput): Project {\n const patch = { ...input };\n if (input.workspaceRoot !== undefined && input.workspaceRoot !== null && input.workspaceRoot.trim()) {\n const workspaceRoot = canonicalWorkspacePath(input.workspaceRoot);\n if (!workspaceRoot) {\n throw new Error('Invalid workspace root');\n }\n const existing = this.findByWorkspaceRoot(workspaceRoot, { excludeProjectId: id });\n if (existing) throw new ProjectWorkspaceConflictError(existing);\n if (!workspaceDirectoryExists(workspaceRoot)) {\n if (input.createWorkspaceRoot === true) {\n ensureWorkspaceDirectory(workspaceRoot);\n } else {\n throw new ProjectWorkspaceMissingError(workspaceRoot);\n }\n }\n patch.workspaceRoot = workspaceRoot;\n }\n return this.store.update(id, patch);\n }\n\n delete(id: string): void {\n this.store.delete(id);\n }\n\n getWithDetails(id: string): ProjectWithDetails | null {\n return this.store.getWithDetails(id);\n }\n\n attachSession(sessionKey: string, projectId: string): void {\n bindSessionToProject(sessionKey, projectId);\n }\n\n detachSession(sessionKey: string): void {\n unbindSessionFromProject(sessionKey);\n }\n\n listSessionKeys(projectId: string, limit?: number, offset?: number): string[] {\n return listProjectSessionKeys(projectId, limit, offset);\n }\n\n attachGoal(goalId: string, projectId: string): void {\n bindGoalToProject(goalId, projectId);\n }\n\n detachGoal(goalId: string): void {\n unbindGoalFromProject(goalId);\n }\n\n listGoalIds(projectId: string, limit?: number, offset?: number): string[] {\n return listProjectGoalIds(projectId, limit, offset);\n }\n\n suggestProjectsForSession(sessionKey: string): ProjectSuggestion[] {\n const session = getSessionMetadata(sessionKey);\n if (!session) return [];\n const projects = this.list({ status: 'active', limit: 500 }).items;\n const haystack = [session.key, session.name, session.routing?.agentId, session.sourceChannel, ...(session.tags ?? [])]\n .filter(Boolean)\n .join(' ')\n .toLowerCase();\n const suggestions: ProjectSuggestion[] = [];\n for (const project of projects) {\n let score = 0;\n const reasons: string[] = [];\n if (haystack.includes(project.slug.toLowerCase())) {\n score += 10;\n reasons.push('session metadata matches project slug');\n }\n for (const token of project.name.toLowerCase().split(/\\s+/).filter((t) => t.length >= 3)) {\n if (haystack.includes(token)) {\n score += 3;\n reasons.push(`matches ${token}`);\n break;\n }\n }\n if (score > 0) {\n suggestions.push({ projectId: project.id, projectName: project.name, score, reason: reasons.join(', ') });\n }\n }\n return suggestions.sort((a, b) => b.score - a.score).slice(0, 5);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
2
|
+
import { escapeFts5Query } from "../storage/sqlite/fts.js";
|
|
2
3
|
import { randomUUID } from "node:crypto";
|
|
3
4
|
//#region src/projects/project-store.ts
|
|
4
5
|
function trimOptional(value) {
|
|
@@ -43,6 +44,25 @@ function projectSortColumn(sortBy) {
|
|
|
43
44
|
default: return "updated_at";
|
|
44
45
|
}
|
|
45
46
|
}
|
|
47
|
+
function projectSortExpression(sortBy) {
|
|
48
|
+
const column = projectSortColumn(sortBy);
|
|
49
|
+
return column === "LOWER(name)" ? "LOWER(p.name)" : `p.${column}`;
|
|
50
|
+
}
|
|
51
|
+
function projectFtsContent(project) {
|
|
52
|
+
return [
|
|
53
|
+
project.name,
|
|
54
|
+
project.slug,
|
|
55
|
+
project.description,
|
|
56
|
+
project.workspaceRoot,
|
|
57
|
+
project.brief,
|
|
58
|
+
project.instructions,
|
|
59
|
+
project.defaultAgentId
|
|
60
|
+
].filter((value) => Boolean(value?.trim())).join("\n");
|
|
61
|
+
}
|
|
62
|
+
function syncProjectFts(db, project) {
|
|
63
|
+
db.prepare(`DELETE FROM projects_fts WHERE project_id = ?`).run(project.id);
|
|
64
|
+
db.prepare(`INSERT INTO projects_fts (content, project_id) VALUES (?, ?)`).run(projectFtsContent(project), project.id);
|
|
65
|
+
}
|
|
46
66
|
var ProjectStore = class {
|
|
47
67
|
create(input) {
|
|
48
68
|
const now = Date.now();
|
|
@@ -67,6 +87,7 @@ var ProjectStore = class {
|
|
|
67
87
|
project_id, name, slug, description, status, default_agent_id, workspace_root,
|
|
68
88
|
brief, instructions, created_at, updated_at, last_active_at
|
|
69
89
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(project.id, project.name, project.slug, project.description ?? null, project.status, project.defaultAgentId ?? null, project.workspaceRoot ?? null, project.brief ?? null, project.instructions ?? null, project.createdAt, project.updatedAt, project.lastActiveAt ?? null);
|
|
90
|
+
syncProjectFts(db, project);
|
|
70
91
|
});
|
|
71
92
|
return project;
|
|
72
93
|
}
|
|
@@ -81,25 +102,26 @@ var ProjectStore = class {
|
|
|
81
102
|
list(query = {}) {
|
|
82
103
|
const conditions = [];
|
|
83
104
|
const params = [];
|
|
105
|
+
const search = query.search?.trim();
|
|
106
|
+
const ftsQuery = search ? escapeFts5Query(search) : "";
|
|
84
107
|
if (query.status) {
|
|
85
108
|
const statuses = Array.isArray(query.status) ? query.status : [query.status];
|
|
86
|
-
conditions.push(`status IN (${statuses.map(() => "?").join(", ")})`);
|
|
109
|
+
conditions.push(`p.status IN (${statuses.map(() => "?").join(", ")})`);
|
|
87
110
|
params.push(...statuses);
|
|
88
111
|
}
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
params.push(like, like, like);
|
|
112
|
+
if (ftsQuery) {
|
|
113
|
+
conditions.push(`p.project_id IN (SELECT project_id FROM projects_fts WHERE projects_fts MATCH ?)`);
|
|
114
|
+
params.push(ftsQuery);
|
|
93
115
|
}
|
|
94
116
|
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
95
117
|
const limit = clampLimit(query.limit, 50);
|
|
96
118
|
const offset = Math.max(0, Math.floor(query.offset ?? 0));
|
|
97
119
|
const sortOrder = query.sortOrder === "asc" ? "ASC" : "DESC";
|
|
98
|
-
const sortColumn =
|
|
120
|
+
const sortColumn = projectSortExpression(query.sortBy);
|
|
99
121
|
const db = getSqliteDatabase();
|
|
100
|
-
const total = db.prepare(`SELECT COUNT(*) AS total FROM projects ${where}`).get(...params).total;
|
|
122
|
+
const total = db.prepare(`SELECT COUNT(*) AS total FROM projects p ${where}`).get(...params).total;
|
|
101
123
|
return {
|
|
102
|
-
items: db.prepare(`SELECT
|
|
124
|
+
items: db.prepare(`SELECT p.* FROM projects p ${where} ORDER BY ${sortColumn} ${sortOrder} LIMIT ? OFFSET ?`).all(...params, limit, offset).map(projectFromRow),
|
|
103
125
|
total,
|
|
104
126
|
limit,
|
|
105
127
|
offset,
|
|
@@ -124,6 +146,7 @@ var ProjectStore = class {
|
|
|
124
146
|
db.prepare(`UPDATE projects SET
|
|
125
147
|
name = ?, description = ?, status = ?, default_agent_id = ?, workspace_root = ?, brief = ?, instructions = ?, updated_at = ?
|
|
126
148
|
WHERE project_id = ?`).run(next.name, next.description ?? null, next.status, next.defaultAgentId ?? null, next.workspaceRoot ?? null, next.brief ?? null, next.instructions ?? null, next.updatedAt, id);
|
|
149
|
+
syncProjectFts(db, next);
|
|
127
150
|
});
|
|
128
151
|
return this.get(id);
|
|
129
152
|
}
|
|
@@ -134,6 +157,7 @@ var ProjectStore = class {
|
|
|
134
157
|
db.prepare(`UPDATE workflow_runs SET project_id = NULL WHERE project_id = ?`).run(id);
|
|
135
158
|
db.prepare(`UPDATE automations SET project_id = NULL WHERE project_id = ?`).run(id);
|
|
136
159
|
db.prepare(`UPDATE memory_records SET project_id = NULL WHERE project_id = ?`).run(id);
|
|
160
|
+
db.prepare(`DELETE FROM projects_fts WHERE project_id = ?`).run(id);
|
|
137
161
|
db.prepare(`DELETE FROM projects WHERE project_id = ?`).run(id);
|
|
138
162
|
});
|
|
139
163
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-store.js","names":[],"sources":["../../../src/projects/project-store.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../storage/sqlite/transaction.js';\nimport type {\n CreateProjectInput,\n Project,\n ProjectListQuery,\n ProjectListResult,\n ProjectStatus,\n ProjectWithDetails,\n UpdateProjectInput,\n} from './types.js';\n\nexport type ProjectRow = {\n project_id: string;\n name: string;\n slug: string;\n description: string | null;\n status: string;\n default_agent_id: string | null;\n workspace_root: string | null;\n brief: string | null;\n instructions: string | null;\n created_at: number;\n updated_at: number;\n last_active_at: number | null;\n};\n\nfunction trimOptional(value: string | null | undefined): string | undefined {\n if (value == null) return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction nullableText(value: string | null | undefined): string | null {\n if (value == null) return null;\n const trimmed = value.trim();\n return trimmed || null;\n}\n\nfunction projectFromRow(row: ProjectRow): Project {\n return {\n id: row.project_id,\n name: row.name,\n slug: row.slug,\n description: row.description ?? undefined,\n status: row.status as ProjectStatus,\n defaultAgentId: row.default_agent_id ?? undefined,\n workspaceRoot: row.workspace_root ?? undefined,\n brief: row.brief ?? undefined,\n instructions: row.instructions ?? undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n lastActiveAt: row.last_active_at ?? undefined,\n };\n}\n\nfunction clampLimit(limit: number | undefined, fallback = 50): number {\n return Math.min(500, Math.max(1, Math.floor(limit ?? fallback)));\n}\n\nfunction normalizeName(name: string): string {\n const trimmed = name.trim();\n if (!trimmed) throw new Error('Project name is required');\n return trimmed;\n}\n\nexport function slugifyProjectName(name: string): string {\n const slug = name\n .trim()\n .toLowerCase()\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^\\p{L}\\p{N}]+/gu, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80);\n return slug || `project-${Date.now()}`;\n}\n\nfunction projectSortColumn(sortBy: ProjectListQuery['sortBy']): string {\n switch (sortBy) {\n case 'createdAt':\n return 'created_at';\n case 'name':\n return 'LOWER(name)';\n case 'updatedAt':\n default:\n return 'updated_at';\n }\n}\n\nexport class ProjectStore {\n create(input: CreateProjectInput): Project {\n const now = Date.now();\n const name = normalizeName(input.name);\n const slug = input.slug?.trim() || slugifyProjectName(name);\n const project: Project = {\n id: randomUUID(),\n name,\n slug,\n description: trimOptional(input.description),\n status: 'active',\n defaultAgentId: trimOptional(input.defaultAgentId),\n workspaceRoot: trimOptional(input.workspaceRoot),\n brief: trimOptional(input.brief),\n instructions: trimOptional(input.instructions),\n createdAt: now,\n updatedAt: now,\n lastActiveAt: now,\n };\n\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO projects (\n project_id, name, slug, description, status, default_agent_id, workspace_root,\n brief, instructions, created_at, updated_at, last_active_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n project.id,\n project.name,\n project.slug,\n project.description ?? null,\n project.status,\n project.defaultAgentId ?? null,\n project.workspaceRoot ?? null,\n project.brief ?? null,\n project.instructions ?? null,\n project.createdAt,\n project.updatedAt,\n project.lastActiveAt ?? null,\n );\n });\n return project;\n }\n\n get(id: string): Project | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM projects WHERE project_id = ?`)\n .get(id) as ProjectRow | undefined;\n return row ? projectFromRow(row) : null;\n }\n\n findBySlug(slug: string): Project | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM projects WHERE slug = ?`)\n .get(slug.trim()) as ProjectRow | undefined;\n return row ? projectFromRow(row) : null;\n }\n\n list(query: ProjectListQuery = {}): ProjectListResult {\n const conditions: string[] = [];\n const params: Array<string | number> = [];\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n conditions.push(`status IN (${statuses.map(() => '?').join(', ')})`);\n params.push(...statuses);\n }\n if (query.search?.trim()) {\n const like = `%${query.search.trim().toLowerCase()}%`;\n conditions.push(`(LOWER(name) LIKE ? OR LOWER(slug) LIKE ? OR LOWER(COALESCE(description, '')) LIKE ?)`);\n params.push(like, like, like);\n }\n const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = clampLimit(query.limit, 50);\n const offset = Math.max(0, Math.floor(query.offset ?? 0));\n const sortOrder = query.sortOrder === 'asc' ? 'ASC' : 'DESC';\n const sortColumn = projectSortColumn(query.sortBy);\n const db = getSqliteDatabase();\n const total = (db.prepare(`SELECT COUNT(*) AS total FROM projects ${where}`).get(...params) as { total: number }).total;\n const rows = db\n .prepare(`SELECT * FROM projects ${where} ORDER BY ${sortColumn} ${sortOrder} LIMIT ? OFFSET ?`)\n .all(...params, limit, offset) as ProjectRow[];\n return { items: rows.map(projectFromRow), total, limit, offset, hasMore: offset + limit < total };\n }\n\n update(id: string, input: UpdateProjectInput): Project {\n const before = this.get(id);\n if (!before) throw new Error(`Project not found: ${id}`);\n const next = {\n ...before,\n ...(input.name !== undefined ? { name: normalizeName(input.name) } : {}),\n ...(input.description !== undefined ? { description: nullableText(input.description) ?? undefined } : {}),\n ...(input.status !== undefined ? { status: input.status } : {}),\n ...(input.defaultAgentId !== undefined ? { defaultAgentId: nullableText(input.defaultAgentId) ?? undefined } : {}),\n ...(input.workspaceRoot !== undefined ? { workspaceRoot: nullableText(input.workspaceRoot) ?? undefined } : {}),\n ...(input.brief !== undefined ? { brief: nullableText(input.brief) ?? undefined } : {}),\n ...(input.instructions !== undefined ? { instructions: nullableText(input.instructions) ?? undefined } : {}),\n updatedAt: Date.now(),\n } satisfies Project;\n\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `UPDATE projects SET\n name = ?, description = ?, status = ?, default_agent_id = ?, workspace_root = ?, brief = ?, instructions = ?, updated_at = ?\n WHERE project_id = ?`,\n ).run(\n next.name,\n next.description ?? null,\n next.status,\n next.defaultAgentId ?? null,\n next.workspaceRoot ?? null,\n next.brief ?? null,\n next.instructions ?? null,\n next.updatedAt,\n id,\n );\n });\n return this.get(id)!;\n }\n\n delete(id: string): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(`UPDATE sessions SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`UPDATE goals SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`UPDATE workflow_runs SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`UPDATE automations SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`UPDATE memory_records SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`DELETE FROM projects WHERE project_id = ?`).run(id);\n });\n }\n\n getSessionCount(id: string): number {\n return (getSqliteDatabase()\n .prepare(`SELECT COUNT(*) AS total FROM sessions WHERE project_id = ? AND hidden_from_session_list = 0`)\n .get(id) as { total: number }).total;\n }\n\n getGoalCount(id: string): number {\n return (getSqliteDatabase().prepare(`SELECT COUNT(*) AS total FROM goals WHERE project_id = ?`).get(id) as { total: number }).total;\n }\n\n getActiveGoalCount(id: string): number {\n return (getSqliteDatabase()\n .prepare(`SELECT COUNT(*) AS total FROM goals WHERE project_id = ? AND status IN ('active', 'paused', 'blocked', 'needs_input')`)\n .get(id) as { total: number }).total;\n }\n\n getRecentSessions(id: string, limit = 5): ProjectWithDetails['recentSessions'] {\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT session_key, name, updated_at, agent_id FROM sessions\n WHERE project_id = ? AND hidden_from_session_list = 0 ORDER BY updated_at DESC LIMIT ?`,\n )\n .all(id, clampLimit(limit, 5)) as Array<{ session_key: string; name: string | null; updated_at: number; agent_id: string }>;\n return rows.map((row) => ({\n key: row.session_key,\n name: row.name ?? undefined,\n updatedAt: new Date(row.updated_at).toISOString(),\n agentId: row.agent_id,\n }));\n }\n\n getRecentWorkflowRuns(id: string, limit = 5): ProjectWithDetails['recentWorkflowRuns'] {\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT run_id, definition_id, status, created_at_ms FROM workflow_runs\n WHERE project_id = ? ORDER BY created_at_ms DESC LIMIT ?`,\n )\n .all(id, clampLimit(limit, 5)) as Array<{ run_id: string; definition_id: string; status: string; created_at_ms: number }>;\n return rows.map((row) => ({ runId: row.run_id, definitionId: row.definition_id, status: row.status, createdAt: row.created_at_ms }));\n }\n\n getWithDetails(id: string): ProjectWithDetails | null {\n const project = this.get(id);\n if (!project) return null;\n return {\n ...project,\n sessionCount: this.getSessionCount(id),\n goalCount: this.getGoalCount(id),\n activeGoalCount: this.getActiveGoalCount(id),\n recentSessions: this.getRecentSessions(id),\n recentWorkflowRuns: this.getRecentWorkflowRuns(id),\n };\n }\n\n generateSlug(name: string): string {\n const base = slugifyProjectName(name);\n let candidate = base;\n let counter = 2;\n while (this.findBySlug(candidate)) {\n candidate = `${base}-${counter}`;\n counter += 1;\n }\n return candidate;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"project-store.js","names":[],"sources":["../../../src/projects/project-store.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { escapeFts5Query } from '../storage/sqlite/fts.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../storage/sqlite/transaction.js';\nimport type {\n CreateProjectInput,\n Project,\n ProjectListQuery,\n ProjectListResult,\n ProjectStatus,\n ProjectWithDetails,\n UpdateProjectInput,\n} from './types.js';\n\nexport type ProjectRow = {\n project_id: string;\n name: string;\n slug: string;\n description: string | null;\n status: string;\n default_agent_id: string | null;\n workspace_root: string | null;\n brief: string | null;\n instructions: string | null;\n created_at: number;\n updated_at: number;\n last_active_at: number | null;\n};\n\nfunction trimOptional(value: string | null | undefined): string | undefined {\n if (value == null) return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction nullableText(value: string | null | undefined): string | null {\n if (value == null) return null;\n const trimmed = value.trim();\n return trimmed || null;\n}\n\nfunction projectFromRow(row: ProjectRow): Project {\n return {\n id: row.project_id,\n name: row.name,\n slug: row.slug,\n description: row.description ?? undefined,\n status: row.status as ProjectStatus,\n defaultAgentId: row.default_agent_id ?? undefined,\n workspaceRoot: row.workspace_root ?? undefined,\n brief: row.brief ?? undefined,\n instructions: row.instructions ?? undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n lastActiveAt: row.last_active_at ?? undefined,\n };\n}\n\nfunction clampLimit(limit: number | undefined, fallback = 50): number {\n return Math.min(500, Math.max(1, Math.floor(limit ?? fallback)));\n}\n\nfunction normalizeName(name: string): string {\n const trimmed = name.trim();\n if (!trimmed) throw new Error('Project name is required');\n return trimmed;\n}\n\nexport function slugifyProjectName(name: string): string {\n const slug = name\n .trim()\n .toLowerCase()\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^\\p{L}\\p{N}]+/gu, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80);\n return slug || `project-${Date.now()}`;\n}\n\nfunction projectSortColumn(sortBy: ProjectListQuery['sortBy']): string {\n switch (sortBy) {\n case 'createdAt':\n return 'created_at';\n case 'name':\n return 'LOWER(name)';\n case 'updatedAt':\n default:\n return 'updated_at';\n }\n}\n\nfunction projectSortExpression(sortBy: ProjectListQuery['sortBy']): string {\n const column = projectSortColumn(sortBy);\n return column === 'LOWER(name)' ? 'LOWER(p.name)' : `p.${column}`;\n}\n\nfunction projectFtsContent(project: Project): string {\n return [\n project.name,\n project.slug,\n project.description,\n project.workspaceRoot,\n project.brief,\n project.instructions,\n project.defaultAgentId,\n ]\n .filter((value): value is string => Boolean(value?.trim()))\n .join('\\n');\n}\n\nfunction syncProjectFts(\n db: ReturnType<typeof getSqliteDatabase>,\n project: Project,\n): void {\n db.prepare(`DELETE FROM projects_fts WHERE project_id = ?`).run(project.id);\n db.prepare(`INSERT INTO projects_fts (content, project_id) VALUES (?, ?)`).run(\n projectFtsContent(project),\n project.id,\n );\n}\n\nexport class ProjectStore {\n create(input: CreateProjectInput): Project {\n const now = Date.now();\n const name = normalizeName(input.name);\n const slug = input.slug?.trim() || slugifyProjectName(name);\n const project: Project = {\n id: randomUUID(),\n name,\n slug,\n description: trimOptional(input.description),\n status: 'active',\n defaultAgentId: trimOptional(input.defaultAgentId),\n workspaceRoot: trimOptional(input.workspaceRoot),\n brief: trimOptional(input.brief),\n instructions: trimOptional(input.instructions),\n createdAt: now,\n updatedAt: now,\n lastActiveAt: now,\n };\n\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO projects (\n project_id, name, slug, description, status, default_agent_id, workspace_root,\n brief, instructions, created_at, updated_at, last_active_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n project.id,\n project.name,\n project.slug,\n project.description ?? null,\n project.status,\n project.defaultAgentId ?? null,\n project.workspaceRoot ?? null,\n project.brief ?? null,\n project.instructions ?? null,\n project.createdAt,\n project.updatedAt,\n project.lastActiveAt ?? null,\n );\n syncProjectFts(db, project);\n });\n return project;\n }\n\n get(id: string): Project | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM projects WHERE project_id = ?`)\n .get(id) as ProjectRow | undefined;\n return row ? projectFromRow(row) : null;\n }\n\n findBySlug(slug: string): Project | null {\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM projects WHERE slug = ?`)\n .get(slug.trim()) as ProjectRow | undefined;\n return row ? projectFromRow(row) : null;\n }\n\n list(query: ProjectListQuery = {}): ProjectListResult {\n const conditions: string[] = [];\n const params: Array<string | number> = [];\n const search = query.search?.trim();\n const ftsQuery = search ? escapeFts5Query(search) : '';\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n conditions.push(`p.status IN (${statuses.map(() => '?').join(', ')})`);\n params.push(...statuses);\n }\n if (ftsQuery) {\n conditions.push(`p.project_id IN (SELECT project_id FROM projects_fts WHERE projects_fts MATCH ?)`);\n params.push(ftsQuery);\n }\n const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = clampLimit(query.limit, 50);\n const offset = Math.max(0, Math.floor(query.offset ?? 0));\n const sortOrder = query.sortOrder === 'asc' ? 'ASC' : 'DESC';\n const sortColumn = projectSortExpression(query.sortBy);\n const db = getSqliteDatabase();\n const total = (db.prepare(`SELECT COUNT(*) AS total FROM projects p ${where}`).get(...params) as { total: number }).total;\n const rows = db\n .prepare(`SELECT p.* FROM projects p ${where} ORDER BY ${sortColumn} ${sortOrder} LIMIT ? OFFSET ?`)\n .all(...params, limit, offset) as ProjectRow[];\n return { items: rows.map(projectFromRow), total, limit, offset, hasMore: offset + limit < total };\n }\n\n update(id: string, input: UpdateProjectInput): Project {\n const before = this.get(id);\n if (!before) throw new Error(`Project not found: ${id}`);\n const next = {\n ...before,\n ...(input.name !== undefined ? { name: normalizeName(input.name) } : {}),\n ...(input.description !== undefined ? { description: nullableText(input.description) ?? undefined } : {}),\n ...(input.status !== undefined ? { status: input.status } : {}),\n ...(input.defaultAgentId !== undefined ? { defaultAgentId: nullableText(input.defaultAgentId) ?? undefined } : {}),\n ...(input.workspaceRoot !== undefined ? { workspaceRoot: nullableText(input.workspaceRoot) ?? undefined } : {}),\n ...(input.brief !== undefined ? { brief: nullableText(input.brief) ?? undefined } : {}),\n ...(input.instructions !== undefined ? { instructions: nullableText(input.instructions) ?? undefined } : {}),\n updatedAt: Date.now(),\n } satisfies Project;\n\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `UPDATE projects SET\n name = ?, description = ?, status = ?, default_agent_id = ?, workspace_root = ?, brief = ?, instructions = ?, updated_at = ?\n WHERE project_id = ?`,\n ).run(\n next.name,\n next.description ?? null,\n next.status,\n next.defaultAgentId ?? null,\n next.workspaceRoot ?? null,\n next.brief ?? null,\n next.instructions ?? null,\n next.updatedAt,\n id,\n );\n syncProjectFts(db, next);\n });\n return this.get(id)!;\n }\n\n delete(id: string): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(`UPDATE sessions SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`UPDATE goals SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`UPDATE workflow_runs SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`UPDATE automations SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`UPDATE memory_records SET project_id = NULL WHERE project_id = ?`).run(id);\n db.prepare(`DELETE FROM projects_fts WHERE project_id = ?`).run(id);\n db.prepare(`DELETE FROM projects WHERE project_id = ?`).run(id);\n });\n }\n\n getSessionCount(id: string): number {\n return (getSqliteDatabase()\n .prepare(`SELECT COUNT(*) AS total FROM sessions WHERE project_id = ? AND hidden_from_session_list = 0`)\n .get(id) as { total: number }).total;\n }\n\n getGoalCount(id: string): number {\n return (getSqliteDatabase().prepare(`SELECT COUNT(*) AS total FROM goals WHERE project_id = ?`).get(id) as { total: number }).total;\n }\n\n getActiveGoalCount(id: string): number {\n return (getSqliteDatabase()\n .prepare(`SELECT COUNT(*) AS total FROM goals WHERE project_id = ? AND status IN ('active', 'paused', 'blocked', 'needs_input')`)\n .get(id) as { total: number }).total;\n }\n\n getRecentSessions(id: string, limit = 5): ProjectWithDetails['recentSessions'] {\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT session_key, name, updated_at, agent_id FROM sessions\n WHERE project_id = ? AND hidden_from_session_list = 0 ORDER BY updated_at DESC LIMIT ?`,\n )\n .all(id, clampLimit(limit, 5)) as Array<{ session_key: string; name: string | null; updated_at: number; agent_id: string }>;\n return rows.map((row) => ({\n key: row.session_key,\n name: row.name ?? undefined,\n updatedAt: new Date(row.updated_at).toISOString(),\n agentId: row.agent_id,\n }));\n }\n\n getRecentWorkflowRuns(id: string, limit = 5): ProjectWithDetails['recentWorkflowRuns'] {\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT run_id, definition_id, status, created_at_ms FROM workflow_runs\n WHERE project_id = ? ORDER BY created_at_ms DESC LIMIT ?`,\n )\n .all(id, clampLimit(limit, 5)) as Array<{ run_id: string; definition_id: string; status: string; created_at_ms: number }>;\n return rows.map((row) => ({ runId: row.run_id, definitionId: row.definition_id, status: row.status, createdAt: row.created_at_ms }));\n }\n\n getWithDetails(id: string): ProjectWithDetails | null {\n const project = this.get(id);\n if (!project) return null;\n return {\n ...project,\n sessionCount: this.getSessionCount(id),\n goalCount: this.getGoalCount(id),\n activeGoalCount: this.getActiveGoalCount(id),\n recentSessions: this.getRecentSessions(id),\n recentWorkflowRuns: this.getRecentWorkflowRuns(id),\n };\n }\n\n generateSlug(name: string): string {\n const base = slugifyProjectName(name);\n let candidate = base;\n let counter = 2;\n while (this.findBySlug(candidate)) {\n candidate = `${base}-${counter}`;\n counter += 1;\n }\n return candidate;\n }\n}\n"],"mappings":"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"}
|
|
@@ -40,11 +40,13 @@ export interface ProjectListQuery {
|
|
|
40
40
|
offset?: number;
|
|
41
41
|
}
|
|
42
42
|
export interface CreateProjectInput {
|
|
43
|
-
name
|
|
43
|
+
name?: string;
|
|
44
44
|
slug?: string;
|
|
45
45
|
description?: string;
|
|
46
46
|
defaultAgentId?: string;
|
|
47
47
|
workspaceRoot?: string;
|
|
48
|
+
createWorkspaceRoot?: boolean;
|
|
49
|
+
projectKind?: string;
|
|
48
50
|
brief?: string;
|
|
49
51
|
instructions?: string;
|
|
50
52
|
}
|
|
@@ -54,6 +56,7 @@ export interface UpdateProjectInput {
|
|
|
54
56
|
defaultAgentId?: string | null;
|
|
55
57
|
status?: ProjectStatus;
|
|
56
58
|
workspaceRoot?: string | null;
|
|
59
|
+
createWorkspaceRoot?: boolean;
|
|
57
60
|
brief?: string | null;
|
|
58
61
|
instructions?: string | null;
|
|
59
62
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Project } from './types.js';
|
|
2
|
+
export type WorkspaceProjectMatchReason = 'exact' | 'contained' | 'auto_created';
|
|
3
|
+
export type WorkspaceProjectMatch = {
|
|
4
|
+
project: Project;
|
|
5
|
+
reason: WorkspaceProjectMatchReason;
|
|
6
|
+
created: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare class ProjectWorkspaceConflictError extends Error {
|
|
9
|
+
readonly project: Project;
|
|
10
|
+
constructor(project: Project, message?: string);
|
|
11
|
+
}
|
|
12
|
+
export declare class ProjectWorkspaceMissingError extends Error {
|
|
13
|
+
readonly workspaceRoot: string;
|
|
14
|
+
constructor(workspaceRoot: string, message?: string);
|
|
15
|
+
}
|
|
16
|
+
export declare function canonicalWorkspacePath(raw: string | null | undefined): string | null;
|
|
17
|
+
export declare function workspaceDirectoryExists(workspaceRoot: string | null | undefined): boolean;
|
|
18
|
+
export declare function ensureWorkspaceDirectory(workspaceRoot: string): void;
|
|
19
|
+
export declare function resolveWorkspaceProjectRoot(raw: string | null | undefined): string | null;
|
|
20
|
+
export declare function isPathSameOrInsideWorkspace(workspaceRoot: string, candidatePath: string): boolean;
|
|
21
|
+
export declare function inferProjectNameFromWorkspaceRoot(workspaceRoot: string | null | undefined): string | null;
|
|
22
|
+
export declare function isSafeAutoCreateWorkspaceRoot(workspaceRoot: string | null | undefined): boolean;
|