@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,202 @@
1
+ // lock.mjs — shared per-resource process lock (public core API).
2
+ //
3
+ // Two runs of the same scheduled task on the same host MUST NOT overlap:
4
+ // a second concurrent run consumes the same inputs (staging areas, work
5
+ // queues) and produces duplicate effects and git races. This module is
6
+ // the platform-wide primitive for "at most one of me at a time" —
7
+ // consumed default-on by `pa launch`, adoptable by any direct-run tool.
8
+ //
9
+ // Implementation: a JSON PID file at a caller-chosen path. Contents:
10
+ // { "pid": 12345, "acquiredAt": "2026-07-06T12:00:00Z" }
11
+ //
12
+ // Staleness policy (two signals, in order):
13
+ // 1. Dead PID — primary. If the recorded process no longer exists, the
14
+ // lock is stale regardless of age and is broken immediately.
15
+ // 2. Age threshold — backstop. If the recorded process appears alive
16
+ // but the lock is older than `staleAfterMs`, it is broken anyway.
17
+ // This covers PID reuse (the recorded pid now names an unrelated
18
+ // process, so signal 1 reports "alive" forever) and hosts where
19
+ // process liveness cannot be determined reliably.
20
+ //
21
+ // Default threshold: 2 hours (DEFAULT_STALE_AFTER_MS). Rationale: the
22
+ // motivating consumers are scheduled agent sweeps whose legitimate
23
+ // runtimes are routinely tens of minutes (a supervised distillation
24
+ // sweep has been observed at 1288+ seconds); a short window (e.g. the
25
+ // 10 minutes the sync tool uses for its seconds-long git runs) would
26
+ // steal a live lock mid-sweep. 2 hours safely clears observed long runs
27
+ // while still self-healing a pid-reuse wedge within one-to-two ticks of
28
+ // a typical 4-hourly schedule. Callers with faster, well-bounded jobs
29
+ // SHOULD pass a tighter `staleAfterMs`; callers with longer legitimate
30
+ // runtimes MUST raise it.
31
+ //
32
+ // Unreadable lock files: a lock file that exists but cannot be parsed
33
+ // (crash mid-write, manual corruption) can never release itself and its
34
+ // pid cannot be checked. It is treated as held until its file mtime
35
+ // exceeds `staleAfterMs`, then broken. This keeps the age threshold as
36
+ // the universal backstop: no lock state can wedge a schedule forever.
37
+ //
38
+ // Contention contract: `acquire` returns `{ acquired: false, reason }`
39
+ // on live contention — it never throws for "someone else holds it".
40
+ // Callers decide the exit semantics (the platform convention for cron
41
+ // consumers is a logged skip + exit 0, matching sync's `noop-locked`).
42
+ //
43
+ // First-run path: neither the lock file nor its parent directory need
44
+ // exist. A missing directory is created on demand (write → ENOENT →
45
+ // mkdir -p → retry) so the empty state is the common case, not an error.
46
+ //
47
+ // Cross-platform: pure node stdlib. Liveness uses `process.kill(pid, 0)`
48
+ // which works on Windows as well as POSIX; tests inject
49
+ // `isProcessAlive` / `now` / `fs` so no real processes or clocks are
50
+ // needed (tooling standard §8 — dependency injection over mocking).
51
+
52
+ import { readFile, writeFile, unlink, stat, mkdir } from 'node:fs/promises';
53
+ import { dirname } from 'node:path';
54
+
55
+ export const DEFAULT_STALE_AFTER_MS = 2 * 60 * 60 * 1000; // 2 hours
56
+
57
+ /**
58
+ * Acquire a per-resource process lock.
59
+ *
60
+ * @param {string} lockPath Absolute path of the lock file. Convention:
61
+ * one path per protected resource (e.g. `.cache/launch/<taskId>.lock`).
62
+ * @param {object} [opts]
63
+ * @param {number} [opts.staleAfterMs] Age past which a lock is broken
64
+ * even if its pid looks alive. Default: DEFAULT_STALE_AFTER_MS (2 h).
65
+ * @param {() => number} [opts.now] Clock seam for tests.
66
+ * @param {number} [opts.pid] Owner pid recorded in the lock file.
67
+ * @param {(pid: number) => boolean} [opts.isProcessAlive] Liveness seam.
68
+ * @param {object} [opts.fs] fs seam ({ readFile, writeFile, unlink,
69
+ * stat, mkdir }) for tests.
70
+ * @returns {Promise<
71
+ * | { acquired: false, holder: { pid: number|null, acquiredAt: string|null }, reason: string }
72
+ * | { acquired: true, record: { pid: number, acquiredAt: string },
73
+ * stale: null | { pid: number|null, acquiredAt: string|null, reason: 'dead-pid'|'age-exceeded'|'unreadable' },
74
+ * release: () => Promise<void> }
75
+ * >}
76
+ */
77
+ export async function acquireLock(lockPath, {
78
+ staleAfterMs = DEFAULT_STALE_AFTER_MS,
79
+ now = () => Date.now(),
80
+ pid = process.pid,
81
+ isProcessAlive = defaultIsProcessAlive,
82
+ fs = defaultFs,
83
+ } = {}) {
84
+ // Read the existing lock, if any.
85
+ let raw = null;
86
+ try {
87
+ raw = await fs.readFile(lockPath, 'utf8');
88
+ } catch (err) {
89
+ if (err.code !== 'ENOENT') throw err;
90
+ // No lock file — proceed to acquire (the common first-run case).
91
+ }
92
+
93
+ let stale = null;
94
+ if (raw != null) {
95
+ let existing = null;
96
+ try {
97
+ existing = JSON.parse(raw);
98
+ } catch {
99
+ existing = null;
100
+ }
101
+
102
+ if (existing && Number.isFinite(existing.pid) && existing.acquiredAt) {
103
+ const ageMs = now() - new Date(existing.acquiredAt).getTime();
104
+ const alive = isProcessAlive(existing.pid);
105
+ if (alive && (!Number.isFinite(ageMs) || ageMs < staleAfterMs)) {
106
+ // Live lock — contention. (A non-finite age means a mangled
107
+ // timestamp; with a live pid we err on the side of holding.)
108
+ return {
109
+ acquired: false,
110
+ holder: { pid: existing.pid, acquiredAt: existing.acquiredAt },
111
+ reason: `held by pid ${existing.pid} since ${existing.acquiredAt}`,
112
+ };
113
+ }
114
+ stale = {
115
+ pid: existing.pid,
116
+ acquiredAt: existing.acquiredAt,
117
+ reason: alive ? 'age-exceeded' : 'dead-pid',
118
+ };
119
+ } else {
120
+ // Unparseable lock file — pid can't be checked, so the age
121
+ // threshold (via file mtime) is the only backstop.
122
+ let mtimeMs = null;
123
+ try {
124
+ mtimeMs = (await fs.stat(lockPath)).mtimeMs;
125
+ } catch (err) {
126
+ if (err.code !== 'ENOENT') throw err;
127
+ // Vanished between read and stat — treat as absent.
128
+ }
129
+ if (mtimeMs != null && now() - mtimeMs < staleAfterMs) {
130
+ return {
131
+ acquired: false,
132
+ holder: { pid: null, acquiredAt: null },
133
+ reason: `unreadable lock file at ${lockPath} — treating as held until stale`,
134
+ };
135
+ }
136
+ if (mtimeMs != null) {
137
+ stale = { pid: null, acquiredAt: null, reason: 'unreadable' };
138
+ }
139
+ }
140
+ }
141
+
142
+ const record = {
143
+ pid,
144
+ acquiredAt: new Date(now()).toISOString().replace(/\.\d+Z$/, 'Z'),
145
+ };
146
+ await writeWithMkdir(fs, lockPath, JSON.stringify(record));
147
+
148
+ return {
149
+ acquired: true,
150
+ record,
151
+ stale,
152
+ async release() {
153
+ try {
154
+ // Defensive: only remove if it's still ours. A long-running run
155
+ // whose lock went stale and was stolen by a later run must not
156
+ // delete the later run's lock when it finally exits.
157
+ const currentRaw = await fs.readFile(lockPath, 'utf8');
158
+ const current = JSON.parse(currentRaw);
159
+ if (current.pid === pid && current.acquiredAt === record.acquiredAt) {
160
+ await fs.unlink(lockPath);
161
+ }
162
+ } catch (err) {
163
+ if (err && err.code === 'ENOENT') return;
164
+ if (err instanceof SyntaxError) return; // clobbered — not ours to delete
165
+ throw err;
166
+ }
167
+ },
168
+ };
169
+ }
170
+
171
+ async function writeWithMkdir(fs, lockPath, content) {
172
+ try {
173
+ await fs.writeFile(lockPath, content);
174
+ } catch (err) {
175
+ if (err.code !== 'ENOENT') throw err;
176
+ // Parent directory missing — first run. Create it and retry once.
177
+ await fs.mkdir(dirname(lockPath), { recursive: true });
178
+ await fs.writeFile(lockPath, content);
179
+ }
180
+ }
181
+
182
+ function defaultIsProcessAlive(p) {
183
+ try {
184
+ // Signal 0 == "does this process exist and can we signal it?".
185
+ // Throws ESRCH if not. EPERM (someone else's process) also means
186
+ // alive — treated conservatively as alive.
187
+ process.kill(p, 0);
188
+ return true;
189
+ } catch (err) {
190
+ if (err.code === 'ESRCH') return false;
191
+ if (err.code === 'EPERM') return true;
192
+ return false;
193
+ }
194
+ }
195
+
196
+ const defaultFs = {
197
+ readFile,
198
+ writeFile,
199
+ unlink,
200
+ stat,
201
+ mkdir,
202
+ };
@@ -0,0 +1,12 @@
1
+ // cli-support.mjs — tiny shared primitives for CLI layer.
2
+
3
+ export class ExitError extends Error {
4
+ constructor(message, code) {
5
+ super(message);
6
+ this.exitCode = code;
7
+ }
8
+ }
9
+
10
+ export function log(msg) {
11
+ process.stderr.write(`[memory-sync] ${msg}\n`);
12
+ }
@@ -0,0 +1,56 @@
1
+ // cli.mjs — memory-sync command router.
2
+ //
3
+ // Preserves the pull/push verb contract from the shell implementation and
4
+ // maps domain errors to the standard exit-code ladder (see README §Exit codes).
5
+
6
+ import { pull } from './commands/pull.mjs';
7
+ import { push } from './commands/push.mjs';
8
+ import { ExitError, log } from './cli-support.mjs';
9
+
10
+ const USAGE = `Usage: memory-sync <pull|push|--help>
11
+ pull Mirror repo memory dir -> local Claude Code cache
12
+ push Mirror local Claude Code cache -> repo memory dir
13
+ -h, --help Show this help
14
+ Exit codes: 0 ok, 1 usage, 2 source dir missing, 3 io failure`;
15
+
16
+ export async function main(argv) {
17
+ const code = await run(argv);
18
+ process.exit(code ?? 0);
19
+ }
20
+
21
+ /**
22
+ * Exported for tests: returns an exit code rather than calling process.exit.
23
+ */
24
+ export async function run(argv, opts = {}) {
25
+ const mode = argv[0];
26
+ if (mode === '-h' || mode === '--help' || mode === 'help') {
27
+ process.stdout.write(USAGE + '\n');
28
+ return 0;
29
+ }
30
+ if (mode == null || mode === '') {
31
+ process.stderr.write(USAGE + '\n');
32
+ return 1;
33
+ }
34
+ if (mode !== 'pull' && mode !== 'push') {
35
+ log(`unknown verb: ${mode}`);
36
+ process.stderr.write(USAGE + '\n');
37
+ return 1;
38
+ }
39
+ if (argv.length > 1) {
40
+ log(`unexpected arguments: ${argv.slice(1).join(' ')}`);
41
+ process.stderr.write(USAGE + '\n');
42
+ return 1;
43
+ }
44
+
45
+ try {
46
+ if (mode === 'pull') return await pull(opts);
47
+ return await push(opts);
48
+ } catch (err) {
49
+ if (err instanceof ExitError) {
50
+ log(err.message);
51
+ return err.exitCode;
52
+ }
53
+ log(`internal error: ${err.message || err}`);
54
+ return 3;
55
+ }
56
+ }
@@ -0,0 +1,44 @@
1
+ // commands/pull.mjs — mirror repo memory dir -> local Claude Code cache.
2
+
3
+ import { stat } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { findRepoRoot, cacheDirFor } from '../repo.mjs';
6
+ import { mirror } from '../mirror.mjs';
7
+ import { ExitError, log } from '../cli-support.mjs';
8
+
9
+ async function isDir(p) {
10
+ try {
11
+ const s = await stat(p);
12
+ return s.isDirectory();
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ export async function pull({ cwd = process.cwd() } = {}) {
19
+ const { root, worktree } = await findRepoRoot(cwd);
20
+ if (!root) throw new ExitError('not inside a git repo', 2);
21
+ if (worktree) {
22
+ log('in a git worktree — skipping pull');
23
+ return 0;
24
+ }
25
+
26
+ const repoDir = join(root, 'operator', 'memory');
27
+ const cacheDir = cacheDirFor(root);
28
+
29
+ if (!(await isDir(repoDir))) {
30
+ throw new ExitError(`repo memory dir missing: ${repoDir}`, 2);
31
+ }
32
+ if (!(await isDir(cacheDir))) {
33
+ throw new ExitError(`local cache missing: ${cacheDir}`, 2);
34
+ }
35
+
36
+ let n;
37
+ try {
38
+ n = await mirror(repoDir, cacheDir);
39
+ } catch (err) {
40
+ throw new ExitError(`mirror failed: ${err.message || err}`, 3);
41
+ }
42
+ log(`pull: ${n} files mirrored from repo to cache`);
43
+ return 0;
44
+ }
@@ -0,0 +1,46 @@
1
+ // commands/push.mjs — mirror local Claude Code cache -> repo memory dir.
2
+
3
+ import { mkdir, stat } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { findRepoRoot, cacheDirFor } from '../repo.mjs';
6
+ import { mirror } from '../mirror.mjs';
7
+ import { ExitError, log } from '../cli-support.mjs';
8
+
9
+ async function isDir(p) {
10
+ try {
11
+ const s = await stat(p);
12
+ return s.isDirectory();
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ export async function push({ cwd = process.cwd() } = {}) {
19
+ const { root, worktree } = await findRepoRoot(cwd);
20
+ if (!root) throw new ExitError('not inside a git repo', 2);
21
+ if (worktree) {
22
+ log('in a git worktree — skipping push');
23
+ return 0;
24
+ }
25
+
26
+ const repoDir = join(root, 'operator', 'memory');
27
+ const cacheDir = cacheDirFor(root);
28
+
29
+ if (!(await isDir(cacheDir))) {
30
+ throw new ExitError(`local cache missing: ${cacheDir}`, 2);
31
+ }
32
+ try {
33
+ await mkdir(repoDir, { recursive: true });
34
+ } catch (err) {
35
+ throw new ExitError(`cannot create repo memory dir: ${err.message || err}`, 3);
36
+ }
37
+
38
+ let n;
39
+ try {
40
+ n = await mirror(cacheDir, repoDir);
41
+ } catch (err) {
42
+ throw new ExitError(`mirror failed: ${err.message || err}`, 3);
43
+ }
44
+ log(`push: ${n} files mirrored from cache to repo`);
45
+ return 0;
46
+ }
@@ -0,0 +1,114 @@
1
+ // mirror.mjs — recursive directory mirror with deletion semantics.
2
+ //
3
+ // This is the single chokepoint for filesystem mutation in the tool.
4
+ // `mirror(src, dst)` makes `dst` a byte-for-byte copy of `src`:
5
+ //
6
+ // 1. every regular file under `src` is copied to `dst` (mkdir -p as needed),
7
+ // preserving mtime where possible
8
+ // 2. every file under `dst` that does not exist under `src` is removed
9
+ // 3. empty directories under `dst` left behind after deletions are pruned
10
+ //
11
+ // Errors in this module map to the tool's exit code 3 (IO failure). The
12
+ // caller is responsible for verifying `src` exists — that is a different
13
+ // class of failure (exit 2) and lives in the command layer.
14
+
15
+ import { mkdir, readdir, copyFile, stat, utimes, rm, rmdir } from 'node:fs/promises';
16
+ import { join, dirname, relative } from 'node:path';
17
+
18
+ /**
19
+ * Recursively list every regular file under `root`, returning paths relative
20
+ * to `root` (always using forward slashes for stable comparison).
21
+ */
22
+ async function listFiles(root) {
23
+ const out = [];
24
+ async function walk(dir) {
25
+ let entries;
26
+ try {
27
+ entries = await readdir(dir, { withFileTypes: true });
28
+ } catch (err) {
29
+ if (err.code === 'ENOENT') return;
30
+ throw err;
31
+ }
32
+ for (const ent of entries) {
33
+ const full = join(dir, ent.name);
34
+ if (ent.isDirectory()) {
35
+ await walk(full);
36
+ } else if (ent.isFile()) {
37
+ out.push(relative(root, full).split('\\').join('/'));
38
+ }
39
+ // symlinks / sockets / etc. are intentionally skipped — matches the
40
+ // shell implementation which used `find -type f`
41
+ }
42
+ }
43
+ await walk(root);
44
+ return out;
45
+ }
46
+
47
+ /**
48
+ * Remove empty directories under `root` from the bottom up. The `root`
49
+ * itself is never removed.
50
+ */
51
+ async function pruneEmptyDirs(root) {
52
+ async function walk(dir) {
53
+ let entries;
54
+ try {
55
+ entries = await readdir(dir, { withFileTypes: true });
56
+ } catch (err) {
57
+ if (err.code === 'ENOENT') return;
58
+ throw err;
59
+ }
60
+ for (const ent of entries) {
61
+ if (ent.isDirectory()) await walk(join(dir, ent.name));
62
+ }
63
+ if (dir === root) return;
64
+ try {
65
+ const remaining = await readdir(dir);
66
+ if (remaining.length === 0) await rmdir(dir);
67
+ } catch (err) {
68
+ if (err.code !== 'ENOENT' && err.code !== 'ENOTEMPTY') throw err;
69
+ }
70
+ }
71
+ await walk(root);
72
+ }
73
+
74
+ /**
75
+ * Mirror src -> dst. See module header for full semantics.
76
+ * Returns the number of files present in `dst` after the mirror completes
77
+ * (which equals the count of files in `src`).
78
+ */
79
+ export async function mirror(src, dst) {
80
+ await mkdir(dst, { recursive: true });
81
+
82
+ const srcFiles = await listFiles(src);
83
+ const srcSet = new Set(srcFiles);
84
+
85
+ // Copy everything from src to dst. We copy unconditionally rather than
86
+ // comparing mtimes: the source wins, full stop. This matches `rsync -a
87
+ // --delete` behaviour the shell version used.
88
+ for (const rel of srcFiles) {
89
+ const from = join(src, rel);
90
+ const to = join(dst, rel);
91
+ await mkdir(dirname(to), { recursive: true });
92
+ await copyFile(from, to);
93
+ // Preserve mtime and atime from source so tooling that relies on
94
+ // timestamps (rsync's -a behaviour) stays consistent.
95
+ try {
96
+ const s = await stat(from);
97
+ await utimes(to, s.atime, s.mtime);
98
+ } catch {
99
+ // utimes can fail on some filesystems; not fatal.
100
+ }
101
+ }
102
+
103
+ // Delete anything in dst that isn't in src.
104
+ const dstFiles = await listFiles(dst);
105
+ for (const rel of dstFiles) {
106
+ if (!srcSet.has(rel)) {
107
+ await rm(join(dst, rel), { force: true });
108
+ }
109
+ }
110
+
111
+ await pruneEmptyDirs(dst);
112
+
113
+ return srcFiles.length;
114
+ }
@@ -0,0 +1,65 @@
1
+ // repo.mjs — repo root discovery and cache-dir derivation.
2
+ //
3
+ // Pure helpers, no I/O side effects beyond `stat`-class lookups. Mirrors the
4
+ // semantics of the original sync.sh:
5
+ //
6
+ // - findRepoRoot() walks up from a start dir looking for a `.git` directory.
7
+ // If it finds a `.git` *file* first (i.e. this is a worktree), it reports
8
+ // that via the `worktree` flag so the caller can skip cleanly.
9
+ // - cacheDirFor(repo) sanitises the repo path by replacing '/' with '-' and
10
+ // composes `$HOME/.claude/projects/<sanitised>/memory`. This matches the
11
+ // path Claude Code itself uses for its per-project memory cache.
12
+
13
+ import { stat } from 'node:fs/promises';
14
+ import { dirname, join } from 'node:path';
15
+ import { homedir } from 'node:os';
16
+
17
+ async function pathKind(p) {
18
+ try {
19
+ const s = await stat(p);
20
+ if (s.isDirectory()) return 'dir';
21
+ if (s.isFile()) return 'file';
22
+ return 'other';
23
+ } catch {
24
+ return 'missing';
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Walk up from `start` looking for a `.git` entry.
30
+ *
31
+ * Returns `{ root, worktree }` where:
32
+ * - `root` is the directory containing the `.git` entry (or null if none)
33
+ * - `worktree` is true when the `.git` entry is a regular file (git worktree)
34
+ * and false when it is a directory (primary checkout)
35
+ *
36
+ * If nothing is found before the filesystem root, returns `{ root: null, worktree: false }`.
37
+ */
38
+ export async function findRepoRoot(start) {
39
+ let d = start;
40
+ // Guard against infinite loops on malformed paths
41
+ while (d && d !== dirname(d)) {
42
+ const gitPath = join(d, '.git');
43
+ const kind = await pathKind(gitPath);
44
+ if (kind === 'dir') return { root: d, worktree: false };
45
+ if (kind === 'file') return { root: d, worktree: true };
46
+ d = dirname(d);
47
+ }
48
+ return { root: null, worktree: false };
49
+ }
50
+
51
+ /**
52
+ * Given a repository absolute path, return the path of the Claude Code
53
+ * per-project memory cache directory for that repo.
54
+ *
55
+ * The cache lives under `$HOME/.claude/projects/<sanitised>/memory` where
56
+ * `<sanitised>` is the repo path with every '/' (or platform separator)
57
+ * replaced by '-'. This matches Claude Code's own sanitisation.
58
+ */
59
+ export function cacheDirFor(repoPath, { home = homedir() } = {}) {
60
+ // Sanitise both '/' and '\\' to '-' so we produce a stable key regardless
61
+ // of the host platform's path-separator conventions. Claude Code itself
62
+ // uses this sanitisation to namespace per-project caches.
63
+ const sanitised = repoPath.replace(/[\\/]/g, '-');
64
+ return join(home, '.claude', 'projects', sanitised, 'memory');
65
+ }
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * detect.mjs -- Detect available scheduling backends
4
+ *
5
+ * Outputs a JSON report of OS type and available scheduling facilities.
6
+ * Used by the PA to determine which backend to offer during setup.
7
+ *
8
+ * Usage: node node_modules/@stravica-ai/engineering-pa-core/src/scheduler/detect.mjs
9
+ */
10
+
11
+ import { execSync } from 'node:child_process';
12
+ import { realpathSync } from 'node:fs';
13
+ import { resolve } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+
16
+ /**
17
+ * Check whether a command exists on this system.
18
+ * @param {string} cmd
19
+ * @returns {boolean}
20
+ */
21
+ function commandExists(cmd) {
22
+ try {
23
+ const check = process.platform === 'win32'
24
+ ? `where ${cmd} 2>nul`
25
+ : `command -v ${cmd} 2>/dev/null`;
26
+ execSync(check, { stdio: 'pipe' });
27
+ return true;
28
+ } catch {
29
+ return false;
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Detect OS and available scheduling backends.
35
+ * @returns {{ os: string, available: Array<{ backend: string, recommended: boolean, notes: string }> }}
36
+ */
37
+ export function detect() {
38
+ const os = process.platform === 'darwin' ? 'darwin'
39
+ : process.platform === 'linux' ? 'linux'
40
+ : process.platform === 'win32' ? 'win32'
41
+ : 'unknown';
42
+
43
+ const available = [];
44
+ let hasSystemd = false;
45
+
46
+ // launchd (macOS only)
47
+ if (os === 'darwin' && commandExists('launchctl')) {
48
+ available.push({ backend: 'launchd', recommended: true, notes: 'Native macOS, survives reboots' });
49
+ }
50
+
51
+ // systemd (Linux only)
52
+ if (os === 'linux' && commandExists('systemctl')) {
53
+ try {
54
+ execSync('systemctl --user status', { stdio: 'pipe' });
55
+ available.push({ backend: 'systemd', recommended: true, notes: 'Native Linux, survives reboots' });
56
+ hasSystemd = true;
57
+ } catch {
58
+ // systemctl exists but user-level not available — skip
59
+ }
60
+ }
61
+
62
+ // cron (macOS and Linux)
63
+ if (commandExists('crontab')) {
64
+ if (os === 'darwin') {
65
+ available.push({ backend: 'cron', recommended: false, notes: 'Available but launchd preferred on macOS' });
66
+ } else if (os === 'linux') {
67
+ if (hasSystemd) {
68
+ available.push({ backend: 'cron', recommended: false, notes: 'Available but systemd preferred on Linux' });
69
+ } else {
70
+ available.push({ backend: 'cron', recommended: true, notes: 'Classic scheduler, widely available' });
71
+ }
72
+ }
73
+ }
74
+
75
+ // schtasks (Windows)
76
+ if (os === 'win32' && commandExists('schtasks.exe')) {
77
+ available.push({ backend: 'schtasks', recommended: true, notes: 'Native Windows Task Scheduler' });
78
+ }
79
+
80
+ // Docker (any OS)
81
+ if (commandExists('docker')) {
82
+ try {
83
+ execSync('docker info', { stdio: 'pipe' });
84
+ available.push({ backend: 'docker', recommended: false, notes: 'Docker detected and running' });
85
+ } catch {
86
+ // docker exists but daemon not running
87
+ }
88
+ }
89
+
90
+ return { os, available };
91
+ }
92
+
93
+ // ── CLI entry point ──────────────────────────────────────────────────
94
+ //
95
+ // BUG 3 fix — see install.mjs for the full rationale: realpath both sides
96
+ // instead of the previous realpath-vs-symlink endsWith comparison, which
97
+ // silently no-op'd under pnpm.
98
+
99
+ function isDirectlyInvoked() {
100
+ if (!process.argv[1]) return false;
101
+ try {
102
+ return fileURLToPath(import.meta.url) === realpathSync(resolve(process.argv[1]));
103
+ } catch {
104
+ return false;
105
+ }
106
+ }
107
+
108
+ if (isDirectlyInvoked()) {
109
+ const result = detect();
110
+ process.stdout.write(JSON.stringify(result) + '\n');
111
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * host.mjs -- shared hostname normalisation + matching for schedule host
3
+ * enforcement.
4
+ *
5
+ * Same short-name-before-first-dot algorithm used by the estate's
6
+ * device-resolution tooling (operator/extensions/tools/library-sync's
7
+ * resolveDeviceId) — kept deliberately simple and dependency-free so this
8
+ * module has zero coupling to the registry data model: schedule host
9
+ * enforcement only needs "does this string match the current machine",
10
+ * not "which registry device am I".
11
+ */
12
+
13
+ import { hostname as osHostname } from 'node:os';
14
+
15
+ /** The portion of a hostname before its first "." — case preserved. */
16
+ export function shortHost(h) {
17
+ if (typeof h !== 'string' || h.length === 0) return '';
18
+ return h.split('.')[0];
19
+ }
20
+
21
+ /**
22
+ * Does `scheduleHost` (a schedule's declared `host` field) match the
23
+ * current machine? Absent/empty `scheduleHost` is treated as "no
24
+ * constraint declared" -> always matches (callers surface that as a
25
+ * warning, not a refusal — see evaluateHostGate in install.mjs).
26
+ *
27
+ * Match order: exact string equality, then short-name-before-first-dot
28
+ * equality (so `smarthome01` matches `smarthome01.local` and vice versa).
29
+ *
30
+ * @param {string|null|undefined} scheduleHost
31
+ * @param {string} [currentHost] - defaults to the real os.hostname()
32
+ * @returns {boolean}
33
+ */
34
+ export function hostMatches(scheduleHost, currentHost = osHostname()) {
35
+ if (scheduleHost == null || scheduleHost === '') return true;
36
+ if (scheduleHost === currentHost) return true;
37
+ return shortHost(scheduleHost) === shortHost(currentHost);
38
+ }