godpowers 0.15.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 (444) hide show
  1. package/AGENTS.md +37 -0
  2. package/CHANGELOG.md +639 -0
  3. package/INSPIRATION.md +52 -0
  4. package/LICENSE +21 -0
  5. package/README.md +232 -0
  6. package/SKILL.md +500 -0
  7. package/agents/god-archaeologist.md +139 -0
  8. package/agents/god-architect.md +92 -0
  9. package/agents/god-auditor.md +150 -0
  10. package/agents/god-browser-tester.md +144 -0
  11. package/agents/god-context-writer.md +137 -0
  12. package/agents/god-coordinator.md +138 -0
  13. package/agents/god-debt-assessor.md +132 -0
  14. package/agents/god-debugger.md +77 -0
  15. package/agents/god-deploy-engineer.md +87 -0
  16. package/agents/god-deps-auditor.md +111 -0
  17. package/agents/god-design-reviewer.md +137 -0
  18. package/agents/god-designer.md +171 -0
  19. package/agents/god-docs-writer.md +102 -0
  20. package/agents/god-executor.md +76 -0
  21. package/agents/god-explorer.md +110 -0
  22. package/agents/god-harden-auditor.md +163 -0
  23. package/agents/god-incident-investigator.md +144 -0
  24. package/agents/god-launch-strategist.md +103 -0
  25. package/agents/god-migration-strategist.md +126 -0
  26. package/agents/god-observability-engineer.md +76 -0
  27. package/agents/god-orchestrator.md +728 -0
  28. package/agents/god-org-context-loader.md +124 -0
  29. package/agents/god-planner.md +73 -0
  30. package/agents/god-pm.md +105 -0
  31. package/agents/god-quality-reviewer.md +74 -0
  32. package/agents/god-reconciler.md +230 -0
  33. package/agents/god-reconstructor.md +124 -0
  34. package/agents/god-repo-scaffolder.md +60 -0
  35. package/agents/god-retrospective.md +109 -0
  36. package/agents/god-roadmap-reconciler.md +123 -0
  37. package/agents/god-roadmap-updater.md +89 -0
  38. package/agents/god-roadmapper.md +82 -0
  39. package/agents/god-spec-reviewer.md +70 -0
  40. package/agents/god-spike-runner.md +119 -0
  41. package/agents/god-stack-selector.md +93 -0
  42. package/agents/god-standards-check.md +132 -0
  43. package/agents/god-storyteller.md +116 -0
  44. package/agents/god-updater.md +174 -0
  45. package/bin/install.js +514 -0
  46. package/extensions/data-pack/README.md +33 -0
  47. package/extensions/data-pack/agents/god-dashboard-builder.md +66 -0
  48. package/extensions/data-pack/agents/god-etl-engineer.md +64 -0
  49. package/extensions/data-pack/agents/god-ml-feature-engineer.md +66 -0
  50. package/extensions/data-pack/manifest.yaml +39 -0
  51. package/extensions/data-pack/package.json +42 -0
  52. package/extensions/data-pack/skills/god-dashboard.md +28 -0
  53. package/extensions/data-pack/skills/god-etl.md +28 -0
  54. package/extensions/data-pack/skills/god-ml-feature.md +28 -0
  55. package/extensions/data-pack/workflows/dashboard-arc.yaml +13 -0
  56. package/extensions/data-pack/workflows/etl-arc.yaml +13 -0
  57. package/extensions/data-pack/workflows/ml-feature-arc.yaml +13 -0
  58. package/extensions/launch-pack/README.md +36 -0
  59. package/extensions/launch-pack/agents/god-indie-hackers-strategist.md +128 -0
  60. package/extensions/launch-pack/agents/god-oss-release-strategist.md +125 -0
  61. package/extensions/launch-pack/agents/god-product-hunt-strategist.md +118 -0
  62. package/extensions/launch-pack/agents/god-show-hn-strategist.md +113 -0
  63. package/extensions/launch-pack/manifest.yaml +45 -0
  64. package/extensions/launch-pack/package.json +41 -0
  65. package/extensions/launch-pack/skills/god-indie-hackers.md +39 -0
  66. package/extensions/launch-pack/skills/god-oss-release.md +43 -0
  67. package/extensions/launch-pack/skills/god-product-hunt.md +41 -0
  68. package/extensions/launch-pack/skills/god-show-hn.md +40 -0
  69. package/extensions/launch-pack/workflows/indie-hackers.yaml +13 -0
  70. package/extensions/launch-pack/workflows/oss-release.yaml +13 -0
  71. package/extensions/launch-pack/workflows/product-hunt.yaml +13 -0
  72. package/extensions/launch-pack/workflows/show-hn.yaml +13 -0
  73. package/extensions/security-pack/README.md +48 -0
  74. package/extensions/security-pack/agents/god-hipaa-auditor.md +117 -0
  75. package/extensions/security-pack/agents/god-pci-auditor.md +100 -0
  76. package/extensions/security-pack/agents/god-soc2-auditor.md +107 -0
  77. package/extensions/security-pack/manifest.yaml +39 -0
  78. package/extensions/security-pack/package.json +42 -0
  79. package/extensions/security-pack/skills/god-hipaa-audit.md +41 -0
  80. package/extensions/security-pack/skills/god-pci-audit.md +40 -0
  81. package/extensions/security-pack/skills/god-soc2-audit.md +42 -0
  82. package/extensions/security-pack/workflows/hipaa-arc.yaml +15 -0
  83. package/extensions/security-pack/workflows/pci-arc.yaml +15 -0
  84. package/extensions/security-pack/workflows/soc2-arc.yaml +15 -0
  85. package/hooks/pre-tool-use.sh +40 -0
  86. package/hooks/session-start.sh +74 -0
  87. package/lib/README.md +28 -0
  88. package/lib/agent-browser-driver.js +215 -0
  89. package/lib/agent-cache.js +194 -0
  90. package/lib/agent-validator.js +275 -0
  91. package/lib/artifact-diff.js +168 -0
  92. package/lib/artifact-linter.js +142 -0
  93. package/lib/awesome-design.js +312 -0
  94. package/lib/browser-bridge.js +209 -0
  95. package/lib/budget.js +215 -0
  96. package/lib/checkpoint.js +390 -0
  97. package/lib/code-scanner.js +262 -0
  98. package/lib/context-budget.js +170 -0
  99. package/lib/context-writer.js +348 -0
  100. package/lib/cost-tracker.js +325 -0
  101. package/lib/cross-artifact-impact.js +162 -0
  102. package/lib/cross-repo-linkage.js +150 -0
  103. package/lib/design-detector.js +167 -0
  104. package/lib/design-spec.js +348 -0
  105. package/lib/drift-detector.js +212 -0
  106. package/lib/event-reader.js +174 -0
  107. package/lib/events.js +183 -0
  108. package/lib/extensions.js +257 -0
  109. package/lib/have-nots-validator.js +647 -0
  110. package/lib/impact.js +314 -0
  111. package/lib/impeccable-bridge.js +139 -0
  112. package/lib/intent.js +177 -0
  113. package/lib/linkage.js +232 -0
  114. package/lib/meta-linter.js +263 -0
  115. package/lib/multi-repo-detector.js +182 -0
  116. package/lib/otel-exporter.js +308 -0
  117. package/lib/recipes.js +186 -0
  118. package/lib/reverse-sync.js +332 -0
  119. package/lib/review-required.js +224 -0
  120. package/lib/router.js +278 -0
  121. package/lib/runtime-audit.js +455 -0
  122. package/lib/runtime-test.js +309 -0
  123. package/lib/skillui-bridge.js +216 -0
  124. package/lib/state-lock.js +201 -0
  125. package/lib/state.js +142 -0
  126. package/lib/story-validator.js +301 -0
  127. package/lib/suite-state.js +220 -0
  128. package/lib/workflow-parser.js +109 -0
  129. package/lib/workflow-runner.js +221 -0
  130. package/package.json +63 -0
  131. package/references/HAVE-NOTS.md +573 -0
  132. package/references/building/BUILD-ANTIPATTERNS.md +102 -0
  133. package/references/building/BUILD-VERTICAL-SLICES.md +75 -0
  134. package/references/building/BUILD-WAVES.md +61 -0
  135. package/references/building/README.md +17 -0
  136. package/references/design/COLOR.md +122 -0
  137. package/references/design/DESIGN-ANATOMY.md +121 -0
  138. package/references/design/DESIGN-ANTIPATTERNS.md +108 -0
  139. package/references/design/INTERACTION.md +148 -0
  140. package/references/design/MOTION.md +120 -0
  141. package/references/design/RESPONSIVE.md +157 -0
  142. package/references/design/SPATIAL.md +109 -0
  143. package/references/design/TYPOGRAPHY.md +121 -0
  144. package/references/design/UX-WRITING.md +135 -0
  145. package/references/orchestration/MODE-DETECTION.md +74 -0
  146. package/references/orchestration/README.md +18 -0
  147. package/references/orchestration/SCALE-DETECTION.md +81 -0
  148. package/references/planning/ARCH-ANATOMY.md +143 -0
  149. package/references/planning/ARCH-ANTIPATTERNS.md +52 -0
  150. package/references/planning/PRD-ANATOMY.md +117 -0
  151. package/references/planning/PRD-ANTIPATTERNS.md +138 -0
  152. package/references/planning/README.md +16 -0
  153. package/references/planning/ROADMAP-ANATOMY.md +43 -0
  154. package/references/planning/ROADMAP-ANTIPATTERNS.md +94 -0
  155. package/references/planning/STACK-ANATOMY.md +60 -0
  156. package/references/planning/STACK-ANTIPATTERNS.md +95 -0
  157. package/references/shared/GLOSSARY.md +80 -0
  158. package/references/shared/ORCHESTRATORS.md +76 -0
  159. package/references/shared/README.md +14 -0
  160. package/references/shipping/DEPLOY-ANTIPATTERNS.md +64 -0
  161. package/references/shipping/DEPLOY-PATTERNS.md +110 -0
  162. package/references/shipping/HARDEN-ANTIPATTERNS.md +66 -0
  163. package/references/shipping/HARDEN-OWASP-WORKSHEETS.md +89 -0
  164. package/references/shipping/LAUNCH-ANTIPATTERNS.md +68 -0
  165. package/references/shipping/OBSERVE-ANTIPATTERNS.md +62 -0
  166. package/references/shipping/OBSERVE-SLO-EXAMPLES.md +107 -0
  167. package/references/shipping/README.md +18 -0
  168. package/routing/god-add-backlog.yaml +24 -0
  169. package/routing/god-add-tests.yaml +27 -0
  170. package/routing/god-add-todo.yaml +24 -0
  171. package/routing/god-agent-audit.yaml +24 -0
  172. package/routing/god-arch.yaml +46 -0
  173. package/routing/god-archaeology.yaml +28 -0
  174. package/routing/god-audit.yaml +32 -0
  175. package/routing/god-budget.yaml +24 -0
  176. package/routing/god-build-agent.yaml +24 -0
  177. package/routing/god-build.yaml +46 -0
  178. package/routing/god-cache-clear.yaml +24 -0
  179. package/routing/god-check-todos.yaml +24 -0
  180. package/routing/god-context-scan.yaml +24 -0
  181. package/routing/god-context.yaml +44 -0
  182. package/routing/god-cost.yaml +24 -0
  183. package/routing/god-debug.yaml +28 -0
  184. package/routing/god-deploy.yaml +34 -0
  185. package/routing/god-design-impact.yaml +25 -0
  186. package/routing/god-design.yaml +67 -0
  187. package/routing/god-discuss.yaml +27 -0
  188. package/routing/god-docs.yaml +33 -0
  189. package/routing/god-doctor.yaml +27 -0
  190. package/routing/god-explore.yaml +27 -0
  191. package/routing/god-extension-add.yaml +24 -0
  192. package/routing/god-extension-info.yaml +24 -0
  193. package/routing/god-extension-list.yaml +24 -0
  194. package/routing/god-extension-remove.yaml +24 -0
  195. package/routing/god-extract-learnings.yaml +24 -0
  196. package/routing/god-fast.yaml +27 -0
  197. package/routing/god-feature.yaml +34 -0
  198. package/routing/god-graph.yaml +24 -0
  199. package/routing/god-harden.yaml +41 -0
  200. package/routing/god-help.yaml +27 -0
  201. package/routing/god-hotfix.yaml +34 -0
  202. package/routing/god-hygiene.yaml +28 -0
  203. package/routing/god-init.yaml +37 -0
  204. package/routing/god-intel.yaml +24 -0
  205. package/routing/god-launch.yaml +41 -0
  206. package/routing/god-lifecycle.yaml +27 -0
  207. package/routing/god-link.yaml +24 -0
  208. package/routing/god-lint.yaml +24 -0
  209. package/routing/god-list-assumptions.yaml +27 -0
  210. package/routing/god-locate.yaml +24 -0
  211. package/routing/god-logs.yaml +24 -0
  212. package/routing/god-map-codebase.yaml +24 -0
  213. package/routing/god-metrics.yaml +24 -0
  214. package/routing/god-mode.yaml +31 -0
  215. package/routing/god-next.yaml +27 -0
  216. package/routing/god-note.yaml +24 -0
  217. package/routing/god-observe.yaml +34 -0
  218. package/routing/god-org-context.yaml +28 -0
  219. package/routing/god-party.yaml +24 -0
  220. package/routing/god-pause-work.yaml +27 -0
  221. package/routing/god-plant-seed.yaml +24 -0
  222. package/routing/god-postmortem.yaml +34 -0
  223. package/routing/god-pr-branch.yaml +25 -0
  224. package/routing/god-prd.yaml +49 -0
  225. package/routing/god-quick.yaml +28 -0
  226. package/routing/god-reconcile.yaml +48 -0
  227. package/routing/god-reconstruct.yaml +36 -0
  228. package/routing/god-redo.yaml +27 -0
  229. package/routing/god-refactor.yaml +36 -0
  230. package/routing/god-repair.yaml +27 -0
  231. package/routing/god-repo.yaml +35 -0
  232. package/routing/god-restore.yaml +27 -0
  233. package/routing/god-resume-work.yaml +27 -0
  234. package/routing/god-review-changes.yaml +25 -0
  235. package/routing/god-review.yaml +28 -0
  236. package/routing/god-roadmap-check.yaml +39 -0
  237. package/routing/god-roadmap-update.yaml +37 -0
  238. package/routing/god-roadmap.yaml +42 -0
  239. package/routing/god-rollback.yaml +27 -0
  240. package/routing/god-scan.yaml +24 -0
  241. package/routing/god-set-profile.yaml +24 -0
  242. package/routing/god-settings.yaml +24 -0
  243. package/routing/god-skip.yaml +27 -0
  244. package/routing/god-smite.yaml +29 -0
  245. package/routing/god-spike.yaml +35 -0
  246. package/routing/god-sprint.yaml +25 -0
  247. package/routing/god-stack.yaml +41 -0
  248. package/routing/god-standards.yaml +24 -0
  249. package/routing/god-status.yaml +27 -0
  250. package/routing/god-stories.yaml +24 -0
  251. package/routing/god-story-build.yaml +25 -0
  252. package/routing/god-story-close.yaml +25 -0
  253. package/routing/god-story-verify.yaml +25 -0
  254. package/routing/god-story.yaml +24 -0
  255. package/routing/god-suite-init.yaml +24 -0
  256. package/routing/god-suite-patch.yaml +25 -0
  257. package/routing/god-suite-release.yaml +25 -0
  258. package/routing/god-suite-status.yaml +25 -0
  259. package/routing/god-suite-sync.yaml +25 -0
  260. package/routing/god-sync.yaml +33 -0
  261. package/routing/god-tech-debt.yaml +32 -0
  262. package/routing/god-test-extension.yaml +24 -0
  263. package/routing/god-test-runtime.yaml +25 -0
  264. package/routing/god-thread.yaml +24 -0
  265. package/routing/god-trace.yaml +24 -0
  266. package/routing/god-undo.yaml +27 -0
  267. package/routing/god-update-deps.yaml +39 -0
  268. package/routing/god-upgrade.yaml +33 -0
  269. package/routing/god-version.yaml +24 -0
  270. package/routing/god-workstream.yaml +24 -0
  271. package/routing/god.yaml +24 -0
  272. package/routing/recipes/add-feature-defer-current-milestone.yaml +21 -0
  273. package/routing/recipes/add-feature-future-conditional.yaml +21 -0
  274. package/routing/recipes/add-feature-mid-arc-pause.yaml +33 -0
  275. package/routing/recipes/add-feature-next-milestone.yaml +23 -0
  276. package/routing/recipes/add-feature-parallel.yaml +29 -0
  277. package/routing/recipes/add-feature-prd-update.yaml +21 -0
  278. package/routing/recipes/add-feature-small.yaml +24 -0
  279. package/routing/recipes/add-feature-tiny.yaml +24 -0
  280. package/routing/recipes/bluefield-org-aware.yaml +27 -0
  281. package/routing/recipes/broken-install.yaml +22 -0
  282. package/routing/recipes/brownfield-onboarding.yaml +32 -0
  283. package/routing/recipes/bug-no-urgency.yaml +21 -0
  284. package/routing/recipes/capture-idea.yaml +22 -0
  285. package/routing/recipes/capture-todo.yaml +21 -0
  286. package/routing/recipes/clean-pr.yaml +21 -0
  287. package/routing/recipes/code-cleanup.yaml +23 -0
  288. package/routing/recipes/docs-drift.yaml +21 -0
  289. package/routing/recipes/existing-codebase-onboarding.yaml +32 -0
  290. package/routing/recipes/extract-learnings.yaml +22 -0
  291. package/routing/recipes/greenfield-fast.yaml +25 -0
  292. package/routing/recipes/greenfield-manual.yaml +32 -0
  293. package/routing/recipes/greenfield-with-ideation.yaml +29 -0
  294. package/routing/recipes/incident-postmortem.yaml +24 -0
  295. package/routing/recipes/major-framework-upgrade.yaml +23 -0
  296. package/routing/recipes/monthly-deps.yaml +22 -0
  297. package/routing/recipes/multi-repo-suite.yaml +56 -0
  298. package/routing/recipes/parallel-engineers.yaml +26 -0
  299. package/routing/recipes/pause-handoff.yaml +21 -0
  300. package/routing/recipes/production-broken.yaml +26 -0
  301. package/routing/recipes/rerun-tier.yaml +21 -0
  302. package/routing/recipes/returning-after-break.yaml +31 -0
  303. package/routing/recipes/state-drift.yaml +21 -0
  304. package/routing/recipes/undo-last.yaml +21 -0
  305. package/routing/recipes/weekly-health-check.yaml +24 -0
  306. package/routing/recipes/whats-next.yaml +22 -0
  307. package/routing/recipes/where-am-i.yaml +21 -0
  308. package/schema/events.v1.json +63 -0
  309. package/schema/extension-manifest.v1.json +84 -0
  310. package/schema/intent.v1.yaml.json +116 -0
  311. package/schema/recipe.v1.json +120 -0
  312. package/schema/routing.v1.json +163 -0
  313. package/schema/state.v1.json +146 -0
  314. package/schema/workflow.v1.json +96 -0
  315. package/skills/god-add-backlog.md +40 -0
  316. package/skills/god-add-tests.md +53 -0
  317. package/skills/god-add-todo.md +32 -0
  318. package/skills/god-agent-audit.md +87 -0
  319. package/skills/god-arch.md +81 -0
  320. package/skills/god-archaeology.md +48 -0
  321. package/skills/god-audit.md +65 -0
  322. package/skills/god-budget.md +103 -0
  323. package/skills/god-build-agent.md +91 -0
  324. package/skills/god-build.md +90 -0
  325. package/skills/god-cache-clear.md +75 -0
  326. package/skills/god-check-todos.md +42 -0
  327. package/skills/god-context-scan.md +125 -0
  328. package/skills/god-context.md +147 -0
  329. package/skills/god-cost.md +118 -0
  330. package/skills/god-debug.md +30 -0
  331. package/skills/god-deploy.md +76 -0
  332. package/skills/god-design-impact.md +86 -0
  333. package/skills/god-design.md +275 -0
  334. package/skills/god-discuss.md +46 -0
  335. package/skills/god-docs.md +81 -0
  336. package/skills/god-doctor.md +94 -0
  337. package/skills/god-explore.md +50 -0
  338. package/skills/god-export-otel.md +87 -0
  339. package/skills/god-extension-add.md +79 -0
  340. package/skills/god-extension-info.md +75 -0
  341. package/skills/god-extension-list.md +55 -0
  342. package/skills/god-extension-remove.md +66 -0
  343. package/skills/god-extract-learnings.md +60 -0
  344. package/skills/god-fast.md +47 -0
  345. package/skills/god-feature.md +114 -0
  346. package/skills/god-graph.md +56 -0
  347. package/skills/god-harden.md +106 -0
  348. package/skills/god-help.md +66 -0
  349. package/skills/god-hotfix.md +139 -0
  350. package/skills/god-hygiene.md +104 -0
  351. package/skills/god-init.md +161 -0
  352. package/skills/god-intel.md +36 -0
  353. package/skills/god-launch.md +86 -0
  354. package/skills/god-lifecycle.md +119 -0
  355. package/skills/god-link.md +90 -0
  356. package/skills/god-lint.md +128 -0
  357. package/skills/god-list-assumptions.md +56 -0
  358. package/skills/god-locate.md +97 -0
  359. package/skills/god-logs.md +57 -0
  360. package/skills/god-map-codebase.md +45 -0
  361. package/skills/god-metrics.md +51 -0
  362. package/skills/god-mode.md +159 -0
  363. package/skills/god-next.md +257 -0
  364. package/skills/god-note.md +39 -0
  365. package/skills/god-observe.md +76 -0
  366. package/skills/god-org-context.md +81 -0
  367. package/skills/god-party.md +87 -0
  368. package/skills/god-pause-work.md +64 -0
  369. package/skills/god-plant-seed.md +59 -0
  370. package/skills/god-postmortem.md +103 -0
  371. package/skills/god-pr-branch.md +50 -0
  372. package/skills/god-prd.md +90 -0
  373. package/skills/god-quick.md +50 -0
  374. package/skills/god-reconcile.md +90 -0
  375. package/skills/god-reconstruct.md +72 -0
  376. package/skills/god-redo.md +73 -0
  377. package/skills/god-refactor.md +137 -0
  378. package/skills/god-repair.md +82 -0
  379. package/skills/god-repo.md +49 -0
  380. package/skills/god-restore.md +91 -0
  381. package/skills/god-resume-work.md +42 -0
  382. package/skills/god-review-changes.md +93 -0
  383. package/skills/god-review.md +52 -0
  384. package/skills/god-roadmap-check.md +66 -0
  385. package/skills/god-roadmap-update.md +64 -0
  386. package/skills/god-roadmap.md +77 -0
  387. package/skills/god-rollback.md +88 -0
  388. package/skills/god-scan.md +106 -0
  389. package/skills/god-set-profile.md +58 -0
  390. package/skills/god-settings.md +44 -0
  391. package/skills/god-skip.md +78 -0
  392. package/skills/god-smite.md +86 -0
  393. package/skills/god-spike.md +120 -0
  394. package/skills/god-sprint.md +77 -0
  395. package/skills/god-stack.md +74 -0
  396. package/skills/god-standards.md +62 -0
  397. package/skills/god-status.md +99 -0
  398. package/skills/god-stories.md +60 -0
  399. package/skills/god-story-build.md +76 -0
  400. package/skills/god-story-close.md +82 -0
  401. package/skills/god-story-verify.md +71 -0
  402. package/skills/god-story.md +55 -0
  403. package/skills/god-suite-init.md +75 -0
  404. package/skills/god-suite-patch.md +64 -0
  405. package/skills/god-suite-release.md +58 -0
  406. package/skills/god-suite-status.md +63 -0
  407. package/skills/god-suite-sync.md +49 -0
  408. package/skills/god-sync.md +102 -0
  409. package/skills/god-tech-debt.md +56 -0
  410. package/skills/god-test-extension.md +87 -0
  411. package/skills/god-test-runtime.md +144 -0
  412. package/skills/god-thread.md +39 -0
  413. package/skills/god-trace.md +50 -0
  414. package/skills/god-undo.md +68 -0
  415. package/skills/god-update-deps.md +134 -0
  416. package/skills/god-upgrade.md +139 -0
  417. package/skills/god-version.md +37 -0
  418. package/skills/god-workstream.md +61 -0
  419. package/skills/god.md +207 -0
  420. package/templates/ARCH.md +99 -0
  421. package/templates/DEPS-AUDIT.md +66 -0
  422. package/templates/DESIGN.md +71 -0
  423. package/templates/DOCS-UPDATE-LOG.md +64 -0
  424. package/templates/HARDEN-FINDINGS.md +69 -0
  425. package/templates/MIGRATION.md +86 -0
  426. package/templates/POSTMORTEM.md +88 -0
  427. package/templates/PRD.md +80 -0
  428. package/templates/PROGRESS.md +49 -0
  429. package/templates/ROADMAP.md +47 -0
  430. package/templates/SPIKE.md +72 -0
  431. package/templates/STACK-DECISION.md +61 -0
  432. package/workflows/audit-only.yaml +22 -0
  433. package/workflows/bluefield-arc.yaml +87 -0
  434. package/workflows/brownfield-arc.yaml +44 -0
  435. package/workflows/deps-audit.yaml +56 -0
  436. package/workflows/docs-arc.yaml +22 -0
  437. package/workflows/feature-arc.yaml +59 -0
  438. package/workflows/full-arc.yaml +84 -0
  439. package/workflows/hotfix-arc.yaml +59 -0
  440. package/workflows/hygiene.yaml +43 -0
  441. package/workflows/migration-arc.yaml +73 -0
  442. package/workflows/postmortem.yaml +31 -0
  443. package/workflows/refactor-arc.yaml +59 -0
  444. package/workflows/spike.yaml +23 -0
