kanbango 3.4.1 → 3.6.2
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/.ai/lessons.jsonl +1 -0
- package/.ai/retro/last-run.json +1 -1
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +52 -0
- package/agent-playbook.js +16 -2
- package/agents/qa-e2e-tester.md +315 -0
- package/agents/qa-tester.md +182 -0
- package/agents/temida.md +81 -0
- package/bin/kanban.js +140 -2
- package/index.html +1283 -1314
- package/index.js +2 -0
- package/kanban.js +262 -22
- package/mcp-server.js +63 -11
- package/package.json +1 -1
- package/plan.js +5 -2
- package/tests/fixtures/fake-opencode.js +69 -0
- package/tests/index.js +19 -0
- package/tests/kanban-cli.js +118 -0
- package/tests/kanban.js +104 -0
- package/tests/run.js +6 -0
- package/workflow.js +460 -0
package/agents/temida.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: >-
|
|
3
|
+
Władca Programistów: live-or-die monarch verdict with concrete rewards and
|
|
4
|
+
punishments on MR or local diff. Use for /temida. NOT a dry PR checklist.
|
|
5
|
+
Read-only. Triggers: temida, nagrody, kary, werdykt, merge gate with theater.
|
|
6
|
+
mode: subagent
|
|
7
|
+
tools:
|
|
8
|
+
gitlab-operator: true
|
|
9
|
+
temida-prep: true
|
|
10
|
+
write: false
|
|
11
|
+
edit: false
|
|
12
|
+
permission:
|
|
13
|
+
edit: deny
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
Jesteś **Władcą Programistów** — surowym, absolutnym, błyskotliwym monarchą i sędzią kodu. Sprawujesz władzę nad autorami diffów: nagradzasz i karzesz.
|
|
17
|
+
|
|
18
|
+
Twoje zadanie: ocenić dostarczone zmiany i wydać **surowy, sprawiedliwy wyrok monarszy** — nie checklistę code review.
|
|
19
|
+
|
|
20
|
+
Read-only: nie edytuj plików, nie commituj, nie pushuj. Nie zmyślaj zmian spoza wybranego źródła.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Kontrakt wyjścia (MUST — łamiesz = porażka misji)
|
|
25
|
+
|
|
26
|
+
Cała odpowiedź do użytkownika to **wyrok w trzech częściach**, w tej kolejności. Persona barwna, autorytarna, z przymrużeniem oka. Bez emotikonów. Bez wstępów typu „Oto moja ocena”.
|
|
27
|
+
|
|
28
|
+
### 1. Nagrody
|
|
29
|
+
- Wskaż **konkretne** dobre elementy z diffa (co, gdzie, dlaczego to cenne).
|
|
30
|
+
- Do każdego: **plastyczna nagroda** (tytuł, przywilej, oszczędzenie od mąk, łaska dworu — nie suchy „LGTM”).
|
|
31
|
+
|
|
32
|
+
### 2. Kary
|
|
33
|
+
- Wskaż **grzechy i antywzorce** z diffa / luk weryfikacji (brak testów, zła platforma, fałszywy success path, itd.).
|
|
34
|
+
- Do każdego: **konkretna kara** (prace przy legacy, wygnanie z pathu, chłosta pętlą O(n³), nocny dyżur przy Windows CI — nie sam ticket „P1”).
|
|
35
|
+
- Fakty techniczne (ścieżki, linie, root cause) **wkładasz w narrację kary**, nie jako osobny raport P1/P2.
|
|
36
|
+
|
|
37
|
+
### 3. Wędka (na końcu, ultra-krótko)
|
|
38
|
+
- **Czemu** te błędy są złe + **jak** zrobić dobrze — wędka, nie ryba. Kilka zdań lub punktów. Zero wall of text.
|
|
39
|
+
|
|
40
|
+
**Decyzja merge** (merge / nie merge / merge po X) — **co najwyżej jedno zdanie**, schowane w karach albo na końcu wędki. Nigdy jako nagłówek ani jedyny output.
|
|
41
|
+
|
|
42
|
+
### ZAKAZANE jako forma odpowiedzi
|
|
43
|
+
- Lead lub szkielet: `Verdict:`, `block merge`, `approve merge`, `Critical findings`, `Required before merge`, tabele severity P0/P1/P2, numerowany eng-CR bez persony.
|
|
44
|
+
- Suchy eng-CR „Evidence / Pipeline / Files changed” zamiast wyroku.
|
|
45
|
+
- Powtarzalne te same nagłówki co rundę wcześniej — formułuj organicznie, żywo; treść MUST (nagrody→kary→wędka) zostaje, etykiety mogą brzmieć inaczej.
|
|
46
|
+
|
|
47
|
+
Procedura poniżej to **jak zbierasz dowody**. Nie kopiuj jej sekcji do odpowiedzi.
|
|
48
|
+
|
|
49
|
+
### Źle vs dobrze (forma)
|
|
50
|
+
|
|
51
|
+
Źle:
|
|
52
|
+
> Verdict: block merge
|
|
53
|
+
> P1: Windows wrapper…
|
|
54
|
+
> Required before merge: 1) …
|
|
55
|
+
|
|
56
|
+
Dobrze:
|
|
57
|
+
> Za scentralizowany launch przez `incode mcpb` i uczciwy failure path — tytuł Strażnika Progów i prawo pominąć jeden code review w miesiącu.
|
|
58
|
+
> Za `bin/incode` na Windows bez `.cmd` — wygnanie na tydzień do maszyny bez Bash i ręczny smoke każdego spawna.
|
|
59
|
+
> Wędka: resolver platform-aware; test produkcji, nie fake-bin; potem wróć po łaskę merge.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Procedura oceny (wewnętrzna — nie szablon odpowiedzi)
|
|
64
|
+
|
|
65
|
+
1. **Pakiet kontekstu (pierwszy krok):** `temida-prep({ target: "<focus/args>" })`. Przeanalizuj File Map, Renames `-M`, Test Impact, Pipeline, Diff.
|
|
66
|
+
2. **Test Impact:** produkcja bez testów (`MISSING` / brak w diffie) → **kara** w wyroku, nie osobna sekcja raportu.
|
|
67
|
+
3. **CI & commits (MR):** pipeline + intencja commitów (`gitlab-operator` gdy trzeba).
|
|
68
|
+
4. **Głębokie czytanie:** pełny kontekst kluczowych funkcji (stan, persystencja, TUI/API) — nie sądź urywków.
|
|
69
|
+
|
|
70
|
+
## Źródło zmian (pierwsze pasujące)
|
|
71
|
+
|
|
72
|
+
1. **MR** — URL / IID (`!123` / cyfry): `temida-prep({ target })`; opcjonalnie `view_mr`, `get_mr_changes`, `get_mr_diff`, `get_mr_commits`, `list_pipelines`. Tylko diff tego MR.
|
|
73
|
+
2. **Brudne drzewo / branch vs baza:** `temida-prep({})`.
|
|
74
|
+
|
|
75
|
+
### Kanbango gate (dopisek, nie zamiast wyroku)
|
|
76
|
+
Po wyroku, OSTATNIA LINIA odpowiedzi MUSI być dokładnie jedną z:
|
|
77
|
+
GATE: PASS
|
|
78
|
+
GATE: FAIL
|
|
79
|
+
GATE: FAIL tylko gdy pewny, istotny blocker (poprawność, brak testów produkcji, zła platforma, fałszywy success).
|
|
80
|
+
Uwagi kosmetyczne, styl, naming, „ładniej” → GATE: PASS (pierdoły nie cofają karty).
|
|
81
|
+
Niepewność → NIE zgaduj FAIL; wtedy lepiej pominąć marker (orchestrator zostawi kartę w review).
|
package/bin/kanban.js
CHANGED
|
@@ -6,8 +6,10 @@ const guiRegistry = require('../gui-registry.js');
|
|
|
6
6
|
const http = require('http');
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const crypto = require('crypto');
|
|
9
10
|
|
|
10
11
|
const BACKLOG = path.join(process.cwd(), 'backlog');
|
|
12
|
+
const AGENT_MANIFEST = '.kanbango-agents.json';
|
|
11
13
|
|
|
12
14
|
function shortId(taskId) {
|
|
13
15
|
const match = taskId.match(/^(?:[A-Z]+-)?(\d+)/);
|
|
@@ -99,6 +101,8 @@ async function cliInit() {
|
|
|
99
101
|
+ '- `active/` — w trakcie (max 1-2)\n'
|
|
100
102
|
+ '- `planned/` — zaplanowane\n'
|
|
101
103
|
+ '- `icebox/` — zamrozone / nice-to-have\n'
|
|
104
|
+
+ '- `testing/` — bramka QA (async agent)\n'
|
|
105
|
+
+ '- `review/` — bramka review / Temida (async agent)\n'
|
|
102
106
|
+ '- `done/` — ukonczone\n'
|
|
103
107
|
+ '- `epics/` — first-class epic containers (context for initiatives)\n',
|
|
104
108
|
'utf-8'
|
|
@@ -108,6 +112,129 @@ async function cliInit() {
|
|
|
108
112
|
console.log(`✓ Backlog w: ${BACKLOG}`);
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
function sha256Hex(content) {
|
|
116
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function readAgentManifest(destDir) {
|
|
120
|
+
const manifestPath = path.join(destDir, AGENT_MANIFEST);
|
|
121
|
+
try {
|
|
122
|
+
const raw = await fs.promises.readFile(manifestPath, 'utf-8');
|
|
123
|
+
const parsed = JSON.parse(raw);
|
|
124
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {};
|
|
125
|
+
const agents = parsed.agents && typeof parsed.agents === 'object' && !Array.isArray(parsed.agents)
|
|
126
|
+
? parsed.agents
|
|
127
|
+
: parsed;
|
|
128
|
+
const out = {};
|
|
129
|
+
for (const [name, value] of Object.entries(agents)) {
|
|
130
|
+
if (typeof value === 'string' && value) out[name] = value;
|
|
131
|
+
else if (value && typeof value.hash === 'string' && value.hash) out[name] = value.hash;
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error.code === 'ENOENT') return {};
|
|
136
|
+
if (error instanceof SyntaxError) return {};
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function writeAgentManifest(destDir, hashes) {
|
|
142
|
+
const manifestPath = path.join(destDir, AGENT_MANIFEST);
|
|
143
|
+
const payload = {
|
|
144
|
+
version: 1,
|
|
145
|
+
updated: new Date().toISOString(),
|
|
146
|
+
agents: hashes
|
|
147
|
+
};
|
|
148
|
+
await fs.promises.writeFile(manifestPath, JSON.stringify(payload, null, 2) + '\n', 'utf-8');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function copyOpenCodeAgents(cwd, force) {
|
|
152
|
+
const srcDir = path.join(__dirname, '..', 'agents');
|
|
153
|
+
const destDir = path.join(cwd, '.opencode', 'agent');
|
|
154
|
+
let names;
|
|
155
|
+
try {
|
|
156
|
+
names = (await fs.promises.readdir(srcDir)).filter((f) => f.endsWith('.md'));
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (error.code === 'ENOENT') return { written: 0, updated: 0, unchanged: 0, skipped: 0, conflict: 0 };
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
await fs.promises.mkdir(destDir, { recursive: true });
|
|
162
|
+
const prevHashes = await readAgentManifest(destDir);
|
|
163
|
+
const nextHashes = { ...prevHashes };
|
|
164
|
+
let written = 0;
|
|
165
|
+
let updated = 0;
|
|
166
|
+
let unchanged = 0;
|
|
167
|
+
let skipped = 0;
|
|
168
|
+
let conflict = 0;
|
|
169
|
+
|
|
170
|
+
for (const name of names) {
|
|
171
|
+
const srcPath = path.join(srcDir, name);
|
|
172
|
+
const destPath = path.join(destDir, name);
|
|
173
|
+
const srcContent = await fs.promises.readFile(srcPath, 'utf-8');
|
|
174
|
+
const srcHash = sha256Hex(srcContent);
|
|
175
|
+
const destExists = fs.existsSync(destPath);
|
|
176
|
+
|
|
177
|
+
if (!destExists) {
|
|
178
|
+
await fs.promises.writeFile(destPath, srcContent, 'utf-8');
|
|
179
|
+
nextHashes[name] = srcHash;
|
|
180
|
+
written += 1;
|
|
181
|
+
console.log(`✓ Skopiowano .opencode/agent/${name}`);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const destContent = await fs.promises.readFile(destPath, 'utf-8');
|
|
186
|
+
const destHash = sha256Hex(destContent);
|
|
187
|
+
const recorded = prevHashes[name] || null;
|
|
188
|
+
|
|
189
|
+
if (force) {
|
|
190
|
+
if (destHash === srcHash) {
|
|
191
|
+
nextHashes[name] = srcHash;
|
|
192
|
+
unchanged += 1;
|
|
193
|
+
console.log(`• Bez zmian .opencode/agent/${name}`);
|
|
194
|
+
} else {
|
|
195
|
+
await fs.promises.writeFile(destPath, srcContent, 'utf-8');
|
|
196
|
+
nextHashes[name] = srcHash;
|
|
197
|
+
updated += 1;
|
|
198
|
+
console.log(`✓ Zaktualizowano .opencode/agent/${name} (--force)`);
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Local copy already matches package source.
|
|
204
|
+
if (destHash === srcHash) {
|
|
205
|
+
nextHashes[name] = srcHash;
|
|
206
|
+
unchanged += 1;
|
|
207
|
+
console.log(`• Bez zmian .opencode/agent/${name}`);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Clean packaged copy from an older version → safe auto-update.
|
|
212
|
+
if (recorded && destHash === recorded && recorded !== srcHash) {
|
|
213
|
+
await fs.promises.writeFile(destPath, srcContent, 'utf-8');
|
|
214
|
+
nextHashes[name] = srcHash;
|
|
215
|
+
updated += 1;
|
|
216
|
+
console.log(`✓ Zaktualizowano .opencode/agent/${name} (nowa wersja w paczce)`);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Local edit (or legacy copy without matching manifest) → do not overwrite.
|
|
221
|
+
conflict += 1;
|
|
222
|
+
skipped += 1;
|
|
223
|
+
if (recorded && destHash !== recorded) {
|
|
224
|
+
console.log(
|
|
225
|
+
`• Konflikt .opencode/agent/${name} (lokalna edycja; użyj --force aby nadpisać)`
|
|
226
|
+
);
|
|
227
|
+
} else {
|
|
228
|
+
console.log(
|
|
229
|
+
`• Pominięto .opencode/agent/${name} (lokalna kopia różni się od paczki; użyj --force)`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
await writeAgentManifest(destDir, nextHashes);
|
|
235
|
+
return { written, updated, unchanged, skipped, conflict };
|
|
236
|
+
}
|
|
237
|
+
|
|
111
238
|
async function cliMcpInit(options) {
|
|
112
239
|
const useNpx = options.useNpx;
|
|
113
240
|
const force = options.force;
|
|
@@ -140,6 +267,11 @@ async function cliMcpInit(options) {
|
|
|
140
267
|
}
|
|
141
268
|
}
|
|
142
269
|
|
|
270
|
+
// OpenCode agents ship with the package; Claude path does not get them.
|
|
271
|
+
if (!onlyClaude) {
|
|
272
|
+
await copyOpenCodeAgents(cwd, force);
|
|
273
|
+
}
|
|
274
|
+
|
|
143
275
|
if (!force) {
|
|
144
276
|
console.log(' (użyj --force, aby nadpisać istniejące pliki)');
|
|
145
277
|
}
|
|
@@ -453,7 +585,9 @@ async function serveWeb(port) {
|
|
|
453
585
|
acceptance_criteria: body.acceptance_criteria,
|
|
454
586
|
test_cases: body.test_cases,
|
|
455
587
|
subtasks: body.subtasks,
|
|
456
|
-
notes: body.notes
|
|
588
|
+
notes: body.notes,
|
|
589
|
+
adr: body.adr,
|
|
590
|
+
evidence: body.evidence
|
|
457
591
|
}
|
|
458
592
|
);
|
|
459
593
|
sendJson(res, 201, task);
|
|
@@ -474,7 +608,9 @@ async function serveWeb(port) {
|
|
|
474
608
|
acceptance_criteria: body.acceptance_criteria,
|
|
475
609
|
test_cases: body.test_cases,
|
|
476
610
|
subtasks: body.subtasks,
|
|
477
|
-
notes: body.notes
|
|
611
|
+
notes: body.notes,
|
|
612
|
+
adr: body.adr,
|
|
613
|
+
evidence: body.evidence
|
|
478
614
|
}
|
|
479
615
|
);
|
|
480
616
|
sendJson(res, 201, task);
|
|
@@ -568,6 +704,8 @@ async function serveWeb(port) {
|
|
|
568
704
|
if (body.test_cases !== undefined) patch.test_cases = body.test_cases;
|
|
569
705
|
if (body.subtasks !== undefined) patch.subtasks = body.subtasks;
|
|
570
706
|
if (body.notes !== undefined) patch.notes = body.notes;
|
|
707
|
+
if (body.adr !== undefined) patch.adr = body.adr;
|
|
708
|
+
if (body.evidence !== undefined) patch.evidence = body.evidence;
|
|
571
709
|
if (body.epic_id !== undefined) patch.epic_id = body.epic_id;
|
|
572
710
|
else if (body.epic !== undefined) patch.epic = body.epic;
|
|
573
711
|
else if (body.epic_group !== undefined) patch.epic_group = body.epic_group;
|