@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,237 @@
1
+ // inspector.mjs — orchestrates platform modules to produce inspection.json.
2
+ // READ-ONLY: never writes anything (cli.mjs handles --out file writes).
3
+
4
+ import os from 'node:os';
5
+ import { readFile, readdir, stat } from 'node:fs/promises';
6
+ import { homedir } from 'node:os';
7
+ import { join, resolve } from 'node:path';
8
+
9
+ /**
10
+ * Run a full host inspection.
11
+ * @param {object} opts
12
+ * @param {string|null} [opts.daveRoot]
13
+ * @param {object} runner - injected runner (spawnCapture, readPlist)
14
+ * @returns {Promise<object>} inspection result
15
+ */
16
+ export async function inspect(opts = {}, runner) {
17
+ const [host, runtimes, platformData, daveEnvData] = await Promise.all([
18
+ _collectHost(),
19
+ _collectRuntimes(runner),
20
+ _collectPlatformData(runner),
21
+ Promise.resolve(_collectDaveEnv(opts)),
22
+ ]);
23
+
24
+ const daveRoot = daveEnvData.daveRoot;
25
+ const [paths, extensions, ageKey] = await Promise.all([
26
+ _collectPaths(daveRoot),
27
+ _collectExtensions(daveRoot),
28
+ _collectAgeKey(opts),
29
+ ]);
30
+
31
+ return {
32
+ host,
33
+ runtimes,
34
+ scheduled: platformData.scheduled,
35
+ mounts: platformData.mounts,
36
+ daveEnv: daveEnvData,
37
+ paths,
38
+ extensions,
39
+ ageKey,
40
+ inspectedAt: new Date().toISOString(),
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Collect age private key locations. Reports v1 (`~/.config/dave/age-key.txt`)
46
+ * and v2 (`~/.config/stravica-pa/age-key.txt`) so callers can plan or perform
47
+ * a migration. Pure file existence checks — never reads key contents.
48
+ *
49
+ * @param {object} opts — may include { home } for tests.
50
+ * @returns {Promise<{v1Path: string, v1Exists: boolean, v2Path: string,
51
+ * v2Exists: boolean, recommendation: string}>}
52
+ */
53
+ export async function _collectAgeKey(opts = {}) {
54
+ const home = opts.home ?? homedir();
55
+ const v1Path = join(home, '.config', 'dave', 'age-key.txt');
56
+ const v2Path = join(home, '.config', 'stravica-pa', 'age-key.txt');
57
+ const [v1Exists, v2Exists] = await Promise.all([_exists(v1Path), _exists(v2Path)]);
58
+
59
+ // Recommendation matrix:
60
+ // v1 only -> migrate (copy v1 -> v2, keep v1 as one-session safety net)
61
+ // v2 only -> ok (canonical location populated)
62
+ // both -> v2-already-populated (no action needed; v1 can be removed later)
63
+ // neither -> none-present (host genuinely has no key — e.g. smarthome01)
64
+ let recommendation;
65
+ if (v1Exists && !v2Exists) recommendation = 'migrate';
66
+ else if (!v1Exists && v2Exists) recommendation = 'ok';
67
+ else if (v1Exists && v2Exists) recommendation = 'v2-already-populated';
68
+ else recommendation = 'none-present';
69
+
70
+ return { v1Path, v1Exists, v2Path, v2Exists, recommendation };
71
+ }
72
+
73
+ function _collectHost() {
74
+ return {
75
+ hostname: os.hostname(),
76
+ os: process.platform,
77
+ osVersion: os.release(),
78
+ arch: process.arch,
79
+ };
80
+ }
81
+
82
+ async function _collectRuntimes(runner) {
83
+ const [nodeRes, pnpmRes, gitRes] = await Promise.all([
84
+ runner.spawnCapture('node', ['--version']),
85
+ runner.spawnCapture('pnpm', ['--version']),
86
+ runner.spawnCapture('git', ['--version']),
87
+ ]);
88
+
89
+ function parseVersion(res, pattern) {
90
+ if (res.exitCode !== 0) return null;
91
+ const m = res.stdout.trim().match(pattern);
92
+ return m ? m[1] : (res.stdout.trim() || null);
93
+ }
94
+
95
+ return {
96
+ nodeVersion: parseVersion(nodeRes, /v?(\d+\.\d+\.\d+)/),
97
+ pnpmVersion: parseVersion(pnpmRes, /(\d+\.\d+\.\d+)/),
98
+ gitVersion: parseVersion(gitRes, /git version (.+)/),
99
+ };
100
+ }
101
+
102
+ async function _collectPlatformData(runner) {
103
+ const platform = process.platform;
104
+ if (platform === 'darwin') {
105
+ const { collectScheduled, collectMounts } = await import('./platforms/macos.mjs');
106
+ const [scheduled, mounts] = await Promise.all([
107
+ collectScheduled(runner),
108
+ collectMounts(runner),
109
+ ]);
110
+ return { scheduled, mounts };
111
+ } else if (platform === 'linux') {
112
+ const { collectScheduled, collectMounts } = await import('./platforms/linux.mjs');
113
+ const [scheduled, mounts] = await Promise.all([
114
+ collectScheduled(runner),
115
+ collectMounts(runner),
116
+ ]);
117
+ return { scheduled, mounts };
118
+ }
119
+ return {
120
+ scheduled: { crontab: [], launchdPlists: [] },
121
+ mounts: [],
122
+ };
123
+ }
124
+
125
+ function _collectDaveEnv(opts) {
126
+ const daveRoot =
127
+ opts.daveRoot ||
128
+ process.env.DAVE_ROOT ||
129
+ process.env.PA_REPO_ROOT ||
130
+ null;
131
+
132
+ const daveRelevantEnvVars = {};
133
+ for (const [key, val] of Object.entries(process.env)) {
134
+ if (key.startsWith('DAVE_') || key === 'PA_REPO_ROOT') {
135
+ daveRelevantEnvVars[key] = val;
136
+ }
137
+ }
138
+
139
+ const pathSegments = [];
140
+ if (daveRoot && process.env.PATH) {
141
+ const resolved = resolve(daveRoot);
142
+ for (const seg of process.env.PATH.split(':')) {
143
+ if (seg.startsWith(resolved)) {
144
+ pathSegments.push(seg);
145
+ }
146
+ }
147
+ }
148
+
149
+ return { daveRoot, daveRelevantEnvVars, pathSegments };
150
+ }
151
+
152
+ async function _collectPaths(daveRoot) {
153
+ if (!daveRoot) {
154
+ return {
155
+ daveRoot: null,
156
+ coreToolsPath: null,
157
+ operatorPath: null,
158
+ layoutVersion: 'unknown',
159
+ };
160
+ }
161
+
162
+ const coreToolsPath = join(daveRoot, 'core', 'tools');
163
+ const operatorPath = join(daveRoot, 'operator');
164
+ const pkgJsonPath = join(daveRoot, 'package.json');
165
+
166
+ const [hasCoreTools, hasOperator, hasPkgJson] = await Promise.all([
167
+ _exists(coreToolsPath),
168
+ _exists(operatorPath),
169
+ _exists(pkgJsonPath),
170
+ ]);
171
+
172
+ let layoutVersion = 'unknown';
173
+ if (hasCoreTools) {
174
+ layoutVersion = 'v1';
175
+ } else if (hasPkgJson) {
176
+ layoutVersion = 'v2';
177
+ }
178
+
179
+ return {
180
+ daveRoot,
181
+ coreToolsPath: hasCoreTools ? coreToolsPath : null,
182
+ operatorPath: hasOperator ? operatorPath : null,
183
+ layoutVersion,
184
+ };
185
+ }
186
+
187
+ async function _collectExtensions(daveRoot) {
188
+ if (!daveRoot) return [];
189
+
190
+ const extensionsToolsDir = join(daveRoot, 'operator', 'extensions', 'tools');
191
+ let entries;
192
+ try {
193
+ entries = await readdir(extensionsToolsDir);
194
+ } catch {
195
+ return [];
196
+ }
197
+
198
+ // Manifest may exist at operator/extensions/manifest.json. Accept array of
199
+ // { name } / { id } / plain strings, or object keyed by extension name.
200
+ let manifestNames = null;
201
+ try {
202
+ const manifestPath = join(daveRoot, 'operator', 'extensions', 'manifest.json');
203
+ const raw = await readFile(manifestPath, 'utf8');
204
+ const parsed = JSON.parse(raw);
205
+ if (Array.isArray(parsed)) {
206
+ manifestNames = new Set(parsed.map(e => (typeof e === 'string' ? e : e.name || e.id)).filter(Boolean));
207
+ } else if (parsed && typeof parsed === 'object') {
208
+ manifestNames = new Set(Object.keys(parsed));
209
+ }
210
+ } catch {
211
+ // no manifest — leave hasManifestEntry as false for all entries
212
+ }
213
+
214
+ const extensions = [];
215
+ for (const name of entries) {
216
+ const path = join(extensionsToolsDir, name);
217
+ let isDir = false;
218
+ try {
219
+ isDir = (await stat(path)).isDirectory();
220
+ } catch {
221
+ continue;
222
+ }
223
+ if (!isDir) continue;
224
+ const hasManifestEntry = manifestNames ? manifestNames.has(name) : false;
225
+ extensions.push({ name, path, hasManifestEntry });
226
+ }
227
+ return extensions;
228
+ }
229
+
230
+ async function _exists(p) {
231
+ try {
232
+ await stat(p);
233
+ return true;
234
+ } catch {
235
+ return false;
236
+ }
237
+ }
@@ -0,0 +1,87 @@
1
+ // linux.mjs — pure Linux-specific collectors.
2
+ // All spawning is done via the injected runner for testability.
3
+
4
+ import { readdir, readFile } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+
7
+ /**
8
+ * Collect scheduled tasks: crontab entries + (empty) launchdPlists.
9
+ * @param {import('../runner.mjs')} runner
10
+ * @returns {Promise<{crontab: Array<{schedule: string, command: string}>, launchdPlists: []}>}
11
+ */
12
+ export async function collectScheduled(runner) {
13
+ const crontab = await _collectCrontab(runner);
14
+ const cronD = await _collectCronD();
15
+ return { crontab: [...crontab, ...cronD], launchdPlists: [] };
16
+ }
17
+
18
+ async function _collectCrontab(runner) {
19
+ const { stdout, exitCode } = await runner.spawnCapture('crontab', ['-l']);
20
+ if (exitCode !== 0) return [];
21
+ return _parseCrontab(stdout);
22
+ }
23
+
24
+ async function _collectCronD() {
25
+ const cronDDir = '/etc/cron.d';
26
+ let files;
27
+ try {
28
+ files = await readdir(cronDDir);
29
+ } catch (err) {
30
+ if (err.code === 'ENOENT' || err.code === 'EACCES') return [];
31
+ return [];
32
+ }
33
+ const entries = [];
34
+ for (const file of files) {
35
+ try {
36
+ const content = await readFile(join(cronDDir, file), 'utf8');
37
+ const parsed = _parseCrontab(content);
38
+ entries.push(...parsed);
39
+ } catch (err) {
40
+ if (err.code === 'ENOENT' || err.code === 'EACCES') continue;
41
+ // skip other errors silently
42
+ }
43
+ }
44
+ return entries;
45
+ }
46
+
47
+ function _parseCrontab(text) {
48
+ const entries = [];
49
+ for (const line of text.split('\n')) {
50
+ const trimmed = line.trim();
51
+ if (!trimmed || trimmed.startsWith('#')) continue;
52
+ // skip env var lines (e.g. MAILTO=...)
53
+ if (trimmed.match(/^\w+=\S*/)) continue;
54
+ const parts = trimmed.split(/\s+/);
55
+ if (parts.length < 6) continue;
56
+ const schedule = parts.slice(0, 5).join(' ');
57
+ const command = parts.slice(5).join(' ');
58
+ entries.push({ schedule, command });
59
+ }
60
+ return entries;
61
+ }
62
+
63
+ /**
64
+ * Collect network/remote mounts from mount output.
65
+ * @param {import('../runner.mjs')} runner
66
+ * @returns {Promise<Array<{source: string, target: string, fsType: string}>>}
67
+ */
68
+ export async function collectMounts(runner) {
69
+ const { stdout } = await runner.spawnCapture('mount', []);
70
+ const REMOTE_FS_TYPES = new Set(['nfs', 'cifs', 'smbfs', 'fuse', 'nfs4', 'ceph', 'glusterfs']);
71
+ const mounts = [];
72
+ for (const line of stdout.split('\n')) {
73
+ const trimmed = line.trim();
74
+ if (!trimmed) continue;
75
+ // Linux mount format: source on target type fstype (options)
76
+ const m = trimmed.match(/^(\S+)\s+on\s+(\S+)\s+type\s+(\S+)/);
77
+ if (!m) continue;
78
+ const [, source, target, fsType] = m;
79
+ const isRemote =
80
+ REMOTE_FS_TYPES.has(fsType) ||
81
+ source.includes('//') ||
82
+ source.match(/\S+:\//);
83
+ if (!isRemote) continue;
84
+ mounts.push({ source, target, fsType });
85
+ }
86
+ return mounts;
87
+ }
@@ -0,0 +1,105 @@
1
+ // macos.mjs — pure macOS-specific collectors.
2
+ // All spawning is done via the injected runner for testability.
3
+
4
+ import os from 'node:os';
5
+ import { readdir } from 'node:fs/promises';
6
+ import { join } from 'node:path';
7
+
8
+ /**
9
+ * Collect scheduled tasks: crontab entries + launchd plists.
10
+ * @param {import('../runner.mjs')} runner
11
+ * @returns {Promise<{crontab: Array<{schedule: string, command: string}>, launchdPlists: Array<object>}>}
12
+ */
13
+ export async function collectScheduled(runner) {
14
+ const crontab = await _collectCrontab(runner);
15
+ const launchdPlists = await _collectLaunchdPlists(runner);
16
+ return { crontab, launchdPlists };
17
+ }
18
+
19
+ async function _collectCrontab(runner) {
20
+ const { stdout, exitCode } = await runner.spawnCapture('crontab', ['-l']);
21
+ if (exitCode !== 0) return [];
22
+ return _parseCrontab(stdout);
23
+ }
24
+
25
+ function _parseCrontab(text) {
26
+ const entries = [];
27
+ for (const line of text.split('\n')) {
28
+ const trimmed = line.trim();
29
+ if (!trimmed || trimmed.startsWith('#')) continue;
30
+ // first 5 fields = schedule, rest = command
31
+ const parts = trimmed.split(/\s+/);
32
+ if (parts.length < 6) continue;
33
+ const schedule = parts.slice(0, 5).join(' ');
34
+ const command = parts.slice(5).join(' ');
35
+ entries.push({ schedule, command });
36
+ }
37
+ return entries;
38
+ }
39
+
40
+ async function _collectLaunchdPlists(runner) {
41
+ const launchAgentsDir = join(os.homedir(), 'Library', 'LaunchAgents');
42
+ let files;
43
+ try {
44
+ files = await readdir(launchAgentsDir);
45
+ } catch {
46
+ return [];
47
+ }
48
+ const plists = [];
49
+ for (const file of files) {
50
+ if (!file.endsWith('.plist')) continue;
51
+ const filePath = join(launchAgentsDir, file);
52
+ try {
53
+ const parsed = await runner.readPlist(filePath);
54
+ plists.push(parsed);
55
+ } catch {
56
+ // skip unreadable plists
57
+ }
58
+ }
59
+ return plists;
60
+ }
61
+
62
+ /**
63
+ * Collect network/remote mounts from mount output.
64
+ * @param {import('../runner.mjs')} runner
65
+ * @returns {Promise<Array<{source: string, target: string, fsType: string}>>}
66
+ */
67
+ export async function collectMounts(runner) {
68
+ const { stdout } = await runner.spawnCapture('mount', []);
69
+ const mounts = [];
70
+ for (const line of stdout.split('\n')) {
71
+ const trimmed = line.trim();
72
+ if (!trimmed) continue;
73
+ // macOS mount format: source on target (type, options)
74
+ // or: //user@host/share on /mount/point (type smbfs, ...)
75
+ const typeMatch = trimmed.match(/\(type\s+(\S+)/);
76
+ const fsType = typeMatch ? typeMatch[1].replace(',', '') : '';
77
+ const isRemote =
78
+ fsType === 'nfs' ||
79
+ fsType === 'smbfs' ||
80
+ fsType === 'afpfs' ||
81
+ trimmed.includes('//') ||
82
+ trimmed.match(/\S+:\//);
83
+ if (!isRemote) continue;
84
+ // parse: source on target (...)
85
+ const onMatch = trimmed.match(/^(.+?)\s+on\s+(.+?)\s+\(/);
86
+ if (!onMatch) continue;
87
+ mounts.push({
88
+ source: onMatch[1].trim(),
89
+ target: onMatch[2].trim(),
90
+ fsType: fsType || 'unknown',
91
+ });
92
+ }
93
+ return mounts;
94
+ }
95
+
96
+ /**
97
+ * Detect chip architecture and total memory.
98
+ * @returns {{ chip: string, memory: number }}
99
+ */
100
+ export function detectChipAndMemory() {
101
+ return {
102
+ chip: process.arch,
103
+ memory: os.totalmem(),
104
+ };
105
+ }
@@ -0,0 +1,87 @@
1
+ // runner.mjs — CHOKEPOINT: all subprocess spawning happens here.
2
+ // Zero external deps; Node stdlib only.
3
+
4
+ import { spawn } from 'node:child_process';
5
+ import { readFile } from 'node:fs/promises';
6
+
7
+ /**
8
+ * Spawns a process and captures stdout + stderr.
9
+ * Returns { stdout, stderr, exitCode }.
10
+ * Never throws unless spawn itself fails (in which case exitCode=3).
11
+ *
12
+ * @param {string} cmd
13
+ * @param {string[]} args
14
+ * @returns {Promise<{stdout: string, stderr: string, exitCode: number}>}
15
+ */
16
+ export async function spawnCapture(cmd, args) {
17
+ return new Promise((resolve) => {
18
+ let stdout = '';
19
+ let stderr = '';
20
+ let child;
21
+ try {
22
+ child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] });
23
+ } catch (err) {
24
+ resolve({ stdout: '', stderr: err.message, exitCode: 3 });
25
+ return;
26
+ }
27
+
28
+ child.stdout.on('data', (chunk) => { stdout += chunk; });
29
+ child.stderr.on('data', (chunk) => { stderr += chunk; });
30
+
31
+ child.on('error', (err) => {
32
+ resolve({ stdout, stderr: stderr + err.message, exitCode: 3 });
33
+ });
34
+
35
+ child.on('close', (code) => {
36
+ resolve({ stdout, stderr, exitCode: code ?? 0 });
37
+ });
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Reads a macOS plist XML file and parses the relevant fields.
43
+ * Handles only the specific keys we need: Label, Program, ProgramArguments,
44
+ * WorkingDirectory, RunAtLoad, KeepAlive.
45
+ *
46
+ * @param {string} filePath
47
+ * @returns {Promise<{label: string|null, program: string|null, args: string[]|null, workingDirectory: string|null, runAtLoad: boolean, keepAlive: boolean}>}
48
+ */
49
+ export async function readPlist(filePath) {
50
+ const xml = await readFile(filePath, 'utf8');
51
+
52
+ function extractStringAfterKey(key) {
53
+ // Matches <key>KeyName</key>\s*<string>value</string>
54
+ const re = new RegExp(`<key>${key}</key>\\s*<string>([^<]*)</string>`, 's');
55
+ const m = xml.match(re);
56
+ return m ? m[1].trim() : null;
57
+ }
58
+
59
+ function extractBoolAfterKey(key) {
60
+ const re = new RegExp(`<key>${key}</key>\\s*<(true|false)\\s*/>`, 's');
61
+ const m = xml.match(re);
62
+ if (!m) return false;
63
+ return m[1] === 'true';
64
+ }
65
+
66
+ function extractArrayAfterKey(key) {
67
+ const re = new RegExp(`<key>${key}</key>\\s*<array>(.*?)</array>`, 's');
68
+ const m = xml.match(re);
69
+ if (!m) return null;
70
+ const strings = [];
71
+ const strRe = /<string>([^<]*)<\/string>/g;
72
+ let sm;
73
+ while ((sm = strRe.exec(m[1])) !== null) {
74
+ strings.push(sm[1].trim());
75
+ }
76
+ return strings;
77
+ }
78
+
79
+ return {
80
+ label: extractStringAfterKey('Label'),
81
+ program: extractStringAfterKey('Program'),
82
+ args: extractArrayAfterKey('ProgramArguments'),
83
+ workingDirectory: extractStringAfterKey('WorkingDirectory'),
84
+ runAtLoad: extractBoolAfterKey('RunAtLoad'),
85
+ keepAlive: extractBoolAfterKey('KeepAlive'),
86
+ };
87
+ }
@@ -0,0 +1,103 @@
1
+ # @stravica-ai/engineering-pa-runbook
2
+
3
+ Migration runbook JSON Schema, reference runbooks, and structural validator.
4
+
5
+ ## What the runbook schema is for
6
+
7
+ A migration runbook is a structured, machine-readable document that describes the sequence of steps needed to migrate a Dave PA instance from v1 (fork layout) to v2 (operator-repo layout). Each step has a declared `intent`, optional `params`, optional `preconditions`, expected outcomes (`expect`), and a `rollback` spec.
8
+
9
+ Runbooks are designed to be executed by an autonomous agent that reads `inspection.json` (output of `pa migrate-inspect`), substitutes template variables, and dispatches each step to host-specific actuators.
10
+
11
+ ## Intent vocabulary
12
+
13
+ | Intent | Description |
14
+ |---|---|
15
+ | `snapshot-v1-fork` | Preserve the v1 fork as a rollback point (git tag or directory copy) before migration begins |
16
+ | `run-migrate-to-v2` | Invoke the `migrate-to-v2` CLI to produce the v2 operator-repo layout from the v1 fork |
17
+ | `rewrite-crontab` | Rewrite one or more crontab entries to use the new v2 instance working directory |
18
+ | `drop-cron-entry` | Remove a specific named cron entry (e.g. `upstream-triage`) that is not carried forward to v2 |
19
+ | `update-host-service` | Update a service/daemon entry — launchd plist on macOS, systemd unit on Linux |
20
+ | `verify-sync-continuity` | Verify sync jobs run and reach their destinations (e.g. NFS/SMB mount reachable, sync exits 0) |
21
+ | `verify-boot-health` | Run `pa status` in the v2 instance and assert exit 0 |
22
+ | `define-roster-entry` | Add or update a host entry in the operator roster, recording layout version and daveRoot |
23
+
24
+ ## The inspect → runbook → execute flow
25
+
26
+ ```
27
+ 1. pa migrate-inspect --out inspection-<host>.json
28
+ Produces a structured snapshot of host state: layout version, runtimes,
29
+ crontab entries, launchd plists, mounts, env vars, extensions.
30
+
31
+ 2. Agent reads inspection-<host>.json and selects the appropriate reference
32
+ runbook from reference/ (or a custom runbook), substituting {{daveRoot}}
33
+ and {{inspectedAt}} template variables.
34
+
35
+ 3. Agent executes each step sequentially:
36
+ - Evaluates preconditions against inspection data or live host state
37
+ - Dispatches the intent to a host-specific actuator
38
+ - Checks expect outcomes
39
+ - On failure: triggers rollback action
40
+
41
+ 4. On completion: define-roster-entry registers the host as v2.
42
+ ```
43
+
44
+ ## Executing-agent contract
45
+
46
+ When an agent interprets a runbook step, it maps `intent` to host-specific commands as follows:
47
+
48
+ | Intent | macOS | Linux |
49
+ |---|---|---|
50
+ | `snapshot-v1-fork` | `git tag` or `cp -r` | Same |
51
+ | `run-migrate-to-v2` | `node migrate-to-v2/cli.mjs` | Same |
52
+ | `rewrite-crontab` | `crontab -l` + edit + `crontab -` | Same |
53
+ | `drop-cron-entry` | `crontab -l` + filter + `crontab -` | Same |
54
+ | `update-host-service` | Edit `~/Library/LaunchAgents/<label>.plist`, `launchctl unload/load` | Edit `/etc/systemd/system/<unit>.service`, `systemctl daemon-reload && restart` |
55
+ | `verify-sync-continuity` | Check mount reachable, run sync script | Same |
56
+ | `verify-boot-health` | `pa status` in `daveRoot` | Same |
57
+ | `define-roster-entry` | Write to `operator/roster.json` | Same |
58
+
59
+ The agent reads the `host.os` field from `inspection.json` to determine which platform-specific path to take for `update-host-service`. All other intents are platform-neutral.
60
+
61
+ ## Schema reference
62
+
63
+ - `schema/runbook.schema.json` — JSON Schema 2020-12 for runbook documents
64
+ - `schema/inspection.schema.json` — JSON Schema 2020-12 for inspection.json output
65
+
66
+ ## How to validate a runbook
67
+
68
+ ```js
69
+ import { validate } from '@stravica-ai/engineering-pa-runbook/src/validator.mjs';
70
+ import { readFileSync } from 'node:fs';
71
+
72
+ const schema = JSON.parse(readFileSync('./schema/runbook.schema.json', 'utf8'));
73
+ const runbook = JSON.parse(readFileSync('./reference/hq-dave.runbook.json', 'utf8'));
74
+ const result = validate(schema, runbook);
75
+ if (!result.valid) {
76
+ console.error(result.errors);
77
+ }
78
+ ```
79
+
80
+ Or from the CLI:
81
+
82
+ ```bash
83
+ node -e "
84
+ import('./src/validator.mjs').then(async ({validate}) => {
85
+ const {readFile} = await import('node:fs/promises');
86
+ const schema = JSON.parse(await readFile('./schema/runbook.schema.json','utf8'));
87
+ const rb = JSON.parse(await readFile('./reference/hq-dave.runbook.json','utf8'));
88
+ const r = validate(schema, rb);
89
+ console.log(r.valid ? 'VALID' : 'INVALID: ' + r.errors.join('; '));
90
+ });
91
+ "
92
+ ```
93
+
94
+ ## Tests
95
+
96
+ ```bash
97
+ pnpm --filter @stravica-ai/engineering-pa-runbook test
98
+ # or
99
+ node --test 'test/**/*.test.mjs'
100
+ ```
101
+
102
+ - `test/validator.test.mjs` — unit tests for the structural validator
103
+ - `test/runbooks.test.mjs` — validates all 3 reference runbooks against the schema
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@stravica-ai/engineering-pa-runbook",
3
+ "version": "0.1.1",
4
+ "description": "Migration runbook JSON Schema, reference runbooks, and structural validator.",
5
+ "type": "module",
6
+ "main": "./src/validator.mjs",
7
+ "exports": {
8
+ ".": "./src/validator.mjs",
9
+ "./src/*": "./src/*"
10
+ },
11
+ "private": true,
12
+ "license": "MIT",
13
+ "engines": { "node": ">=24" },
14
+ "scripts": {
15
+ "test": "node --test --test-isolation=none 'test/**/*.test.mjs'"
16
+ },
17
+ "//": "test pins --test-isolation=none: default process-isolation mode silently undercounts (drops passed tests from totals) when a test awaits async work while stdout is patched by a capture() helper — see packages/cli/test/secrets. Counts must never lie."
18
+ }