@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,354 @@
1
+ // generator.mjs — pure scaffolding plan.
2
+ //
3
+ // Given a normalised config, return a list of `{ kind, path, contents? }`
4
+ // entries that the runner writes to disk. NO IO HERE. All filesystem
5
+ // effects live in runner.mjs.
6
+ //
7
+ // kind: 'dir' (mkdir -p) | 'file' (write file with contents) | 'json'
8
+ // (stringify + write).
9
+
10
+ import { readFile } from 'node:fs/promises';
11
+ import { dirname, join, resolve } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+
16
+ /** Path to the CLAUDE.md template (relative to this module). */
17
+ export const CLAUDE_MD_TEMPLATE_PATH = resolve(__dirname, '..', 'templates', 'CLAUDE.md.template');
18
+
19
+ /** Path to the operator README template (relative to this module). */
20
+ export const OPERATOR_README_TEMPLATE_PATH = resolve(
21
+ __dirname,
22
+ '..',
23
+ 'templates',
24
+ 'operator-README.md.template',
25
+ );
26
+
27
+ /**
28
+ * The base set of knowledge domains seeded into every fresh PA instance.
29
+ * Per the methodology doc: engineering, ops, agent-ops, knowledge-meta,
30
+ * operator-profile.
31
+ */
32
+ export const BASE_DOMAINS = [
33
+ { id: 'engineering', label: 'Engineering — the discipline (patterns, standards, decisions, tech debt)' },
34
+ { id: 'ops', label: 'Operational rules and process' },
35
+ { id: 'agent-ops', label: 'Agentic team management (roles, identity, roster, autonomy envelopes)' },
36
+ { id: 'knowledge-meta', label: 'The librarian and methodology' },
37
+ { id: 'operator-profile', label: 'Who the operator is — context, preferences, constraints' },
38
+ ];
39
+
40
+ const DEFAULT_PURPOSE =
41
+ 'Be a self-learning personal assistant who learns the operator deeply over time and acts on their behalf with increasing autonomy.';
42
+
43
+ const DEFAULT_COMMUNICATION_STYLE =
44
+ 'Casual, direct, no filler. Voice opinions with reasoning. Light humour welcome; obsequiousness is not.';
45
+
46
+ /**
47
+ * Build a normalised render config from raw inputs.
48
+ */
49
+ export function buildRenderConfig(input) {
50
+ const generatedAt = input.generatedAt || new Date().toISOString();
51
+ return {
52
+ paName: input.paName,
53
+ operatorName: input.operatorName,
54
+ purpose: input.purpose && input.purpose.trim().length > 0 ? input.purpose.trim() : DEFAULT_PURPOSE,
55
+ communicationStyle:
56
+ input.communicationStyle && input.communicationStyle.trim().length > 0
57
+ ? input.communicationStyle.trim()
58
+ : DEFAULT_COMMUNICATION_STYLE,
59
+ targetDir: resolve(input.targetDir),
60
+ generatedAt,
61
+ pkgVersion: input.pkgVersion || '^0.1.0',
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Render the CLAUDE.md template with the config. Pure token replacement
67
+ * (regex, no template engine). Template tokens follow the existing v2
68
+ * convention from Task 4: {PA_NAME}, {OPERATOR_NAME}, {PURPOSE},
69
+ * {COMMUNICATION_STYLE}.
70
+ */
71
+ export function renderClaudeMd(template, config) {
72
+ return template
73
+ .replaceAll('{PA_NAME}', config.paName)
74
+ .replaceAll('{OPERATOR_NAME}', config.operatorName)
75
+ .replaceAll('{PURPOSE}', config.purpose)
76
+ .replaceAll('{COMMUNICATION_STYLE}', config.communicationStyle);
77
+ }
78
+
79
+ /**
80
+ * Render the operator-repo README template. Uses the same token set as
81
+ * CLAUDE.md.template for consistency.
82
+ */
83
+ export function renderOperatorReadme(template, config) {
84
+ return template
85
+ .replaceAll('{PA_NAME}', config.paName)
86
+ .replaceAll('{OPERATOR_NAME}', config.operatorName);
87
+ }
88
+
89
+ /**
90
+ * Build the operator-repo package.json contents.
91
+ *
92
+ * Per the brief: the generated repo depends on `@stravica-ai/digital-operator-pa`
93
+ * (the public surface). For Task 5 verification via `pnpm link`, the test
94
+ * harness ALSO links `@stravica-ai/engineering-pa-cli` to expose the actual
95
+ * `pa` / `desk` bins. The generated package.json reflects the
96
+ * post-publication wiring; the test setup is a layer on top.
97
+ */
98
+ export function buildOperatorPackageJson(config) {
99
+ return {
100
+ name: `${config.paName}-operator`,
101
+ version: '0.0.1',
102
+ private: true,
103
+ type: 'module',
104
+ description: `Personal-assistant operator repo for ${config.operatorName} (PA: ${config.paName}).`,
105
+ engines: { node: '>=24' },
106
+ dependencies: {
107
+ '@stravica-ai/digital-operator-pa': config.pkgVersion,
108
+ },
109
+ scripts: {
110
+ pa: 'pa',
111
+ desk: 'desk',
112
+ status: 'pa status',
113
+ },
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Build the .gitignore contents for the operator repo. Per tooling
119
+ * standard §16: per-host operational files must be gitignored.
120
+ */
121
+ export function buildGitignore() {
122
+ return [
123
+ '# Dependencies',
124
+ 'node_modules/',
125
+ '.pnpm-store/',
126
+ '',
127
+ '# Environment',
128
+ '.env',
129
+ '.env.local',
130
+ '.env.*.local',
131
+ '',
132
+ '# Editor',
133
+ '.vscode/',
134
+ '.idea/',
135
+ '*.swp',
136
+ '*.swo',
137
+ '*~',
138
+ '',
139
+ '# OS',
140
+ '.DS_Store',
141
+ 'Thumbs.db',
142
+ '',
143
+ '# Session-local state (per-host)',
144
+ '.session/',
145
+ '.state/',
146
+ '',
147
+ '# Per-host operational artefacts (tooling standard §16)',
148
+ '*.pid',
149
+ '*.log',
150
+ 'runs.jsonl',
151
+ '.cache/',
152
+ '',
153
+ '# Vault — only the encrypted store is tracked; raw key material never',
154
+ '.vault/age-key.txt',
155
+ '.vault/*.key',
156
+ '',
157
+ '# Workspace runtime',
158
+ '.ws.pid',
159
+ '',
160
+ ].join('\n');
161
+ }
162
+
163
+ /**
164
+ * Build the operator/config/preferences.json contents.
165
+ *
166
+ * This file is the operator's machine-readable preferences store. The
167
+ * `pa session` verb keys off the `paName` field to locate the agent
168
+ * directory; future verbs and the librarian will lean on the same file
169
+ * for things like communication style and the workspace port.
170
+ *
171
+ * All keys are camelCase per the firm rule.
172
+ */
173
+ export function buildPreferencesJson(config) {
174
+ return {
175
+ paName: config.paName,
176
+ operator: { name: config.operatorName, role: 'Operator' },
177
+ pa: { name: capitalise(config.paName), role: 'PA' },
178
+ communicationStyle: config.communicationStyle,
179
+ workspacePort: 4200,
180
+ generatedAt: config.generatedAt,
181
+ };
182
+ }
183
+
184
+ function capitalise(s) {
185
+ if (typeof s !== 'string' || s.length === 0) return s;
186
+ return s.charAt(0).toUpperCase() + s.slice(1);
187
+ }
188
+
189
+ /** Build the operator/extensions/manifest.json contents. */
190
+ export function buildExtensionsManifest(config) {
191
+ return {
192
+ version: '1.0',
193
+ generatedAt: config.generatedAt,
194
+ extensions: [],
195
+ note: 'Extensions land in Phase 2. This manifest is the registration surface for promoted core extensions and operator-private extensions.',
196
+ };
197
+ }
198
+
199
+ /** Build the operator/knowledge/domains.json contents. */
200
+ export function buildDomainsJson(config) {
201
+ return {
202
+ version: '1.0',
203
+ generatedAt: config.generatedAt,
204
+ domains: BASE_DOMAINS.map((d) => ({ id: d.id, label: d.label })),
205
+ };
206
+ }
207
+
208
+ /** Build the operator/knowledge/index.json contents (empty top-level index). */
209
+ export function buildKnowledgeIndexJson(config) {
210
+ return {
211
+ version: '1.0',
212
+ generatedAt: config.generatedAt,
213
+ principles: 'principles.md',
214
+ domains: [],
215
+ };
216
+ }
217
+
218
+ /** Build the agents/{paName}/spawn-log.json contents. */
219
+ export function buildSpawnLogJson(config) {
220
+ return {
221
+ version: '1.0',
222
+ description: `Log of all agent delegations by ${config.paName}. Used for distillation learning and team improvement.`,
223
+ spawns: [],
224
+ };
225
+ }
226
+
227
+ /** Build the operator/profile/profile.md placeholder. */
228
+ export function buildProfileMd(config) {
229
+ return [
230
+ `# ${config.operatorName}`,
231
+ '',
232
+ 'Operator profile. The librarian distills observations from your PA into this file over time.',
233
+ 'For now it is a placeholder — your PA will lead an intake conversation early on to capture',
234
+ 'preferences, working style, vocabulary, and the immediate context the PA needs to be useful.',
235
+ '',
236
+ `Generated by pa-init at ${config.generatedAt}.`,
237
+ '',
238
+ ].join('\n');
239
+ }
240
+
241
+ /** Build the agents/{paName}/journal.md placeholder. */
242
+ export function buildJournalMd(config) {
243
+ return [
244
+ `# Journal — ${config.paName}`,
245
+ '',
246
+ 'Self-improvement notes. What went well, what to do differently, capability gaps to address.',
247
+ 'Append timestamped entries here as you reflect on each session.',
248
+ '',
249
+ ].join('\n');
250
+ }
251
+
252
+ /** Build the agents/{paName}/next-session.md placeholder. */
253
+ export function buildNextSessionMd(config) {
254
+ return [
255
+ '# Next session',
256
+ '',
257
+ `(Empty — generated at ${config.generatedAt}.)`,
258
+ '',
259
+ 'Maintain this file continuously. The "Top priority — start here" section is operator-facing.',
260
+ '',
261
+ ].join('\n');
262
+ }
263
+
264
+ /**
265
+ * Build the staging/.gitkeep contents. Empty directories cannot be tracked
266
+ * by git directly; we drop a .gitkeep marker so the directory is preserved.
267
+ */
268
+ export function buildGitkeep() {
269
+ return '';
270
+ }
271
+
272
+ /**
273
+ * Compose the full plan. Returns an ordered list of `{ kind, path, contents }`
274
+ * relative to `config.targetDir`. The runner resolves the paths and writes
275
+ * them.
276
+ */
277
+ export async function buildPlan(config) {
278
+ const claudeTemplate = await readFile(CLAUDE_MD_TEMPLATE_PATH, 'utf8');
279
+ const operatorReadmeTemplate = await readFile(OPERATOR_README_TEMPLATE_PATH, 'utf8');
280
+ return buildPlanFromTemplates(config, { claudeTemplate, operatorReadmeTemplate });
281
+ }
282
+
283
+ /**
284
+ * Pure: composes the plan given pre-loaded template strings. The
285
+ * non-template-loading variant is exposed so tests can drive the
286
+ * generator without touching the disk.
287
+ */
288
+ export function buildPlanFromTemplates(config, templates) {
289
+ const { paName } = config;
290
+ const claudeMd = renderClaudeMd(templates.claudeTemplate, config);
291
+ const readmeMd = renderOperatorReadme(templates.operatorReadmeTemplate, config);
292
+ const pkg = buildOperatorPackageJson(config);
293
+ const gitignore = buildGitignore();
294
+ const manifest = buildExtensionsManifest(config);
295
+ const domains = buildDomainsJson(config);
296
+ const knowledgeIndex = buildKnowledgeIndexJson(config);
297
+ const spawnLog = buildSpawnLogJson(config);
298
+ const profile = buildProfileMd(config);
299
+ const journal = buildJournalMd(config);
300
+ const nextSession = buildNextSessionMd(config);
301
+
302
+ const dirs = [
303
+ 'operator',
304
+ 'operator/profile',
305
+ 'operator/knowledge',
306
+ 'operator/config',
307
+ 'operator/extensions',
308
+ 'operator/extensions/roles',
309
+ 'operator/extensions/tools',
310
+ 'operator/extensions/workspace',
311
+ `agents/${paName}`,
312
+ `agents/${paName}/staging`,
313
+ 'work',
314
+ 'projects',
315
+ 'schedules',
316
+ '.vault',
317
+ ];
318
+
319
+ const plan = [];
320
+ for (const d of dirs) plan.push({ kind: 'dir', path: d });
321
+
322
+ plan.push({ kind: 'file', path: 'CLAUDE.md', contents: claudeMd });
323
+ plan.push({ kind: 'file', path: 'README.md', contents: readmeMd });
324
+ plan.push({ kind: 'json', path: 'package.json', contents: pkg });
325
+ plan.push({ kind: 'file', path: '.gitignore', contents: gitignore });
326
+ plan.push({ kind: 'file', path: '.nvmrc', contents: '24\n' });
327
+
328
+ plan.push({ kind: 'file', path: 'operator/profile/profile.md', contents: profile });
329
+ plan.push({ kind: 'json', path: 'operator/knowledge/domains.json', contents: domains });
330
+ plan.push({ kind: 'json', path: 'operator/knowledge/index.json', contents: knowledgeIndex });
331
+ plan.push({ kind: 'json', path: 'operator/config/preferences.json', contents: buildPreferencesJson(config) });
332
+ plan.push({ kind: 'json', path: 'operator/extensions/manifest.json', contents: manifest });
333
+
334
+ plan.push({ kind: 'file', path: `agents/${paName}/CLAUDE.md`, contents: claudeMd });
335
+ plan.push({ kind: 'file', path: `agents/${paName}/journal.md`, contents: journal });
336
+ plan.push({ kind: 'file', path: `agents/${paName}/next-session.md`, contents: nextSession });
337
+ plan.push({ kind: 'json', path: `agents/${paName}/spawn-log.json`, contents: spawnLog });
338
+
339
+ // Empty-directory markers — git won't track empty dirs.
340
+ for (const d of [
341
+ `agents/${paName}/staging`,
342
+ 'work',
343
+ 'projects',
344
+ 'schedules',
345
+ '.vault',
346
+ 'operator/extensions/roles',
347
+ 'operator/extensions/tools',
348
+ 'operator/extensions/workspace',
349
+ ]) {
350
+ plan.push({ kind: 'file', path: join(d, '.gitkeep'), contents: buildGitkeep() });
351
+ }
352
+
353
+ return plan;
354
+ }
@@ -0,0 +1,125 @@
1
+ // Interactive prompts for pa-init.
2
+ //
3
+ // Pure-ish: pure validation functions (testable directly) + a `promptValues`
4
+ // driver that takes a `readLine` seam. The default seam wraps `node:readline`;
5
+ // tests inject a fake seam.
6
+
7
+ import { resolve } from 'node:path';
8
+ import { createInterface } from 'node:readline';
9
+
10
+ /** kebab-case, lowercase, 2-40 chars, must start with a letter. */
11
+ export function validatePaName(value) {
12
+ if (typeof value !== 'string') return { ok: false, reason: 'must be a string' };
13
+ const trimmed = value.trim();
14
+ if (trimmed.length < 2) return { ok: false, reason: 'must be at least 2 characters' };
15
+ if (trimmed.length > 40) return { ok: false, reason: 'must be at most 40 characters' };
16
+ if (!/^[a-z][a-z0-9-]*$/.test(trimmed)) {
17
+ return {
18
+ ok: false,
19
+ reason: 'must be kebab-case, lowercase, start with a letter (e.g. "dave", "anna-pa")',
20
+ };
21
+ }
22
+ if (trimmed.endsWith('-') || trimmed.includes('--')) {
23
+ return { ok: false, reason: 'no trailing or doubled dashes' };
24
+ }
25
+ return { ok: true, value: trimmed };
26
+ }
27
+
28
+ /** Non-empty, 1-80 chars, trimmed. Allows letters / spaces / punctuation. */
29
+ export function validateOperatorName(value) {
30
+ if (typeof value !== 'string') return { ok: false, reason: 'must be a string' };
31
+ const trimmed = value.trim();
32
+ if (trimmed.length < 1) return { ok: false, reason: 'must not be empty' };
33
+ if (trimmed.length > 80) return { ok: false, reason: 'must be at most 80 characters' };
34
+ return { ok: true, value: trimmed };
35
+ }
36
+
37
+ /**
38
+ * Target dir must be either:
39
+ * - not exist (we will create it), OR
40
+ * - exist and be empty (no entries other than dotfile-free check).
41
+ *
42
+ * The actual filesystem check is done by the runner — this helper only
43
+ * validates the *path-shape* (non-empty, absolute or relative).
44
+ */
45
+ export function validateTargetPath(value) {
46
+ if (typeof value !== 'string') return { ok: false, reason: 'must be a string' };
47
+ const trimmed = value.trim();
48
+ if (trimmed.length < 1) return { ok: false, reason: 'must not be empty' };
49
+ return { ok: true, value: resolve(trimmed) };
50
+ }
51
+
52
+ /**
53
+ * Drive an interactive prompt sequence. `seam.ask(question) -> Promise<string>`
54
+ * is the only IO surface; tests substitute a scripted fake.
55
+ *
56
+ * `defaults` carries pre-populated values from CLI flags. The function
57
+ * prompts only for missing values (except operatorName which always prompts
58
+ * in interactive mode — there's no flag for it in the v1 contract... wait,
59
+ * there is. `--operator`. So we prompt only when missing).
60
+ */
61
+ export async function promptValues(seam, defaults) {
62
+ const out = {};
63
+
64
+ out.operatorName = await ensureValid(
65
+ seam,
66
+ defaults.operatorName,
67
+ "Operator name (the human this PA serves, e.g. 'Baz'): ",
68
+ validateOperatorName,
69
+ );
70
+
71
+ out.paName = await ensureValid(
72
+ seam,
73
+ defaults.paName,
74
+ "PA name (kebab-case identity, e.g. 'dave'): ",
75
+ validatePaName,
76
+ );
77
+
78
+ out.targetDir = await ensureValid(
79
+ seam,
80
+ defaults.targetDir,
81
+ `Target directory [${defaults.targetDirDefault}]: `,
82
+ validateTargetPath,
83
+ defaults.targetDirDefault,
84
+ );
85
+
86
+ return out;
87
+ }
88
+
89
+ async function ensureValid(seam, presupplied, question, validator, fallback) {
90
+ if (presupplied != null && presupplied !== '') {
91
+ const result = validator(presupplied);
92
+ if (result.ok) return result.value;
93
+ const err = new Error(`invalid value '${presupplied}': ${result.reason}`);
94
+ err.exitCode = 1;
95
+ throw err;
96
+ }
97
+ // Loop until valid.
98
+ for (;;) {
99
+ const raw = await seam.ask(question);
100
+ const supplied = raw === '' && fallback != null ? fallback : raw;
101
+ const result = validator(supplied);
102
+ if (result.ok) return result.value;
103
+ seam.write(` ✗ ${result.reason}\n`);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Default seam — wraps `node:readline`. Returns an object exposing
109
+ * `ask(question) -> Promise<string>` and `write(text)` so the prompts
110
+ * module never imports readline directly.
111
+ */
112
+ export function defaultSeam() {
113
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
114
+ return {
115
+ ask(question) {
116
+ return new Promise((res) => rl.question(question, (answer) => res(answer)));
117
+ },
118
+ write(text) {
119
+ process.stdout.write(text);
120
+ },
121
+ close() {
122
+ rl.close();
123
+ },
124
+ };
125
+ }
@@ -0,0 +1,142 @@
1
+ // runner.mjs — chokepoint for external effects.
2
+ //
3
+ // All filesystem writes for the scaffolder happen here. All process spawns
4
+ // (pnpm install, pa status, git init) happen here. Nothing else in the
5
+ // scaffolder reaches the disk or the OS.
6
+ //
7
+ // Tests inject a `{ spawn }` seam to verify framing without executing
8
+ // real subprocesses. The filesystem writes are exercised against a real
9
+ // tmp dir in the end-to-end test.
10
+
11
+ import { mkdir, readdir, stat, writeFile } from 'node:fs/promises';
12
+ import { spawn as realSpawn } from 'node:child_process';
13
+ import { dirname, join } from 'node:path';
14
+
15
+ /**
16
+ * Pre-flight: verify the target dir is either missing or empty. Throws
17
+ * with exitCode=1 on a clobber risk.
18
+ */
19
+ export async function assertTargetIsEmptyOrMissing(targetDir) {
20
+ let entries;
21
+ try {
22
+ entries = await readdir(targetDir);
23
+ } catch (err) {
24
+ if (err.code === 'ENOENT') return; // OK: we'll create it.
25
+ throw err;
26
+ }
27
+ const visible = entries.filter((name) => !name.startsWith('.DS_Store'));
28
+ if (visible.length > 0) {
29
+ const e = new Error(
30
+ `target directory is not empty: ${targetDir} (contains ${visible.length} entries)`,
31
+ );
32
+ e.exitCode = 1;
33
+ throw e;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Write the plan to disk under `targetDir`. Creates parent dirs as needed.
39
+ * Order matters: directories first, then files.
40
+ */
41
+ export async function writePlan(targetDir, plan) {
42
+ // Phase 1 — all directories. mkdir recursive is idempotent and creates
43
+ // parents.
44
+ for (const entry of plan) {
45
+ if (entry.kind !== 'dir') continue;
46
+ const abs = join(targetDir, entry.path);
47
+ await mkdir(abs, { recursive: true });
48
+ }
49
+
50
+ // Phase 2 — files. Each file's parent dir is guaranteed by mkdir on the
51
+ // parent if it isn't in the plan as an explicit 'dir' entry.
52
+ for (const entry of plan) {
53
+ if (entry.kind === 'dir') continue;
54
+ const abs = join(targetDir, entry.path);
55
+ await mkdir(dirname(abs), { recursive: true });
56
+ if (entry.kind === 'json') {
57
+ await writeFile(abs, JSON.stringify(entry.contents, null, 2) + '\n', 'utf8');
58
+ } else if (entry.kind === 'file') {
59
+ await writeFile(abs, entry.contents, 'utf8');
60
+ } else {
61
+ const e = new Error(`runner: unknown plan-entry kind '${entry.kind}' for ${entry.path}`);
62
+ e.exitCode = 2;
63
+ throw e;
64
+ }
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Run a subprocess and stream its stdio. Returns the exit code.
70
+ *
71
+ * `cwd` is the working directory of the spawned process. `env` extends the
72
+ * parent env. `seam.spawn` is the injection point for tests; defaults to
73
+ * `node:child_process.spawn`.
74
+ */
75
+ export async function runProcess({ cmd, args, cwd, env, seam }) {
76
+ const spawnFn = (seam && seam.spawn) || realSpawn;
77
+ return new Promise((resolve, reject) => {
78
+ const child = spawnFn(cmd, args, {
79
+ cwd,
80
+ env: { ...process.env, ...(env || {}) },
81
+ stdio: 'inherit',
82
+ });
83
+ child.on('error', (err) => {
84
+ const e = new Error(`spawn ${cmd}: ${err.message}`);
85
+ e.exitCode = 3;
86
+ reject(e);
87
+ });
88
+ child.on('close', (code) => resolve(code ?? 0));
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Initialise a git repo at `targetDir`. The operator-repo conventions
94
+ * assume a `.git` directory at the root (desk's repo-walker depends on it).
95
+ */
96
+ export async function initGitRepo({ targetDir, seam }) {
97
+ return runProcess({
98
+ cmd: 'git',
99
+ args: ['init', '--quiet', '--initial-branch=main'],
100
+ cwd: targetDir,
101
+ seam,
102
+ });
103
+ }
104
+
105
+ /**
106
+ * Install dependencies. Default uses pnpm; can be overridden via env or
107
+ * the seam for tests.
108
+ */
109
+ export async function installDeps({ targetDir, seam }) {
110
+ return runProcess({
111
+ cmd: process.env.PA_INIT_PKG_MANAGER || 'pnpm',
112
+ args: ['install'],
113
+ cwd: targetDir,
114
+ seam,
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Run `pa status` from inside the freshly-scaffolded repo. The exit code
120
+ * is the runner's signal to the operator that the instance booted.
121
+ */
122
+ export async function paStatus({ targetDir, seam }) {
123
+ return runProcess({
124
+ cmd: join(targetDir, 'node_modules', '.bin', 'pa'),
125
+ args: ['status'],
126
+ cwd: targetDir,
127
+ seam,
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Confirm a path exists and is a directory. Returns true/false; does not
133
+ * throw.
134
+ */
135
+ export async function isDirectory(path) {
136
+ try {
137
+ const s = await stat(path);
138
+ return s.isDirectory();
139
+ } catch {
140
+ return false;
141
+ }
142
+ }