@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,230 @@
1
+ // distill — orchestrator.
2
+ //
3
+ // Flow:
4
+ // 1. resolvePrereqs (repo root, paName, librarian def, claude on PATH)
5
+ // 2. countStagedObservations
6
+ // 3. invoke claude
7
+ // 4. append runs.jsonl (audit record)
8
+ //
9
+ // **Git is handled by core/tools/sync, not by distill.** Distill writes
10
+ // promotion outputs into the working tree and stops; the per-host sync
11
+ // tool (running every 30 minutes) picks them up, composes a commit body
12
+ // from each cron tool's runs.jsonl, and does the pull / commit / push.
13
+ //
14
+ // Prior to 2026-05-11 this tool also did `git fetch + pull --rebase` as
15
+ // a pre-flight, then `git add / commit / push` as a post-flight. That
16
+ // design raced against the other cron tools (`wsd-ingest` every 15m,
17
+ // `upstream-triage` every 30m) which were doing the same thing on their
18
+ // own clocks — a 2-second window between `isTreeClean` and `pull --rebase`
19
+ // was enough to trip `fail-rebase` and `fail-dirty-tree` audits 12 times
20
+ // in late April. Stripping git from the cron writers and centralising it
21
+ // in `core/tools/sync` is the structural fix.
22
+ //
23
+ // Exit codes (§6 tooling standard):
24
+ // 0 ok | 1 usage | 2 prerequisite missing | 3 claude transport failure
25
+ //
26
+ // Audit-record contract (runs.jsonl):
27
+ // Every non-dry-run invocation appends exactly one JSON line. The record
28
+ // carries both `exitCode` (for scripts / cron) and `status` (semantic,
29
+ // for monitors). Status vocabulary is closed — consumers may match on
30
+ // prefixes (`noop-*` = healthy, `fail-*` = operational failure, `ok` =
31
+ // successful distillation). `reason` is a human-readable one-liner,
32
+ // omitted on `ok`. `log` is null on exits that happened before the
33
+ // per-run log file was created.
34
+
35
+ import { appendFile, readFile } from 'node:fs/promises';
36
+ import { basename, join } from 'node:path';
37
+
38
+ import { resolvePrereqs } from './prereqs.mjs';
39
+ import { countStagedObservations } from './staging.mjs';
40
+ import { DISTILL_PROMPT, claudeArgs } from './prompt.mjs';
41
+
42
+ function log(line, { stderr = process.stderr } = {}) {
43
+ stderr.write(`[distill] ${line}\n`);
44
+ }
45
+
46
+ function opError(msg, code = 2) {
47
+ const err = new Error(msg);
48
+ err.distillExitCode = code;
49
+ return err;
50
+ }
51
+
52
+ // Append a one-line audit record to runs.jsonl. Errors writing the
53
+ // audit record are swallowed with a stderr note — losing the audit
54
+ // line is less bad than masking the real exit code of the sweep.
55
+ async function writeAudit(runsLog, record, stderr) {
56
+ try {
57
+ await appendFile(runsLog, JSON.stringify(record) + '\n');
58
+ } catch (e) {
59
+ log(`warning: failed to append audit record to ${runsLog}: ${e.message}`, { stderr });
60
+ }
61
+ }
62
+
63
+ // Composition joiner for the bundled-definition + operator-supplement
64
+ // resolution outcome. Pure string building — the precedence statement is
65
+ // part of the composed system prompt, so the librarian session knows the
66
+ // supplement wins wherever it conflicts with the bundled text.
67
+ export function composeLibrarianDef(bundledContent, supplementContent) {
68
+ return (
69
+ bundledContent.replace(/\s+$/, '') +
70
+ '\n\n---\n\n' +
71
+ '# Operator supplement\n\n' +
72
+ 'Everything below this line is the operator\'s supplement to the bundled\n' +
73
+ 'Librarian definition above. **Wherever the supplement conflicts with the\n' +
74
+ 'bundled definition, the supplement takes precedence — follow the\n' +
75
+ 'supplement.** Where the supplement is silent, the bundled definition\n' +
76
+ 'applies unchanged.\n\n' +
77
+ supplementContent.replace(/\s+$/, '') +
78
+ '\n'
79
+ );
80
+ }
81
+
82
+ export async function runDistill({
83
+ dryRun = false,
84
+ runner,
85
+ prereqs,
86
+ stderr = process.stderr,
87
+ } = {}) {
88
+ if (!runner) throw new Error('runDistill: runner is required');
89
+
90
+ // Step 1 — prerequisites.
91
+ const env = prereqs ?? (await resolvePrereqs());
92
+ const { repoRoot, paName, librarianDef, librarianSupplement, logDir, runsLog } = env;
93
+
94
+ // Start timing as soon as we have a commitment to run. Early exits
95
+ // (empty staging) still record durationSec — it will typically be 0
96
+ // but reflects time spent in preconditions.
97
+ const startedAtIso = runner.tsIso();
98
+ const startedAtMs = Date.now();
99
+
100
+ // Step 2 — count staged observations.
101
+ const stagingCount = await countStagedObservations(repoRoot);
102
+ if (stagingCount === 0) {
103
+ log('no staged observations to process — nothing to do', { stderr });
104
+ if (!dryRun) {
105
+ await writeAudit(runsLog, {
106
+ ts: startedAtIso,
107
+ status: 'noop-empty-staging',
108
+ exitCode: 0,
109
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
110
+ stagingCount: 0,
111
+ log: null,
112
+ reason: 'no staged observations to process',
113
+ }, stderr);
114
+ }
115
+ return 0;
116
+ }
117
+ log(`found ${stagingCount} staged observation(s)`, { stderr });
118
+
119
+ // Step 3 — assemble & invoke.
120
+ //
121
+ // Two timestamp shapes for two jobs:
122
+ // * `ts` — filename-safe (colons replaced with dashes) for the log
123
+ // filename and session-name slug. Must not contain `:` because
124
+ // filesystems/tools dislike it.
125
+ // * `tsIso` — strict RFC-3339 / ISO-8601 for the `runs.jsonl` audit
126
+ // field, so downstream consumers can `new Date(entry.ts)` without
127
+ // getting NaN. The filename-safe form looks like ISO but is not
128
+ // parseable — don't conflate the two.
129
+ const ts = runner.ts();
130
+ const tsIso = runner.tsIso();
131
+ const runLogPath = join(logDir, `${ts}.log`);
132
+ const sessionName = `distill-${isoCompactForSession(runner.now())}`;
133
+
134
+ // Assemble the librarian system prompt. Three shapes:
135
+ // * no supplement resolved -> the resolved file, byte-for-byte
136
+ // (explicit opt, full operator override, or bundled alone);
137
+ // * supplement resolved, has content -> bundled + supplement composed
138
+ // with the operator-wins precedence statement;
139
+ // * supplement resolved, whitespace-only -> treated as absent. Appending
140
+ // an empty supplement under an operator-wins banner would claim a
141
+ // customisation that does not exist; the bundled definition ships alone
142
+ // and the dry-run/plan output says so.
143
+ const librarianBase = await readFile(librarianDef, 'utf8');
144
+ let supplementContent = null;
145
+ if (librarianSupplement) {
146
+ const raw = await readFile(librarianSupplement, 'utf8');
147
+ if (raw.trim() !== '') supplementContent = raw;
148
+ }
149
+ const librarianIdentity =
150
+ supplementContent === null ? librarianBase : composeLibrarianDef(librarianBase, supplementContent);
151
+ const supplementPlanLine =
152
+ supplementContent !== null
153
+ ? `${librarianSupplement} (${supplementContent.length} bytes, appended; operator-wins on conflict)`
154
+ : librarianSupplement
155
+ ? `${librarianSupplement} (empty — ignored)`
156
+ : 'none';
157
+
158
+ if (dryRun) {
159
+ log('DRY RUN — would invoke:', { stderr });
160
+ log(` cwd: ${repoRoot}`, { stderr });
161
+ log(` pa name: ${paName}`, { stderr });
162
+ log(` staging count: ${stagingCount}`, { stderr });
163
+ log(` log file: ${runLogPath}`, { stderr });
164
+ log(` session name: ${sessionName}`, { stderr });
165
+ log(` appended system: ${librarianDef} (${librarianBase.length} bytes)`, { stderr });
166
+ log(` operator supplement: ${supplementPlanLine}`, { stderr });
167
+ log(` composed total: ${librarianIdentity.length} bytes`, { stderr });
168
+ log(` prompt: <inline> (${DISTILL_PROMPT.length} bytes)`, { stderr });
169
+ log(' command: claude --print --permission-mode bypassPermissions \\', { stderr });
170
+ log(` --no-session-persistence --name ${sessionName} \\`, { stderr });
171
+ log(' --append-system-prompt <librarian definition> \\', { stderr });
172
+ log(' <distill prompt as positional arg>', { stderr });
173
+ log(' git: none — sync (core/tools/sync) picks up changes', { stderr });
174
+ return 0;
175
+ }
176
+
177
+ log(`starting distillation sweep — log: ${runLogPath}`, { stderr });
178
+ const start = Date.now();
179
+
180
+ const claudeArgv = claudeArgs({ sessionName, librarianDef: librarianIdentity });
181
+ const claudeResult = await runner.spawn('claude', claudeArgv, { cwd: repoRoot });
182
+
183
+ // Persist claude stdout/stderr to the per-run log file.
184
+ await appendFile(runLogPath, claudeResult.stdout || '');
185
+ await appendFile(runLogPath, claudeResult.stderr || '');
186
+
187
+ const end = Date.now();
188
+ const durationSec = Math.round((end - start) / 1000);
189
+
190
+ if (claudeResult.code !== 0) {
191
+ log(`distillation FAILED (exit ${claudeResult.code}) — see ${runLogPath}`, { stderr });
192
+ await writeAudit(runsLog, {
193
+ ts: tsIso,
194
+ status: 'fail-claude',
195
+ exitCode: 3,
196
+ durationSec,
197
+ stagingCount,
198
+ log: basename(runLogPath),
199
+ reason: `claude exited ${claudeResult.code}`,
200
+ }, stderr);
201
+ return 3;
202
+ }
203
+
204
+ log(`distillation OK (${durationSec}s) — log: ${runLogPath}`, { stderr });
205
+ log('changes (if any) will be committed by the next sync run', { stderr });
206
+
207
+ // Step 4 — append success audit. Sync will pick up any file changes
208
+ // the librarian produced on its next 30-minute tick.
209
+ await writeAudit(runsLog, {
210
+ ts: tsIso,
211
+ status: 'ok',
212
+ exitCode: 0,
213
+ durationSec,
214
+ stagingCount,
215
+ log: basename(runLogPath),
216
+ }, stderr);
217
+ return 0;
218
+ }
219
+
220
+ function isoCompactForSession(d) {
221
+ // Matches the historical `$(date -u +%Y%m%d-%H%M)` shape used pre-Node.
222
+ const pad = (n) => String(n).padStart(2, '0');
223
+ return (
224
+ `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}` +
225
+ `-${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}`
226
+ );
227
+ }
228
+
229
+ // Export for unit test.
230
+ export { opError };
@@ -0,0 +1,86 @@
1
+ // distill — chokepoint for external effects.
2
+ //
3
+ // Every process spawn, file write (for run logs), and ts/now call
4
+ // funnels through here. This module is the ONLY place that touches
5
+ // the outside world at runtime. Tests inject a fake `make()` factory
6
+ // to assert on what would have happened without actually spawning.
7
+ //
8
+ // Contract:
9
+ // - spawn(cmd, args, { stdin?, capture? }) -> { code, stdout, stderr }
10
+ // * No shell interpolation. Arguments are passed as an array to
11
+ // child_process.spawn with shell:false. A malicious staging-file
12
+ // name cannot become a shell command because the shell is never
13
+ // invoked.
14
+ // * stdin is optional; when provided it is written as-is and the
15
+ // stream is closed.
16
+ // * capture:true collects stdout/stderr into strings (default).
17
+ // capture:false streams directly to the parent process.
18
+ // - now() -> Date — single source of wall-clock time, for testing.
19
+ // - ts() -> ISO-ish UTC timestamp `YYYY-MM-DDTHH-MM-SSZ` suitable
20
+ // for filenames (no colons). Not a valid RFC-3339 string — do not
21
+ // feed this to `new Date()`, use `tsIso()` for parseable output.
22
+ // - tsIso() -> strict RFC-3339 / ISO-8601 UTC timestamp
23
+ // `YYYY-MM-DDTHH:MM:SSZ` (second precision). Safe to round-trip via
24
+ // `new Date()`; use this for any field a downstream reader may parse.
25
+
26
+ import { spawn as realSpawn } from 'node:child_process';
27
+
28
+ export function createRunner({ spawner = realSpawn, clock = () => new Date() } = {}) {
29
+ return {
30
+ async spawn(cmd, args, { stdin, capture = true, cwd, env } = {}) {
31
+ return new Promise((resolve, reject) => {
32
+ const child = spawner(cmd, args, {
33
+ shell: false,
34
+ cwd,
35
+ env: env ?? process.env,
36
+ stdio: capture ? ['pipe', 'pipe', 'pipe'] : ['pipe', 'inherit', 'inherit'],
37
+ });
38
+
39
+ let stdout = '';
40
+ let stderr = '';
41
+
42
+ if (capture) {
43
+ child.stdout.on('data', (d) => {
44
+ stdout += d.toString('utf8');
45
+ });
46
+ child.stderr.on('data', (d) => {
47
+ stderr += d.toString('utf8');
48
+ });
49
+ }
50
+
51
+ child.on('error', (err) => reject(err));
52
+ child.on('close', (code) => resolve({ code: code ?? 0, stdout, stderr }));
53
+
54
+ if (stdin !== undefined) {
55
+ child.stdin.end(stdin);
56
+ } else {
57
+ child.stdin.end();
58
+ }
59
+ });
60
+ },
61
+
62
+ now() {
63
+ return clock();
64
+ },
65
+
66
+ ts() {
67
+ const d = clock();
68
+ const pad = (n) => String(n).padStart(2, '0');
69
+ return (
70
+ `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}` +
71
+ `T${pad(d.getUTCHours())}-${pad(d.getUTCMinutes())}-${pad(d.getUTCSeconds())}Z`
72
+ );
73
+ },
74
+
75
+ tsIso() {
76
+ const d = clock();
77
+ const pad = (n) => String(n).padStart(2, '0');
78
+ return (
79
+ `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}` +
80
+ `T${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}Z`
81
+ );
82
+ },
83
+ };
84
+ }
85
+
86
+ export const defaultRunner = createRunner();
@@ -0,0 +1,59 @@
1
+ // distill — staging observation counter.
2
+ //
3
+ // Walks `agents/*/staging/` (depth ≤ 2) counting *.md files, excluding
4
+ // anything under `_archived/`. Pure filesystem logic, no spawning. The
5
+ // original shell script used `find -maxdepth 2`; we preserve that.
6
+
7
+ import { readdir } from 'node:fs/promises';
8
+ import { join } from 'node:path';
9
+
10
+ export async function countStagedObservations(repoRoot) {
11
+ const agentsDir = join(repoRoot, 'agents');
12
+ let agents;
13
+ try {
14
+ agents = await readdir(agentsDir, { withFileTypes: true });
15
+ } catch (err) {
16
+ if (err.code === 'ENOENT') return 0;
17
+ throw err;
18
+ }
19
+
20
+ let count = 0;
21
+ for (const a of agents) {
22
+ if (!a.isDirectory()) continue;
23
+ const stagingDir = join(agentsDir, a.name, 'staging');
24
+ count += await countMdsDepth2(stagingDir);
25
+ }
26
+ return count;
27
+ }
28
+
29
+ async function countMdsDepth2(dir) {
30
+ let entries;
31
+ try {
32
+ entries = await readdir(dir, { withFileTypes: true });
33
+ } catch (err) {
34
+ if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return 0;
35
+ throw err;
36
+ }
37
+ let n = 0;
38
+ for (const e of entries) {
39
+ if (e.name === '_archived') continue;
40
+ if (e.isFile() && e.name.endsWith('.md')) {
41
+ n++;
42
+ continue;
43
+ }
44
+ if (e.isDirectory()) {
45
+ // depth 2: list one more level, skip _archived subtrees.
46
+ const sub = join(dir, e.name);
47
+ let subEntries;
48
+ try {
49
+ subEntries = await readdir(sub, { withFileTypes: true });
50
+ } catch {
51
+ continue;
52
+ }
53
+ for (const s of subEntries) {
54
+ if (s.isFile() && s.name.endsWith('.md')) n++;
55
+ }
56
+ }
57
+ }
58
+ return n;
59
+ }
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ // backlink-indexer.mjs — Scans markdown files for internal links and builds a backlink index.
3
+ //
4
+ // Finds wiki-style [[target]] links and markdown [text](relative/path.md) links
5
+ // in all .md files under operator/knowledge/ and core/knowledge/.
6
+ //
7
+ // Output: operator/knowledge/.backlinks.json
8
+ //
9
+ // CLI: node backlink-indexer.mjs [--root /path/to/repo]
10
+
11
+ import { readdir, readFile, writeFile, stat } from 'node:fs/promises';
12
+ import { join, resolve, dirname, relative, normalize } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+ const DEFAULT_ROOT = resolve(__dirname, '../../../..');
17
+
18
+ const USAGE = `
19
+ backlink-indexer — Build a backlink index from markdown internal links
20
+
21
+ Usage:
22
+ node backlink-indexer.mjs [options]
23
+
24
+ Options:
25
+ --root <path> Repository root (default: 4 levels up from this script)
26
+ --help, -h Show this help
27
+
28
+ Scans all .md files under operator/knowledge/ and core/knowledge/.
29
+ Finds:
30
+ - Wiki-style links: [[target]]
31
+ - Markdown links: [text](relative/path.md)
32
+
33
+ Output: operator/knowledge/.backlinks.json
34
+ `.trim();
35
+
36
+ // ── Helpers ─────────────────────────────────────────────────────────
37
+
38
+ async function walkDir(dir) {
39
+ const results = [];
40
+ let entries;
41
+ try {
42
+ entries = await readdir(dir, { withFileTypes: true });
43
+ } catch (err) {
44
+ if (err.code === 'ENOENT') return results;
45
+ throw err;
46
+ }
47
+ for (const entry of entries) {
48
+ const full = join(dir, entry.name);
49
+ if (entry.isDirectory()) {
50
+ if (entry.name.startsWith('.')) continue;
51
+ results.push(...await walkDir(full));
52
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
53
+ results.push(full);
54
+ }
55
+ }
56
+ return results;
57
+ }
58
+
59
+ function extractLinks(content, sourcePath, repoRoot) {
60
+ const links = [];
61
+ const sourceDir = dirname(sourcePath);
62
+
63
+ // Wiki-style: [[target]]
64
+ const wikiRe = /\[\[([^\]]+)\]\]/g;
65
+ let m;
66
+ while ((m = wikiRe.exec(content)) !== null) {
67
+ let target = m[1].trim();
68
+ // Resolve as relative to repo root
69
+ if (!target.endsWith('.md')) target += '.md';
70
+ const resolved = normalize(target);
71
+ links.push(resolved);
72
+ }
73
+
74
+ // Markdown-style: [text](relative/path.md)
75
+ // Exclude external URLs and anchors
76
+ const mdRe = /\[([^\]]*)\]\(([^)]+)\)/g;
77
+ while ((m = mdRe.exec(content)) !== null) {
78
+ const href = m[2].trim();
79
+ if (href.startsWith('http://') || href.startsWith('https://') || href.startsWith('#') || href.startsWith('mailto:')) continue;
80
+ // Strip anchor fragment
81
+ const path = href.split('#')[0];
82
+ if (!path) continue;
83
+ if (!path.endsWith('.md')) continue;
84
+ // Resolve relative to source file's directory
85
+ const abs = resolve(sourceDir, path);
86
+ const rel = relative(repoRoot, abs);
87
+ links.push(normalize(rel));
88
+ }
89
+
90
+ return links;
91
+ }
92
+
93
+ // ── Main ────────────────────────────────────────────────────────────
94
+
95
+ async function main(args) {
96
+ let root = DEFAULT_ROOT;
97
+
98
+ for (let i = 0; i < args.length; i++) {
99
+ const a = args[i];
100
+ if (a === '--help' || a === '-h') {
101
+ console.log(USAGE);
102
+ return 0;
103
+ }
104
+ if (a === '--root') {
105
+ root = resolve(args[++i]);
106
+ }
107
+ }
108
+
109
+ const scanDirs = [
110
+ join(root, 'operator', 'knowledge'),
111
+ join(root, 'core', 'knowledge'),
112
+ ];
113
+
114
+ // Collect all markdown files
115
+ const allFiles = [];
116
+ for (const dir of scanDirs) {
117
+ allFiles.push(...await walkDir(dir));
118
+ }
119
+
120
+ // Build backlinks map: target -> [sources]
121
+ const backlinks = {};
122
+
123
+ for (const filePath of allFiles) {
124
+ const content = await readFile(filePath, 'utf8');
125
+ const relSource = relative(root, filePath);
126
+ const links = extractLinks(content, filePath, root);
127
+
128
+ for (const target of links) {
129
+ if (!backlinks[target]) backlinks[target] = [];
130
+ if (!backlinks[target].includes(relSource)) {
131
+ backlinks[target].push(relSource);
132
+ }
133
+ }
134
+ }
135
+
136
+ // Sort source arrays for deterministic output
137
+ for (const target of Object.keys(backlinks)) {
138
+ backlinks[target].sort();
139
+ }
140
+
141
+ const output = {
142
+ version: '1.0',
143
+ generatedAt: new Date().toISOString(),
144
+ backlinks,
145
+ };
146
+
147
+ const outPath = join(root, 'operator', 'knowledge', '.backlinks.json');
148
+ await writeFile(outPath, JSON.stringify(output, null, 2) + '\n', 'utf8');
149
+ console.log(`Wrote ${outPath} (${Object.keys(backlinks).length} targets indexed)`);
150
+ return 0;
151
+ }
152
+
153
+ const exitCode = await main(process.argv.slice(2));
154
+ process.exit(exitCode ?? 0);