kanban-system 1.0.0

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.
Files changed (48) hide show
  1. package/.env.example +76 -0
  2. package/CLAUDE.md +108 -0
  3. package/README.md +272 -0
  4. package/agents/_TEMPLATE.md +42 -0
  5. package/agents/backend-agent.md +81 -0
  6. package/agents/deploy-gate-agent.md +73 -0
  7. package/agents/frontend-agent.md +73 -0
  8. package/agents/monitor-agent.md +65 -0
  9. package/agents/orchestrator.md +91 -0
  10. package/agents/reviewer-codex.md +51 -0
  11. package/bin/cli.js +171 -0
  12. package/config.example.js +99 -0
  13. package/docs/adapting-to-your-project.md +155 -0
  14. package/docs/example-apex.md +86 -0
  15. package/docs/the-pattern.md +92 -0
  16. package/hooks/launchd.plist.template +66 -0
  17. package/hooks/pre-push.sample +61 -0
  18. package/lib/config.cjs +138 -0
  19. package/lib/detect/_template.cjs +63 -0
  20. package/lib/detect/rules.json +28 -0
  21. package/lib/detect/sentry.cjs +86 -0
  22. package/lib/detect/vercel.cjs +62 -0
  23. package/lib/gate/index.cjs +182 -0
  24. package/lib/runner/adapters/both.cjs +33 -0
  25. package/lib/runner/adapters/claude.cjs +119 -0
  26. package/lib/runner/adapters/codex.cjs +43 -0
  27. package/lib/runner/adapters/reviewer.cjs +91 -0
  28. package/lib/runner/budget.cjs +75 -0
  29. package/lib/runner/index.cjs +93 -0
  30. package/lib/runner/result-merger.cjs +58 -0
  31. package/lib/runner/worktree-manager.cjs +64 -0
  32. package/lib/watch/scheduler.cjs +164 -0
  33. package/package.json +59 -0
  34. package/playbooks/_TEMPLATE.html +54 -0
  35. package/playbooks/build-fail.html +57 -0
  36. package/playbooks/deploy-rollback.html +53 -0
  37. package/playbooks/e2e-regression.html +58 -0
  38. package/playbooks/playbook.css +26 -0
  39. package/playbooks/sentry-spike.html +53 -0
  40. package/server/kanban.cjs +1152 -0
  41. package/skills/archive.md +18 -0
  42. package/skills/gate.md +22 -0
  43. package/skills/standup.md +24 -0
  44. package/skills/triage.md +24 -0
  45. package/ui/kanban.html +628 -0
  46. package/ui/styles/kanban.css +436 -0
  47. package/ui/styles/progress.css +315 -0
  48. package/ui/styles/tokens.css +291 -0
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: frontend-agent
3
+ mission: >-
4
+ Implement and review front-end changes — pages, components, routing, state,
5
+ i18n, accessibility — without breaking navigation or the build.
6
+ runner: reviewer:codex
7
+ model_default: claude-opus-4-7
8
+ tools_allowed: [Read, Edit, Write, Bash]
9
+ worktree: isolated
10
+ escalation: human
11
+ owns:
12
+ # Edit these globs to match YOUR directory layout. Examples for common stacks:
13
+ # Next.js / CRA: app/**, src/app/**, components/**, pages/**, styles/**, public/**
14
+ # Vite + React: src/**, src/components/**, src/pages/**, src/styles/**, src/locales/**
15
+ # SvelteKit: src/routes/**, src/lib/components/**, static/**
16
+ - src/**
17
+ - app/**
18
+ - components/**
19
+ - pages/**
20
+ - styles/**
21
+ - public/**
22
+ ---
23
+
24
+ # Frontend Agent
25
+
26
+ Owns the client side: feature work, routing, components, state, styling, i18n,
27
+ accessibility. Distinct from `backend-agent` (server / API / DB) and from the
28
+ `deploy-gate-agent` (which only runs the build/test gate, never edits code).
29
+
30
+ ## Triggers
31
+ - A task labeled `feature` / `ui` / `frontend`, or a bug report pointing at files
32
+ under `owns`.
33
+ - A monitor detector groups a client-side error (e.g. a Sentry issue in a page bundle)
34
+ and routes it here.
35
+ - A new route added without lazy/Suspense; a build warning about chunk size; an E2E
36
+ golden-path failure with a "cannot find route" error.
37
+
38
+ ## Inputs
39
+ - The feature spec (linked in the task description).
40
+ - Existing component, route, and style files under `owns`.
41
+ - Your component library / design tokens.
42
+ - i18n message catalogs (if the project is localized).
43
+
44
+ ## Outputs
45
+ - A code change (PR or branch) with a test.
46
+ - `data/runs/<task-id>/report.md` — what changed, why, and the verdict.
47
+ - For route changes: the list of route names that changed (so an E2E agent can run a
48
+ selective suite) and the bundle-size delta if it grew meaningfully.
49
+
50
+ ## Cross-validation policy
51
+ Default `reviewer:codex` — Claude implements, Codex reviews. The review gate (must
52
+ pass before merge):
53
+ - Type safety — no new `any`, no suppressed errors.
54
+ - Framework rules — hook dependency arrays, effect cleanup, key props, etc.
55
+ - Accessibility — visible focus states, `aria-*` on icon-only controls, labelled inputs.
56
+ - i18n — every visible string comes from the message catalog, not a literal.
57
+
58
+ Promote to `runner: both` (independent re-implementation, not just review) when the
59
+ change touches a high-stakes surface — auth, payments, anything user-data-bearing.
60
+
61
+ ## Failure handling
62
+ - Missing test → block self-merge, escalate to a human.
63
+ - Build fails → revert the worktree, log the error with `file:line`, mark
64
+ `needs_human`.
65
+ - Codex flags ≥ 2 blocking issues → re-implement (or escalate).
66
+
67
+ ## Example
68
+ ```
69
+ Trigger: PR adds /admin/coupons without lazy()
70
+ Claude: detects, wraps in lazy() + Suspense fallback, re-runs typecheck → pass
71
+ Codex: confirms no dangling reference, but the route label is missing one locale → flags
72
+ Resolve: frontend-agent adds the missing translation; PR ready for human review
73
+ ```
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: monitor-agent
3
+ mission: >-
4
+ Watch external observability signals (error tracking, hosting logs, app metrics)
5
+ and convert anomalies into kanban tasks routed to the right specialist.
6
+ runner: codex
7
+ model_default: gpt-5.4
8
+ tools_allowed: [WebFetch, Bash, Write]
9
+ worktree: inline
10
+ escalation: orchestrator
11
+ ---
12
+
13
+ # Monitor Agent
14
+
15
+ Polls 24/7 (via `lib/watch/scheduler.cjs`), so it must run cheap. Default model is
16
+ the second model (Codex / GPT) — log-pattern recognition and anomaly classification
17
+ play to its strengths — but it falls back to a cheap Claude tier when the daily budget
18
+ is exhausted (see `lib/runner/budget.cjs`).
19
+
20
+ ## Triggers
21
+ - Cron every `WATCH_INTERVAL_MS` (default 5 min).
22
+ - A manual `/monitor-once`.
23
+ - (Future) a webhook from external alerting.
24
+
25
+ ## Inputs
26
+ Whatever detectors you enable in `config.js → detectors` (each maps to a module under
27
+ `lib/detect/`). Built-in detectors degrade gracefully when their env vars are blank —
28
+ they surface "config missing" as a low-severity task instead of crashing:
29
+ - `sentry` — error groups + error-rate spikes (`SENTRY_AUTH_TOKEN`, `SENTRY_ORG_SLUG`, `SENTRY_PROJECT_SLUG`).
30
+ - `vercel` — deploy state + 5xx rate (`VERCEL_TOKEN`, `VERCEL_PROJECT_ID`, optional `VERCEL_TEAM_ID`).
31
+ - `_template` — skeleton; copy it to wire up Datadog / CloudWatch / Prometheus / a custom endpoint.
32
+ Plus the local trend cache at `data/runs/watch-state.json` for baselines.
33
+
34
+ ## Outputs
35
+ - A new task (in the "needs human" column for high severity, otherwise routed to a
36
+ specialist) when an anomaly is detected.
37
+ - `data/runs/watch-findings/sweep-<timestamp>.md` — what each sweep found.
38
+ - An hourly trend snapshot for the standup.
39
+
40
+ ## Anomaly rules (declarative — `lib/detect/rules.json`)
41
+ Each rule maps a detector signal to a severity and a routing target. Example shape:
42
+
43
+ | Signal | Threshold | Severity | Routes to |
44
+ |---|---|---|---|
45
+ | error-rate spike (5m) | > 3× rolling baseline | high | frontend-agent / backend-agent |
46
+ | host 5xx rate (5m) | > 0.5% | high | backend-agent |
47
+ | deploy failure | state = ERROR | high | deploy-gate-agent |
48
+ | bundle size on deploy | > +10% | low | frontend-agent |
49
+
50
+ Edit `rules.json` freely — the scheduler re-reads it on every sweep, no restart needed.
51
+
52
+ ## Cross-validation policy
53
+ Single-model for routine polling. Auto-promote a finding to `both` (independent second
54
+ analysis) when the severity is high, or when the same anomaly recurs ≥ 3 times in 24h
55
+ (a subtle root cause).
56
+
57
+ ## Failure handling
58
+ - API rate-limited → exponential backoff, note it in the standup.
59
+ - API down > 30 min → degrade to last-known-good, alert in your infra channel.
60
+ - False-positive rate > 20% over a week → schedule a rule-retuning task.
61
+
62
+ ## Cost management
63
+ - Per-signal cache TTLs; one daily summary instead of per-poll logs.
64
+ - `DAILY_CODEX_BUDGET` enforced — falls back to a cheap Claude tier for routine
65
+ summaries once the budget is spent.
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: orchestrator
3
+ mission: >-
4
+ Route incoming work to the right specialist agent, enforce the task state
5
+ machine, resolve cross-validation disagreements, and run the daily standup.
6
+ runner: claude
7
+ model_default: claude-opus-4-7
8
+ tools_allowed: [Read, Edit, Bash]
9
+ worktree: inline
10
+ escalation: human
11
+ ---
12
+
13
+ # Orchestrator
14
+
15
+ Single decision-maker for routing and state transitions. Never modifies application
16
+ code directly — it delegates to specialist agents (frontend-agent, backend-agent,
17
+ deploy-gate-agent, monitor-agent, …). Edit the routing rules below to match your
18
+ project; the rest of the contract is generic.
19
+
20
+ ## Triggers
21
+ - **A user instruction is received (any session, any channel).** The orchestrator's
22
+ first responsibility is to register that instruction as a kanban task *before* any
23
+ work begins — see "Kanban-first instruction protocol" below.
24
+ - A new task is created (any source — UI, API, a detector, a playbook).
25
+ - A task update arrives with `metadata.crossValidation.agreement = "disagreed"`.
26
+ - The daily standup cron fires.
27
+ - A manual `/triage` is requested.
28
+
29
+ ## Inputs
30
+ - The full task list (`GET /api/tasks`).
31
+ - Agent capabilities (`agents/*.md` frontmatter, exposed at `GET /api/agents`).
32
+ - Per-run reports under `data/runs/<task-id>/report.md`.
33
+
34
+ ## Outputs
35
+ - `task.agent` and `task.metadata.runner` set on every task it routes.
36
+ - `data/runs/<task-id>/decision.md` — the routing rationale.
37
+ - `data/runs/standup-<date>.md`.
38
+ - A kanban task record for every user instruction, created before dispatch.
39
+
40
+ ## Kanban-first instruction protocol (the orchestrator's primary duty)
41
+ Every user instruction must become a kanban task **before** specialist agents start.
42
+
43
+ 1. Capture the instruction verbatim into `description`; derive a concise `subject`
44
+ (`[TAG] gist`).
45
+ 2. Apply the routing rules below to set `agent`, `metadata.runner`, `priority`.
46
+ 3. `POST /api/tasks` to create the task. Transition it to `in_progress` only after
47
+ the agent assignment is confirmed.
48
+ 4. On completion, write `data/runs/<task-id>/decision.md` and set the task's
49
+ `reportPath` + `reportSummary` before marking it `completed`.
50
+ 5. **Exception — incident response**: a production-impacting incident or a 1-line,
51
+ obviously-reversible hotfix may be executed immediately, but the orchestrator
52
+ must register a post-hoc task within 1 hour, tagged `metadata.source =
53
+ "incident-response"`, with the action taken and any follow-up. Nothing else
54
+ qualifies — refactors, docs, features, ordinary bugs all go through step 1 first.
55
+
56
+ See docs/the-pattern.md → "Kanban-first" for the rationale.
57
+
58
+ ## Routing rules (edit for your project)
59
+ 1. Task already has an explicit `metadata.agent` → respect it.
60
+ 2. Task touches files matching exactly one agent's `owns` glob → assign that agent.
61
+ 3. Task severity ≥ `medium` → set `runner: both` (independent cross-validation).
62
+ 4. The same area had a regression in the last 30 days → set `runner: reviewer:codex`.
63
+ 5. None of the above → ask a human (Slack thread, or push to the "needs human" column).
64
+
65
+ ## Cross-validation policy
66
+ The orchestrator itself runs single-model (`claude`). State-machine decisions must be
67
+ deterministic; a second opinion adds latency without improving correctness.
68
+
69
+ ## Failure handling
70
+ - No owning agent found → label `unrouted`, move to the "needs human" column.
71
+ - Disagreement deadlock → freeze the task, post the diff, wait for a human verdict.
72
+ - Agent unresponsive past its timeout → reassign to its declared backup, or to a human.
73
+
74
+ ## State machine
75
+
76
+ ```
77
+ (user instruction) → pending → triaging → in_progress → in_review → completed
78
+
79
+ blocked / needs_human
80
+
81
+ incident-response: (immediate work) → post-hoc pending → in_progress → completed (≤1h)
82
+ ```
83
+
84
+ Rules:
85
+ - Only the orchestrator writes status transitions. Specialist agents write `verdict`
86
+ (in their report) and the report fields only.
87
+ - No transition into `in_progress` without a kanban task record. A session that
88
+ starts work without one is a protocol violation — retro-create the task, back-fill
89
+ `startedAt`, and log it to `data/runs/protocol-violations-<date>.md`.
90
+ - `completed` requires `reportPath` and `reportSummary`. Tasks missing them bounce
91
+ back to `in_review`.
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: reviewer-codex
3
+ mission: >-
4
+ Notes on the "Claude implements, Codex reviews" pattern — not a standalone agent,
5
+ but the reviewer half of any agent whose `runner` is `reviewer:codex`.
6
+ runner: reviewer:codex
7
+ model_default: gpt-5.4
8
+ tools_allowed: [Read]
9
+ worktree: inline
10
+ escalation: orchestrator
11
+ ---
12
+
13
+ # Reviewer (Codex) — the cross-validation review role
14
+
15
+ This file documents the *reviewer* leg of the `reviewer:codex` runner. Most agents
16
+ (frontend-agent, deploy-gate-agent, route work, …) use it: Claude does the work,
17
+ Codex inspects the result and can veto. You do not normally assign tasks to
18
+ `reviewer-codex` directly — you set another agent's `runner: reviewer:codex` and the
19
+ runner (`lib/runner/adapters/reviewer.cjs`) wires this in automatically.
20
+
21
+ ## How it works (see lib/runner/adapters/reviewer.cjs)
22
+ 1. **Executor stage** — the primary model (Claude) runs the task in an isolated git
23
+ worktree and produces a report with a `verdict` + `confidence`.
24
+ 2. **Reviewer stage** — the second model (Codex) is handed the executor's full report
25
+ (no worktree, no code changes) and asked to surface anything the executor missed.
26
+ 3. **Resolution** —
27
+ - Reviewer concurs → final verdict = executor's verdict, `agreement: agreed`.
28
+ - Reviewer flags `needs_human` / `fail` → final verdict downgraded to `needs_human`,
29
+ `agreement: disagreed`, task moves to the "needs human" column.
30
+ - Reviewer disagrees but not blockingly → `agreement: partial`, lower confidence wins.
31
+
32
+ ## When to use `reviewer:codex` vs `both` vs single-model
33
+ - **single-model** (`claude` or `codex`) — deterministic / mechanical work where a
34
+ second opinion only adds latency (running a test suite, polling an API, a state
35
+ transition).
36
+ - **reviewer:codex** — implementation work where a fast independent review catches
37
+ most mistakes: front-end features, routing, the deploy gate, refactors.
38
+ - **both** — high-stakes work where you want two *independent* implementations to
39
+ converge before shipping: schema migrations, access-control policies, anything that
40
+ can corrupt or leak data, money paths. Disagreement here is the safety feature.
41
+
42
+ See docs/the-pattern.md → "Multi-agent cross-validation" for the full reasoning.
43
+
44
+ ## What a good Codex review checks
45
+ - Did the executor miss an edge case the spec implies?
46
+ - Type/contract safety — anything weakened, suppressed, or `any`-ed?
47
+ - Side effects — did a change ripple somewhere the executor didn't check (a removed
48
+ symbol still referenced, a new env var not in `.env.example`, a new heavy dep)?
49
+ - Security & data — for backend work, are access rules enumerated and migrations reversible?
50
+ - Output discipline — does the report follow the agreed format (frontmatter verdict,
51
+ Summary, Findings with `file:line`, Recommended action)?
package/bin/cli.js ADDED
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * kanban-system CLI — npx entry point.
4
+ *
5
+ * npx kanban-system init <name> scaffold a fresh kanban-system into ./<name>
6
+ * npx kanban-system start start the kanban server (server/kanban.cjs)
7
+ * npx kanban-system watch [--once] run the 24h watch scheduler
8
+ * npx kanban-system gate run the pre-deploy gate
9
+ * npx kanban-system whoami GET /api/telegram/whoami on a running server
10
+ * npx kanban-system --help usage
11
+ * npx kanban-system --version
12
+ *
13
+ * Designed to run with zero install: every file the harness needs ships inside
14
+ * the npm tarball (see package.json → files). `init` copies them into the user's
15
+ * working directory; the other commands are thin shells over the existing
16
+ * server/ + lib/ scripts.
17
+ */
18
+ const fs = require("fs");
19
+ const path = require("path");
20
+ const { spawn, spawnSync } = require("child_process");
21
+
22
+ const PKG_ROOT = path.resolve(__dirname, "..");
23
+ const VERSION = (() => { try { return require(path.join(PKG_ROOT, "package.json")).version; } catch { return "?"; } })();
24
+
25
+ const args = process.argv.slice(2);
26
+ const cmd = args[0] || "help";
27
+
28
+ function help() {
29
+ console.log(`kanban-system v${VERSION}
30
+
31
+ npx kanban-system init <name> scaffold a new kanban-system into ./<name>
32
+ npx kanban-system start [--port N] start the kanban server (default port 8080)
33
+ npx kanban-system watch [--once] run the 24h watch scheduler
34
+ npx kanban-system gate run the pre-deploy gate
35
+ npx kanban-system whoami dump recent Telegram getUpdates (find chat id)
36
+ npx kanban-system --help
37
+ npx kanban-system --version
38
+
39
+ A typical flow:
40
+
41
+ npx kanban-system init my-board # creates ./my-board
42
+ cd my-board
43
+ cp config.example.js config.js # then edit: repoPath, deployCommands
44
+ cp .env.example .env # then fill in TELEGRAM_BOT_TOKEN etc. (optional)
45
+ npm start # → http://localhost:8080
46
+ `);
47
+ }
48
+
49
+ // ── init: scaffold a fresh checkout ──────────────────────────────────────────
50
+ function cmdInit() {
51
+ const name = args[1];
52
+ if (!name) { console.error("error: missing <name>. usage: npx kanban-system init <name>"); process.exit(1); }
53
+ if (name.startsWith("-") || name.includes("..") || name.includes("/")) {
54
+ console.error("error: <name> must be a plain directory name (no slashes, no leading dash)");
55
+ process.exit(1);
56
+ }
57
+ const dest = path.resolve(process.cwd(), name);
58
+ if (fs.existsSync(dest)) {
59
+ const entries = fs.readdirSync(dest).filter((f) => !f.startsWith("."));
60
+ if (entries.length) { console.error(`error: ${dest} exists and is not empty`); process.exit(1); }
61
+ } else {
62
+ fs.mkdirSync(dest, { recursive: true });
63
+ }
64
+
65
+ // What ships to the user. Order matches the README's "Layout" table.
66
+ // Anything not in this list (.git, node_modules, bin/, etc.) is omitted.
67
+ const COPY = [
68
+ "server", "ui", "lib", "agents", "playbooks", "hooks", "skills", "docs",
69
+ "config.example.js", ".env.example", ".gitignore",
70
+ "package.json", "README.md", "CLAUDE.md",
71
+ ];
72
+ let copied = 0;
73
+ for (const rel of COPY) {
74
+ const src = path.join(PKG_ROOT, rel);
75
+ if (!fs.existsSync(src)) continue;
76
+ copyRecursive(src, path.join(dest, rel));
77
+ copied++;
78
+ }
79
+
80
+ // Strip bin/ and the executable bit reference from the scaffolded package.json,
81
+ // since the user is now consuming the harness as a project, not a CLI.
82
+ try {
83
+ const pkgPath = path.join(dest, "package.json");
84
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
85
+ pkg.name = name;
86
+ pkg.version = "0.1.0";
87
+ pkg.private = true;
88
+ delete pkg.bin;
89
+ delete pkg.files;
90
+ delete pkg.publishConfig;
91
+ pkg.description = `${name} — kanban-system instance (multi-agent harness)`;
92
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
93
+ } catch {}
94
+
95
+ console.log(`✓ kanban-system v${VERSION} scaffolded into ${dest} (${copied} top-level entries)`);
96
+ console.log("");
97
+ console.log("Next:");
98
+ console.log(` cd ${name}`);
99
+ console.log(" cp config.example.js config.js # edit: repoPath, deployCommands");
100
+ console.log(" cp .env.example .env # (optional) fill TELEGRAM_BOT_TOKEN, SENTRY_TOKEN, …");
101
+ console.log(" npm install # of which there is virtually none");
102
+ console.log(" npm start # → http://localhost:8080");
103
+ console.log("");
104
+ console.log("Optional — turn it into its own git repo:");
105
+ console.log(" git init && git add -A && git commit -m \"init: kanban-system instance\"");
106
+ console.log("");
107
+ console.log("Docs: README.md (Quick start) · docs/adapting-to-your-project.md");
108
+ }
109
+
110
+ function copyRecursive(src, dest) {
111
+ const st = fs.statSync(src);
112
+ if (st.isDirectory()) {
113
+ fs.mkdirSync(dest, { recursive: true });
114
+ for (const entry of fs.readdirSync(src)) {
115
+ // never copy these — they're harness-author state, not user-template content
116
+ if (entry === "node_modules" || entry === ".git" || entry === ".DS_Store") continue;
117
+ copyRecursive(path.join(src, entry), path.join(dest, entry));
118
+ }
119
+ } else {
120
+ fs.copyFileSync(src, dest);
121
+ }
122
+ }
123
+
124
+ // ── start / watch / gate — thin wrappers ─────────────────────────────────────
125
+ function runNode(scriptRel, extraArgs, extraEnv) {
126
+ // Prefer the user's local checkout if present (so `npx kanban-system start`
127
+ // run inside a scaffolded directory uses *that* directory's server, with the
128
+ // user's config.js and ui/ edits). Otherwise fall back to the package.
129
+ const local = path.join(process.cwd(), scriptRel);
130
+ const scriptPath = fs.existsSync(local) ? local : path.join(PKG_ROOT, scriptRel);
131
+ const env = Object.assign({}, process.env, extraEnv || {});
132
+ const child = spawn(process.execPath, [scriptPath, ...(extraArgs || [])], { stdio: "inherit", env });
133
+ child.on("exit", (code) => process.exit(code == null ? 0 : code));
134
+ }
135
+
136
+ function cmdStart() {
137
+ const portIdx = args.indexOf("--port");
138
+ const port = portIdx > -1 ? args[portIdx + 1] : null;
139
+ runNode("server/kanban.cjs", [], port ? { PORT: String(port) } : {});
140
+ }
141
+ function cmdWatch() { runNode("lib/watch/scheduler.cjs", args.slice(1)); }
142
+ function cmdGate() { runNode("lib/gate/index.cjs", args.slice(1)); }
143
+
144
+ // ── whoami — probe a running server for Telegram chat id ─────────────────────
145
+ async function cmdWhoami() {
146
+ const base = process.env.KANBAN_BASE || "http://localhost:8080";
147
+ try {
148
+ const r = await fetch(`${base}/api/telegram/whoami`);
149
+ const j = await r.json();
150
+ if (j.error) { console.error(`error: ${j.error}`); process.exit(1); }
151
+ if (!j.ok || !Array.isArray(j.chats) || !j.chats.length) {
152
+ console.log("No recent messages. Send any DM to your bot from your Telegram account, then retry.");
153
+ return;
154
+ }
155
+ console.log("Recent chats (copy the id of yours into .env → TELEGRAM_CHAT_ID):");
156
+ for (const c of j.chats) console.log(` id=${c.id} type=${c.type} name=${[c.first_name, c.last_name].filter(Boolean).join(" ") || c.title || c.username || "(unknown)"}`);
157
+ } catch (e) {
158
+ console.error(`error: cannot reach ${base} — is the server running? (npx kanban-system start)`);
159
+ process.exit(1);
160
+ }
161
+ }
162
+
163
+ // ── dispatch ─────────────────────────────────────────────────────────────────
164
+ if (cmd === "--version" || cmd === "-v") { console.log(VERSION); process.exit(0); }
165
+ if (cmd === "--help" || cmd === "-h" || cmd === "help") { help(); process.exit(0); }
166
+ if (cmd === "init") cmdInit();
167
+ else if (cmd === "start") cmdStart();
168
+ else if (cmd === "watch") cmdWatch();
169
+ else if (cmd === "gate") cmdGate();
170
+ else if (cmd === "whoami") cmdWhoami();
171
+ else { console.error(`unknown command: ${cmd}`); help(); process.exit(1); }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * kanban-system — per-project configuration.
3
+ *
4
+ * Copy to config.js and edit. config.js is gitignored — keep it out of version
5
+ * control if it contains anything you would not want public. (Tokens belong in
6
+ * .env, not here.)
7
+ *
8
+ * Everything here is read by:
9
+ * - server/kanban.cjs (port, projectName, slack, repoPath)
10
+ * - lib/gate/index.cjs (repoPath, deployCommands)
11
+ * - lib/runner/* (repoPath for git worktrees, agents)
12
+ * - lib/watch + detect (detectors)
13
+ */
14
+ module.exports = {
15
+ // Display name for the board UI + Slack messages.
16
+ projectName: "My Project",
17
+
18
+ // Absolute path to the application repo this harness drives.
19
+ // The gate runs build/test commands here; runners create git worktrees here.
20
+ repoPath: "/absolute/path/to/your/app-repo",
21
+
22
+ // Port for the kanban dashboard. Env var PORT overrides this.
23
+ kanbanPort: 8080,
24
+
25
+ // Commands the pre-deploy gate runs, in order, from repoPath. Fail-fast.
26
+ // Empty = the gate is a no-op pass — fill this in for your stack. Examples:
27
+ // Node/Vite: [{ name: "01-typecheck", cmd: "npx", args: ["tsc", "--noEmit"] },
28
+ // { name: "02-build", cmd: "npm", args: ["run", "build"] }]
29
+ // // optional E2E: { name: "03-e2e", cmd: "npx", args: ["playwright","test","e2e/golden-path.spec.ts","--reporter=list"] }
30
+ // Rust: [{ name: "01-build", cmd: "cargo", args: ["build", "--release"] },
31
+ // { name: "02-test", cmd: "cargo", args: ["test"] }]
32
+ // Go: [{ name: "01-vet", cmd: "go", args: ["vet", "./..."] },
33
+ // { name: "02-test", cmd: "go", args: ["test", "./..."] }]
34
+ // Python: [{ name: "01-lint", cmd: "ruff", args: ["check", "."] },
35
+ // { name: "02-test", cmd: "pytest", args: ["-q"] }]
36
+ deployCommands: [],
37
+
38
+ // The built-output directory the gate inspects for bundle-size deltas.
39
+ // Set to null to skip bundle inspection.
40
+ buildOutputDir: "dist",
41
+
42
+ // Specialist agents the orchestrator can route to. The `owns` globs are
43
+ // relative to repoPath and are used for "which agent owns this file?" routing.
44
+ // Edit these to match your directory layout.
45
+ agents: [
46
+ {
47
+ name: "orchestrator",
48
+ def: "agents/orchestrator.md",
49
+ runner: "claude",
50
+ },
51
+ {
52
+ name: "frontend-agent",
53
+ def: "agents/frontend-agent.md",
54
+ runner: "reviewer:codex",
55
+ owns: ["src/**", "app/**", "components/**", "pages/**", "styles/**", "public/**"],
56
+ },
57
+ {
58
+ name: "backend-agent",
59
+ def: "agents/backend-agent.md",
60
+ runner: "both",
61
+ owns: ["server/**", "api/**", "db/**", "migrations/**", "lib/**", "functions/**"],
62
+ },
63
+ {
64
+ name: "deploy-gate-agent",
65
+ def: "agents/deploy-gate-agent.md",
66
+ runner: "reviewer:codex",
67
+ owns: [".git/hooks/pre-push"],
68
+ },
69
+ {
70
+ name: "monitor-agent",
71
+ def: "agents/monitor-agent.md",
72
+ runner: "codex",
73
+ },
74
+ ],
75
+
76
+ // Monitoring detectors to run on the 24h watch loop. Each maps to a module in
77
+ // lib/detect/. Add { detector: "<name>", enabled: true } and provide the
78
+ // matching env vars (see .env.example). No monitoring? Leave this empty —
79
+ // copy lib/detect/_template.cjs to write your own.
80
+ detectors: [
81
+ { detector: "sentry", enabled: false },
82
+ { detector: "vercel", enabled: false },
83
+ ],
84
+
85
+ // Slack reporting (optional). Tokens come from .env, not here.
86
+ slack: {
87
+ command: "/kanban",
88
+ },
89
+
90
+ // Telegram Ops Thread mirror (optional). botToken + chatId come from .env;
91
+ // these are knobs you may want to override per-project. Empty token/chatId
92
+ // ⇒ the right-side Ops Thread panel still works locally; nothing is sent
93
+ // to Telegram and no inbound polling happens.
94
+ telegram: {
95
+ // allowedChatIds: ["6131488858"], // optional allowlist; empty ⇒ chatId only
96
+ pollEnabled: true,
97
+ pollIntervalMs: 1500,
98
+ },
99
+ };