homegraph 1.1.2 → 1.1.3

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 (175) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +243 -243
  3. package/dist/bin/homegraph.js +371 -9
  4. package/dist/bin/homegraph.js.map +1 -1
  5. package/dist/db/migrations.js +18 -18
  6. package/dist/db/queries.js +140 -140
  7. package/dist/db/schema.sql +152 -152
  8. package/dist/directory.js +5 -5
  9. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  10. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  11. package/dist/installer/instructions-template.js +9 -9
  12. package/dist/installer/targets/shared.d.ts +5 -6
  13. package/dist/installer/targets/shared.d.ts.map +1 -1
  14. package/dist/installer/targets/shared.js +5 -6
  15. package/dist/installer/targets/shared.js.map +1 -1
  16. package/dist/mcp/liveness-watchdog.js +16 -16
  17. package/dist/mcp/server-instructions.js +69 -69
  18. package/dist/mcp/tools.d.ts +17 -4
  19. package/dist/mcp/tools.d.ts.map +1 -1
  20. package/dist/mcp/tools.js +192 -22
  21. package/dist/mcp/tools.js.map +1 -1
  22. package/dist/reasoning/reasoner.js +32 -32
  23. package/dist/spec/config.d.ts +43 -0
  24. package/dist/spec/config.d.ts.map +1 -0
  25. package/dist/spec/config.js +275 -0
  26. package/dist/spec/config.js.map +1 -0
  27. package/dist/spec/db/commit-node.d.ts +23 -0
  28. package/dist/spec/db/commit-node.d.ts.map +1 -0
  29. package/dist/spec/db/commit-node.js +62 -0
  30. package/dist/spec/db/commit-node.js.map +1 -0
  31. package/dist/spec/db/fragment-node.d.ts +23 -0
  32. package/dist/spec/db/fragment-node.d.ts.map +1 -0
  33. package/dist/spec/db/fragment-node.js +120 -0
  34. package/dist/spec/db/fragment-node.js.map +1 -0
  35. package/dist/spec/db/fts.d.ts +60 -0
  36. package/dist/spec/db/fts.d.ts.map +1 -0
  37. package/dist/spec/db/fts.js +285 -0
  38. package/dist/spec/db/fts.js.map +1 -0
  39. package/dist/spec/db/index.d.ts +13 -0
  40. package/dist/spec/db/index.d.ts.map +1 -0
  41. package/dist/spec/db/index.js +50 -0
  42. package/dist/spec/db/index.js.map +1 -0
  43. package/dist/spec/db/relations.d.ts +55 -0
  44. package/dist/spec/db/relations.d.ts.map +1 -0
  45. package/dist/spec/db/relations.js +158 -0
  46. package/dist/spec/db/relations.js.map +1 -0
  47. package/dist/spec/db/schema.d.ts +30 -0
  48. package/dist/spec/db/schema.d.ts.map +1 -0
  49. package/dist/spec/db/schema.js +87 -0
  50. package/dist/spec/db/schema.js.map +1 -0
  51. package/dist/spec/db/schema.sql +107 -0
  52. package/dist/spec/db/spec-node.d.ts +41 -0
  53. package/dist/spec/db/spec-node.d.ts.map +1 -0
  54. package/dist/spec/db/spec-node.js +114 -0
  55. package/dist/spec/db/spec-node.js.map +1 -0
  56. package/dist/spec/evolve/impact-locator.d.ts +13 -0
  57. package/dist/spec/evolve/impact-locator.d.ts.map +1 -0
  58. package/dist/spec/evolve/impact-locator.js +25 -0
  59. package/dist/spec/evolve/impact-locator.js.map +1 -0
  60. package/dist/spec/evolve/llm-client.d.ts +50 -0
  61. package/dist/spec/evolve/llm-client.d.ts.map +1 -0
  62. package/dist/spec/evolve/llm-client.js +176 -0
  63. package/dist/spec/evolve/llm-client.js.map +1 -0
  64. package/dist/spec/evolve/logic-checker.d.ts +12 -0
  65. package/dist/spec/evolve/logic-checker.d.ts.map +1 -0
  66. package/dist/spec/evolve/logic-checker.js +48 -0
  67. package/dist/spec/evolve/logic-checker.js.map +1 -0
  68. package/dist/spec/evolve/pipeline.d.ts +40 -0
  69. package/dist/spec/evolve/pipeline.d.ts.map +1 -0
  70. package/dist/spec/evolve/pipeline.js +314 -0
  71. package/dist/spec/evolve/pipeline.js.map +1 -0
  72. package/dist/spec/evolve/spec-rewriter.d.ts +42 -0
  73. package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -0
  74. package/dist/spec/evolve/spec-rewriter.js +254 -0
  75. package/dist/spec/evolve/spec-rewriter.js.map +1 -0
  76. package/dist/spec/graph/queries.d.ts +58 -0
  77. package/dist/spec/graph/queries.d.ts.map +1 -0
  78. package/dist/spec/graph/queries.js +215 -0
  79. package/dist/spec/graph/queries.js.map +1 -0
  80. package/dist/spec/mining/diff-parser.d.ts +33 -0
  81. package/dist/spec/mining/diff-parser.d.ts.map +1 -0
  82. package/dist/spec/mining/diff-parser.js +166 -0
  83. package/dist/spec/mining/diff-parser.js.map +1 -0
  84. package/dist/spec/mining/git-scanner.d.ts +91 -0
  85. package/dist/spec/mining/git-scanner.d.ts.map +1 -0
  86. package/dist/spec/mining/git-scanner.js +266 -0
  87. package/dist/spec/mining/git-scanner.js.map +1 -0
  88. package/dist/spec/mining/pipeline.d.ts +53 -0
  89. package/dist/spec/mining/pipeline.d.ts.map +1 -0
  90. package/dist/spec/mining/pipeline.js +165 -0
  91. package/dist/spec/mining/pipeline.js.map +1 -0
  92. package/dist/spec/mining/scope-resolver.d.ts +45 -0
  93. package/dist/spec/mining/scope-resolver.d.ts.map +1 -0
  94. package/dist/spec/mining/scope-resolver.js +103 -0
  95. package/dist/spec/mining/scope-resolver.js.map +1 -0
  96. package/dist/spec/mining/spec-extractor.d.ts +69 -0
  97. package/dist/spec/mining/spec-extractor.d.ts.map +1 -0
  98. package/dist/spec/mining/spec-extractor.js +369 -0
  99. package/dist/spec/mining/spec-extractor.js.map +1 -0
  100. package/dist/spec/types.d.ts +149 -0
  101. package/dist/spec/types.d.ts.map +1 -0
  102. package/dist/spec/types.js +15 -0
  103. package/dist/spec/types.js.map +1 -0
  104. package/dist/spec/utils.d.ts +166 -0
  105. package/dist/spec/utils.d.ts.map +1 -0
  106. package/dist/spec/utils.js +461 -0
  107. package/dist/spec/utils.js.map +1 -0
  108. package/package.json +58 -57
  109. package/scripts/add-lang/bench.sh +60 -60
  110. package/scripts/add-lang/check-grammar.mjs +75 -75
  111. package/scripts/add-lang/dump-ast.mjs +103 -103
  112. package/scripts/add-lang/verify-extraction.mjs +70 -70
  113. package/scripts/agent-eval/ab-adoption.sh +91 -91
  114. package/scripts/agent-eval/ab-hook.sh +86 -86
  115. package/scripts/agent-eval/ab-impl.sh +78 -78
  116. package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -102
  117. package/scripts/agent-eval/ab-sufficiency.sh +78 -78
  118. package/scripts/agent-eval/arms-F.sh +21 -21
  119. package/scripts/agent-eval/arms-matrix.sh +37 -37
  120. package/scripts/agent-eval/audit.sh +68 -68
  121. package/scripts/agent-eval/bench-readme.sh +28 -28
  122. package/scripts/agent-eval/bench-why-repo.sh +22 -22
  123. package/scripts/agent-eval/block-read-hook.sh +19 -19
  124. package/scripts/agent-eval/hook-settings.json +15 -15
  125. package/scripts/agent-eval/itrun.sh +120 -120
  126. package/scripts/agent-eval/offload-eval-3arm.sh +72 -72
  127. package/scripts/agent-eval/offload-eval-cost.mjs +133 -133
  128. package/scripts/agent-eval/offload-eval-effort.mjs +108 -108
  129. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -25
  130. package/scripts/agent-eval/offload-eval-frontload.sh +47 -47
  131. package/scripts/agent-eval/offload-eval-ground-truth.json +18 -18
  132. package/scripts/agent-eval/offload-eval-hook.mjs +84 -84
  133. package/scripts/agent-eval/offload-eval-judge.mjs +103 -103
  134. package/scripts/agent-eval/offload-eval-matrix.sh +20 -20
  135. package/scripts/agent-eval/offload-eval-metrics.mjs +94 -94
  136. package/scripts/agent-eval/offload-eval-refs1.sh +50 -50
  137. package/scripts/agent-eval/offload-eval-setup.sh +24 -24
  138. package/scripts/agent-eval/offload-eval-styles.sh +71 -71
  139. package/scripts/agent-eval/offload-eval-summarize.mjs +68 -68
  140. package/scripts/agent-eval/offload-eval.md +76 -76
  141. package/scripts/agent-eval/parse-arms.mjs +116 -116
  142. package/scripts/agent-eval/parse-bench-readme.mjs +84 -84
  143. package/scripts/agent-eval/parse-run.mjs +45 -45
  144. package/scripts/agent-eval/parse-session.mjs +93 -93
  145. package/scripts/agent-eval/probe-context.mjs +21 -21
  146. package/scripts/agent-eval/probe-explore.mjs +40 -40
  147. package/scripts/agent-eval/probe-node.mjs +20 -20
  148. package/scripts/agent-eval/probe-sweep.mjs +119 -119
  149. package/scripts/agent-eval/probe-trace.mjs +20 -20
  150. package/scripts/agent-eval/redirect-read-hook.sh +38 -38
  151. package/scripts/agent-eval/run-agent.sh +34 -34
  152. package/scripts/agent-eval/run-all.sh +69 -69
  153. package/scripts/agent-eval/run-arms.sh +56 -56
  154. package/scripts/agent-eval/seq-matrix.mjs +137 -137
  155. package/scripts/build-bundle.sh +118 -118
  156. package/scripts/extract-release-notes.mjs +130 -130
  157. package/scripts/local-install.sh +41 -41
  158. package/scripts/npm-sdk.js +75 -75
  159. package/scripts/npm-shim.js +246 -246
  160. package/scripts/pack-npm.sh +119 -119
  161. package/scripts/prepare-release.mjs +270 -270
  162. package/scripts/qa_eval/README.md +407 -404
  163. package/scripts/qa_eval/_test_deveco_probe.py +41 -41
  164. package/scripts/qa_eval/agent_runner.py +526 -526
  165. package/scripts/qa_eval/data/.gitignore +4 -4
  166. package/scripts/qa_eval/data/test-set.jsonl +2 -22
  167. package/scripts/qa_eval/eval_metrics.py +274 -233
  168. package/scripts/qa_eval/external_agent.py +976 -671
  169. package/scripts/qa_eval/llm_config.py +92 -92
  170. package/scripts/qa_eval/memory_monitor.py +132 -132
  171. package/scripts/qa_eval/my_answer_accuracy.py +187 -187
  172. package/scripts/qa_eval/requirements.txt +2 -2
  173. package/scripts/qa_eval/run_pipeline.py +804 -711
  174. package/scripts/qa_eval/stats_efficiency.py +279 -279
  175. package/scripts/qa_eval/stats_scores.py +207 -207
