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,144 @@
|
|
|
1
|
+
function record(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
3
|
+
? value
|
|
4
|
+
: null;
|
|
5
|
+
}
|
|
6
|
+
export function stripFence(text) {
|
|
7
|
+
let s = text.trim();
|
|
8
|
+
const lead = s.match(/^```(?:[a-zA-Z0-9]+)?\n?/);
|
|
9
|
+
if (lead)
|
|
10
|
+
s = s.slice(lead[0].length);
|
|
11
|
+
const trail = s.match(/\n?```\s*$/);
|
|
12
|
+
if (trail)
|
|
13
|
+
s = s.slice(0, s.length - trail[0].length);
|
|
14
|
+
return s.trim();
|
|
15
|
+
}
|
|
16
|
+
export function extractJsonObject(raw) {
|
|
17
|
+
const stripped = stripFence(raw);
|
|
18
|
+
try {
|
|
19
|
+
JSON.parse(stripped);
|
|
20
|
+
return stripped;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Odpowiedź może zawierać prozę przed właściwym obiektem.
|
|
24
|
+
}
|
|
25
|
+
const starts = [];
|
|
26
|
+
const marker = /\{\s*"proposals"\s*:/g;
|
|
27
|
+
for (let match = marker.exec(stripped); match; match = marker.exec(stripped))
|
|
28
|
+
starts.push(match.index);
|
|
29
|
+
for (let candidateIndex = starts.length - 1; candidateIndex >= 0; candidateIndex -= 1) {
|
|
30
|
+
const start = starts[candidateIndex];
|
|
31
|
+
let depth = 0;
|
|
32
|
+
let inString = false;
|
|
33
|
+
let escaped = false;
|
|
34
|
+
for (let index = start; index < stripped.length; index += 1) {
|
|
35
|
+
const char = stripped[index];
|
|
36
|
+
if (inString) {
|
|
37
|
+
if (escaped)
|
|
38
|
+
escaped = false;
|
|
39
|
+
else if (char === "\\")
|
|
40
|
+
escaped = true;
|
|
41
|
+
else if (char === '"')
|
|
42
|
+
inString = false;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (char === '"') {
|
|
46
|
+
inString = true;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (char === "{")
|
|
50
|
+
depth += 1;
|
|
51
|
+
else if (char === "}")
|
|
52
|
+
depth -= 1;
|
|
53
|
+
if (depth !== 0)
|
|
54
|
+
continue;
|
|
55
|
+
const candidate = stripped.slice(start, index + 1);
|
|
56
|
+
try {
|
|
57
|
+
const parsed = record(JSON.parse(candidate));
|
|
58
|
+
if (parsed && Array.isArray(parsed["proposals"]))
|
|
59
|
+
return candidate;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
export function parseProposals(raw, goals, nsIds = [], qualifyCard) {
|
|
70
|
+
const jsonText = extractJsonObject(raw);
|
|
71
|
+
if (jsonText === null)
|
|
72
|
+
return { ok: [], rejected: [], warnings: [], rawCount: 0, prose: raw.trim(), error: "nie-JSON: brak obiektu proposals" };
|
|
73
|
+
const stripped = stripFence(raw);
|
|
74
|
+
const proseIndex = stripped.lastIndexOf(jsonText);
|
|
75
|
+
const prose = proseIndex > 0 ? stripped.slice(0, proseIndex).trim() : "";
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = JSON.parse(jsonText);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return { ok: [], rejected: [], warnings: [], rawCount: 0, prose, error: `nie-JSON: ${error instanceof Error ? error.message : String(error)}` };
|
|
82
|
+
}
|
|
83
|
+
const parsedRecord = record(parsed);
|
|
84
|
+
const arr = parsedRecord?.["proposals"];
|
|
85
|
+
if (!Array.isArray(arr))
|
|
86
|
+
return { ok: [], rejected: [], warnings: [], rawCount: 0, prose, error: "brak pola proposals[]" };
|
|
87
|
+
const emptyReason = typeof parsedRecord?.["empty_reason"] === "string" ? parsedRecord["empty_reason"].trim() : "";
|
|
88
|
+
if (arr.length === 0 && !emptyReason && !prose)
|
|
89
|
+
return { ok: [], rejected: [], warnings: [], rawCount: 0, prose, error: "brak empty_reason dla zera" };
|
|
90
|
+
const canon = new Set(goals.map((g) => g.canon));
|
|
91
|
+
const nsCanon = new Set(nsIds.map((id) => id.trim().toUpperCase()).filter(Boolean));
|
|
92
|
+
const ok = [];
|
|
93
|
+
const rejected = [];
|
|
94
|
+
const warnings = [];
|
|
95
|
+
for (const it of arr) {
|
|
96
|
+
if (typeof it !== "object" || it === null || Array.isArray(it)) {
|
|
97
|
+
rejected.push({ title: "", reason: "propozycja nie jest obiektem" });
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const p = it;
|
|
101
|
+
const title = String(p.title ?? "").trim();
|
|
102
|
+
if (title.length < 10) {
|
|
103
|
+
rejected.push({ title, reason: "tytuł < 10 znaków" });
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (!canon.has(String(p.servesGoal))) {
|
|
107
|
+
rejected.push({ title, reason: `servesGoal "${String(p.servesGoal)}" poza kanonem ${[...canon].join("|")}` });
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const goalRef = String(p.goal_ref ?? "").trim();
|
|
111
|
+
if (!goalRef) {
|
|
112
|
+
rejected.push({ title, reason: "brak goal_ref" });
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const dw = Array.isArray(p.done_when) ? p.done_when.map(String).filter((s) => s.trim().length > 5) : [];
|
|
116
|
+
if (dw.length === 0) {
|
|
117
|
+
rejected.push({ title, reason: "brak mierzalnego done_when" });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const scope = Array.isArray(p.scope) ? p.scope.map(String) : [];
|
|
121
|
+
const qualification = qualifyCard({ id: "selector-proposal", title, details: { scope } });
|
|
122
|
+
if (!qualification.effort || !qualification.trackName) {
|
|
123
|
+
rejected.push({ title, reason: qualification.reasons.join("; ") });
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const requestedNS = typeof p.servesNS === "string" ? p.servesNS.trim().toUpperCase() : "";
|
|
127
|
+
const servesNS = requestedNS && nsCanon.has(requestedNS) ? requestedNS : null;
|
|
128
|
+
if (servesNS === null) {
|
|
129
|
+
rejected.push({ title, reason: "brak servesNS z kanonu North Star" });
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const nsProof = typeof p.ns_proof === "string" ? p.ns_proof.trim() : "";
|
|
133
|
+
if (!nsProof) {
|
|
134
|
+
rejected.push({ title, reason: "brak ns_proof" });
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
ok.push({
|
|
138
|
+
title, servesGoal: String(p.servesGoal), servesNS, ns_proof: nsProof, goal_ref: goalRef, why_now: String(p.why_now ?? "").trim(),
|
|
139
|
+
done_when: dw, scope, out: Array.isArray(p.out) ? p.out.map(String) : [],
|
|
140
|
+
codex_effort: qualification.effort, proposed_track: qualification.trackName, qualification,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return { ok, rejected, warnings, rawCount: arr.length, prose, ...(emptyReason ? { empty_reason: emptyReason } : {}) };
|
|
144
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Null oznacza nieczytelny/pusty scope i zachowanie pełnego checkoutu. Pusta tablica = tylko pliki korzeniowe. */
|
|
2
|
+
export declare function sparseDirectoriesFromScope(scope: unknown): string[] | null;
|
|
3
|
+
/** Odczyt całego HEAD nie rezerwuje zakresu zapisu karty. */
|
|
4
|
+
export declare const SPARSE_EXCLUDED_DIRS: readonly [];
|
|
5
|
+
export declare function sparseReadSet(scope: unknown, topLevelDirectories: readonly string[]): string[] | null;
|
|
6
|
+
export declare function sparseDirectoryError(directory: string): string;
|
|
7
|
+
/** Chroni przed plikiem-widmem utworzonym w katalogu, którego karta nie materializuje. */
|
|
8
|
+
export declare function sparseScopeWriteError(scope: unknown, untrackedPaths: readonly string[]): string | null;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
/** Null oznacza nieczytelny/pusty scope i zachowanie pełnego checkoutu. Pusta tablica = tylko pliki korzeniowe. */
|
|
3
|
+
export function sparseDirectoriesFromScope(scope) {
|
|
4
|
+
if (!Array.isArray(scope) || scope.length === 0 || scope.some((entry) => typeof entry !== "string"))
|
|
5
|
+
return null;
|
|
6
|
+
const directories = new Set();
|
|
7
|
+
for (const entry of scope) {
|
|
8
|
+
const token = entry.trim().split(/\s+/, 1)[0].replace(/`/g, "").replace(/:\d+(?:[-:]\d+)*$/, "");
|
|
9
|
+
if (!token || path.posix.isAbsolute(token))
|
|
10
|
+
return null;
|
|
11
|
+
const normalized = path.posix.normalize(token);
|
|
12
|
+
if (normalized === "." || normalized === ".." || normalized.startsWith("../"))
|
|
13
|
+
return null;
|
|
14
|
+
if (token.includes("/"))
|
|
15
|
+
directories.add(normalized.split("/", 1)[0]);
|
|
16
|
+
}
|
|
17
|
+
return [...directories].sort();
|
|
18
|
+
}
|
|
19
|
+
/** Odczyt całego HEAD nie rezerwuje zakresu zapisu karty. */
|
|
20
|
+
export const SPARSE_EXCLUDED_DIRS = [];
|
|
21
|
+
export function sparseReadSet(scope, topLevelDirectories) {
|
|
22
|
+
const scopedDirectories = sparseDirectoriesFromScope(scope);
|
|
23
|
+
if (scopedDirectories === null)
|
|
24
|
+
return null;
|
|
25
|
+
const excluded = new Set(SPARSE_EXCLUDED_DIRS);
|
|
26
|
+
const readSet = new Set(topLevelDirectories.filter((directory) => directory && !excluded.has(directory)));
|
|
27
|
+
for (const directory of scopedDirectories)
|
|
28
|
+
readSet.add(directory);
|
|
29
|
+
return [...readSet].sort();
|
|
30
|
+
}
|
|
31
|
+
export function sparseDirectoryError(directory) {
|
|
32
|
+
return `katalog \`${directory}/\` poza sparse checkout — dopisz go do \`details.scope\` karty`;
|
|
33
|
+
}
|
|
34
|
+
/** Chroni przed plikiem-widmem utworzonym w katalogu, którego karta nie materializuje. */
|
|
35
|
+
export function sparseScopeWriteError(scope, untrackedPaths) {
|
|
36
|
+
const allowed = sparseDirectoriesFromScope(scope);
|
|
37
|
+
if (allowed === null)
|
|
38
|
+
return null;
|
|
39
|
+
const allowedSet = new Set(allowed);
|
|
40
|
+
for (const candidate of untrackedPaths) {
|
|
41
|
+
const normalized = path.posix.normalize(candidate);
|
|
42
|
+
if (path.posix.isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../"))
|
|
43
|
+
continue;
|
|
44
|
+
const segments = normalized.split("/");
|
|
45
|
+
if (segments.length > 1 && !allowedSet.has(segments[0]))
|
|
46
|
+
return sparseDirectoryError(segments[0]);
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type CardLike = {
|
|
2
|
+
id: string;
|
|
3
|
+
details?: Record<string, unknown> | null;
|
|
4
|
+
};
|
|
5
|
+
export declare const TESTER_IDENTITY_DETAILS_KEYS: readonly ["landed", "land_receipt", "decision_queue"];
|
|
6
|
+
export declare const TESTER_IDENTITY_EVENT_FIELDS: readonly ["card_id", "event_type", "proof"];
|
|
7
|
+
/** Recover only a unique receipt or a card-labelled commit already on main. No fetch. */
|
|
8
|
+
export declare function resolveTesterLandedSha(card: CardLike, git: (args: string[]) => string): string | null;
|
|
9
|
+
export declare function validTestSha(value: unknown): value is string;
|
|
10
|
+
export declare function landedSha(card: CardLike): string | null;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const TESTER_IDENTITY_DETAILS_KEYS = ["landed", "land_receipt", "decision_queue"];
|
|
2
|
+
export const TESTER_IDENTITY_EVENT_FIELDS = ["card_id", "event_type", "proof"];
|
|
3
|
+
const recordValue = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
4
|
+
/** Recover only a unique receipt or a card-labelled commit already on main. No fetch. */
|
|
5
|
+
export function resolveTesterLandedSha(card, git) {
|
|
6
|
+
const existing = landedSha(card);
|
|
7
|
+
if (existing)
|
|
8
|
+
return existing;
|
|
9
|
+
try {
|
|
10
|
+
const details = card.details ?? {};
|
|
11
|
+
const landed = recordValue(details.landed);
|
|
12
|
+
const candidates = [recordValue(landed.receipt).sha, landed.merge_sha, landed.merge_commit, recordValue(details.land_receipt).sha];
|
|
13
|
+
for (const raw of Object.values(recordValue(details.decision_queue))) {
|
|
14
|
+
const event = recordValue(raw);
|
|
15
|
+
const proof = recordValue(event.proof);
|
|
16
|
+
if (event.card_id === card.id && event.event_type === "land" && proof.key === "landed")
|
|
17
|
+
candidates.push(recordValue(proof.value).sha);
|
|
18
|
+
}
|
|
19
|
+
const receipts = [...new Set(candidates.filter((value) => validTestSha(value)))];
|
|
20
|
+
if (receipts.length > 1)
|
|
21
|
+
return null;
|
|
22
|
+
if (receipts.length === 1) {
|
|
23
|
+
git(["merge-base", "--is-ancestor", receipts[0], "origin/main"]);
|
|
24
|
+
return receipts[0];
|
|
25
|
+
}
|
|
26
|
+
const branch = `masz-${card.id.slice(0, 8)}`;
|
|
27
|
+
// Bound visited commits, not grep matches: --grep could walk all of main.
|
|
28
|
+
const parts = git(["log", "origin/main", "--first-parent", "--max-count=200", "--format=%H%x00%B%x00"]).split("\0");
|
|
29
|
+
const escaped = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
30
|
+
const matches = new RegExp(`(^|[^a-zA-Z0-9_-])(?:${escaped(card.id)}|${escaped(branch)})(?=$|[^a-zA-Z0-9_-])`);
|
|
31
|
+
const commits = [];
|
|
32
|
+
for (let index = 0; index + 1 < parts.length; index += 2) {
|
|
33
|
+
const sha = parts[index].trim(), message = parts[index + 1];
|
|
34
|
+
if (validTestSha(sha) && matches.test(message))
|
|
35
|
+
commits.push(sha);
|
|
36
|
+
}
|
|
37
|
+
return commits.length === 1 ? commits[0] : null;
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error(`tester identity ${card.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function validTestSha(value) {
|
|
45
|
+
return typeof value === "string" && /^[a-f0-9]{7,40}$/i.test(value);
|
|
46
|
+
}
|
|
47
|
+
export function landedSha(card) {
|
|
48
|
+
const landed = card.details?.landed;
|
|
49
|
+
return record(landed) && validTestSha(landed.sha) ? landed.sha : null;
|
|
50
|
+
}
|
|
51
|
+
function record(value) {
|
|
52
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
53
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function makeWorkerTmpdir(base?: string, identity?: string): string;
|
|
2
|
+
/** The brief and child process must use this same path, with no per-command override. */
|
|
3
|
+
export declare function workerTmpdirForBrief(env: NodeJS.ProcessEnv): string;
|
|
4
|
+
/** Pass an already allocated directory only when retaining a task's existing ownership. */
|
|
5
|
+
export declare function workerEnv(base: NodeJS.ProcessEnv, tmp?: string): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { mkdtempSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
// Darwin's sockaddr_un has a 104-byte path budget, including the trailing NUL.
|
|
4
|
+
const SOCKET_SUFFIX = `/tsx-4294967295/${"x".repeat(40)}`;
|
|
5
|
+
export function makeWorkerTmpdir(base = "/tmp", identity = "") {
|
|
6
|
+
try {
|
|
7
|
+
if (!path.isAbsolute(base))
|
|
8
|
+
throw new Error("wymagana absolutna ścieżka bazowa");
|
|
9
|
+
if (identity && !/^[a-f0-9]{8}$/.test(identity))
|
|
10
|
+
throw new Error("niepoprawna tożsamość katalogu");
|
|
11
|
+
const prefix = path.join(base, `rw-${identity ? `${identity}-` : ""}`);
|
|
12
|
+
if (Buffer.byteLength(`${prefix}XXXXXX${SOCKET_SUFFIX}`) >= 104)
|
|
13
|
+
throw new Error("ścieżka za długa dla gniazda Unix");
|
|
14
|
+
// mkdtemp creates a unique directory with mode 0700, independently of the worktree.
|
|
15
|
+
return mkdtempSync(prefix);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
throw new Error(`ACTIONABLE: TMPDIR Workera: ${error instanceof Error ? error.message : String(error)}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** The brief and child process must use this same path, with no per-command override. */
|
|
22
|
+
export function workerTmpdirForBrief(env) {
|
|
23
|
+
const tmp = env.TMPDIR;
|
|
24
|
+
if (!tmp || !path.isAbsolute(tmp) || env.TMP !== tmp || env.TEMP !== tmp) {
|
|
25
|
+
throw new Error("ACTIONABLE: sprzeczne TMPDIR/TMP/TEMP Workera");
|
|
26
|
+
}
|
|
27
|
+
return tmp;
|
|
28
|
+
}
|
|
29
|
+
/** Pass an already allocated directory only when retaining a task's existing ownership. */
|
|
30
|
+
export function workerEnv(base, tmp = makeWorkerTmpdir()) {
|
|
31
|
+
const env = { ...base, TMPDIR: tmp, TMP: tmp, TEMP: tmp };
|
|
32
|
+
workerTmpdirForBrief(env);
|
|
33
|
+
return env;
|
|
34
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# API silnika z paczki robisie
|
|
2
|
+
|
|
3
|
+
Po instalacji `npm i robisie` używaj publicznego importu `robisie/dist/planer-client.js` w module ESM. Nie potrzebujesz checkoutu aplikacji ani aliasów repozytorium. Poniższe bloki można zapisać jako osobne pliki `.mjs`; eksportują funkcje, a sam import nie wykonuje odczytów ani zapisów.
|
|
4
|
+
|
|
5
|
+
Caller przekazuje `config`: **dokładnie jedno z `origin` XOR `endpoint`**, np. `origin: 'https://robisie.app'` albo `endpoint: 'https://robisie.app/api/mcp'`, oraz własny `token`. Opcjonalne `fetch` pozwala wstrzyknąć transport (w teście wyłącznie fixture); bez niego SDK używa natywnego fetch. Opcjonalne `deadlineMs` ogranicza czas żądania. SDK nie pobiera tokena z pliku ani środowiska. Nie generuj, nie osadzaj i nie loguj tokena; przekaż go z bezpiecznego źródła callera poza rozmową i historią poleceń.
|
|
6
|
+
|
|
7
|
+
## Odczyt i jednoznaczny wybór
|
|
8
|
+
|
|
9
|
+
Wywołaj `readCard(config, selection)`. `selection` może zawierać `projectId`, `trackId` i `id` rozpoznane przez callera. Brak identyfikatora pozwala kontynuować tylko wtedy, gdy discovery zwraca dokładnie jedną pozycję na danym poziomie. Przy pustej liście, braku wskazanego ID albo wielu kandydatach wynik `wybierz-jednoznacznie` wskazuje etap; caller ustala wybór przed kolejnym odczytem. Nie wybieraj przypadkowej pierwszej pozycji. Ten odczyt niczego nie zapisuje ani nie tworzy brakujących obiektów.
|
|
10
|
+
|
|
11
|
+
`detailsKeys` w `listTasks` oraz `include` w `getTask` są jawnymi listami kluczy, np. `detailsKeys: ['seed_brief']` i `include: ['seed_brief', 'scope']`. Puste `[]` oznacza brak `details` w wyniku. Wybrane `details` mogą być `null`; brak wybranego klucza nie oznacza błędu. SDK sam wysyła `full: true` w `list_tasks`. Przykład jawnie przekazuje wszystkie statusy, łącznie z `done`, które serwer domyślnie wyklucza z listy; dzięki temu można odczytać także zakończoną kartę po ID. `getTask` zwracające `null` oznacza brak karty. Wadliwa odpowiedź powoduje istniejący `PlanerClientError` SDK (np. `INVALID_RESPONSE`), bez normalizowania legacy ani dopisywania stanu HOLD. Funkcja przekazuje błąd callerowi i zamyka klienta w `finally`, także przy błędzie connect.
|
|
12
|
+
|
|
13
|
+
<!-- engine-api:read:start -->
|
|
14
|
+
```js
|
|
15
|
+
import { PlanerClient } from 'robisie/dist/planer-client.js';
|
|
16
|
+
|
|
17
|
+
export async function readCard(config, {
|
|
18
|
+
projectId, trackId, id, detailsKeys = [], include = [],
|
|
19
|
+
} = {}) {
|
|
20
|
+
const client = new PlanerClient(config);
|
|
21
|
+
try {
|
|
22
|
+
await client.connect();
|
|
23
|
+
const projects = (await client.listProjects())
|
|
24
|
+
.filter(project => projectId === undefined || project.id === projectId);
|
|
25
|
+
if (projects.length !== 1) return { outcome: 'wybierz-jednoznacznie', stage: 'list_projects' };
|
|
26
|
+
const project = projects[0];
|
|
27
|
+
const tracks = (await client.listTracks({ projectId: project.id }))
|
|
28
|
+
.filter(track => trackId === undefined || track.id === trackId);
|
|
29
|
+
if (tracks.length !== 1) return { outcome: 'wybierz-jednoznacznie', stage: 'list_tracks' };
|
|
30
|
+
const track = tracks[0];
|
|
31
|
+
const tasks = (await client.listTasks({
|
|
32
|
+
projectId: project.id, trackId: track.id, detailsKeys,
|
|
33
|
+
statuses: ['draft', 'ready', 'in_progress', 'in_review', 'blocked', 'delivered', 'done'],
|
|
34
|
+
}))
|
|
35
|
+
.filter(task => id === undefined || task.id === id);
|
|
36
|
+
if (tasks.length !== 1) return { outcome: 'wybierz-jednoznacznie', stage: 'list_tasks' };
|
|
37
|
+
const task = await client.getTask({ id: tasks[0].id, include });
|
|
38
|
+
return { outcome: task === null ? 'brak-karty' : 'odczytano', task };
|
|
39
|
+
} finally {
|
|
40
|
+
await client.close();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
<!-- engine-api:read:end -->
|
|
45
|
+
|
|
46
|
+
## Jeden append, osobny wynik odczytu
|
|
47
|
+
|
|
48
|
+
Caller przekazuje rozpoznane `id` i `patch` do `appendCard(config, { id, patch })` dopiero po uzyskaniu zgody na ten zapis. `appendDetail` wykonuje jeden zapis i osobny readback tylko kluczy patcha. `write` jest potwierdzeniem zapisu, a `readback` wynikiem późniejszego odczytu. Gdy `readback.ok === false`, zachowaj `write`; nie powtarzaj append, aby naprawić odczyt. Caller może później wykonać wyłącznie dodatkowy `getTask({ id, include: Object.keys(patch) })`, przez połączonego klienta z `close` w `finally`.
|
|
49
|
+
|
|
50
|
+
`PlanerClientError.uncertain === true` oznacza, że zapis mógł dojść do serwera mimo utraty odpowiedzi lub błędu narzędzia. Wynik `wynik-niepewny` kończy automatyczne pisanie. Caller odczytuje stan i podejmuje decyzję; funkcja nie ponawia append ani żadnej innej mutacji. Pozostałe błędy są przekazywane callerowi. Nie loguj konfiguracji ani argumentów z tokenem.
|
|
51
|
+
|
|
52
|
+
<!-- engine-api:append:start -->
|
|
53
|
+
```js
|
|
54
|
+
import { PlanerClient, PlanerClientError } from 'robisie/dist/planer-client.js';
|
|
55
|
+
|
|
56
|
+
export async function appendCard(config, { id, patch }) {
|
|
57
|
+
const client = new PlanerClient(config);
|
|
58
|
+
try {
|
|
59
|
+
await client.connect();
|
|
60
|
+
const result = await client.appendDetail({ id, patch });
|
|
61
|
+
return { outcome: 'zapis-potwierdzony', ...result };
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error instanceof PlanerClientError && error.uncertain) {
|
|
64
|
+
return { outcome: 'wynik-niepewny', error };
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
} finally {
|
|
68
|
+
await client.close();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
<!-- engine-api:append:end -->
|
|
73
|
+
|
|
74
|
+
Serwer stosuje **płytki merge**: patch zastępuje całą wartość pod wskazanym kluczem, bez deep merge. `null` jest wartością literalną, a brak klucza w patchu nie kasuje dotychczasowej wartości. Przykładowo patch `{ notes: { nowa: 2 }, literal: null }` zastąpi całe poprzednie `notes`, zapisze literalne `null` i pozostawi inne klucze.
|
|
75
|
+
|
|
76
|
+
Osobna operacja `updateTask({ id, status: 'ready', expected: { status: 'draft' } })` przekazuje warunek oczekiwanego statusu. Warunek, przejście statusu i uprawnienia rozstrzyga serwer; SDK nie implementuje własnego CAS. Powyższe przykłady nie wykonują update.
|
|
77
|
+
|
|
78
|
+
SDK nie tworzy projektu ani karty, nie nadaje uprawnień, nie dowodzi odbioru `done` i nie uruchamia silnika. Potwierdzenie append nie jest dowodem wykonania zadania ani niezależnej recenzji. Role, odbiór i uruchomienie pracy wymagają ich własnych kontraktów.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Konfiguracja silnika z paczki
|
|
2
|
+
|
|
3
|
+
Ten przykład korzysta z modułu `robisie/dist/engine-config.js` z lokalnie
|
|
4
|
+
zainstalowanej paczki `robisie`. Wymaga Node.js 20 lub nowszego. Odczytuje
|
|
5
|
+
i waliduje plik; nie wykonuje komend z konfiguracji ani połączeń sieciowych.
|
|
6
|
+
Nie uruchamia silnika w tle i nie zapewnia samodzielnej realizacji zadań.
|
|
7
|
+
|
|
8
|
+
Opis operacji na tablicy znajdziesz w [API silnika](ENGINE-API.md),
|
|
9
|
+
a konfigurację agenta i instalatora w [przewodniku klienta](README.md).
|
|
10
|
+
|
|
11
|
+
## Zapisz konfigurację
|
|
12
|
+
|
|
13
|
+
Zapisz poniższy blok jako `robisie.config.json`, np. w `config/setup/`.
|
|
14
|
+
UUID projektu i toru są **fikcyjne** — nie wskazują rzeczywistych danych.
|
|
15
|
+
Przed użyciem własnej konfiguracji podmień:
|
|
16
|
+
|
|
17
|
+
- `project` na UUID swojego projektu; `tracks` na UUID i nazwy jego torów;
|
|
18
|
+
- `engine.repo` na ścieżkę swojego repozytorium i `engine.branch` na jego gałąź;
|
|
19
|
+
- `stateDir` na katalog stanu swojej instancji;
|
|
20
|
+
- `engine.commands.test`, `lint`, `build` na własne programy i argumenty;
|
|
21
|
+
- `engine.requiredChecks` na nazwy wymaganych kontroli CI;
|
|
22
|
+
- `roles` na własne nazwy i dokładne formy ról, `coordinator.id` na swój
|
|
23
|
+
identyfikator koordynatora, a `owner.genitive` na dopełniacz imienia właściciela.
|
|
24
|
+
|
|
25
|
+
`stateDir` i `engine.repo` są liczone względem **katalogu pliku konfiguracji**,
|
|
26
|
+
nie katalogu, z którego uruchamiasz Node. Dla `config/setup/robisie.config.json`
|
|
27
|
+
wartość `../repo with spaces` wskazuje `config/repo with spaces`.
|
|
28
|
+
|
|
29
|
+
Każda komenda to niepuste **argv**: program, następnie osobne argumenty.
|
|
30
|
+
Spacje w argumencie i metaznaki są danymi. Przykładowe `a b` i `$(never-run)`
|
|
31
|
+
mają pozostać dosłowne; nie sklejaj argv w polecenie powłoki. Poniższy odczyt
|
|
32
|
+
nie wykonuje nawet testu, lintowania ani budowania. Komendy są ilustracyjne
|
|
33
|
+
i wymagają podmiany przed wykorzystaniem konfiguracji do wykonywania pracy.
|
|
34
|
+
`delivery: "pr"` opisuje sposób dostarczenia; sam odczyt nie tworzy PR.
|
|
35
|
+
|
|
36
|
+
Role silnika to `coordinator`, `owner`, `planner`, `advisor`.
|
|
37
|
+
`forms` zawierają dosłowne formy z uwzględnieniem wielkości liter, a
|
|
38
|
+
`owner.genitive` służy m.in. do zapisu „Efekt dla Ali”. To osobne ustawienia
|
|
39
|
+
od `roleProviders` instalatora dla ról `ceo`, `builder`, `quality`.
|
|
40
|
+
|
|
41
|
+
<!-- engine-config:json:start -->
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"project": "11111111-1111-4111-8111-111111111111",
|
|
45
|
+
"tracks": [
|
|
46
|
+
{ "id": "22222222-2222-4222-8222-222222222222", "name": "Mój tor" }
|
|
47
|
+
],
|
|
48
|
+
"stateDir": "../state with spaces",
|
|
49
|
+
"roles": {
|
|
50
|
+
"coordinator": { "id": "przystan", "name": "Przystań", "forms": ["Przystań", "Przystani"] },
|
|
51
|
+
"owner": { "name": "Ala", "forms": ["Ala", "Ali"], "genitive": "Ali" },
|
|
52
|
+
"planner": { "name": "Mapa", "forms": ["Mapa", "Mapy"] },
|
|
53
|
+
"advisor": { "name": "Latarnia", "forms": ["Latarnia", "Latarni"] }
|
|
54
|
+
},
|
|
55
|
+
"engine": {
|
|
56
|
+
"repo": "../repo with spaces",
|
|
57
|
+
"branch": "main",
|
|
58
|
+
"commands": {
|
|
59
|
+
"test": ["node", "a b", "$(never-run)"],
|
|
60
|
+
"lint": ["npm", "run", "lint"],
|
|
61
|
+
"build": ["npm", "run", "build:check"]
|
|
62
|
+
},
|
|
63
|
+
"requiredChecks": ["Test", "Lint", "Build"],
|
|
64
|
+
"delivery": "pr"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
<!-- engine-config:json:end -->
|
|
69
|
+
|
|
70
|
+
## Odczytaj plik
|
|
71
|
+
|
|
72
|
+
W katalogu, w którym dostępna jest paczka `robisie` w `node_modules`, zapisz
|
|
73
|
+
poniższy kod jako `read-engine-config.mjs`.
|
|
74
|
+
|
|
75
|
+
<!-- engine-config:esm:start -->
|
|
76
|
+
```js
|
|
77
|
+
import { loadEngineConfig } from 'robisie/dist/engine-config.js';
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const configPath = process.argv[2];
|
|
81
|
+
if (!configPath) throw new Error('Podaj ścieżkę do robisie.config.json');
|
|
82
|
+
const config = loadEngineConfig(configPath);
|
|
83
|
+
console.log(JSON.stringify(config));
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
<!-- engine-config:esm:end -->
|
|
90
|
+
|
|
91
|
+
Uruchom lokalnie, podając absolutną ścieżkę do zapisanego JSON:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
node read-engine-config.mjs /absolutna/sciezka/robisie.config.json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Katalog skryptu może być inny niż katalog konfiguracji. Sukces oznacza exit 0,
|
|
98
|
+
pusty stderr i jeden obiekt JSON na stdout. Poniżej kształt wybranych pól wyniku
|
|
99
|
+
dla powyższej ścieżki (pozostałe pola konfiguracji też są wypisywane):
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
configPath: "/absolutna/sciezka/robisie.config.json"
|
|
103
|
+
stateDir: "/absolutna/state with spaces"
|
|
104
|
+
engine.repo: "/absolutna/repo with spaces"
|
|
105
|
+
engine.commands.test: ["node", "a b", "$(never-run)"]
|
|
106
|
+
engine.delivery: "pr"
|
|
107
|
+
roles.owner: {"name":"Ala","forms":["Ala","Ali"],"genitive":"Ali"}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Ścieżka pliku jest kanonizowana, więc przy dowiązaniach wynik wskazuje jego
|
|
111
|
+
rzeczywistą lokalizację. Brak pliku, niepoprawny JSON lub błędne pola kończą
|
|
112
|
+
odczyt z niezerowym exit i komunikatem na stderr. Na przykład brak `test`
|
|
113
|
+
w `engine.commands` daje błąd zawierający `engine.commands.test`, a
|
|
114
|
+
`delivery: "invalid"` — `engine.delivery`. Odczyt nie dopisuje brakujących
|
|
115
|
+
komend ani nie zastępuje błędnego sposobu dostarczenia wartością domyślną.
|
package/docs/README.en.md
CHANGED
|
@@ -5,7 +5,7 @@ Polski: [README.md](README.md).
|
|
|
5
5
|
<!-- sekcja: dostepnosc -->
|
|
6
6
|
## Availability
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Install robisie from npm in the prepared directory; run subsequent commands and agent sessions there. [Preparation](#wymagania) → [account](#konto) → [key](#klucz) → [installation](#instalacja) → [acceptance](#recenzja-i-odbior).
|
|
9
9
|
|
|
10
10
|
<!-- sekcja: wymagania -->
|
|
11
11
|
<a id="wymagania"></a>
|
|
@@ -13,7 +13,7 @@ This guide uses a supplied `robisie-0.1.0.tgz`, without obtaining the installer
|
|
|
13
13
|
|
|
14
14
|
The human prepares Node.js 20+, npm, internet for dependencies and robisie, and one provider program: Claude Code, Codex CLI or OpenCode with their own AI access. Check `node --version`, `npm --version` and the chosen program's version (`claude --version`, `codex --version` or `opencode --version`). If absent from PATH, install it following its provider's instructions and check again; a configuration directory alone is insufficient. Use WSL on Windows (POSIX paths).
|
|
15
15
|
|
|
16
|
-
Prepare an empty directory on disk
|
|
16
|
+
Prepare an empty directory on disk. No website or `package.json` is needed; npm installation creates dependency files. This is the agents' working directory, separate from the dashboard project.
|
|
17
17
|
|
|
18
18
|
<!-- sekcja: konto -->
|
|
19
19
|
<a id="konto"></a>
|
|
@@ -35,16 +35,19 @@ The package stores **one key for CEO, Builder and Quality Officer** as `ROBISIE_
|
|
|
35
35
|
|
|
36
36
|
<!-- sekcja: instalacja -->
|
|
37
37
|
<a id="instalacja"></a>
|
|
38
|
-
## Install
|
|
38
|
+
## Install from npm
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
After preparing the account and key, the human opens a terminal in the prepared directory. Three steps: install, init with the hidden key prompt, doctor. In zsh/bash clear stale ENV before init and load the saved key before doctor — init cannot set its parent environment:
|
|
41
41
|
|
|
42
42
|
```sh
|
|
43
|
-
npm i
|
|
44
|
-
|
|
43
|
+
npm i robisie
|
|
44
|
+
unset ROBISIE_KEY
|
|
45
|
+
npx robisie init --agent claude
|
|
46
|
+
[ -r "${XDG_CONFIG_HOME:-$HOME/.config}/robisie/key" ] && export ROBISIE_KEY="$(cat "${XDG_CONFIG_HOME:-$HOME/.config}/robisie/key")"
|
|
47
|
+
npx robisie doctor
|
|
45
48
|
```
|
|
46
49
|
|
|
47
|
-
Expected result: 0.
|
|
50
|
+
For Codex or OpenCode replace `claude` with `codex` or `opencode`. With existing configuration, use the complete map in the next section; `--agent` alone does not clear it. Expected doctor result: exit 0. As a fallback, install a supplied file with `npm i ./robisie-0.1.0.tgz`. The agent starts `client-launch-context` and `ceo-client-launcher` via `npx -y robisie@0.1.0 mcp <name>`; the local package must be accessible from the session's cwd.
|
|
48
51
|
|
|
49
52
|
<!-- sekcja: init -->
|
|
50
53
|
## Connect your agent
|
|
@@ -108,11 +111,15 @@ Doctor runs the helper file via node, the agent via npx. `ROBISIE_PROJECT_CONTEX
|
|
|
108
111
|
<!-- sekcja: pierwsza-karta -->
|
|
109
112
|
## First card and Builder startup
|
|
110
113
|
|
|
111
|
-
The human starts the CEO in the installation directory: `claude --agent ceo-client`, or `codex` and requests the robisie CEO role (reading the full file referenced by AGENTS.md), or `opencode` and selects `ceo-client`. After successful discovery use this request, which works with just README
|
|
114
|
+
The human starts the CEO in the installation directory: `claude --agent ceo-client`, or `codex` and requests the robisie CEO role (reading the full file referenced by AGENTS.md), or `opencode` and selects `ceo-client`. After successful discovery use this request, which works after npm installation with just README:
|
|
112
115
|
|
|
113
116
|
> Create wynik.txt in this directory with exactly “robisie działa” followed by a newline. Read it back and verify the exact contents. Boundaries: this file only, no dependencies, accounts or publication. Prepare the scope and wait for my approval; after the work I need independent review and my acceptance.
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
After human approval, the CEO by default calls `spawn_builder` with the ID of the card confirmed in `ready`. The full flow in Claude Code is: `draft` → `ready` → `in_progress` → `in_review` → independent review and verified receipt → `delivered` → owner acceptance → `done`; see [Review and explicit acceptance](#recenzja-i-odbior) for acceptance limits in other sessions.
|
|
119
|
+
|
|
120
|
+
If `spawn_builder` fails, the CEO reports the error and resolves the cause before retrying; it does not automatically switch to manual startup.
|
|
121
|
+
|
|
122
|
+
Manual startup is available only as the operator's explicit choice: the CEO then provides a command to run in a new terminal in the installation directory.
|
|
116
123
|
|
|
117
124
|
The ID comes from the created or freshly read card, not the project or session. Below is a fictitious lowercase canonical UUID; replace it with the exact card ID. The Codex example assumes XDG_CONFIG_HOME=/home/tester/Robisie config: replace the entire path with the actual installed path (default $HOME/.config/robisie/roles/codex/manager-builder-client.md). A tilde inside the quoted prompt will not expand. Choose one command:
|
|
118
125
|
|
|
@@ -128,16 +135,18 @@ Expected startup result: a fresh CEO read confirms the assigned card in_progress
|
|
|
128
135
|
<a id="recenzja-i-odbior"></a>
|
|
129
136
|
## Review and explicit acceptance
|
|
130
137
|
|
|
131
|
-
|
|
138
|
+
In Claude Code, the CEO starts an independent Quality Officer through `Agent(quality-officer-client)` after a fresh in_review read. The Quality Officer independently checks the result and records review and evidence on the card; the Builder does not review its own work. The CEO verifies the review confirmation (verified receipt) and performs the fresh card reads required by the protocol before moving to `delivered`; then explicit acceptance by the operator permits moving to `done`. Checking evidence and explicitly accepting the result is the operator's responsibility. After verified review and delivery, check the acceptance criteria; if evidence needed for your acceptance or your approval is missing, leave the card in `delivered`.
|
|
139
|
+
|
|
140
|
+
OpenCode: the card stays in `in_review` regardless of the availability of the Agent tool; there is no supported transition to `delivered`. Full acceptance requires the Claude Code route described above. A task/VERIFIED result, role label or receipt JSON alone does not open this transition; do not bypass a refusal with a manual status change or self-review.
|
|
132
141
|
|
|
133
|
-
|
|
142
|
+
In other CEO sessions, including Codex, moving to delivered requires independent review and a verified receipt. If the CEO session lacks the Agent tool, the card stays in `in_review`: this is a product capability gap for CEO/CR to resolve. Do not substitute Builder self-review or a manual status change. Installing three roles does not provide that integration.
|
|
134
143
|
|
|
135
144
|
The project owner can use Dashboard → Planer → card → Etap → “Zrobione” to move `delivered` to `done`, including when the agent uses a `worker` key. For the first card described here, the server does not enforce stored acceptance or evidence before a change to `done`: the dashboard lets the owner select “Zrobione” even without them. Do this only after checking and explicitly accepting the result. The dashboard cannot edit `details`; review and evidence are recorded through the supported board tool flow. This guide alone does not prove a customer has completed that flow.
|
|
136
145
|
|
|
137
146
|
<!-- sekcja: aktualizacja -->
|
|
138
147
|
## Update
|
|
139
148
|
|
|
140
|
-
|
|
149
|
+
In the installation directory run `npm i robisie@latest` → `npx robisie init` → `npx robisie doctor`. Doctor checks the server contract MAJOR; expect a compatible version, complete configuration and exit 0. If incompatible, choose a compatible release instead of repeating the same installation. For damaged files reinstall from npm; as a fallback install a supplied compatible tgz with `npm i ./robisie-0.1.0.tgz`. For a local return, retain the previous compatible tgz and a configuration backup, install it with the same command, then run init and doctor.
|
|
141
150
|
|
|
142
151
|
<!-- sekcja: odinstalowanie -->
|
|
143
152
|
## Uninstall
|
|
@@ -159,16 +168,16 @@ CLI messages remain in Polish. Match the message, then follow the English explan
|
|
|
159
168
|
| brak-klucza | `Brak klucza robisie.` | No key: generate one in Connect and supply it to init. |
|
|
160
169
|
| odrzucony-klucz | `Serwer robisie odrzucił klucz (nieważny albo unieważniony).` | Generate a new worker key; in zsh/bash run `unset ROBISIE_KEY`, then init with the hidden prompt. |
|
|
161
170
|
| brak-odczytu | `Ten klucz nie ma uprawnień do odczytu tablicy (np. rola seeder).` | Select worker in Connect; `unset ROBISIE_KEY`, then init with the hidden prompt. |
|
|
162
|
-
| major | `Wersja kontraktu serwera (2.0.0) nie pasuje do paczki robisie 0.1.0 (obsługuje 1.x).` | Example of a MAJOR mismatch:
|
|
171
|
+
| major | `Wersja kontraktu serwera (2.0.0) nie pasuje do paczki robisie 0.1.0 (obsługuje 1.x).` | Example of a MAJOR mismatch: install a release compatible with the server contract. |
|
|
163
172
|
| brak-env | `Zmienna ROBISIE_KEY nie jest ustawiona — role i agenci jej potrzebują.` | Check profile support; with --no-shell/another shell load the key file into the session environment as shown in init, then doctor. |
|
|
164
173
|
| brak-programu | `katalog jest, programu nie ma w PATH` | Install the named provider program or add it to PATH; a configuration directory alone is insufficient. |
|
|
165
174
|
| brak-programu-i-katalogu | `brak programu w PATH i katalogu dostawcy` | Install the selected program, repeat init with the complete shared map and doctor. |
|
|
166
175
|
| jsonc | `nie zmieniam automatycznie` | Preserve JSONC; prepare supported JSON or choose another provider for all roles. Remote MCP alone is insufficient. |
|
|
167
176
|
| niepelna-instalacja | `Claude Code: niepełna instalacja` | Example of missing configuration: repeat init for your agent. |
|
|
168
177
|
|
|
169
|
-
After repair, run local `npx robisie init` and `npx robisie doctor`; for missing files first reinstall the
|
|
178
|
+
After repair, run local `npx robisie init` and `npx robisie doctor`; for missing files first reinstall the package from npm, for MAJOR obtain a compatible release. If the problem remains, send the team the message, version and cwd without the key. Uninstall success means removed entries and exit 0; exit 1 needs the indicated repair and does not mean complete removal.
|
|
170
179
|
|
|
171
180
|
<!-- sekcja: ograniczenia -->
|
|
172
181
|
## Limitations
|
|
173
182
|
|
|
174
|
-
The
|
|
183
|
+
The installer does not start background services or the Builder on its own; after operator approval, the CEO can start the Builder through `spawn_builder`. Helpers run during agent sessions using the project directory (or `ROBISIE_PROJECT_CONTEXT`) as context. The installer contacts robisie to check the contract and key; the package file does not provide an account or AI provider access. One shared key identity does not provide three different permission levels. Acceptance requires independent review, evidence and the operator's approval.
|