@edspencer/paddock 0.0.1 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/install-notice.mjs +29 -0
- package/package.json +58 -5
- package/packages/server/dist/adoptable.d.ts +127 -0
- package/packages/server/dist/adoptable.js +315 -0
- package/packages/server/dist/app.d.ts +67 -0
- package/packages/server/dist/app.js +325 -0
- package/packages/server/dist/archive.d.ts +42 -0
- package/packages/server/dist/archive.js +125 -0
- package/packages/server/dist/attachments-config.d.ts +114 -0
- package/packages/server/dist/attachments-config.js +165 -0
- package/packages/server/dist/attachments-hint.d.ts +53 -0
- package/packages/server/dist/attachments-hint.js +140 -0
- package/packages/server/dist/attachments.d.ts +33 -0
- package/packages/server/dist/attachments.js +117 -0
- package/packages/server/dist/auth.d.ts +84 -0
- package/packages/server/dist/auth.js +247 -0
- package/packages/server/dist/background.d.ts +36 -0
- package/packages/server/dist/background.js +137 -0
- package/packages/server/dist/bind-safety.d.ts +56 -0
- package/packages/server/dist/bind-safety.js +51 -0
- package/packages/server/dist/brand.d.ts +44 -0
- package/packages/server/dist/brand.js +88 -0
- package/packages/server/dist/chat-dto.d.ts +210 -0
- package/packages/server/dist/chat-dto.js +308 -0
- package/packages/server/dist/cli/import-chats.d.ts +21 -0
- package/packages/server/dist/cli/import-chats.js +197 -0
- package/packages/server/dist/cli/paddock.d.ts +2 -0
- package/packages/server/dist/cli/paddock.js +247 -0
- package/packages/server/dist/concurrency.d.ts +57 -0
- package/packages/server/dist/concurrency.js +71 -0
- package/packages/server/dist/config.d.ts +512 -0
- package/packages/server/dist/config.js +593 -0
- package/packages/server/dist/curation-config.d.ts +55 -0
- package/packages/server/dist/curation-config.js +64 -0
- package/packages/server/dist/event-bus.d.ts +87 -0
- package/packages/server/dist/event-bus.js +48 -0
- package/packages/server/dist/git.d.ts +128 -0
- package/packages/server/dist/git.js +446 -0
- package/packages/server/dist/github-auth.d.ts +44 -0
- package/packages/server/dist/github-auth.js +169 -0
- package/packages/server/dist/herdctl-agent-config.d.ts +98 -0
- package/packages/server/dist/herdctl-agent-config.js +331 -0
- package/packages/server/dist/herdctl-agent-names.d.ts +172 -0
- package/packages/server/dist/herdctl-agent-names.js +228 -0
- package/packages/server/dist/herdctl-jobs-index.d.ts +65 -0
- package/packages/server/dist/herdctl-jobs-index.js +225 -0
- package/packages/server/dist/herdctl-jobs.d.ts +98 -0
- package/packages/server/dist/herdctl-jobs.js +225 -0
- package/packages/server/dist/herdctl.d.ts +572 -0
- package/packages/server/dist/herdctl.js +1218 -0
- package/packages/server/dist/hook-config.d.ts +217 -0
- package/packages/server/dist/hook-config.js +224 -0
- package/packages/server/dist/http-bytes.d.ts +24 -0
- package/packages/server/dist/http-bytes.js +52 -0
- package/packages/server/dist/index.d.ts +1 -0
- package/packages/server/dist/index.js +14 -0
- package/packages/server/dist/instance-config.d.ts +135 -0
- package/packages/server/dist/instance-config.js +345 -0
- package/packages/server/dist/localcommand.d.ts +23 -0
- package/packages/server/dist/localcommand.js +140 -0
- package/packages/server/dist/management-auth.d.ts +99 -0
- package/packages/server/dist/management-auth.js +165 -0
- package/packages/server/dist/management-config.d.ts +164 -0
- package/packages/server/dist/management-config.js +286 -0
- package/packages/server/dist/management-mcp-server.d.ts +20 -0
- package/packages/server/dist/management-mcp-server.js +95 -0
- package/packages/server/dist/management-metadata.d.ts +67 -0
- package/packages/server/dist/management-metadata.js +56 -0
- package/packages/server/dist/management-ops.d.ts +110 -0
- package/packages/server/dist/management-ops.js +449 -0
- package/packages/server/dist/management-policy.d.ts +172 -0
- package/packages/server/dist/management-policy.js +259 -0
- package/packages/server/dist/message-provenance.d.ts +112 -0
- package/packages/server/dist/message-provenance.js +260 -0
- package/packages/server/dist/models.d.ts +164 -0
- package/packages/server/dist/models.js +208 -0
- package/packages/server/dist/openapi.d.ts +66 -0
- package/packages/server/dist/openapi.js +183 -0
- package/packages/server/dist/parent-detach.d.ts +30 -0
- package/packages/server/dist/parent-detach.js +109 -0
- package/packages/server/dist/preload.d.ts +43 -0
- package/packages/server/dist/preload.js +55 -0
- package/packages/server/dist/project-files.d.ts +76 -0
- package/packages/server/dist/project-files.js +191 -0
- package/packages/server/dist/project-mime.d.ts +35 -0
- package/packages/server/dist/project-mime.js +55 -0
- package/packages/server/dist/project-paths.d.ts +88 -0
- package/packages/server/dist/project-paths.js +126 -0
- package/packages/server/dist/project-types.d.ts +316 -0
- package/packages/server/dist/project-types.js +48 -0
- package/packages/server/dist/projects.d.ts +202 -0
- package/packages/server/dist/projects.js +894 -0
- package/packages/server/dist/queued-message.d.ts +35 -0
- package/packages/server/dist/queued-message.js +116 -0
- package/packages/server/dist/read-state.d.ts +46 -0
- package/packages/server/dist/read-state.js +139 -0
- package/packages/server/dist/recovery-config.d.ts +111 -0
- package/packages/server/dist/recovery-config.js +105 -0
- package/packages/server/dist/recovery.d.ts +262 -0
- package/packages/server/dist/recovery.js +490 -0
- package/packages/server/dist/resume-drain.d.ts +122 -0
- package/packages/server/dist/resume-drain.js +158 -0
- package/packages/server/dist/route-context.d.ts +168 -0
- package/packages/server/dist/route-context.js +39 -0
- package/packages/server/dist/route-errors.d.ts +8 -0
- package/packages/server/dist/route-errors.js +13 -0
- package/packages/server/dist/routes/chats.d.ts +12 -0
- package/packages/server/dist/routes/chats.js +1406 -0
- package/packages/server/dist/routes/git.d.ts +13 -0
- package/packages/server/dist/routes/git.js +236 -0
- package/packages/server/dist/routes/mcp.d.ts +83 -0
- package/packages/server/dist/routes/mcp.js +250 -0
- package/packages/server/dist/routes/meta.d.ts +15 -0
- package/packages/server/dist/routes/meta.js +447 -0
- package/packages/server/dist/routes/projects.d.ts +25 -0
- package/packages/server/dist/routes/projects.js +736 -0
- package/packages/server/dist/routes/triggers.d.ts +21 -0
- package/packages/server/dist/routes/triggers.js +324 -0
- package/packages/server/dist/routes/workspace-mount.d.ts +48 -0
- package/packages/server/dist/routes/workspace-mount.js +32 -0
- package/packages/server/dist/routes.d.ts +26 -0
- package/packages/server/dist/routes.js +29 -0
- package/packages/server/dist/run-provenance.d.ts +109 -0
- package/packages/server/dist/run-provenance.js +206 -0
- package/packages/server/dist/runs.d.ts +92 -0
- package/packages/server/dist/runs.js +72 -0
- package/packages/server/dist/schedule-config.d.ts +68 -0
- package/packages/server/dist/schedule-config.js +75 -0
- package/packages/server/dist/schedule-session.d.ts +34 -0
- package/packages/server/dist/schedule-session.js +120 -0
- package/packages/server/dist/self-mcp-descriptions.d.ts +20 -0
- package/packages/server/dist/self-mcp-descriptions.js +145 -0
- package/packages/server/dist/self-mcp-read.d.ts +15 -0
- package/packages/server/dist/self-mcp-read.js +97 -0
- package/packages/server/dist/self-mcp-triggers.d.ts +30 -0
- package/packages/server/dist/self-mcp-triggers.js +144 -0
- package/packages/server/dist/self-mcp-types.d.ts +248 -0
- package/packages/server/dist/self-mcp-types.js +10 -0
- package/packages/server/dist/self-mcp-util.d.ts +73 -0
- package/packages/server/dist/self-mcp-util.js +157 -0
- package/packages/server/dist/self-mcp-write.d.ts +35 -0
- package/packages/server/dist/self-mcp-write.js +215 -0
- package/packages/server/dist/self-mcp.d.ts +95 -0
- package/packages/server/dist/self-mcp.js +448 -0
- package/packages/server/dist/send-file-mcp.d.ts +46 -0
- package/packages/server/dist/send-file-mcp.js +281 -0
- package/packages/server/dist/session-hub.d.ts +186 -0
- package/packages/server/dist/session-hub.js +291 -0
- package/packages/server/dist/spawn-capability.d.ts +99 -0
- package/packages/server/dist/spawn-capability.js +106 -0
- package/packages/server/dist/star.d.ts +26 -0
- package/packages/server/dist/star.js +94 -0
- package/packages/server/dist/start.d.ts +1 -0
- package/packages/server/dist/start.js +21 -0
- package/packages/server/dist/subagents.d.ts +337 -0
- package/packages/server/dist/subagents.js +626 -0
- package/packages/server/dist/sweep.d.ts +122 -0
- package/packages/server/dist/sweep.js +696 -0
- package/packages/server/dist/tooldetails.d.ts +33 -0
- package/packages/server/dist/tooldetails.js +466 -0
- package/packages/server/dist/transcribe.d.ts +69 -0
- package/packages/server/dist/transcribe.js +119 -0
- package/packages/server/dist/transcripts.d.ts +42 -0
- package/packages/server/dist/transcripts.js +164 -0
- package/packages/server/dist/trigger-config.d.ts +278 -0
- package/packages/server/dist/trigger-config.js +417 -0
- package/packages/server/dist/trigger-runtime.d.ts +94 -0
- package/packages/server/dist/trigger-runtime.js +82 -0
- package/packages/server/dist/trigger-session.d.ts +34 -0
- package/packages/server/dist/trigger-session.js +122 -0
- package/packages/server/dist/triggers.d.ts +83 -0
- package/packages/server/dist/triggers.js +124 -0
- package/packages/server/dist/trusted-proxy.d.ts +57 -0
- package/packages/server/dist/trusted-proxy.js +157 -0
- package/packages/server/dist/turn-notice.d.ts +154 -0
- package/packages/server/dist/turn-notice.js +271 -0
- package/packages/server/dist/unread.d.ts +36 -0
- package/packages/server/dist/unread.js +123 -0
- package/packages/server/dist/usage.d.ts +66 -0
- package/packages/server/dist/usage.js +243 -0
- package/packages/server/dist/wake-injection.d.ts +156 -0
- package/packages/server/dist/wake-injection.js +136 -0
- package/packages/server/dist/whisper-local.d.ts +9 -0
- package/packages/server/dist/whisper-local.js +122 -0
- package/packages/server/dist/ws-context.d.ts +158 -0
- package/packages/server/dist/ws-context.js +1 -0
- package/packages/server/dist/ws-protocol.d.ts +423 -0
- package/packages/server/dist/ws-protocol.js +251 -0
- package/packages/server/dist/ws-self-mcp.d.ts +52 -0
- package/packages/server/dist/ws-self-mcp.js +28 -0
- package/packages/server/dist/ws-triggers.d.ts +29 -0
- package/packages/server/dist/ws-triggers.js +261 -0
- package/packages/server/dist/ws-turn.d.ts +86 -0
- package/packages/server/dist/ws-turn.js +751 -0
- package/packages/server/dist/ws.d.ts +62 -0
- package/packages/server/dist/ws.js +1024 -0
- package/packages/server/package.json +6 -0
- package/packages/web/dist/assets/InstanceConfigPage-4cQGTj9Y.js +2 -0
- package/packages/web/dist/assets/MarkdownRenderer-CJZFLj_8.js +30 -0
- package/packages/web/dist/assets/ProjectRedirect-BHwVbbqB.js +2 -0
- package/packages/web/dist/assets/ProjectView-BOvuAITg.js +13 -0
- package/packages/web/dist/assets/ProjectsGrid-BCojZOGY.js +2 -0
- package/packages/web/dist/assets/arc-BVyxvnkl.js +2 -0
- package/packages/web/dist/assets/architectureDiagram-3BPJPVTR-Cd9Puqg1.js +37 -0
- package/packages/web/dist/assets/bash-I8pq0VWm.js +2 -0
- package/packages/web/dist/assets/blockDiagram-GPEHLZMM-B_AQt_Rx.js +133 -0
- package/packages/web/dist/assets/c-D2F1sQUL.js +2 -0
- package/packages/web/dist/assets/c4Diagram-AAUBKEIU-7e0TQtXB.js +11 -0
- package/packages/web/dist/assets/channel-C_1aISNn.js +2 -0
- package/packages/web/dist/assets/chunk-2J33WTMH-Dz4WZmUZ.js +2 -0
- package/packages/web/dist/assets/chunk-4BX2VUAB-DVWYldo6.js +2 -0
- package/packages/web/dist/assets/chunk-55IACEB6-BIMrizQd.js +2 -0
- package/packages/web/dist/assets/chunk-727SXJPM-CL2q9fF1.js +207 -0
- package/packages/web/dist/assets/chunk-AQP2D5EJ-BSq3HlQ9.js +232 -0
- package/packages/web/dist/assets/chunk-FMBD7UC4-Bgt64pLE.js +16 -0
- package/packages/web/dist/assets/chunk-ND2GUHAM-C8Pr2A38.js +2 -0
- package/packages/web/dist/assets/chunk-QZHKN3VN-BAOqh-MN.js +2 -0
- package/packages/web/dist/assets/classDiagram-4FO5ZUOK-B8JE7cHJ.js +2 -0
- package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-B8JE7cHJ.js +2 -0
- package/packages/web/dist/assets/core-lJeGACQg.js +4 -0
- package/packages/web/dist/assets/cose-bilkent-S5V4N54A-BF4tameA.js +2 -0
- package/packages/web/dist/assets/cpp-Bm3ii6kY.js +2 -0
- package/packages/web/dist/assets/csharp-BNpuYZMG.js +2 -0
- package/packages/web/dist/assets/css-DazXZka4.js +2 -0
- package/packages/web/dist/assets/cytoscape.esm-CUqq0XTU.js +332 -0
- package/packages/web/dist/assets/dagre-BM42HDAG-DdsLM6ql.js +5 -0
- package/packages/web/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
- package/packages/web/dist/assets/diagram-2AECGRRQ-BQ78S_RM.js +44 -0
- package/packages/web/dist/assets/diagram-5GNKFQAL-CAKE6nHE.js +11 -0
- package/packages/web/dist/assets/diagram-KO2AKTUF-fEvHRXgr.js +4 -0
- package/packages/web/dist/assets/diagram-LMA3HP47-CtWsgeSm.js +25 -0
- package/packages/web/dist/assets/diagram-OG6HWLK6-Buog8k7_.js +25 -0
- package/packages/web/dist/assets/erDiagram-TEJ5UH35-CWvVVPZS.js +86 -0
- package/packages/web/dist/assets/flowDiagram-I6XJVG4X-DsCGXtp4.js +163 -0
- package/packages/web/dist/assets/format-DamJQ9LO.js +2 -0
- package/packages/web/dist/assets/ganttDiagram-6RSMTGT7-CsdwIvvC.js +293 -0
- package/packages/web/dist/assets/gitGraphDiagram-PVQCEYII-hZvrX1sD.js +107 -0
- package/packages/web/dist/assets/go-BPx_y7j8.js +2 -0
- package/packages/web/dist/assets/graph-BlwPajkw.js +2 -0
- package/packages/web/dist/assets/index-CGbZdrm8.js +71 -0
- package/packages/web/dist/assets/index-DJDQ210-.css +1 -0
- package/packages/web/dist/assets/infoDiagram-5YYISTIA-nE_Vtz_l.js +3 -0
- package/packages/web/dist/assets/ini-B3BVgEsG.js +2 -0
- package/packages/web/dist/assets/init-Gi6I4Gst.js +2 -0
- package/packages/web/dist/assets/ishikawaDiagram-YF4QCWOH-2Nlqs0oH.js +71 -0
- package/packages/web/dist/assets/java-B5VKqtH2.js +2 -0
- package/packages/web/dist/assets/javascript-BKRaQes9.js +2 -0
- package/packages/web/dist/assets/journeyDiagram-JHISSGLW-mjU1fMc4.js +140 -0
- package/packages/web/dist/assets/json-DIYVocXf.js +2 -0
- package/packages/web/dist/assets/kanban-definition-UN3LZRKU-C_9xNaa3.js +90 -0
- package/packages/web/dist/assets/katex-C5jXJg4s.js +258 -0
- package/packages/web/dist/assets/kotlin-Cw-eF_bm.js +3 -0
- package/packages/web/dist/assets/lastTab-CU1zsF0j.js +2 -0
- package/packages/web/dist/assets/layout-Boy8vhSP.js +2 -0
- package/packages/web/dist/assets/linear-BxKd91ho.js +2 -0
- package/packages/web/dist/assets/mermaid.core-C-CuhD8I.js +310 -0
- package/packages/web/dist/assets/mindmap-definition-RKZ34NQL-Bs6yfcZv.js +97 -0
- package/packages/web/dist/assets/ordinal-Cboi1Yqb.js +2 -0
- package/packages/web/dist/assets/php-CRdd4dfS.js +3 -0
- package/packages/web/dist/assets/pieDiagram-4H26LBE5-DNdqX7O5.js +31 -0
- package/packages/web/dist/assets/python-kSlZHhJb.js +2 -0
- package/packages/web/dist/assets/quadrantDiagram-W4KKPZXB-ZMJl9OdZ.js +8 -0
- package/packages/web/dist/assets/queued-LamEU0GH.js +2 -0
- package/packages/web/dist/assets/requirementDiagram-4Y6WPE33-RmxMdm2q.js +85 -0
- package/packages/web/dist/assets/ruby-7T4ktxyv.js +2 -0
- package/packages/web/dist/assets/rust-HC3MBUhY.js +2 -0
- package/packages/web/dist/assets/sankeyDiagram-5OEKKPKP-OAAhWcfK.js +41 -0
- package/packages/web/dist/assets/scss-DZLA7YXz.js +2 -0
- package/packages/web/dist/assets/sequenceDiagram-3UESZ5HK-BWF7rCyN.js +163 -0
- package/packages/web/dist/assets/sql-BC88IN-V.js +2 -0
- package/packages/web/dist/assets/stateDiagram-AJRCARHV-M3b_zqqo.js +2 -0
- package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-CI7saFDa.js +2 -0
- package/packages/web/dist/assets/swift-Dn8kKO_1.js +2 -0
- package/packages/web/dist/assets/timeline-definition-PNZ67QCA-BpOCh63N.js +121 -0
- package/packages/web/dist/assets/typescript-C2FFdlUC.js +2 -0
- package/packages/web/dist/assets/vennDiagram-CIIHVFJN-D_iPH45Q.js +35 -0
- package/packages/web/dist/assets/wardley-L42UT6IY-DVD9I2Om.js +174 -0
- package/packages/web/dist/assets/wardleyDiagram-YWT4CUSO-Bn1IKwjn.js +79 -0
- package/packages/web/dist/assets/xml-BXBhIUeX.js +2 -0
- package/packages/web/dist/assets/xychartDiagram-2RQKCTM6-DwtI0bkR.js +8 -0
- package/packages/web/dist/assets/yaml-drsDnIKT.js +2 -0
- package/packages/web/dist/favicon.ico +0 -0
- package/packages/web/dist/fonts/inter-latin.woff2 +0 -0
- package/packages/web/dist/fonts/jetbrains-mono-latin.woff2 +0 -0
- package/packages/web/dist/icons/apple-touch-icon.png +0 -0
- package/packages/web/dist/icons/favicon-16.png +0 -0
- package/packages/web/dist/icons/favicon-32.png +0 -0
- package/packages/web/dist/icons/icon-192.png +0 -0
- package/packages/web/dist/icons/icon-512.png +0 -0
- package/packages/web/dist/icons/maskable-512.png +0 -0
- package/packages/web/dist/index.html +56 -0
- package/packages/web/dist/manifest.webmanifest +32 -0
- package/packages/web/dist/sw.js +139 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* project-paths — pure path/slug/repo helpers + the project error type.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from projects.ts (issue #403): slug + repo-URL validation, the
|
|
5
|
+
* notebook-vs-repo-backed working-directory resolution, the date stamp, and the
|
|
6
|
+
* seed CLAUDE.md template. All pure (only `node:path`), no `ProjectStore` ties.
|
|
7
|
+
* The public helpers (`isValidRepoUrl`/`repoCheckoutName`/`workingDirFor`/
|
|
8
|
+
* `slugify`/`ProjectError`) are re-exported from projects.ts so existing
|
|
9
|
+
* importers keep resolving in one place.
|
|
10
|
+
*/
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
export const SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
13
|
+
/** The root workspace's key: its path relative to `projectsRoot` is empty. */
|
|
14
|
+
export const ROOT_KEY = "";
|
|
15
|
+
/** Whether a key addresses the root workspace (i.e. `projectsRoot` itself). */
|
|
16
|
+
export function isRootKey(key) {
|
|
17
|
+
return key === ROOT_KEY;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The root workspace's name in the **herdctl agent namespace**.
|
|
21
|
+
*
|
|
22
|
+
* This is the one place a sentinel is unavoidable, because that namespace
|
|
23
|
+
* genuinely cannot represent the empty key: herdctl agent names must be
|
|
24
|
+
* non-empty and must satisfy TWO disagreeing patterns — `AGENT_NAME_PATTERN`
|
|
25
|
+
* (`/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/`, used by `addAgent`) and the session store's
|
|
26
|
+
* `SAFE_IDENTIFIER_PATTERN` (`/^[a-zA-Z0-9]([a-zA-Z0-9_.-]*[a-zA-Z0-9])?$/`,
|
|
27
|
+
* which requires a trailing alphanumeric). A bare `keeper-` fails both, and the
|
|
28
|
+
* shape #516 originally proposed (`keeper-__root__`) registered cleanly and then
|
|
29
|
+
* threw PathTraversalError on the first resume.
|
|
30
|
+
*
|
|
31
|
+
* `_root` satisfies both patterns and can never collide with a project, because
|
|
32
|
+
* {@link SLUG_RE} rejects underscores — no slug can equal it.
|
|
33
|
+
*
|
|
34
|
+
* **This is a NAME, not an identity.** A workspace is identified by its relative
|
|
35
|
+
* path ({@link WorkspaceKey}); this encoding exists only at the herdctl
|
|
36
|
+
* boundary, is applied in one function, and keeps the four agent-name builders
|
|
37
|
+
* (`keeper-`/`sweeper-`/`hook-`/`trigger-`) uniform with no root branch.
|
|
38
|
+
*/
|
|
39
|
+
export const ROOT_AGENT_KEY = "_root";
|
|
40
|
+
/** Encode a workspace key for use inside a herdctl agent name. */
|
|
41
|
+
export function agentKeyFor(key) {
|
|
42
|
+
return isRootKey(key) ? ROOT_AGENT_KEY : key;
|
|
43
|
+
}
|
|
44
|
+
/** Decode a herdctl agent-name segment back to a workspace key. */
|
|
45
|
+
export function keyFromAgentKey(agentKey) {
|
|
46
|
+
return agentKey === ROOT_AGENT_KEY ? ROOT_KEY : agentKey;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Accepted repo-URL shapes for a repo-backed project (issue #187): https(s),
|
|
50
|
+
* ssh (`git@host:owner/repo`), git://, and a local `file://` or absolute path
|
|
51
|
+
* (the last two make deterministic tests possible without a network). Anything
|
|
52
|
+
* else is rejected up front so a bad value never reaches `git clone`.
|
|
53
|
+
*/
|
|
54
|
+
export const REPO_URL_RE = /^(?:https?:\/\/|git:\/\/|ssh:\/\/|file:\/\/|\/|git@[^\s]+:).+/i;
|
|
55
|
+
/** Validate a candidate repo URL (issue #187). */
|
|
56
|
+
export function isValidRepoUrl(url) {
|
|
57
|
+
const u = url.trim();
|
|
58
|
+
return u.length > 0 && u.length <= 512 && REPO_URL_RE.test(u);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Derive the checkout directory NAME for a repo-backed project from its repo URL
|
|
62
|
+
* (issue #187) — the repo's own basename, sans a trailing `.git`, sanitised to a
|
|
63
|
+
* filesystem-safe token. The keeper's cwd becomes `<projectDir>/<thisName>`, so
|
|
64
|
+
* the cwd basename reads like the repo (e.g. `paddock`). Deterministic (same URL
|
|
65
|
+
* ⇒ same name), which is why `repo` is immutable once set.
|
|
66
|
+
*/
|
|
67
|
+
export function repoCheckoutName(repo) {
|
|
68
|
+
const cleaned = repo
|
|
69
|
+
.trim()
|
|
70
|
+
.replace(/\/+$/, "")
|
|
71
|
+
.replace(/\.git$/i, "");
|
|
72
|
+
const base = cleaned.split(/[/:]/).pop() ?? "";
|
|
73
|
+
const safe = base.replace(/[^A-Za-z0-9._-]/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
|
74
|
+
return safe || "repo";
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolve a project's working directory (keeper cwd) from its metadata dir + repo
|
|
78
|
+
* URL: the nested checkout for a repo-backed project, else the metadata dir
|
|
79
|
+
* itself for a notebook project (issue #187).
|
|
80
|
+
*/
|
|
81
|
+
export function workingDirFor(dir, repo) {
|
|
82
|
+
return repo ? path.join(dir, repoCheckoutName(repo)) : dir;
|
|
83
|
+
}
|
|
84
|
+
export class ProjectError extends Error {
|
|
85
|
+
code;
|
|
86
|
+
constructor(message, code) {
|
|
87
|
+
super(message);
|
|
88
|
+
this.code = code;
|
|
89
|
+
this.name = "ProjectError";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export function slugify(name) {
|
|
93
|
+
return name
|
|
94
|
+
.toLowerCase()
|
|
95
|
+
.trim()
|
|
96
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
97
|
+
.replace(/^-+|-+$/g, "")
|
|
98
|
+
.slice(0, 64);
|
|
99
|
+
}
|
|
100
|
+
export function today() {
|
|
101
|
+
return new Date().toISOString().slice(0, 10);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Minimal seed for a per-project CLAUDE.md (issue #177): the durable-identity
|
|
105
|
+
* layer of the two-level native-context model (instance-wide + per-project
|
|
106
|
+
* CLAUDE.md, loaded natively once #176 lands). Deliberately terse — the sweeper
|
|
107
|
+
* amends it conservatively over time under "Curated notes"; everything a human
|
|
108
|
+
* writes is preserved. OVERVIEW.md holds current state, CHANGELOG.md holds
|
|
109
|
+
* history; this file holds only what the project durably IS and how we work on it.
|
|
110
|
+
*/
|
|
111
|
+
export function claudeTemplate(name, summary) {
|
|
112
|
+
return [
|
|
113
|
+
`# ${name}`,
|
|
114
|
+
"",
|
|
115
|
+
summary?.trim() || "<!-- One-line description of what this project is. -->",
|
|
116
|
+
"",
|
|
117
|
+
"<!--",
|
|
118
|
+
"Durable project identity & conventions — what this project fundamentally is,",
|
|
119
|
+
"key long-lived facts, and how we work on it. Changes rarely. Current state",
|
|
120
|
+
"lives in OVERVIEW.md; per-turn history lives in CHANGELOG.md. Content you",
|
|
121
|
+
"write here is preserved; the curator only APPENDS newly-discovered durable",
|
|
122
|
+
'facts under the "Curated notes" heading below.',
|
|
123
|
+
"-->",
|
|
124
|
+
"",
|
|
125
|
+
].join("\n");
|
|
126
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* project-types — the project layer's type surface + link normaliser.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from projects.ts (issue #403): the on-disk `project.yaml` schema
|
|
5
|
+
* ({@link ProjectYaml}), the API-facing {@link Project} DTO, the create/update
|
|
6
|
+
* input shapes, the small render/listing enums, and the pure {@link normalizeLinks}
|
|
7
|
+
* read-boundary coercion. These are declarations only (plus one pure function), so
|
|
8
|
+
* they carry no coupling to `ProjectStore`. projects.ts re-exports everything here
|
|
9
|
+
* (a barrel) so every existing `import { ... } from "./projects.js"` keeps resolving.
|
|
10
|
+
*/
|
|
11
|
+
import { type PaddockSchedule } from "./schedule-config.js";
|
|
12
|
+
import { type PaddockHook } from "./hook-config.js";
|
|
13
|
+
import { type PaddockTrigger } from "./trigger-config.js";
|
|
14
|
+
import { type RecoveryOverride } from "./recovery-config.js";
|
|
15
|
+
import { type CurationOverride } from "./curation-config.js";
|
|
16
|
+
import { type AttachmentsOverride } from "./attachments-config.js";
|
|
17
|
+
export type { PaddockSchedule, PaddockHook, PaddockTrigger, RecoveryOverride, AttachmentsOverride, };
|
|
18
|
+
/**
|
|
19
|
+
* project.yaml status enum (matches the documented standard). The runtime list is
|
|
20
|
+
* the single source of truth, with {@link ProjectStatus} derived from it, so a
|
|
21
|
+
* caller that must VALIDATE a status string (the `create_project` MCP tool, issue
|
|
22
|
+
* #467) and a caller that merely types one can't drift apart.
|
|
23
|
+
*/
|
|
24
|
+
export declare const PROJECT_STATUSES: readonly ["idea", "active", "paused", "blocked", "done", "abandoned"];
|
|
25
|
+
export type ProjectStatus = (typeof PROJECT_STATUSES)[number];
|
|
26
|
+
export type ProjectVisibility = "public" | "private";
|
|
27
|
+
/** Render-kind hint for a project file (drives the UI renderer choice). */
|
|
28
|
+
export type FileKind = "markdown" | "html" | "text" | "image";
|
|
29
|
+
/**
|
|
30
|
+
* One entry in a project directory listing (issue #259): a name plus whether
|
|
31
|
+
* it's a file or a subdirectory, so the Files tab can distinguish the two and
|
|
32
|
+
* let the user descend into folders. Listed one level at a time.
|
|
33
|
+
*/
|
|
34
|
+
export interface FileEntry {
|
|
35
|
+
name: string;
|
|
36
|
+
kind: "file" | "dir";
|
|
37
|
+
}
|
|
38
|
+
export interface ProjectLink {
|
|
39
|
+
label: string;
|
|
40
|
+
url: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Coerce a raw parsed `links` value into well-formed {label,url} objects.
|
|
44
|
+
*
|
|
45
|
+
* A legacy / hand-edited `project.yaml` may declare `links` as a bare YAML
|
|
46
|
+
* string list (the natural shorthand — `- https://example.com`) rather than the
|
|
47
|
+
* {label,url} object form. Passing those through untouched yields a `string[]`
|
|
48
|
+
* DTO, which crashes the Settings pane (its `cleanedLinks` memo calls
|
|
49
|
+
* `l.url.trim()` / `l.label.trim()` on what is actually a string → TypeError
|
|
50
|
+
* during render). So normalize here at the read boundary: a bare string becomes
|
|
51
|
+
* `{label:"", url:<string>}`, an object entry is trimmed and kept, and any entry
|
|
52
|
+
* without a usable url (or of an unexpected type) is dropped. Because this runs
|
|
53
|
+
* in `normalize`, the next save round-trips the file into the object form — the
|
|
54
|
+
* project self-heals.
|
|
55
|
+
*/
|
|
56
|
+
export declare function normalizeLinks(raw: unknown): ProjectLink[];
|
|
57
|
+
/**
|
|
58
|
+
* The on-disk project.yaml shape.
|
|
59
|
+
*
|
|
60
|
+
* Note on dates: `started` is the creation date and `updated` is last-touched.
|
|
61
|
+
*/
|
|
62
|
+
export interface ProjectYaml {
|
|
63
|
+
name: string;
|
|
64
|
+
slug: string;
|
|
65
|
+
status: ProjectStatus;
|
|
66
|
+
domain: string[];
|
|
67
|
+
visibility: ProjectVisibility;
|
|
68
|
+
started: string;
|
|
69
|
+
updated: string;
|
|
70
|
+
summary: string;
|
|
71
|
+
/**
|
|
72
|
+
* The project's "area" — its single, exclusive home (Homelab / House / Side
|
|
73
|
+
* Projects). One per project (unlike the many cross-cutting `domain` tags);
|
|
74
|
+
* drives the sectioned landing page. Optional on disk: an absent/empty value
|
|
75
|
+
* means "Unsorted" and is rendered in its own section. Free-form so new areas
|
|
76
|
+
* can be added without a schema change; the UI knows the canonical set.
|
|
77
|
+
*/
|
|
78
|
+
group?: string;
|
|
79
|
+
links?: ProjectLink[];
|
|
80
|
+
/**
|
|
81
|
+
* Files (relative names within the project dir) the user has pinned as
|
|
82
|
+
* sibling tabs in the UI. Order-preserving, deduped. Default [].
|
|
83
|
+
*/
|
|
84
|
+
pinned?: string[];
|
|
85
|
+
/**
|
|
86
|
+
* The Claude model the project's keeper agent runs on. Optional on disk: an
|
|
87
|
+
* absent value resolves to DEFAULT_MODEL in the DTO. (CONTRACT-v3 §4.)
|
|
88
|
+
*/
|
|
89
|
+
model?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Per-project ALLOW-LIST of offered models (issue #457 Step 2), by catalog id.
|
|
92
|
+
* Optional on disk: absent ⇒ this project offers the instance list
|
|
93
|
+
* (`cfg.models` resolved, else the full catalog). When set it NARROWS the picker
|
|
94
|
+
* to this subset — every id must be a known catalog model AND within the instance
|
|
95
|
+
* allow-list (enforced by PATCH validation); it never widens beyond the instance
|
|
96
|
+
* list. The picker's default `model` is constrained to this subset in the UI. Same
|
|
97
|
+
* round-trip discipline as `model` — carried only when present so files without it
|
|
98
|
+
* are unchanged, resolved against the instance list in the web (never baked
|
|
99
|
+
* concrete on disk).
|
|
100
|
+
*/
|
|
101
|
+
models?: string[];
|
|
102
|
+
/**
|
|
103
|
+
* Per-project keeper-agent overrides (issue #12). All optional on disk — an
|
|
104
|
+
* absent value inherits the fleet default and resolves to the concrete default
|
|
105
|
+
* in the DTO. `permissionMode`/`maxTurns`/`docker` (Docker isolation on/off).
|
|
106
|
+
*/
|
|
107
|
+
permissionMode?: string;
|
|
108
|
+
maxTurns?: number;
|
|
109
|
+
docker?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* How the keeper's chat turns are driven — `batch` (one-shot trigger) or
|
|
112
|
+
* `session` (persistent managed openChatSession, enabling cross-turn autonomy;
|
|
113
|
+
* Paddock#111). Optional on disk: absent inherits the global default
|
|
114
|
+
* (`PADDOCK_DRIVE_MODE`, else DEFAULT_DRIVE_MODE).
|
|
115
|
+
*/
|
|
116
|
+
driveMode?: string;
|
|
117
|
+
/**
|
|
118
|
+
* How deep a spawn tree rooted at this project's chats may grow before spawned
|
|
119
|
+
* children stop receiving the self-management MCP (issue #262 / DD-3). A spawned
|
|
120
|
+
* turn at depth `d` gets the self-MCP (incl. `send_message`, so a child can
|
|
121
|
+
* report back) iff `d <= maxSpawnDepth`. Optional on disk: absent inherits the
|
|
122
|
+
* instance default (`PADDOCK_MAX_SPAWN_DEPTH`, else DEFAULT_MAX_SPAWN_DEPTH = 1),
|
|
123
|
+
* resolved at dispatch — the same inherit/override discipline as `driveMode`.
|
|
124
|
+
*/
|
|
125
|
+
maxSpawnDepth?: number;
|
|
126
|
+
/**
|
|
127
|
+
* Per-project override for the hook-management MCP gate (Epic G / G5, GG-4) —
|
|
128
|
+
* whether this project's turns get the `mcp__paddock_manage__{list,set,remove}_hook`
|
|
129
|
+
* tools. Optional on disk: absent inherits the instance default
|
|
130
|
+
* (`PADDOCK_HOOKS_MCP`, else OFF), resolved at dispatch via
|
|
131
|
+
* {@link import("./hook-config.js").resolveHooksMcpEnabled} — the same
|
|
132
|
+
* inherit/override discipline as `driveMode`/`maxSpawnDepth`. `true` opts this
|
|
133
|
+
* project agent in; `false` opts it out even when the instance default is on.
|
|
134
|
+
*/
|
|
135
|
+
hooksMcpEnabled?: boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Per-project keeper-chat recovery override (issue #301). Every field optional;
|
|
138
|
+
* an absent field inherits the instance default (`cfg.recovery`, itself
|
|
139
|
+
* `PADDOCK_RECOVERY_*` env / YAML), resolved at dispatch via
|
|
140
|
+
* {@link import("./recovery-config.js").resolveRecoveryConfig} — the same
|
|
141
|
+
* inherit/override discipline as `driveMode`/`maxSpawnDepth`. Absent ⇒ this
|
|
142
|
+
* project inherits every recovery default. Persisted (and re-read) through
|
|
143
|
+
* {@link sanitizeRecoveryOverride} so a malformed hand-edit degrades cleanly.
|
|
144
|
+
*/
|
|
145
|
+
recovery?: RecoveryOverride;
|
|
146
|
+
/**
|
|
147
|
+
* Per-project inbound-attachment override (issue #328). Every field optional;
|
|
148
|
+
* an absent field inherits the instance default (`cfg.attachments`, itself
|
|
149
|
+
* `PADDOCK_ATTACHMENTS_*` env / YAML), resolved at request time via
|
|
150
|
+
* {@link import("./attachments-config.js").resolveAttachmentsConfig} — the same
|
|
151
|
+
* inherit/override discipline as `recovery`. Absent ⇒ this project inherits
|
|
152
|
+
* every attachment default. Persisted (and re-read) through
|
|
153
|
+
* {@link sanitizeAttachmentsOverride} so a malformed hand-edit degrades cleanly.
|
|
154
|
+
* (Surfacing this in the Settings tab is deferred to Phase 2; the resolve/
|
|
155
|
+
* override plumbing exists now.)
|
|
156
|
+
*/
|
|
157
|
+
attachments?: AttachmentsOverride;
|
|
158
|
+
/**
|
|
159
|
+
* Per-project sweeper-curation budget override (issue #384). Every field
|
|
160
|
+
* optional; an absent field inherits the instance default (`cfg.curation`,
|
|
161
|
+
* itself `PADDOCK_CURATION_*` env / YAML), resolved at sweep time via
|
|
162
|
+
* {@link import("./curation-config.js").resolveCurationConfig} — the same
|
|
163
|
+
* inherit/override discipline as `recovery`/`attachments`. Absent ⇒ this
|
|
164
|
+
* project inherits every curation default. Persisted (and re-read) through
|
|
165
|
+
* {@link sanitizeCurationOverride} so a malformed hand-edit degrades cleanly.
|
|
166
|
+
*/
|
|
167
|
+
curation?: CurationOverride;
|
|
168
|
+
/**
|
|
169
|
+
* External git repo URL that backs this project (issue #187). When present the
|
|
170
|
+
* project is REPO-BACKED: Paddock clones the repo into a nested `.gitignore`d
|
|
171
|
+
* checkout under the project dir and the keeper's working directory is that
|
|
172
|
+
* checkout (so the repo's OWN `CLAUDE.md`, git history, branches and PR flow
|
|
173
|
+
* work natively). Absent ⇒ a NOTEBOOK project (the classic type: cwd = the
|
|
174
|
+
* project dir in the instance data repo). Set at creation; immutable thereafter
|
|
175
|
+
* (like `slug`/`started`).
|
|
176
|
+
*/
|
|
177
|
+
repo?: string;
|
|
178
|
+
/**
|
|
179
|
+
* Scheduled chat sessions for this project (issue #265 / DD-2), keyed by a
|
|
180
|
+
* stable schedule name. Each value is herdctl's `ScheduleSchema` shape (plus the
|
|
181
|
+
* Paddock-only `promptFile`), forwarded unmolested into the keeper agent's
|
|
182
|
+
* `schedules` block so herdctl arms the cron/interval directly. Absent/empty ⇒
|
|
183
|
+
* the project has no schedules (unchanged behaviour).
|
|
184
|
+
*/
|
|
185
|
+
schedules?: Record<string, PaddockSchedule>;
|
|
186
|
+
/**
|
|
187
|
+
* Event hooks for this project (Epic G / G1), keyed by a stable hook name. Each
|
|
188
|
+
* value is a {@link PaddockHook} — a lifecycle event + a capability set + a prompt
|
|
189
|
+
* (inline or `.paddock/hooks/*.md`) + `enabled`. Unlike schedules, a hook is NOT
|
|
190
|
+
* forwarded into the keeper agent's config: each hook is registered as its OWN
|
|
191
|
+
* herdctl agent `hook-<slug>-<name>` whose tool config IS its capability (GG-1).
|
|
192
|
+
* Absent/empty ⇒ the project has no hooks (unchanged behaviour). New hooks default
|
|
193
|
+
* `enabled: false` (GG-3).
|
|
194
|
+
*/
|
|
195
|
+
hooks?: Record<string, PaddockHook>;
|
|
196
|
+
/**
|
|
197
|
+
* Unified triggers for this project (Epic T "Unify Triggers" / T1), keyed by a
|
|
198
|
+
* stable trigger name. Each value is a {@link PaddockTrigger} — a discriminated
|
|
199
|
+
* `trigger` (schedule / event / webhook) + a shared `run` (prompt, session, tools,
|
|
200
|
+
* …) + `enabled`. The declarative successor collapsing the separate `schedules` +
|
|
201
|
+
* `hooks` blocks (which T3/T5 retire): a SCHEDULE trigger is forwarded into the
|
|
202
|
+
* keeper agent's `schedules` block; an EVENT trigger is registered as its own
|
|
203
|
+
* `trigger-<slug>-<name>` agent (like a hook); a WEBHOOK trigger is shape-reserved.
|
|
204
|
+
* Absent/empty ⇒ the project has no triggers. New triggers default `enabled: false`.
|
|
205
|
+
*/
|
|
206
|
+
triggers?: Record<string, PaddockTrigger>;
|
|
207
|
+
}
|
|
208
|
+
/** API-facing project DTO (adds derived fields). */
|
|
209
|
+
export interface Project extends ProjectYaml {
|
|
210
|
+
/** The project's area — ALWAYS concrete in the DTO (`yaml.group ?? ""`). An
|
|
211
|
+
* empty string means "Unsorted". */
|
|
212
|
+
group: string;
|
|
213
|
+
/**
|
|
214
|
+
* Absolute path to the project's METADATA directory — the per-slug dir in the
|
|
215
|
+
* instance data repo that holds `project.yaml`, `OVERVIEW.md`, `CHANGELOG.md`,
|
|
216
|
+
* `.chats/`, and (for a notebook project) `CLAUDE.md`. For a NOTEBOOK project
|
|
217
|
+
* this is also the keeper's cwd; for a REPO-BACKED project the cwd is
|
|
218
|
+
* {@link workingDir} (the nested checkout) instead — see #187.
|
|
219
|
+
*/
|
|
220
|
+
dir: string;
|
|
221
|
+
/**
|
|
222
|
+
* The keeper agent's working directory (cwd). For a notebook project this
|
|
223
|
+
* equals {@link dir}; for a repo-backed project it's the nested checkout
|
|
224
|
+
* (`<dir>/<repo-name>`), so the repo's own `CLAUDE.md` + git tooling apply
|
|
225
|
+
* (issue #187). Always concrete in the DTO.
|
|
226
|
+
*/
|
|
227
|
+
workingDir: string;
|
|
228
|
+
/** Whether this project is backed by an external git repo (issue #187). */
|
|
229
|
+
repoBacked: boolean;
|
|
230
|
+
/** Whether OVERVIEW.md exists in the project dir (sweep-curated context). */
|
|
231
|
+
hasOverview: boolean;
|
|
232
|
+
/** Always present in the DTO (defaults to []). */
|
|
233
|
+
pinned: string[];
|
|
234
|
+
/**
|
|
235
|
+
* The keeper agent's model — ALWAYS concrete in the DTO (`yaml.model ??
|
|
236
|
+
* DEFAULT_MODEL`), even though it's optional on disk.
|
|
237
|
+
*/
|
|
238
|
+
model: string;
|
|
239
|
+
/** Keeper permission mode — ALWAYS concrete in the DTO (issue #12). */
|
|
240
|
+
permissionMode: string;
|
|
241
|
+
/** Keeper max_turns — ALWAYS concrete in the DTO (issue #12). */
|
|
242
|
+
maxTurns: number;
|
|
243
|
+
/** Whether the keeper runs in a Docker sandbox — ALWAYS concrete (issue #12). */
|
|
244
|
+
docker: boolean;
|
|
245
|
+
}
|
|
246
|
+
export interface CreateProjectInput {
|
|
247
|
+
name: string;
|
|
248
|
+
slug?: string;
|
|
249
|
+
status?: ProjectStatus;
|
|
250
|
+
domain?: string[];
|
|
251
|
+
group?: string;
|
|
252
|
+
visibility?: ProjectVisibility;
|
|
253
|
+
summary?: string;
|
|
254
|
+
links?: ProjectLink[];
|
|
255
|
+
/**
|
|
256
|
+
* External git repo URL to back this project (issue #187). When set, the
|
|
257
|
+
* project is created as REPO-BACKED: the repo is cloned into a nested
|
|
258
|
+
* `.gitignore`d checkout and the keeper's cwd becomes that checkout. Absent ⇒
|
|
259
|
+
* a notebook project (the classic type).
|
|
260
|
+
*/
|
|
261
|
+
repo?: string;
|
|
262
|
+
}
|
|
263
|
+
/** Partial metadata update (slug + started are immutable). */
|
|
264
|
+
export type UpdateProjectInput = Partial<Pick<ProjectYaml, "name" | "status" | "domain" | "group" | "visibility" | "summary" | "links" | "model" | "permissionMode" | "maxTurns" | "docker">> & {
|
|
265
|
+
/**
|
|
266
|
+
* Per-project offered-models allow-list (issue #457 Step 2). A non-empty string
|
|
267
|
+
* array sets the per-project override (each id validated known + within the
|
|
268
|
+
* instance allow-list); an empty array or `null` CLEARS it so the project offers
|
|
269
|
+
* the instance list again; `undefined`/absent leaves the current value untouched.
|
|
270
|
+
* Same tri-state discipline as `driveMode`.
|
|
271
|
+
*/
|
|
272
|
+
models?: string[] | null;
|
|
273
|
+
/**
|
|
274
|
+
* Keeper drive mode (Paddock#111). A string sets a per-project override;
|
|
275
|
+
* `null` CLEARS the override so the project inherits the box-wide global
|
|
276
|
+
* default again (issue #122's reset-to-inherit); `undefined`/absent leaves the
|
|
277
|
+
* current value untouched.
|
|
278
|
+
*/
|
|
279
|
+
driveMode?: string | null;
|
|
280
|
+
/**
|
|
281
|
+
* Max spawn depth (issue #262). A number sets a per-project override; `null`
|
|
282
|
+
* CLEARS it so the project inherits the instance default again; `undefined`/
|
|
283
|
+
* absent leaves the current value untouched. Same tri-state as `driveMode`.
|
|
284
|
+
*/
|
|
285
|
+
maxSpawnDepth?: number | null;
|
|
286
|
+
/**
|
|
287
|
+
* Hook-management MCP override (Epic G / G5). A boolean sets a per-project
|
|
288
|
+
* override; `null` CLEARS it so the project inherits the instance default again;
|
|
289
|
+
* `undefined`/absent leaves the current value untouched. Same tri-state as
|
|
290
|
+
* `driveMode`/`maxSpawnDepth`.
|
|
291
|
+
*/
|
|
292
|
+
hooksMcpEnabled?: boolean | null;
|
|
293
|
+
/**
|
|
294
|
+
* Keeper-chat recovery override (issue #301). A {@link RecoveryOverride} object
|
|
295
|
+
* REPLACES the per-project override; `null` CLEARS it so the project inherits
|
|
296
|
+
* every instance default again; `undefined`/absent leaves the current value
|
|
297
|
+
* untouched. Same tri-state as `driveMode`/`maxSpawnDepth` (the whole override
|
|
298
|
+
* object is the unit — sanitised on write).
|
|
299
|
+
*/
|
|
300
|
+
recovery?: RecoveryOverride | null;
|
|
301
|
+
/**
|
|
302
|
+
* Inbound-attachment override (issue #328). An {@link AttachmentsOverride}
|
|
303
|
+
* object REPLACES the per-project override; `null` CLEARS it so the project
|
|
304
|
+
* inherits every instance default again; `undefined`/absent leaves it
|
|
305
|
+
* untouched. Same tri-state as `recovery` (the whole override object is the
|
|
306
|
+
* unit — sanitised on write). Wired now for the Phase 2 Settings surface.
|
|
307
|
+
*/
|
|
308
|
+
attachments?: AttachmentsOverride | null;
|
|
309
|
+
/**
|
|
310
|
+
* Sweeper-curation budget override (issue #384). A {@link CurationOverride}
|
|
311
|
+
* object REPLACES the per-project override; `null` CLEARS it so the project
|
|
312
|
+
* inherits every instance default again; `undefined`/absent leaves it
|
|
313
|
+
* untouched. Same tri-state as `recovery`/`attachments`, sanitised on write.
|
|
314
|
+
*/
|
|
315
|
+
curation?: CurationOverride | null;
|
|
316
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* project.yaml status enum (matches the documented standard). The runtime list is
|
|
3
|
+
* the single source of truth, with {@link ProjectStatus} derived from it, so a
|
|
4
|
+
* caller that must VALIDATE a status string (the `create_project` MCP tool, issue
|
|
5
|
+
* #467) and a caller that merely types one can't drift apart.
|
|
6
|
+
*/
|
|
7
|
+
export const PROJECT_STATUSES = [
|
|
8
|
+
"idea",
|
|
9
|
+
"active",
|
|
10
|
+
"paused",
|
|
11
|
+
"blocked",
|
|
12
|
+
"done",
|
|
13
|
+
"abandoned",
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Coerce a raw parsed `links` value into well-formed {label,url} objects.
|
|
17
|
+
*
|
|
18
|
+
* A legacy / hand-edited `project.yaml` may declare `links` as a bare YAML
|
|
19
|
+
* string list (the natural shorthand — `- https://example.com`) rather than the
|
|
20
|
+
* {label,url} object form. Passing those through untouched yields a `string[]`
|
|
21
|
+
* DTO, which crashes the Settings pane (its `cleanedLinks` memo calls
|
|
22
|
+
* `l.url.trim()` / `l.label.trim()` on what is actually a string → TypeError
|
|
23
|
+
* during render). So normalize here at the read boundary: a bare string becomes
|
|
24
|
+
* `{label:"", url:<string>}`, an object entry is trimmed and kept, and any entry
|
|
25
|
+
* without a usable url (or of an unexpected type) is dropped. Because this runs
|
|
26
|
+
* in `normalize`, the next save round-trips the file into the object form — the
|
|
27
|
+
* project self-heals.
|
|
28
|
+
*/
|
|
29
|
+
export function normalizeLinks(raw) {
|
|
30
|
+
if (!Array.isArray(raw))
|
|
31
|
+
return [];
|
|
32
|
+
const out = [];
|
|
33
|
+
for (const item of raw) {
|
|
34
|
+
if (typeof item === "string") {
|
|
35
|
+
const url = item.trim();
|
|
36
|
+
if (url)
|
|
37
|
+
out.push({ label: "", url });
|
|
38
|
+
}
|
|
39
|
+
else if (item && typeof item === "object") {
|
|
40
|
+
const rec = item;
|
|
41
|
+
const url = typeof rec.url === "string" ? rec.url.trim() : "";
|
|
42
|
+
const label = typeof rec.label === "string" ? rec.label.trim() : "";
|
|
43
|
+
if (url)
|
|
44
|
+
out.push({ label, url });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|