package/lib/linkage.js ADDED
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Linkage Map Manager
3
+ *
4
+ * Bidirectional linkage between artifact elements (PRD requirements,
5
+ * ADRs, ARCH containers, ROADMAP milestones, STACK decisions, DESIGN
6
+ * tokens/components) and code files.
7
+ *
8
+ * Storage:
9
+ * .godpowers/links/artifact-to-code.json - forward map
10
+ * .godpowers/links/code-to-artifact.json - reverse map
11
+ * .godpowers/links/LINKAGE-LOG.md - append-only history
12
+ *
13
+ * Stable ID format:
14
+ * PRD requirement: P-{MUST,SHOULD,COULD}-NN (e.g., P-MUST-01)
15
+ * ADR: ADR-NNN
16
+ * ARCH container: C-{slug} (e.g., C-auth-service)
17
+ * ROADMAP milestone: M-{slug} (e.g., M-launch-v1)
18
+ * STACK decision: S-{slug} (e.g., S-postgres-15)
19
+ * DESIGN token: YAML path (e.g., colors.primary)
20
+ * DESIGN component: D-{slug} (e.g., D-button-primary)
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+
26
+ const ID_PATTERNS = {
27
+ prd: /^P-(MUST|SHOULD|COULD)-\d+$/,
28
+ adr: /^ADR-\d+$/,
29
+ container: /^C-[\w-]+$/,
30
+ milestone: /^M-[\w-]+$/,
31
+ stack: /^S-[\w-]+$/,
32
+ story: /^STORY-[\w-]+-\d+$/,
33
+ design: /^D-[\w-]+$/,
34
+ token: /^[a-z][\w-]*\.[\w.-]+$/ // colors.primary, typography.display, etc.
35
+ };
36
+
37
+ function classifyId(id) {
38
+ for (const [kind, regex] of Object.entries(ID_PATTERNS)) {
39
+ if (regex.test(id)) return kind;
40
+ }
41
+ return 'unknown';
42
+ }
43
+
44
+ function linksDir(projectRoot) {
45
+ return path.join(projectRoot, '.godpowers', 'links');
46
+ }
47
+
48
+ function forwardPath(projectRoot) {
49
+ return path.join(linksDir(projectRoot), 'artifact-to-code.json');
50
+ }
51
+
52
+ function reversePath(projectRoot) {
53
+ return path.join(linksDir(projectRoot), 'code-to-artifact.json');
54
+ }
55
+
56
+ function logPath(projectRoot) {
57
+ return path.join(linksDir(projectRoot), 'LINKAGE-LOG.md');
58
+ }
59
+
60
+ function ensureDir(projectRoot) {
61
+ const dir = linksDir(projectRoot);
62
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
63
+ }
64
+
65
+ function readMap(filePath) {
66
+ if (!fs.existsSync(filePath)) return {};
67
+ try {
68
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
69
+ } catch (e) {
70
+ return {};
71
+ }
72
+ }
73
+
74
+ function writeMap(filePath, data) {
75
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
76
+ }
77
+
78
+ /**
79
+ * Read forward map: artifact ID -> array of file paths.
80
+ */
81
+ function readForward(projectRoot) {
82
+ return readMap(forwardPath(projectRoot));
83
+ }
84
+
85
+ /**
86
+ * Read reverse map: file path -> array of artifact IDs.
87
+ */
88
+ function readReverse(projectRoot) {
89
+ return readMap(reversePath(projectRoot));
90
+ }
91
+
92
+ /**
93
+ * Add a link: artifact ID <-> file path. Bidirectional, idempotent.
94
+ * Returns { added: bool, ... }.
95
+ */
96
+ function addLink(projectRoot, artifactId, filePath, opts = {}) {
97
+ ensureDir(projectRoot);
98
+ const fwd = readForward(projectRoot);
99
+ const rev = readReverse(projectRoot);
100
+
101
+ const normFile = path.relative(projectRoot, path.resolve(projectRoot, filePath));
102
+ if (!fwd[artifactId]) fwd[artifactId] = [];
103
+ if (!rev[normFile]) rev[normFile] = [];
104
+
105
+ const fwdHad = fwd[artifactId].includes(normFile);
106
+ const revHad = rev[normFile].includes(artifactId);
107
+
108
+ if (!fwdHad) fwd[artifactId].push(normFile);
109
+ if (!revHad) rev[normFile].push(artifactId);
110
+
111
+ fwd[artifactId].sort();
112
+ rev[normFile].sort();
113
+
114
+ writeMap(forwardPath(projectRoot), fwd);
115
+ writeMap(reversePath(projectRoot), rev);
116
+
117
+ if (!fwdHad || !revHad) {
118
+ appendLog(projectRoot, `+ link: ${artifactId} <-> ${normFile}` + (opts.source ? ` (via ${opts.source})` : ''));
119
+ }
120
+ return { added: !fwdHad || !revHad, artifactId, file: normFile };
121
+ }
122
+
123
+ /**
124
+ * Remove a link.
125
+ */
126
+ function removeLink(projectRoot, artifactId, filePath) {
127
+ ensureDir(projectRoot);
128
+ const fwd = readForward(projectRoot);
129
+ const rev = readReverse(projectRoot);
130
+ const normFile = path.relative(projectRoot, path.resolve(projectRoot, filePath));
131
+
132
+ let removed = false;
133
+ if (fwd[artifactId]) {
134
+ const before = fwd[artifactId].length;
135
+ fwd[artifactId] = fwd[artifactId].filter(f => f !== normFile);
136
+ if (fwd[artifactId].length === 0) delete fwd[artifactId];
137
+ if (before !== (fwd[artifactId] ? fwd[artifactId].length : 0)) removed = true;
138
+ }
139
+ if (rev[normFile]) {
140
+ const before = rev[normFile].length;
141
+ rev[normFile] = rev[normFile].filter(id => id !== artifactId);
142
+ if (rev[normFile].length === 0) delete rev[normFile];
143
+ if (before !== (rev[normFile] ? rev[normFile].length : 0)) removed = true;
144
+ }
145
+
146
+ writeMap(forwardPath(projectRoot), fwd);
147
+ writeMap(reversePath(projectRoot), rev);
148
+
149
+ if (removed) {
150
+ appendLog(projectRoot, `- link: ${artifactId} <-> ${normFile}`);
151
+ }
152
+ return { removed };
153
+ }
154
+
155
+ /**
156
+ * Query: given an artifact ID, return linked files.
157
+ */
158
+ function queryByArtifact(projectRoot, artifactId) {
159
+ const fwd = readForward(projectRoot);
160
+ return fwd[artifactId] || [];
161
+ }
162
+
163
+ /**
164
+ * Query: given a file path, return linked artifact IDs.
165
+ */
166
+ function queryByFile(projectRoot, filePath) {
167
+ const rev = readReverse(projectRoot);
168
+ const normFile = path.relative(projectRoot, path.resolve(projectRoot, filePath));
169
+ return rev[normFile] || [];
170
+ }
171
+
172
+ /**
173
+ * List orphan artifact IDs (no implementing file).
174
+ * `knownIds` is an array of artifact IDs the user wants to check (typically
175
+ * sourced by parsing PRD/ARCH/etc. for declared IDs).
176
+ */
177
+ function listOrphans(projectRoot, knownIds) {
178
+ const fwd = readForward(projectRoot);
179
+ return knownIds.filter(id => !fwd[id] || fwd[id].length === 0);
180
+ }
181
+
182
+ /**
183
+ * Compute coverage: percentage of known IDs that have at least one link.
184
+ */
185
+ function coverage(projectRoot, knownIds) {
186
+ if (knownIds.length === 0) return 1;
187
+ const fwd = readForward(projectRoot);
188
+ const linked = knownIds.filter(id => fwd[id] && fwd[id].length > 0).length;
189
+ return linked / knownIds.length;
190
+ }
191
+
192
+ /**
193
+ * Append to LINKAGE-LOG.md.
194
+ */
195
+ function appendLog(projectRoot, message) {
196
+ ensureDir(projectRoot);
197
+ const ts = new Date().toISOString();
198
+ const line = `${ts} ${message}\n`;
199
+ fs.appendFileSync(logPath(projectRoot), line);
200
+ }
201
+
202
+ /**
203
+ * Replace all links from a source (used by code-scanner to do bulk updates).
204
+ */
205
+ function bulkReplaceFromSource(projectRoot, source, links) {
206
+ ensureDir(projectRoot);
207
+ const log = [];
208
+ for (const { artifactId, file } of links) {
209
+ const r = addLink(projectRoot, artifactId, file, { source });
210
+ if (r.added) log.push(`from ${source}: ${artifactId} -> ${file}`);
211
+ }
212
+ return { count: links.length, added: log.length };
213
+ }
214
+
215
+ module.exports = {
216
+ classifyId,
217
+ ID_PATTERNS,
218
+ readForward,
219
+ readReverse,
220
+ addLink,
221
+ removeLink,
222
+ queryByArtifact,
223
+ queryByFile,
224
+ listOrphans,
225
+ coverage,
226
+ appendLog,
227
+ bulkReplaceFromSource,
228
+ forwardPath,
229
+ reversePath,
230
+ logPath,
231
+ linksDir
232
+ };
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Meta-Linter
3
+ *
4
+ * Cross-repo invariant checks for Mode D suites. Validates:
5
+ * - Byte-identical files across repos (LICENSE, .editorconfig, etc.)
6
+ * - Version table consistency (declared versions match package.json)
7
+ * - Shared standards drift (linter, formatter, Node version)
8
+ *
9
+ * Per locked plan answer Q2: byte-identical drift surfaces as warnings
10
+ * by default; hard gate via `strict: true` opt-in.
11
+ *
12
+ * Public API:
13
+ * checkByteIdentical(hubPath, opts) -> findings
14
+ * checkVersionTable(hubPath, opts) -> findings
15
+ * checkSharedStandards(hubPath, opts) -> findings
16
+ * runAll(hubPath, opts) -> { findings, summary }
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+ const crypto = require('crypto');
22
+
23
+ const detector = require('./multi-repo-detector');
24
+
25
+ /**
26
+ * SHA-256 of file content. Returns null if file missing.
27
+ */
28
+ function fileHash(filePath) {
29
+ if (!fs.existsSync(filePath)) return null;
30
+ const content = fs.readFileSync(filePath);
31
+ return crypto.createHash('sha256').update(content).digest('hex');
32
+ }
33
+
34
+ /**
35
+ * For each declared byte-identical file, hash its content in each
36
+ * sibling repo and report mismatches.
37
+ */
38
+ function checkByteIdentical(hubPath, opts = {}) {
39
+ const findings = [];
40
+ const config = detector.readSuiteConfig(hubPath);
41
+ if (!config) return findings;
42
+
43
+ const files = detector.getByteIdenticalFiles(hubPath);
44
+ if (files.length === 0) return findings;
45
+
46
+ const siblings = (config.siblings || []).map(sib => {
47
+ if (typeof sib === 'string') return { name: sib, path: path.resolve(hubPath, sib) };
48
+ return { name: sib.name, path: path.resolve(hubPath, sib.path || sib.name) };
49
+ });
50
+
51
+ const severity = opts.strict ? 'error' : 'warning';
52
+
53
+ for (const fileSpec of files) {
54
+ const filePath = fileSpec.path;
55
+ const hashes = {};
56
+ for (const sib of siblings) {
57
+ const full = path.join(sib.path, filePath);
58
+ hashes[sib.name] = fileHash(full);
59
+ }
60
+
61
+ const uniqueHashes = new Set(Object.values(hashes).filter(h => h !== null));
62
+ if (uniqueHashes.size > 1) {
63
+ findings.push({
64
+ severity,
65
+ kind: 'byte-identical-drift',
66
+ file: filePath,
67
+ message: `File "${filePath}" differs across repos. Hashes:`,
68
+ hashes
69
+ });
70
+ }
71
+ // Missing in some siblings is also a drift
72
+ for (const [repo, hash] of Object.entries(hashes)) {
73
+ if (hash === null) {
74
+ findings.push({
75
+ severity,
76
+ kind: 'byte-identical-missing',
77
+ file: filePath,
78
+ repo,
79
+ message: `Byte-identical file "${filePath}" is missing in repo "${repo}".`
80
+ });
81
+ }
82
+ }
83
+ }
84
+
85
+ return findings;
86
+ }
87
+
88
+ /**
89
+ * Verify the suite-level version table matches each repo's package.json.
90
+ * Strictness setting same as byte-identical.
91
+ */
92
+ function checkVersionTable(hubPath, opts = {}) {
93
+ const findings = [];
94
+ const config = detector.readSuiteConfig(hubPath);
95
+ if (!config) return findings;
96
+
97
+ const versionTable = detector.getVersionTable(hubPath);
98
+ if (Object.keys(versionTable).length === 0) return findings;
99
+
100
+ const severity = opts.strict ? 'error' : 'warning';
101
+
102
+ const siblings = (config.siblings || []).map(sib => {
103
+ if (typeof sib === 'string') return { name: sib, path: path.resolve(hubPath, sib) };
104
+ return { name: sib.name, path: path.resolve(hubPath, sib.path || sib.name) };
105
+ });
106
+
107
+ for (const [packageName, perRepoVersions] of Object.entries(versionTable)) {
108
+ for (const [repoName, declaredVersion] of Object.entries(perRepoVersions)) {
109
+ const sib = siblings.find(s => s.name === repoName);
110
+ if (!sib) continue;
111
+ const pkgPath = path.join(sib.path, 'package.json');
112
+ if (!fs.existsSync(pkgPath)) continue;
113
+ try {
114
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
115
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
116
+ if (packageName === repoName) {
117
+ // The repo's own version
118
+ if (pkg.version !== declaredVersion) {
119
+ findings.push({
120
+ severity,
121
+ kind: 'version-table-drift',
122
+ repo: repoName,
123
+ package: packageName,
124
+ declared: declaredVersion,
125
+ actual: pkg.version,
126
+ message: `Suite declares ${repoName} at ${declaredVersion}; package.json has ${pkg.version}.`
127
+ });
128
+ }
129
+ } else if (allDeps[packageName]) {
130
+ // A sibling's dependency on packageName
131
+ const actualSpec = String(allDeps[packageName]).replace(/^[\^~>=<]+/, '');
132
+ if (actualSpec.split('.')[0] !== declaredVersion.split('.')[0]) {
133
+ findings.push({
134
+ severity,
135
+ kind: 'version-table-drift',
136
+ repo: repoName,
137
+ package: packageName,
138
+ declared: declaredVersion,
139
+ actual: allDeps[packageName],
140
+ message: `Suite declares ${packageName} at ${declaredVersion} for ${repoName}; package.json has ${allDeps[packageName]}.`
141
+ });
142
+ }
143
+ }
144
+ } catch (e) {
145
+ // ignore parse errors
146
+ }
147
+ }
148
+ }
149
+
150
+ return findings;
151
+ }
152
+
153
+ /**
154
+ * Validate shared standards: all repos use the same linter, formatter,
155
+ * Node version, etc. Declared in suite-config under `shared-standards`.
156
+ */
157
+ function checkSharedStandards(hubPath, opts = {}) {
158
+ const findings = [];
159
+ const config = detector.readSuiteConfig(hubPath);
160
+ if (!config) return findings;
161
+ const standards = config['shared-standards'] || config.sharedStandards;
162
+ if (!standards) return findings;
163
+
164
+ const severity = opts.strict ? 'error' : 'warning';
165
+
166
+ const siblings = (config.siblings || []).map(sib => {
167
+ if (typeof sib === 'string') return { name: sib, path: path.resolve(hubPath, sib) };
168
+ return { name: sib.name, path: path.resolve(hubPath, sib.path || sib.name) };
169
+ });
170
+
171
+ // Node version (from .nvmrc or engines.node)
172
+ if (standards['node-version']) {
173
+ const expected = standards['node-version'];
174
+ for (const sib of siblings) {
175
+ const nvmrc = path.join(sib.path, '.nvmrc');
176
+ const pkg = path.join(sib.path, 'package.json');
177
+ let actual = null;
178
+ if (fs.existsSync(nvmrc)) {
179
+ actual = fs.readFileSync(nvmrc, 'utf8').trim();
180
+ } else if (fs.existsSync(pkg)) {
181
+ try {
182
+ const j = JSON.parse(fs.readFileSync(pkg, 'utf8'));
183
+ actual = j.engines && j.engines.node;
184
+ } catch (e) {
185
+ // ignore
186
+ }
187
+ }
188
+ if (actual && !String(actual).includes(String(expected).split('.')[0])) {
189
+ findings.push({
190
+ severity,
191
+ kind: 'shared-standard-drift',
192
+ repo: sib.name,
193
+ standard: 'node-version',
194
+ declared: expected,
195
+ actual,
196
+ message: `Repo ${sib.name} uses Node ${actual}; suite declares ${expected}.`
197
+ });
198
+ }
199
+ }
200
+ }
201
+
202
+ // Linter
203
+ if (standards.linter) {
204
+ const expected = standards.linter; // 'biome', 'eslint', etc.
205
+ for (const sib of siblings) {
206
+ const pkg = path.join(sib.path, 'package.json');
207
+ if (!fs.existsSync(pkg)) continue;
208
+ try {
209
+ const j = JSON.parse(fs.readFileSync(pkg, 'utf8'));
210
+ const allDeps = { ...j.dependencies, ...j.devDependencies };
211
+ const usesBiome = allDeps['@biomejs/biome'] || allDeps.biome;
212
+ const usesEslint = allDeps.eslint || Object.keys(allDeps).some(k => k.startsWith('eslint-'));
213
+ let detected = null;
214
+ if (usesBiome) detected = 'biome';
215
+ else if (usesEslint) detected = 'eslint';
216
+ if (detected && detected !== expected) {
217
+ findings.push({
218
+ severity,
219
+ kind: 'shared-standard-drift',
220
+ repo: sib.name,
221
+ standard: 'linter',
222
+ declared: expected,
223
+ actual: detected,
224
+ message: `Repo ${sib.name} uses ${detected}; suite declares ${expected}.`
225
+ });
226
+ }
227
+ } catch (e) {
228
+ // ignore
229
+ }
230
+ }
231
+ }
232
+
233
+ return findings;
234
+ }
235
+
236
+ /**
237
+ * Run all meta-lint checks.
238
+ */
239
+ function runAll(hubPath, opts = {}) {
240
+ const findings = [
241
+ ...checkByteIdentical(hubPath, opts),
242
+ ...checkVersionTable(hubPath, opts),
243
+ ...checkSharedStandards(hubPath, opts)
244
+ ];
245
+ const summary = {
246
+ errors: findings.filter(f => f.severity === 'error').length,
247
+ warnings: findings.filter(f => f.severity === 'warning').length,
248
+ infos: findings.filter(f => f.severity === 'info').length,
249
+ byKind: {}
250
+ };
251
+ for (const f of findings) {
252
+ summary.byKind[f.kind] = (summary.byKind[f.kind] || 0) + 1;
253
+ }
254
+ return { findings, summary };
255
+ }
256
+
257
+ module.exports = {
258
+ checkByteIdentical,
259
+ checkVersionTable,
260
+ checkSharedStandards,
261
+ runAll,
262
+ fileHash
263
+ };
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Multi-Repo Detector
3
+ *
4
+ * Detects whether the working directory is part of a multi-repo suite
5
+ * (Mode D in the repo-ready taxonomy).
6
+ *
7
+ * Detection signals (any one is enough):
8
+ * 1. `.godpowers/suite-config.yaml` declares siblings (canonical)
9
+ * 2. Multiple `.godpowers/` dirs under a common parent that user
10
+ * explicitly registered (via /god-suite-init)
11
+ * 3. Hub indicator: directory contains a suite-config.yaml AND has
12
+ * no source code of its own (pure coordination repo)
13
+ *
14
+ * Per the locked plan answer Q1: explicit declaration only. We do NOT
15
+ * auto-walk parent dirs to find siblings. Users must register them.
16
+ *
17
+ * Public API:
18
+ * detect(projectRoot) -> { isMultiRepo, role, siblings, hubPath }
19
+ * readSuiteConfig(projectRoot) -> { siblings, byteIdentical, ... } | null
20
+ * isHub(projectRoot) -> bool
21
+ * findHub(projectRoot) -> hubPath | null
22
+ */
23
+
24
+ const fs = require('fs');
25
+ const path = require('path');
26
+ const { parseSimpleYaml } = require('./intent');
27
+
28
+ /**
29
+ * Look for .godpowers/suite-config.yaml in projectRoot or any
30
+ * registered parent. Returns parsed content or null.
31
+ */
32
+ function readSuiteConfig(projectRoot) {
33
+ const localConfig = path.join(projectRoot, '.godpowers', 'suite-config.yaml');
34
+ if (fs.existsSync(localConfig)) {
35
+ try {
36
+ const content = fs.readFileSync(localConfig, 'utf8');
37
+ return parseSimpleYaml(content);
38
+ } catch (e) {
39
+ return null;
40
+ }
41
+ }
42
+ // Check if state.json points to a hub
43
+ const statePath = path.join(projectRoot, '.godpowers', 'state.json');
44
+ if (fs.existsSync(statePath)) {
45
+ try {
46
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
47
+ if (state.suite && state.suite.hubPath) {
48
+ const hubConfigPath = path.join(state.suite.hubPath, '.godpowers', 'suite-config.yaml');
49
+ if (fs.existsSync(hubConfigPath)) {
50
+ return parseSimpleYaml(fs.readFileSync(hubConfigPath, 'utf8'));
51
+ }
52
+ }
53
+ } catch (e) {
54
+ // ignore
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ /**
61
+ * Is this directory the hub of a suite?
62
+ * Heuristic: has suite-config.yaml AND declares siblings AND is not
63
+ * itself listed in those siblings.
64
+ */
65
+ function isHub(projectRoot) {
66
+ const config = readSuiteConfig(projectRoot);
67
+ if (!config) return false;
68
+ const siblings = (config.siblings || []).map(s => normalize(s));
69
+ const myName = normalize(path.basename(projectRoot));
70
+ return siblings.length > 0 && !siblings.includes(myName);
71
+ }
72
+
73
+ /**
74
+ * Find the hub from a sibling repo. Walks up via state.json.suite.hubPath.
75
+ * Returns hub absolute path or null.
76
+ */
77
+ function findHub(projectRoot) {
78
+ const statePath = path.join(projectRoot, '.godpowers', 'state.json');
79
+ if (!fs.existsSync(statePath)) return null;
80
+ try {
81
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
82
+ if (state.suite && state.suite.hubPath) {
83
+ return state.suite.hubPath;
84
+ }
85
+ } catch (e) {
86
+ return null;
87
+ }
88
+ return null;
89
+ }
90
+
91
+ /**
92
+ * Detect this directory's role in a suite.
93
+ *
94
+ * Returns:
95
+ * {
96
+ * isMultiRepo: bool,
97
+ * role: 'hub' | 'sibling' | null,
98
+ * siblings: [{ name, path }], // populated when isMultiRepo
99
+ * hubPath: string | null
100
+ * }
101
+ */
102
+ function detect(projectRoot) {
103
+ const config = readSuiteConfig(projectRoot);
104
+ if (!config) {
105
+ return { isMultiRepo: false, role: null, siblings: [], hubPath: null };
106
+ }
107
+
108
+ const myName = path.basename(projectRoot);
109
+ const declaredSiblings = config.siblings || [];
110
+
111
+ // Resolve sibling paths. Per Q1, paths are explicit (relative or absolute)
112
+ // in suite-config.yaml.
113
+ const siblingsResolved = declaredSiblings.map(sib => {
114
+ if (typeof sib === 'string') {
115
+ return { name: sib, path: path.resolve(projectRoot, sib) };
116
+ }
117
+ if (typeof sib === 'object' && sib.name) {
118
+ return {
119
+ name: sib.name,
120
+ path: sib.path ? path.resolve(projectRoot, sib.path) : null
121
+ };
122
+ }
123
+ return null;
124
+ }).filter(Boolean);
125
+
126
+ const isCurrentHub = isHub(projectRoot);
127
+
128
+ if (isCurrentHub) {
129
+ return {
130
+ isMultiRepo: true,
131
+ role: 'hub',
132
+ siblings: siblingsResolved,
133
+ hubPath: projectRoot
134
+ };
135
+ }
136
+
137
+ // We're a sibling. Find the hub.
138
+ const hubPath = findHub(projectRoot);
139
+ return {
140
+ isMultiRepo: !!hubPath,
141
+ role: hubPath ? 'sibling' : null,
142
+ siblings: siblingsResolved,
143
+ hubPath
144
+ };
145
+ }
146
+
147
+ /**
148
+ * List byte-identical files declared in the suite config.
149
+ * Returns [{ path, description }].
150
+ */
151
+ function getByteIdenticalFiles(projectRoot) {
152
+ const config = readSuiteConfig(projectRoot);
153
+ if (!config) return [];
154
+ const list = config['byte-identical'] || config.byteIdentical || [];
155
+ return list.map(item => {
156
+ if (typeof item === 'string') return { path: item, description: '' };
157
+ return { path: item.path, description: item.description || '' };
158
+ });
159
+ }
160
+
161
+ /**
162
+ * Get the version table from suite-config.
163
+ * Format: { 'package-name': { 'repo-name': '1.2.3' } }
164
+ */
165
+ function getVersionTable(projectRoot) {
166
+ const config = readSuiteConfig(projectRoot);
167
+ if (!config) return {};
168
+ return config['version-table'] || config.versionTable || {};
169
+ }
170
+
171
+ function normalize(name) {
172
+ return String(name || '').toLowerCase().trim();
173
+ }
174
+
175
+ module.exports = {
176
+ detect,
177
+ readSuiteConfig,
178
+ isHub,
179
+ findHub,
180
+ getByteIdenticalFiles,
181
+ getVersionTable
182
+ };