@@ -60,6 +60,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
60
60
  const commander_1 = require("commander");
61
61
  const path = __importStar(require("path"));
62
62
  const fs = __importStar(require("fs"));
63
+ const utils_1 = require("../spec/utils");
63
64
  const directory_1 = require("../directory");
64
65
  const worktree_1 = require("../sync/worktree");
65
66
  const shimmer_progress_1 = require("../ui/shimmer-progress");
@@ -235,6 +236,38 @@ function main() {
235
236
  // Not found - return original path (will fail later with helpful error)
236
237
  return absolutePath;
237
238
  }
239
+ /**
240
+ * Resolve the project root for spec operations by walking up from a path
241
+ * looking for `${SPEC_DATA_DIR}/meta.json` or `${SPEC_DATA_DIR}/commit4spec.db`.
242
+ * Falls back to the starting path when neither marker is found (callers
243
+ * handle the not-found case).
244
+ */
245
+ function resolveSpecProjectPath(pathArg) {
246
+ const startPath = path.resolve(pathArg || process.cwd());
247
+ // Direct hit
248
+ const checkDirs = [
249
+ `${utils_1.SPEC_DATA_DIR}/meta.json`,
250
+ `${utils_1.SPEC_DATA_DIR}/commit4spec.db`,
251
+ ];
252
+ for (const check of checkDirs) {
253
+ if (fs.existsSync(path.join(startPath, check)))
254
+ return startPath;
255
+ }
256
+ // Walk up
257
+ let current = startPath;
258
+ const root = path.parse(current).root;
259
+ while (current !== root) {
260
+ const parent = path.dirname(current);
261
+ if (parent === current)
262
+ break;
263
+ current = parent;
264
+ for (const check of checkDirs) {
265
+ if (fs.existsSync(path.join(current, check)))
266
+ return current;
267
+ }
268
+ }
269
+ return startPath; // fallback — callers handle not-found
270
+ }
238
271
  /**
239
272
  * Format a number with commas
240
273
  */
