@stravica-ai/digital-operator-pa 0.1.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.
Files changed (372) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -0
  3. package/bin/desk +9 -0
  4. package/bin/distill +2 -0
  5. package/bin/estate-sync +2 -0
  6. package/bin/launch +2 -0
  7. package/bin/memory-sync +2 -0
  8. package/bin/migrate-inspect +2 -0
  9. package/bin/pa +2 -0
  10. package/bin/pa-coordinator-hook +2 -0
  11. package/bin/pa-init +2 -0
  12. package/bin/research +2 -0
  13. package/bin/scheduler +2 -0
  14. package/bin/secrets +2 -0
  15. package/bin/slice +2 -0
  16. package/bin/smoke +9 -0
  17. package/bin/sync +2 -0
  18. package/dist/engineering-pa-cli/README.md +205 -0
  19. package/dist/engineering-pa-cli/bin/estate-sync +2 -0
  20. package/dist/engineering-pa-cli/bin/launch +2 -0
  21. package/dist/engineering-pa-cli/bin/pa +2 -0
  22. package/dist/engineering-pa-cli/bin/pa-coordinator-hook +12 -0
  23. package/dist/engineering-pa-cli/bin/research +2 -0
  24. package/dist/engineering-pa-cli/bin/scheduler +2 -0
  25. package/dist/engineering-pa-cli/bin/secrets +2 -0
  26. package/dist/engineering-pa-cli/bin/smoke +9 -0
  27. package/dist/engineering-pa-cli/bin/sync +2 -0
  28. package/dist/engineering-pa-cli/config/coordinator-allowlist.json +118 -0
  29. package/dist/engineering-pa-cli/docs/coordinator-hook.md +211 -0
  30. package/dist/engineering-pa-cli/docs/estate-sync.md +412 -0
  31. package/dist/engineering-pa-cli/docs/gsc.md +175 -0
  32. package/dist/engineering-pa-cli/docs/lint-linkedin.md +139 -0
  33. package/dist/engineering-pa-cli/docs/secrets.md +221 -0
  34. package/dist/engineering-pa-cli/docs/smoke.md +113 -0
  35. package/dist/engineering-pa-cli/docs/staging-alias.md +114 -0
  36. package/dist/engineering-pa-cli/index.mjs +17 -0
  37. package/dist/engineering-pa-cli/package.json +37 -0
  38. package/dist/engineering-pa-cli/src/autonomy/cli.mjs +329 -0
  39. package/dist/engineering-pa-cli/src/autonomy/envelope.mjs +301 -0
  40. package/dist/engineering-pa-cli/src/coordinator-hook/cli.mjs +140 -0
  41. package/dist/engineering-pa-cli/src/coordinator-hook/config.mjs +134 -0
  42. package/dist/engineering-pa-cli/src/coordinator-hook/message.mjs +47 -0
  43. package/dist/engineering-pa-cli/src/coordinator-hook/policy.mjs +211 -0
  44. package/dist/engineering-pa-cli/src/coordinator-hook/repo-root.mjs +44 -0
  45. package/dist/engineering-pa-cli/src/coordinator-hook/self.mjs +30 -0
  46. package/dist/engineering-pa-cli/src/environment/cli.mjs +287 -0
  47. package/dist/engineering-pa-cli/src/environment/probes.mjs +302 -0
  48. package/dist/engineering-pa-cli/src/gsc/auth.mjs +148 -0
  49. package/dist/engineering-pa-cli/src/gsc/cli.mjs +93 -0
  50. package/dist/engineering-pa-cli/src/gsc/cloudflare.mjs +318 -0
  51. package/dist/engineering-pa-cli/src/gsc/commands/add-property.mjs +70 -0
  52. package/dist/engineering-pa-cli/src/gsc/commands/list-properties.mjs +33 -0
  53. package/dist/engineering-pa-cli/src/gsc/commands/pull-analytics.mjs +130 -0
  54. package/dist/engineering-pa-cli/src/gsc/commands/submit-sitemap.mjs +41 -0
  55. package/dist/engineering-pa-cli/src/gsc/commands/verify-via-cf-dns.mjs +189 -0
  56. package/dist/engineering-pa-cli/src/knowledge/call-log.mjs +59 -0
  57. package/dist/engineering-pa-cli/src/knowledge/cli.mjs +242 -0
  58. package/dist/engineering-pa-cli/src/knowledge/eval.mjs +446 -0
  59. package/dist/engineering-pa-cli/src/knowledge/formatters.mjs +331 -0
  60. package/dist/engineering-pa-cli/src/knowledge/graph.mjs +710 -0
  61. package/dist/engineering-pa-cli/src/launch/README.md +214 -0
  62. package/dist/engineering-pa-cli/src/launch/cli.mjs +51 -0
  63. package/dist/engineering-pa-cli/src/launch/commands/launch.mjs +106 -0
  64. package/dist/engineering-pa-cli/src/launch/identity.mjs +43 -0
  65. package/dist/engineering-pa-cli/src/launch/planner.mjs +44 -0
  66. package/dist/engineering-pa-cli/src/launch/repo-root.mjs +53 -0
  67. package/dist/engineering-pa-cli/src/launch/runner.mjs +147 -0
  68. package/dist/engineering-pa-cli/src/launch/task-loader.mjs +85 -0
  69. package/dist/engineering-pa-cli/src/lint/cli.mjs +61 -0
  70. package/dist/engineering-pa-cli/src/lint/commands/linkedin.mjs +133 -0
  71. package/dist/engineering-pa-cli/src/lint/detectors.mjs +90 -0
  72. package/dist/engineering-pa-cli/src/lint/linter.mjs +69 -0
  73. package/dist/engineering-pa-cli/src/lint/reporter.mjs +32 -0
  74. package/dist/engineering-pa-cli/src/lint/rules.mjs +132 -0
  75. package/dist/engineering-pa-cli/src/pa/cli.mjs +369 -0
  76. package/dist/engineering-pa-cli/src/pa/doc-groups.mjs +164 -0
  77. package/dist/engineering-pa-cli/src/pa/io.mjs +80 -0
  78. package/dist/engineering-pa-cli/src/pa/project-cli.mjs +385 -0
  79. package/dist/engineering-pa-cli/src/pa/projects.mjs +352 -0
  80. package/dist/engineering-pa-cli/src/pa/repo-root.mjs +64 -0
  81. package/dist/engineering-pa-cli/src/pa/session.mjs +254 -0
  82. package/dist/engineering-pa-cli/src/pa/work-cli.mjs +164 -0
  83. package/dist/engineering-pa-cli/src/pa/work.mjs +196 -0
  84. package/dist/engineering-pa-cli/src/pa/workstreams.mjs +201 -0
  85. package/dist/engineering-pa-cli/src/research/README.md +360 -0
  86. package/dist/engineering-pa-cli/src/research/briefs/extractor.md +103 -0
  87. package/dist/engineering-pa-cli/src/research/briefs/searcher.md +113 -0
  88. package/dist/engineering-pa-cli/src/research/briefs/synthesizer.md +124 -0
  89. package/dist/engineering-pa-cli/src/research/briefs/verifier.md +86 -0
  90. package/dist/engineering-pa-cli/src/research/cli.mjs +141 -0
  91. package/dist/engineering-pa-cli/src/research/commands/gates.mjs +176 -0
  92. package/dist/engineering-pa-cli/src/research/commands/list.mjs +56 -0
  93. package/dist/engineering-pa-cli/src/research/commands/mark.mjs +97 -0
  94. package/dist/engineering-pa-cli/src/research/commands/paths.mjs +69 -0
  95. package/dist/engineering-pa-cli/src/research/commands/prune.mjs +86 -0
  96. package/dist/engineering-pa-cli/src/research/commands/run.mjs +94 -0
  97. package/dist/engineering-pa-cli/src/research/commands/show.mjs +43 -0
  98. package/dist/engineering-pa-cli/src/research/docs/operator-present-flow.md +120 -0
  99. package/dist/engineering-pa-cli/src/research/format.mjs +59 -0
  100. package/dist/engineering-pa-cli/src/research/promote.mjs +260 -0
  101. package/dist/engineering-pa-cli/src/research/schema/finding.schema.json +55 -0
  102. package/dist/engineering-pa-cli/src/research/schema/gates.schema.json +34 -0
  103. package/dist/engineering-pa-cli/src/research/schema/manifest.schema.json +59 -0
  104. package/dist/engineering-pa-cli/src/research/schema/plan.schema.json +41 -0
  105. package/dist/engineering-pa-cli/src/research/schema/sources.schema.json +31 -0
  106. package/dist/engineering-pa-cli/src/research/store/findings.mjs +61 -0
  107. package/dist/engineering-pa-cli/src/research/store/gates.mjs +73 -0
  108. package/dist/engineering-pa-cli/src/research/store/index.mjs +312 -0
  109. package/dist/engineering-pa-cli/src/research/store/manifest.mjs +56 -0
  110. package/dist/engineering-pa-cli/src/research/store/plan.mjs +59 -0
  111. package/dist/engineering-pa-cli/src/research/store/sources.mjs +44 -0
  112. package/dist/engineering-pa-cli/src/research/store/validate.mjs +92 -0
  113. package/dist/engineering-pa-cli/src/research/workflows/research-normal.js +573 -0
  114. package/dist/engineering-pa-cli/src/scheduler/cli.mjs +197 -0
  115. package/dist/engineering-pa-cli/src/secrets/cli.mjs +254 -0
  116. package/dist/engineering-pa-cli/src/secrets/commands/backup-template.mjs +67 -0
  117. package/dist/engineering-pa-cli/src/secrets/commands/backup.mjs +85 -0
  118. package/dist/engineering-pa-cli/src/secrets/commands/delete.mjs +58 -0
  119. package/dist/engineering-pa-cli/src/secrets/commands/find.mjs +36 -0
  120. package/dist/engineering-pa-cli/src/secrets/commands/get.mjs +49 -0
  121. package/dist/engineering-pa-cli/src/secrets/commands/init.mjs +69 -0
  122. package/dist/engineering-pa-cli/src/secrets/commands/list.mjs +37 -0
  123. package/dist/engineering-pa-cli/src/secrets/commands/rotate.mjs +46 -0
  124. package/dist/engineering-pa-cli/src/secrets/commands/set.mjs +123 -0
  125. package/dist/engineering-pa-cli/src/secrets/commands/status.mjs +146 -0
  126. package/dist/engineering-pa-cli/src/secrets/commands/vaults.mjs +57 -0
  127. package/dist/engineering-pa-cli/src/secrets/commands/verify.mjs +143 -0
  128. package/dist/engineering-pa-cli/src/secrets/vault.mjs +766 -0
  129. package/dist/engineering-pa-cli/src/smoke/checks.mjs +199 -0
  130. package/dist/engineering-pa-cli/src/smoke/cli.mjs +108 -0
  131. package/dist/engineering-pa-cli/src/smoke/commands/run.mjs +237 -0
  132. package/dist/engineering-pa-cli/src/smoke/runner.mjs +191 -0
  133. package/dist/engineering-pa-cli/src/staging-alias/cli.mjs +91 -0
  134. package/dist/engineering-pa-cli/src/staging-alias/cloudflare.mjs +380 -0
  135. package/dist/engineering-pa-cli/src/staging-alias/commands/list.mjs +36 -0
  136. package/dist/engineering-pa-cli/src/staging-alias/commands/set.mjs +188 -0
  137. package/dist/engineering-pa-cli/src/staging-alias/commands/status.mjs +113 -0
  138. package/dist/engineering-pa-cli/src/staging-alias/commands/unset.mjs +95 -0
  139. package/dist/engineering-pa-cli/src/staging-alias/sites.mjs +105 -0
  140. package/dist/engineering-pa-cli/src/sync/cli.mjs +151 -0
  141. package/dist/engineering-pa-cli/src/sync/git.mjs +149 -0
  142. package/dist/engineering-pa-cli/src/sync/lock.mjs +30 -0
  143. package/dist/engineering-pa-cli/src/sync/regenerated-artifacts.mjs +114 -0
  144. package/dist/engineering-pa-cli/src/sync/registry-scope.mjs +260 -0
  145. package/dist/engineering-pa-cli/src/sync/run.mjs +531 -0
  146. package/dist/engineering-pa-cli/src/sync/runner.mjs +62 -0
  147. package/dist/engineering-pa-cli/src/sync/runs-digest.mjs +138 -0
  148. package/dist/engineering-pa-core/README.md +60 -0
  149. package/dist/engineering-pa-core/bin/desk +9 -0
  150. package/dist/engineering-pa-core/bin/distill +2 -0
  151. package/dist/engineering-pa-core/bin/memory-sync +2 -0
  152. package/dist/engineering-pa-core/bin/slice +2 -0
  153. package/dist/engineering-pa-core/docs/autonomy-envelope-tracker.md +111 -0
  154. package/dist/engineering-pa-core/docs/blueprints/README.md +45 -0
  155. package/dist/engineering-pa-core/docs/blueprints/docker-services/blueprint.md +168 -0
  156. package/dist/engineering-pa-core/docs/blueprints/git-identity/blueprint.md +116 -0
  157. package/dist/engineering-pa-core/docs/blueprints/ideas/custom-workspace-views.md +9 -0
  158. package/dist/engineering-pa-core/docs/blueprints/ideas/knowledge-domain-patterns.md +15 -0
  159. package/dist/engineering-pa-core/docs/blueprints/ideas/office-integration.md +11 -0
  160. package/dist/engineering-pa-core/docs/blueprints/ideas/team-scaling.md +9 -0
  161. package/dist/engineering-pa-core/docs/blueprints/ideas/work-personal-separation.md +9 -0
  162. package/dist/engineering-pa-core/docs/blueprints/scheduled-distillation/blueprint.md +129 -0
  163. package/dist/engineering-pa-core/docs/blueprints/tool-integration/blueprint.md +110 -0
  164. package/dist/engineering-pa-core/docs/commands/init.md +159 -0
  165. package/dist/engineering-pa-core/docs/commands/schedule.md +98 -0
  166. package/dist/engineering-pa-core/docs/commands/upgrade.md +67 -0
  167. package/dist/engineering-pa-core/docs/entity-location-map.md +93 -0
  168. package/dist/engineering-pa-core/docs/knowledge-scaffold.md +121 -0
  169. package/dist/engineering-pa-core/docs/lock.md +155 -0
  170. package/dist/engineering-pa-core/docs/memory-sync.md +28 -0
  171. package/dist/engineering-pa-core/docs/pa-operating-model.md +457 -0
  172. package/dist/engineering-pa-core/docs/scheduling.md +35 -0
  173. package/dist/engineering-pa-core/docs/standards/README.md +24 -0
  174. package/dist/engineering-pa-core/docs/standards/briefing-contract.md +161 -0
  175. package/dist/engineering-pa-core/docs/standards/tooling.md +240 -0
  176. package/dist/engineering-pa-core/docs/tools/distill.md +230 -0
  177. package/dist/engineering-pa-core/docs/tools/memory-sync.md +98 -0
  178. package/dist/engineering-pa-core/docs/tools/scheduler.md +243 -0
  179. package/dist/engineering-pa-core/docs/tools/slice.md +150 -0
  180. package/dist/engineering-pa-core/docs/upstream-issue-filing.md +88 -0
  181. package/dist/engineering-pa-core/docs/working-directory.md +29 -0
  182. package/dist/engineering-pa-core/docs/workspace-operations.md +46 -0
  183. package/dist/engineering-pa-core/index.mjs +21 -0
  184. package/dist/engineering-pa-core/knowledge/README.md +53 -0
  185. package/dist/engineering-pa-core/knowledge/canonical/.gitkeep +0 -0
  186. package/dist/engineering-pa-core/knowledge/content-voice/README.md +64 -0
  187. package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-baseline.md +133 -0
  188. package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-shape.md +58 -0
  189. package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-taxonomy.md +33 -0
  190. package/dist/engineering-pa-core/knowledge/content-voice/content-pipeline-routing.md +58 -0
  191. package/dist/engineering-pa-core/knowledge/content-voice/new-tell-capture-procedure.md +40 -0
  192. package/dist/engineering-pa-core/knowledge/content-voice/voice-guide-shape.md +43 -0
  193. package/dist/engineering-pa-core/knowledge/delegation-dispatch/README.md +34 -0
  194. package/dist/engineering-pa-core/knowledge/delegation-dispatch/briefing-contract.md +77 -0
  195. package/dist/engineering-pa-core/knowledge/delegation-dispatch/delegation-modes.md +84 -0
  196. package/dist/engineering-pa-core/knowledge/delegation-dispatch/dispatch-envelope.md +238 -0
  197. package/dist/engineering-pa-core/knowledge/delegation-dispatch/task-groups-and-orchestrators.md +62 -0
  198. package/dist/engineering-pa-core/knowledge/delegation-dispatch/team-improvement-loop.md +48 -0
  199. package/dist/engineering-pa-core/knowledge/delegation-dispatch/tool-development-policy.md +48 -0
  200. package/dist/engineering-pa-core/knowledge/engineering-discipline/README.md +33 -0
  201. package/dist/engineering-pa-core/knowledge/engineering-discipline/blast-radius-and-safety.md +50 -0
  202. package/dist/engineering-pa-core/knowledge/engineering-discipline/investigate-root-causes.md +48 -0
  203. package/dist/engineering-pa-core/knowledge/engineering-discipline/role-ladder.md +57 -0
  204. package/dist/engineering-pa-core/knowledge/engineering-discipline/verify-runtime-behaviour.md +53 -0
  205. package/dist/engineering-pa-core/knowledge/engineering-discipline/worktree-isolation.md +50 -0
  206. package/dist/engineering-pa-core/knowledge/knowledge-memory/README.md +33 -0
  207. package/dist/engineering-pa-core/knowledge/knowledge-memory/claude-md-hygiene.md +47 -0
  208. package/dist/engineering-pa-core/knowledge/knowledge-memory/journal-and-self-improvement.md +53 -0
  209. package/dist/engineering-pa-core/knowledge/knowledge-memory/knowledge-retrieval-discipline.md +62 -0
  210. package/dist/engineering-pa-core/knowledge/knowledge-memory/librarian-synthesis-mandate.md +56 -0
  211. package/dist/engineering-pa-core/knowledge/knowledge-memory/staging-and-observations.md +58 -0
  212. package/dist/engineering-pa-core/knowledge/operator-interface/README.md +32 -0
  213. package/dist/engineering-pa-core/knowledge/operator-interface/communication-style.md +33 -0
  214. package/dist/engineering-pa-core/knowledge/operator-interface/escalation-and-correction.md +45 -0
  215. package/dist/engineering-pa-core/knowledge/operator-interface/push-back-discipline.md +42 -0
  216. package/dist/engineering-pa-core/knowledge/operator-interface/session-boot-and-end.md +56 -0
  217. package/dist/engineering-pa-core/knowledge/platform-tooling/README.md +37 -0
  218. package/dist/engineering-pa-core/knowledge/platform-tooling/desk-cli-reference.md +52 -0
  219. package/dist/engineering-pa-core/knowledge/platform-tooling/environment-awareness.md +91 -0
  220. package/dist/engineering-pa-core/knowledge/platform-tooling/mcp-servers-catalogue.md +52 -0
  221. package/dist/engineering-pa-core/knowledge/platform-tooling/pa-cli-reference.md +55 -0
  222. package/dist/engineering-pa-core/knowledge/platform-tooling/scheduling-and-launchd.md +56 -0
  223. package/dist/engineering-pa-core/knowledge/platform-tooling/tool-discovery-discipline.md +49 -0
  224. package/dist/engineering-pa-core/knowledge/platform-tooling/working-directory-vs-project-root.md +43 -0
  225. package/dist/engineering-pa-core/knowledge/platform-tooling/workspace-server.md +51 -0
  226. package/dist/engineering-pa-core/knowledge/raw/.gitkeep +0 -0
  227. package/dist/engineering-pa-core/knowledge/scaffold/domains.json +5 -0
  228. package/dist/engineering-pa-core/knowledge/schema/.gitkeep +0 -0
  229. package/dist/engineering-pa-core/knowledge/work-management/README.md +33 -0
  230. package/dist/engineering-pa-core/knowledge/work-management/desk-cli.md +84 -0
  231. package/dist/engineering-pa-core/knowledge/work-management/progress-md.md +48 -0
  232. package/dist/engineering-pa-core/knowledge/work-management/projects.md +64 -0
  233. package/dist/engineering-pa-core/knowledge/work-management/spawn-log.md +48 -0
  234. package/dist/engineering-pa-core/knowledge/work-management/work-items.md +67 -0
  235. package/dist/engineering-pa-core/package.json +26 -0
  236. package/dist/engineering-pa-core/roles/agent-ops/definition.md +79 -0
  237. package/dist/engineering-pa-core/roles/dispatch-briefer/definition.md +195 -0
  238. package/dist/engineering-pa-core/roles/librarian/definition.md +176 -0
  239. package/dist/engineering-pa-core/roles/pa/definition.md +93 -0
  240. package/dist/engineering-pa-core/roles/system-admin/definition.md +106 -0
  241. package/dist/engineering-pa-core/roles/tool-developer/definition.md +116 -0
  242. package/dist/engineering-pa-core/roles/writer/definition.md +130 -0
  243. package/dist/engineering-pa-core/schema/autonomy-envelope.schema.json +61 -0
  244. package/dist/engineering-pa-core/schema/project.json +114 -0
  245. package/dist/engineering-pa-core/schema/schedule.json +63 -0
  246. package/dist/engineering-pa-core/schema/work-item.json +92 -0
  247. package/dist/engineering-pa-core/src/config/pa-package-name.mjs +30 -0
  248. package/dist/engineering-pa-core/src/desk/cli.mjs +95 -0
  249. package/dist/engineering-pa-core/src/desk/commands.mjs +398 -0
  250. package/dist/engineering-pa-core/src/desk/format.mjs +84 -0
  251. package/dist/engineering-pa-core/src/desk/repo.mjs +172 -0
  252. package/dist/engineering-pa-core/src/desk/team-scope.mjs +142 -0
  253. package/dist/engineering-pa-core/src/distill/cli.mjs +112 -0
  254. package/dist/engineering-pa-core/src/distill/prereqs.mjs +171 -0
  255. package/dist/engineering-pa-core/src/distill/prompt.mjs +74 -0
  256. package/dist/engineering-pa-core/src/distill/run.mjs +230 -0
  257. package/dist/engineering-pa-core/src/distill/runner.mjs +86 -0
  258. package/dist/engineering-pa-core/src/distill/staging.mjs +59 -0
  259. package/dist/engineering-pa-core/src/knowledge/retrieval/backlink-indexer.mjs +154 -0
  260. package/dist/engineering-pa-core/src/knowledge/retrieval/graph-builder.mjs +186 -0
  261. package/dist/engineering-pa-core/src/knowledge/retrieval/index-builder.mjs +225 -0
  262. package/dist/engineering-pa-core/src/knowledge/retrieval/query.mjs +263 -0
  263. package/dist/engineering-pa-core/src/lock/lock.mjs +202 -0
  264. package/dist/engineering-pa-core/src/memory-sync/cli-support.mjs +12 -0
  265. package/dist/engineering-pa-core/src/memory-sync/cli.mjs +56 -0
  266. package/dist/engineering-pa-core/src/memory-sync/commands/pull.mjs +44 -0
  267. package/dist/engineering-pa-core/src/memory-sync/commands/push.mjs +46 -0
  268. package/dist/engineering-pa-core/src/memory-sync/mirror.mjs +114 -0
  269. package/dist/engineering-pa-core/src/memory-sync/repo.mjs +65 -0
  270. package/dist/engineering-pa-core/src/scheduler/detect.mjs +111 -0
  271. package/dist/engineering-pa-core/src/scheduler/host.mjs +38 -0
  272. package/dist/engineering-pa-core/src/scheduler/index.mjs +13 -0
  273. package/dist/engineering-pa-core/src/scheduler/install.mjs +854 -0
  274. package/dist/engineering-pa-core/src/scheduler/list.mjs +168 -0
  275. package/dist/engineering-pa-core/src/scheduler/remove.mjs +176 -0
  276. package/dist/engineering-pa-core/src/scheduler/verify.mjs +435 -0
  277. package/dist/engineering-pa-core/src/session-boot/autonomy-injection.mjs +84 -0
  278. package/dist/engineering-pa-core/src/slice/cli.mjs +139 -0
  279. package/dist/engineering-pa-core/src/slice/inputs.mjs +174 -0
  280. package/dist/engineering-pa-core/src/slice/prompt.mjs +94 -0
  281. package/dist/engineering-pa-core/src/slice/prompts/dave-boot.md +91 -0
  282. package/dist/engineering-pa-core/src/slice/run.mjs +334 -0
  283. package/dist/engineering-pa-core-extensions-atlassian/README.md +31 -0
  284. package/dist/engineering-pa-core-extensions-atlassian/index.mjs +9 -0
  285. package/dist/engineering-pa-core-extensions-atlassian/package.json +19 -0
  286. package/dist/engineering-pa-core-extensions-n8n/README.md +29 -0
  287. package/dist/engineering-pa-core-extensions-n8n/index.mjs +8 -0
  288. package/dist/engineering-pa-core-extensions-n8n/package.json +19 -0
  289. package/dist/engineering-pa-librarian/README.md +38 -0
  290. package/dist/engineering-pa-librarian/index.mjs +12 -0
  291. package/dist/engineering-pa-librarian/package.json +19 -0
  292. package/dist/engineering-pa-migrate-inspect/README.md +131 -0
  293. package/dist/engineering-pa-migrate-inspect/bin/migrate-inspect +2 -0
  294. package/dist/engineering-pa-migrate-inspect/package.json +21 -0
  295. package/dist/engineering-pa-migrate-inspect/src/age-key-migrate.mjs +83 -0
  296. package/dist/engineering-pa-migrate-inspect/src/cli.mjs +97 -0
  297. package/dist/engineering-pa-migrate-inspect/src/inspector.mjs +237 -0
  298. package/dist/engineering-pa-migrate-inspect/src/platforms/linux.mjs +87 -0
  299. package/dist/engineering-pa-migrate-inspect/src/platforms/macos.mjs +105 -0
  300. package/dist/engineering-pa-migrate-inspect/src/runner.mjs +87 -0
  301. package/dist/engineering-pa-runbook/README.md +103 -0
  302. package/dist/engineering-pa-runbook/package.json +18 -0
  303. package/dist/engineering-pa-runbook/reference/dex-template.runbook.json +66 -0
  304. package/dist/engineering-pa-runbook/reference/hq-dave.runbook.json +69 -0
  305. package/dist/engineering-pa-runbook/reference/smarthome01.runbook.json +92 -0
  306. package/dist/engineering-pa-runbook/schema/inspection.schema.json +113 -0
  307. package/dist/engineering-pa-runbook/schema/runbook.schema.json +67 -0
  308. package/dist/engineering-pa-runbook/src/validator.mjs +167 -0
  309. package/dist/engineering-pa-scaffold/README.md +164 -0
  310. package/dist/engineering-pa-scaffold/bin/pa-init +2 -0
  311. package/dist/engineering-pa-scaffold/index.mjs +42 -0
  312. package/dist/engineering-pa-scaffold/package.json +22 -0
  313. package/dist/engineering-pa-scaffold/src/cli.mjs +115 -0
  314. package/dist/engineering-pa-scaffold/src/commands/init.mjs +169 -0
  315. package/dist/engineering-pa-scaffold/src/generator.mjs +354 -0
  316. package/dist/engineering-pa-scaffold/src/prompts.mjs +125 -0
  317. package/dist/engineering-pa-scaffold/src/runner.mjs +142 -0
  318. package/dist/engineering-pa-scaffold/templates/CLAUDE.md.template +196 -0
  319. package/dist/engineering-pa-scaffold/templates/operator-README.md.template +44 -0
  320. package/dist/engineering-pa-types-engineering/README.md +35 -0
  321. package/dist/engineering-pa-types-engineering/docs/commands/product-build.md +120 -0
  322. package/dist/engineering-pa-types-engineering/index.mjs +9 -0
  323. package/dist/engineering-pa-types-engineering/package.json +19 -0
  324. package/dist/engineering-pa-types-engineering/roles/product-engineer/definition.md +650 -0
  325. package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/build-spec.md +82 -0
  326. package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/preflight-checklist.md +158 -0
  327. package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/worker-brief.md +130 -0
  328. package/dist/engineering-pa-workspace/README.md +197 -0
  329. package/dist/engineering-pa-workspace/app/assets/md.js +187 -0
  330. package/dist/engineering-pa-workspace/app/assets/modal.js +120 -0
  331. package/dist/engineering-pa-workspace/app/assets/theme.css +132 -0
  332. package/dist/engineering-pa-workspace/app/assets/theme.js +159 -0
  333. package/dist/engineering-pa-workspace/app/banner.js +320 -0
  334. package/dist/engineering-pa-workspace/app/delegations.html +345 -0
  335. package/dist/engineering-pa-workspace/app/home.html +1060 -0
  336. package/dist/engineering-pa-workspace/app/index.html +361 -0
  337. package/dist/engineering-pa-workspace/app/knowledge-map.html +163 -0
  338. package/dist/engineering-pa-workspace/app/knowledge.html +304 -0
  339. package/dist/engineering-pa-workspace/app/nav.json +75 -0
  340. package/dist/engineering-pa-workspace/app/notes.html +199 -0
  341. package/dist/engineering-pa-workspace/app/onboarding.html +592 -0
  342. package/dist/engineering-pa-workspace/app/profile.html +243 -0
  343. package/dist/engineering-pa-workspace/app/project-detail.html +432 -0
  344. package/dist/engineering-pa-workspace/app/projects.html +297 -0
  345. package/dist/engineering-pa-workspace/app/repos.html +598 -0
  346. package/dist/engineering-pa-workspace/app/schedules.html +486 -0
  347. package/dist/engineering-pa-workspace/app/secrets.html +164 -0
  348. package/dist/engineering-pa-workspace/app/staging.html +319 -0
  349. package/dist/engineering-pa-workspace/app/tasks.html +465 -0
  350. package/dist/engineering-pa-workspace/app/vendor/chart.umd.min.js +21 -0
  351. package/dist/engineering-pa-workspace/app/viewer.html +116 -0
  352. package/dist/engineering-pa-workspace/docs/DAEMON.md +104 -0
  353. package/dist/engineering-pa-workspace/docs/page-blueprint.md +286 -0
  354. package/dist/engineering-pa-workspace/docs/status-mon.md +168 -0
  355. package/dist/engineering-pa-workspace/index.mjs +12 -0
  356. package/dist/engineering-pa-workspace/nav.json.example +8 -0
  357. package/dist/engineering-pa-workspace/package.json +27 -0
  358. package/dist/engineering-pa-workspace/src/api.mjs +2086 -0
  359. package/dist/engineering-pa-workspace/src/install-daemon.mjs +573 -0
  360. package/dist/engineering-pa-workspace/src/nav.mjs +190 -0
  361. package/dist/engineering-pa-workspace/src/secrets-api.mjs +175 -0
  362. package/dist/engineering-pa-workspace/src/server.mjs +516 -0
  363. package/dist/engineering-pa-workspace/src/status-mon/poll.mjs +220 -0
  364. package/dist/engineering-pa-workspace/src/status-mon/poller.mjs +113 -0
  365. package/dist/engineering-pa-workspace/src/status-mon/run.mjs +133 -0
  366. package/dist/engineering-pa-workspace/src/supervisor.mjs +264 -0
  367. package/dist/engineering-pa-workspace/src/ws.mjs +633 -0
  368. package/index.mjs +60 -0
  369. package/package.json +58 -0
  370. package/templates/banned-ai-tells.md +102 -0
  371. package/templates/purpose.md +105 -0
  372. package/templates/voice-guide.md +98 -0
