@lsctech/polaris 0.3.2 → 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
|
+
}
|
|
@@ -142,12 +142,24 @@ function createDocsCommand(options = {}) {
|
|
|
142
142
|
.description("Interactively review pending governance decisions in the review queue")
|
|
143
143
|
.option("--queue <path>", "path to _review-queue.json (default: smartdocs/raw/_review-queue.json)")
|
|
144
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")
|
|
145
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
|
+
}
|
|
146
161
|
try {
|
|
147
|
-
|
|
148
|
-
? (0, node_path_1.resolve)(opts.repoRoot, opts.queue).replace(/_review-queue\.json$/, "").replace(/\/$/, "")
|
|
149
|
-
: (0, node_path_1.resolve)(opts.repoRoot, "smartdocs", "raw");
|
|
150
|
-
await (0, review_js_1.runReviewSession)({ repoRoot: opts.repoRoot, queueDir });
|
|
162
|
+
await (0, review_js_1.runReviewSession)({ repoRoot, queueDir, queueFilename, readKey });
|
|
151
163
|
}
|
|
152
164
|
catch (err) {
|
|
153
165
|
console.error(`polaris docs review: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -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;
|