@@ -1393,15 +1426,15 @@ function main() {
1393
1426
  console.error(chalk.bold('\nHomeGraph MCP Server\n'));
1394
1427
  console.error(chalk.blue((0, glyphs_1.getGlyphs)().info) + ' Use --mcp flag to start the MCP server');
1395
1428
  console.error('\nTo use with Claude Code, add to your MCP configuration:');
1396
- console.error(chalk.dim(`
1397
- {
1398
- "mcpServers": {
1399
- "homegraph": {
1400
- "command": "homegraph",
1401
- "args": ["serve", "--mcp"]
1402
- }
1403
- }
1404
- }
1429
+ console.error(chalk.dim(`
1430
+ {
1431
+ "mcpServers": {
1432
+ "homegraph": {
1433
+ "command": "homegraph",
1434
+ "args": ["serve", "--mcp"]
1435
+ }
1436
+ }
1437
+ }
1405
1438
  `));
1406
1439
  console.error('Available tools:');
1407
1440
  console.error(chalk.cyan(' homegraph_explore') + ' - Primary: source of the relevant symbols for any question');
@@ -1817,6 +1850,335 @@ function main() {
1817
1850
  process.exit(1);
1818
1851
  }
1819
1852
  });
1853
+ // =============================================================================
1854
+ // Spec commands (Commit4Spec knowledge graph)
1855
+ // =============================================================================
1856
+ const specCommand = program
1857
+ .command('spec')
1858
+ .description('Manage spec knowledge graph (reverse-mining, search, self-evolve)');
1859
+ /**
1860
+ * homegraph spec mine
1861
+ */
1862
+ specCommand
1863
+ .command('mine')
1864
+ .description('Reverse-mine spec knowledge from Git history')
1865
+ .option('-p, --path <path>', 'Path to the repository')
1866
+ .option('--spec-storage-path <path>', 'Path to the .spec directory')
1867
+ .option('--db-path <path>', 'Path to the SQLite database file')
1868
+ .option('-v, --verbose', 'Show detailed output')
1869
+ .option('-j, --json', 'Output as JSON')
1870
+ .action(async (options) => {
1871
+ try {
1872
+ const repoPath = path.resolve(options.path || process.cwd());
1873
+ const specStoragePath = options.specStoragePath ?? path.join(repoPath, '.spec');
1874
+ const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
1875
+ const { runMiningPipeline } = await Promise.resolve().then(() => __importStar(require('../spec/mining/pipeline')));
1876
+ const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
1877
+ const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
1878
+ const dbPath = resolveDbPath(repoPath, options.dbPath);
1879
+ if (!isGitRepo(repoPath)) {
1880
+ error(`Not a git repository: ${repoPath}`);
1881
+ process.exit(1);
1882
+ }
1883
+ if (options.verbose) {
1884
+ info(`Repository: ${repoPath}`);
1885
+ info(`Spec storage: ${specStoragePath}`);
1886
+ info(`Database: ${dbPath}`);
1887
+ }
1888
+ const { db } = createDatabase(dbPath);
1889
+ const result = runMiningPipeline(repoPath, specStoragePath, db);
1890
+ if (options.json) {
1891
+ console.log(JSON.stringify(result, null, 2));
1892
+ }
1893
+ else {
1894
+ info(`Specs found: ${result.specsFound}`);
1895
+ info(`Commits found: ${result.commitsFound}`);
1896
+ info(`Fragments: ${result.fragmentsFound}`);
1897
+ info(`Relations: ${result.relationsCreated}`);
1898
+ info(`Total entries: ${result.totalEntries}`);
1899
+ info(`Skipped: ${result.skippedEntries.length}`);
1900
+ if (result.skippedEntries.length > 0) {
1901
+ for (const entry of result.skippedEntries) {
1902
+ warn(`${entry.specId}: ${entry.reason}`);
1903
+ }
1904
+ }
1905
+ }
1906
+ }
1907
+ catch (err) {
1908
+ error(`Mining failed: ${err instanceof Error ? err.message : String(err)}`);
1909
+ process.exit(1);
1910
+ }
1911
+ });
1912
+ /**
1913
+ * homegraph spec match <text>
1914
+ */
1915
+ specCommand
1916
+ .command('match <text>')
1917
+ .description('Search the spec knowledge graph for a term or phrase')
1918
+ .option('-p, --path <path>', 'Path to the repository')
1919
+ .option('--db-path <path>', 'Path to the SQLite database file')
1920
+ .option('--top-k <number>', 'Number of results to return', '5')
1921
+ .option('--no-fragments', 'Exclude code fragments from results')
1922
+ .option('-j, --json', 'Output as JSON')
1923
+ .action(async (text, options) => {
1924
+ let db;
1925
+ try {
1926
+ const repoPath = resolveSpecProjectPath(options.path);
1927
+ const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
1928
+ const { resolveDbPath, computeBudgetProfile } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
1929
+ const { initSpecSchema } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
1930
+ const { searchAndGetContext } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
1931
+ const dbPath = resolveDbPath(repoPath, options.dbPath);
1932
+ if (!fs.existsSync(dbPath)) {
1933
+ error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
1934
+ process.exit(1);
1935
+ }
1936
+ const created = createDatabase(dbPath);
1937
+ db = created.db;
1938
+ initSpecSchema(db);
1939
+ const topK = Math.max(1, Math.min(parseInt(options.topK || '5', 10) || 5, 50));
1940
+ const includeFragments = options.fragments !== false;
1941
+ const contexts = searchAndGetContext(db, text, topK, includeFragments);
1942
+ if (contexts.length === 0) {
1943
+ info('No specs matched your query.');
1944
+ return;
1945
+ }
1946
+ const profile = computeBudgetProfile(contexts.length);
1947
+ const results = contexts.map((ctx) => ({
1948
+ spec_id: ctx.spec.id,
1949
+ title: ctx.spec.title,
1950
+ subtitles: ctx.spec.subtitles,
1951
+ file_path: ctx.spec.filePath,
1952
+ commits: ctx.commits.map((c) => ({
1953
+ hash: c.commit.hash,
1954
+ message: c.commit.message,
1955
+ relation_type: c.relationType,
1956
+ fragments: c.fragments.map((f) => ({
1957
+ file_path: f.filePath,
1958
+ change_type: f.changeType,
1959
+ start_line: f.startLine,
1960
+ end_line: f.endLine,
1961
+ code_diff: f.codeDiff,
1962
+ })),
1963
+ })),
1964
+ }));
1965
+ if (options.json) {
1966
+ console.log(JSON.stringify({
1967
+ query: text.slice(0, 200),
1968
+ matched_count: contexts.length,
1969
+ results,
1970
+ }, null, 2));
1971
+ }
1972
+ else {
1973
+ console.log(chalk.bold(`\n${contexts.length} spec${contexts.length !== 1 ? 's' : ''} matched:\n`));
1974
+ for (const r of results) {
1975
+ console.log(chalk.bold(r.title));
1976
+ for (const commit of r.commits) {
1977
+ console.log(` ${chalk.yellow(commit.hash.slice(0, 7))} ${commit.message}`);
1978
+ if (includeFragments && commit.fragments && commit.fragments.length > 0) {
1979
+ for (const fragment of commit.fragments.slice(0, profile.maxFragments || 3)) {
1980
+ console.log(chalk.dim(` ${fragment.file_path}:${fragment.start_line}-${fragment.end_line} [${fragment.change_type}]`));
1981
+ }
1982
+ }
1983
+ }
1984
+ console.log();
1985
+ }
1986
+ }
1987
+ }
1988
+ catch (err) {
1989
+ error(`Match failed: ${err instanceof Error ? err.message : String(err)}`);
1990
+ process.exit(1);
1991
+ }
1992
+ finally {
1993
+ try {
1994
+ db?.close();
1995
+ }
1996
+ catch { /* best effort */ }
1997
+ }
1998
+ });
1999
+ /**
2000
+ * homegraph spec evolve
2001
+ */
2002
+ const evolveCommand = specCommand
2003
+ .command('evolve')
2004
+ .description('Self-evolve specs after code changes');
2005
+ /**
2006
+ * homegraph spec evolve install
2007
+ */
2008
+ evolveCommand
2009
+ .command('install')
2010
+ .description('Install a git post-commit hook that triggers spec evolution')
2011
+ .option('-p, --path <path>', 'Path to the repository')
2012
+ .option('-f, --force', 'Overwrite existing hook without prompting')
2013
+ .action(async (options) => {
2014
+ try {
2015
+ const repoPath = resolveSpecProjectPath(options.path);
2016
+ const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
2017
+ if (!isGitRepo(repoPath)) {
2018
+ error(`Not a git repository: ${repoPath}`);
2019
+ process.exit(1);
2020
+ }
2021
+ const { execFileSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
2022
+ // Resolve the absolute path to homegraph so the hook works
2023
+ // regardless of PATH (matching Python's shutil.which("c4s")).
2024
+ let homegraphBin;
2025
+ try {
2026
+ homegraphBin = execFileSync('command', ['-v', 'homegraph'], {
2027
+ encoding: 'utf8',
2028
+ stdio: ['ignore', 'pipe', 'ignore'],
2029
+ }).trim();
2030
+ }
2031
+ catch {
2032
+ homegraphBin = '';
2033
+ }
2034
+ if (!homegraphBin) {
2035
+ error('homegraph command not found in PATH.\n' +
2036
+ ' Make sure homegraph is installed globally: npm install -g homegraph');
2037
+ process.exit(1);
2038
+ }
2039
+ const hooksDir = execFileSync('git', ['rev-parse', '--git-path', 'hooks'], {
2040
+ cwd: repoPath,
2041
+ encoding: 'utf8',
2042
+ stdio: ['ignore', 'pipe', 'ignore'],
2043
+ windowsHide: true,
2044
+ }).trim();
2045
+ const resolvedHooksDir = path.isAbsolute(hooksDir)
2046
+ ? hooksDir
2047
+ : path.resolve(repoPath, hooksDir);
2048
+ fs.mkdirSync(resolvedHooksDir, { recursive: true });
2049
+ const hookPath = path.join(resolvedHooksDir, 'post-commit');
2050
+ if (fs.existsSync(hookPath) && !options.force) {
2051
+ const readline = await Promise.resolve().then(() => __importStar(require('readline')));
2052
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2053
+ const answer = await new Promise((resolve) => {
2054
+ rl.question(chalk.yellow('A post-commit hook already exists. Overwrite? (y/N) '), resolve);
2055
+ });
2056
+ rl.close();
2057
+ if (answer.toLowerCase() !== 'y') {
2058
+ info('Cancelled');
2059
+ return;
2060
+ }
2061
+ }
2062
+ // Ensure logs directory
2063
+ fs.mkdirSync(path.join(repoPath, utils_1.SPEC_DATA_DIR, 'logs'), { recursive: true });
2064
+ // Marker constants matching the git-hooks pattern
2065
+ const MARKER_BEGIN = '# >>> homegraph spec evolve hook >>>';
2066
+ const MARKER_END = '# <<< homegraph spec evolve hook <<<';
2067
+ const hookBlock = [
2068
+ MARKER_BEGIN,
2069
+ '# Triggers spec self-evolution after each commit. Runs in background.',
2070
+ '# Installed by: homegraph spec evolve install',
2071
+ `# Logs: ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log`,
2072
+ `"${homegraphBin}" spec evolve process --path "$(pwd)" --json \\`,
2073
+ ` >> ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log 2>&1 &`,
2074
+ MARKER_END,
2075
+ ].join('\n');
2076
+ let content;
2077
+ if (fs.existsSync(hookPath)) {
2078
+ // Strip any prior marker block, then re-append the current one
2079
+ const existing = fs.readFileSync(hookPath, 'utf8');
2080
+ const lines = existing.split('\n');
2081
+ const kept = [];
2082
+ let inBlock = false;
2083
+ for (const line of lines) {
2084
+ const trimmed = line.trim();
2085
+ if (trimmed === MARKER_BEGIN) {
2086
+ inBlock = true;
2087
+ continue;
2088
+ }
2089
+ if (trimmed === MARKER_END) {
2090
+ inBlock = false;
2091
+ continue;
2092
+ }
2093
+ if (!inBlock)
2094
+ kept.push(line);
2095
+ }
2096
+ const base = kept.join('\n').replace(/\s*$/, '');
2097
+ content = base.length > 0
2098
+ ? `${base}\n\n${hookBlock}\n`
2099
+ : `#!/bin/sh\n${hookBlock}\n`;
2100
+ }
2101
+ else {
2102
+ content = `#!/bin/sh\n${hookBlock}\n`;
2103
+ }
2104
+ fs.writeFileSync(hookPath, content);
2105
+ fs.chmodSync(hookPath, 0o755);
2106
+ success(`Post-commit hook installed at ${hookPath}`);
2107
+ info(`Logs written to ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log`);
2108
+ }
2109
+ catch (err) {
2110
+ error(`Hook install failed: ${err instanceof Error ? err.message : String(err)}`);
2111
+ process.exit(1);
2112
+ }
2113
+ });
2114
+ /**
2115
+ * homegraph spec evolve process
2116
+ */
2117
+ evolveCommand
2118
+ .command('process')
2119
+ .description('Process a commit through the spec self-evolve pipeline')
2120
+ .option('-p, --path <path>', 'Path to the repository')
2121
+ .option('--db-path <path>', 'Path to the SQLite database file')
2122
+ .option('--commit-hash <hash>', 'Commit hash to process (default: HEAD)', 'HEAD')
2123
+ .option('-j, --json', 'Output as JSON')
2124
+ .action(async (options) => {
2125
+ let db;
2126
+ try {
2127
+ const repoPath = resolveSpecProjectPath(options.path);
2128
+ const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
2129
+ const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2130
+ const { initSpecSchema } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
2131
+ const { loadSpecConfig } = await Promise.resolve().then(() => __importStar(require('../spec/config')));
2132
+ const { runEvolvePipeline } = await Promise.resolve().then(() => __importStar(require('../spec/evolve/pipeline')));
2133
+ const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
2134
+ if (!isGitRepo(repoPath)) {
2135
+ error(`Not a git repository: ${repoPath}`);
2136
+ process.exit(1);
2137
+ }
2138
+ const dbPath = resolveDbPath(repoPath, options.dbPath);
2139
+ const created = createDatabase(dbPath);
2140
+ db = created.db;
2141
+ initSpecSchema(db);
2142
+ const config = loadSpecConfig(repoPath);
2143
+ const llmConfig = config.llm;
2144
+ const commitHash = options.commitHash || 'HEAD';
2145
+ const result = await runEvolvePipeline(repoPath, db, commitHash, llmConfig);
2146
+ if (options.json) {
2147
+ console.log(JSON.stringify(result, null, 2));
2148
+ }
2149
+ else {
2150
+ console.log(chalk.bold(`Processing commit ${result.commitHash.slice(0, 7)}`));
2151
+ console.log(`Is logic change: ${result.isLogicChange ? chalk.green('✅ yes') : chalk.red('❌ no')}`);
2152
+ console.log(`Reason: ${result.logicCheckReason}`);
2153
+ if (result.generateSpecId) {
2154
+ console.log(chalk.cyan(`Link Path A - GENERATE ${result.generateSpecId}`));
2155
+ }
2156
+ if (result.evolvedSpecs.length > 0) {
2157
+ console.log(chalk.cyan(`Cycle Path B - Self-evolve (${result.affectedSpecCount} affected)`));
2158
+ for (const evolved of result.evolvedSpecs) {
2159
+ console.log(` ${evolved.specId}: ${evolved.action}`);
2160
+ }
2161
+ }
2162
+ console.log(`Summary: fragments=${result.fragmentsCount}, relations=${result.relationsCreated}`);
2163
+ }
2164
+ }
2165
+ catch (err) {
2166
+ const message = err instanceof Error ? err.message : String(err);
2167
+ if (message.includes('No meta.json found')) {
2168
+ error("No meta.json found. Run 'homegraph spec mine' first.");
2169
+ }
2170
+ else {
2171
+ error(`Evolve failed: ${message}`);
2172
+ }
2173
+ process.exit(1);
2174
+ }
2175
+ finally {
2176
+ try {
2177
+ db?.close();
2178
+ }
2179
+ catch { /* best effort */ }
2180
+ }
2181
+ });
1820
2182
  /**
1821
2183
  * homegraph install
1822
2184
  */