@lsctech/polaris 0.3.4 → 0.3.6
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/dist/cli/agent-setup.js +126 -0
- package/dist/cli/index.js +9 -0
- package/dist/skill-packet/generator.js +2 -1
- package/dist/smartdocs-engine/agentic-review.js +27 -39
- package/dist/smartdocs-engine/librarian-dispatch.js +123 -0
- package/dist/workspace/workspace/.polaris/skills/docs-review/chain.md +5 -3
- package/package.json +1 -1
|
@@ -0,0 +1,126 @@
|
|
|
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.runAgentSetup = runAgentSetup;
|
|
37
|
+
const node_child_process_1 = require("node:child_process");
|
|
38
|
+
const readline = __importStar(require("node:readline"));
|
|
39
|
+
const node_fs_1 = require("node:fs");
|
|
40
|
+
const node_path_1 = require("node:path");
|
|
41
|
+
const SUPPORTED_PROVIDERS = [
|
|
42
|
+
{ name: "claude", detectCmd: "claude", displayName: "Claude (Anthropic)" },
|
|
43
|
+
{ name: "codex", detectCmd: "codex", displayName: "Codex (OpenAI)" },
|
|
44
|
+
{ name: "copilot", detectCmd: "copilot", displayName: "Copilot (GitHub)" },
|
|
45
|
+
{ name: "devin", detectCmd: "devin", displayName: "Devin (Cognition)" },
|
|
46
|
+
{ name: "gemini", detectCmd: "gemini", displayName: "Gemini (Google)" },
|
|
47
|
+
];
|
|
48
|
+
const ROLES = ["librarian", "foreman", "worker", "analyst"];
|
|
49
|
+
function isInstalled(cmd) {
|
|
50
|
+
const { status } = (0, node_child_process_1.spawnSync)("which", [cmd], { stdio: "ignore" });
|
|
51
|
+
return status === 0;
|
|
52
|
+
}
|
|
53
|
+
function detectProviders() {
|
|
54
|
+
return SUPPORTED_PROVIDERS.map((p) => ({
|
|
55
|
+
name: p.name,
|
|
56
|
+
displayName: p.displayName,
|
|
57
|
+
installed: isInstalled(p.detectCmd),
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
async function selectProvider(role, detected, currentProviders) {
|
|
61
|
+
const installed = detected.filter((p) => p.installed);
|
|
62
|
+
if (installed.length === 0)
|
|
63
|
+
return null;
|
|
64
|
+
console.log(`\nRole: ${role}`);
|
|
65
|
+
detected.forEach((p, i) => {
|
|
66
|
+
const marker = p.installed ? "✓" : "✗";
|
|
67
|
+
const current = currentProviders.includes(p.name) ? " (current)" : "";
|
|
68
|
+
const selectable = p.installed ? `[${i + 1}]` : " ";
|
|
69
|
+
console.log(` ${selectable} ${marker} ${p.displayName}${current}`);
|
|
70
|
+
});
|
|
71
|
+
const defaultChoice = installed.find((p) => currentProviders.includes(p.name)) ?? installed[0];
|
|
72
|
+
const defaultIdx = detected.indexOf(defaultChoice) + 1;
|
|
73
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
74
|
+
return new Promise((res) => {
|
|
75
|
+
rl.question(`\nSelect provider for ${role} [${defaultIdx}]: `, (answer) => {
|
|
76
|
+
rl.close();
|
|
77
|
+
const idx = parseInt(answer.trim() || String(defaultIdx), 10) - 1;
|
|
78
|
+
const chosen = detected[idx];
|
|
79
|
+
if (!chosen?.installed) {
|
|
80
|
+
console.log(" That provider is not installed. Using default.");
|
|
81
|
+
res(defaultChoice.name);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
res(chosen.name);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async function runAgentSetup(repoRoot) {
|
|
90
|
+
console.log("\nPolaris Agent Setup — configure one provider per role\n");
|
|
91
|
+
const detected = detectProviders();
|
|
92
|
+
const installed = detected.filter((p) => p.installed);
|
|
93
|
+
if (installed.length === 0) {
|
|
94
|
+
console.error("No supported agents detected. Install one of: " +
|
|
95
|
+
SUPPORTED_PROVIDERS.map((p) => p.name).join(", "));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
console.log("Detected agents:");
|
|
99
|
+
installed.forEach((p) => console.log(` ✓ ${p.displayName}`));
|
|
100
|
+
const configPath = (0, node_path_1.resolve)(repoRoot, "polaris.config.json");
|
|
101
|
+
let config;
|
|
102
|
+
try {
|
|
103
|
+
config = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
console.error(`polaris agent setup: could not read ${configPath}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
const execution = config.execution ?? {};
|
|
110
|
+
const providerPolicy = execution.providerPolicy ?? {};
|
|
111
|
+
for (const role of ROLES) {
|
|
112
|
+
const currentProviders = providerPolicy[role]?.providers ?? [];
|
|
113
|
+
const chosen = await selectProvider(role, detected, currentProviders);
|
|
114
|
+
if (chosen) {
|
|
115
|
+
providerPolicy[role] = {
|
|
116
|
+
...(providerPolicy[role] ?? {}),
|
|
117
|
+
providers: [chosen, ...currentProviders.filter((p) => p !== chosen)],
|
|
118
|
+
};
|
|
119
|
+
console.log(` → ${role}: ${chosen}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
execution.providerPolicy = providerPolicy;
|
|
123
|
+
config.execution = execution;
|
|
124
|
+
(0, node_fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
125
|
+
console.log(`\nAgent configuration saved to ${configPath}`);
|
|
126
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.createPolarisCommand = createPolarisCommand;
|
|
|
5
5
|
const node_fs_1 = require("node:fs");
|
|
6
6
|
const node_path_1 = require("node:path");
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
|
+
const agent_setup_js_1 = require("./agent-setup.js");
|
|
8
9
|
const version_js_1 = require("./version.js");
|
|
9
10
|
const branding_js_1 = require("./branding.js");
|
|
10
11
|
const index_js_1 = require("../loop/index.js");
|
|
@@ -105,6 +106,14 @@ function createPolarisCommand(options = {}) {
|
|
|
105
106
|
program.addCommand((0, medic_js_1.createMedicCommand)({
|
|
106
107
|
repoRoot,
|
|
107
108
|
}));
|
|
109
|
+
const agentCmd = new commander_1.Command("agent").description("Manage Polaris agent provider configuration");
|
|
110
|
+
agentCmd.addCommand(new commander_1.Command("setup")
|
|
111
|
+
.description("Configure agent providers per role (librarian, foreman, worker, analyst)")
|
|
112
|
+
.option("-r, --repo-root <path>", "Repository root", repoRoot)
|
|
113
|
+
.action(async (opts) => {
|
|
114
|
+
await (0, agent_setup_js_1.runAgentSetup)(opts.repoRoot);
|
|
115
|
+
}));
|
|
116
|
+
program.addCommand(agentCmd);
|
|
108
117
|
program
|
|
109
118
|
.command("welfare-check")
|
|
110
119
|
.description("safe/read-only: run route welfare checks and report per-route health")
|
|
@@ -229,7 +229,8 @@ function buildReviewPacket() {
|
|
|
229
229
|
"Read candidate documents from smartdocs/doctrine/candidate/ to inform decisions",
|
|
230
230
|
"Evaluate each flagged packet: read the doc, consider the flag type and stale symbols, decide approve/reject/defer",
|
|
231
231
|
"Write decisions back to the triage queue by calling polaris docs review --write-decision <sourcePath> <decision>",
|
|
232
|
-
"Call polaris docs
|
|
232
|
+
"Call polaris docs promote <path> for each approved packet (candidate → active)",
|
|
233
|
+
"Call polaris doctrine deprecate <path> for each rejected packet",
|
|
233
234
|
"Emit telemetry events",
|
|
234
235
|
],
|
|
235
236
|
prohibited_actions: [
|
|
@@ -1,49 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.agenticDecideKey = agenticDecideKey;
|
|
4
|
-
const
|
|
4
|
+
const librarian_dispatch_js_1 = require("./librarian-dispatch.js");
|
|
5
|
+
const loader_js_1 = require("../config/loader.js");
|
|
5
6
|
async function agenticDecideKey(packet) {
|
|
6
|
-
let
|
|
7
|
+
let config = null;
|
|
7
8
|
try {
|
|
8
|
-
|
|
9
|
-
docContent = (0, node_fs_1.readFileSync)(packet.sourcePath, "utf-8").slice(0, 3000);
|
|
10
|
-
}
|
|
9
|
+
config = (0, loader_js_1.loadConfig)(process.cwd());
|
|
11
10
|
}
|
|
12
|
-
catch {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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"))
|
|
11
|
+
catch {
|
|
12
|
+
throw new Error("polaris agent setup required: could not load polaris.config.json.\n" +
|
|
13
|
+
"Run `polaris agent setup` to configure a librarian or foreman agent.");
|
|
14
|
+
}
|
|
15
|
+
const providers = (config.execution?.providers ?? {});
|
|
16
|
+
const librarianOrder = config.execution?.providerPolicy?.librarian?.providers ?? [];
|
|
17
|
+
const foremanOrder = config.execution?.providerPolicy?.foreman?.providers ?? [];
|
|
18
|
+
// Try librarian first, then foreman as fallback
|
|
19
|
+
const resolvedRole = (0, librarian_dispatch_js_1.resolveLibrarianProvider)(providers, librarianOrder) != null ? "librarian"
|
|
20
|
+
: (0, librarian_dispatch_js_1.resolveLibrarianProvider)(providers, foremanOrder) != null ? "foreman"
|
|
21
|
+
: null;
|
|
22
|
+
if (!resolvedRole) {
|
|
23
|
+
const configured = [...new Set([...librarianOrder, ...foremanOrder])];
|
|
24
|
+
const msg = configured.length > 0
|
|
25
|
+
? `Configured agents (${configured.join(", ")}) are not installed on this machine.`
|
|
26
|
+
: "No librarian or foreman agents are configured.";
|
|
27
|
+
throw new Error(`polaris agent setup required: ${msg}\n` +
|
|
28
|
+
"Run `polaris agent setup` to configure an available agent.");
|
|
29
|
+
}
|
|
30
|
+
const providerOrder = resolvedRole === "librarian" ? librarianOrder : foremanOrder;
|
|
31
|
+
const result = await (0, librarian_dispatch_js_1.dispatchLibrarianReview)({ packet, repoRoot: process.cwd(), providers, providerOrder });
|
|
32
|
+
if (result.decision === "approve")
|
|
45
33
|
return "a";
|
|
46
|
-
if (
|
|
34
|
+
if (result.decision === "reject")
|
|
47
35
|
return "r";
|
|
48
36
|
return "d";
|
|
49
37
|
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveLibrarianProvider = resolveLibrarianProvider;
|
|
4
|
+
exports.dispatchLibrarianReview = dispatchLibrarianReview;
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
// Returns the first provider name from providerOrder whose command is installed (via `which`)
|
|
8
|
+
function resolveLibrarianProvider(providers, providerOrder) {
|
|
9
|
+
for (const name of providerOrder) {
|
|
10
|
+
const cfg = providers[name];
|
|
11
|
+
if (!cfg)
|
|
12
|
+
continue;
|
|
13
|
+
const cmd = cfg.command === "env"
|
|
14
|
+
? (cfg.args ?? []).find((a) => !a.includes("=") && !a.startsWith("-")) ?? cfg.command
|
|
15
|
+
: cfg.command;
|
|
16
|
+
try {
|
|
17
|
+
const { status } = (0, node_child_process_1.spawnSync)("which", [cmd], { stdio: "ignore" });
|
|
18
|
+
if (status === 0)
|
|
19
|
+
return name;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function grepEvidence(repoRoot, symbols) {
|
|
28
|
+
const evidence = {};
|
|
29
|
+
for (const sym of symbols.slice(0, 8)) {
|
|
30
|
+
try {
|
|
31
|
+
const result = (0, node_child_process_1.spawnSync)("grep", [
|
|
32
|
+
"-r",
|
|
33
|
+
"--include=*.ts",
|
|
34
|
+
"--include=*.tsx",
|
|
35
|
+
"--include=*.swift",
|
|
36
|
+
"--include=*.dart",
|
|
37
|
+
"-l",
|
|
38
|
+
sym,
|
|
39
|
+
".",
|
|
40
|
+
], { cwd: repoRoot, encoding: "utf-8", timeout: 5000 });
|
|
41
|
+
const files = (result.stdout ?? "")
|
|
42
|
+
.trim()
|
|
43
|
+
.split("\n")
|
|
44
|
+
.filter(Boolean)
|
|
45
|
+
.slice(0, 3);
|
|
46
|
+
evidence[sym] = files.length > 0 ? `found in: ${files.join(", ")}` : "not found in codebase";
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
evidence[sym] = "search error";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return evidence;
|
|
53
|
+
}
|
|
54
|
+
async function dispatchLibrarianReview(options) {
|
|
55
|
+
const { packet, repoRoot, providers, providerOrder } = options;
|
|
56
|
+
const providerName = resolveLibrarianProvider(providers, providerOrder);
|
|
57
|
+
if (!providerName) {
|
|
58
|
+
throw new Error("No librarian provider available. Run `polaris agent setup` to configure one.");
|
|
59
|
+
}
|
|
60
|
+
const cfg = providers[providerName];
|
|
61
|
+
const staleSymbols = packet.staleSymbols ?? [];
|
|
62
|
+
const evidence = grepEvidence(repoRoot, staleSymbols);
|
|
63
|
+
let docContent = "";
|
|
64
|
+
try {
|
|
65
|
+
if ((0, node_fs_1.existsSync)(packet.sourcePath)) {
|
|
66
|
+
docContent = (0, node_fs_1.readFileSync)(packet.sourcePath, "utf-8").slice(0, 2000);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* ignore */
|
|
71
|
+
}
|
|
72
|
+
const evidenceLines = Object.entries(evidence)
|
|
73
|
+
.map(([sym, result]) => ` ${sym}: ${result}`)
|
|
74
|
+
.join("\n");
|
|
75
|
+
const prompt = `You are an agentic librarian reviewing a candidate documentation file for the Polaris docs review pipeline.
|
|
76
|
+
|
|
77
|
+
Doc path: ${packet.sourcePath}
|
|
78
|
+
Flag type: stale-reference
|
|
79
|
+
Stale symbols (flagged as not in the codebase graph):
|
|
80
|
+
${staleSymbols.join(", ")}
|
|
81
|
+
|
|
82
|
+
Live codebase evidence (grep results):
|
|
83
|
+
${evidenceLines}
|
|
84
|
+
|
|
85
|
+
Document content (first 2000 chars):
|
|
86
|
+
---
|
|
87
|
+
${docContent}
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
Decision rules:
|
|
91
|
+
- approve: symbols found in live codebase OR doc describes concepts still clearly relevant to current architecture
|
|
92
|
+
- reject: doc describes systems/components that are clearly gone with no trace in codebase
|
|
93
|
+
- defer: ambiguous — cannot determine from evidence alone
|
|
94
|
+
|
|
95
|
+
Respond with ONLY valid JSON on a single line:
|
|
96
|
+
{"decision":"approve"|"reject"|"defer","reasoning":"one sentence"}`;
|
|
97
|
+
const args = (cfg.args ?? []).map((a) => (a === "{{worker_prompt}}" ? prompt : a));
|
|
98
|
+
const result = (0, node_child_process_1.spawnSync)(cfg.command, args, {
|
|
99
|
+
encoding: "utf-8",
|
|
100
|
+
timeout: 60000,
|
|
101
|
+
cwd: repoRoot,
|
|
102
|
+
});
|
|
103
|
+
const stdout = (result.stdout ?? "").trim();
|
|
104
|
+
const jsonLine = stdout
|
|
105
|
+
.split("\n")
|
|
106
|
+
.reverse()
|
|
107
|
+
.find((l) => l.trim().startsWith("{") && l.includes("decision"));
|
|
108
|
+
if (!jsonLine) {
|
|
109
|
+
return {
|
|
110
|
+
decision: "defer",
|
|
111
|
+
reasoning: "Provider response did not contain a valid JSON decision",
|
|
112
|
+
provider: providerName,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const parsed = JSON.parse(jsonLine);
|
|
117
|
+
const decision = (["approve", "reject", "defer"].includes(parsed.decision) ? parsed.decision : "defer");
|
|
118
|
+
return { decision, reasoning: parsed.reasoning ?? "", provider: providerName };
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return { decision: "defer", reasoning: "Could not parse provider response", provider: providerName };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -37,7 +37,7 @@ All decision output written to `smartdocs/raw/`:
|
|
|
37
37
|
```text
|
|
38
38
|
01-load-queue ← read _triage-queue.json; count pending items; emit run-start telemetry
|
|
39
39
|
02-review-packets ← for each pending packet: read doc → evaluate → record decision
|
|
40
|
-
03-apply-decisions ← run polaris docs
|
|
40
|
+
03-apply-decisions ← run polaris docs promote for approved packets; run polaris doctrine deprecate for rejected packets
|
|
41
41
|
04-hand-off ← report decision summary (approved / rejected / deferred); emit triage-review-complete telemetry
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -52,8 +52,10 @@ All decision output written to `smartdocs/raw/`:
|
|
|
52
52
|
- Process all packets before moving to step 03 — do not apply decisions mid-loop
|
|
53
53
|
|
|
54
54
|
**Step 03:**
|
|
55
|
-
- If no packets were approved or rejected → skip
|
|
56
|
-
-
|
|
55
|
+
- If no packets were approved or rejected → skip, report counts only
|
|
56
|
+
- For approved packets: call `polaris docs promote <path>` for each (requires `docs-promote` skill authority if conflicts found)
|
|
57
|
+
- For rejected packets: call `polaris doctrine deprecate <path>` for each
|
|
58
|
+
- Error on apply → report the error; do not retry automatically
|
|
57
59
|
|
|
58
60
|
## Run ID format
|
|
59
61
|
|