@lsctech/polaris 0.2.0 → 0.2.1
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.
|
@@ -6,6 +6,7 @@ const node_fs_1 = require("node:fs");
|
|
|
6
6
|
const node_path_1 = require("node:path");
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const ingest_js_1 = require("./ingest.js");
|
|
9
|
+
const review_js_1 = require("./review.js");
|
|
9
10
|
const migrate_js_1 = require("./migrate.js");
|
|
10
11
|
const seed_instructions_js_1 = require("./seed-instructions.js");
|
|
11
12
|
const validate_instructions_js_1 = require("./validate-instructions.js");
|
|
@@ -133,6 +134,23 @@ function createDocsCommand(options = {}) {
|
|
|
133
134
|
process.exit(1);
|
|
134
135
|
}
|
|
135
136
|
});
|
|
137
|
+
docs
|
|
138
|
+
.command("review")
|
|
139
|
+
.description("Interactively review pending governance decisions in the review queue")
|
|
140
|
+
.option("--queue <path>", "path to _review-queue.json (default: smartdocs/raw/_review-queue.json)")
|
|
141
|
+
.option("-r, --repo-root <path>", "Repository root", defaultRepoRoot)
|
|
142
|
+
.action(async (opts) => {
|
|
143
|
+
try {
|
|
144
|
+
const queueDir = opts.queue
|
|
145
|
+
? (0, node_path_1.resolve)(opts.repoRoot, opts.queue).replace(/_review-queue\.json$/, "").replace(/\/$/, "")
|
|
146
|
+
: (0, node_path_1.resolve)(opts.repoRoot, "smartdocs", "raw");
|
|
147
|
+
await (0, review_js_1.runReviewSession)({ repoRoot: opts.repoRoot, queueDir });
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
console.error(`polaris docs review: ${err instanceof Error ? err.message : String(err)}`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
136
154
|
docs
|
|
137
155
|
.command("migrate")
|
|
138
156
|
.description("Find scattered markdown files, move them to smartdocs/raw/, and produce an ingest cluster list")
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.filterUndecided = filterUndecided;
|
|
37
|
+
exports.formatPacketCard = formatPacketCard;
|
|
38
|
+
exports.runReviewSession = runReviewSession;
|
|
39
|
+
const node_child_process_1 = require("node:child_process");
|
|
40
|
+
const readline = __importStar(require("node:readline"));
|
|
41
|
+
const node_path_1 = require("node:path");
|
|
42
|
+
const index_js_1 = require("../governance/index.js");
|
|
43
|
+
const ingest_js_1 = require("./ingest.js");
|
|
44
|
+
function filterUndecided(packets) {
|
|
45
|
+
return packets.filter((p) => p.reviewDecision === undefined || p.reviewDecision === "defer");
|
|
46
|
+
}
|
|
47
|
+
function formatPacketCard(packet, index, total) {
|
|
48
|
+
const divider = "─".repeat(65);
|
|
49
|
+
return [
|
|
50
|
+
divider,
|
|
51
|
+
`[${index}/${total}] ${packet.sourcePath}`,
|
|
52
|
+
` → ${packet.proposedDestination}`,
|
|
53
|
+
` Authority risk: ${packet.authorityRisk.toUpperCase()}`,
|
|
54
|
+
` Recommendation: ${packet.recommendation}`,
|
|
55
|
+
``,
|
|
56
|
+
`[a]pprove [r]eject [d]efer [s]kip [q]uit`,
|
|
57
|
+
divider,
|
|
58
|
+
].join("\n");
|
|
59
|
+
}
|
|
60
|
+
function defaultGetReviewedBy() {
|
|
61
|
+
try {
|
|
62
|
+
return (0, node_child_process_1.execSync)("git config user.name", { encoding: "utf-8" }).trim() || "unknown";
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return "unknown";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function readSingleKey() {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
readline.emitKeypressEvents(process.stdin);
|
|
71
|
+
if (process.stdin.isTTY)
|
|
72
|
+
process.stdin.setRawMode(true);
|
|
73
|
+
const handler = (_str, key) => {
|
|
74
|
+
if (process.stdin.isTTY)
|
|
75
|
+
process.stdin.setRawMode(false);
|
|
76
|
+
process.stdin.removeListener("keypress", handler);
|
|
77
|
+
if (key?.ctrl && key.name === "c")
|
|
78
|
+
process.exit(0);
|
|
79
|
+
resolve(key?.name ?? _str ?? "");
|
|
80
|
+
};
|
|
81
|
+
process.stdin.on("keypress", handler);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async function runReviewSession(options) {
|
|
85
|
+
const { repoRoot, readKey, getReviewedBy = defaultGetReviewedBy, output = (msg) => process.stdout.write(msg + "\n"), } = options;
|
|
86
|
+
const queueDir = options.queueDir ?? (0, node_path_1.resolve)(repoRoot, "smartdocs", "raw");
|
|
87
|
+
const packets = (0, index_js_1.readReviewQueue)(queueDir);
|
|
88
|
+
if (packets.length === 0) {
|
|
89
|
+
output("No review queue found. Run polaris docs ingest first.");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const undecided = filterUndecided(packets);
|
|
93
|
+
if (undecided.length === 0) {
|
|
94
|
+
output("Nothing to review. All decisions are final — run polaris docs ingest to apply.");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let decided = 0;
|
|
98
|
+
for (let i = 0; i < undecided.length; i++) {
|
|
99
|
+
const packet = undecided[i];
|
|
100
|
+
output(formatPacketCard(packet, i + 1, undecided.length));
|
|
101
|
+
const key = readKey ? await readKey() : await readSingleKey();
|
|
102
|
+
if (key === "q") {
|
|
103
|
+
const remaining = undecided.length - decided;
|
|
104
|
+
output(`\nSession ended. ${decided} decision(s) saved, ${remaining} packet(s) still pending.`);
|
|
105
|
+
output("Run polaris docs review to continue.");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (key === "s") {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const decision = key === "a" ? "approve" :
|
|
112
|
+
key === "r" ? "reject" :
|
|
113
|
+
key === "d" ? "defer" :
|
|
114
|
+
null;
|
|
115
|
+
if (!decision) {
|
|
116
|
+
output("Unrecognized key. Use [a], [r], [d], [s], or [q].");
|
|
117
|
+
i--;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const idx = packets.findIndex((p) => p.sourcePath === packet.sourcePath);
|
|
121
|
+
if (idx !== -1) {
|
|
122
|
+
packets[idx] = {
|
|
123
|
+
...packets[idx],
|
|
124
|
+
reviewDecision: decision,
|
|
125
|
+
reviewedAt: new Date().toISOString(),
|
|
126
|
+
reviewedBy: getReviewedBy(),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
(0, index_js_1.writeReviewQueue)(packets, "review-session", queueDir);
|
|
130
|
+
decided++;
|
|
131
|
+
}
|
|
132
|
+
const approved = packets.filter((p) => p.reviewDecision === "approve").length;
|
|
133
|
+
const rejected = packets.filter((p) => p.reviewDecision === "reject").length;
|
|
134
|
+
const deferred = packets.filter((p) => p.reviewDecision === "defer").length;
|
|
135
|
+
output(`\nReview complete: ${approved} approved, ${rejected} rejected, ${deferred} deferred.`);
|
|
136
|
+
const pendingFiles = packets
|
|
137
|
+
.filter((p) => p.reviewDecision === "approve" || p.reviewDecision === "reject")
|
|
138
|
+
.map((p) => p.sourcePath);
|
|
139
|
+
if (pendingFiles.length > 0) {
|
|
140
|
+
output("Running docs ingest to apply decisions...");
|
|
141
|
+
try {
|
|
142
|
+
const results = (0, ingest_js_1.ingestDocs)(pendingFiles, { repoRoot });
|
|
143
|
+
(0, ingest_js_1.printIngestResults)(results);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
output(`Ingest error: ${err instanceof Error ? err.message : String(err)}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|