@mycodemap/mycodemap 0.5.0 → 0.5.2-beta.1

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 (254) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +77 -9
  3. package/dist/cli/commands/analyze.d.ts +18 -0
  4. package/dist/cli/commands/analyze.d.ts.map +1 -1
  5. package/dist/cli/commands/analyze.js +239 -6
  6. package/dist/cli/commands/analyze.js.map +1 -1
  7. package/dist/cli/commands/check.d.ts +22 -0
  8. package/dist/cli/commands/check.d.ts.map +1 -0
  9. package/dist/cli/commands/check.js +168 -0
  10. package/dist/cli/commands/check.js.map +1 -0
  11. package/dist/cli/commands/ci.d.ts +25 -0
  12. package/dist/cli/commands/ci.d.ts.map +1 -1
  13. package/dist/cli/commands/ci.js +139 -36
  14. package/dist/cli/commands/ci.js.map +1 -1
  15. package/dist/cli/commands/complexity.d.ts.map +1 -1
  16. package/dist/cli/commands/complexity.js +6 -0
  17. package/dist/cli/commands/complexity.js.map +1 -1
  18. package/dist/cli/commands/design.d.ts +5 -0
  19. package/dist/cli/commands/design.d.ts.map +1 -1
  20. package/dist/cli/commands/design.js +6 -0
  21. package/dist/cli/commands/design.js.map +1 -1
  22. package/dist/cli/commands/generate.d.ts +1 -0
  23. package/dist/cli/commands/generate.d.ts.map +1 -1
  24. package/dist/cli/commands/generate.js +121 -8
  25. package/dist/cli/commands/generate.js.map +1 -1
  26. package/dist/cli/commands/history.d.ts +26 -0
  27. package/dist/cli/commands/history.d.ts.map +1 -0
  28. package/dist/cli/commands/history.js +92 -0
  29. package/dist/cli/commands/history.js.map +1 -0
  30. package/dist/cli/commands/mcp.d.ts +13 -0
  31. package/dist/cli/commands/mcp.d.ts.map +1 -0
  32. package/dist/cli/commands/mcp.js +108 -0
  33. package/dist/cli/commands/mcp.js.map +1 -0
  34. package/dist/cli/commands/workflow.d.ts.map +1 -1
  35. package/dist/cli/commands/workflow.js +22 -2
  36. package/dist/cli/commands/workflow.js.map +1 -1
  37. package/dist/cli/config-loader.d.ts.map +1 -1
  38. package/dist/cli/config-loader.js +3 -2
  39. package/dist/cli/config-loader.js.map +1 -1
  40. package/dist/cli/contract-checker.d.ts +33 -0
  41. package/dist/cli/contract-checker.d.ts.map +1 -0
  42. package/dist/cli/contract-checker.js +719 -0
  43. package/dist/cli/contract-checker.js.map +1 -0
  44. package/dist/cli/contract-diff-scope.d.ts +14 -0
  45. package/dist/cli/contract-diff-scope.d.ts.map +1 -0
  46. package/dist/cli/contract-diff-scope.js +127 -0
  47. package/dist/cli/contract-diff-scope.js.map +1 -0
  48. package/dist/cli/contract-gate-thresholds.d.ts +14 -0
  49. package/dist/cli/contract-gate-thresholds.d.ts.map +1 -0
  50. package/dist/cli/contract-gate-thresholds.js +19 -0
  51. package/dist/cli/contract-gate-thresholds.js.map +1 -0
  52. package/dist/cli/design-contract-loader.d.ts.map +1 -1
  53. package/dist/cli/design-contract-loader.js +355 -3
  54. package/dist/cli/design-contract-loader.js.map +1 -1
  55. package/dist/cli/design-scope-resolver.d.ts.map +1 -1
  56. package/dist/cli/design-scope-resolver.js +89 -41
  57. package/dist/cli/design-scope-resolver.js.map +1 -1
  58. package/dist/cli/index.js +18 -6
  59. package/dist/cli/index.js.map +1 -1
  60. package/dist/cli/paths.d.ts.map +1 -1
  61. package/dist/cli/paths.js +30 -7
  62. package/dist/cli/paths.js.map +1 -1
  63. package/dist/core/analyzer.d.ts.map +1 -1
  64. package/dist/core/analyzer.js +16 -0
  65. package/dist/core/analyzer.js.map +1 -1
  66. package/dist/domain/entities/CodeGraph.d.ts +5 -1
  67. package/dist/domain/entities/CodeGraph.d.ts.map +1 -1
  68. package/dist/domain/entities/CodeGraph.js +29 -12
  69. package/dist/domain/entities/CodeGraph.js.map +1 -1
  70. package/dist/domain/entities/Dependency.d.ts +8 -1
  71. package/dist/domain/entities/Dependency.d.ts.map +1 -1
  72. package/dist/domain/entities/Dependency.js +19 -4
  73. package/dist/domain/entities/Dependency.js.map +1 -1
  74. package/dist/domain/entities/Symbol.d.ts +2 -1
  75. package/dist/domain/entities/Symbol.d.ts.map +1 -1
  76. package/dist/domain/entities/Symbol.js +6 -3
  77. package/dist/domain/entities/Symbol.js.map +1 -1
  78. package/dist/infrastructure/storage/StorageFactory.d.ts +1 -0
  79. package/dist/infrastructure/storage/StorageFactory.d.ts.map +1 -1
  80. package/dist/infrastructure/storage/StorageFactory.js +7 -2
  81. package/dist/infrastructure/storage/StorageFactory.js.map +1 -1
  82. package/dist/infrastructure/storage/adapters/FileSystemStorage.d.ts +3 -1
  83. package/dist/infrastructure/storage/adapters/FileSystemStorage.d.ts.map +1 -1
  84. package/dist/infrastructure/storage/adapters/FileSystemStorage.js +10 -2
  85. package/dist/infrastructure/storage/adapters/FileSystemStorage.js.map +1 -1
  86. package/dist/infrastructure/storage/adapters/KuzuDBStorage.d.ts +3 -1
  87. package/dist/infrastructure/storage/adapters/KuzuDBStorage.d.ts.map +1 -1
  88. package/dist/infrastructure/storage/adapters/KuzuDBStorage.js +9 -1
  89. package/dist/infrastructure/storage/adapters/KuzuDBStorage.js.map +1 -1
  90. package/dist/infrastructure/storage/adapters/MemoryStorage.d.ts +3 -1
  91. package/dist/infrastructure/storage/adapters/MemoryStorage.d.ts.map +1 -1
  92. package/dist/infrastructure/storage/adapters/MemoryStorage.js +9 -1
  93. package/dist/infrastructure/storage/adapters/MemoryStorage.js.map +1 -1
  94. package/dist/infrastructure/storage/adapters/SQLiteStorage.d.ts +53 -0
  95. package/dist/infrastructure/storage/adapters/SQLiteStorage.d.ts.map +1 -0
  96. package/dist/infrastructure/storage/adapters/SQLiteStorage.js +879 -0
  97. package/dist/infrastructure/storage/adapters/SQLiteStorage.js.map +1 -0
  98. package/dist/infrastructure/storage/graph-helpers.d.ts +3 -1
  99. package/dist/infrastructure/storage/graph-helpers.d.ts.map +1 -1
  100. package/dist/infrastructure/storage/graph-helpers.js +90 -0
  101. package/dist/infrastructure/storage/graph-helpers.js.map +1 -1
  102. package/dist/infrastructure/storage/index.d.ts +1 -1
  103. package/dist/infrastructure/storage/index.d.ts.map +1 -1
  104. package/dist/infrastructure/storage/interfaces/StorageBase.d.ts +3 -1
  105. package/dist/infrastructure/storage/interfaces/StorageBase.d.ts.map +1 -1
  106. package/dist/infrastructure/storage/interfaces/StorageBase.js.map +1 -1
  107. package/dist/infrastructure/storage/sqlite/GovernanceGraphCache.d.ts +27 -0
  108. package/dist/infrastructure/storage/sqlite/GovernanceGraphCache.d.ts.map +1 -0
  109. package/dist/infrastructure/storage/sqlite/GovernanceGraphCache.js +246 -0
  110. package/dist/infrastructure/storage/sqlite/GovernanceGraphCache.js.map +1 -0
  111. package/dist/infrastructure/storage/sqlite/perf-thresholds.d.ts +25 -0
  112. package/dist/infrastructure/storage/sqlite/perf-thresholds.d.ts.map +1 -0
  113. package/dist/infrastructure/storage/sqlite/perf-thresholds.js +25 -0
  114. package/dist/infrastructure/storage/sqlite/perf-thresholds.js.map +1 -0
  115. package/dist/infrastructure/storage/sqlite/schema.d.ts +4 -0
  116. package/dist/infrastructure/storage/sqlite/schema.d.ts.map +1 -0
  117. package/dist/infrastructure/storage/sqlite/schema.js +111 -0
  118. package/dist/infrastructure/storage/sqlite/schema.js.map +1 -0
  119. package/dist/interface/types/design-check.d.ts +73 -0
  120. package/dist/interface/types/design-check.d.ts.map +1 -0
  121. package/dist/interface/types/design-check.js +4 -0
  122. package/dist/interface/types/design-check.js.map +1 -0
  123. package/dist/interface/types/design-contract.d.ts +56 -1
  124. package/dist/interface/types/design-contract.d.ts.map +1 -1
  125. package/dist/interface/types/history-risk.d.ts +90 -0
  126. package/dist/interface/types/history-risk.d.ts.map +1 -0
  127. package/dist/interface/types/history-risk.js +4 -0
  128. package/dist/interface/types/history-risk.js.map +1 -0
  129. package/dist/interface/types/index.d.ts +17 -2
  130. package/dist/interface/types/index.d.ts.map +1 -1
  131. package/dist/interface/types/storage.d.ts +28 -1
  132. package/dist/interface/types/storage.d.ts.map +1 -1
  133. package/dist/orchestrator/adapters/ast-grep-adapter.d.ts +10 -0
  134. package/dist/orchestrator/adapters/ast-grep-adapter.d.ts.map +1 -1
  135. package/dist/orchestrator/adapters/ast-grep-adapter.js +46 -17
  136. package/dist/orchestrator/adapters/ast-grep-adapter.js.map +1 -1
  137. package/dist/orchestrator/adapters/codemap-adapter.d.ts.map +1 -1
  138. package/dist/orchestrator/adapters/codemap-adapter.js +2 -22
  139. package/dist/orchestrator/adapters/codemap-adapter.js.map +1 -1
  140. package/dist/orchestrator/history-risk-service.d.ts +55 -0
  141. package/dist/orchestrator/history-risk-service.d.ts.map +1 -0
  142. package/dist/orchestrator/history-risk-service.js +680 -0
  143. package/dist/orchestrator/history-risk-service.js.map +1 -0
  144. package/dist/orchestrator/types.d.ts +19 -1
  145. package/dist/orchestrator/types.d.ts.map +1 -1
  146. package/dist/orchestrator/types.js +19 -0
  147. package/dist/orchestrator/types.js.map +1 -1
  148. package/dist/server/mcp/index.d.ts +4 -0
  149. package/dist/server/mcp/index.d.ts.map +1 -0
  150. package/dist/server/mcp/index.js +5 -0
  151. package/dist/server/mcp/index.js.map +1 -0
  152. package/dist/server/mcp/server.d.ts +17 -0
  153. package/dist/server/mcp/server.d.ts.map +1 -0
  154. package/dist/server/mcp/server.js +84 -0
  155. package/dist/server/mcp/server.js.map +1 -0
  156. package/dist/server/mcp/service.d.ts +22 -0
  157. package/dist/server/mcp/service.d.ts.map +1 -0
  158. package/dist/server/mcp/service.js +177 -0
  159. package/dist/server/mcp/service.js.map +1 -0
  160. package/dist/server/mcp/types.d.ts +56 -0
  161. package/dist/server/mcp/types.d.ts.map +1 -0
  162. package/dist/server/mcp/types.js +4 -0
  163. package/dist/server/mcp/types.js.map +1 -0
  164. package/docs/AI_ASSISTANT_SETUP.md +1 -1
  165. package/docs/PUBLISHING.md +41 -12
  166. package/docs/SETUP_GUIDE.md +6 -6
  167. package/docs/ai-guide/COMMANDS.md +98 -4
  168. package/docs/ai-guide/INTEGRATION.md +137 -433
  169. package/docs/ai-guide/OUTPUT.md +476 -6
  170. package/docs/ai-guide/PATTERNS.md +41 -11
  171. package/docs/ai-guide/PROMPTS.md +11 -6
  172. package/docs/backlog.md +177 -0
  173. package/docs/eatdogfood-reports/2026-04-17-eatdogfood-agent-experience.md +231 -0
  174. package/docs/exec-plans/completed/2026-04-17-eatdogfood-codemap-cli.md +103 -0
  175. package/docs/ideation/2026-04-15-executable-architecture-constitution-ideation.md +102 -0
  176. package/docs/product-specs/DESIGN_CONTRACT_TEMPLATE.md +47 -0
  177. package/docs/product-specs/MVP3-ARCHITECTURE-COMPARISON.md +11 -10
  178. package/docs/product-specs/MVP3-ARCHITECTURE-REDESIGN-PRD.md +10 -10
  179. package/docs/product-specs/MVP3-ARCHITECTURE-REDESIGN-TECH-PRD.md +17 -12
  180. package/docs/rules/README.md +16 -11
  181. package/docs/rules/architecture-guardrails.md +24 -336
  182. package/docs/rules/code-quality-redlines.md +25 -311
  183. package/docs/rules/engineering-with-codex-openai.md +14 -1
  184. package/docs/rules/pre-release-checklist.md +9 -4
  185. package/docs/rules/validation.md +91 -40
  186. package/mycodemap.config.schema.json +3 -3
  187. package/package.json +7 -2
  188. package/scripts/benchmark-governance-graph.mjs +132 -0
  189. package/scripts/calibrate-contract-gate.mjs +221 -0
  190. package/scripts/capability-report.py +255 -0
  191. package/scripts/pre-release-check.js +2 -2
  192. package/scripts/qa-rule-control.sh +254 -0
  193. package/scripts/report-high-risk-files.mjs +395 -0
  194. package/scripts/rule-context.mjs +155 -0
  195. package/scripts/smoke-sqlite-impact.mjs +85 -0
  196. package/scripts/sync-analyze-docs.js +1 -0
  197. package/scripts/tests/test_capability_report.py +89 -0
  198. package/scripts/tests/test_rule_control_workflow.py +51 -0
  199. package/scripts/tests/test_validate_rules.py +81 -0
  200. package/scripts/validate-ai-docs.js +283 -1
  201. package/scripts/validate-docs.js +249 -42
  202. package/scripts/validate-rules.py +254 -0
  203. package/dist/ai/claude.d.ts +0 -38
  204. package/dist/ai/claude.d.ts.map +0 -1
  205. package/dist/ai/claude.js +0 -169
  206. package/dist/ai/claude.js.map +0 -1
  207. package/dist/ai/codex.d.ts +0 -38
  208. package/dist/ai/codex.d.ts.map +0 -1
  209. package/dist/ai/codex.js +0 -169
  210. package/dist/ai/codex.js.map +0 -1
  211. package/dist/ai/factory.d.ts +0 -48
  212. package/dist/ai/factory.d.ts.map +0 -1
  213. package/dist/ai/factory.js +0 -95
  214. package/dist/ai/factory.js.map +0 -1
  215. package/dist/ai/index.d.ts +0 -12
  216. package/dist/ai/index.d.ts.map +0 -1
  217. package/dist/ai/index.js +0 -29
  218. package/dist/ai/index.js.map +0 -1
  219. package/dist/ai/provider.d.ts +0 -70
  220. package/dist/ai/provider.d.ts.map +0 -1
  221. package/dist/ai/provider.js +0 -31
  222. package/dist/ai/provider.js.map +0 -1
  223. package/dist/ai/subagent-caller.d.ts +0 -90
  224. package/dist/ai/subagent-caller.d.ts.map +0 -1
  225. package/dist/ai/subagent-caller.js +0 -280
  226. package/dist/ai/subagent-caller.js.map +0 -1
  227. package/dist/ai/types.d.ts +0 -70
  228. package/dist/ai/types.d.ts.map +0 -1
  229. package/dist/ai/types.js +0 -5
  230. package/dist/ai/types.js.map +0 -1
  231. package/dist/cli/commands/server.d.ts +0 -9
  232. package/dist/cli/commands/server.d.ts.map +0 -1
  233. package/dist/cli/commands/server.js +0 -65
  234. package/dist/cli/commands/server.js.map +0 -1
  235. package/dist/cli-new/commands/server.d.ts +0 -13
  236. package/dist/cli-new/commands/server.d.ts.map +0 -1
  237. package/dist/cli-new/commands/server.js +0 -90
  238. package/dist/cli-new/commands/server.js.map +0 -1
  239. package/dist/generator/ai-overview.d.ts +0 -51
  240. package/dist/generator/ai-overview.d.ts.map +0 -1
  241. package/dist/generator/ai-overview.js +0 -160
  242. package/dist/generator/ai-overview.js.map +0 -1
  243. package/dist/infrastructure/storage/adapters/Neo4jStorage.d.ts +0 -41
  244. package/dist/infrastructure/storage/adapters/Neo4jStorage.d.ts.map +0 -1
  245. package/dist/infrastructure/storage/adapters/Neo4jStorage.js +0 -162
  246. package/dist/infrastructure/storage/adapters/Neo4jStorage.js.map +0 -1
  247. package/dist/orchestrator/ai-feed-generator.d.ts +0 -210
  248. package/dist/orchestrator/ai-feed-generator.d.ts.map +0 -1
  249. package/dist/orchestrator/ai-feed-generator.js +0 -377
  250. package/dist/orchestrator/ai-feed-generator.js.map +0 -1
  251. package/docs/archive/test-report-symbol-search.md +0 -384
  252. package/docs/archive/test-scenario-4-complexity-analysis.md +0 -460
  253. package/docs/archive/test_report_scenario5.md +0 -615
  254. package/docs/archive/test_scenario_3_impact_analysis_report.md +0 -520
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from 'node:path';
4
+
5
+ const USAGE = 'Usage: node scripts/rule-context.mjs --files <path> [<path>...] --format json|prompt';
6
+ const DEFAULT_VERIFY_COMMANDS = ['python3 scripts/validate-rules.py code --report-only'];
7
+
8
+ const ROUTES = [
9
+ {
10
+ matches: filePath => filePath.endsWith('.test.ts'),
11
+ rules: ['docs/rules/testing.md'],
12
+ },
13
+ {
14
+ matches: filePath => /^src\/interface\//.test(filePath),
15
+ rules: ['docs/rules/architecture-guardrails.md'],
16
+ },
17
+ {
18
+ matches: filePath => /^src\/(?:cli|domain|server|infrastructure)\//.test(filePath),
19
+ rules: [
20
+ 'docs/rules/code-quality-redlines.md',
21
+ 'docs/rules/architecture-guardrails.md',
22
+ ],
23
+ },
24
+ {
25
+ matches: filePath =>
26
+ /^(?:docs\/|\.githooks\/|\.github\/workflows\/)/.test(filePath),
27
+ rules: [
28
+ 'docs/rules/validation.md',
29
+ 'docs/rules/engineering-with-codex-openai.md',
30
+ ],
31
+ },
32
+ ];
33
+
34
+ function normalizeFilePath(filePath) {
35
+ const resolved = path.isAbsolute(filePath)
36
+ ? path.relative(process.cwd(), filePath)
37
+ : filePath;
38
+
39
+ return resolved.replace(/\\/g, '/').replace(/^\.\//, '');
40
+ }
41
+
42
+ function parseArgs(argv) {
43
+ const files = [];
44
+ let format = 'json';
45
+
46
+ for (let index = 0; index < argv.length; index += 1) {
47
+ const current = argv[index];
48
+
49
+ if (current === '--files') {
50
+ index += 1;
51
+ while (index < argv.length && !argv[index].startsWith('--')) {
52
+ files.push(argv[index]);
53
+ index += 1;
54
+ }
55
+ index -= 1;
56
+ continue;
57
+ }
58
+
59
+ if (current === '--format') {
60
+ format = argv[index + 1] ?? '';
61
+ index += 1;
62
+ continue;
63
+ }
64
+
65
+ if (current === '--help' || current === '-h') {
66
+ console.log(USAGE);
67
+ process.exit(0);
68
+ }
69
+ }
70
+
71
+ if (files.length === 0 || (format !== 'json' && format !== 'prompt')) {
72
+ console.error(USAGE);
73
+ process.exit(1);
74
+ }
75
+
76
+ return {
77
+ files: files.map(normalizeFilePath),
78
+ format,
79
+ };
80
+ }
81
+
82
+ function inferScopedRules(files) {
83
+ const matchedRules = [];
84
+ const matchedFiles = [];
85
+
86
+ for (const filePath of files) {
87
+ const route = ROUTES.find(candidate => candidate.matches(filePath));
88
+ if (!route) {
89
+ continue;
90
+ }
91
+
92
+ matchedFiles.push(filePath);
93
+
94
+ for (const rulePath of route.rules) {
95
+ if (!matchedRules.includes(rulePath)) {
96
+ matchedRules.push(rulePath);
97
+ }
98
+
99
+ if (matchedRules.length === 2) {
100
+ return matchedRules.length === 0
101
+ ? { files, matchedFiles: [], matchedRules: [], verifyCommands: [] }
102
+ : {
103
+ files,
104
+ matchedFiles,
105
+ matchedRules,
106
+ verifyCommands: DEFAULT_VERIFY_COMMANDS,
107
+ };
108
+ }
109
+ }
110
+ }
111
+
112
+ return matchedRules.length === 0
113
+ ? { files, matchedFiles: [], matchedRules: [], verifyCommands: [] }
114
+ : {
115
+ files,
116
+ matchedFiles,
117
+ matchedRules,
118
+ verifyCommands: DEFAULT_VERIFY_COMMANDS,
119
+ };
120
+ }
121
+
122
+ function formatPrompt(result) {
123
+ if (result.matchedRules.length === 0) {
124
+ return 'No scoped rules inferred';
125
+ }
126
+
127
+ return [
128
+ 'Only inject matched rules:',
129
+ ...result.matchedRules.map(rulePath => `- ${rulePath}`),
130
+ '',
131
+ 'Verify after edits:',
132
+ ...result.verifyCommands.map(command => `- ${command}`),
133
+ ].join('\n');
134
+ }
135
+
136
+ const args = parseArgs(process.argv.slice(2));
137
+ const result = inferScopedRules(args.files);
138
+
139
+ if (args.format === 'prompt') {
140
+ console.log(formatPrompt(result));
141
+ process.exit(0);
142
+ }
143
+
144
+ console.log(
145
+ JSON.stringify(
146
+ {
147
+ files: result.files,
148
+ matchedFiles: result.matchedFiles,
149
+ matchedRules: result.matchedRules,
150
+ verifyCommands: result.verifyCommands,
151
+ },
152
+ null,
153
+ 2,
154
+ ),
155
+ );
@@ -0,0 +1,85 @@
1
+ import { mkdtempSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import path from 'node:path';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+
6
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
7
+ const loadModule = async (relativePath) => import(pathToFileURL(path.join(repoRoot, relativePath)).href);
8
+ const { SQLiteStorage } = await loadModule('dist/infrastructure/storage/adapters/SQLiteStorage.js');
9
+ const { QueryHandler } = await loadModule('dist/server/handlers/QueryHandler.js');
10
+
11
+ function createImpactFixture() {
12
+ return {
13
+ project: {
14
+ id: 'proj-smoke',
15
+ name: 'sqlite-impact-smoke',
16
+ rootPath: '/fixture',
17
+ createdAt: new Date('2026-04-15T00:00:00Z'),
18
+ updatedAt: new Date('2026-04-15T00:00:00Z'),
19
+ },
20
+ modules: [
21
+ {
22
+ id: 'core',
23
+ projectId: 'proj-smoke',
24
+ path: 'src/core.ts',
25
+ language: 'ts',
26
+ stats: { lines: 10, codeLines: 8, commentLines: 1, blankLines: 1 },
27
+ },
28
+ {
29
+ id: 'service',
30
+ projectId: 'proj-smoke',
31
+ path: 'src/service.ts',
32
+ language: 'ts',
33
+ stats: { lines: 10, codeLines: 8, commentLines: 1, blankLines: 1 },
34
+ },
35
+ {
36
+ id: 'api',
37
+ projectId: 'proj-smoke',
38
+ path: 'src/api.ts',
39
+ language: 'ts',
40
+ stats: { lines: 10, codeLines: 8, commentLines: 1, blankLines: 1 },
41
+ },
42
+ ],
43
+ symbols: [],
44
+ dependencies: [
45
+ { id: 'dep-1', sourceId: 'service', targetId: 'core', type: 'import' },
46
+ { id: 'dep-2', sourceId: 'api', targetId: 'service', type: 'import' },
47
+ ],
48
+ };
49
+ }
50
+
51
+ const rootDir = mkdtempSync(path.join(tmpdir(), 'codemap-sqlite-impact-smoke-'));
52
+ const storage = new SQLiteStorage({ type: 'sqlite', databasePath: '.codemap/governance.sqlite' });
53
+
54
+ try {
55
+ await storage.initialize(rootDir);
56
+ await storage.saveCodeGraph(createImpactFixture());
57
+ const handler = new QueryHandler(storage);
58
+ const result = await handler.analyzeImpact({ moduleId: 'core', depth: 3 });
59
+ const stats = storage.getGovernanceGraphRuntimeStats();
60
+
61
+ if (result.rootModule !== 'core') {
62
+ throw new Error(`unexpected root module: ${result.rootModule}`);
63
+ }
64
+
65
+ if (result.totalAffected !== 2 || result.maxDepth !== 2) {
66
+ throw new Error(`unexpected impact result: ${JSON.stringify(result)}`);
67
+ }
68
+
69
+ if (stats.cacheMode !== 'memory-eager') {
70
+ throw new Error(`expected memory-eager smoke path, got ${stats.cacheMode} (${stats.warning ?? 'no warning'})`);
71
+ }
72
+
73
+ console.log(JSON.stringify({
74
+ ok: true,
75
+ smoke: 'sqlite-impact',
76
+ cacheMode: stats.cacheMode,
77
+ rootModule: result.rootModule,
78
+ affectedModules: result.affectedModules,
79
+ loadMs: stats.loadMs,
80
+ rssDeltaMb: stats.rssDeltaMb,
81
+ }, null, 2));
82
+ } finally {
83
+ await storage.close();
84
+ rmSync(rootDir, { recursive: true, force: true });
85
+ }
@@ -92,6 +92,7 @@ function renderCommandsAnalyzeIntentExamples() {
92
92
  title: '1. find - 查找符号 / 文本',
93
93
  commands: [
94
94
  'mycodemap analyze -i find -k "UnifiedResult"',
95
+ 'mycodemap analyze -i find -k "SourceLocation" --json --structured',
95
96
  'mycodemap analyze -i find -t "src/orchestrator" -k "IntentRouter" --topK 20',
96
97
  ],
97
98
  },
@@ -0,0 +1,89 @@
1
+ import contextlib
2
+ import io
3
+ import importlib.util
4
+ import json
5
+ import tempfile
6
+ import unittest
7
+ from pathlib import Path
8
+
9
+
10
+ def load_capability_report_module():
11
+ module_path = Path(__file__).resolve().parents[1] / "capability-report.py"
12
+ spec = importlib.util.spec_from_file_location("capability_report", module_path)
13
+ if spec is None or spec.loader is None:
14
+ raise RuntimeError(f"Unable to load module from {module_path}")
15
+ module = importlib.util.module_from_spec(spec)
16
+ spec.loader.exec_module(module)
17
+ return module
18
+
19
+
20
+ class CapabilityReportTests(unittest.TestCase):
21
+ @classmethod
22
+ def setUpClass(cls):
23
+ cls.module = load_capability_report_module()
24
+
25
+ def test_required_fail_status(self):
26
+ def failing_runner(command, cwd, timeout):
27
+ return {
28
+ "available": True,
29
+ "returncode": 1,
30
+ "stdout": "",
31
+ "stderr": "failed",
32
+ "duration_ms": 8,
33
+ }
34
+
35
+ item = self.module.build_item(
36
+ "python3",
37
+ "required",
38
+ ["python3", "--version"],
39
+ cwd=Path("."),
40
+ timeout=10,
41
+ runner=failing_runner,
42
+ )
43
+
44
+ self.assertEqual(item["status"], "REQUIRED_FAIL")
45
+
46
+ def test_optional_disabled_status(self):
47
+ def missing_runner(command, cwd, timeout):
48
+ return {
49
+ "available": False,
50
+ "returncode": None,
51
+ "stdout": "",
52
+ "stderr": "missing",
53
+ "duration_ms": 5,
54
+ }
55
+
56
+ item = self.module.build_item(
57
+ "validate_rules",
58
+ "optional",
59
+ ["python3", "scripts/validate-rules.py", "code", "--report-only"],
60
+ cwd=Path("."),
61
+ timeout=10,
62
+ runner=missing_runner,
63
+ )
64
+
65
+ self.assertEqual(item["status"], "OPTIONAL_DISABLED")
66
+
67
+ def test_output_file_written(self):
68
+ fake_report = {
69
+ "version": 1,
70
+ "generated_at": "2026-04-19T00:00:00Z",
71
+ "summary": {"required": {"passed": 1, "failed": 0, "total": 1}},
72
+ "items": [{"name": "python3", "kind": "required", "status": "PASS"}],
73
+ }
74
+
75
+ with tempfile.TemporaryDirectory() as temp_dir:
76
+ output_path = Path(temp_dir) / "capability-report.json"
77
+ with contextlib.redirect_stdout(io.StringIO()):
78
+ exit_code = self.module.main(["--output", str(output_path)], report_builder=lambda: fake_report)
79
+
80
+ self.assertEqual(exit_code, 0)
81
+ self.assertTrue(output_path.exists())
82
+
83
+ payload = json.loads(output_path.read_text(encoding="utf-8"))
84
+ self.assertIn("summary", payload)
85
+ self.assertIn("items", payload)
86
+
87
+
88
+ if __name__ == "__main__":
89
+ unittest.main()
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ import unittest
6
+ from pathlib import Path
7
+
8
+
9
+ ROOT = Path(__file__).resolve().parents[2]
10
+
11
+
12
+ class RuleControlWorkflowTests(unittest.TestCase):
13
+ def test_rule_context_is_scoped(self):
14
+ result = subprocess.run(
15
+ [
16
+ "node",
17
+ "scripts/rule-context.mjs",
18
+ "--files",
19
+ "src/cli/index.ts",
20
+ "--format",
21
+ "json",
22
+ ],
23
+ cwd=ROOT,
24
+ check=True,
25
+ capture_output=True,
26
+ text=True,
27
+ )
28
+
29
+ payload = json.loads(result.stdout)
30
+ self.assertIn("docs/rules/code-quality-redlines.md", payload["matchedRules"])
31
+ self.assertNotIn("docs/rules/testing.md", payload["matchedRules"])
32
+
33
+ def test_execute_workflows_include_rule_context(self):
34
+ workflow_paths = [
35
+ ROOT / ".codex/get-shit-done/workflows/execute-phase.md",
36
+ ROOT / ".claude/get-shit-done/workflows/execute-phase.md",
37
+ ]
38
+
39
+ for workflow_path in workflow_paths:
40
+ content = workflow_path.read_text(encoding="utf-8")
41
+ self.assertIn("rule-context.mjs --files", content)
42
+ self.assertIn("<rule_context>", content)
43
+
44
+ def test_ci_contains_rule_backstop(self):
45
+ content = (ROOT / ".github/workflows/ci-gateway.yml").read_text(encoding="utf-8")
46
+ self.assertIn("Rule validation backstop", content)
47
+ self.assertIn("python3 scripts/validate-rules.py code", content)
48
+
49
+
50
+ if __name__ == "__main__":
51
+ unittest.main()
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib.util
4
+ import sys
5
+ import unittest
6
+ from pathlib import Path
7
+
8
+
9
+ def load_validate_rules_module():
10
+ module_path = Path(__file__).resolve().parents[1] / "validate-rules.py"
11
+ spec = importlib.util.spec_from_file_location("validate_rules", module_path)
12
+ if spec is None or spec.loader is None:
13
+ raise RuntimeError(f"Failed to load module from {module_path}")
14
+
15
+ module = importlib.util.module_from_spec(spec)
16
+ sys.modules[spec.name] = module
17
+ spec.loader.exec_module(module)
18
+ return module
19
+
20
+
21
+ validate_rules = load_validate_rules_module()
22
+
23
+
24
+ def make_check(level: str, status: str):
25
+ return validate_rules.make_check(
26
+ name=f"{level.lower()}-{status}",
27
+ level=level,
28
+ command=["echo", status],
29
+ status=status,
30
+ output=status,
31
+ )
32
+
33
+
34
+ class ValidateRulesExitCodeTests(unittest.TestCase):
35
+ def test_exit_code_for_no_findings(self):
36
+ checks = [make_check("P0", "passed"), make_check("P1", "passed")]
37
+ self.assertEqual(validate_rules.resolve_exit_code(checks, report_only=False), 0)
38
+
39
+ def test_exit_code_for_p0(self):
40
+ checks = [make_check("P0", "failed"), make_check("P1", "passed")]
41
+ self.assertEqual(validate_rules.resolve_exit_code(checks, report_only=False), 1)
42
+
43
+ def test_exit_code_for_p1(self):
44
+ checks = [make_check("P0", "passed"), make_check("P1", "failed")]
45
+ self.assertEqual(validate_rules.resolve_exit_code(checks, report_only=False), 2)
46
+
47
+ def test_exit_code_for_p2(self):
48
+ checks = [make_check("P0", "passed"), make_check("P2", "failed")]
49
+ self.assertEqual(validate_rules.resolve_exit_code(checks, report_only=False), 3)
50
+
51
+ def test_exit_code_for_unavailable(self):
52
+ checks = [make_check("P0", "failed"), make_check("P1", "unavailable")]
53
+ self.assertEqual(validate_rules.resolve_exit_code(checks, report_only=False), 4)
54
+
55
+ def test_report_only_exits_zero(self):
56
+ checks = [make_check("P0", "failed"), make_check("P1", "unavailable")]
57
+ self.assertEqual(validate_rules.resolve_exit_code(checks, report_only=True), 0)
58
+
59
+ def test_report_status_values_are_runtime_only(self):
60
+ checks = [
61
+ make_check("P0", "passed"),
62
+ make_check("P1", "failed"),
63
+ make_check("P2", "unavailable"),
64
+ ]
65
+ report = validate_rules.build_report("all", checks, report_only=False)
66
+ self.assertEqual([check["level"] for check in report["checks"]], ["P0", "P1", "P2"])
67
+ self.assertSetEqual(
68
+ {check["status"] for check in report["checks"]},
69
+ {"passed", "failed", "unavailable"},
70
+ )
71
+
72
+ def test_arch_check_marks_missing_dist_as_unavailable(self):
73
+ missing_dist = Path("/tmp/validate-rules-missing-dist.js")
74
+ checks = validate_rules.execute_checks("arch", dist_cli_path=missing_dist)
75
+ self.assertEqual(len(checks), 1)
76
+ self.assertEqual(checks[0]["level"], "P0")
77
+ self.assertEqual(checks[0]["status"], "unavailable")
78
+
79
+
80
+ if __name__ == "__main__":
81
+ unittest.main()