@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.
@@ -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 ingest to apply approved/rejected decisions after review is complete",
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 node_fs_1 = require("node:fs");
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 docContent = "";
7
+ let config = null;
7
8
  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
- }
9
+ config = (0, loader_js_1.loadConfig)(process.cwd());
11
10
  }
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"))
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 (text.startsWith("reject"))
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 ingest to apply approved/rejected packets
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 ingest, report counts only
56
- - Ingest error report the error; do not retry automatically
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lsctech/polaris",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Polaris — standalone taskchain orchestration framework for governed AI agent workflows",
5
5
  "bin": {
6
6
  "polaris-cli": "dist/cli/index.js"