@@ -0,0 +1,164 @@
1
+ // work-cli.mjs — argv router for `pa work <verb>`.
2
+ //
3
+ // Verbs:
4
+ // pa work create [--project <id>] --title "..." --category <c> [--priority N] [--summary "..."] [--owner ...] [--assignee ...]
5
+ // pa work status <id> <status>
6
+ // pa work list [--project <id>] [--status ...] [--mine] [--theirs] [--all] [--json]
7
+ //
8
+ // PA name (for --mine resolution) is read from
9
+ // operator/config/preferences.json. Falls back to 'pa' if missing.
10
+
11
+ import { readFile } from 'node:fs/promises';
12
+ import { join } from 'node:path';
13
+
14
+ import { createWork, setWorkStatus, listWork } from './work.mjs';
15
+ import { getRepoRoot } from './repo-root.mjs';
16
+
17
+ const USAGE = `pa work — manage operator-repo work items
18
+
19
+ Usage:
20
+ pa work create [--project <id>] --title "..." --category <feature|bug|chore|discussion|research>
21
+ [--priority 0|1|2|3] [--summary "..."] [--owner ...] [--assignee ...]
22
+ pa work status <id> <draft|planned|in-progress|blocked|done|archived>
23
+ pa work list [--project <id>] [--status ...] [--category ...] [--mine] [--theirs] [--all] [--json]
24
+ pa work -h, --help
25
+
26
+ Notes:
27
+ - status=done stamps \`completedAt\` to today (only if not already set).
28
+ - --mine resolves the PA name from operator/config/preferences.json
29
+ (paName / agentName). Default: "pa".
30
+
31
+ Exit codes:
32
+ 0 ok | 1 usage/not-found | 2 operational failure
33
+ `;
34
+
35
+ export async function run(argv) {
36
+ if (!argv.length || argv[0] === '-h' || argv[0] === '--help') {
37
+ process.stdout.write(USAGE);
38
+ return 0;
39
+ }
40
+ const verb = argv[0];
41
+ const rest = argv.slice(1);
42
+
43
+ try {
44
+ switch (verb) {
45
+ case 'create': return await doCreate(rest);
46
+ case 'status': return await doStatus(rest);
47
+ case 'list': return await doList(rest);
48
+ default:
49
+ process.stderr.write(`[pa work] unknown verb: ${verb}\n`);
50
+ process.stderr.write(USAGE);
51
+ return 1;
52
+ }
53
+ } catch (err) {
54
+ process.stderr.write(`[pa work] ${err.message || err}\n`);
55
+ return err.exitCode || 2;
56
+ }
57
+ }
58
+
59
+ function parseFlags(args) {
60
+ const positional = [];
61
+ const flags = {};
62
+ for (let i = 0; i < args.length; i++) {
63
+ const a = args[i];
64
+ if (a.startsWith('--')) {
65
+ const key = a.slice(2);
66
+ const peek = args[i + 1];
67
+ if (peek == null || peek.startsWith('--')) {
68
+ flags[key] = true;
69
+ } else {
70
+ flags[key] = peek;
71
+ i += 1;
72
+ }
73
+ } else {
74
+ positional.push(a);
75
+ }
76
+ }
77
+ return { positional, flags };
78
+ }
79
+
80
+ async function getPaName() {
81
+ try {
82
+ const repoRoot = await getRepoRoot();
83
+ const raw = await readFile(
84
+ join(repoRoot, 'operator', 'config', 'preferences.json'),
85
+ 'utf8',
86
+ );
87
+ const config = JSON.parse(raw);
88
+ return config.paName || config.agentName || 'pa';
89
+ } catch {
90
+ return 'pa';
91
+ }
92
+ }
93
+
94
+ async function doCreate(args) {
95
+ const { flags } = parseFlags(args);
96
+ const priority = flags.priority != null && flags.priority !== true
97
+ ? Number(flags.priority)
98
+ : null;
99
+ const res = await createWork({
100
+ projectId: typeof flags.project === 'string' ? flags.project : null,
101
+ title: typeof flags.title === 'string' ? flags.title : null,
102
+ category: typeof flags.category === 'string' ? flags.category : null,
103
+ priority,
104
+ summary: typeof flags.summary === 'string' ? flags.summary : null,
105
+ owner: typeof flags.owner === 'string' ? flags.owner : null,
106
+ assignee: typeof flags.assignee === 'string' ? flags.assignee : null,
107
+ });
108
+ process.stdout.write(`created ${res.id}\n`);
109
+ process.stdout.write(` ${res.path}\n`);
110
+ return 0;
111
+ }
112
+
113
+ async function doStatus(args) {
114
+ if (args.length < 2) {
115
+ process.stderr.write('usage: pa work status <id> <status>\n');
116
+ return 1;
117
+ }
118
+ const [id, status] = args;
119
+ const res = await setWorkStatus(id, status);
120
+ process.stdout.write(`work ${res.id} → ${res.status}\n`);
121
+ if (res.completedAt && status === 'done') {
122
+ process.stdout.write(` completedAt: ${res.completedAt}\n`);
123
+ }
124
+ return 0;
125
+ }
126
+
127
+ async function doList(args) {
128
+ const { flags } = parseFlags(args);
129
+ const paName = (flags.mine || flags.theirs) ? await getPaName() : null;
130
+ const items = await listWork({
131
+ all: flags.all === true,
132
+ status: typeof flags.status === 'string' ? flags.status : null,
133
+ category: typeof flags.category === 'string' ? flags.category : null,
134
+ project: typeof flags.project === 'string' ? flags.project : null,
135
+ owner: typeof flags.owner === 'string' ? flags.owner : null,
136
+ assignee: typeof flags.assignee === 'string' ? flags.assignee : null,
137
+ mine: flags.mine === true,
138
+ theirs: flags.theirs === true,
139
+ paName,
140
+ });
141
+ if (flags.json) {
142
+ process.stdout.write(JSON.stringify(items, null, 2) + '\n');
143
+ return 0;
144
+ }
145
+ if (!items.length) {
146
+ process.stdout.write('no work items match\n');
147
+ return 0;
148
+ }
149
+ const rows = items.map((w) => ({
150
+ id: w.id,
151
+ status: w.status || 'draft',
152
+ priority: w.priority == null ? '-' : `P${w.priority}`,
153
+ project: w.projectId || '-',
154
+ title: (w.title || '').slice(0, 60),
155
+ }));
156
+ const cols = ['id', 'status', 'priority', 'project', 'title'];
157
+ const widths = {};
158
+ for (const c of cols) widths[c] = Math.max(c.length, ...rows.map((r) => String(r[c]).length));
159
+ const pad = (s, w) => String(s).padEnd(w, ' ');
160
+ process.stdout.write(cols.map((c) => pad(c, widths[c])).join(' ') + '\n');
161
+ process.stdout.write(cols.map((c) => '-'.repeat(widths[c])).join(' ') + '\n');
162
+ for (const r of rows) process.stdout.write(cols.map((c) => pad(r[c], widths[c])).join(' ') + '\n');
163
+ return 0;
164
+ }
@@ -0,0 +1,196 @@
1
+ // work.mjs — write-side commands for `pa work <verb>`.
2
+ //
3
+ // Source of truth is work/w-YYYY-MM-DD-NNN.json. We never cache.
4
+ //
5
+ // What lives here:
6
+ // - create: scaffold a new work item with the next-free NNN for today
7
+ // - status: change status (stamps `completedAt` on done)
8
+ // - list: list work items, optionally filtered
9
+ //
10
+ // ID format and status/category enums match
11
+ // packages/core/schema/work-item.json.
12
+
13
+ import { existsSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+
16
+ import { getRepoRoot } from './repo-root.mjs';
17
+ import { readJson, writeJson, ensureDir, listJsonFiles, today } from './io.mjs';
18
+
19
+ const STATUSES = new Set(['draft', 'planned', 'in-progress', 'blocked', 'done', 'archived']);
20
+
21
+ const CATEGORIES = new Set(['feature', 'bug', 'chore', 'discussion', 'research']);
22
+
23
+ const ID_RE = /^w-(\d{4}-\d{2}-\d{2})-(\d{3})$/;
24
+
25
+ function validateStatus(status) {
26
+ if (!STATUSES.has(status)) {
27
+ const err = new Error(
28
+ `invalid status: ${status} (expected one of: ${[...STATUSES].join(', ')})`,
29
+ );
30
+ err.exitCode = 1;
31
+ throw err;
32
+ }
33
+ }
34
+
35
+ function validateCategory(category) {
36
+ if (!category) return;
37
+ if (!CATEGORIES.has(category)) {
38
+ const err = new Error(
39
+ `invalid category: ${category} (expected one of: ${[...CATEGORIES].join(', ')})`,
40
+ );
41
+ err.exitCode = 1;
42
+ throw err;
43
+ }
44
+ }
45
+
46
+ function validatePriority(priority) {
47
+ if (priority == null) return;
48
+ if (!Number.isInteger(priority) || priority < 0 || priority > 3) {
49
+ const err = new Error(`invalid priority: ${priority} (expected integer 0–3)`);
50
+ err.exitCode = 1;
51
+ throw err;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Scan work/*.json for the highest NNN with today's date. Returns the
57
+ * next free NNN, zero-padded to 3 digits. Result is always "001"–"999".
58
+ *
59
+ * If 999 is already taken, throws — callers should treat as an
60
+ * operational failure and report. In practice this is unreachable.
61
+ */
62
+ async function nextWorkNumber(repoRoot, dateStr) {
63
+ const dir = join(repoRoot, 'work');
64
+ const files = await listJsonFiles(dir);
65
+ let max = 0;
66
+ const prefix = `w-${dateStr}-`;
67
+ for (const file of files) {
68
+ if (!file.startsWith(prefix)) continue;
69
+ const m = file.match(/^w-\d{4}-\d{2}-\d{2}-(\d{3})\.json$/);
70
+ if (!m) continue;
71
+ const n = parseInt(m[1], 10);
72
+ if (Number.isFinite(n) && n > max) max = n;
73
+ }
74
+ const next = max + 1;
75
+ if (next > 999) {
76
+ const err = new Error(`no free work-item slot for ${dateStr} (NNN exhausted)`);
77
+ err.exitCode = 2;
78
+ throw err;
79
+ }
80
+ return String(next).padStart(3, '0');
81
+ }
82
+
83
+ /**
84
+ * Create a new work item. Returns { id, path }.
85
+ *
86
+ * Status defaults to "draft" per the brief. Date and NNN are
87
+ * auto-derived from today; we do not accept overrides.
88
+ */
89
+ export async function createWork(opts) {
90
+ if (!opts.title) {
91
+ const err = new Error('--title is required');
92
+ err.exitCode = 1;
93
+ throw err;
94
+ }
95
+ validateCategory(opts.category);
96
+ validatePriority(opts.priority);
97
+
98
+ const repoRoot = await getRepoRoot();
99
+ const workDir = join(repoRoot, 'work');
100
+ await ensureDir(workDir);
101
+
102
+ const dateStr = today();
103
+ const nnn = await nextWorkNumber(repoRoot, dateStr);
104
+ const id = `w-${dateStr}-${nnn}`;
105
+ const path = join(workDir, `${id}.json`);
106
+
107
+ const item = {
108
+ id,
109
+ projectId: opts.projectId || null,
110
+ title: opts.title,
111
+ category: opts.category || 'chore',
112
+ status: 'draft',
113
+ priority: opts.priority == null ? 2 : opts.priority,
114
+ owner: opts.owner || 'system-architect',
115
+ assignee: opts.assignee || opts.owner || 'system-architect',
116
+ created: dateStr,
117
+ updated: dateStr,
118
+ completedAt: null,
119
+ summary: opts.summary || '',
120
+ blocked: null,
121
+ links: [],
122
+ };
123
+ await writeJson(path, item);
124
+ return { id, path };
125
+ }
126
+
127
+ /**
128
+ * Set work-item status. Stamps `completedAt` to today's date when
129
+ * status=done (only if not already set). Always bumps `updated`.
130
+ */
131
+ export async function setWorkStatus(id, status) {
132
+ if (!ID_RE.test(id)) {
133
+ const err = new Error(`invalid work id: ${id} (expected w-YYYY-MM-DD-NNN)`);
134
+ err.exitCode = 1;
135
+ throw err;
136
+ }
137
+ validateStatus(status);
138
+
139
+ const repoRoot = await getRepoRoot();
140
+ const path = join(repoRoot, 'work', `${id}.json`);
141
+ if (!existsSync(path)) {
142
+ const err = new Error(`work item not found: ${id}`);
143
+ err.exitCode = 1;
144
+ throw err;
145
+ }
146
+ const item = await readJson(path);
147
+ item.status = status;
148
+ item.updated = today();
149
+ if (status === 'done' && !item.completedAt) {
150
+ item.completedAt = today();
151
+ }
152
+ await writeJson(path, item);
153
+ return { id, status, completedAt: item.completedAt, path };
154
+ }
155
+
156
+ /**
157
+ * List work items. Filters: status, category, project, owner, assignee,
158
+ * mine, theirs, all, paName (used for --mine resolution).
159
+ * By default excludes done + archived (mirrors desk-list semantics).
160
+ */
161
+ export async function listWork(filters = {}) {
162
+ const repoRoot = await getRepoRoot();
163
+ const dir = join(repoRoot, 'work');
164
+ const files = await listJsonFiles(dir);
165
+ const items = [];
166
+ for (const f of files) {
167
+ try {
168
+ items.push(await readJson(join(dir, f)));
169
+ } catch {
170
+ // Skip unparseable.
171
+ }
172
+ }
173
+ let filtered = items;
174
+ if (!filters.all) {
175
+ filtered = filtered.filter((w) => !['done', 'archived'].includes(w.status));
176
+ }
177
+ if (filters.status) filtered = filtered.filter((w) => w.status === filters.status);
178
+ if (filters.category) filtered = filtered.filter((w) => w.category === filters.category);
179
+ if (filters.project) filtered = filtered.filter((w) => w.projectId === filters.project);
180
+ if (filters.owner) filtered = filtered.filter((w) => w.owner === filters.owner);
181
+ if (filters.assignee) filtered = filtered.filter((w) => w.assignee === filters.assignee);
182
+ if (filters.mine && filters.paName) filtered = filtered.filter((w) => w.assignee === filters.paName);
183
+ if (filters.theirs) filtered = filtered.filter((w) => w.assignee === 'operator');
184
+
185
+ const STATE_ORDER = ['in-progress', 'blocked', 'planned', 'draft', 'done', 'archived'];
186
+ filtered.sort((a, b) => {
187
+ const sa = STATE_ORDER.indexOf(a.status || 'draft');
188
+ const sb = STATE_ORDER.indexOf(b.status || 'draft');
189
+ if (sa !== sb) return sa - sb;
190
+ const pa = a.priority == null ? 99 : a.priority;
191
+ const pb = b.priority == null ? 99 : b.priority;
192
+ if (pa !== pb) return pa - pb;
193
+ return (a.id || '').localeCompare(b.id || '');
194
+ });
195
+ return filtered;
196
+ }
@@ -0,0 +1,201 @@
1
+ // workstreams.mjs — write-side commands for `pa project workstream <verb>`.
2
+ //
3
+ // A workstream is a named unit of work within a project. Each workstream
4
+ // has its own status, blockedOn signal, next-action text for operator and
5
+ // Dave, and owns a set of documents.
6
+ //
7
+ // Workstream schema:
8
+ // {
9
+ // "id": "content-pipeline", // kebab-case
10
+ // "name": "Content pipeline",
11
+ // "status": "in-progress", // same vocab as project status
12
+ // "blockedOn": "operator", // optional: "operator" | "dave" | free text
13
+ // "nextOperator": "Review week 1 drafts", // optional
14
+ // "nextDave": "Publish after approval",// optional
15
+ // "docs": [{ "label": "...", "path": "...", "status": "review" }]
16
+ // }
17
+
18
+ import { existsSync } from 'node:fs';
19
+ import { join } from 'node:path';
20
+
21
+ import { getRepoRoot } from './repo-root.mjs';
22
+ import { readJson, writeJson, today } from './io.mjs';
23
+ import { VALID_DOC_STATUSES } from './doc-groups.mjs';
24
+
25
+ const STATUSES = new Set(['draft', 'planned', 'in-progress', 'blocked', 'done', 'archived']);
26
+
27
+ function fail(msg, code = 1) {
28
+ const err = new Error(msg);
29
+ err.exitCode = code;
30
+ throw err;
31
+ }
32
+
33
+ function validateWsId(id) {
34
+ if (!id || typeof id !== 'string') fail('workstream --id is required');
35
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(id)) {
36
+ fail(`invalid workstream id: ${id} (lowercase letters, digits, and '-' only; must start with a letter or digit)`);
37
+ }
38
+ }
39
+
40
+ function validateStatus(status) {
41
+ if (!STATUSES.has(status)) {
42
+ fail(`invalid status: ${status} (expected one of: ${[...STATUSES].join(', ')})`);
43
+ }
44
+ }
45
+
46
+ async function loadProject(projectId) {
47
+ const repoRoot = await getRepoRoot();
48
+ const path = join(repoRoot, 'projects', projectId, 'project.json');
49
+ if (!existsSync(path)) fail(`project not found: ${projectId}`);
50
+ const manifest = await readJson(path);
51
+ return { manifest, path };
52
+ }
53
+
54
+ async function saveProject(path, manifest) {
55
+ manifest.updated = today();
56
+ await writeJson(path, manifest);
57
+ }
58
+
59
+ function ensureWorkstreams(manifest) {
60
+ if (!Array.isArray(manifest.workstreams)) {
61
+ manifest.workstreams = [];
62
+ }
63
+ return manifest.workstreams;
64
+ }
65
+
66
+ function findWorkstream(workstreams, wsId) {
67
+ return workstreams.find((ws) => ws.id === wsId);
68
+ }
69
+
70
+ /**
71
+ * Add a new workstream to a project.
72
+ */
73
+ export async function addWorkstream(projectId, { id: wsId, name, status, blockedOn }) {
74
+ validateWsId(wsId);
75
+ if (!name) fail('--name is required');
76
+ if (status) validateStatus(status);
77
+
78
+ const { manifest, path } = await loadProject(projectId);
79
+ const workstreams = ensureWorkstreams(manifest);
80
+
81
+ if (findWorkstream(workstreams, wsId)) {
82
+ fail(`workstream already exists: ${wsId} in project ${projectId}`);
83
+ }
84
+
85
+ const ws = {
86
+ id: wsId,
87
+ name,
88
+ status: status || 'draft',
89
+ docs: [],
90
+ };
91
+ if (blockedOn) ws.blockedOn = blockedOn;
92
+
93
+ workstreams.push(ws);
94
+ await saveProject(path, manifest);
95
+
96
+ return { projectId, workstream: ws, path };
97
+ }
98
+
99
+ /**
100
+ * Update a workstream's status and optionally blockedOn.
101
+ */
102
+ export async function setWorkstreamStatus(projectId, { id: wsId, status, blockedOn }) {
103
+ validateWsId(wsId);
104
+ validateStatus(status);
105
+
106
+ const { manifest, path } = await loadProject(projectId);
107
+ const workstreams = ensureWorkstreams(manifest);
108
+ const ws = findWorkstream(workstreams, wsId);
109
+ if (!ws) fail(`workstream not found: ${wsId} in project ${projectId}`);
110
+
111
+ ws.status = status;
112
+ if (blockedOn !== undefined) {
113
+ if (blockedOn) {
114
+ ws.blockedOn = blockedOn;
115
+ } else {
116
+ delete ws.blockedOn;
117
+ }
118
+ }
119
+
120
+ await saveProject(path, manifest);
121
+ return { projectId, workstream: ws, path };
122
+ }
123
+
124
+ /**
125
+ * Set the next-action text for operator and/or Dave on a workstream.
126
+ */
127
+ export async function setWorkstreamNext(projectId, { id: wsId, operator: nextOp, dave: nextDave }) {
128
+ validateWsId(wsId);
129
+
130
+ const { manifest, path } = await loadProject(projectId);
131
+ const workstreams = ensureWorkstreams(manifest);
132
+ const ws = findWorkstream(workstreams, wsId);
133
+ if (!ws) fail(`workstream not found: ${wsId} in project ${projectId}`);
134
+
135
+ if (nextOp !== undefined) {
136
+ if (nextOp) {
137
+ ws.nextOperator = nextOp;
138
+ } else {
139
+ delete ws.nextOperator;
140
+ }
141
+ }
142
+ if (nextDave !== undefined) {
143
+ if (nextDave) {
144
+ ws.nextDave = nextDave;
145
+ } else {
146
+ delete ws.nextDave;
147
+ }
148
+ }
149
+
150
+ await saveProject(path, manifest);
151
+ return { projectId, workstream: ws, path };
152
+ }
153
+
154
+ /**
155
+ * Remove a workstream from a project.
156
+ */
157
+ export async function removeWorkstream(projectId, { id: wsId }) {
158
+ validateWsId(wsId);
159
+
160
+ const { manifest, path } = await loadProject(projectId);
161
+ const workstreams = ensureWorkstreams(manifest);
162
+ const idx = workstreams.findIndex((ws) => ws.id === wsId);
163
+ if (idx === -1) fail(`workstream not found: ${wsId} in project ${projectId}`);
164
+
165
+ workstreams.splice(idx, 1);
166
+ await saveProject(path, manifest);
167
+
168
+ return { projectId, removed: wsId, path };
169
+ }
170
+
171
+ /**
172
+ * Add a doc entry to a specific workstream's docs array.
173
+ * Idempotent on {label, path} match within the workstream.
174
+ */
175
+ export async function addDocToWorkstream(projectId, wsId, { label, path: docPath, status }) {
176
+ validateWsId(wsId);
177
+ if (!label) fail('--label is required');
178
+ if (!docPath) fail('--path is required');
179
+ if (status && !VALID_DOC_STATUSES.has(status)) {
180
+ fail(`invalid doc status: ${status} (expected one of: ${[...VALID_DOC_STATUSES].join(', ')})`);
181
+ }
182
+
183
+ const { manifest, path } = await loadProject(projectId);
184
+ const workstreams = ensureWorkstreams(manifest);
185
+ const ws = findWorkstream(workstreams, wsId);
186
+ if (!ws) fail(`workstream not found: ${wsId} in project ${projectId}`);
187
+
188
+ if (!Array.isArray(ws.docs)) ws.docs = [];
189
+
190
+ const dup = ws.docs.find((d) => d.path === docPath && d.label === label);
191
+ if (dup) {
192
+ return { projectId, workstream: wsId, label, path: docPath, status: status || null, changed: false, manifestPath: path };
193
+ }
194
+
195
+ const entry = { label, path: docPath };
196
+ if (status) entry.status = status;
197
+ ws.docs.push(entry);
198
+
199
+ await saveProject(path, manifest);
200
+ return { projectId, workstream: wsId, label, path: docPath, status: status || null, changed: true, manifestPath: path };
201
+ }