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.
- package/.env.example +76 -0
- package/CLAUDE.md +108 -0
- package/README.md +272 -0
- package/agents/_TEMPLATE.md +42 -0
- package/agents/backend-agent.md +81 -0
- package/agents/deploy-gate-agent.md +73 -0
- package/agents/frontend-agent.md +73 -0
- package/agents/monitor-agent.md +65 -0
- package/agents/orchestrator.md +91 -0
- package/agents/reviewer-codex.md +51 -0
- package/bin/cli.js +171 -0
- package/config.example.js +99 -0
- package/docs/adapting-to-your-project.md +155 -0
- package/docs/example-apex.md +86 -0
- package/docs/the-pattern.md +92 -0
- package/hooks/launchd.plist.template +66 -0
- package/hooks/pre-push.sample +61 -0
- package/lib/config.cjs +138 -0
- package/lib/detect/_template.cjs +63 -0
- package/lib/detect/rules.json +28 -0
- package/lib/detect/sentry.cjs +86 -0
- package/lib/detect/vercel.cjs +62 -0
- package/lib/gate/index.cjs +182 -0
- package/lib/runner/adapters/both.cjs +33 -0
- package/lib/runner/adapters/claude.cjs +119 -0
- package/lib/runner/adapters/codex.cjs +43 -0
- package/lib/runner/adapters/reviewer.cjs +91 -0
- package/lib/runner/budget.cjs +75 -0
- package/lib/runner/index.cjs +93 -0
- package/lib/runner/result-merger.cjs +58 -0
- package/lib/runner/worktree-manager.cjs +64 -0
- package/lib/watch/scheduler.cjs +164 -0
- package/package.json +59 -0
- package/playbooks/_TEMPLATE.html +54 -0
- package/playbooks/build-fail.html +57 -0
- package/playbooks/deploy-rollback.html +53 -0
- package/playbooks/e2e-regression.html +58 -0
- package/playbooks/playbook.css +26 -0
- package/playbooks/sentry-spike.html +53 -0
- package/server/kanban.cjs +1152 -0
- package/skills/archive.md +18 -0
- package/skills/gate.md +22 -0
- package/skills/standup.md +24 -0
- package/skills/triage.md +24 -0
- package/ui/kanban.html +628 -0
- package/ui/styles/kanban.css +436 -0
- package/ui/styles/progress.css +315 -0
- 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
|
+
};
|