vemora 0.1.0-alpha.8
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 +716 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +589 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ask.d.ts +14 -0
- package/dist/commands/ask.d.ts.map +1 -0
- package/dist/commands/ask.js +136 -0
- package/dist/commands/ask.js.map +1 -0
- package/dist/commands/audit.d.ts +17 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +408 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/brief.d.ts +14 -0
- package/dist/commands/brief.d.ts.map +1 -0
- package/dist/commands/brief.js +73 -0
- package/dist/commands/brief.js.map +1 -0
- package/dist/commands/chat.d.ts +7 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +161 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/context.d.ts +61 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +778 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/deps.d.ts +20 -0
- package/dist/commands/deps.d.ts.map +1 -0
- package/dist/commands/deps.js +138 -0
- package/dist/commands/deps.js.map +1 -0
- package/dist/commands/focus.d.ts +6 -0
- package/dist/commands/focus.d.ts.map +1 -0
- package/dist/commands/focus.js +302 -0
- package/dist/commands/focus.js.map +1 -0
- package/dist/commands/index.d.ts +10 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +366 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init-agent.d.ts +23 -0
- package/dist/commands/init-agent.d.ts.map +1 -0
- package/dist/commands/init-agent.js +447 -0
- package/dist/commands/init-agent.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +122 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/knowledge.d.ts +8 -0
- package/dist/commands/knowledge.d.ts.map +1 -0
- package/dist/commands/knowledge.js +98 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/plan.d.ts +16 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/plan.js +535 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/query.d.ts +39 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +389 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/remember.d.ts +11 -0
- package/dist/commands/remember.d.ts.map +1 -0
- package/dist/commands/remember.js +174 -0
- package/dist/commands/remember.js.map +1 -0
- package/dist/commands/report.d.ts +10 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +180 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +127 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/summarize.d.ts +14 -0
- package/dist/commands/summarize.d.ts.map +1 -0
- package/dist/commands/summarize.js +205 -0
- package/dist/commands/summarize.js.map +1 -0
- package/dist/commands/triage.d.ts +33 -0
- package/dist/commands/triage.d.ts.map +1 -0
- package/dist/commands/triage.js +419 -0
- package/dist/commands/triage.js.map +1 -0
- package/dist/commands/usages.d.ts +14 -0
- package/dist/commands/usages.d.ts.map +1 -0
- package/dist/commands/usages.js +236 -0
- package/dist/commands/usages.js.map +1 -0
- package/dist/core/config.d.ts +35 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +140 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/types.d.ts +251 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -0
- package/dist/embeddings/factory.d.ts +9 -0
- package/dist/embeddings/factory.d.ts.map +1 -0
- package/dist/embeddings/factory.js +26 -0
- package/dist/embeddings/factory.js.map +1 -0
- package/dist/embeddings/noop.d.ts +17 -0
- package/dist/embeddings/noop.d.ts.map +1 -0
- package/dist/embeddings/noop.js +22 -0
- package/dist/embeddings/noop.js.map +1 -0
- package/dist/embeddings/ollama.d.ts +16 -0
- package/dist/embeddings/ollama.d.ts.map +1 -0
- package/dist/embeddings/ollama.js +41 -0
- package/dist/embeddings/ollama.js.map +1 -0
- package/dist/embeddings/openai.d.ts +10 -0
- package/dist/embeddings/openai.d.ts.map +1 -0
- package/dist/embeddings/openai.js +67 -0
- package/dist/embeddings/openai.js.map +1 -0
- package/dist/embeddings/provider.d.ts +19 -0
- package/dist/embeddings/provider.d.ts.map +1 -0
- package/dist/embeddings/provider.js +3 -0
- package/dist/embeddings/provider.js.map +1 -0
- package/dist/indexer/callgraph.d.ts +16 -0
- package/dist/indexer/callgraph.d.ts.map +1 -0
- package/dist/indexer/callgraph.js +154 -0
- package/dist/indexer/callgraph.js.map +1 -0
- package/dist/indexer/chunkBySlidingWindow.d.ts +6 -0
- package/dist/indexer/chunkBySlidingWindow.d.ts.map +1 -0
- package/dist/indexer/chunkBySlidingWindow.js +30 -0
- package/dist/indexer/chunkBySlidingWindow.js.map +1 -0
- package/dist/indexer/chunkBySymbols.d.ts +7 -0
- package/dist/indexer/chunkBySymbols.d.ts.map +1 -0
- package/dist/indexer/chunkBySymbols.js +57 -0
- package/dist/indexer/chunkBySymbols.js.map +1 -0
- package/dist/indexer/chunker.d.ts +15 -0
- package/dist/indexer/chunker.d.ts.map +1 -0
- package/dist/indexer/chunker.js +26 -0
- package/dist/indexer/chunker.js.map +1 -0
- package/dist/indexer/classHeader.d.ts +7 -0
- package/dist/indexer/classHeader.d.ts.map +1 -0
- package/dist/indexer/classHeader.js +37 -0
- package/dist/indexer/classHeader.js.map +1 -0
- package/dist/indexer/deps.d.ts +66 -0
- package/dist/indexer/deps.d.ts.map +1 -0
- package/dist/indexer/deps.js +409 -0
- package/dist/indexer/deps.js.map +1 -0
- package/dist/indexer/hasher.d.ts +17 -0
- package/dist/indexer/hasher.d.ts.map +1 -0
- package/dist/indexer/hasher.js +38 -0
- package/dist/indexer/hasher.js.map +1 -0
- package/dist/indexer/parser.d.ts +18 -0
- package/dist/indexer/parser.d.ts.map +1 -0
- package/dist/indexer/parser.js +355 -0
- package/dist/indexer/parser.js.map +1 -0
- package/dist/indexer/scanner.d.ts +18 -0
- package/dist/indexer/scanner.d.ts.map +1 -0
- package/dist/indexer/scanner.js +37 -0
- package/dist/indexer/scanner.js.map +1 -0
- package/dist/indexer/strategy.d.ts +11 -0
- package/dist/indexer/strategy.d.ts.map +1 -0
- package/dist/indexer/strategy.js +15 -0
- package/dist/indexer/strategy.js.map +1 -0
- package/dist/indexer/tests.d.ts +15 -0
- package/dist/indexer/tests.d.ts.map +1 -0
- package/dist/indexer/tests.js +68 -0
- package/dist/indexer/tests.js.map +1 -0
- package/dist/indexer/todos.d.ts +9 -0
- package/dist/indexer/todos.d.ts.map +1 -0
- package/dist/indexer/todos.js +29 -0
- package/dist/indexer/todos.js.map +1 -0
- package/dist/llm/anthropic.d.ts +8 -0
- package/dist/llm/anthropic.d.ts.map +1 -0
- package/dist/llm/anthropic.js +76 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/factory.d.ts +7 -0
- package/dist/llm/factory.d.ts.map +1 -0
- package/dist/llm/factory.js +39 -0
- package/dist/llm/factory.js.map +1 -0
- package/dist/llm/ollama.d.ts +8 -0
- package/dist/llm/ollama.d.ts.map +1 -0
- package/dist/llm/ollama.js +83 -0
- package/dist/llm/ollama.js.map +1 -0
- package/dist/llm/openai.d.ts +8 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +68 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/llm/provider.d.ts +29 -0
- package/dist/llm/provider.d.ts.map +1 -0
- package/dist/llm/provider.js +3 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/search/bm25.d.ts +3 -0
- package/dist/search/bm25.d.ts.map +1 -0
- package/dist/search/bm25.js +102 -0
- package/dist/search/bm25.js.map +1 -0
- package/dist/search/formatter.d.ts +43 -0
- package/dist/search/formatter.d.ts.map +1 -0
- package/dist/search/formatter.js +208 -0
- package/dist/search/formatter.js.map +1 -0
- package/dist/search/hybrid.d.ts +10 -0
- package/dist/search/hybrid.d.ts.map +1 -0
- package/dist/search/hybrid.js +53 -0
- package/dist/search/hybrid.js.map +1 -0
- package/dist/search/merge.d.ts +33 -0
- package/dist/search/merge.d.ts.map +1 -0
- package/dist/search/merge.js +158 -0
- package/dist/search/merge.js.map +1 -0
- package/dist/search/mmr.d.ts +23 -0
- package/dist/search/mmr.d.ts.map +1 -0
- package/dist/search/mmr.js +95 -0
- package/dist/search/mmr.js.map +1 -0
- package/dist/search/rerank.d.ts +15 -0
- package/dist/search/rerank.d.ts.map +1 -0
- package/dist/search/rerank.js +76 -0
- package/dist/search/rerank.js.map +1 -0
- package/dist/search/signature.d.ts +42 -0
- package/dist/search/signature.d.ts.map +1 -0
- package/dist/search/signature.js +112 -0
- package/dist/search/signature.js.map +1 -0
- package/dist/search/vector.d.ts +41 -0
- package/dist/search/vector.d.ts.map +1 -0
- package/dist/search/vector.js +185 -0
- package/dist/search/vector.js.map +1 -0
- package/dist/storage/cache.d.ts +30 -0
- package/dist/storage/cache.d.ts.map +1 -0
- package/dist/storage/cache.js +160 -0
- package/dist/storage/cache.js.map +1 -0
- package/dist/storage/knowledge.d.ts +17 -0
- package/dist/storage/knowledge.d.ts.map +1 -0
- package/dist/storage/knowledge.js +58 -0
- package/dist/storage/knowledge.js.map +1 -0
- package/dist/storage/repository.d.ts +27 -0
- package/dist/storage/repository.d.ts.map +1 -0
- package/dist/storage/repository.js +95 -0
- package/dist/storage/repository.js.map +1 -0
- package/dist/storage/session.d.ts +38 -0
- package/dist/storage/session.d.ts.map +1 -0
- package/dist/storage/session.js +100 -0
- package/dist/storage/session.js.map +1 -0
- package/dist/storage/summaries.d.ts +19 -0
- package/dist/storage/summaries.d.ts.map +1 -0
- package/dist/storage/summaries.js +66 -0
- package/dist/storage/summaries.js.map +1 -0
- package/dist/storage/usage.d.ts +35 -0
- package/dist/storage/usage.d.ts.map +1 -0
- package/dist/storage/usage.js +55 -0
- package/dist/storage/usage.js.map +1 -0
- package/dist/utils/git.d.ts +15 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +38 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/tokenizer.d.ts +24 -0
- package/dist/utils/tokenizer.d.ts.map +1 -0
- package/dist/utils/tokenizer.js +52 -0
- package/dist/utils/tokenizer.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runKnowledgeList = runKnowledgeList;
|
|
7
|
+
exports.runKnowledgeForget = runKnowledgeForget;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const config_1 = require("../core/config");
|
|
10
|
+
const knowledge_1 = require("../storage/knowledge");
|
|
11
|
+
async function runKnowledgeList(rootDir, options = {}) {
|
|
12
|
+
(0, config_1.loadConfig)(rootDir);
|
|
13
|
+
const storage = new knowledge_1.KnowledgeStorage(rootDir);
|
|
14
|
+
let entries = storage.load();
|
|
15
|
+
if (entries.length === 0) {
|
|
16
|
+
console.log(chalk_1.default.gray('No knowledge entries found. Use `vemora remember "<text>"` to add one.'));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Filter
|
|
20
|
+
if (options.category) {
|
|
21
|
+
entries = entries.filter((e) => e.category === options.category);
|
|
22
|
+
}
|
|
23
|
+
if (options.file) {
|
|
24
|
+
entries = entries.filter((e) => e.relatedFiles?.some((f) => f.includes(options.file)));
|
|
25
|
+
}
|
|
26
|
+
if (options.symbol) {
|
|
27
|
+
entries = entries.filter((e) => e.relatedSymbols?.includes(options.symbol));
|
|
28
|
+
}
|
|
29
|
+
if (entries.length === 0) {
|
|
30
|
+
console.log(chalk_1.default.gray("No entries match the given filters."));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Group by category
|
|
34
|
+
const order = [
|
|
35
|
+
"gotcha",
|
|
36
|
+
"pattern",
|
|
37
|
+
"decision",
|
|
38
|
+
"glossary",
|
|
39
|
+
];
|
|
40
|
+
const grouped = new Map();
|
|
41
|
+
for (const cat of order)
|
|
42
|
+
grouped.set(cat, []);
|
|
43
|
+
for (const e of entries) {
|
|
44
|
+
grouped.get(e.category)?.push(e) ?? grouped.set(e.category, [e]);
|
|
45
|
+
}
|
|
46
|
+
const categoryColor = {
|
|
47
|
+
gotcha: chalk_1.default.red,
|
|
48
|
+
pattern: chalk_1.default.cyan,
|
|
49
|
+
decision: chalk_1.default.yellow,
|
|
50
|
+
glossary: chalk_1.default.gray,
|
|
51
|
+
};
|
|
52
|
+
for (const [cat, group] of grouped) {
|
|
53
|
+
if (group.length === 0)
|
|
54
|
+
continue;
|
|
55
|
+
const colorFn = categoryColor[cat] ?? chalk_1.default.white;
|
|
56
|
+
console.log();
|
|
57
|
+
console.log(colorFn(chalk_1.default.bold(`โธ ${cat.toUpperCase()}`)));
|
|
58
|
+
for (const e of group) {
|
|
59
|
+
const shortId = e.id.slice(0, 8);
|
|
60
|
+
const conf = e.confidence === "high"
|
|
61
|
+
? chalk_1.default.green("โ")
|
|
62
|
+
: e.confidence === "medium"
|
|
63
|
+
? chalk_1.default.yellow("โ")
|
|
64
|
+
: chalk_1.default.gray("โ");
|
|
65
|
+
console.log(` ${conf} ${chalk_1.default.bold(e.title)} ${chalk_1.default.gray(`[${shortId}]`)}`);
|
|
66
|
+
if (e.body !== e.title) {
|
|
67
|
+
const preview = e.body.length > 120 ? `${e.body.slice(0, 120)}โฆ` : e.body;
|
|
68
|
+
console.log(` ${chalk_1.default.gray(preview)}`);
|
|
69
|
+
}
|
|
70
|
+
if (e.relatedFiles?.length) {
|
|
71
|
+
console.log(` ${chalk_1.default.gray("files:")} ${e.relatedFiles.join(", ")}`);
|
|
72
|
+
}
|
|
73
|
+
if (e.relatedSymbols?.length) {
|
|
74
|
+
console.log(` ${chalk_1.default.gray("symbols:")} ${e.relatedSymbols.join(", ")}`);
|
|
75
|
+
}
|
|
76
|
+
if (e.supersedes) {
|
|
77
|
+
console.log(` ${chalk_1.default.gray(`supersedes: ${e.supersedes.slice(0, 8)}`)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
console.log();
|
|
82
|
+
console.log(chalk_1.default.gray(`Total: ${entries.length} entr${entries.length === 1 ? "y" : "ies"}`));
|
|
83
|
+
}
|
|
84
|
+
// โโโ Forget โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
85
|
+
async function runKnowledgeForget(rootDir, id) {
|
|
86
|
+
(0, config_1.loadConfig)(rootDir);
|
|
87
|
+
const storage = new knowledge_1.KnowledgeStorage(rootDir);
|
|
88
|
+
// Support short ID prefix (first 8 chars)
|
|
89
|
+
const entries = storage.load();
|
|
90
|
+
const match = entries.find((e) => e.id === id || e.id.startsWith(id));
|
|
91
|
+
if (!match) {
|
|
92
|
+
console.error(chalk_1.default.red(`Error: no entry found with ID starting with "${id}".`));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
storage.remove(match.id);
|
|
96
|
+
console.log(chalk_1.default.green(`โ Removed entry [${match.id.slice(0, 8)}] "${match.title}".`));
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=knowledge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge.js","sourceRoot":"","sources":["../../src/commands/knowledge.ts"],"names":[],"mappings":";;;;;AAaA,4CAqGC;AAID,gDAuBC;AA7ID,kDAA0B;AAC1B,2CAA4C;AAE5C,oDAAwD;AAUjD,KAAK,UAAU,gBAAgB,CACpC,OAAe,EACf,UAAgC,EAAE;IAElC,IAAA,mBAAU,EAAC,OAAO,CAAC,CAAC;IAEpB,MAAM,OAAO,GAAG,IAAI,4BAAgB,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAE7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CACT,eAAK,CAAC,IAAI,CACR,wEAAwE,CACzE,CACF,CAAC;QACF,OAAO;IACT,CAAC;IAED,SAAS;IACT,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7B,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAK,CAAC,CAAC,CACvD,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7B,CAAC,CAAC,cAAc,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAO,CAAC,CAC5C,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC,CAAC;QAC/D,OAAO;IACT,CAAC;IAED,oBAAoB;IACpB,MAAM,KAAK,GAAiC;QAC1C,QAAQ;QACR,SAAS;QACT,UAAU;QACV,UAAU;KACX,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IACpD,KAAK,MAAM,GAAG,IAAI,KAAK;QAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,aAAa,GAA0C;QAC3D,MAAM,EAAE,eAAK,CAAC,GAAG;QACjB,OAAO,EAAE,eAAK,CAAC,IAAI;QACnB,QAAQ,EAAE,eAAK,CAAC,MAAM;QACtB,QAAQ,EAAE,eAAK,CAAC,IAAI;KACrB,CAAC;IAEF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACjC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,eAAK,CAAC,KAAK,CAAC;QAClD,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,eAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAE3D,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACjC,MAAM,IAAI,GACR,CAAC,CAAC,UAAU,KAAK,MAAM;gBACrB,CAAC,CAAC,eAAK,CAAC,KAAK,CAAC,GAAG,CAAC;gBAClB,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,QAAQ;oBACzB,CAAC,CAAC,eAAK,CAAC,MAAM,CAAC,GAAG,CAAC;oBACnB,CAAC,CAAC,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxB,OAAO,CAAC,GAAG,CACT,KAAK,IAAI,IAAI,eAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,eAAK,CAAC,IAAI,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAClE,CAAC;YACF,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;gBACvB,MAAM,OAAO,GACX,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC5D,OAAO,CAAC,GAAG,CAAC,OAAO,eAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5C,CAAC;YACD,IAAI,CAAC,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,OAAO,eAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC1E,CAAC;YACD,IAAI,CAAC,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;gBAC7B,OAAO,CAAC,GAAG,CACT,OAAO,eAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC/D,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CACT,OAAO,eAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAC/D,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CACT,eAAK,CAAC,IAAI,CACR,UAAU,OAAO,CAAC,MAAM,QAAQ,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CACrE,CACF,CAAC;AACJ,CAAC;AAED,iFAAiF;AAE1E,KAAK,UAAU,kBAAkB,CACtC,OAAe,EACf,EAAU;IAEV,IAAA,mBAAU,EAAC,OAAO,CAAC,CAAC;IAEpB,MAAM,OAAO,GAAG,IAAI,4BAAgB,CAAC,OAAO,CAAC,CAAC;IAE9C,0CAA0C;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IAEtE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CACX,eAAK,CAAC,GAAG,CAAC,gDAAgD,EAAE,IAAI,CAAC,CAClE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACzB,OAAO,CAAC,GAAG,CACT,eAAK,CAAC,KAAK,CAAC,oBAAoB,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,KAAK,IAAI,CAAC,CAC3E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface PlanOptions {
|
|
2
|
+
/** Number of chunks to retrieve per step when falling back to search (default: 5) */
|
|
3
|
+
topK?: number;
|
|
4
|
+
/** Force keyword/BM25 search, skip embeddings */
|
|
5
|
+
keyword?: boolean;
|
|
6
|
+
/** Max tokens per step context (default: 4000) */
|
|
7
|
+
budget?: number;
|
|
8
|
+
/** Print retrieved context for each step */
|
|
9
|
+
showContext?: boolean;
|
|
10
|
+
/** Show the plan and ask for confirmation before executing */
|
|
11
|
+
confirm?: boolean;
|
|
12
|
+
/** After all steps, call the planner again to synthesize a final answer */
|
|
13
|
+
synthesize?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function runPlan(rootDir: string, task: string, options?: PlanOptions): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW,WAAW;IAC1B,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAmaD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAsYf"}
|
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runPlan = runPlan;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const crypto_1 = require("crypto");
|
|
10
|
+
const ora_1 = __importDefault(require("ora"));
|
|
11
|
+
const readline_1 = __importDefault(require("readline"));
|
|
12
|
+
const util_1 = require("util");
|
|
13
|
+
const config_1 = require("../core/config");
|
|
14
|
+
const factory_1 = require("../embeddings/factory");
|
|
15
|
+
const factory_2 = require("../llm/factory");
|
|
16
|
+
const bm25_1 = require("../search/bm25");
|
|
17
|
+
const vector_1 = require("../search/vector");
|
|
18
|
+
const cache_1 = require("../storage/cache");
|
|
19
|
+
const knowledge_1 = require("../storage/knowledge");
|
|
20
|
+
const repository_1 = require("../storage/repository");
|
|
21
|
+
const summaries_1 = require("../storage/summaries");
|
|
22
|
+
const tokenizer_1 = require("../utils/tokenizer");
|
|
23
|
+
const context_1 = require("./context");
|
|
24
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
25
|
+
// โโโ Prompts โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
26
|
+
const PLANNER_SYSTEM_PROMPT = `You are an expert software architect.
|
|
27
|
+
Decompose a complex codebase task into a concrete step-by-step plan for a smaller model to execute.
|
|
28
|
+
|
|
29
|
+
Available step actions:
|
|
30
|
+
read โ pull code into context with no LLM call (free; use to share files across steps)
|
|
31
|
+
analyze โ executor answers a question in prose (default)
|
|
32
|
+
write โ executor produces a unified diff (use for code changes)
|
|
33
|
+
test โ run a shell command and capture its output (use to verify changes)
|
|
34
|
+
|
|
35
|
+
Guidelines:
|
|
36
|
+
- Each step must be atomic and focused on a single concern.
|
|
37
|
+
- Prefer "files" and "symbols" over "query" โ direct retrieval is faster and cheaper.
|
|
38
|
+
- Use "dependsOn" when a step needs the output of a prior step.
|
|
39
|
+
- Use action:"read" to share a file's content with later write/analyze steps.
|
|
40
|
+
- Use action:"write" for code changes; the executor will produce a unified diff.
|
|
41
|
+
- Use action:"test" with a "command" field to verify changes.
|
|
42
|
+
- If unsure of exact file paths, use "query" as fallback.
|
|
43
|
+
- Keep steps โค 7 unless truly necessary.
|
|
44
|
+
|
|
45
|
+
Return ONLY valid JSON โ no markdown fences, no explanation:
|
|
46
|
+
{
|
|
47
|
+
"goal": "<one-line summary>",
|
|
48
|
+
"steps": [
|
|
49
|
+
{
|
|
50
|
+
"id": 1,
|
|
51
|
+
"action": "read|analyze|write|test",
|
|
52
|
+
"goal": "<what this step determines or produces>",
|
|
53
|
+
"instruction": "<precise instruction for the executor>",
|
|
54
|
+
"files": ["src/path/to/file.ts"],
|
|
55
|
+
"symbols": ["SymbolName"],
|
|
56
|
+
"query": "<fallback search query>",
|
|
57
|
+
"dependsOn": [],
|
|
58
|
+
"command": "<shell command, only for action:test>"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}`;
|
|
62
|
+
const EXECUTOR_ANALYZE_PROMPT = "You are an expert software engineer. " +
|
|
63
|
+
"Answer the specific question using ONLY the provided code context. " +
|
|
64
|
+
"Be concise and technical. Reference file paths and symbol names when relevant.\n" +
|
|
65
|
+
"If context is insufficient, start your response with INSUFFICIENT: and describe what is missing.";
|
|
66
|
+
const EXECUTOR_WRITE_PROMPT = "You are an expert software engineer. " +
|
|
67
|
+
"Produce the code changes requested as a unified diff.\n\n" +
|
|
68
|
+
"Rules:\n" +
|
|
69
|
+
"- Output ONLY the diff, no explanation before or after.\n" +
|
|
70
|
+
"- Use standard unified diff format: --- a/path and +++ b/path headers.\n" +
|
|
71
|
+
"- Include 3 lines of context around each change.\n" +
|
|
72
|
+
"- If context is insufficient to produce a correct diff, start with INSUFFICIENT: and describe what is missing.";
|
|
73
|
+
const SYNTHESIZER_SYSTEM_PROMPT = "You are an expert software engineer. " +
|
|
74
|
+
"Synthesize the results from multiple analysis steps into a single, coherent, well-structured answer. " +
|
|
75
|
+
"Avoid repeating information. Be concise and precise.";
|
|
76
|
+
const REPLAN_SYSTEM_PROMPT = "You are an expert software architect. " +
|
|
77
|
+
"One or more steps in a plan failed because the executor had insufficient context. " +
|
|
78
|
+
"Provide 1-3 additional steps to recover the missing information.\n\n" +
|
|
79
|
+
"Return ONLY valid JSON โ no markdown fences:\n" +
|
|
80
|
+
'{ "steps": [{ "id": <next_id>, "action": "read|analyze", "goal": "...", "instruction": "...", "files": [], "symbols": [], "query": "...", "dependsOn": [<failed_step_id>] }] }';
|
|
81
|
+
// โโโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
82
|
+
/** Topological sort into waves โ each wave can run in parallel. */
|
|
83
|
+
function buildExecutionWaves(steps) {
|
|
84
|
+
const waves = [];
|
|
85
|
+
const resolved = new Set();
|
|
86
|
+
let remaining = [...steps];
|
|
87
|
+
while (remaining.length > 0) {
|
|
88
|
+
const wave = remaining.filter((s) => (s.dependsOn ?? []).every((id) => resolved.has(id)));
|
|
89
|
+
if (wave.length === 0)
|
|
90
|
+
break; // circular dependency guard
|
|
91
|
+
for (const s of wave)
|
|
92
|
+
resolved.add(s.id);
|
|
93
|
+
remaining = remaining.filter((s) => !wave.some((w) => w.id === s.id));
|
|
94
|
+
waves.push(wave);
|
|
95
|
+
}
|
|
96
|
+
return waves;
|
|
97
|
+
}
|
|
98
|
+
/** Cache key for context deduplication within a session. */
|
|
99
|
+
function contextCacheKey(step) {
|
|
100
|
+
return JSON.stringify({
|
|
101
|
+
f: [...(step.files ?? [])].sort(),
|
|
102
|
+
s: [...(step.symbols ?? [])].sort(),
|
|
103
|
+
q: step.query,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** Prompt the user for y/n confirmation. */
|
|
107
|
+
async function askConfirm(question) {
|
|
108
|
+
return new Promise((resolve) => {
|
|
109
|
+
const rl = readline_1.default.createInterface({
|
|
110
|
+
input: process.stdin,
|
|
111
|
+
output: process.stdout,
|
|
112
|
+
});
|
|
113
|
+
rl.question(question, (answer) => {
|
|
114
|
+
rl.close();
|
|
115
|
+
const a = answer.trim().toLowerCase();
|
|
116
|
+
resolve(a === "y" || a === "yes" || a === "");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/** Display the plan in a human-readable table before execution. */
|
|
121
|
+
function displayPlan(plan, steps) {
|
|
122
|
+
const actionColor = {
|
|
123
|
+
read: chalk_1.default.blue,
|
|
124
|
+
analyze: chalk_1.default.gray,
|
|
125
|
+
write: chalk_1.default.green,
|
|
126
|
+
test: chalk_1.default.magenta,
|
|
127
|
+
};
|
|
128
|
+
console.log(chalk_1.default.bold(`\nGoal: ${plan.goal}\n`));
|
|
129
|
+
for (const step of steps) {
|
|
130
|
+
const action = step.action ?? "analyze";
|
|
131
|
+
const badge = (actionColor[action] ?? chalk_1.default.gray)(`[${action}]`);
|
|
132
|
+
const targets = (step.files?.length ?? 0) + (step.symbols?.length ?? 0) > 0
|
|
133
|
+
? chalk_1.default.gray(` โ ${[...(step.files ?? []), ...(step.symbols ?? [])].join(", ")}`)
|
|
134
|
+
: step.query
|
|
135
|
+
? chalk_1.default.gray(` โ search: "${step.query}"`)
|
|
136
|
+
: "";
|
|
137
|
+
const deps = step.dependsOn?.length
|
|
138
|
+
? chalk_1.default.gray(` (depends on: ${step.dependsOn.join(", ")})`)
|
|
139
|
+
: "";
|
|
140
|
+
console.log(` ${chalk_1.default.bold.yellow(String(step.id).padStart(2))} ${badge} ${chalk_1.default.bold(step.goal)}${targets}${deps}`);
|
|
141
|
+
if (action === "test" && step.command) {
|
|
142
|
+
console.log(chalk_1.default.gray(` $ ${step.command}`));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
console.log();
|
|
146
|
+
}
|
|
147
|
+
/** Build the cheap planner context from summaries + symbol list (no raw code). */
|
|
148
|
+
function buildPlannerContext(projectName, projectOverview, fileSummaries, symbols) {
|
|
149
|
+
const lines = [`# Project: ${projectName}\n`];
|
|
150
|
+
if (projectOverview) {
|
|
151
|
+
lines.push("## Overview\n", projectOverview, "");
|
|
152
|
+
}
|
|
153
|
+
const fileEntries = Object.entries(fileSummaries);
|
|
154
|
+
if (fileEntries.length > 0) {
|
|
155
|
+
lines.push(`## Files (${fileEntries.length})\n`);
|
|
156
|
+
for (const [file, entry] of fileEntries) {
|
|
157
|
+
lines.push(`${file} โ ${entry.summary}`);
|
|
158
|
+
}
|
|
159
|
+
lines.push("");
|
|
160
|
+
}
|
|
161
|
+
const symbolEntries = Object.entries(symbols);
|
|
162
|
+
if (symbolEntries.length > 0) {
|
|
163
|
+
lines.push(`## Symbols (${symbolEntries.length})\n`);
|
|
164
|
+
for (const [name, entry] of symbolEntries) {
|
|
165
|
+
lines.push(`${name} (${entry.type}) โ ${entry.file}:${entry.startLine}`);
|
|
166
|
+
}
|
|
167
|
+
lines.push("");
|
|
168
|
+
}
|
|
169
|
+
return lines.join("\n");
|
|
170
|
+
}
|
|
171
|
+
/** Retrieve and format code context for an executor step, with session-level caching. */
|
|
172
|
+
async function retrieveStepContext(step, config, data, cacheStorage, rootDir, topK, budget, forceKeyword, contextCache) {
|
|
173
|
+
const cacheKey = contextCacheKey(step);
|
|
174
|
+
const cached = contextCache.get(cacheKey);
|
|
175
|
+
if (cached)
|
|
176
|
+
return cached;
|
|
177
|
+
const { chunks, symbols, depGraph, callGraph, fileSummaries, projectOverview, knowledgeEntries } = data;
|
|
178
|
+
let results = [];
|
|
179
|
+
const hasTargets = (step.files?.length ?? 0) + (step.symbols?.length ?? 0) > 0;
|
|
180
|
+
if (hasTargets) {
|
|
181
|
+
const fileSet = new Set(step.files ?? []);
|
|
182
|
+
const symbolSet = new Set(step.symbols ?? []);
|
|
183
|
+
const targeted = chunks.filter((c) => (fileSet.size > 0 && fileSet.has(c.file)) ||
|
|
184
|
+
(symbolSet.size > 0 && c.symbol !== undefined && symbolSet.has(c.symbol)));
|
|
185
|
+
results = targeted.map((chunk) => ({
|
|
186
|
+
chunk,
|
|
187
|
+
score: 1,
|
|
188
|
+
symbol: chunk.symbol ? symbols[chunk.symbol] : undefined,
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
if (results.length === 0) {
|
|
192
|
+
if (forceKeyword) {
|
|
193
|
+
results = (0, bm25_1.computeBM25Scores)(step.query, chunks, symbols, topK);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
try {
|
|
197
|
+
const cache = cacheStorage.load();
|
|
198
|
+
const cachedCount = cache
|
|
199
|
+
? (cache.chunkIds?.length ?? Object.keys(cache.embeddings ?? {}).length)
|
|
200
|
+
: 0;
|
|
201
|
+
if (!cache || cachedCount === 0) {
|
|
202
|
+
results = (0, bm25_1.computeBM25Scores)(step.query, chunks, symbols, topK);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const provider = (0, factory_1.createEmbeddingProvider)(config.embedding);
|
|
206
|
+
const [queryEmbedding] = await provider.embed([step.query]);
|
|
207
|
+
results = (0, vector_1.vectorSearch)(queryEmbedding, chunks, cache, symbols, topK);
|
|
208
|
+
if (results.length === 0) {
|
|
209
|
+
results = (0, bm25_1.computeBM25Scores)(step.query, chunks, symbols, topK);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
results = (0, bm25_1.computeBM25Scores)(step.query, chunks, symbols, topK);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
results = (0, tokenizer_1.applyTokenBudget)(results, budget);
|
|
219
|
+
const contextFormat = config.display?.format === "terse" ? "terse" : "plain";
|
|
220
|
+
const contextStr = (0, context_1.generateContextString)(config, results, depGraph, callGraph, fileSummaries, projectOverview, { query: step.query, format: contextFormat }, rootDir, chunks, knowledgeEntries);
|
|
221
|
+
contextCache.set(cacheKey, contextStr);
|
|
222
|
+
return contextStr;
|
|
223
|
+
}
|
|
224
|
+
/** Execute a single step, returning its answer and whether context was insufficient. */
|
|
225
|
+
async function executeStep(step, config, data, cacheStorage, rootDir, executorConfig, executor, topK, budget, forceKeyword, stepResults, contextCache, showContext) {
|
|
226
|
+
const action = step.action ?? "analyze";
|
|
227
|
+
// โโ action: test โ run shell command, no LLM โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
228
|
+
if (action === "test") {
|
|
229
|
+
if (!step.command) {
|
|
230
|
+
return { stepId: step.id, answer: "No command specified for test step.", insufficient: false };
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const { stdout, stderr } = await execAsync(step.command, {
|
|
234
|
+
cwd: rootDir,
|
|
235
|
+
timeout: 60_000,
|
|
236
|
+
});
|
|
237
|
+
const answer = `$ ${step.command}\n\n` +
|
|
238
|
+
(stdout ? `stdout:\n${stdout}` : "") +
|
|
239
|
+
(stderr ? `\nstderr:\n${stderr}` : "");
|
|
240
|
+
return { stepId: step.id, answer, insufficient: false };
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
const e = err;
|
|
244
|
+
const answer = `$ ${step.command} โ FAILED\n\n` +
|
|
245
|
+
(e.stdout ? `stdout:\n${e.stdout}\n` : "") +
|
|
246
|
+
(e.stderr ? `stderr:\n${e.stderr}\n` : "") +
|
|
247
|
+
(e.message ? `error: ${e.message}` : "");
|
|
248
|
+
return { stepId: step.id, answer, insufficient: false };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// โโ retrieve context (shared cache) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
252
|
+
const contextStr = await retrieveStepContext(step, config, data, cacheStorage, rootDir, topK, budget, forceKeyword, contextCache);
|
|
253
|
+
if (showContext) {
|
|
254
|
+
console.log(chalk_1.default.gray(" โโ context โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ"));
|
|
255
|
+
console.log(chalk_1.default.gray(contextStr));
|
|
256
|
+
console.log(chalk_1.default.gray(" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n"));
|
|
257
|
+
}
|
|
258
|
+
// โโ action: read โ return context as-is, no LLM call โโโโโโโโโโโโโโโโโโโโโ
|
|
259
|
+
if (action === "read") {
|
|
260
|
+
return { stepId: step.id, answer: contextStr, insufficient: false };
|
|
261
|
+
}
|
|
262
|
+
// โโ inject dependsOn results โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
263
|
+
let dependencySection = "";
|
|
264
|
+
if (step.dependsOn?.length) {
|
|
265
|
+
const parts = [];
|
|
266
|
+
for (const depId of step.dependsOn) {
|
|
267
|
+
const depResult = stepResults.get(depId);
|
|
268
|
+
if (depResult)
|
|
269
|
+
parts.push(`### Step ${depId}\n${depResult}`);
|
|
270
|
+
}
|
|
271
|
+
if (parts.length > 0) {
|
|
272
|
+
dependencySection = `\n\n## Prior step results\n${parts.join("\n\n")}`;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// โโ action: analyze | write โ call executor LLM โโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
276
|
+
const systemPrompt = action === "write" ? EXECUTOR_WRITE_PROMPT : EXECUTOR_ANALYZE_PROMPT;
|
|
277
|
+
const response = await executor.chat([
|
|
278
|
+
{ role: "system", content: `${systemPrompt}\n\n${contextStr}` },
|
|
279
|
+
{
|
|
280
|
+
role: "user",
|
|
281
|
+
content: `Goal: ${step.goal}\n\nInstruction: ${step.instruction}${dependencySection}`,
|
|
282
|
+
},
|
|
283
|
+
], {
|
|
284
|
+
model: executorConfig.model,
|
|
285
|
+
temperature: action === "write" ? 0.1 : 0.3,
|
|
286
|
+
});
|
|
287
|
+
const answer = response.content;
|
|
288
|
+
const insufficient = answer.trimStart().startsWith("INSUFFICIENT:");
|
|
289
|
+
return { stepId: step.id, answer, insufficient };
|
|
290
|
+
}
|
|
291
|
+
// โโโ Main command โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
292
|
+
async function runPlan(rootDir, task, options = {}) {
|
|
293
|
+
const config = (0, config_1.loadConfig)(rootDir);
|
|
294
|
+
if (!config.summarization) {
|
|
295
|
+
console.error(chalk_1.default.red('No executor LLM configured. Add a "summarization" block to .vemora/config.json.'));
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
// config.summarization is guaranteed non-undefined here (checked above)
|
|
299
|
+
const executorConfig = config.summarization;
|
|
300
|
+
const plannerConfig = config.planner ?? executorConfig;
|
|
301
|
+
const topK = options.topK ?? 5;
|
|
302
|
+
const budget = options.budget ?? 4000;
|
|
303
|
+
const forceKeyword = options.keyword || config.embedding.provider === "none";
|
|
304
|
+
// โโ Load index data once โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
305
|
+
const repo = new repository_1.RepositoryStorage(rootDir);
|
|
306
|
+
const cacheStorage = new cache_1.EmbeddingCacheStorage(config.projectId);
|
|
307
|
+
const summaryStorage = new summaries_1.SummaryStorage(rootDir);
|
|
308
|
+
const chunks = repo.loadChunks();
|
|
309
|
+
if (chunks.length === 0) {
|
|
310
|
+
console.error(chalk_1.default.red("No index found. Run `vemora index` first."));
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
const data = {
|
|
314
|
+
chunks,
|
|
315
|
+
symbols: repo.loadSymbols(),
|
|
316
|
+
depGraph: repo.loadDeps(),
|
|
317
|
+
callGraph: repo.loadCallGraph(),
|
|
318
|
+
fileSummaries: summaryStorage.hasFileSummaries()
|
|
319
|
+
? summaryStorage.loadFileSummaries()
|
|
320
|
+
: {},
|
|
321
|
+
projectOverview: summaryStorage.loadProjectSummary()?.overview ?? null,
|
|
322
|
+
knowledgeEntries: new knowledge_1.KnowledgeStorage(rootDir).load(),
|
|
323
|
+
};
|
|
324
|
+
const planner = (0, factory_2.createLLMProvider)(plannerConfig);
|
|
325
|
+
const executor = (0, factory_2.createLLMProvider)(executorConfig);
|
|
326
|
+
const sameLLM = plannerConfig.provider === executorConfig.provider &&
|
|
327
|
+
plannerConfig.model === executorConfig.model;
|
|
328
|
+
console.log(chalk_1.default.bold.cyan("\n[vemora plan]"));
|
|
329
|
+
console.log(chalk_1.default.gray(` Planner: ${planner.name} ยท ${plannerConfig.model}${sameLLM ? " (also executor)" : ""}`));
|
|
330
|
+
if (!sameLLM) {
|
|
331
|
+
console.log(chalk_1.default.gray(` Executor: ${executor.name} ยท ${executorConfig.model}`));
|
|
332
|
+
}
|
|
333
|
+
const hasSummaries = data.projectOverview !== null || Object.keys(data.fileSummaries).length > 0;
|
|
334
|
+
if (!hasSummaries) {
|
|
335
|
+
console.log(chalk_1.default.yellow(" Tip: run `vemora summarize` for cheaper planning (summaries replace raw code)."));
|
|
336
|
+
}
|
|
337
|
+
console.log();
|
|
338
|
+
// โโ Phase 1: build planner context (summaries + symbols, no raw code) โโโโโโ
|
|
339
|
+
const plannerSpinner = (0, ora_1.default)("Planning...").start();
|
|
340
|
+
const plannerContext = hasSummaries
|
|
341
|
+
? buildPlannerContext(config.projectName, data.projectOverview, data.fileSummaries, data.symbols)
|
|
342
|
+
: (0, context_1.generateContextString)(config, (0, tokenizer_1.applyTokenBudget)((0, bm25_1.computeBM25Scores)(task, chunks, data.symbols, topK), budget), data.depGraph, data.callGraph, data.fileSummaries, data.projectOverview, { query: task, format: "terse" }, rootDir, chunks, data.knowledgeEntries);
|
|
343
|
+
// โโ Phase 2: call planner โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
344
|
+
// definite assignment: process.exit(1) in catch guarantees assignment
|
|
345
|
+
let plan;
|
|
346
|
+
try {
|
|
347
|
+
const plannerResponse = await planner.chat([
|
|
348
|
+
{
|
|
349
|
+
role: "system",
|
|
350
|
+
content: `${PLANNER_SYSTEM_PROMPT}\n\n${plannerContext}`,
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
role: "user",
|
|
354
|
+
content: `Decompose this task into concrete steps:\n\n${task}`,
|
|
355
|
+
},
|
|
356
|
+
], { model: plannerConfig.model, temperature: 0.2 });
|
|
357
|
+
const raw = plannerResponse.content.trim();
|
|
358
|
+
const jsonStr = raw.startsWith("```")
|
|
359
|
+
? raw.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "")
|
|
360
|
+
: raw;
|
|
361
|
+
plan = JSON.parse(jsonStr);
|
|
362
|
+
if (!Array.isArray(plan.steps) || plan.steps.length === 0) {
|
|
363
|
+
throw new Error("Plan has no steps");
|
|
364
|
+
}
|
|
365
|
+
plannerSpinner.succeed(`Plan ready โ ${plan.steps.length} step${plan.steps.length !== 1 ? "s" : ""}`);
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
plannerSpinner.fail(`Planning failed: ${err.message}`);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
// โโ Phase 3: display plan + optional confirmation โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
372
|
+
displayPlan(plan, plan.steps);
|
|
373
|
+
if (options.confirm) {
|
|
374
|
+
const ok = await askConfirm(chalk_1.default.bold("Proceed with this plan? [Y/n] "));
|
|
375
|
+
if (!ok) {
|
|
376
|
+
console.log(chalk_1.default.gray("Aborted."));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
console.log();
|
|
380
|
+
}
|
|
381
|
+
// โโ Phase 4: execute in topological waves (parallel within each wave) โโโโโโ
|
|
382
|
+
const waves = buildExecutionWaves(plan.steps);
|
|
383
|
+
const stepResults = new Map();
|
|
384
|
+
const contextCache = new Map();
|
|
385
|
+
let nextId = Math.max(...plan.steps.map((s) => s.id)) + 1;
|
|
386
|
+
for (const wave of waves) {
|
|
387
|
+
const isParallel = wave.length > 1;
|
|
388
|
+
if (isParallel) {
|
|
389
|
+
console.log(chalk_1.default.gray(`โโ parallel wave (${wave.length} steps: ${wave.map((s) => s.id).join(", ")}) โโ`));
|
|
390
|
+
}
|
|
391
|
+
const waveSpinners = wave.map((step) => {
|
|
392
|
+
const action = step.action ?? "analyze";
|
|
393
|
+
const badge = { read: "๐", analyze: "๐", write: "โ๏ธ", test: "๐งช" }[action] ?? "โข";
|
|
394
|
+
const label = `${badge} [${step.id}] ${step.goal}`;
|
|
395
|
+
return isParallel ? (0, ora_1.default)(` ${label}`).start() : null;
|
|
396
|
+
});
|
|
397
|
+
if (!isParallel) {
|
|
398
|
+
const step = wave[0];
|
|
399
|
+
const action = step.action ?? "analyze";
|
|
400
|
+
const badge = { read: chalk_1.default.blue, analyze: chalk_1.default.gray, write: chalk_1.default.green, test: chalk_1.default.magenta }[action] ?? chalk_1.default.gray;
|
|
401
|
+
const targets = (step.files?.length ?? 0) + (step.symbols?.length ?? 0) > 0
|
|
402
|
+
? chalk_1.default.gray(` [${[...(step.files ?? []), ...(step.symbols ?? [])].join(", ")}]`)
|
|
403
|
+
: "";
|
|
404
|
+
console.log(`${chalk_1.default.bold.yellow(`[${step.id}/${plan.steps.length}]`)} ${badge(`[${action}]`)} ${chalk_1.default.bold(step.goal)}${targets}`);
|
|
405
|
+
}
|
|
406
|
+
const waveResults = await Promise.all(wave.map((step, i) => executeStep(step, config, data, cacheStorage, rootDir, executorConfig, executor, topK, budget, forceKeyword, stepResults, contextCache, options.showContext ?? false).then((result) => {
|
|
407
|
+
waveSpinners[i]?.succeed(` [${step.id}] ${step.goal}`);
|
|
408
|
+
return result;
|
|
409
|
+
}).catch((err) => {
|
|
410
|
+
waveSpinners[i]?.fail(` [${step.id}] ${step.goal}`);
|
|
411
|
+
return { stepId: step.id, answer: `Error: ${err.message}`, insufficient: false };
|
|
412
|
+
})));
|
|
413
|
+
for (const result of waveResults) {
|
|
414
|
+
stepResults.set(result.stepId, result.answer);
|
|
415
|
+
if (!isParallel) {
|
|
416
|
+
const indented = result.answer.replace(/\n/g, "\n ");
|
|
417
|
+
console.log(` ${indented}\n`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (isParallel) {
|
|
421
|
+
for (const result of waveResults) {
|
|
422
|
+
const step = wave.find((s) => s.id === result.stepId);
|
|
423
|
+
console.log(chalk_1.default.bold.yellow(`\n [${step.id}] ${step.goal}`));
|
|
424
|
+
console.log(` ${result.answer.replace(/\n/g, "\n ")}\n`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// โโ Adaptive re-planning: handle INSUFFICIENT steps โโโโโโโโโโโโโโโโโโโโ
|
|
428
|
+
const insufficientSteps = waveResults.filter((r) => r.insufficient);
|
|
429
|
+
if (insufficientSteps.length > 0) {
|
|
430
|
+
const replanSpinner = (0, ora_1.default)(` Re-planning for ${insufficientSteps.length} insufficient step(s)...`).start();
|
|
431
|
+
const failedSummary = insufficientSteps
|
|
432
|
+
.map((r) => {
|
|
433
|
+
const step = wave.find((s) => s.id === r.stepId);
|
|
434
|
+
return `Step ${r.stepId} (${step.goal}):\n${r.answer}`;
|
|
435
|
+
})
|
|
436
|
+
.join("\n\n");
|
|
437
|
+
try {
|
|
438
|
+
const replanResponse = await planner.chat([
|
|
439
|
+
{
|
|
440
|
+
role: "system",
|
|
441
|
+
content: `${REPLAN_SYSTEM_PROMPT}\n\n${plannerContext}`,
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
role: "user",
|
|
445
|
+
content: `The following steps reported insufficient context:\n\n${failedSummary}\n\n` +
|
|
446
|
+
`Next available step ID: ${nextId}. ` +
|
|
447
|
+
`Provide remediation steps to gather the missing information.`,
|
|
448
|
+
},
|
|
449
|
+
], { model: plannerConfig.model, temperature: 0.2 });
|
|
450
|
+
const raw = replanResponse.content.trim();
|
|
451
|
+
const jsonStr = raw.startsWith("```")
|
|
452
|
+
? raw.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "")
|
|
453
|
+
: raw;
|
|
454
|
+
const replan = JSON.parse(jsonStr);
|
|
455
|
+
if (Array.isArray(replan.steps) && replan.steps.length > 0) {
|
|
456
|
+
// Reassign IDs to avoid collisions
|
|
457
|
+
const remediationSteps = replan.steps.map((s, i) => ({
|
|
458
|
+
...s,
|
|
459
|
+
id: nextId + i,
|
|
460
|
+
}));
|
|
461
|
+
nextId += remediationSteps.length;
|
|
462
|
+
replanSpinner.succeed(` Re-plan: ${remediationSteps.length} remediation step(s) added`);
|
|
463
|
+
// Execute remediation steps immediately (as a new wave)
|
|
464
|
+
const remediationWaves = buildExecutionWaves(remediationSteps);
|
|
465
|
+
for (const remWave of remediationWaves) {
|
|
466
|
+
const remResults = await Promise.all(remWave.map((step) => executeStep(step, config, data, cacheStorage, rootDir, executorConfig, executor, topK, budget, forceKeyword, stepResults, contextCache, options.showContext ?? false)));
|
|
467
|
+
for (const r of remResults) {
|
|
468
|
+
stepResults.set(r.stepId, r.answer);
|
|
469
|
+
const step = remWave.find((s) => s.id === r.stepId);
|
|
470
|
+
console.log(chalk_1.default.bold.yellow(`\n [${step.id}แดฟ] ${step.goal}`));
|
|
471
|
+
console.log(` ${r.answer.replace(/\n/g, "\n ")}\n`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
replanSpinner.warn(" Re-plan returned no steps.");
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
catch {
|
|
480
|
+
replanSpinner.warn(" Re-planning failed โ continuing.");
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// โโ Phase 5: optional synthesis โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
485
|
+
let synthesisText = "";
|
|
486
|
+
if (options.synthesize && stepResults.size > 0) {
|
|
487
|
+
const synthSpinner = (0, ora_1.default)("Synthesizing final answer...").start();
|
|
488
|
+
const stepSummaries = plan.steps
|
|
489
|
+
.map((s) => {
|
|
490
|
+
const result = stepResults.get(s.id);
|
|
491
|
+
return result ? `### Step ${s.id}: ${s.goal}\n${result}` : null;
|
|
492
|
+
})
|
|
493
|
+
.filter(Boolean)
|
|
494
|
+
.join("\n\n");
|
|
495
|
+
try {
|
|
496
|
+
const response = await planner.chat([
|
|
497
|
+
{ role: "system", content: SYNTHESIZER_SYSTEM_PROMPT },
|
|
498
|
+
{
|
|
499
|
+
role: "user",
|
|
500
|
+
content: `Original task: ${task}\n\n## Step results\n\n${stepSummaries}\n\nSynthesize a final, complete answer.`,
|
|
501
|
+
},
|
|
502
|
+
], { model: plannerConfig.model, temperature: 0.3 });
|
|
503
|
+
synthesisText = response.content;
|
|
504
|
+
synthSpinner.succeed("Synthesis complete");
|
|
505
|
+
console.log(chalk_1.default.bold.cyan("\nโโ Final answer โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n"));
|
|
506
|
+
console.log(synthesisText);
|
|
507
|
+
console.log(chalk_1.default.gray("\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ"));
|
|
508
|
+
}
|
|
509
|
+
catch (err) {
|
|
510
|
+
synthSpinner.fail(`Synthesis error: ${err.message}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
// โโ Phase 6: offer to save synthesis as knowledge entry โโโโโโโโโโโโโโโโโโโ
|
|
514
|
+
if (synthesisText && options.synthesize) {
|
|
515
|
+
const save = await askConfirm(chalk_1.default.bold("\nSave synthesis as a knowledge entry? [y/N] "));
|
|
516
|
+
if (save) {
|
|
517
|
+
const knowledge = new knowledge_1.KnowledgeStorage(rootDir);
|
|
518
|
+
const entries = knowledge.load();
|
|
519
|
+
entries.push({
|
|
520
|
+
id: (0, crypto_1.randomUUID)(),
|
|
521
|
+
category: "pattern",
|
|
522
|
+
title: plan.goal,
|
|
523
|
+
body: synthesisText,
|
|
524
|
+
createdAt: new Date().toISOString(),
|
|
525
|
+
createdBy: `llm:${plannerConfig.model}`,
|
|
526
|
+
confidence: "medium",
|
|
527
|
+
});
|
|
528
|
+
knowledge.save(entries);
|
|
529
|
+
console.log(chalk_1.default.green(" Saved."));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
console.log(chalk_1.default.gray(`\nPlan complete: ${stepResults.size} step(s) executed` +
|
|
533
|
+
(options.synthesize ? " + synthesis" : "")));
|
|
534
|
+
}
|
|
535
|
+
//# sourceMappingURL=plan.js.map
|