robisie 0.1.0 → 0.1.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.
- package/CHANGELOG.md +69 -0
- package/README.md +76 -12
- package/dist/active-recovery.d.ts +58 -0
- package/dist/active-recovery.js +143 -0
- package/dist/agents.d.ts +34 -0
- package/dist/agents.js +77 -16
- package/dist/attempt-identity.d.ts +21 -0
- package/dist/attempt-identity.js +39 -0
- package/dist/ci-failure-result.d.ts +23 -0
- package/dist/ci-failure-result.js +90 -0
- package/dist/claude-headless.d.ts +7 -0
- package/dist/claude-headless.js +28 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +55 -15
- package/dist/commands.d.ts +18 -0
- package/dist/commands.js +190 -85
- package/dist/constants.d.ts +32 -0
- package/dist/constants.js +3 -3
- package/dist/context.d.ts +24 -0
- package/dist/context.js +22 -6
- package/dist/dependency-readiness.d.ts +13 -0
- package/dist/dependency-readiness.js +80 -0
- package/dist/dispatch-scope.d.ts +18 -0
- package/dist/dispatch-scope.js +46 -0
- package/dist/done-evidence.d.ts +31 -0
- package/dist/done-evidence.js +216 -0
- package/dist/engine-config.d.ts +65 -0
- package/dist/engine-config.js +139 -0
- package/dist/fsops.d.ts +87 -0
- package/dist/fsops.js +117 -24
- package/dist/helpers.d.ts +15 -0
- package/dist/helpers.js +262 -45
- package/dist/intent-rules/admission.d.ts +10 -0
- package/dist/intent-rules/admission.js +35 -0
- package/dist/intent-rules/done-when-roles.d.ts +21 -0
- package/dist/intent-rules/done-when-roles.js +101 -0
- package/dist/intent-rules/hooks.d.ts +29 -0
- package/dist/intent-rules/hooks.js +23 -0
- package/dist/k3-codex-role.d.ts +20 -0
- package/dist/k3-codex-role.js +68 -0
- package/dist/manifest.d.ts +41 -0
- package/dist/manifest.js +59 -14
- package/dist/mcp.d.ts +18 -0
- package/dist/mcp.js +89 -39
- package/dist/opencode-result.d.ts +19 -0
- package/dist/opencode-result.js +136 -0
- package/dist/opencode-review-guard.d.ts +1 -0
- package/dist/opencode-review-guard.js +19 -0
- package/dist/opencode-session-export.d.ts +5 -0
- package/dist/opencode-session-export.js +28 -0
- package/dist/outcome-moves.d.ts +15 -0
- package/dist/outcome-moves.js +39 -0
- package/dist/planer-client.d.ts +214 -0
- package/dist/planer-client.js +351 -0
- package/dist/restart-recovery.d.ts +47 -0
- package/dist/restart-recovery.js +70 -0
- package/dist/review-source-packet.d.ts +17 -0
- package/dist/review-source-packet.js +44 -0
- package/dist/role-registry.d.ts +19 -0
- package/dist/role-registry.js +83 -0
- package/dist/role-results.d.ts +15 -0
- package/dist/role-results.js +42 -0
- package/dist/roles.d.ts +30 -0
- package/dist/roles.js +20 -16
- package/dist/seed-validation.d.ts +28 -0
- package/dist/seed-validation.js +294 -0
- package/dist/selection-result.d.ts +44 -0
- package/dist/selection-result.js +144 -0
- package/dist/sparse-policy.d.ts +8 -0
- package/dist/sparse-policy.js +49 -0
- package/dist/tester-identity.d.ts +11 -0
- package/dist/tester-identity.js +53 -0
- package/dist/worker-env.d.ts +5 -0
- package/dist/worker-env.js +34 -0
- package/docs/ENGINE-API.md +78 -0
- package/docs/ENGINE-CONFIG.md +115 -0
- package/docs/README.en.md +24 -15
- package/docs/README.md +26 -15
- package/docs/RELEASE-0.1.0.md +81 -8
- package/docs/RELEASE-0.1.1.md +110 -0
- package/helpers/ceo-client-launcher-mcp.mjs +36 -8
- package/helpers/client-spawn-session.sh +181 -0
- package/helpers/client-spawn.sh +825 -0
- package/package.json +9 -4
- package/roles/ceo-client.md +3 -2
- package/roles/manager-builder-client.md +18 -4
- package/roles/quality-officer-client.md +20 -6
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function classifyStart(input) {
|
|
2
|
+
const expected = input.attempt ?? input.seq; // P1-e: brak attempt → stary seq
|
|
3
|
+
if (!nonempty(input.jobId))
|
|
4
|
+
return { ok: false, reason: "brak jobId" };
|
|
5
|
+
if (!positiveInteger(input.attempts) || input.attempts !== expected)
|
|
6
|
+
return { ok: false, reason: "próba się nie zgadza" };
|
|
7
|
+
const active = input.actives.find(a => a.taskId === input.cardId);
|
|
8
|
+
if (!active)
|
|
9
|
+
return { ok: false, reason: "brak w state.actives" };
|
|
10
|
+
if (active.jobId !== input.jobId || active.attempts !== expected) {
|
|
11
|
+
return { ok: false, reason: "state.actives nie zgadza się z próbą/jobId" };
|
|
12
|
+
}
|
|
13
|
+
const status = typeof input.helperStatus === "string" ? input.helperStatus.trim().toLowerCase() : "";
|
|
14
|
+
if (status !== "queued" && status !== "running" && status !== "completed") {
|
|
15
|
+
return { ok: false, reason: `helper ${status || "brak"}` };
|
|
16
|
+
}
|
|
17
|
+
return { ok: true };
|
|
18
|
+
}
|
|
19
|
+
export function nextAttempt(details) {
|
|
20
|
+
const worker = details?.maszynista;
|
|
21
|
+
if (!record(worker))
|
|
22
|
+
return 1;
|
|
23
|
+
const previous = worker.attempts ?? worker.attempt;
|
|
24
|
+
if (worker.outcome === "MODEL_AT_CAPACITY" && positiveInteger(previous))
|
|
25
|
+
return previous;
|
|
26
|
+
// Karta po blokadzie wrocila do ready reka czlowieka/CR -> nowa seria prob (inaczej 4/3 = blokada po 1 probie).
|
|
27
|
+
if (worker.move === "blocked")
|
|
28
|
+
return 1;
|
|
29
|
+
return positiveInteger(previous) ? previous + 1 : 1;
|
|
30
|
+
}
|
|
31
|
+
function record(value) {
|
|
32
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
function positiveInteger(value) {
|
|
35
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
36
|
+
}
|
|
37
|
+
function nonempty(value) {
|
|
38
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
39
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type CiJobSteps = {
|
|
2
|
+
name: string;
|
|
3
|
+
diskTrusted?: boolean;
|
|
4
|
+
evidenceMissing?: boolean;
|
|
5
|
+
interruptedTrusted?: "cancelled" | "timed_out";
|
|
6
|
+
steps: Array<{
|
|
7
|
+
name: string;
|
|
8
|
+
conclusion: string | null;
|
|
9
|
+
}>;
|
|
10
|
+
};
|
|
11
|
+
export declare function failedTestFiles(raw: string): string[];
|
|
12
|
+
export declare function normalizeCiLogTail(raw: string, maxLines?: number, maxChars?: number): string;
|
|
13
|
+
export declare function formatCiFailNote(failed: Array<{
|
|
14
|
+
name: string;
|
|
15
|
+
conclusion: string | null;
|
|
16
|
+
}>, jobs: CiJobSteps[], tails: Array<{
|
|
17
|
+
runId: string;
|
|
18
|
+
tail: string;
|
|
19
|
+
}>, maxChars?: number): string;
|
|
20
|
+
export declare const CI_DISK_HOLD_STEP = "Robisie disk HOLD v1";
|
|
21
|
+
export declare const CI_DISK_HOLD_JOBS: string[];
|
|
22
|
+
export declare function hasCiDiskHold(job: CiJobSteps): boolean;
|
|
23
|
+
export declare function classifyCiFailure(failedJobs: CiJobSteps[]): "disk" | "host" | "infra" | "code" | "unknown";
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export function failedTestFiles(raw) {
|
|
2
|
+
const clean = raw.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
|
|
3
|
+
return [...new Set(Array.from(clean.matchAll(/(?:^|[\t ])FAIL[\t ]+([^\s]+\.test\.(?:ts|tsx|mts|mjs|js))[\t ]+(?:>|\[)/gm), match => match[1]))];
|
|
4
|
+
}
|
|
5
|
+
export function normalizeCiLogTail(raw, maxLines = 40, maxChars = 1000) {
|
|
6
|
+
const lines = raw.split(/\r?\n/).map((line) => line
|
|
7
|
+
.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "")
|
|
8
|
+
.replace(/^[^\t]*\t[^\t]*\t/, "")
|
|
9
|
+
.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z\s?/, "")
|
|
10
|
+
.trimEnd()).filter((line) => line.trim().length > 0);
|
|
11
|
+
const tail = lines.slice(-maxLines).join("\n");
|
|
12
|
+
return tail.length > maxChars ? `…${tail.slice(-(maxChars - 1))}` : tail;
|
|
13
|
+
}
|
|
14
|
+
export function formatCiFailNote(failed, jobs, tails, maxChars = 1400) {
|
|
15
|
+
const header = `CI: ${failed.map((run) => `${run.name}: ${run.conclusion}`).join(", ")}`;
|
|
16
|
+
const steps = jobs.flatMap((job) => {
|
|
17
|
+
const failedSteps = job.steps.filter((step) => step.conclusion === "failure");
|
|
18
|
+
return failedSteps.length > 0 ? failedSteps.map((step) => `${job.name} › ${step.name}`) : [job.name];
|
|
19
|
+
});
|
|
20
|
+
const marker = "…(ucięto)";
|
|
21
|
+
if (header.length >= maxChars)
|
|
22
|
+
return header.slice(0, maxChars);
|
|
23
|
+
let note = header;
|
|
24
|
+
const nonEmptyTails = tails.filter(({ tail }) => tail.length > 0);
|
|
25
|
+
const firstTail = nonEmptyTails[0];
|
|
26
|
+
const firstBlock = firstTail ? `\n--- ogon logu run ${firstTail.runId} ---\n${firstTail.tail}` : "";
|
|
27
|
+
const tailReserve = Math.min(firstBlock.length, 500) + (nonEmptyTails.length > 1 ? marker.length : 0);
|
|
28
|
+
if (steps.length > 0) {
|
|
29
|
+
const stepLine = `\nPadłe: ${steps.join(", ")}`;
|
|
30
|
+
const stepBudget = maxChars - note.length - tailReserve;
|
|
31
|
+
const stepMarker = firstTail ? "…(ucięto kroki)" : marker;
|
|
32
|
+
if (stepLine.length <= stepBudget)
|
|
33
|
+
note += stepLine;
|
|
34
|
+
else if (stepBudget >= stepMarker.length)
|
|
35
|
+
note += `${stepLine.slice(0, stepBudget - stepMarker.length)}${stepMarker}`;
|
|
36
|
+
}
|
|
37
|
+
for (const [index, { runId, tail }] of nonEmptyTails.entries()) {
|
|
38
|
+
const blockHeader = `\n--- ogon logu run ${runId} ---\n`;
|
|
39
|
+
const block = `${blockHeader}${tail}`;
|
|
40
|
+
if (note.length + block.length > maxChars) {
|
|
41
|
+
if (index === 0) {
|
|
42
|
+
const omittedMarker = nonEmptyTails.length > 1 ? marker : "";
|
|
43
|
+
const tailRoom = maxChars - note.length - blockHeader.length - omittedMarker.length;
|
|
44
|
+
if (tailRoom > 1) {
|
|
45
|
+
note += `${blockHeader}…${tail.slice(-(tailRoom - 1))}${omittedMarker}`;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (note.length + marker.length <= maxChars)
|
|
50
|
+
note += marker;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
note += block;
|
|
54
|
+
}
|
|
55
|
+
return note;
|
|
56
|
+
}
|
|
57
|
+
export const CI_DISK_HOLD_STEP = "Robisie disk HOLD v1";
|
|
58
|
+
export const CI_DISK_HOLD_JOBS = ["Verify (lint + build:check)", "Secret scan (gitleaks)", "check-agent-writes (ROB brief narrative discipline)", "check-postmortem-required (ROB_099 — bugfix postmortem gate)", "Deploy (wrangler → Cloudflare Workers)"];
|
|
59
|
+
export function hasCiDiskHold(job) {
|
|
60
|
+
return job.diskTrusted === true && CI_DISK_HOLD_JOBS.includes(job.name)
|
|
61
|
+
&& job.steps.some(step => step.name === CI_DISK_HOLD_STEP && step.conclusion === "failure");
|
|
62
|
+
}
|
|
63
|
+
export function classifyCiFailure(failedJobs) {
|
|
64
|
+
if (failedJobs.length === 0)
|
|
65
|
+
return "unknown";
|
|
66
|
+
const hostStep = /^(Set up runner|Complete runner|Set up job|Complete job)$/;
|
|
67
|
+
let sawFailedStep = false;
|
|
68
|
+
let sawNotStartedJob = false;
|
|
69
|
+
let sawDisk = false;
|
|
70
|
+
const diskPresent = failedJobs.some(hasCiDiskHold);
|
|
71
|
+
for (const job of failedJobs) {
|
|
72
|
+
if (job.evidenceMissing && job.interruptedTrusted && !diskPresent)
|
|
73
|
+
continue;
|
|
74
|
+
if (job.steps.length === 0 && !job.evidenceMissing)
|
|
75
|
+
sawNotStartedJob = true;
|
|
76
|
+
const failed = job.steps.filter((st) => st.conclusion === "failure");
|
|
77
|
+
if (failed.length === 0)
|
|
78
|
+
continue;
|
|
79
|
+
sawFailedStep = true;
|
|
80
|
+
const disk = hasCiDiskHold(job);
|
|
81
|
+
if (!failed.every((st) => hostStep.test(st.name) || disk && st.name === CI_DISK_HOLD_STEP))
|
|
82
|
+
return "code";
|
|
83
|
+
sawDisk ||= disk;
|
|
84
|
+
}
|
|
85
|
+
if (sawDisk)
|
|
86
|
+
return failedJobs.some(job => !job.interruptedTrusted && (job.evidenceMissing || job.steps.length > 0 && !job.steps.some(step => step.conclusion === "failure"))) ? "unknown" : "disk";
|
|
87
|
+
if (sawNotStartedJob)
|
|
88
|
+
return "infra";
|
|
89
|
+
return sawFailedStep ? "host" : "unknown";
|
|
90
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const HEADLESS_SYSTEM_PROMPT = "Jeste\u015B bezstanowym modu\u0142em fabryki Robisie wywo\u0142ywanym przez skrypt. Nie masz narz\u0119dzi ani dost\u0119pu do repozytorium, plik\u00F3w, tablicy ani historii \u2014 opierasz si\u0119 wy\u0142\u0105cznie na tre\u015Bci tej wiadomo\u015Bci. Odpowiadasz dok\u0142adnie w formacie, kt\u00F3rego \u017C\u0105da wiadomo\u015B\u0107.";
|
|
2
|
+
export declare const HEADLESS_CLAUDE_ENV: {
|
|
3
|
+
readonly CLAUDE_CODE_DISABLE_ATTACHMENTS: "1";
|
|
4
|
+
};
|
|
5
|
+
export declare function headlessClaudeArgs(model: string): string[];
|
|
6
|
+
/** Tokeny wejścia z koperty `--output-format json`; null, gdy koperta nie ma liczbowego `usage`. */
|
|
7
|
+
export declare function envelopeInputTokens(envelope: unknown): number | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Jedno źródło argumentów `claude -p` dla automatu (selektor, agent zasiewu).
|
|
2
|
+
// Po co (Reksio-364, 2026-09-22, zmierzone): `--allowedTools ""` NIE odbiera narzędzi, a
|
|
3
|
+
// `--exclude-dynamic-system-prompt-sections` PRZENOSI `git status` do pierwszej wiadomości zamiast go
|
|
4
|
+
// usuwać; z cwd = repo model dostawał też personę Reksia (CLAUDE.local.md) i hooki SessionStart —
|
|
5
|
+
// 44 562 tokeny wejścia i 12 narzędzi. Z listą poniżej: 0 narzędzi, 0 instrukcji, 0 hooków, 1 234 tokeny.
|
|
6
|
+
// `--bare` odpada: wymaga ANTHROPIC_API_KEY, a fabryka działa na OAuth (plan Max).
|
|
7
|
+
export const HEADLESS_SYSTEM_PROMPT = "Jesteś bezstanowym modułem fabryki Robisie wywoływanym przez skrypt. Nie masz narzędzi ani dostępu do repozytorium, plików, tablicy ani historii — opierasz się wyłącznie na treści tej wiadomości. Odpowiadasz dokładnie w formacie, którego żąda wiadomość.";
|
|
8
|
+
// --tools "" does not stop CLI-side @file expansion before the model sees the prompt.
|
|
9
|
+
export const HEADLESS_CLAUDE_ENV = { CLAUDE_CODE_DISABLE_ATTACHMENTS: "1" };
|
|
10
|
+
export function headlessClaudeArgs(model) {
|
|
11
|
+
return [
|
|
12
|
+
"-p", "--model", model, "--disable-slash-commands",
|
|
13
|
+
"--tools", "", "--setting-sources", "", "--strict-mcp-config",
|
|
14
|
+
"--system-prompt", HEADLESS_SYSTEM_PROMPT, "--output-format", "json",
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
const TOKEN_FIELDS = ["input_tokens", "cache_creation_input_tokens", "cache_read_input_tokens"];
|
|
18
|
+
/** Tokeny wejścia z koperty `--output-format json`; null, gdy koperta nie ma liczbowego `usage`. */
|
|
19
|
+
export function envelopeInputTokens(envelope) {
|
|
20
|
+
if (typeof envelope !== "object" || envelope === null)
|
|
21
|
+
return null;
|
|
22
|
+
const usage = envelope.usage;
|
|
23
|
+
if (typeof usage !== "object" || usage === null)
|
|
24
|
+
return null;
|
|
25
|
+
const values = TOKEN_FIELDS.map((field) => usage[field])
|
|
26
|
+
.filter((value) => typeof value === "number");
|
|
27
|
+
return values.length === 0 ? null : values.reduce((sum, value) => sum + value, 0);
|
|
28
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type AgentId } from "./agents.js";
|
|
3
|
+
import { type Ctx } from "./context.js";
|
|
4
|
+
import { type RoleProviders } from "./manifest.js";
|
|
5
|
+
export interface ParsedArgs {
|
|
6
|
+
command: string;
|
|
7
|
+
help: boolean;
|
|
8
|
+
/** Positional argument (`robisie mcp <name>`). */
|
|
9
|
+
target: string | null;
|
|
10
|
+
agents: AgentId[] | null;
|
|
11
|
+
roles: Partial<RoleProviders>;
|
|
12
|
+
shell: boolean;
|
|
13
|
+
keyStdin: boolean;
|
|
14
|
+
engineConfig: string | null;
|
|
15
|
+
}
|
|
16
|
+
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
17
|
+
export declare function run(argv: readonly string[], ctx: Ctx): Promise<number>;
|
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
4
|
import { AGENT_IDS, isAgentId } from "./agents.js";
|
|
5
|
-
import { doctor, init, uninstall } from "./commands.js";
|
|
5
|
+
import { doctor, init, recoveryStep, uninstall } from "./commands.js";
|
|
6
6
|
import { HELPER_SERVERS, PACKAGE_VERSION } from "./constants.js";
|
|
7
7
|
import { RobisieError, errorMessage, nodeCtx } from "./context.js";
|
|
8
|
-
import { isHelperServer, runHelper } from "./helpers.js";
|
|
8
|
+
import { helperPath, isHelperServer, runHelper } from "./helpers.js";
|
|
9
9
|
import { ROLE_IDS, ROLE_STEP } from "./manifest.js";
|
|
10
|
+
import { doctorEngineConfig } from "./role-registry.js";
|
|
10
11
|
const HELP = `robisie ${PACKAGE_VERSION} — łączy Twojego agenta z tablicą robisie
|
|
11
12
|
|
|
12
13
|
npx robisie init klucz rob_… (z ${"ROBISIE_KEY"} albo z pytania) + konfiguracja MCP + role klienta
|
|
@@ -15,6 +16,10 @@ const HELP = `robisie ${PACKAGE_VERSION} — łączy Twojego agenta z tablicą r
|
|
|
15
16
|
npx robisie mcp <${HELPER_SERVERS.join("|")}>
|
|
16
17
|
pomocnik ról (serwer MCP stdio) — uruchamia go agent, nie Ty
|
|
17
18
|
|
|
19
|
+
Opcje doctor:
|
|
20
|
+
--engine-config <path> lokalnie i offline sprawdza K2 i rejestr wykonania czterech ról K7
|
|
21
|
+
--engine-config=<path> ta sama składnia; nie sprawdza uruchomienia dostawców
|
|
22
|
+
|
|
18
23
|
Opcje init:
|
|
19
24
|
--agent <${AGENT_IDS.join("|")}> wybrany dostawca (można powtórzyć albo podać po przecinku); razem z dostawcami mapy, bez --agent również wykryci
|
|
20
25
|
--role <rola=dostawca> wybór ceo, builder, quality → claude, codex, opencode (można powtórzyć)
|
|
@@ -29,13 +34,30 @@ export function parseArgs(argv) {
|
|
|
29
34
|
const roles = {};
|
|
30
35
|
let shell = true;
|
|
31
36
|
let keyStdin = false;
|
|
37
|
+
let engineConfig = null;
|
|
32
38
|
let target = null;
|
|
33
39
|
// Help wins over everything else, so `init --help` never starts an install.
|
|
34
|
-
if (
|
|
35
|
-
return { command, help: true, target, agents: null, roles, shell, keyStdin };
|
|
40
|
+
if (argv.includes("--help") || argv.includes("-h"))
|
|
41
|
+
return { command, help: true, target, agents: null, roles, shell, keyStdin, engineConfig };
|
|
36
42
|
while (args.length > 0) {
|
|
37
43
|
const arg = args.shift();
|
|
38
|
-
if (arg === "--no-shell"
|
|
44
|
+
if (command !== "init" && (arg === "--no-shell" || arg === "--key-stdin" ||
|
|
45
|
+
arg === "--agent" || arg.startsWith("--agent=") ||
|
|
46
|
+
arg === "--role" || arg.startsWith("--role="))) {
|
|
47
|
+
throw new RobisieError(`Opcja „${arg.split("=")[0]}” jest dostępna tylko dla polecenia init.`, "Zobacz `npx robisie help`.");
|
|
48
|
+
}
|
|
49
|
+
if (arg === "--engine-config" || arg.startsWith("--engine-config=")) {
|
|
50
|
+
const step = "Użyj `npx robisie doctor --engine-config <path>` z jedną niepustą ścieżką pliku.";
|
|
51
|
+
if (command !== "doctor")
|
|
52
|
+
throw new RobisieError("Opcja --engine-config jest dostępna tylko dla polecenia doctor.", step);
|
|
53
|
+
if (engineConfig !== null)
|
|
54
|
+
throw new RobisieError("Opcję --engine-config można podać tylko raz.", step);
|
|
55
|
+
const value = arg === "--engine-config" ? args.shift() ?? "" : arg.slice("--engine-config=".length);
|
|
56
|
+
if (!value.trim() || value.startsWith("-"))
|
|
57
|
+
throw new RobisieError("Opcja --engine-config wymaga ścieżki pliku.", step);
|
|
58
|
+
engineConfig = value;
|
|
59
|
+
}
|
|
60
|
+
else if (arg === "--no-shell")
|
|
39
61
|
shell = false;
|
|
40
62
|
else if (arg === "--key-stdin")
|
|
41
63
|
keyStdin = true;
|
|
@@ -45,9 +67,9 @@ export function parseArgs(argv) {
|
|
|
45
67
|
throw new RobisieError("Opcja --role wymaga przypisania rola=dostawca.", ROLE_STEP);
|
|
46
68
|
const [role, provider, extra] = value.split("=");
|
|
47
69
|
if (!ROLE_IDS.some((id) => id === role))
|
|
48
|
-
throw new RobisieError(
|
|
70
|
+
throw new RobisieError("Nieznana rola (--role).", ROLE_STEP);
|
|
49
71
|
if (extra !== undefined || !provider || !isAgentId(provider))
|
|
50
|
-
throw new RobisieError(
|
|
72
|
+
throw new RobisieError("Niepoprawny dostawca lub przypisanie (--role).", ROLE_STEP);
|
|
51
73
|
const id = role;
|
|
52
74
|
if (roles[id] && roles[id] !== provider)
|
|
53
75
|
throw new RobisieError(`Sprzeczne przypisania roli „${role}”: ${roles[id]} i ${provider}.`, ROLE_STEP);
|
|
@@ -58,11 +80,11 @@ export function parseArgs(argv) {
|
|
|
58
80
|
const ids = value.split(",").map((v) => v.trim());
|
|
59
81
|
// An empty value must not silently fall back to "every detected agent".
|
|
60
82
|
if (ids.some((id) => id === "") || value.startsWith("--")) {
|
|
61
|
-
throw new RobisieError("Opcja --agent wymaga nazwy agenta.",
|
|
83
|
+
throw new RobisieError("Opcja --agent wymaga nazwy agenta.", ROLE_STEP);
|
|
62
84
|
}
|
|
63
85
|
for (const id of ids) {
|
|
64
86
|
if (!isAgentId(id))
|
|
65
|
-
throw new RobisieError(
|
|
87
|
+
throw new RobisieError("Nieznany agent (--agent).", ROLE_STEP);
|
|
66
88
|
if (!agents.includes(id))
|
|
67
89
|
agents.push(id);
|
|
68
90
|
}
|
|
@@ -70,9 +92,9 @@ export function parseArgs(argv) {
|
|
|
70
92
|
else if (command === "mcp" && target === null && !arg.startsWith("-"))
|
|
71
93
|
target = arg;
|
|
72
94
|
else
|
|
73
|
-
throw new RobisieError(
|
|
95
|
+
throw new RobisieError("Nieznana opcja argumentów CLI.", "Zobacz `npx robisie help`.");
|
|
74
96
|
}
|
|
75
|
-
return { command, help: false, target, agents: agents.length > 0 ? agents : null, roles, shell, keyStdin };
|
|
97
|
+
return { command, help: false, target, agents: agents.length > 0 ? agents : null, roles, shell, keyStdin, engineConfig };
|
|
76
98
|
}
|
|
77
99
|
export async function run(argv, ctx) {
|
|
78
100
|
try {
|
|
@@ -86,12 +108,30 @@ export async function run(argv, ctx) {
|
|
|
86
108
|
await init(ctx, { agents: args.agents, roles: args.roles, shell: args.shell, keyStdin: args.keyStdin });
|
|
87
109
|
return 0;
|
|
88
110
|
case "doctor":
|
|
111
|
+
if (args.engineConfig !== null)
|
|
112
|
+
return doctorEngineConfig(ctx, args.engineConfig);
|
|
113
|
+
if (ctx.env.ROBISIE_KEY) {
|
|
114
|
+
try {
|
|
115
|
+
const helper = await import(pathToFileURL(helperPath("client-launch-context")).href);
|
|
116
|
+
if (!helper || typeof helper !== "object" || !("resolveProjectContext" in helper)
|
|
117
|
+
|| typeof helper.resolveProjectContext !== "function")
|
|
118
|
+
throw new Error("Invalid project-context export");
|
|
119
|
+
const result = helper.resolveProjectContext(ctx.env, ctx.cwd);
|
|
120
|
+
if (!result || typeof result !== "object" || !("projectContext" in result)
|
|
121
|
+
|| typeof result.projectContext !== "string" || !("plannerAccess" in result)
|
|
122
|
+
|| result.plannerAccess !== "configured")
|
|
123
|
+
throw new Error("Invalid project-context result");
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
throw new RobisieError("Nie mogę potwierdzić kontekstu projektu używanego przez pomocnik klienta.", "W katalogu projektu wykonaj `unset ROBISIE_PROJECT_CONTEXT`, potem `npx robisie doctor`. Albo ustaw istniejący absolutny rzeczywisty katalog: `export ROBISIE_PROJECT_CONTEXT='/absolutna/sciezka/do/projektu'` (zastąp ścieżkę katalogiem projektu), potem `npx robisie doctor`.");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
89
129
|
return await doctor(ctx);
|
|
90
130
|
case "uninstall":
|
|
91
131
|
return uninstall(ctx);
|
|
92
132
|
case "mcp":
|
|
93
133
|
if (!args.target || !isHelperServer(args.target)) {
|
|
94
|
-
throw new RobisieError(
|
|
134
|
+
throw new RobisieError("Nieznany pomocnik (mcp).", `Użyj jednego z: ${HELPER_SERVERS.join(", ")}.`);
|
|
95
135
|
}
|
|
96
136
|
return await runHelper(args.target);
|
|
97
137
|
case "--version":
|
|
@@ -104,13 +144,13 @@ export async function run(argv, ctx) {
|
|
|
104
144
|
ctx.out(HELP);
|
|
105
145
|
return 0;
|
|
106
146
|
default:
|
|
107
|
-
throw new RobisieError(
|
|
147
|
+
throw new RobisieError("Nieznane polecenie CLI.", "Zobacz `npx robisie help`.");
|
|
108
148
|
}
|
|
109
149
|
}
|
|
110
150
|
catch (error) {
|
|
111
151
|
if (error instanceof RobisieError) {
|
|
112
152
|
ctx.err(`✗ ${error.message}`);
|
|
113
|
-
ctx.err(`Następny krok: ${error.nextStep}`);
|
|
153
|
+
ctx.err(`Następny krok: ${recoveryStep(ctx, error.nextStep)}`);
|
|
114
154
|
}
|
|
115
155
|
else {
|
|
116
156
|
ctx.err(`✗ Coś poszło nie tak: ${errorMessage(error)}`);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type AgentId } from "./agents.js";
|
|
2
|
+
import { type Ctx } from "./context.js";
|
|
3
|
+
import { type RoleProviders } from "./manifest.js";
|
|
4
|
+
export interface InitOptions {
|
|
5
|
+
agents: AgentId[] | null;
|
|
6
|
+
roles?: Partial<RoleProviders>;
|
|
7
|
+
shell: boolean;
|
|
8
|
+
/** Read the key from piped stdin — only on request, so an open non-TTY stdin can never hang init. */
|
|
9
|
+
keyStdin: boolean;
|
|
10
|
+
rolesDir?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Advice chooses an executable CLI, not merely a leftover configuration directory. */
|
|
13
|
+
export declare function initRecoveryStep(ctx: Ctx, preferred?: AgentId): string;
|
|
14
|
+
export declare function recoveryStep(ctx: Ctx, step: string): string;
|
|
15
|
+
export declare function init(ctx: Ctx, options: InitOptions): Promise<void>;
|
|
16
|
+
/** Returns the process exit code: 0 = all good, 1 = something needs one action. */
|
|
17
|
+
export declare function doctor(ctx: Ctx): Promise<number>;
|
|
18
|
+
export declare function uninstall(ctx: Ctx): number;
|