@lsctech/polaris 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -52,14 +52,15 @@ function groupAndSort(packets) {
|
|
|
52
52
|
* Write _review-queue.json (canonical) and _review-queue.md (display-only) to outputDir.
|
|
53
53
|
* Markdown is regenerated from JSON — never parse markdown to recover decisions.
|
|
54
54
|
*/
|
|
55
|
-
function writeReviewQueue(packets, runId, outputDir) {
|
|
55
|
+
function writeReviewQueue(packets, runId, outputDir, filename = "_review-queue.json") {
|
|
56
56
|
(0, node_fs_1.mkdirSync)(outputDir, { recursive: true });
|
|
57
57
|
const queueFile = {
|
|
58
58
|
generated_at: new Date().toISOString(),
|
|
59
59
|
run_id: runId,
|
|
60
60
|
packets,
|
|
61
61
|
};
|
|
62
|
-
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(outputDir,
|
|
62
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(outputDir, filename), JSON.stringify(queueFile, null, 2) + "\n", "utf-8");
|
|
63
|
+
const mdFilename = filename.replace(/\.json$/, ".md");
|
|
63
64
|
const sorted = groupAndSort(packets);
|
|
64
65
|
const sections = sorted.map(renderPacketMarkdown).join("\n\n---\n\n");
|
|
65
66
|
const md = [
|
|
@@ -69,21 +70,21 @@ function writeReviewQueue(packets, runId, outputDir) {
|
|
|
69
70
|
`**Generated:** ${queueFile.generated_at}`,
|
|
70
71
|
`**Pending review:** ${packets.length} document(s)`,
|
|
71
72
|
``,
|
|
72
|
-
`> Markdown is display-only. Edit \`
|
|
73
|
+
`> Markdown is display-only. Edit \`${filename}\` to set \`reviewDecision\` fields.`,
|
|
73
74
|
`> Rerun \`polaris docs ingest\` to apply decisions.`,
|
|
74
75
|
``,
|
|
75
76
|
`---`,
|
|
76
77
|
``,
|
|
77
78
|
sections,
|
|
78
79
|
].join("\n");
|
|
79
|
-
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(outputDir,
|
|
80
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(outputDir, mdFilename), md, "utf-8");
|
|
80
81
|
}
|
|
81
82
|
/**
|
|
82
83
|
* Read review queue from JSON. Returns empty array if no queue file exists.
|
|
83
84
|
* Never reads markdown.
|
|
84
85
|
*/
|
|
85
|
-
function readReviewQueue(outputDir) {
|
|
86
|
-
const jsonPath = (0, node_path_1.join)(outputDir,
|
|
86
|
+
function readReviewQueue(outputDir, filename = "_review-queue.json") {
|
|
87
|
+
const jsonPath = (0, node_path_1.join)(outputDir, filename);
|
|
87
88
|
if (!(0, node_fs_1.existsSync)(jsonPath))
|
|
88
89
|
return [];
|
|
89
90
|
try {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agenticDecideKey = agenticDecideKey;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
async function agenticDecideKey(packet) {
|
|
6
|
+
let docContent = "";
|
|
7
|
+
try {
|
|
8
|
+
if ((0, node_fs_1.existsSync)(packet.sourcePath)) {
|
|
9
|
+
docContent = (0, node_fs_1.readFileSync)(packet.sourcePath, "utf-8").slice(0, 3000);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
catch { /* ignore */ }
|
|
13
|
+
// @ts-ignore: @anthropic-ai/sdk may not be installed as a dependency
|
|
14
|
+
const mod = await import("@anthropic-ai/sdk");
|
|
15
|
+
const Anthropic = mod.default ?? mod.Anthropic;
|
|
16
|
+
const client = new Anthropic();
|
|
17
|
+
const staleSymbols = packet.staleSymbols;
|
|
18
|
+
const triageFlag = packet.triageFlag;
|
|
19
|
+
const prompt = `You are reviewing a candidate documentation file flagged during Polaris docs triage.
|
|
20
|
+
|
|
21
|
+
Flag type: ${String(triageFlag ?? "unknown")}
|
|
22
|
+
Authority risk: ${packet.authorityRisk}
|
|
23
|
+
Recommendation from triage: ${packet.recommendation}
|
|
24
|
+
Reasoning: ${packet.reasoning.join("; ")}
|
|
25
|
+
${Array.isArray(staleSymbols) ? `Stale symbols (not found in codebase graph): ${staleSymbols.join(", ")}` : ""}
|
|
26
|
+
|
|
27
|
+
Document content:
|
|
28
|
+
---
|
|
29
|
+
${docContent}
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
Decide whether this document should be:
|
|
33
|
+
- approved: doc is valid doctrine and should be promoted to active
|
|
34
|
+
- rejected: doc is outdated, incorrect, or superseded and should be deprecated
|
|
35
|
+
- deferred: uncertain — needs human review
|
|
36
|
+
|
|
37
|
+
Respond with exactly one word: approve, reject, or defer.`;
|
|
38
|
+
const response = await client.messages.create({
|
|
39
|
+
model: "claude-haiku-4-5-20251001",
|
|
40
|
+
max_tokens: 10,
|
|
41
|
+
messages: [{ role: "user", content: prompt }],
|
|
42
|
+
});
|
|
43
|
+
const text = (response.content[0].text ?? "defer").trim().toLowerCase();
|
|
44
|
+
if (text.startsWith("approve"))
|
|
45
|
+
return "a";
|
|
46
|
+
if (text.startsWith("reject"))
|
|
47
|
+
return "r";
|
|
48
|
+
return "d";
|
|
49
|
+
}
|
|
@@ -13,6 +13,8 @@ const validate_instructions_js_1 = require("./validate-instructions.js");
|
|
|
13
13
|
const doctrine_js_1 = require("./doctrine.js");
|
|
14
14
|
const audit_js_1 = require("./audit.js");
|
|
15
15
|
const triage_js_1 = require("./triage.js");
|
|
16
|
+
const adapter_js_1 = require("../graph/store/adapter.js");
|
|
17
|
+
const index_js_1 = require("../graph/query/index.js");
|
|
16
18
|
/**
|
|
17
19
|
* Build and return the top-level "docs" Commander command group for Polaris docs lifecycle workflows.
|
|
18
20
|
*
|
|
@@ -140,12 +142,24 @@ function createDocsCommand(options = {}) {
|
|
|
140
142
|
.description("Interactively review pending governance decisions in the review queue")
|
|
141
143
|
.option("--queue <path>", "path to _review-queue.json (default: smartdocs/raw/_review-queue.json)")
|
|
142
144
|
.option("-r, --repo-root <path>", "Repository root", defaultRepoRoot)
|
|
145
|
+
.option("--agentic", "use LLM agent to make review decisions automatically")
|
|
146
|
+
.option("--triage", "review the triage queue (_triage-queue.json) instead of the review queue")
|
|
143
147
|
.action(async (opts) => {
|
|
148
|
+
const repoRoot = opts.repoRoot;
|
|
149
|
+
const queueFilename = opts.triage ? "_triage-queue.json" : "_review-queue.json";
|
|
150
|
+
const queueDir = opts.queue
|
|
151
|
+
? (0, node_path_1.resolve)(opts.repoRoot, opts.queue)
|
|
152
|
+
.replace(/_review-queue\.json$/, "")
|
|
153
|
+
.replace(/_triage-queue\.json$/, "")
|
|
154
|
+
.replace(/\/$/, "")
|
|
155
|
+
: (0, node_path_1.resolve)(repoRoot, "smartdocs", "raw");
|
|
156
|
+
let readKey;
|
|
157
|
+
if (opts.agentic) {
|
|
158
|
+
const { agenticDecideKey } = await import("./agentic-review.js");
|
|
159
|
+
readKey = agenticDecideKey;
|
|
160
|
+
}
|
|
144
161
|
try {
|
|
145
|
-
|
|
146
|
-
? (0, node_path_1.resolve)(opts.repoRoot, opts.queue).replace(/_review-queue\.json$/, "").replace(/\/$/, "")
|
|
147
|
-
: (0, node_path_1.resolve)(opts.repoRoot, "smartdocs", "raw");
|
|
148
|
-
await (0, review_js_1.runReviewSession)({ repoRoot: opts.repoRoot, queueDir });
|
|
162
|
+
await (0, review_js_1.runReviewSession)({ repoRoot, queueDir, queueFilename, readKey });
|
|
149
163
|
}
|
|
150
164
|
catch (err) {
|
|
151
165
|
console.error(`polaris docs review: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -160,18 +174,35 @@ function createDocsCommand(options = {}) {
|
|
|
160
174
|
.option("--resume", "Resume from last checkpoint (auto-detected by default)")
|
|
161
175
|
.option("--dry-run", "Plan batches and print cost estimate without calling the LLM")
|
|
162
176
|
.action(async (options) => {
|
|
177
|
+
const repoRoot = options.repoRoot;
|
|
178
|
+
const dbPath = (0, node_path_1.join)(repoRoot, ".polaris", "graph", "graph.sqlite");
|
|
179
|
+
const graphOutputPath = (0, node_path_1.join)(".polaris", "graph");
|
|
180
|
+
let store = null;
|
|
181
|
+
try {
|
|
182
|
+
store = new adapter_js_1.GraphStoreAdapter({ repoRoot, graphOutputPath, dbPath });
|
|
183
|
+
store.open();
|
|
184
|
+
(0, index_js_1.configureGraphQuery)({ graphStore: store });
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
(0, index_js_1.configureGraphQuery)({ graphStore: null });
|
|
188
|
+
}
|
|
163
189
|
try {
|
|
164
190
|
await (0, triage_js_1.runTriage)({
|
|
165
|
-
repoRoot
|
|
191
|
+
repoRoot,
|
|
166
192
|
batchSize: parseInt(options.batchSize, 10) || 10,
|
|
167
193
|
resume: options.resume,
|
|
168
194
|
dryRun: options.dryRun,
|
|
195
|
+
symbolLookup: (name) => (0, index_js_1.lookupSymbol)(name) !== null,
|
|
196
|
+
graphStats: () => (0, index_js_1.getGraphStats)(),
|
|
169
197
|
});
|
|
170
198
|
}
|
|
171
199
|
catch (err) {
|
|
172
200
|
console.error(`polaris docs triage: ${err instanceof Error ? err.message : String(err)}`);
|
|
173
201
|
process.exit(1);
|
|
174
202
|
}
|
|
203
|
+
finally {
|
|
204
|
+
store?.close();
|
|
205
|
+
}
|
|
175
206
|
});
|
|
176
207
|
docs
|
|
177
208
|
.command("migrate")
|
|
@@ -84,7 +84,8 @@ function readSingleKey() {
|
|
|
84
84
|
async function runReviewSession(options) {
|
|
85
85
|
const { repoRoot, readKey, getReviewedBy = defaultGetReviewedBy, output = (msg) => process.stdout.write(msg + "\n"), } = options;
|
|
86
86
|
const queueDir = options.queueDir ?? (0, node_path_1.resolve)(repoRoot, "smartdocs", "raw");
|
|
87
|
-
const
|
|
87
|
+
const queueFilename = options.queueFilename;
|
|
88
|
+
const packets = (0, index_js_1.readReviewQueue)(queueDir, queueFilename);
|
|
88
89
|
if (packets.length === 0) {
|
|
89
90
|
output("No review queue found. Run polaris docs ingest first.");
|
|
90
91
|
return;
|
|
@@ -98,7 +99,7 @@ async function runReviewSession(options) {
|
|
|
98
99
|
for (let i = 0; i < undecided.length; i++) {
|
|
99
100
|
const packet = undecided[i];
|
|
100
101
|
output(formatPacketCard(packet, i + 1, undecided.length));
|
|
101
|
-
const key = readKey ? await readKey() : await readSingleKey();
|
|
102
|
+
const key = readKey ? await readKey(packet) : await readSingleKey();
|
|
102
103
|
if (key === "q") {
|
|
103
104
|
const remaining = undecided.length - decided;
|
|
104
105
|
output(`\nSession ended. ${decided} decision(s) saved, ${remaining} packet(s) still pending.`);
|
|
@@ -126,7 +127,7 @@ async function runReviewSession(options) {
|
|
|
126
127
|
reviewedBy: getReviewedBy(),
|
|
127
128
|
};
|
|
128
129
|
}
|
|
129
|
-
(0, index_js_1.writeReviewQueue)(packets, "review-session", queueDir);
|
|
130
|
+
(0, index_js_1.writeReviewQueue)(packets, "review-session", queueDir, queueFilename);
|
|
130
131
|
decided++;
|
|
131
132
|
}
|
|
132
133
|
const approved = packets.filter((p) => p.reviewDecision === "approve").length;
|