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.
@@ -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;