homegraph 1.4.1 → 1.5.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.
- package/README.md +48 -11
- package/dist/bin/homegraph.js +295 -72
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +32 -11
- package/dist/context/index.js.map +1 -1
- package/dist/db/index.d.ts +143 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +293 -16
- package/dist/db/index.js.map +1 -1
- package/dist/db/queries.d.ts +43 -4
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +138 -14
- package/dist/db/queries.js.map +1 -1
- package/dist/db/sqlite-adapter.d.ts +24 -17
- package/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/dist/db/sqlite-adapter.js +292 -36
- package/dist/db/sqlite-adapter.js.map +1 -1
- package/dist/db/wal-valve.d.ts +15 -2
- package/dist/db/wal-valve.d.ts.map +1 -1
- package/dist/db/wal-valve.js +88 -6
- package/dist/db/wal-valve.js.map +1 -1
- package/dist/extraction/arkts-batch-worker.js +3 -2
- package/dist/extraction/arkts-batch-worker.js.map +1 -1
- package/dist/extraction/default-ignore.d.ts +39 -0
- package/dist/extraction/default-ignore.d.ts.map +1 -0
- package/dist/extraction/default-ignore.js +221 -0
- package/dist/extraction/default-ignore.js.map +1 -0
- package/dist/extraction/index.d.ts +9 -10
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +227 -323
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/arkts.d.ts +90 -6
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +1579 -128
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/extraction/store-worker.d.ts +20 -0
- package/dist/extraction/store-worker.d.ts.map +1 -0
- package/dist/extraction/store-worker.js +102 -0
- package/dist/extraction/store-worker.js.map +1 -0
- package/dist/extraction/store-writer.d.ts +48 -0
- package/dist/extraction/store-writer.d.ts.map +1 -0
- package/dist/extraction/store-writer.js +167 -0
- package/dist/extraction/store-writer.js.map +1 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.js +47 -0
- package/dist/extraction/tree-sitter.js.map +1 -1
- package/dist/extraction/wasm-runtime-flags.d.ts +14 -4
- package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -1
- package/dist/extraction/wasm-runtime-flags.js +55 -7
- package/dist/extraction/wasm-runtime-flags.js.map +1 -1
- package/dist/index.d.ts +16 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +135 -97
- package/dist/index.js.map +1 -1
- package/dist/mcp/query-worker.d.ts +2 -2
- package/dist/mcp/query-worker.js +2 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +42 -26
- package/dist/mcp/tools.js.map +1 -1
- package/dist/resolution/callback-synthesizer.d.ts +52 -16
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/dist/resolution/callback-synthesizer.js +234 -180
- package/dist/resolution/callback-synthesizer.js.map +1 -1
- package/dist/resolution/cooperative-yield.d.ts +10 -2
- package/dist/resolution/cooperative-yield.d.ts.map +1 -1
- package/dist/resolution/cooperative-yield.js +6 -4
- package/dist/resolution/cooperative-yield.js.map +1 -1
- package/dist/resolution/frameworks/go.d.ts.map +1 -1
- package/dist/resolution/frameworks/go.js +29 -6
- package/dist/resolution/frameworks/go.js.map +1 -1
- package/dist/resolution/import-resolver.d.ts +2 -3
- package/dist/resolution/import-resolver.d.ts.map +1 -1
- package/dist/resolution/import-resolver.js +166 -7
- package/dist/resolution/import-resolver.js.map +1 -1
- package/dist/resolution/index.d.ts +87 -2
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +739 -131
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/memory-budget.d.ts +48 -0
- package/dist/resolution/memory-budget.d.ts.map +1 -0
- package/dist/resolution/memory-budget.js +162 -0
- package/dist/resolution/memory-budget.js.map +1 -0
- package/dist/resolution/name-matcher.d.ts +12 -1
- package/dist/resolution/name-matcher.d.ts.map +1 -1
- package/dist/resolution/name-matcher.js +439 -97
- package/dist/resolution/name-matcher.js.map +1 -1
- package/dist/resolution/resolver-pool.d.ts +106 -0
- package/dist/resolution/resolver-pool.d.ts.map +1 -0
- package/dist/resolution/resolver-pool.js +344 -0
- package/dist/resolution/resolver-pool.js.map +1 -0
- package/dist/resolution/resolver-worker.d.ts +17 -0
- package/dist/resolution/resolver-worker.d.ts.map +1 -0
- package/dist/resolution/resolver-worker.js +141 -0
- package/dist/resolution/resolver-worker.js.map +1 -0
- package/dist/resolution/types.d.ts +3 -0
- package/dist/resolution/types.d.ts.map +1 -1
- package/dist/spec/build/diff-parser.d.ts +55 -0
- package/dist/spec/build/diff-parser.d.ts.map +1 -0
- package/dist/spec/build/diff-parser.js +205 -0
- package/dist/spec/build/diff-parser.js.map +1 -0
- package/dist/spec/build/git-scanner.d.ts +93 -0
- package/dist/spec/build/git-scanner.d.ts.map +1 -0
- package/dist/spec/build/git-scanner.js +254 -0
- package/dist/spec/build/git-scanner.js.map +1 -0
- package/dist/spec/build/pipeline.d.ts +54 -0
- package/dist/spec/build/pipeline.d.ts.map +1 -0
- package/dist/spec/build/pipeline.js +170 -0
- package/dist/spec/build/pipeline.js.map +1 -0
- package/dist/spec/build/scan.d.ts +48 -0
- package/dist/spec/build/scan.d.ts.map +1 -0
- package/dist/spec/build/scan.js +89 -0
- package/dist/spec/build/scan.js.map +1 -0
- package/dist/spec/build/scope-resolver.d.ts +50 -0
- package/dist/spec/build/scope-resolver.d.ts.map +1 -0
- package/dist/spec/build/scope-resolver.js +105 -0
- package/dist/spec/build/scope-resolver.js.map +1 -0
- package/dist/spec/build/spec-extractor.d.ts +74 -0
- package/dist/spec/build/spec-extractor.d.ts.map +1 -0
- package/dist/spec/build/spec-extractor.js +344 -0
- package/dist/spec/build/spec-extractor.js.map +1 -0
- package/dist/spec/config.d.ts +35 -1
- package/dist/spec/config.d.ts.map +1 -1
- package/dist/spec/config.js +30 -5
- package/dist/spec/config.js.map +1 -1
- package/dist/spec/db/fts.d.ts.map +1 -1
- package/dist/spec/db/fts.js +19 -50
- package/dist/spec/db/fts.js.map +1 -1
- package/dist/spec/db/index.d.ts +5 -3
- package/dist/spec/db/index.d.ts.map +1 -1
- package/dist/spec/db/index.js +16 -1
- package/dist/spec/db/index.js.map +1 -1
- package/dist/spec/db/persist.d.ts +45 -0
- package/dist/spec/db/persist.d.ts.map +1 -0
- package/dist/spec/db/persist.js +66 -0
- package/dist/spec/db/persist.js.map +1 -0
- package/dist/spec/db/relations.d.ts +80 -1
- package/dist/spec/db/relations.d.ts.map +1 -1
- package/dist/spec/db/relations.js +175 -4
- package/dist/spec/db/relations.js.map +1 -1
- package/dist/spec/db/schema.d.ts +4 -0
- package/dist/spec/db/schema.d.ts.map +1 -1
- package/dist/spec/db/schema.js +4 -0
- package/dist/spec/db/schema.js.map +1 -1
- package/dist/spec/db/schema.sql +5 -1
- package/dist/spec/db/spec-node.d.ts +5 -0
- package/dist/spec/db/spec-node.d.ts.map +1 -1
- package/dist/spec/db/spec-node.js +11 -5
- package/dist/spec/db/spec-node.js.map +1 -1
- package/dist/spec/db/sql-utils.d.ts +11 -0
- package/dist/spec/db/sql-utils.d.ts.map +1 -0
- package/dist/spec/db/sql-utils.js +19 -0
- package/dist/spec/db/sql-utils.js.map +1 -0
- package/dist/spec/evolve/cluster-context.d.ts +30 -0
- package/dist/spec/evolve/cluster-context.d.ts.map +1 -0
- package/dist/spec/evolve/cluster-context.js +78 -0
- package/dist/spec/evolve/cluster-context.js.map +1 -0
- package/dist/spec/evolve/commit-spec-analyzer.d.ts +51 -0
- package/dist/spec/evolve/commit-spec-analyzer.d.ts.map +1 -0
- package/dist/spec/evolve/commit-spec-analyzer.js +88 -0
- package/dist/spec/evolve/commit-spec-analyzer.js.map +1 -0
- package/dist/spec/evolve/commit-spec-persister.d.ts +53 -0
- package/dist/spec/evolve/commit-spec-persister.d.ts.map +1 -0
- package/dist/spec/evolve/commit-spec-persister.js +93 -0
- package/dist/spec/evolve/commit-spec-persister.js.map +1 -0
- package/dist/spec/evolve/impact-locator.d.ts +23 -7
- package/dist/spec/evolve/impact-locator.d.ts.map +1 -1
- package/dist/spec/evolve/impact-locator.js +58 -14
- package/dist/spec/evolve/impact-locator.js.map +1 -1
- package/dist/spec/evolve/pipeline.d.ts +62 -25
- package/dist/spec/evolve/pipeline.d.ts.map +1 -1
- package/dist/spec/evolve/pipeline.js +436 -457
- package/dist/spec/evolve/pipeline.js.map +1 -1
- package/dist/spec/evolve/spec-rewriter.d.ts +13 -13
- package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -1
- package/dist/spec/evolve/spec-rewriter.js +45 -45
- package/dist/spec/evolve/spec-rewriter.js.map +1 -1
- package/dist/spec/git/commits.d.ts +85 -0
- package/dist/spec/git/commits.d.ts.map +1 -0
- package/dist/spec/git/commits.js +218 -0
- package/dist/spec/git/commits.js.map +1 -0
- package/dist/spec/git/exec.d.ts +13 -0
- package/dist/spec/git/exec.d.ts.map +1 -0
- package/dist/spec/git/exec.js +19 -0
- package/dist/spec/git/exec.js.map +1 -0
- package/dist/spec/git/index.d.ts +9 -0
- package/dist/spec/git/index.d.ts.map +1 -0
- package/dist/spec/git/index.js +22 -0
- package/dist/spec/git/index.js.map +1 -0
- package/dist/spec/git-utils.d.ts +8 -0
- package/dist/spec/git-utils.d.ts.map +1 -0
- package/dist/spec/git-utils.js +14 -0
- package/dist/spec/git-utils.js.map +1 -0
- package/dist/spec/graph/index.d.ts +8 -0
- package/dist/spec/graph/index.d.ts.map +1 -0
- package/dist/spec/graph/index.js +17 -0
- package/dist/spec/graph/index.js.map +1 -0
- package/dist/spec/graph/queries.d.ts +6 -35
- package/dist/spec/graph/queries.d.ts.map +1 -1
- package/dist/spec/graph/queries.js +71 -248
- package/dist/spec/graph/queries.js.map +1 -1
- package/dist/spec/llm/agent-client.d.ts +59 -0
- package/dist/spec/llm/agent-client.d.ts.map +1 -0
- package/dist/spec/llm/agent-client.js +213 -0
- package/dist/spec/llm/agent-client.js.map +1 -0
- package/dist/spec/llm/agents/claude-code.d.ts +32 -0
- package/dist/spec/llm/agents/claude-code.d.ts.map +1 -0
- package/dist/spec/llm/agents/claude-code.js +125 -0
- package/dist/spec/llm/agents/claude-code.js.map +1 -0
- package/dist/spec/llm/agents/codex.d.ts +32 -0
- package/dist/spec/llm/agents/codex.d.ts.map +1 -0
- package/dist/spec/llm/agents/codex.js +145 -0
- package/dist/spec/llm/agents/codex.js.map +1 -0
- package/dist/spec/llm/agents/detect-utils.d.ts +28 -0
- package/dist/spec/llm/agents/detect-utils.d.ts.map +1 -0
- package/dist/spec/llm/agents/detect-utils.js +91 -0
- package/dist/spec/llm/agents/detect-utils.js.map +1 -0
- package/dist/spec/llm/agents/deveco-code.d.ts +33 -0
- package/dist/spec/llm/agents/deveco-code.d.ts.map +1 -0
- package/dist/spec/llm/agents/deveco-code.js +165 -0
- package/dist/spec/llm/agents/deveco-code.js.map +1 -0
- package/dist/spec/llm/agents/index.d.ts +31 -0
- package/dist/spec/llm/agents/index.d.ts.map +1 -0
- package/dist/spec/llm/agents/index.js +65 -0
- package/dist/spec/llm/agents/index.js.map +1 -0
- package/dist/spec/llm/agents/types.d.ts +76 -0
- package/dist/spec/llm/agents/types.d.ts.map +1 -0
- package/dist/spec/llm/agents/types.js +15 -0
- package/dist/spec/llm/agents/types.js.map +1 -0
- package/dist/spec/llm/client.d.ts +31 -11
- package/dist/spec/llm/client.d.ts.map +1 -1
- package/dist/spec/llm/client.js +143 -81
- package/dist/spec/llm/client.js.map +1 -1
- package/dist/spec/llm/factory.d.ts +41 -0
- package/dist/spec/llm/factory.d.ts.map +1 -0
- package/dist/spec/llm/factory.js +80 -0
- package/dist/spec/llm/factory.js.map +1 -0
- package/dist/spec/llm/prompts.d.ts +24 -8
- package/dist/spec/llm/prompts.d.ts.map +1 -1
- package/dist/spec/llm/prompts.js +100 -50
- package/dist/spec/llm/prompts.js.map +1 -1
- package/dist/spec/llm/retry.d.ts +53 -0
- package/dist/spec/llm/retry.d.ts.map +1 -0
- package/dist/spec/llm/retry.js +153 -0
- package/dist/spec/llm/retry.js.map +1 -0
- package/dist/spec/mine/clusterer.d.ts +63 -0
- package/dist/spec/mine/clusterer.d.ts.map +1 -0
- package/dist/spec/mine/clusterer.js +904 -0
- package/dist/spec/mine/clusterer.js.map +1 -0
- package/dist/spec/mine/clustering/features.d.ts +38 -0
- package/dist/spec/mine/clustering/features.d.ts.map +1 -0
- package/dist/spec/mine/clustering/features.js +108 -0
- package/dist/spec/mine/clustering/features.js.map +1 -0
- package/dist/spec/mine/clustering/index.d.ts +62 -0
- package/dist/spec/mine/clustering/index.d.ts.map +1 -0
- package/dist/spec/mine/clustering/index.js +259 -0
- package/dist/spec/mine/clustering/index.js.map +1 -0
- package/dist/spec/mine/clustering/leiden.d.ts +61 -0
- package/dist/spec/mine/clustering/leiden.d.ts.map +1 -0
- package/dist/spec/mine/clustering/leiden.js +489 -0
- package/dist/spec/mine/clustering/leiden.js.map +1 -0
- package/dist/spec/mine/clustering/text-similarity.d.ts +22 -0
- package/dist/spec/mine/clustering/text-similarity.d.ts.map +1 -0
- package/dist/spec/mine/clustering/text-similarity.js +98 -0
- package/dist/spec/mine/clustering/text-similarity.js.map +1 -0
- package/dist/spec/mine/generator.d.ts +54 -0
- package/dist/spec/mine/generator.d.ts.map +1 -0
- package/dist/spec/mine/generator.js +267 -0
- package/dist/spec/mine/generator.js.map +1 -0
- package/dist/spec/mine/persist.d.ts +37 -0
- package/dist/spec/mine/persist.d.ts.map +1 -0
- package/dist/spec/mine/persist.js +170 -0
- package/dist/spec/mine/persist.js.map +1 -0
- package/dist/spec/mine/pipeline.d.ts +41 -0
- package/dist/spec/mine/pipeline.d.ts.map +1 -0
- package/dist/spec/mine/pipeline.js +236 -0
- package/dist/spec/mine/pipeline.js.map +1 -0
- package/dist/spec/mine/progress-handler.d.ts +22 -0
- package/dist/spec/mine/progress-handler.d.ts.map +1 -0
- package/dist/spec/mine/progress-handler.js +108 -0
- package/dist/spec/mine/progress-handler.js.map +1 -0
- package/dist/spec/mine/progress.d.ts +23 -0
- package/dist/spec/mine/progress.d.ts.map +1 -0
- package/dist/spec/mine/progress.js +12 -0
- package/dist/spec/mine/progress.js.map +1 -0
- package/dist/spec/mine/scanner.d.ts +59 -0
- package/dist/spec/mine/scanner.d.ts.map +1 -0
- package/dist/spec/mine/scanner.js +383 -0
- package/dist/spec/mine/scanner.js.map +1 -0
- package/dist/spec/types.d.ts +32 -0
- package/dist/spec/types.d.ts.map +1 -1
- package/dist/spec/ui/index.d.ts +9 -0
- package/dist/spec/ui/index.d.ts.map +1 -0
- package/dist/spec/ui/index.js +16 -0
- package/dist/spec/ui/index.js.map +1 -0
- package/dist/spec/ui/progress-handler.d.ts +33 -0
- package/dist/spec/ui/progress-handler.d.ts.map +1 -0
- package/dist/spec/ui/progress-handler.js +132 -0
- package/dist/spec/ui/progress-handler.js.map +1 -0
- package/dist/spec/ui/progress.d.ts +24 -0
- package/dist/spec/ui/progress.d.ts.map +1 -0
- package/dist/spec/ui/progress.js +13 -0
- package/dist/spec/ui/progress.js.map +1 -0
- package/dist/spec/utils/fs.d.ts +45 -0
- package/dist/spec/utils/fs.d.ts.map +1 -0
- package/dist/spec/utils/fs.js +163 -0
- package/dist/spec/utils/fs.js.map +1 -0
- package/dist/spec/utils/index.d.ts +12 -0
- package/dist/spec/utils/index.d.ts.map +1 -0
- package/dist/spec/utils/index.js +26 -0
- package/dist/spec/utils/index.js.map +1 -0
- package/dist/spec/utils/meta.d.ts +32 -0
- package/dist/spec/utils/meta.d.ts.map +1 -0
- package/dist/spec/utils/meta.js +122 -0
- package/dist/spec/utils/meta.js.map +1 -0
- package/dist/spec/utils/truncate.d.ts +86 -0
- package/dist/spec/utils/truncate.d.ts.map +1 -0
- package/dist/spec/utils/truncate.js +172 -0
- package/dist/spec/utils/truncate.js.map +1 -0
- package/dist/spec/utils.d.ts +4 -16
- package/dist/spec/utils.d.ts.map +1 -1
- package/dist/spec/utils.js +6 -58
- package/dist/spec/utils.js.map +1 -1
- package/dist/sync/watcher.d.ts +25 -1
- package/dist/sync/watcher.d.ts.map +1 -1
- package/dist/sync/watcher.js +76 -3
- package/dist/sync/watcher.js.map +1 -1
- package/dist/telemetry/index.js +3 -3
- package/dist/telemetry/index.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -3
package/dist/bin/homegraph.js
CHANGED
|
@@ -60,8 +60,9 @@ 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 directory_1 = require("../directory");
|
|
64
63
|
const utils_1 = require("../spec/utils");
|
|
64
|
+
const config_1 = require("../spec/config");
|
|
65
|
+
const directory_1 = require("../directory");
|
|
65
66
|
const worktree_1 = require("../sync/worktree");
|
|
66
67
|
const shimmer_progress_1 = require("../ui/shimmer-progress");
|
|
67
68
|
const glyphs_1 = require("../ui/glyphs");
|
|
@@ -426,9 +427,9 @@ function main() {
|
|
|
426
427
|
if (arktsDegraded.length > 0) {
|
|
427
428
|
clack.log.warn('ArkTS index quality notice — see details below.');
|
|
428
429
|
clack.note(`${arktsDegraded[0].message}\n\n` +
|
|
429
|
-
'
|
|
430
|
-
'
|
|
431
|
-
'
|
|
430
|
+
'ArkTS Scene builds run in-process by default (fastest). If the indexer\n' +
|
|
431
|
+
'exits with a Windows stack overflow on a huge repo, opt into the isolated\n' +
|
|
432
|
+
'worker: HOMEGRAPH_ARKTS_ISOLATED=1 (optional HOMEGRAPH_ARKTS_STACK_SIZES_KB).', 'ArkTS quality notice');
|
|
432
433
|
}
|
|
433
434
|
}
|
|
434
435
|
/**
|
|
@@ -847,14 +848,11 @@ function main() {
|
|
|
847
848
|
console.log(` Nodes: ${formatNumber(stats.nodeCount)}`);
|
|
848
849
|
console.log(` Edges: ${formatNumber(stats.edgeCount)}`);
|
|
849
850
|
console.log(` DB Size: ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
850
|
-
//
|
|
851
|
-
|
|
852
|
-
|
|
851
|
+
// Prefer node:sqlite → better-sqlite3; wasm is last-resort (no WAL).
|
|
852
|
+
const backendLabel = backend === 'node-sqlite' ? chalk.green('node-sqlite')
|
|
853
|
+
: backend === 'native' ? chalk.green('native (better-sqlite3)')
|
|
854
|
+
: chalk.yellow(`wasm ${(0, glyphs_1.getGlyphs)().dash} slower fallback; use Node 22.5+ or \`npm rebuild better-sqlite3\``);
|
|
853
855
|
console.log(` Backend: ${backendLabel}`);
|
|
854
|
-
// Effective journal mode: 'wal' means concurrent reads never block on a
|
|
855
|
-
// writer; anything else means they can ("database is locked"). node:sqlite
|
|
856
|
-
// supports WAL everywhere, so a non-wal mode means the filesystem can't
|
|
857
|
-
// (network mounts, WSL2 /mnt). See issue #238.
|
|
858
856
|
const journalLabel = journalMode === 'wal'
|
|
859
857
|
? chalk.green('wal')
|
|
860
858
|
: chalk.yellow(`${journalMode || 'unknown'} ${(0, glyphs_1.getGlyphs)().dash} WAL inactive; reads can block on writes`);
|
|
@@ -2008,27 +2006,27 @@ function main() {
|
|
|
2008
2006
|
// =============================================================================
|
|
2009
2007
|
const specCommand = program
|
|
2010
2008
|
.command('spec')
|
|
2011
|
-
.description('Manage spec knowledge graph (
|
|
2009
|
+
.description('Manage spec knowledge graph (build, mine, search, self-evolve)');
|
|
2012
2010
|
/**
|
|
2013
|
-
* homegraph spec
|
|
2011
|
+
* homegraph spec build
|
|
2014
2012
|
*/
|
|
2015
2013
|
specCommand
|
|
2016
|
-
.command('
|
|
2017
|
-
.description('
|
|
2014
|
+
.command('build')
|
|
2015
|
+
.description('Build spec knowledge graph from scanned Git history')
|
|
2018
2016
|
.option('-p, --path <path>', 'Path to the repository')
|
|
2019
|
-
.option('--spec-
|
|
2020
|
-
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2017
|
+
.option('--spec-dir <path>', 'Path to the .spec directory')
|
|
2021
2018
|
.option('-v, --verbose', 'Show detailed output')
|
|
2022
2019
|
.option('-j, --json', 'Output as JSON')
|
|
2023
2020
|
.action(async (options) => {
|
|
2024
2021
|
try {
|
|
2025
2022
|
const repoPath = path.resolve(options.path || process.cwd());
|
|
2026
|
-
const specStoragePath =
|
|
2023
|
+
const specStoragePath = path.resolve(repoPath, options.specDir || '.spec');
|
|
2027
2024
|
const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
|
|
2028
|
-
const {
|
|
2029
|
-
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/
|
|
2025
|
+
const { runBuildPipeline } = await Promise.resolve().then(() => __importStar(require('../spec/build/pipeline')));
|
|
2026
|
+
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
|
|
2030
2027
|
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2031
|
-
const
|
|
2028
|
+
const { createBuildProgressHandler } = await Promise.resolve().then(() => __importStar(require('../spec/ui')));
|
|
2029
|
+
const dbPath = resolveDbPath(repoPath);
|
|
2032
2030
|
if (!isGitRepo(repoPath)) {
|
|
2033
2031
|
error(`Not a git repository: ${repoPath}`);
|
|
2034
2032
|
process.exit(1);
|
|
@@ -2038,8 +2036,25 @@ function main() {
|
|
|
2038
2036
|
info(`Spec storage: ${specStoragePath}`);
|
|
2039
2037
|
info(`Database: ${dbPath}`);
|
|
2040
2038
|
}
|
|
2039
|
+
// Progress reporting (mirrors `spec mine`):
|
|
2040
|
+
// - JSON mode: no progress output (only final JSON).
|
|
2041
|
+
// - Verbose mode: plain-text lines with timestamps.
|
|
2042
|
+
// - TTY (default): ANSI progress bar with phase + item details.
|
|
2043
|
+
// - Pipe / non-TTY: fall back to verbose (5 % stepping).
|
|
2044
|
+
const onProgress = options.json
|
|
2045
|
+
? undefined
|
|
2046
|
+
: createBuildProgressHandler(options.verbose ? 'verbose' : process.stdout.isTTY ? 'bar' : 'verbose');
|
|
2041
2047
|
const { db } = createDatabase(dbPath);
|
|
2042
|
-
const result =
|
|
2048
|
+
const result = runBuildPipeline(repoPath, specStoragePath, db, onProgress);
|
|
2049
|
+
if (result.upToDate) {
|
|
2050
|
+
if (options.json) {
|
|
2051
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2052
|
+
}
|
|
2053
|
+
else {
|
|
2054
|
+
info('Spec knowledge graph is up to date — nothing to build.');
|
|
2055
|
+
}
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2043
2058
|
if (options.json) {
|
|
2044
2059
|
console.log(JSON.stringify(result, null, 2));
|
|
2045
2060
|
}
|
|
@@ -2062,6 +2077,168 @@ function main() {
|
|
|
2062
2077
|
process.exit(1);
|
|
2063
2078
|
}
|
|
2064
2079
|
});
|
|
2080
|
+
/**
|
|
2081
|
+
* homegraph spec mine
|
|
2082
|
+
*
|
|
2083
|
+
* Mine design specs from Git history using AST analysis and LLM
|
|
2084
|
+
*/
|
|
2085
|
+
specCommand
|
|
2086
|
+
.command('mine')
|
|
2087
|
+
.description('Mine design specs from Git history using AST analysis and LLM')
|
|
2088
|
+
.option('-p, --path <path>', 'Path to the repository')
|
|
2089
|
+
.option('--limit <number>', 'Maximum commits to scan', '100')
|
|
2090
|
+
.option('--output <path>', 'Output directory for generated specs', '.spec')
|
|
2091
|
+
.option('--threshold <number>', 'Clustering similarity threshold (0-1)', '0.25')
|
|
2092
|
+
.option('--max-cluster <number>', 'Maximum number of clusters', '10')
|
|
2093
|
+
.option('--template <path>', 'Path to a spec template markdown file')
|
|
2094
|
+
.option('--skip-llm', 'Skip LLM generation — only output clusters')
|
|
2095
|
+
.option('-v, --verbose', 'Show detailed output')
|
|
2096
|
+
.option('-j, --json', 'Output as JSON')
|
|
2097
|
+
.action(async (options) => {
|
|
2098
|
+
try {
|
|
2099
|
+
const repoPath = path.resolve(options.path || process.cwd());
|
|
2100
|
+
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
|
|
2101
|
+
const { loadSpecConfig } = await Promise.resolve().then(() => __importStar(require('../spec/config')));
|
|
2102
|
+
const { runMinePipeline } = await Promise.resolve().then(() => __importStar(require('../spec/mine/pipeline')));
|
|
2103
|
+
const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
|
|
2104
|
+
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2105
|
+
const { createMineProgressHandler } = await Promise.resolve().then(() => __importStar(require('../spec/ui')));
|
|
2106
|
+
if (!isGitRepo(repoPath)) {
|
|
2107
|
+
error(`Not a git repository: ${repoPath}`);
|
|
2108
|
+
process.exit(1);
|
|
2109
|
+
}
|
|
2110
|
+
// Parse numeric options
|
|
2111
|
+
const limit = parseInt(options.limit || '100', 10);
|
|
2112
|
+
if (isNaN(limit) || limit < 1 || limit > 1000) {
|
|
2113
|
+
error('--limit must be an integer between 1 and 1000');
|
|
2114
|
+
process.exit(1);
|
|
2115
|
+
}
|
|
2116
|
+
const threshold = parseFloat(options.threshold || '0.25');
|
|
2117
|
+
if (isNaN(threshold) || threshold < 0 || threshold > 1) {
|
|
2118
|
+
error('--threshold must be a number between 0 and 1');
|
|
2119
|
+
process.exit(1);
|
|
2120
|
+
}
|
|
2121
|
+
const maxCluster = parseInt(options.maxCluster || '10', 10);
|
|
2122
|
+
if (isNaN(maxCluster) || maxCluster < 1 || maxCluster > 50) {
|
|
2123
|
+
error('--max-cluster must be an integer between 1 and 50');
|
|
2124
|
+
process.exit(1);
|
|
2125
|
+
}
|
|
2126
|
+
// Resolve output directory: if meta.json exists, reuse its specStoragePath;
|
|
2127
|
+
// otherwise fall back to --output option, then default '.spec'.
|
|
2128
|
+
// All paths are resolved relative to repoPath so that relative values
|
|
2129
|
+
// (e.g. '.spec', or a relative specStoragePath from meta.json) are
|
|
2130
|
+
// anchored correctly regardless of cwd; absolute paths pass through unchanged.
|
|
2131
|
+
const existingMeta = (0, utils_1.readMeta)(repoPath);
|
|
2132
|
+
const outputDir = existingMeta
|
|
2133
|
+
? path.resolve(repoPath, existingMeta.specStoragePath)
|
|
2134
|
+
: path.resolve(repoPath, options.output || '.spec');
|
|
2135
|
+
// Load spec config for LLM setup
|
|
2136
|
+
const specConfig = loadSpecConfig(repoPath);
|
|
2137
|
+
const llmConfig = specConfig.llm;
|
|
2138
|
+
const { resolveAgent } = await Promise.resolve().then(() => __importStar(require('../spec/llm/agents')));
|
|
2139
|
+
const codingAgent = options.skipLlm ? null : resolveAgent();
|
|
2140
|
+
if (!options.skipLlm && !llmConfig && !codingAgent) {
|
|
2141
|
+
info('No coding agent (Claude Code / Codex / DevEco Code) detected and no LLM configuration found.\n' +
|
|
2142
|
+
'Set up "llm" in .homegraph/commit4spec/configs.json.\n' +
|
|
2143
|
+
'\n' +
|
|
2144
|
+
'All available options (fields marked * are required):\n' +
|
|
2145
|
+
'{\n' +
|
|
2146
|
+
' "llm": {\n' +
|
|
2147
|
+
' "provider": "openai", // * "openai" or "anthropic"\n' +
|
|
2148
|
+
' "apiKey": "sk-...", // * API key string (plain text)\n' +
|
|
2149
|
+
' "apiKeyEnv": "OPENAI_API_KEY", // or read from env var (takes precedence)\n' +
|
|
2150
|
+
' "model": "gpt-4o", // * model name (e.g. gpt-4o, claude-3-5-sonnet)\n' +
|
|
2151
|
+
' "baseUrl": "https://...", // custom endpoint (proxies / local models)\n' +
|
|
2152
|
+
' "temperature": 0.2, // creativity control (default: 0.2)\n' +
|
|
2153
|
+
' "maxTokens": 20000 // max output tokens (default: 20000)\n' +
|
|
2154
|
+
' }\n' +
|
|
2155
|
+
'}\n' +
|
|
2156
|
+
'\n' +
|
|
2157
|
+
'Continuing with --skip-llm (clustering only).');
|
|
2158
|
+
}
|
|
2159
|
+
else if (!options.skipLlm && !llmConfig && codingAgent) {
|
|
2160
|
+
info(`Using ${codingAgent.displayName} (headless) for spec generation — no LLM configuration needed.`);
|
|
2161
|
+
}
|
|
2162
|
+
else if (!options.skipLlm && llmConfig && codingAgent) {
|
|
2163
|
+
info(`Using ${codingAgent.displayName} (headless) for spec generation — configured LLM is kept as fallback.`);
|
|
2164
|
+
}
|
|
2165
|
+
const mineConfig = (0, config_1.createMineConfig)({
|
|
2166
|
+
limit,
|
|
2167
|
+
threshold,
|
|
2168
|
+
maxCluster,
|
|
2169
|
+
outputDir,
|
|
2170
|
+
template: options.template,
|
|
2171
|
+
skipLlm: !!options.skipLlm,
|
|
2172
|
+
}, !!llmConfig || codingAgent !== null);
|
|
2173
|
+
if (options.verbose) {
|
|
2174
|
+
info(`Repository: ${repoPath}`);
|
|
2175
|
+
info(`Limit: ${limit === 100 ? '100 (default)' : limit}`);
|
|
2176
|
+
info(`Threshold: ${threshold}`);
|
|
2177
|
+
info(`Max clusters: ${maxCluster}`);
|
|
2178
|
+
info(`Output: ${outputDir}`);
|
|
2179
|
+
info(`Skip LLM: ${mineConfig.skipLlm}`);
|
|
2180
|
+
if (options.template) {
|
|
2181
|
+
info(`Template: ${options.template}`);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
// Open knowledge graph database for persistence
|
|
2185
|
+
let db;
|
|
2186
|
+
try {
|
|
2187
|
+
const dbPath = resolveDbPath(repoPath);
|
|
2188
|
+
const created = createDatabase(dbPath);
|
|
2189
|
+
db = created.db;
|
|
2190
|
+
}
|
|
2191
|
+
catch (err) {
|
|
2192
|
+
// Non-fatal — spec generation and file output still work without DB
|
|
2193
|
+
if (options.verbose) {
|
|
2194
|
+
warn(`Failed to open commit4spec.db: ${err instanceof Error ? err.message : String(err)}`);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
// Wire up progress reporting.
|
|
2198
|
+
// - JSON mode: no progress output (only final JSON).
|
|
2199
|
+
// - Verbose mode: plain-text lines with timestamps.
|
|
2200
|
+
// - TTY (default): ANSI progress bar with phase + item details.
|
|
2201
|
+
// - Pipe / non-TTY: fall back to verbose (5 % stepping).
|
|
2202
|
+
let onProgress;
|
|
2203
|
+
if (!options.json) {
|
|
2204
|
+
onProgress = createMineProgressHandler(options.verbose ? 'verbose' : process.stdout.isTTY ? 'bar' : 'verbose');
|
|
2205
|
+
}
|
|
2206
|
+
let result;
|
|
2207
|
+
try {
|
|
2208
|
+
result = await runMinePipeline(repoPath, mineConfig, llmConfig, db, onProgress);
|
|
2209
|
+
}
|
|
2210
|
+
finally {
|
|
2211
|
+
// Close database even if pipeline throws
|
|
2212
|
+
try {
|
|
2213
|
+
db?.close();
|
|
2214
|
+
}
|
|
2215
|
+
catch { /* best effort */ }
|
|
2216
|
+
}
|
|
2217
|
+
if (options.json) {
|
|
2218
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2219
|
+
}
|
|
2220
|
+
else {
|
|
2221
|
+
success(`Scanned ${result.commitsScanned} commits, ` +
|
|
2222
|
+
`${result.changesFound} with structural changes, ` +
|
|
2223
|
+
`${result.clusters} clusters found.`);
|
|
2224
|
+
if (result.specsGenerated > 0) {
|
|
2225
|
+
success(`${result.specsGenerated} specs generated in ${outputDir}/`);
|
|
2226
|
+
}
|
|
2227
|
+
if (result.specsWritten > 0) {
|
|
2228
|
+
info(`${result.specsWritten} spec(s), ${result.commitsWritten} commit(s), ${result.fragmentsWritten} fragment(s) written to commit4spec.db`);
|
|
2229
|
+
}
|
|
2230
|
+
if (result.errors.length > 0) {
|
|
2231
|
+
for (const err of result.errors) {
|
|
2232
|
+
warn(err);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
catch (err) {
|
|
2238
|
+
error(`Reverse pipeline failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2239
|
+
process.exit(1);
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2065
2242
|
/**
|
|
2066
2243
|
* homegraph spec match <text>
|
|
2067
2244
|
*/
|
|
@@ -2069,7 +2246,6 @@ function main() {
|
|
|
2069
2246
|
.command('match <text>')
|
|
2070
2247
|
.description('Search the spec knowledge graph for a term or phrase')
|
|
2071
2248
|
.option('-p, --path <path>', 'Path to the repository')
|
|
2072
|
-
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2073
2249
|
.option('--top-k <number>', 'Number of results to return', '5')
|
|
2074
2250
|
.option('--no-fragments', 'Exclude code fragments from results')
|
|
2075
2251
|
.option('-j, --json', 'Output as JSON')
|
|
@@ -2080,9 +2256,9 @@ function main() {
|
|
|
2080
2256
|
const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
|
|
2081
2257
|
const { resolveDbPath, computeBudgetProfile } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2082
2258
|
const { searchAndGetContext } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
|
|
2083
|
-
const dbPath = resolveDbPath(repoPath
|
|
2259
|
+
const dbPath = resolveDbPath(repoPath);
|
|
2084
2260
|
if (!fs.existsSync(dbPath)) {
|
|
2085
|
-
error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
|
|
2261
|
+
error(`Database not found at ${dbPath}. Run 'homegraph spec build/mine' first.`);
|
|
2086
2262
|
process.exit(1);
|
|
2087
2263
|
}
|
|
2088
2264
|
const created = createDatabase(dbPath);
|
|
@@ -2154,7 +2330,6 @@ function main() {
|
|
|
2154
2330
|
.command('find <filePath>')
|
|
2155
2331
|
.description('Find specs related to the given file path via code-fragment matching')
|
|
2156
2332
|
.option('-p, --path <path>', 'Path to the repository')
|
|
2157
|
-
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2158
2333
|
.option('-j, --json', 'Output as JSON')
|
|
2159
2334
|
.action(async (filePath, options) => {
|
|
2160
2335
|
let db;
|
|
@@ -2163,9 +2338,9 @@ function main() {
|
|
|
2163
2338
|
const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
|
|
2164
2339
|
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2165
2340
|
const { findSpecsByFilePath } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
|
|
2166
|
-
const dbPath = resolveDbPath(repoPath
|
|
2341
|
+
const dbPath = resolveDbPath(repoPath);
|
|
2167
2342
|
if (!fs.existsSync(dbPath)) {
|
|
2168
|
-
error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
|
|
2343
|
+
error(`Database not found at ${dbPath}. Run 'homegraph spec build/mine' first.`);
|
|
2169
2344
|
process.exit(1);
|
|
2170
2345
|
}
|
|
2171
2346
|
const created = createDatabase(dbPath);
|
|
@@ -2214,7 +2389,6 @@ function main() {
|
|
|
2214
2389
|
.option('-f, --file <path>', 'File path for symbol disambiguation')
|
|
2215
2390
|
.option('-l, --line <number>', 'Line number for symbol disambiguation')
|
|
2216
2391
|
.option('-p, --path <path>', 'Path to the repository')
|
|
2217
|
-
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2218
2392
|
.option('--top-k <number>', 'Number of results to return', '10')
|
|
2219
2393
|
.option('-j, --json', 'Output as JSON')
|
|
2220
2394
|
.action(async (symbol, options) => {
|
|
@@ -2304,9 +2478,9 @@ function main() {
|
|
|
2304
2478
|
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2305
2479
|
const { findSpecsByCodeSymbol } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
|
|
2306
2480
|
const { initSpecSchema, runSpecMigrations, getCurrentSpecVersion, CURRENT_SPEC_SCHEMA_VERSION } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
|
|
2307
|
-
const dbPath = resolveDbPath(repoPath
|
|
2481
|
+
const dbPath = resolveDbPath(repoPath);
|
|
2308
2482
|
if (!fs.existsSync(dbPath)) {
|
|
2309
|
-
console.log(chalk.yellow(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`));
|
|
2483
|
+
console.log(chalk.yellow(`Database not found at ${dbPath}. Run 'homegraph spec build/mine' first.`));
|
|
2310
2484
|
return;
|
|
2311
2485
|
}
|
|
2312
2486
|
const created = createDatabase(dbPath);
|
|
@@ -2394,7 +2568,6 @@ function main() {
|
|
|
2394
2568
|
.command('stats')
|
|
2395
2569
|
.description('Show statistics about the spec knowledge graph')
|
|
2396
2570
|
.option('-p, --path <path>', 'Path to the repository')
|
|
2397
|
-
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2398
2571
|
.option('-j, --json', 'Output as JSON')
|
|
2399
2572
|
.action(async (options) => {
|
|
2400
2573
|
let db;
|
|
@@ -2403,9 +2576,9 @@ function main() {
|
|
|
2403
2576
|
const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
|
|
2404
2577
|
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2405
2578
|
const { getSpecStats } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
|
|
2406
|
-
const dbPath = resolveDbPath(repoPath
|
|
2579
|
+
const dbPath = resolveDbPath(repoPath);
|
|
2407
2580
|
if (!fs.existsSync(dbPath)) {
|
|
2408
|
-
error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
|
|
2581
|
+
error(`Database not found at ${dbPath}. Run 'homegraph spec build/mine' first.`);
|
|
2409
2582
|
process.exit(1);
|
|
2410
2583
|
}
|
|
2411
2584
|
const created = createDatabase(dbPath);
|
|
@@ -2424,7 +2597,7 @@ function main() {
|
|
|
2424
2597
|
console.log(` Relations: ${stats.relationCount}`);
|
|
2425
2598
|
console.log();
|
|
2426
2599
|
if (stats.specCount === 0) {
|
|
2427
|
-
info("No specs yet. Run 'homegraph spec mine' to build the knowledge graph.");
|
|
2600
|
+
info("No specs yet. Run 'homegraph spec build/mine' to build the knowledge graph.");
|
|
2428
2601
|
}
|
|
2429
2602
|
}
|
|
2430
2603
|
}
|
|
@@ -2453,10 +2626,19 @@ function main() {
|
|
|
2453
2626
|
.description('Install a git post-commit hook that triggers spec evolution')
|
|
2454
2627
|
.option('-p, --path <path>', 'Path to the repository')
|
|
2455
2628
|
.option('-f, --force', 'Overwrite existing hook without prompting')
|
|
2629
|
+
.option('-t, --commit-threshold <n>', 'Number of pending commits to accumulate before triggering evolve', (v) => {
|
|
2630
|
+
const n = parseInt(v, 10);
|
|
2631
|
+
if (isNaN(n) || n < 1) {
|
|
2632
|
+
error('--commit-threshold must be a positive integer');
|
|
2633
|
+
process.exit(1);
|
|
2634
|
+
}
|
|
2635
|
+
return n;
|
|
2636
|
+
}, 3)
|
|
2456
2637
|
.action(async (options) => {
|
|
2457
2638
|
try {
|
|
2458
2639
|
const repoPath = resolveSpecProjectPath(options.path);
|
|
2459
|
-
const
|
|
2640
|
+
const commitThreshold = options.commitThreshold ?? 3;
|
|
2641
|
+
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
|
|
2460
2642
|
if (!isGitRepo(repoPath)) {
|
|
2461
2643
|
error(`Not a git repository: ${repoPath}`);
|
|
2462
2644
|
process.exit(1);
|
|
@@ -2509,14 +2691,47 @@ function main() {
|
|
|
2509
2691
|
const MARKER_END = '# <<< homegraph spec evolve hook <<<';
|
|
2510
2692
|
const hookBlock = [
|
|
2511
2693
|
MARKER_BEGIN,
|
|
2512
|
-
'# Triggers spec self-evolution after
|
|
2694
|
+
'# Triggers spec self-evolution after N commits have accumulated.',
|
|
2695
|
+
'# Default threshold: 3 commits (configurable via --commit-threshold).',
|
|
2513
2696
|
'# Installed by: homegraph spec evolve install',
|
|
2514
2697
|
`# Logs: ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log`,
|
|
2698
|
+
'',
|
|
2699
|
+
`THRESHOLD=${commitThreshold}`,
|
|
2700
|
+
`LOGS_DIR="${utils_1.SPEC_DATA_DIR}/logs"`,
|
|
2701
|
+
`META_FILE="${utils_1.SPEC_DATA_DIR}/meta.json"`,
|
|
2702
|
+
'',
|
|
2703
|
+
'# Ensure logs directory exists',
|
|
2704
|
+
'mkdir -p "$LOGS_DIR"',
|
|
2705
|
+
'',
|
|
2706
|
+
'# Read currentCommitID from meta.json',
|
|
2707
|
+
'if [ -f "$META_FILE" ]; then',
|
|
2708
|
+
' CURRENT=$(sed -n \'s/.*"currentCommitID"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p\' "$META_FILE")',
|
|
2709
|
+
'else',
|
|
2710
|
+
' echo "[$(date -Iseconds)] meta.json not found — run \'homegraph spec build\' or \'homegraph spec mine\' first" \\',
|
|
2711
|
+
' >> "$LOGS_DIR"/evolve-hook.log',
|
|
2712
|
+
' exit 0',
|
|
2713
|
+
'fi',
|
|
2714
|
+
'',
|
|
2715
|
+
'# Count pending commits since last evolved commit',
|
|
2716
|
+
'if [ -z "$CURRENT" ] || ! git rev-parse --quiet --verify "$CURRENT^{commit}" >/dev/null 2>&1; then',
|
|
2717
|
+
' # First run or anchor commit no longer exists — trigger immediately',
|
|
2718
|
+
' PENDING=$THRESHOLD',
|
|
2719
|
+
'else',
|
|
2720
|
+
' PENDING=$(git rev-list --count "${CURRENT}..HEAD" 2>/dev/null || echo 0)',
|
|
2721
|
+
'fi',
|
|
2722
|
+
'',
|
|
2723
|
+
'if [ "$PENDING" -lt "$THRESHOLD" ]; then',
|
|
2724
|
+
' echo "[$(date -Iseconds)] Pending: $PENDING/$THRESHOLD — skipping" \\',
|
|
2725
|
+
' >> "$LOGS_DIR"/evolve-hook.log',
|
|
2726
|
+
' exit 0',
|
|
2727
|
+
'fi',
|
|
2728
|
+
'',
|
|
2729
|
+
'# Threshold reached — trigger evolution (async, non-blocking)',
|
|
2515
2730
|
'# Runtime guard: skip if homegraph is not available',
|
|
2516
2731
|
`HOMEGRAPH_BIN="${homegraphBin}"`,
|
|
2517
2732
|
'if [ -x "$HOMEGRAPH_BIN" ] || command -v homegraph >/dev/null 2>&1; then',
|
|
2518
2733
|
' "${HOMEGRAPH_BIN:-homegraph}" spec evolve process --path "$(pwd)" --json \\',
|
|
2519
|
-
` >> $
|
|
2734
|
+
` >> "$LOGS_DIR"/evolve-hook.log 2>&1 &`,
|
|
2520
2735
|
'fi',
|
|
2521
2736
|
MARKER_END,
|
|
2522
2737
|
].join('\n');
|
|
@@ -2551,6 +2766,7 @@ function main() {
|
|
|
2551
2766
|
fs.writeFileSync(hookPath, content);
|
|
2552
2767
|
fs.chmodSync(hookPath, 0o755);
|
|
2553
2768
|
success(`Post-commit hook installed at ${hookPath}`);
|
|
2769
|
+
info(`Threshold: ${commitThreshold} commit(s) — evolution triggers when pending commits reach this count`);
|
|
2554
2770
|
info(`Logs written to ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log`);
|
|
2555
2771
|
}
|
|
2556
2772
|
catch (err) {
|
|
@@ -2568,7 +2784,7 @@ function main() {
|
|
|
2568
2784
|
.action(async (options) => {
|
|
2569
2785
|
try {
|
|
2570
2786
|
const repoPath = resolveSpecProjectPath(options.path);
|
|
2571
|
-
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/
|
|
2787
|
+
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
|
|
2572
2788
|
if (!isGitRepo(repoPath)) {
|
|
2573
2789
|
error(`Not a git repository: ${repoPath}`);
|
|
2574
2790
|
process.exit(1);
|
|
@@ -2636,7 +2852,6 @@ function main() {
|
|
|
2636
2852
|
.command('process')
|
|
2637
2853
|
.description('Process commits through the spec self-evolve pipeline since last evolve')
|
|
2638
2854
|
.option('-p, --path <path>', 'Path to the repository')
|
|
2639
|
-
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2640
2855
|
.option('-j, --json', 'Output as JSON')
|
|
2641
2856
|
.action(async (options) => {
|
|
2642
2857
|
let db;
|
|
@@ -2646,50 +2861,61 @@ function main() {
|
|
|
2646
2861
|
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2647
2862
|
const { initSpecSchema } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
|
|
2648
2863
|
const { loadSpecConfig } = await Promise.resolve().then(() => __importStar(require('../spec/config')));
|
|
2649
|
-
const {
|
|
2650
|
-
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/
|
|
2864
|
+
const { runEvolvePipeline } = await Promise.resolve().then(() => __importStar(require('../spec/evolve/pipeline')));
|
|
2865
|
+
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
|
|
2651
2866
|
if (!isGitRepo(repoPath)) {
|
|
2652
2867
|
error(`Not a git repository: ${repoPath}`);
|
|
2653
2868
|
process.exit(1);
|
|
2654
2869
|
}
|
|
2655
|
-
const dbPath = resolveDbPath(repoPath
|
|
2870
|
+
const dbPath = resolveDbPath(repoPath);
|
|
2656
2871
|
const created = createDatabase(dbPath);
|
|
2657
2872
|
db = created.db;
|
|
2658
2873
|
initSpecSchema(db);
|
|
2659
2874
|
const config = loadSpecConfig(repoPath);
|
|
2660
|
-
const
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
warn('
|
|
2875
|
+
const { resolveAgent } = await Promise.resolve().then(() => __importStar(require('../spec/llm/agents')));
|
|
2876
|
+
const codingAgent = resolveAgent();
|
|
2877
|
+
if (!config.llm && !codingAgent) {
|
|
2878
|
+
warn('No coding agent (Claude Code / Codex / DevEco Code) detected and LLM not configured — phase 3 (LLM-based spec evolution) will be skipped.');
|
|
2879
|
+
warn('Phase 1 (commit-spec graph construction) will still run.');
|
|
2664
2880
|
warn('Configure LLM in .homegraph/commit4spec/configs.json for full functionality:\n' +
|
|
2665
2881
|
'{\n' +
|
|
2666
2882
|
' "llm": {\n' +
|
|
2667
|
-
' "provider": "openai"
|
|
2668
|
-
' "
|
|
2669
|
-
' "
|
|
2883
|
+
' "provider": "openai", // * "openai" or "anthropic"\n' +
|
|
2884
|
+
' "apiKey": "sk-...", // * API key string (plain text)\n' +
|
|
2885
|
+
' "apiKeyEnv": "OPENAI_API_KEY", // or read from env var (takes precedence)\n' +
|
|
2886
|
+
' "model": "gpt-4o", // * model name\n' +
|
|
2887
|
+
' "baseUrl": "https://...", // custom endpoint\n' +
|
|
2888
|
+
' "temperature": 0.2, // creativity control (default: 0.2)\n' +
|
|
2889
|
+
' "maxTokens": 20000 // max output tokens (default: 20000)\n' +
|
|
2670
2890
|
' }\n' +
|
|
2671
2891
|
'}');
|
|
2672
2892
|
}
|
|
2673
|
-
|
|
2893
|
+
else if (!config.llm && codingAgent) {
|
|
2894
|
+
info(`Using ${codingAgent.displayName} (headless) for phase 3 spec evolution — no LLM configuration needed.`);
|
|
2895
|
+
}
|
|
2896
|
+
else if (config.llm && codingAgent) {
|
|
2897
|
+
info(`Using ${codingAgent.displayName} (headless) for phase 3 spec evolution — configured LLM is kept as fallback.`);
|
|
2898
|
+
}
|
|
2899
|
+
const result = await runEvolvePipeline(repoPath, db, config);
|
|
2674
2900
|
if (options.json) {
|
|
2675
2901
|
console.log(JSON.stringify(result, null, 2));
|
|
2676
2902
|
}
|
|
2677
2903
|
else {
|
|
2678
|
-
if (result.
|
|
2904
|
+
if (result.commitsScanned === 0) {
|
|
2679
2905
|
console.log(chalk.green('No new commits to evolve.'));
|
|
2680
2906
|
console.log(`Last evolved commit: ${result.fromCommit ? result.fromCommit.slice(0, 7) : 'none'}`);
|
|
2681
2907
|
console.log(`Current HEAD: ${result.toCommit.slice(0, 7)}`);
|
|
2682
2908
|
}
|
|
2683
2909
|
else {
|
|
2684
|
-
console.log(chalk.bold(`Evolve complete: ${result.
|
|
2685
|
-
console.log(`
|
|
2910
|
+
console.log(chalk.bold(`Evolve complete: ${result.commitsScanned} commit(s) processed`));
|
|
2911
|
+
console.log(`Evolved to: ${result.toCommit.slice(0, 7)}`);
|
|
2686
2912
|
if (result.metaUpdated) {
|
|
2687
2913
|
console.log(chalk.green(`meta.json updated (currentCommitID = ${result.toCommit.slice(0, 7)})`));
|
|
2688
2914
|
}
|
|
2689
|
-
else if (result.
|
|
2690
|
-
console.log(chalk.yellow(`⚠ meta.json NOT updated — ${result.
|
|
2915
|
+
else if (result.phaseOneFailures > 0) {
|
|
2916
|
+
console.log(chalk.yellow(`⚠ meta.json NOT updated — ${result.phaseOneFailures} commit(s) failed`));
|
|
2691
2917
|
}
|
|
2692
|
-
else if (result.
|
|
2918
|
+
else if (result.phaseOneSkipped > 0) {
|
|
2693
2919
|
console.log(chalk.yellow('⚠ meta.json NOT updated — all commits were skipped (no Path A match, no LLM)'));
|
|
2694
2920
|
}
|
|
2695
2921
|
else {
|
|
@@ -2698,34 +2924,31 @@ function main() {
|
|
|
2698
2924
|
// Show per-commit summary
|
|
2699
2925
|
for (const r of result.perCommitResults) {
|
|
2700
2926
|
let prefix;
|
|
2701
|
-
if (r.
|
|
2927
|
+
if (r.phaseOneSkipped) {
|
|
2702
2928
|
prefix = chalk.yellow(' ⚠');
|
|
2703
2929
|
}
|
|
2704
2930
|
else {
|
|
2705
|
-
prefix = r.
|
|
2931
|
+
prefix = r.matched ? chalk.green(' ✓') : chalk.red(' ✗');
|
|
2706
2932
|
}
|
|
2707
2933
|
console.log(`${prefix} ${r.commitHash.slice(0, 7)}`);
|
|
2708
|
-
if (r.
|
|
2709
|
-
console.log(` skipped: ${r.
|
|
2710
|
-
}
|
|
2711
|
-
if (r.isLogicChange) {
|
|
2712
|
-
console.log(` logic change: ${r.logicCheckReason}`);
|
|
2934
|
+
if (r.phaseOneSkipped) {
|
|
2935
|
+
console.log(` skipped: ${r.phaseOneSkipReason}`);
|
|
2713
2936
|
}
|
|
2714
|
-
if (r.
|
|
2715
|
-
console.log(` Path A - GENERATE ${r.
|
|
2937
|
+
if (r.matchedSpecId) {
|
|
2938
|
+
console.log(` Path A - GENERATE ${r.matchedSpecId}`);
|
|
2716
2939
|
}
|
|
2717
2940
|
for (const ev of r.evolvedSpecs) {
|
|
2718
2941
|
console.log(` ${ev.specId}: ${ev.action}`);
|
|
2719
2942
|
}
|
|
2720
2943
|
}
|
|
2721
2944
|
// Aggregate summary
|
|
2722
|
-
const totalFragments = result.perCommitResults.reduce((s, r) => s + r.
|
|
2945
|
+
const totalFragments = result.perCommitResults.reduce((s, r) => s + r.fragmentsInserted, 0);
|
|
2723
2946
|
const totalRelations = result.perCommitResults.reduce((s, r) => s + r.relationsCreated, 0);
|
|
2724
|
-
if (result.
|
|
2725
|
-
console.log(chalk.yellow(`${result.
|
|
2947
|
+
if (result.phaseOneSkipped > 0) {
|
|
2948
|
+
console.log(chalk.yellow(`${result.phaseOneSkipped} commit(s) skipped — no LLM configured, did not match Path A`));
|
|
2726
2949
|
}
|
|
2727
|
-
if (result.
|
|
2728
|
-
console.log(chalk.red(`${result.
|
|
2950
|
+
if (result.phaseOneFailures > 0) {
|
|
2951
|
+
console.log(chalk.red(`${result.phaseOneFailures} commit(s) failed — see details above`));
|
|
2729
2952
|
}
|
|
2730
2953
|
console.log(`Summary: fragments=${totalFragments}, relations=${totalRelations}`);
|
|
2731
2954
|
}
|
|
@@ -2734,7 +2957,7 @@ function main() {
|
|
|
2734
2957
|
catch (err) {
|
|
2735
2958
|
const message = err instanceof Error ? err.message : String(err);
|
|
2736
2959
|
if (message.includes('No meta.json found')) {
|
|
2737
|
-
error("No meta.json found. Run 'homegraph spec mine' first.");
|
|
2960
|
+
error("No meta.json found. Run 'homegraph spec build/mine' first.");
|
|
2738
2961
|
}
|
|
2739
2962
|
else {
|
|
2740
2963
|
error(`Evolve failed: ${message}`);
|