onto-mcp 0.3.2 → 0.4.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 (300) hide show
  1. package/.onto/processes/reconstruct/actionable-ontology-seed-recomposition-design.md +447 -0
  2. package/.onto/processes/reconstruct/foundry-style-ontology-seed-contract.md +934 -0
  3. package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +303 -725
  4. package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +1645 -0
  5. package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +26 -22
  6. package/.onto/processes/reconstruct/source-profile-contract.md +49 -23
  7. package/.onto/processes/reconstruct/source-profiles/code.md +6 -3
  8. package/.onto/processes/reconstruct/source-profiles/database.md +5 -2
  9. package/.onto/processes/reconstruct/source-profiles/document.md +5 -2
  10. package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +5 -4
  11. package/.onto/processes/review/review-execution-ux-contract.md +40 -0
  12. package/.onto/processes/shared/pipeline-execution-ledger-contract.md +26 -10
  13. package/.onto/processes/shared/target-material-kind-contract.md +29 -16
  14. package/AGENTS.md +6 -4
  15. package/README.md +135 -76
  16. package/dist/cli.js +8 -8
  17. package/dist/core-api/reconstruct-api.js +117 -31
  18. package/dist/core-api/review-api.js +47 -0
  19. package/dist/core-runtime/cli/codex-review-unit-executor.js +39 -2
  20. package/dist/core-runtime/cli/complete-review-session.js +2 -2
  21. package/dist/core-runtime/cli/mock-review-unit-executor.js +1 -1
  22. package/dist/core-runtime/cli/review-invoke.js +9 -9
  23. package/dist/core-runtime/cli/run-review-prompt-execution.js +39 -5
  24. package/dist/core-runtime/cli/spawn-watcher.js +266 -47
  25. package/dist/core-runtime/cli/start-review-session.js +3 -3
  26. package/dist/core-runtime/llm/llm-caller.js +11 -0
  27. package/dist/core-runtime/llm/llm-tool-loop.js +2 -0
  28. package/dist/core-runtime/observability/runtime-stream-observation.js +118 -0
  29. package/dist/core-runtime/onboard/cli-host.js +149 -0
  30. package/dist/core-runtime/onboard/host-target.js +22 -0
  31. package/dist/core-runtime/onboard/json-config-host.js +122 -0
  32. package/dist/core-runtime/onboard/path-scan.js +26 -0
  33. package/dist/core-runtime/onboard/prompt.js +51 -0
  34. package/dist/core-runtime/onboard/register.js +207 -0
  35. package/dist/core-runtime/onboard/types.js +27 -0
  36. package/dist/core-runtime/reconstruct/actionable-seed-validation.js +1777 -0
  37. package/dist/core-runtime/reconstruct/artifact-types.js +10 -4
  38. package/dist/core-runtime/reconstruct/contract-registry.js +623 -0
  39. package/dist/core-runtime/reconstruct/domain-id.js +10 -0
  40. package/dist/core-runtime/reconstruct/governing-snapshot.js +716 -0
  41. package/dist/core-runtime/reconstruct/material-profile-validation.js +191 -0
  42. package/dist/core-runtime/reconstruct/materialize-preparation.js +49 -11
  43. package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +269 -79
  44. package/dist/core-runtime/reconstruct/post-seed-validation.js +1194 -51
  45. package/dist/core-runtime/reconstruct/record.js +104 -20
  46. package/dist/core-runtime/reconstruct/run.js +2107 -413
  47. package/dist/core-runtime/reconstruct/seed-claim-projections.js +268 -0
  48. package/dist/core-runtime/reconstruct/source-profiles.js +93 -4
  49. package/dist/core-runtime/reconstruct/terminal-validation.js +807 -0
  50. package/dist/core-runtime/review/review-invocation-runner.js +4 -4
  51. package/dist/mcp/server.js +110 -38
  52. package/dist/mcp/tool-schemas.js +20 -6
  53. package/package.json +8 -17
  54. package/scripts/onto-review-watch.sh +486 -0
  55. package/scripts/onto-runtime-watch.sh +122 -0
  56. package/scripts/postinstall-hint.js +22 -0
  57. package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +0 -387
  58. package/dist/core-runtime/cli/bootstrap-review-binding.js +0 -186
  59. package/dist/core-runtime/cli/codex-nested-dispatch.test.js +0 -390
  60. package/dist/core-runtime/cli/codex-nested-teamlead-executor.test.js +0 -335
  61. package/dist/core-runtime/cli/coordinator-helpers.js +0 -583
  62. package/dist/core-runtime/cli/coordinator-state-machine-deliberation.test.js +0 -167
  63. package/dist/core-runtime/cli/coordinator-state-machine.js +0 -794
  64. package/dist/core-runtime/cli/e2e-codex-multi-agent-fixes.test.js +0 -615
  65. package/dist/core-runtime/cli/e2e-start-review-session.test.js +0 -312
  66. package/dist/core-runtime/cli/health.js +0 -44
  67. package/dist/core-runtime/cli/inline-http-review-unit-executor.test.js +0 -567
  68. package/dist/core-runtime/cli/materialize-review-execution-preparation.js +0 -104
  69. package/dist/core-runtime/cli/migrate-session-roots.js +0 -118
  70. package/dist/core-runtime/cli/repo-layout-migration-replace.smoke.test.js +0 -106
  71. package/dist/core-runtime/cli/review-invoke-auto-resolution.test.js +0 -268
  72. package/dist/core-runtime/cli/review-invoke-coordinator-topology.test.js +0 -136
  73. package/dist/core-runtime/cli/review-invoke-resolver-caching.test.js +0 -201
  74. package/dist/core-runtime/cli/review-invoke-topology-dispatch.test.js +0 -192
  75. package/dist/core-runtime/cli/session-root-guard.js +0 -168
  76. package/dist/core-runtime/cli/spawn-watcher.test.js +0 -457
  77. package/dist/core-runtime/cli/strip-wrapping-code-fence.test.js +0 -79
  78. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.js +0 -412
  79. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.test.js +0 -351
  80. package/dist/core-runtime/cli/topology-executor-mapping.js +0 -139
  81. package/dist/core-runtime/cli/topology-executor-mapping.test.js +0 -173
  82. package/dist/core-runtime/cli/write-review-interpretation.js +0 -81
  83. package/dist/core-runtime/config/onto-config-cli.js +0 -278
  84. package/dist/core-runtime/config/onto-config-key-path.js +0 -288
  85. package/dist/core-runtime/config/onto-config-key-path.test.js +0 -195
  86. package/dist/core-runtime/config/onto-config-preview.js +0 -108
  87. package/dist/core-runtime/config/onto-config-preview.test.js +0 -132
  88. package/dist/core-runtime/discovery/config-chain.js +0 -118
  89. package/dist/core-runtime/discovery/config-chain.test.js +0 -103
  90. package/dist/core-runtime/discovery/config-profile.js +0 -199
  91. package/dist/core-runtime/discovery/config-profile.test.js +0 -233
  92. package/dist/core-runtime/discovery/host-detection.test.js +0 -186
  93. package/dist/core-runtime/discovery/installation-paths.test.js +0 -65
  94. package/dist/core-runtime/discovery/lens-registry.test.js +0 -81
  95. package/dist/core-runtime/discovery/path-normalization.test.js +0 -22
  96. package/dist/core-runtime/discovery/plugin-path.js +0 -72
  97. package/dist/core-runtime/discovery/plugin-path.test.js +0 -95
  98. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.js +0 -344
  99. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.test.js +0 -915
  100. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.js +0 -564
  101. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.test.js +0 -708
  102. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.js +0 -165
  103. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.test.js +0 -227
  104. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.js +0 -59
  105. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.test.js +0 -205
  106. package/dist/core-runtime/evolve/adapters/methodology/adapter.js +0 -16
  107. package/dist/core-runtime/evolve/adapters/methodology/adapter.test.js +0 -9
  108. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.js +0 -298
  109. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.test.js +0 -70
  110. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.js +0 -46
  111. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.test.js +0 -73
  112. package/dist/core-runtime/evolve/adapters/registry.js +0 -47
  113. package/dist/core-runtime/evolve/adapters/registry.test.js +0 -67
  114. package/dist/core-runtime/evolve/cli.js +0 -256
  115. package/dist/core-runtime/evolve/commands/align.js +0 -194
  116. package/dist/core-runtime/evolve/commands/align.test.js +0 -82
  117. package/dist/core-runtime/evolve/commands/apply.js +0 -161
  118. package/dist/core-runtime/evolve/commands/apply.test.js +0 -138
  119. package/dist/core-runtime/evolve/commands/close.js +0 -39
  120. package/dist/core-runtime/evolve/commands/close.test.js +0 -99
  121. package/dist/core-runtime/evolve/commands/defer.js +0 -40
  122. package/dist/core-runtime/evolve/commands/defer.test.js +0 -134
  123. package/dist/core-runtime/evolve/commands/draft.js +0 -323
  124. package/dist/core-runtime/evolve/commands/draft.test.js +0 -178
  125. package/dist/core-runtime/evolve/commands/e2e-evolve-full-cycle.test.js +0 -208
  126. package/dist/core-runtime/evolve/commands/error-messages.js +0 -125
  127. package/dist/core-runtime/evolve/commands/error-messages.test.js +0 -167
  128. package/dist/core-runtime/evolve/commands/propose-align.js +0 -222
  129. package/dist/core-runtime/evolve/commands/propose-align.test.js +0 -136
  130. package/dist/core-runtime/evolve/commands/reconstruct.js +0 -330
  131. package/dist/core-runtime/evolve/commands/reconstruct.test.js +0 -278
  132. package/dist/core-runtime/evolve/commands/shared.js +0 -22
  133. package/dist/core-runtime/evolve/commands/stale-check.js +0 -103
  134. package/dist/core-runtime/evolve/commands/stale-check.test.js +0 -84
  135. package/dist/core-runtime/evolve/commands/start.js +0 -887
  136. package/dist/core-runtime/evolve/commands/start.test.js +0 -396
  137. package/dist/core-runtime/evolve/config/project-config.js +0 -99
  138. package/dist/core-runtime/evolve/config/project-config.test.js +0 -170
  139. package/dist/core-runtime/evolve/renderers/align-packet.js +0 -280
  140. package/dist/core-runtime/evolve/renderers/align-packet.test.js +0 -332
  141. package/dist/core-runtime/evolve/renderers/draft-packet.js +0 -303
  142. package/dist/core-runtime/evolve/renderers/draft-packet.test.js +0 -377
  143. package/dist/core-runtime/evolve/renderers/format.js +0 -5
  144. package/dist/core-runtime/evolve/renderers/scope-md.js +0 -237
  145. package/dist/core-runtime/evolve/renderers/scope-md.test.js +0 -306
  146. package/dist/core-runtime/govern/cli.js +0 -369
  147. package/dist/core-runtime/govern/cli.test.js +0 -314
  148. package/dist/core-runtime/govern/drift-engine.js +0 -103
  149. package/dist/core-runtime/govern/drift-engine.test.js +0 -319
  150. package/dist/core-runtime/govern/promote-principle.js +0 -206
  151. package/dist/core-runtime/govern/promote-principle.test.js +0 -368
  152. package/dist/core-runtime/govern/queue.js +0 -81
  153. package/dist/core-runtime/govern/types.js +0 -16
  154. package/dist/core-runtime/install/cli.js +0 -530
  155. package/dist/core-runtime/install/detect.js +0 -128
  156. package/dist/core-runtime/install/detect.test.js +0 -155
  157. package/dist/core-runtime/install/gitignore-update.js +0 -74
  158. package/dist/core-runtime/install/gitignore-update.test.js +0 -64
  159. package/dist/core-runtime/install/install-integration.test.js +0 -373
  160. package/dist/core-runtime/install/prompts.js +0 -389
  161. package/dist/core-runtime/install/prompts.test.js +0 -293
  162. package/dist/core-runtime/install/types.js +0 -26
  163. package/dist/core-runtime/install/validation.js +0 -295
  164. package/dist/core-runtime/install/validation.test.js +0 -313
  165. package/dist/core-runtime/install/writer.js +0 -254
  166. package/dist/core-runtime/install/writer.test.js +0 -218
  167. package/dist/core-runtime/learning/extractor.js +0 -461
  168. package/dist/core-runtime/learning/feedback.js +0 -179
  169. package/dist/core-runtime/learning/health-report.js +0 -165
  170. package/dist/core-runtime/learning/health-report.test.js +0 -169
  171. package/dist/core-runtime/learning/loader.js +0 -388
  172. package/dist/core-runtime/learning/loader.test.js +0 -102
  173. package/dist/core-runtime/learning/promote/apply-state.js +0 -240
  174. package/dist/core-runtime/learning/promote/audit-obligation.js +0 -195
  175. package/dist/core-runtime/learning/promote/collector.js +0 -432
  176. package/dist/core-runtime/learning/promote/degraded-state.js +0 -125
  177. package/dist/core-runtime/learning/promote/domain-doc-proposer.js +0 -166
  178. package/dist/core-runtime/learning/promote/e2e-promote.test.js +0 -6385
  179. package/dist/core-runtime/learning/promote/health-snapshot.js +0 -150
  180. package/dist/core-runtime/learning/promote/insight-reclassifier.js +0 -544
  181. package/dist/core-runtime/learning/promote/judgment-auditor.js +0 -517
  182. package/dist/core-runtime/learning/promote/panel-reviewer.js +0 -1158
  183. package/dist/core-runtime/learning/promote/promote-executor.js +0 -1675
  184. package/dist/core-runtime/learning/promote/promoter.js +0 -307
  185. package/dist/core-runtime/learning/promote/retirement.js +0 -122
  186. package/dist/core-runtime/learning/promote/types.js +0 -23
  187. package/dist/core-runtime/learning/prompt-sections.js +0 -51
  188. package/dist/core-runtime/learning/shared/artifact-registry-init.js +0 -45
  189. package/dist/core-runtime/learning/shared/artifact-registry.js +0 -254
  190. package/dist/core-runtime/learning/shared/audit-obligation-kernel.js +0 -73
  191. package/dist/core-runtime/learning/shared/audit-state.js +0 -99
  192. package/dist/core-runtime/learning/shared/duplicate-check.js +0 -28
  193. package/dist/core-runtime/learning/shared/llm-caller.js +0 -831
  194. package/dist/core-runtime/learning/shared/llm-caller.test.js +0 -601
  195. package/dist/core-runtime/learning/shared/llm-tool-loop.js +0 -393
  196. package/dist/core-runtime/learning/shared/mode.js +0 -25
  197. package/dist/core-runtime/learning/shared/paths.js +0 -84
  198. package/dist/core-runtime/learning/shared/paths.test.js +0 -79
  199. package/dist/core-runtime/learning/shared/patterns.js +0 -37
  200. package/dist/core-runtime/learning/shared/recoverability.js +0 -355
  201. package/dist/core-runtime/learning/shared/recovery-context.js +0 -374
  202. package/dist/core-runtime/learning/shared/scope.js +0 -1
  203. package/dist/core-runtime/learning/shared/semantic-classifier.js +0 -94
  204. package/dist/core-runtime/learning/shared/specs/apply-execution-state-spec.js +0 -42
  205. package/dist/core-runtime/learning/shared/specs/audit-state-spec.js +0 -37
  206. package/dist/core-runtime/learning/shared/specs/backup-metadata-spec.js +0 -39
  207. package/dist/core-runtime/learning/shared/specs/emergency-log-spec.js +0 -41
  208. package/dist/core-runtime/learning/shared/specs/layout-version-spec.js +0 -38
  209. package/dist/core-runtime/learning/shared/specs/promote-decisions-spec.js +0 -43
  210. package/dist/core-runtime/learning/shared/specs/promote-report-spec.js +0 -113
  211. package/dist/core-runtime/learning/shared/specs/prune-log-spec.js +0 -36
  212. package/dist/core-runtime/learning/shared/specs/recovery-resolution-spec.js +0 -48
  213. package/dist/core-runtime/learning/shared/specs/restore-manifest-spec.js +0 -43
  214. package/dist/core-runtime/learning/shared/specs/spec-helpers.js +0 -64
  215. package/dist/core-runtime/learning/usage-tracker.js +0 -190
  216. package/dist/core-runtime/learning/usage-tracker.test.js +0 -176
  217. package/dist/core-runtime/onboard/detect-review-axes.js +0 -122
  218. package/dist/core-runtime/onboard/detect-review-axes.test.js +0 -127
  219. package/dist/core-runtime/onboard/write-review-block.js +0 -188
  220. package/dist/core-runtime/onboard/write-review-block.test.js +0 -240
  221. package/dist/core-runtime/readers/brownfield-builder.js +0 -150
  222. package/dist/core-runtime/readers/brownfield-builder.test.js +0 -136
  223. package/dist/core-runtime/readers/code-chunk-collector.js +0 -53
  224. package/dist/core-runtime/readers/code-chunk-collector.test.js +0 -136
  225. package/dist/core-runtime/readers/file-utils.js +0 -240
  226. package/dist/core-runtime/readers/file-utils.test.js +0 -146
  227. package/dist/core-runtime/readers/lexicon-citation-check.js +0 -93
  228. package/dist/core-runtime/readers/lexicon-citation-check.test.js +0 -77
  229. package/dist/core-runtime/readers/mcp-figma.js +0 -30
  230. package/dist/core-runtime/readers/mcp-figma.test.js +0 -82
  231. package/dist/core-runtime/readers/mcp-generic.js +0 -31
  232. package/dist/core-runtime/readers/mcp-generic.test.js +0 -76
  233. package/dist/core-runtime/readers/ontology-index.js +0 -148
  234. package/dist/core-runtime/readers/ontology-index.test.js +0 -245
  235. package/dist/core-runtime/readers/ontology-query.js +0 -168
  236. package/dist/core-runtime/readers/ontology-query.test.js +0 -311
  237. package/dist/core-runtime/readers/ontology-resolve.js +0 -48
  238. package/dist/core-runtime/readers/ontology-resolve.test.js +0 -48
  239. package/dist/core-runtime/readers/patterns/index.js +0 -7
  240. package/dist/core-runtime/readers/review-log.js +0 -213
  241. package/dist/core-runtime/readers/review-log.test.js +0 -313
  242. package/dist/core-runtime/readers/scan-local.js +0 -102
  243. package/dist/core-runtime/readers/scan-local.test.js +0 -102
  244. package/dist/core-runtime/readers/scan-tarball.js +0 -121
  245. package/dist/core-runtime/readers/scan-tarball.test.js +0 -283
  246. package/dist/core-runtime/readers/scan-vault.js +0 -34
  247. package/dist/core-runtime/readers/scan-vault.test.js +0 -81
  248. package/dist/core-runtime/readers/types.js +0 -42
  249. package/dist/core-runtime/readers/types.test.js +0 -94
  250. package/dist/core-runtime/readers/viewpoint-collectors.js +0 -229
  251. package/dist/core-runtime/reconstruct/seed-candidate-validation.js +0 -385
  252. package/dist/core-runtime/review/citation-audit.test.js +0 -165
  253. package/dist/core-runtime/review/execution-plan-resolver.js +0 -247
  254. package/dist/core-runtime/review/execution-plan-resolver.test.js +0 -243
  255. package/dist/core-runtime/review/execution-topology-resolver-axis-first.test.js +0 -246
  256. package/dist/core-runtime/review/execution-topology-resolver.js +0 -401
  257. package/dist/core-runtime/review/execution-topology-resolver.test.js +0 -315
  258. package/dist/core-runtime/review/inline-context-embedder.test.js +0 -154
  259. package/dist/core-runtime/review/legacy-mode-policy.js +0 -88
  260. package/dist/core-runtime/review/materializers-effort-persist.test.js +0 -79
  261. package/dist/core-runtime/review/ontology-path-classifier.js +0 -179
  262. package/dist/core-runtime/review/ontology-path-classifier.test.js +0 -216
  263. package/dist/core-runtime/review/packet-boundary-policy.test.js +0 -107
  264. package/dist/core-runtime/review/participating-lens-paths.test.js +0 -73
  265. package/dist/core-runtime/review/review-config-legacy-translate.js +0 -244
  266. package/dist/core-runtime/review/review-config-legacy-translate.test.js +0 -161
  267. package/dist/core-runtime/review/review-config-validator.js +0 -289
  268. package/dist/core-runtime/review/review-config-validator.test.js +0 -236
  269. package/dist/core-runtime/review/shape-pipeline-audit.test.js +0 -311
  270. package/dist/core-runtime/review/shape-to-topology-id.js +0 -117
  271. package/dist/core-runtime/review/shape-to-topology-id.test.js +0 -132
  272. package/dist/core-runtime/review/topology-shape-derivation.js +0 -155
  273. package/dist/core-runtime/review/topology-shape-derivation.test.js +0 -195
  274. package/dist/core-runtime/scope-runtime/constants.js +0 -12
  275. package/dist/core-runtime/scope-runtime/constraint-pool.js +0 -166
  276. package/dist/core-runtime/scope-runtime/constraint-pool.test.js +0 -674
  277. package/dist/core-runtime/scope-runtime/domain-validation-log.js +0 -135
  278. package/dist/core-runtime/scope-runtime/domain-validation-log.test.js +0 -156
  279. package/dist/core-runtime/scope-runtime/eval-persistence.js +0 -65
  280. package/dist/core-runtime/scope-runtime/eval-persistence.test.js +0 -84
  281. package/dist/core-runtime/scope-runtime/event-pipeline.js +0 -64
  282. package/dist/core-runtime/scope-runtime/event-pipeline.test.js +0 -450
  283. package/dist/core-runtime/scope-runtime/event-store.js +0 -39
  284. package/dist/core-runtime/scope-runtime/event-store.test.js +0 -95
  285. package/dist/core-runtime/scope-runtime/gate-guard.js +0 -348
  286. package/dist/core-runtime/scope-runtime/gate-guard.test.js +0 -1047
  287. package/dist/core-runtime/scope-runtime/hash.js +0 -4
  288. package/dist/core-runtime/scope-runtime/hash.test.js +0 -33
  289. package/dist/core-runtime/scope-runtime/id.js +0 -4
  290. package/dist/core-runtime/scope-runtime/id.test.js +0 -17
  291. package/dist/core-runtime/scope-runtime/reducer.js +0 -297
  292. package/dist/core-runtime/scope-runtime/reducer.test.js +0 -759
  293. package/dist/core-runtime/scope-runtime/scope-manager.js +0 -161
  294. package/dist/core-runtime/scope-runtime/state-machine.js +0 -309
  295. package/dist/core-runtime/scope-runtime/state-machine.test.js +0 -704
  296. package/dist/core-runtime/scope-runtime/types.js +0 -116
  297. package/dist/core-runtime/scope-runtime/types.test.js +0 -69
  298. package/dist/core-runtime/translate/render-for-user.js +0 -169
  299. package/dist/core-runtime/translate/render-for-user.test.js +0 -122
  300. package/dist/providers/capability-contract.js +0 -1
@@ -1,313 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { mkdirSync, writeFileSync, rmSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { tmpdir } from "node:os";
5
- import { collectReviewLogs, computeProgressiveness } from "./review-log.js";
6
- // ─── Test fixtures ───
7
- const PROJECT_ROOT = "/mock/project";
8
- function makeExecResult(overrides = {}) {
9
- return {
10
- session_id: "20260413-aaa00001",
11
- session_root: "/mock/.onto/review/20260413-aaa00001",
12
- execution_realization: "subagent",
13
- host_runtime: "codex",
14
- review_mode: "full",
15
- execution_status: "completed",
16
- execution_started_at: "2026-04-13T10:00:00+09:00",
17
- execution_completed_at: "2026-04-13T10:01:48+09:00",
18
- total_duration_ms: 107745,
19
- planned_lens_ids: ["logic", "structure", "axiology"],
20
- participating_lens_ids: ["logic", "structure", "axiology"],
21
- degraded_lens_ids: [],
22
- excluded_lens_ids: [],
23
- executed_lens_count: 3,
24
- synthesis_executed: true,
25
- deliberation_status: null,
26
- halt_reason: null,
27
- error_log_path: "error-log.md",
28
- lens_execution_results: [
29
- {
30
- unit_id: "logic",
31
- unit_kind: "lens",
32
- packet_path: "prompt-packets/logic.prompt.md",
33
- output_path: "round1/logic.md",
34
- status: "completed",
35
- started_at: "2026-04-13T10:00:00+09:00",
36
- completed_at: "2026-04-13T10:00:32+09:00",
37
- duration_ms: 32517,
38
- timestamp_provenance: "runner_wallclock",
39
- failure_message: null,
40
- },
41
- {
42
- unit_id: "structure",
43
- unit_kind: "lens",
44
- packet_path: "prompt-packets/structure.prompt.md",
45
- output_path: "round1/structure.md",
46
- status: "completed",
47
- started_at: "2026-04-13T10:00:00+09:00",
48
- completed_at: "2026-04-13T10:00:36+09:00",
49
- duration_ms: 36342,
50
- timestamp_provenance: "runner_wallclock",
51
- failure_message: null,
52
- },
53
- {
54
- unit_id: "axiology",
55
- unit_kind: "lens",
56
- packet_path: "prompt-packets/axiology.prompt.md",
57
- output_path: "round1/axiology.md",
58
- status: "completed",
59
- started_at: "2026-04-13T10:00:00+09:00",
60
- completed_at: "2026-04-13T10:00:33+09:00",
61
- duration_ms: 32791,
62
- failure_message: null,
63
- },
64
- ],
65
- synthesize_execution_result: {
66
- unit_id: "synthesize",
67
- unit_kind: "synthesize",
68
- packet_path: "prompt-packets/synthesize.runtime.prompt.md",
69
- output_path: "synthesis.md",
70
- status: "completed",
71
- started_at: "2026-04-13T10:01:06+09:00",
72
- completed_at: "2026-04-13T10:01:48+09:00",
73
- duration_ms: 42000,
74
- timestamp_provenance: "runner_wallclock",
75
- failure_message: null,
76
- },
77
- ...overrides,
78
- };
79
- }
80
- function makeReviewRecord(overrides = {}) {
81
- return {
82
- review_record_id: "20260413-aaa00001",
83
- session_id: "20260413-aaa00001",
84
- entrypoint: "review",
85
- record_status: "completed",
86
- created_at: "2026-04-13T10:00:00+09:00",
87
- updated_at: "2026-04-13T10:01:48+09:00",
88
- request_text: "design doc 검토",
89
- review_target_scope_ref: "binding.yaml",
90
- ...overrides,
91
- };
92
- }
93
- function makeBinding(refs = ["/mock/project/.onto/processes/evolve.md"]) {
94
- return {
95
- resolved_target_scope: {
96
- kind: "file",
97
- resolved_refs: refs,
98
- },
99
- };
100
- }
101
- function yamlStringify(obj) {
102
- // vitest 환경에서는 yaml 패키지 대신 간단한 변환 사용
103
- const { stringify } = require("yaml");
104
- return stringify(obj);
105
- }
106
- // ─── Temp dir setup ───
107
- let testDir;
108
- function setupReviewDir() {
109
- testDir = join(tmpdir(), `review-log-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
110
- mkdirSync(testDir, { recursive: true });
111
- return testDir;
112
- }
113
- function createSession(reviewRoot, sessionId, execOverrides = {}, recordOverrides = {}, bindingRefs = ["/mock/project/.onto/processes/evolve.md"]) {
114
- const sessionDir = join(reviewRoot, sessionId);
115
- mkdirSync(sessionDir, { recursive: true });
116
- writeFileSync(join(sessionDir, "execution-result.yaml"), yamlStringify(makeExecResult({ session_id: sessionId, ...execOverrides })));
117
- writeFileSync(join(sessionDir, "review-record.yaml"), yamlStringify(makeReviewRecord({ session_id: sessionId, review_record_id: sessionId, ...recordOverrides })));
118
- writeFileSync(join(sessionDir, "binding.yaml"), yamlStringify(makeBinding(bindingRefs)));
119
- }
120
- // ─── Tests ───
121
- describe("review-log", () => {
122
- beforeEach(() => {
123
- setupReviewDir();
124
- });
125
- afterEach(() => {
126
- if (testDir) {
127
- rmSync(testDir, { recursive: true, force: true });
128
- }
129
- });
130
- describe("collectReviewLogs", () => {
131
- it("빈 디렉터리에서 빈 summary 를 반환한다", () => {
132
- const result = collectReviewLogs(testDir, PROJECT_ROOT);
133
- expect(result.total_sessions).toBe(0);
134
- expect(result.entries).toHaveLength(0);
135
- expect(result.progressiveness).toHaveLength(0);
136
- });
137
- it("존재하지 않는 경로에서 빈 summary 를 반환한다", () => {
138
- const result = collectReviewLogs("/nonexistent/path", PROJECT_ROOT);
139
- expect(result.total_sessions).toBe(0);
140
- });
141
- it("단일 세션을 수집한다", () => {
142
- createSession(testDir, "20260413-aaa00001");
143
- const result = collectReviewLogs(testDir, PROJECT_ROOT);
144
- expect(result.total_sessions).toBe(1);
145
- expect(result.entries).toHaveLength(1);
146
- const entry = result.entries[0];
147
- expect(entry.session_id).toBe("20260413-aaa00001");
148
- expect(entry.review_mode).toBe("full");
149
- expect(entry.execution_status).toBe("completed");
150
- expect(entry.total_duration_ms).toBe(107745);
151
- expect(entry.lens_count).toBe(3);
152
- expect(entry.participating_lens_ids).toEqual(["logic", "structure", "axiology"]);
153
- expect(entry.request_text).toBe("design doc 검토");
154
- });
155
- it("per-lens duration 을 정확히 수집한다", () => {
156
- createSession(testDir, "20260413-aaa00001");
157
- const result = collectReviewLogs(testDir, PROJECT_ROOT);
158
- const entry = result.entries[0];
159
- expect(entry.per_lens_duration).toHaveLength(3);
160
- const logic = entry.per_lens_duration.find((d) => d.lens_id === "logic");
161
- expect(logic).toBeDefined();
162
- expect(logic.duration_ms).toBe(32517);
163
- expect(logic.provenance).toBe("runner_wallclock");
164
- });
165
- it("provenance summary 를 정확히 산출한다", () => {
166
- createSession(testDir, "20260413-aaa00001");
167
- const result = collectReviewLogs(testDir, PROJECT_ROOT);
168
- const prov = result.entries[0].provenance_summary;
169
- // logic + structure = 2 runner_wallclock, axiology = unknown (provenance 없음), synth = 1 runner_wallclock
170
- expect(prov.runner_wallclock).toBe(3);
171
- expect(prov.unknown).toBe(1); // axiology 는 provenance 필드 없음
172
- });
173
- it("synthesis duration 을 수집한다", () => {
174
- createSession(testDir, "20260413-aaa00001");
175
- const result = collectReviewLogs(testDir, PROJECT_ROOT);
176
- expect(result.entries[0].synthesis_duration_ms).toBe(42000);
177
- });
178
- it("synthesis 가 없는 세션을 처리한다", () => {
179
- createSession(testDir, "20260413-aaa00001", {
180
- synthesis_executed: false,
181
- synthesize_execution_result: null,
182
- });
183
- const result = collectReviewLogs(testDir, PROJECT_ROOT);
184
- expect(result.entries[0].synthesis_duration_ms).toBeNull();
185
- });
186
- it("여러 세션을 시간순 정렬한다", () => {
187
- createSession(testDir, "20260413-bbb00002", {}, {
188
- created_at: "2026-04-13T12:00:00+09:00",
189
- });
190
- createSession(testDir, "20260413-aaa00001", {}, {
191
- created_at: "2026-04-13T10:00:00+09:00",
192
- });
193
- const result = collectReviewLogs(testDir, PROJECT_ROOT);
194
- expect(result.entries).toHaveLength(2);
195
- expect(result.entries[0].session_id).toBe("20260413-aaa00001");
196
- expect(result.entries[1].session_id).toBe("20260413-bbb00002");
197
- });
198
- it("target refs 를 프로젝트 루트 기준으로 정규화한다", () => {
199
- createSession(testDir, "20260413-aaa00001", {}, {}, ["/mock/project/.onto/processes/evolve.md"]);
200
- const result = collectReviewLogs(testDir, "/mock/project");
201
- expect(result.entries[0].review_target_refs).toEqual([".onto/processes/evolve.md"]);
202
- });
203
- it("execution-result.yaml 없는 세션은 건너뛴다", () => {
204
- // execution-result.yaml 없이 디렉터리만 생성
205
- const sessionDir = join(testDir, "20260413-ccc00003");
206
- mkdirSync(sessionDir, { recursive: true });
207
- writeFileSync(join(sessionDir, "review-record.yaml"), yamlStringify(makeReviewRecord()));
208
- const result = collectReviewLogs(testDir, PROJECT_ROOT);
209
- expect(result.total_sessions).toBe(0);
210
- });
211
- it("total_duration_ms 전체 합산을 반환한다", () => {
212
- createSession(testDir, "20260413-aaa00001", { total_duration_ms: 50000 }, {
213
- created_at: "2026-04-13T10:00:00+09:00",
214
- });
215
- createSession(testDir, "20260413-bbb00002", { total_duration_ms: 30000 }, {
216
- created_at: "2026-04-13T11:00:00+09:00",
217
- });
218
- const result = collectReviewLogs(testDir, PROJECT_ROOT);
219
- expect(result.total_duration_ms).toBe(80000);
220
- });
221
- });
222
- describe("computeProgressiveness", () => {
223
- it("세션 2건 미만 그룹은 제외한다", () => {
224
- const entries = [
225
- makeLogEntry("s1", ["fileA.md"], 100000, "2026-04-13T10:00:00+09:00"),
226
- ];
227
- const metrics = computeProgressiveness(entries);
228
- expect(metrics).toHaveLength(0);
229
- });
230
- it("동일 대상 2회 review 시 점진성 metric 을 산출한다", () => {
231
- const entries = [
232
- makeLogEntry("s1", ["fileA.md"], 100000, "2026-04-13T10:00:00+09:00"),
233
- makeLogEntry("s2", ["fileA.md"], 70000, "2026-04-13T12:00:00+09:00"),
234
- ];
235
- const metrics = computeProgressiveness(entries);
236
- expect(metrics).toHaveLength(1);
237
- const m = metrics[0];
238
- expect(m.session_count).toBe(2);
239
- expect(m.first_duration_ms).toBe(100000);
240
- expect(m.latest_duration_ms).toBe(70000);
241
- expect(m.duration_delta_ms).toBe(-30000);
242
- expect(m.duration_delta_ratio).toBe(-0.3);
243
- expect(m.avg_duration_ms).toBe(85000);
244
- });
245
- it("동일 대상 3회 review 시 첫/마지막 비교 metric 을 산출한다", () => {
246
- const entries = [
247
- makeLogEntry("s1", ["fileA.md"], 100000, "2026-04-13T10:00:00+09:00"),
248
- makeLogEntry("s2", ["fileA.md"], 80000, "2026-04-13T12:00:00+09:00"),
249
- makeLogEntry("s3", ["fileA.md"], 60000, "2026-04-13T14:00:00+09:00"),
250
- ];
251
- const metrics = computeProgressiveness(entries);
252
- expect(metrics[0].session_count).toBe(3);
253
- expect(metrics[0].duration_delta_ms).toBe(-40000);
254
- expect(metrics[0].duration_delta_ratio).toBe(-0.4);
255
- });
256
- it("서로 다른 대상은 별도 그룹으로 분리한다", () => {
257
- const entries = [
258
- makeLogEntry("s1", ["fileA.md"], 100000, "2026-04-13T10:00:00+09:00"),
259
- makeLogEntry("s2", ["fileA.md"], 70000, "2026-04-13T12:00:00+09:00"),
260
- makeLogEntry("s3", ["fileB.md"], 90000, "2026-04-13T11:00:00+09:00"),
261
- makeLogEntry("s4", ["fileB.md"], 85000, "2026-04-13T13:00:00+09:00"),
262
- ];
263
- const metrics = computeProgressiveness(entries);
264
- expect(metrics).toHaveLength(2);
265
- });
266
- it("target refs 순서가 달라도 동일 그룹으로 합친다", () => {
267
- const entries = [
268
- makeLogEntry("s1", ["fileB.md", "fileA.md"], 100000, "2026-04-13T10:00:00+09:00"),
269
- makeLogEntry("s2", ["fileA.md", "fileB.md"], 70000, "2026-04-13T12:00:00+09:00"),
270
- ];
271
- const metrics = computeProgressiveness(entries);
272
- expect(metrics).toHaveLength(1);
273
- expect(metrics[0].session_count).toBe(2);
274
- });
275
- it("target refs 가 빈 세션은 그룹에서 제외한다", () => {
276
- const entries = [
277
- makeLogEntry("s1", [], 100000, "2026-04-13T10:00:00+09:00"),
278
- makeLogEntry("s2", [], 70000, "2026-04-13T12:00:00+09:00"),
279
- ];
280
- const metrics = computeProgressiveness(entries);
281
- expect(metrics).toHaveLength(0);
282
- });
283
- it("시간이 증가한 경우 양수 delta 를 반환한다", () => {
284
- const entries = [
285
- makeLogEntry("s1", ["fileA.md"], 50000, "2026-04-13T10:00:00+09:00"),
286
- makeLogEntry("s2", ["fileA.md"], 80000, "2026-04-13T12:00:00+09:00"),
287
- ];
288
- const metrics = computeProgressiveness(entries);
289
- expect(metrics[0].duration_delta_ms).toBe(30000);
290
- expect(metrics[0].duration_delta_ratio).toBe(0.6);
291
- });
292
- });
293
- });
294
- // ─── Test helper ───
295
- function makeLogEntry(session_id, target_refs, total_duration_ms, created_at) {
296
- return {
297
- session_id,
298
- created_at,
299
- review_target_refs: target_refs,
300
- request_text: "test review",
301
- review_mode: "full",
302
- execution_realization: "subagent",
303
- host_runtime: "codex",
304
- execution_status: "completed",
305
- total_duration_ms,
306
- lens_count: 9,
307
- participating_lens_ids: ["logic"],
308
- degraded_lens_ids: [],
309
- per_lens_duration: [],
310
- synthesis_duration_ms: null,
311
- provenance_summary: { runner_wallclock: 0, coordinator_derived: 0, batch_fallback: 0, unknown: 0 },
312
- };
313
- }
@@ -1,102 +0,0 @@
1
- import { readFileSync, statSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { contentHash } from "../scope-runtime/hash.js";
4
- import { getLogger } from "../logger.js";
5
- import { walkDirectory, computeDirectoryHashFromMap, normalizePath } from "./file-utils.js";
6
- import { detectPatterns } from "./patterns/index.js";
7
- import { sourceKey, emptyScanResult } from "./types.js";
8
- /**
9
- * Scan a local file or directory and produce a ScanResult.
10
- *
11
- * Pure I/O function: reads file system, no network calls.
12
- * For single files, wraps the file as a 1-item scan.
13
- * For directories, walks the tree respecting .gitignore and exclusion rules.
14
- */
15
- export function scanLocal(source) {
16
- const fullPath = normalizePath(source.path);
17
- let stat;
18
- try {
19
- stat = statSync(fullPath);
20
- }
21
- catch {
22
- return emptyScanResult(source);
23
- }
24
- if (stat.isFile()) {
25
- return scanSingleFile(source, fullPath);
26
- }
27
- return scanDirectory(source, fullPath);
28
- }
29
- export function scanDirectory(source, rootPath) {
30
- const files = walkDirectory(rootPath);
31
- const deps = [];
32
- const apis = [];
33
- const schemas = [];
34
- const configs = [];
35
- const docs = [];
36
- const contentHashMap = new Map();
37
- for (const file of files) {
38
- const absPath = join(rootPath, file.path);
39
- let content;
40
- try {
41
- content = readFileSync(absPath, "utf-8");
42
- }
43
- catch (error) {
44
- getLogger().debug("scanDirectory: failed to read file", { path: absPath, error });
45
- contentHashMap.set(file.path, "unreadable");
46
- continue;
47
- }
48
- contentHashMap.set(file.path, contentHash(content));
49
- const patterns = detectPatterns(content, file.path);
50
- deps.push(...patterns.deps);
51
- apis.push(...patterns.apis);
52
- schemas.push(...patterns.schemas);
53
- configs.push(...patterns.configs);
54
- docs.push(...patterns.docs);
55
- }
56
- const dirHash = computeDirectoryHashFromMap(contentHashMap);
57
- return {
58
- source,
59
- scanned_at: new Date().toISOString(),
60
- files,
61
- content_hashes: { [sourceKey(source)]: dirHash },
62
- dependency_graph: deps,
63
- api_patterns: apis,
64
- schema_patterns: schemas,
65
- config_patterns: configs,
66
- doc_structure: docs,
67
- };
68
- }
69
- function scanSingleFile(source, filePath) {
70
- let content;
71
- try {
72
- content = readFileSync(filePath, "utf-8");
73
- }
74
- catch {
75
- return emptyScanResult(source);
76
- }
77
- const hash = contentHash(content);
78
- const fileName = filePath.split("/").pop() ?? filePath;
79
- let sizeBytes = content.length;
80
- try {
81
- sizeBytes = statSync(filePath).size;
82
- }
83
- catch { /* use content.length as fallback */ }
84
- const fileEntry = {
85
- path: fileName,
86
- category: "other",
87
- language: undefined,
88
- size_bytes: sizeBytes,
89
- };
90
- const patterns = detectPatterns(content, fileName);
91
- return {
92
- source,
93
- scanned_at: new Date().toISOString(),
94
- files: [fileEntry],
95
- content_hashes: { [sourceKey(source)]: hash },
96
- dependency_graph: patterns.deps,
97
- api_patterns: patterns.apis,
98
- schema_patterns: patterns.schemas,
99
- config_patterns: patterns.configs,
100
- doc_structure: patterns.docs,
101
- };
102
- }
@@ -1,102 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { mkdirSync, writeFileSync, rmSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { scanLocal } from "./scan-local.js";
5
- const TMP = join(import.meta.dirname ?? ".", ".tmp-scanlocal-test");
6
- beforeEach(() => mkdirSync(TMP, { recursive: true }));
7
- afterEach(() => rmSync(TMP, { recursive: true, force: true }));
8
- describe("scanLocal", () => {
9
- // Depends on patterns/ module (deferred — code parser infrastructure not yet absorbed)
10
- it.skip("scans a directory and returns ScanResult", () => {
11
- writeFileSync(join(TMP, "app.ts"), 'import { foo } from "./foo";\nconst x = process.env.API_KEY;');
12
- writeFileSync(join(TMP, "foo.ts"), "export const foo = 1;");
13
- const result = scanLocal({ type: "add-dir", path: TMP });
14
- expect(result.files.length).toBeGreaterThanOrEqual(2);
15
- expect(result.dependency_graph.length).toBeGreaterThan(0);
16
- expect(result.config_patterns.length).toBeGreaterThan(0);
17
- expect(Object.keys(result.content_hashes).length).toBe(1);
18
- });
19
- // Depends on patterns/ module (deferred)
20
- it.skip("scans a single file", () => {
21
- const filePath = join(TMP, "single.ts");
22
- writeFileSync(filePath, 'import { bar } from "bar";');
23
- const result = scanLocal({ type: "add-dir", path: filePath });
24
- expect(result.files).toHaveLength(1);
25
- expect(result.dependency_graph).toHaveLength(1);
26
- });
27
- it("returns empty for non-existent path", () => {
28
- const result = scanLocal({ type: "add-dir", path: join(TMP, "nope") });
29
- expect(result.files).toEqual([]);
30
- expect(result.dependency_graph).toEqual([]);
31
- });
32
- it("excludes .git and node_modules", () => {
33
- mkdirSync(join(TMP, ".git"), { recursive: true });
34
- writeFileSync(join(TMP, ".git", "config"), "git");
35
- mkdirSync(join(TMP, "node_modules", "pkg"), { recursive: true });
36
- writeFileSync(join(TMP, "node_modules", "pkg", "index.js"), "code");
37
- writeFileSync(join(TMP, "app.ts"), "const a = 1;");
38
- const result = scanLocal({ type: "add-dir", path: TMP });
39
- expect(result.files.map(f => f.path)).toEqual(["app.ts"]);
40
- });
41
- // Depends on patterns/ module (deferred)
42
- it.skip("detects API patterns in Java file", () => {
43
- writeFileSync(join(TMP, "Controller.java"), '@GetMapping("/api/users")\npublic List<User> getUsers() {}');
44
- const result = scanLocal({ type: "add-dir", path: TMP });
45
- expect(result.api_patterns.length).toBeGreaterThan(0);
46
- expect(result.api_patterns[0].method).toBe("GET");
47
- });
48
- // Depends on patterns/ module (deferred)
49
- it.skip("detects schema patterns", () => {
50
- writeFileSync(join(TMP, "V1__init.sql"), "CREATE TABLE users (\n id BIGINT PRIMARY KEY\n);");
51
- const result = scanLocal({ type: "add-dir", path: TMP });
52
- expect(result.schema_patterns.length).toBeGreaterThan(0);
53
- expect(result.schema_patterns[0].table).toBe("users");
54
- });
55
- it("produces consistent hashes", () => {
56
- writeFileSync(join(TMP, "a.ts"), "const a = 1;");
57
- const r1 = scanLocal({ type: "add-dir", path: TMP });
58
- const r2 = scanLocal({ type: "add-dir", path: TMP });
59
- expect(Object.keys(r1.content_hashes)[0]).toBe(Object.keys(r2.content_hashes)[0]);
60
- });
61
- // ─── W-A-76: ontology-absent path (r−) fallback grounding surface ───
62
- //
63
- // scan-local 이 ontology YAML 부재 시 review lens 에게 제공하는 최소 grounding 재료를
64
- // 검증한다. files 와 content_hashes 는 patterns/ 모듈 미이전과 독립적으로 산출되어야
65
- // r− 경로의 기준선 grounding 을 보장한다.
66
- describe("W-A-76 r− fallback grounding", () => {
67
- it("ontology YAML 이 전혀 없어도 files 배열에 직접 파일들을 나열한다", () => {
68
- writeFileSync(join(TMP, "README.md"), "# project\n");
69
- writeFileSync(join(TMP, "service.ts"), "export const x = 1;\n");
70
- writeFileSync(join(TMP, "config.yaml"), "key: value\n");
71
- const result = scanLocal({ type: "add-dir", path: TMP });
72
- // lens 가 grounding 에 쓸 수 있는 최소 signal: 파일 경로 + content_hash
73
- const paths = result.files.map((f) => f.path).sort();
74
- expect(paths).toEqual(["README.md", "config.yaml", "service.ts"]);
75
- expect(Object.keys(result.content_hashes).length).toBe(1);
76
- });
77
- it("code-mapping.yaml / behavior.yaml / model.yaml 이 존재해도 scan-local 자체는 ontology 를 consume 하지 않는다", () => {
78
- // r− 경로는 scan-local 을 사용하는데, scan-local 은 ontology 구조를 해석하지 않고
79
- // 파일만 나열한다. 이것이 r− 경로의 grounding 한계를 구조적으로 보장한다.
80
- writeFileSync(join(TMP, "code-mapping.yaml"), "glossary: []\n");
81
- writeFileSync(join(TMP, "behavior.yaml"), "actions: []\n");
82
- writeFileSync(join(TMP, "model.yaml"), "entities: []\n");
83
- writeFileSync(join(TMP, "service.ts"), "export const x = 1;\n");
84
- const result = scanLocal({ type: "add-dir", path: TMP });
85
- // ontology YAML 들은 단순 파일로만 등장. matched_entities 같은 구조화된 산출 없음.
86
- const paths = result.files.map((f) => f.path).sort();
87
- expect(paths).toContain("code-mapping.yaml");
88
- expect(paths).toContain("behavior.yaml");
89
- expect(paths).toContain("model.yaml");
90
- expect(paths).toContain("service.ts");
91
- // scan-local 은 OntologyQueryResult 를 반환하지 않음 — 형식 자체가 다름.
92
- expect("matched_entities" in result).toBe(false);
93
- });
94
- it("빈 디렉터리도 r− fallback 의 적법한 baseline 을 반환한다 (files=[], 해시 구조 유지)", () => {
95
- const result = scanLocal({ type: "add-dir", path: TMP });
96
- expect(result.files).toEqual([]);
97
- // 디렉터리 스캔은 빈 해시 맵이라도 content_hashes 키를 하나 둔다 (source key).
98
- expect(Object.keys(result.content_hashes).length).toBe(1);
99
- // lens 는 이 baseline 에서 "대상 비어있음" 을 판단할 수 있다.
100
- });
101
- });
102
- });
@@ -1,121 +0,0 @@
1
- import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { tmpdir } from "node:os";
4
- import { execSync } from "node:child_process";
5
- import { scanLocal } from "./scan-local.js";
6
- /**
7
- * Resolve GitHub token: GITHUB_TOKEN env var first, then `gh auth token` fallback.
8
- */
9
- export function resolveGitHubToken() {
10
- if (process.env.GITHUB_TOKEN) {
11
- return process.env.GITHUB_TOKEN;
12
- }
13
- try {
14
- return execSync("gh auth token", { timeout: 5_000, stdio: "pipe" }).toString().trim();
15
- }
16
- catch {
17
- return undefined;
18
- }
19
- }
20
- /**
21
- * Download a GitHub tarball and scan its contents.
22
- *
23
- * Uses Node.js built-in `fetch` to download the tarball, extracts to a temp
24
- * directory with `tar xzf`, then delegates to scanLocal().
25
- * Temp directory is always cleaned up.
26
- *
27
- * Returns either a ScanResult or a ScanError.
28
- */
29
- export async function scanTarball(source, etag, cachedHash) {
30
- const tmpDir = mkdtempSync(join(tmpdir(), "sprint-kit-tarball-"));
31
- try {
32
- // Extract org/repo from URL
33
- const match = source.url.match(/github\.com\/([^/]+\/[^/]+)/);
34
- if (!match) {
35
- return {
36
- source,
37
- error_type: "parse",
38
- message: `Invalid GitHub URL: ${source.url}`,
39
- };
40
- }
41
- const repoPath = match[1].replace(/\.git$/, "");
42
- // Validate repoPath to prevent injection into URL
43
- if (!/^[\w.-]+\/[\w.-]+$/.test(repoPath)) {
44
- return {
45
- source,
46
- error_type: "parse",
47
- message: `Invalid repository path: ${repoPath}`,
48
- };
49
- }
50
- // Build request headers
51
- const headers = {
52
- Accept: "application/vnd.github+json",
53
- "User-Agent": "sprint-kit",
54
- };
55
- const token = resolveGitHubToken();
56
- if (token) {
57
- headers["Authorization"] = `Bearer ${token}`;
58
- }
59
- if (etag) {
60
- headers["If-None-Match"] = etag;
61
- }
62
- // Download tarball via fetch
63
- let response;
64
- try {
65
- response = await fetch(`https://api.github.com/repos/${repoPath}/tarball/HEAD`, {
66
- headers,
67
- signal: AbortSignal.timeout(120_000),
68
- });
69
- }
70
- catch (err) {
71
- const msg = err instanceof Error ? err.message : String(err);
72
- if (msg.includes("TimeoutError") || msg.includes("abort") || msg.includes("timeout")) {
73
- return { source, error_type: "timeout", message: `Timeout downloading ${repoPath}: ${msg}` };
74
- }
75
- return { source, error_type: "network", message: `Failed to download ${repoPath}: ${msg}` };
76
- }
77
- // Handle 304 Not Modified (ETag cache hit)
78
- if (response.status === 304 && cachedHash) {
79
- return { skipped: true, source, cached_hash: cachedHash };
80
- }
81
- // Handle HTTP error responses
82
- if (!response.ok) {
83
- if (response.status === 401 || response.status === 403) {
84
- const rateLimitRemaining = response.headers.get("X-RateLimit-Remaining");
85
- if (rateLimitRemaining === "0") {
86
- return { source, error_type: "auth", message: `GitHub API rate limit exceeded for ${repoPath}. GITHUB_TOKEN 환경변수를 설정하세요.` };
87
- }
88
- return { source, error_type: "auth", message: `Authentication failed for ${repoPath}: HTTP ${response.status}. Private 레포라면 GITHUB_TOKEN 환경변수를 설정하세요.` };
89
- }
90
- if (response.status === 404) {
91
- return { source, error_type: "not_found", message: `Repository not found: ${repoPath}` };
92
- }
93
- return { source, error_type: "network", message: `Failed to download ${repoPath}: HTTP ${response.status}` };
94
- }
95
- // Capture ETag for cache update
96
- const responseEtag = response.headers.get("ETag") ?? undefined;
97
- // Write tarball to temp file and extract
98
- const arrayBuffer = await response.arrayBuffer();
99
- const tarballPath = join(tmpDir, "tarball.tar.gz");
100
- writeFileSync(tarballPath, Buffer.from(arrayBuffer));
101
- execSync("tar xzf tarball.tar.gz --strip-components=1", {
102
- cwd: tmpDir,
103
- timeout: 120_000,
104
- stdio: "pipe",
105
- });
106
- // Scan the extracted directory
107
- const localSource = { type: "add-dir", path: tmpDir, description: source.description };
108
- const result = scanLocal(localSource);
109
- // Return with original source entry + ETag
110
- return { ...result, source, response_etag: responseEtag };
111
- }
112
- finally {
113
- // Always clean up
114
- try {
115
- rmSync(tmpDir, { recursive: true, force: true });
116
- }
117
- catch {
118
- // Best effort cleanup
119
- }
120
- }
121
- }