karajan-code 1.36.0 → 1.37.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/README.md +156 -39
- package/bin/kj-tail +294 -41
- package/docs/README.es.md +150 -47
- package/package.json +2 -1
- package/src/orchestrator/iteration-stages.js +50 -15
- package/src/orchestrator/post-loop-stages.js +25 -11
- package/src/orchestrator/pre-loop-stages.js +28 -20
- package/src/orchestrator/preflight-checks.js +3 -3
- package/src/orchestrator/solomon-escalation.js +3 -2
- package/src/orchestrator.js +6 -5
- package/src/utils/display.js +133 -23
- package/src/utils/injection-guard.js +171 -0
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
-
You describe what you want to build. Karajan orchestrates multiple AI agents to plan it, implement it, test it, review it with SonarQube, and iterate
|
|
25
|
+
You describe what you want to build. Karajan orchestrates multiple AI agents to plan it, implement it, test it, review it with SonarQube, and iterate. No babysitting required.
|
|
26
26
|
|
|
27
27
|
## What is Karajan?
|
|
28
28
|
|
|
@@ -30,7 +30,7 @@ Karajan is a local coding orchestrator. It runs on your machine, uses your exist
|
|
|
30
30
|
|
|
31
31
|
It is not a hosted service. It is not a VS Code extension. It is a tool you install once and use from the terminal or as an MCP server inside your AI agent.
|
|
32
32
|
|
|
33
|
-
The name comes from Herbert von Karajan
|
|
33
|
+
The name comes from Herbert von Karajan, the conductor who believed that the best orchestras are made of great independent musicians who know exactly when to play and when to listen. Same idea, applied to AI agents.
|
|
34
34
|
|
|
35
35
|
## Why not just use Claude Code?
|
|
36
36
|
|
|
@@ -39,16 +39,16 @@ Claude Code is excellent. Use it for interactive, session-based coding.
|
|
|
39
39
|
Use Karajan when you want:
|
|
40
40
|
|
|
41
41
|
- **A repeatable, documented pipeline** that runs the same way every time
|
|
42
|
-
- **TDD by default
|
|
43
|
-
- **SonarQube integration
|
|
44
|
-
- **Solomon as pipeline boss
|
|
45
|
-
- **Multi-provider routing
|
|
46
|
-
- **Zero-config operation
|
|
47
|
-
- **Composable role architecture
|
|
48
|
-
- **Local-first
|
|
49
|
-
- **Zero API costs
|
|
42
|
+
- **TDD by default.** Tests are written before implementation, not after
|
|
43
|
+
- **SonarQube integration.** Code quality gates as part of the flow, not an afterthought
|
|
44
|
+
- **Solomon as pipeline boss.** Every reviewer rejection is evaluated by a supervisor that decides if it's valid or just style noise
|
|
45
|
+
- **Multi-provider routing.** Claude as coder, Codex as reviewer, or any combination
|
|
46
|
+
- **Zero-config operation.** Auto-detects test frameworks, starts SonarQube, simplifies pipeline for trivial tasks
|
|
47
|
+
- **Composable role architecture.** Agent behaviors defined as plain markdown files that travel with your project
|
|
48
|
+
- **Local-first.** Your code, your keys, your machine. No data leaves unless you say so
|
|
49
|
+
- **Zero API costs.** Karajan uses AI agent CLIs (Claude Code, Codex, Gemini CLI), not APIs. You pay your existing subscription (Claude Pro, ChatGPT Plus), not per-token API fees
|
|
50
50
|
|
|
51
|
-
If Claude Code is a smart pair programmer, Karajan is the CI/CD pipeline for AI-assisted development. They work great together
|
|
51
|
+
If Claude Code is a smart pair programmer, Karajan is the CI/CD pipeline for AI-assisted development. They work great together: Karajan is designed to be used as an MCP server inside Claude Code.
|
|
52
52
|
|
|
53
53
|
## Install
|
|
54
54
|
|
|
@@ -58,31 +58,143 @@ npm install -g karajan-code
|
|
|
58
58
|
|
|
59
59
|
That's it. No Docker required (SonarQube uses Docker, but Karajan auto-manages it). No config files to copy. `kj init` auto-detects your installed agents.
|
|
60
60
|
|
|
61
|
-
##
|
|
61
|
+
## Three ways to use Karajan
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
# Run a task — Karajan handles the rest
|
|
65
|
-
kj run "Create a utility function that validates Spanish DNI numbers, with tests"
|
|
66
|
-
```
|
|
63
|
+
Karajan installs **three commands**: `kj`, `kj-tail`, and `karajan-mcp`.
|
|
67
64
|
|
|
68
|
-
|
|
65
|
+
### 1. CLI: direct from terminal
|
|
69
66
|
|
|
70
|
-
Karajan
|
|
71
|
-
1. Triage the task complexity and activate the right roles
|
|
72
|
-
2. Write tests first (TDD)
|
|
73
|
-
3. Implement code to pass those tests
|
|
74
|
-
4. Run SonarQube analysis (auto-starts Docker if needed)
|
|
75
|
-
5. Review the code (Solomon evaluates every rejection)
|
|
76
|
-
6. Iterate until approved or escalate to you
|
|
67
|
+
Run Karajan directly. You see the full pipeline output in real time.
|
|
77
68
|
|
|
78
69
|
```bash
|
|
79
|
-
|
|
70
|
+
kj run "Create a utility function that validates Spanish DNI numbers, with tests"
|
|
80
71
|
kj code "Add input validation to the signup form" # Coder only
|
|
81
72
|
kj review "Check the authentication changes" # Review current diff
|
|
82
73
|
kj audit "Full health analysis of this codebase" # Read-only audit
|
|
83
74
|
kj plan "Refactor the database layer" # Plan without coding
|
|
84
75
|
```
|
|
85
76
|
|
|
77
|
+
### 2. MCP: inside your AI agent
|
|
78
|
+
|
|
79
|
+
This is the primary use case. Karajan runs as an MCP server inside Claude Code, Codex, or Gemini. You ask your AI agent to do something, and it delegates the heavy lifting to Karajan's pipeline.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
You → Claude Code → kj_run (via MCP) → triage → coder → sonar → reviewer → tester → security
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The MCP server auto-registers during `npm install`. Your AI agent sees 20 tools (`kj_run`, `kj_code`, `kj_review`, etc.) and uses them as needed.
|
|
86
|
+
|
|
87
|
+
**The problem**: when Karajan runs inside an AI agent, you lose visibility. The agent shows you the final result, but not the pipeline stages, iterations, or Solomon decisions happening in real time.
|
|
88
|
+
|
|
89
|
+
### 3. kj-tail: monitor from a separate terminal
|
|
90
|
+
|
|
91
|
+
**This is the companion tool.** Open a second terminal in the **same project directory** where your AI agent is working, and run:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
kj-tail
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
You'll see the live pipeline output (stages, results, iterations, errors) as they happen. Same view as running `kj run` directly.
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
kj-tail # Follow pipeline in real time (default)
|
|
101
|
+
kj-tail -v # Verbose: include agent heartbeats and budget
|
|
102
|
+
kj-tail -t # Show timestamps
|
|
103
|
+
kj-tail -s # Snapshot: show current log and exit
|
|
104
|
+
kj-tail -n 50 # Show last 50 lines then follow
|
|
105
|
+
kj-tail --help # Full options
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
> **Important**: `kj-tail` must run from the same directory where the AI agent is executing. It reads `<project>/.kj/run.log`, which is created when Karajan starts a pipeline via MCP.
|
|
109
|
+
|
|
110
|
+
**Typical workflow:**
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
┌─────────────────────────┐ ┌─────────────────────────┐
|
|
114
|
+
│ Terminal 1 │ │ Terminal 2 │
|
|
115
|
+
│ │ │ │
|
|
116
|
+
│ $ claude │ │ $ kj-tail │
|
|
117
|
+
│ > implement the next │ │ │
|
|
118
|
+
│ priority task │ │ ├─ 📋 Triage: medium │
|
|
119
|
+
│ │ │ ├─ 🔬 Researcher ✅ │
|
|
120
|
+
│ (Claude calls kj_run │ │ ├─ 🧠 Planner ✅ │
|
|
121
|
+
│ via MCP, you see │ │ ├─ 🔨 Coder ✅ │
|
|
122
|
+
│ only the final result) │ │ ├─ 🔍 Sonar: OK │
|
|
123
|
+
│ │ │ ├─ 👁️ Reviewer ❌ │
|
|
124
|
+
│ │ │ ├─ ⚖️ Solomon: 2 cond. │
|
|
125
|
+
│ │ │ ├─ 🔨 Coder (iter 2) ✅ │
|
|
126
|
+
│ │ │ ├─ ✅ Review: APPROVED │
|
|
127
|
+
│ │ │ ├─ 🧪 Tester: passed │
|
|
128
|
+
│ │ │ └─ 🏁 Result: APPROVED │
|
|
129
|
+
└─────────────────────────┘ └─────────────────────────┘
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Full pipeline example**, a complex task with all roles:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
┌─ Terminal 1 ─────────────────────────────────────────────────────────────────┐
|
|
136
|
+
│ │
|
|
137
|
+
│ $ claude │
|
|
138
|
+
│ │
|
|
139
|
+
│ > Build a REST API for a booking system. Requirements: │
|
|
140
|
+
│ > - Express + TypeScript with Zod validation on every endpoint │
|
|
141
|
+
│ > - Endpoints: POST /bookings, GET /bookings/:id, PATCH /bookings/:id/cancel│
|
|
142
|
+
│ > - A booking has: id, guestName, roomType (standard|suite|penthouse), │
|
|
143
|
+
│ > checkIn, checkOut, status (confirmed|cancelled) │
|
|
144
|
+
│ > - Validate: checkOut must be after checkIn, no past dates, │
|
|
145
|
+
│ > roomType must be a valid enum value │
|
|
146
|
+
│ > - Cancel returns 409 if already cancelled │
|
|
147
|
+
│ > - Use TDD. Run it through Karajan with architect and planner enabled, │
|
|
148
|
+
│ > paranoid review mode. Coder claude, reviewer codex. │
|
|
149
|
+
│ │
|
|
150
|
+
│ Claude calls kj_run via MCP with: │
|
|
151
|
+
│ --enable-architect --enable-researcher --enable-planner --mode paranoid │
|
|
152
|
+
│ │
|
|
153
|
+
└──────────────────────────────────────────────────────────────────────────────┘
|
|
154
|
+
|
|
155
|
+
┌─ Terminal 2: kj-tail ────────────────────────────────────────────────────────┐
|
|
156
|
+
│ │
|
|
157
|
+
│ kj-tail v1.37.0 — .kj/run.log │
|
|
158
|
+
│ │
|
|
159
|
+
│ ├─ 📋 Triage: medium (sw) — enabling researcher, architect, planner │
|
|
160
|
+
│ ├─ ⚙️ Preflight passed — all checks OK │
|
|
161
|
+
│ ├─ 🔬 Researcher: 8 files analyzed, 3 patterns, 5 constraints │
|
|
162
|
+
│ ├─ 🏗️ Architect: 3-layer design (routes → service → validators) │
|
|
163
|
+
│ ├─ 🧠 Planner: 6 steps — tests first, then routes, service, validators │
|
|
164
|
+
│ │ │
|
|
165
|
+
│ ▶ Iteration 1/5 │
|
|
166
|
+
│ ├─ 🔨 Coder (claude): implemented 3 endpoints + 18 tests │
|
|
167
|
+
│ ├─ 📋 TDD: PASS (3 src, 2 test files) │
|
|
168
|
+
│ ├─ 🔍 Sonar: Quality gate OK — 0 blockers │
|
|
169
|
+
│ ├─ 👁️ Reviewer (codex): REJECTED (2 blocking) │
|
|
170
|
+
│ │ "Missing 404 for GET nonexistent booking" │
|
|
171
|
+
│ │ "Cancel endpoint lacks idempotency test" │
|
|
172
|
+
│ ├─ ⚖️ Solomon: approve_with_conditions (2 conditions) │
|
|
173
|
+
│ │ "Add 404 response and test for GET /bookings/:id with unknown id" │
|
|
174
|
+
│ │ "Add test: cancel already-cancelled booking returns 409, not 500" │
|
|
175
|
+
│ │ │
|
|
176
|
+
│ ▶ Iteration 2/5 │
|
|
177
|
+
│ ├─ 🔨 Coder (claude): fixed — 22 tests now │
|
|
178
|
+
│ ├─ 📋 TDD: PASS │
|
|
179
|
+
│ ├─ 🔍 Sonar: OK │
|
|
180
|
+
│ ├─ 👁️ Reviewer (codex): APPROVED │
|
|
181
|
+
│ ├─ 🧪 Tester: passed — coverage 94%, 22 tests │
|
|
182
|
+
│ ├─ 🔒 Security: passed — 0 critical, 1 low (helmet recommended) │
|
|
183
|
+
│ ├─ 📊 Audit: CERTIFIED (with 3 advisory warnings) │
|
|
184
|
+
│ │ │
|
|
185
|
+
│ 🏁 Result: APPROVED │
|
|
186
|
+
│ 🔬 Research: 8 files, 3 patterns identified │
|
|
187
|
+
│ 🗺 Plan: 6 steps (tests first) │
|
|
188
|
+
│ 🧪 Coverage: 94%, 22 tests │
|
|
189
|
+
│ 🔒 Security: passed │
|
|
190
|
+
│ 🔍 Sonar: OK │
|
|
191
|
+
│ 💰 Budget: $0.42 (claude: $0.38, codex: $0.04) │
|
|
192
|
+
│ │
|
|
193
|
+
└──────────────────────────────────────────────────────────────────────────────┘
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
[**Watch the full pipeline demo**](https://karajancode.com#demo): triage, architecture, TDD, SonarQube, code review, Solomon arbitration, security audit.
|
|
197
|
+
|
|
86
198
|
## The pipeline
|
|
87
199
|
|
|
88
200
|
```
|
|
@@ -105,7 +217,7 @@ hu-reviewer? → triage → discover? → architect? → planner? → coder →
|
|
|
105
217
|
| **reviewer** | Code review with configurable strictness profiles | **Always on** |
|
|
106
218
|
| **tester** | Test quality gate and coverage verification | **On** |
|
|
107
219
|
| **security** | OWASP security audit | **On** |
|
|
108
|
-
| **solomon** | Pipeline boss
|
|
220
|
+
| **solomon** | Pipeline boss: evaluates every rejection, overrides style-only blocks | **On** |
|
|
109
221
|
| **commiter** | Git commit, push, and PR automation after approval | Off |
|
|
110
222
|
| **audit** | Read-only codebase health analysis (5 dimensions, A-F scores) | Standalone |
|
|
111
223
|
|
|
@@ -121,21 +233,25 @@ hu-reviewer? → triage → discover? → architect? → planner? → coder →
|
|
|
121
233
|
|
|
122
234
|
Mix and match. Use Claude as coder and Codex as reviewer. Karajan auto-detects installed agents during `kj init`.
|
|
123
235
|
|
|
124
|
-
## MCP server
|
|
236
|
+
## MCP server (20 tools)
|
|
125
237
|
|
|
126
|
-
|
|
238
|
+
After `npm install -g karajan-code`, the MCP server auto-registers in Claude and Codex. Manual config if needed:
|
|
127
239
|
|
|
128
240
|
```bash
|
|
129
|
-
#
|
|
130
|
-
# Add to ~/.claude.json → "mcpServers":
|
|
241
|
+
# Claude: add to ~/.claude.json → "mcpServers":
|
|
131
242
|
# { "karajan-mcp": { "command": "karajan-mcp" } }
|
|
243
|
+
|
|
244
|
+
# Codex: add to ~/.codex/config.toml → [mcp_servers."karajan-mcp"]
|
|
245
|
+
# command = "karajan-mcp"
|
|
132
246
|
```
|
|
133
247
|
|
|
134
248
|
**20 tools** available: `kj_run`, `kj_code`, `kj_review`, `kj_plan`, `kj_audit`, `kj_scan`, `kj_doctor`, `kj_config`, `kj_report`, `kj_resume`, `kj_roles`, `kj_agents`, `kj_preflight`, `kj_status`, `kj_init`, `kj_discover`, `kj_triage`, `kj_researcher`, `kj_architect`, `kj_impeccable`.
|
|
135
249
|
|
|
250
|
+
Use `kj-tail` in a separate terminal to see what the pipeline is doing in real time (see [Three ways to use Karajan](#three-ways-to-use-karajan)).
|
|
251
|
+
|
|
136
252
|
## The role architecture
|
|
137
253
|
|
|
138
|
-
Every role in Karajan is defined by a markdown file
|
|
254
|
+
Every role in Karajan is defined by a markdown file: a plain document that describes how the agent should behave, what to check, and what good output looks like.
|
|
139
255
|
|
|
140
256
|
```
|
|
141
257
|
.karajan/roles/ # Project overrides (optional)
|
|
@@ -143,7 +259,7 @@ Every role in Karajan is defined by a markdown file — a plain document that de
|
|
|
143
259
|
templates/roles/ # Built-in defaults (shipped with package)
|
|
144
260
|
```
|
|
145
261
|
|
|
146
|
-
You can override any built-in role or create new ones. No code required. The agents read the role files and adapt their behavior.
|
|
262
|
+
You can override any built-in role or create new ones. No code required. The agents read the role files and adapt their behavior. Encode your team's conventions, domain rules, and quality standards, and every run of Karajan applies them automatically.
|
|
147
263
|
|
|
148
264
|
Use `kj roles show <role>` to inspect any template.
|
|
149
265
|
|
|
@@ -152,7 +268,8 @@ Use `kj roles show <role>` to inspect any template.
|
|
|
152
268
|
Karajan auto-detects and auto-configures everything it can:
|
|
153
269
|
|
|
154
270
|
- **TDD**: Detects test framework (vitest, jest, mocha) → auto-enables TDD
|
|
155
|
-
- **Bootstrap gate**: Validates all prerequisites (git repo, remote, config, agents, SonarQube) before any tool runs. Fails hard with actionable fix instructions
|
|
271
|
+
- **Bootstrap gate**: Validates all prerequisites (git repo, remote, config, agents, SonarQube) before any tool runs. Fails hard with actionable fix instructions, never silently degrades
|
|
272
|
+
- **Injection guard**: Scans diffs for prompt injection before AI review. Detects directive overrides, invisible Unicode, oversized comment payloads. Also runs as a GitHub Action on every PR
|
|
156
273
|
- **SonarQube**: Auto-starts Docker container, generates config if missing
|
|
157
274
|
- **Pipeline complexity**: Triage classifies task → trivial tasks skip reviewer loop
|
|
158
275
|
- **Provider outages**: Retries on 500/502/503/504 with backoff (same as rate limits)
|
|
@@ -164,16 +281,16 @@ No per-project configuration required. If you want to customize, config is layer
|
|
|
164
281
|
|
|
165
282
|
Because it should be.
|
|
166
283
|
|
|
167
|
-
Karajan has **
|
|
284
|
+
Karajan has **2044 tests** across 161 files. It runs on Node.js without a build step. You can read the source, understand it, fork it, and modify it without a TypeScript compiler between you and the code.
|
|
168
285
|
|
|
169
|
-
This is a deliberate choice, not a limitation. The tests are the type safety. The legibility is a feature.
|
|
286
|
+
This is a deliberate choice, not a limitation. The tests are the type safety. The legibility is a feature. 57 releases in 45 days. That velocity is possible precisely because vanilla JS with good tests lets you move fast without fear.
|
|
170
287
|
|
|
171
288
|
## Recommended companions
|
|
172
289
|
|
|
173
290
|
| Tool | Why |
|
|
174
291
|
|------|-----|
|
|
175
292
|
| [**RTK**](https://github.com/rtk-ai/rtk) | Reduces token consumption by 60-90% on Bash command outputs |
|
|
176
|
-
| [**Planning Game MCP**](https://github.com/AgenteIA-Geniova/planning-game-mcp) | Agile project management (tasks, sprints, estimation)
|
|
293
|
+
| [**Planning Game MCP**](https://github.com/AgenteIA-Geniova/planning-game-mcp) | Agile project management (tasks, sprints, estimation), XP-native |
|
|
177
294
|
| [**GitHub MCP**](https://github.com/modelcontextprotocol/servers/tree/main/src/github) | Create PRs, manage issues directly from the agent |
|
|
178
295
|
| [**Chrome DevTools MCP**](https://github.com/anthropics/anthropic-quickstarts/tree/main/chrome-devtools-mcp) | Verify UI changes visually after frontend modifications |
|
|
179
296
|
|
|
@@ -183,11 +300,11 @@ This is a deliberate choice, not a limitation. The tests are the type safety. Th
|
|
|
183
300
|
git clone https://github.com/manufosela/karajan-code.git
|
|
184
301
|
cd karajan-code
|
|
185
302
|
npm install
|
|
186
|
-
npm test # Run
|
|
303
|
+
npm test # Run 2044 tests with Vitest
|
|
187
304
|
npm run validate # Lint + test
|
|
188
305
|
```
|
|
189
306
|
|
|
190
|
-
Issues and pull requests welcome. If something doesn't work as documented, [open an issue](https://github.com/manufosela/karajan-code/issues)
|
|
307
|
+
Issues and pull requests welcome. If something doesn't work as documented, [open an issue](https://github.com/manufosela/karajan-code/issues). That's the most useful contribution at this stage.
|
|
191
308
|
|
|
192
309
|
## Links
|
|
193
310
|
|
|
@@ -199,4 +316,4 @@ Issues and pull requests welcome. If something doesn't work as documented, [open
|
|
|
199
316
|
|
|
200
317
|
---
|
|
201
318
|
|
|
202
|
-
Built by [@manufosela](https://github.com/manufosela)
|
|
319
|
+
Built by [@manufosela](https://github.com/manufosela). Head of Engineering at Geniova Technologies, co-organizer of NodeJS Madrid, author of [Liderazgo Afectivo](https://www.amazon.es/dp/B0D7F4C8KC). 90+ npm packages published.
|
package/bin/kj-tail
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# kj-tail —
|
|
3
|
-
#
|
|
2
|
+
# kj-tail — Real-time pipeline monitor for Karajan Code
|
|
3
|
+
# Follows the run log and displays colorized, filtered output.
|
|
4
4
|
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
VERSION="1.36.0"
|
|
8
|
+
|
|
9
|
+
# ── Colors ──────────────────────────────────────────────
|
|
8
10
|
RED='\033[0;31m'
|
|
9
11
|
GREEN='\033[0;32m'
|
|
10
12
|
YELLOW='\033[0;33m'
|
|
@@ -13,58 +15,309 @@ CYAN='\033[0;36m'
|
|
|
13
15
|
MAGENTA='\033[0;35m'
|
|
14
16
|
GRAY='\033[0;90m'
|
|
15
17
|
BOLD='\033[1m'
|
|
18
|
+
DIM='\033[2m'
|
|
16
19
|
RESET='\033[0m'
|
|
17
20
|
|
|
21
|
+
# ── Defaults ────────────────────────────────────────────
|
|
18
22
|
VERBOSE=false
|
|
23
|
+
SHOW_AGENT_OUTPUT=false
|
|
24
|
+
SHOW_TIMESTAMPS=false
|
|
25
|
+
FOLLOW=true
|
|
26
|
+
NUM_LINES=0
|
|
19
27
|
PROJECT_DIR=""
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
# ── Help ────────────────────────────────────────────────
|
|
30
|
+
show_help() {
|
|
31
|
+
cat <<EOF
|
|
32
|
+
${BOLD}kj-tail${RESET} — Real-time pipeline monitor for Karajan Code
|
|
33
|
+
|
|
34
|
+
${BOLD}USAGE${RESET}
|
|
35
|
+
kj-tail [options] [project-dir]
|
|
36
|
+
|
|
37
|
+
${BOLD}DESCRIPTION${RESET}
|
|
38
|
+
Follows the Karajan run log (<project>/.kj/run.log) and displays
|
|
39
|
+
colorized, filtered output. By default shows the same pipeline
|
|
40
|
+
progress view as 'kj run': stages, results, iterations, and errors.
|
|
41
|
+
|
|
42
|
+
Run this in a separate terminal while kj executes via MCP/CLI.
|
|
43
|
+
|
|
44
|
+
${BOLD}OPTIONS${RESET}
|
|
45
|
+
-v, --verbose Show agent heartbeats, budget, and metadata
|
|
46
|
+
-a, --agent-output Show raw agent output lines (very noisy)
|
|
47
|
+
-t, --timestamps Show timestamps on each line
|
|
48
|
+
-n, --lines <N> Show last N lines then follow (default: follow only new)
|
|
49
|
+
-s, --snapshot Show current log (no follow) — like 'cat' with colors
|
|
50
|
+
-h, --help Show this help
|
|
51
|
+
--version Show version
|
|
52
|
+
|
|
53
|
+
${BOLD}EXAMPLES${RESET}
|
|
54
|
+
kj-tail # Follow current directory's run log
|
|
55
|
+
kj-tail /path/to/project # Follow a specific project's log
|
|
56
|
+
kj-tail -v # Verbose: include heartbeats and budget
|
|
57
|
+
kj-tail -a # Show everything including agent output
|
|
58
|
+
kj-tail -n 50 # Show last 50 lines then follow
|
|
59
|
+
kj-tail -s # Snapshot: show full log and exit
|
|
60
|
+
kj-tail -t # Show timestamps
|
|
61
|
+
|
|
62
|
+
${BOLD}LOG LOCATION${RESET}
|
|
63
|
+
<project-dir>/.kj/run.log
|
|
64
|
+
Created automatically when kj_run, kj_code, or kj_review starts.
|
|
65
|
+
|
|
66
|
+
EOF
|
|
67
|
+
exit 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# ── Parse args ──────────────────────────────────────────
|
|
71
|
+
while [[ $# -gt 0 ]]; do
|
|
72
|
+
case "$1" in
|
|
73
|
+
-h|--help) show_help ;;
|
|
74
|
+
--version) echo "kj-tail $VERSION"; exit 0 ;;
|
|
75
|
+
-v|--verbose) VERBOSE=true; shift ;;
|
|
76
|
+
-a|--agent-output) SHOW_AGENT_OUTPUT=true; VERBOSE=true; shift ;;
|
|
77
|
+
-t|--timestamps) SHOW_TIMESTAMPS=true; shift ;;
|
|
78
|
+
-s|--snapshot) FOLLOW=false; shift ;;
|
|
79
|
+
-n|--lines)
|
|
80
|
+
shift
|
|
81
|
+
NUM_LINES="${1:-50}"
|
|
82
|
+
shift
|
|
83
|
+
;;
|
|
84
|
+
-*)
|
|
85
|
+
echo -e "${RED}Unknown option: $1${RESET}" >&2
|
|
86
|
+
echo "Run 'kj-tail --help' for usage." >&2
|
|
87
|
+
exit 1
|
|
88
|
+
;;
|
|
89
|
+
*) PROJECT_DIR="$1"; shift ;;
|
|
25
90
|
esac
|
|
26
91
|
done
|
|
27
92
|
|
|
28
93
|
PROJECT_DIR="${PROJECT_DIR:-$(pwd)}"
|
|
29
94
|
LOG_FILE="${PROJECT_DIR}/.kj/run.log"
|
|
30
95
|
|
|
96
|
+
# ── Locate log ──────────────────────────────────────────
|
|
31
97
|
if [[ ! -f "$LOG_FILE" ]]; then
|
|
32
|
-
echo -e "${RED}No run
|
|
33
|
-
echo "
|
|
98
|
+
echo -e "${RED}No run log found at ${LOG_FILE}${RESET}"
|
|
99
|
+
echo ""
|
|
100
|
+
echo "The run log is created when Karajan starts a pipeline."
|
|
101
|
+
echo "Make sure you're in the project directory, or pass it as argument:"
|
|
102
|
+
echo ""
|
|
103
|
+
echo " kj-tail /path/to/project"
|
|
104
|
+
echo ""
|
|
34
105
|
exit 1
|
|
35
106
|
fi
|
|
36
107
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
108
|
+
# ── Filter & colorize ──────────────────────────────────
|
|
109
|
+
filter_line() {
|
|
110
|
+
local line="$1"
|
|
111
|
+
local ts=""
|
|
112
|
+
local clean="$line"
|
|
40
113
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
# Strip [agent:output] tag — it's the default, no need to show it
|
|
46
|
-
clean="${clean/\[agent:output\] /}"
|
|
47
|
-
|
|
48
|
-
# Colorize by content
|
|
49
|
-
if [[ "$clean" == *"[coder:start]"* ]] || [[ "$clean" == *"[coder:done]"* ]] || [[ "$clean" == *"[coder]"* ]]; then
|
|
50
|
-
echo -e "${GREEN}${clean}${RESET}"
|
|
51
|
-
elif [[ "$clean" == *"[reviewer"* ]]; then
|
|
52
|
-
echo -e "${YELLOW}${clean}${RESET}"
|
|
53
|
-
elif [[ "$clean" == *"[sonar"* ]]; then
|
|
54
|
-
echo -e "${BLUE}${clean}${RESET}"
|
|
55
|
-
elif [[ "$clean" == *"[solomon"* ]]; then
|
|
56
|
-
echo -e "${MAGENTA}${clean}${RESET}"
|
|
57
|
-
elif [[ "$clean" == *"[iteration"* ]] || [[ "$clean" == *"[session"* ]] || [[ "$clean" == *"[kj_run]"* ]] || [[ "$clean" == *"[kj_code]"* ]]; then
|
|
58
|
-
echo -e "${BOLD}${CYAN}${clean}${RESET}"
|
|
59
|
-
elif [[ "$clean" == *"fail"* ]] || [[ "$clean" == *"error"* ]] || [[ "$clean" == *"FAIL"* ]] || [[ "$clean" == *"ERROR"* ]]; then
|
|
60
|
-
echo -e "${RED}${clean}${RESET}"
|
|
61
|
-
elif [[ "$clean" == *"[agent:heartbeat]"* ]]; then
|
|
62
|
-
echo -e "${GRAY}${clean}${RESET}"
|
|
63
|
-
elif [[ "$clean" == *"[standby]"* ]] || [[ "$clean" == *"standby"* ]]; then
|
|
64
|
-
echo -e "${YELLOW}${clean}${RESET}"
|
|
65
|
-
elif [[ "$clean" == "---"* ]]; then
|
|
66
|
-
echo -e "${BOLD}${clean}${RESET}"
|
|
67
|
-
else
|
|
68
|
-
echo "$clean"
|
|
114
|
+
# Extract and optionally strip timestamp (HH:MM:SS.mmm)
|
|
115
|
+
if [[ "$clean" =~ ^([0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3})\ (.*) ]]; then
|
|
116
|
+
ts="${BASH_REMATCH[1]}"
|
|
117
|
+
clean="${BASH_REMATCH[2]}"
|
|
69
118
|
fi
|
|
70
|
-
|
|
119
|
+
|
|
120
|
+
# Skip noise in default mode
|
|
121
|
+
if [[ "$VERBOSE" == "false" ]]; then
|
|
122
|
+
# Skip agent heartbeats
|
|
123
|
+
[[ "$clean" == *"[agent:heartbeat]"* ]] && return
|
|
124
|
+
[[ "$clean" == *"heartbeat"* ]] && return
|
|
125
|
+
# Skip budget lines
|
|
126
|
+
[[ "$clean" == *"Budget:"* ]] && return
|
|
127
|
+
# Skip agent raw output
|
|
128
|
+
[[ "$clean" == *"[agent:output]"* ]] && return
|
|
129
|
+
# Skip internal metadata
|
|
130
|
+
[[ "$clean" == *"[agent:start]"* ]] && return
|
|
131
|
+
[[ "$clean" == *"[agent:end]"* ]] && return
|
|
132
|
+
# Skip noisy JSON dumps (system init, tool lists, etc.)
|
|
133
|
+
[[ "${#clean}" -gt 500 ]] && return
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
if [[ "$SHOW_AGENT_OUTPUT" == "false" ]]; then
|
|
137
|
+
# Skip raw agent output (often JSON/stream data)
|
|
138
|
+
[[ "$clean" == "{"* ]] && [[ "$clean" == *"}" ]] && return
|
|
139
|
+
[[ "$clean" == *'"type":'* ]] && [[ "${#clean}" -gt 200 ]] && return
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# Build prefix
|
|
143
|
+
local prefix=""
|
|
144
|
+
if [[ "$SHOW_TIMESTAMPS" == "true" ]] && [[ -n "$ts" ]]; then
|
|
145
|
+
prefix="${DIM}${ts}${RESET} "
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
# ── Colorize by content ──────────────────────────────
|
|
149
|
+
# Session markers
|
|
150
|
+
if [[ "$clean" == "---"* ]]; then
|
|
151
|
+
echo -e "${prefix}${BOLD}${CYAN}${clean}${RESET}"
|
|
152
|
+
return
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Stage: started/finished
|
|
156
|
+
if [[ "$clean" == *"started"* ]] && [[ "$clean" == *"[kj_"* ]]; then
|
|
157
|
+
echo -e "${prefix}${BOLD}${CYAN}▶ ${clean}${RESET}"
|
|
158
|
+
return
|
|
159
|
+
fi
|
|
160
|
+
if [[ "$clean" == *"finished"* ]] && [[ "$clean" == *"[kj_"* ]]; then
|
|
161
|
+
echo -e "${prefix}${BOLD}${GREEN}✓ ${clean}${RESET}"
|
|
162
|
+
return
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# Iterations
|
|
166
|
+
if [[ "$clean" == *"[iteration]"* ]] || [[ "$clean" == *"Iteration "* ]]; then
|
|
167
|
+
echo -e "${prefix}${BOLD}${clean}${RESET}"
|
|
168
|
+
return
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# Coder
|
|
172
|
+
if [[ "$clean" == *"[coder"* ]] || [[ "$clean" == *"Coder"* ]]; then
|
|
173
|
+
echo -e "${prefix}${GREEN} ├─ 🔨 ${clean}${RESET}"
|
|
174
|
+
return
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# Reviewer
|
|
178
|
+
if [[ "$clean" == *"APPROVED"* ]]; then
|
|
179
|
+
echo -e "${prefix}${GREEN} ├─ ✅ ${clean}${RESET}"
|
|
180
|
+
return
|
|
181
|
+
fi
|
|
182
|
+
if [[ "$clean" == *"REJECTED"* ]]; then
|
|
183
|
+
echo -e "${prefix}${RED} ├─ ❌ ${clean}${RESET}"
|
|
184
|
+
return
|
|
185
|
+
fi
|
|
186
|
+
if [[ "$clean" == *"[reviewer"* ]]; then
|
|
187
|
+
echo -e "${prefix}${YELLOW} ├─ 👁️ ${clean}${RESET}"
|
|
188
|
+
return
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# Solomon
|
|
192
|
+
if [[ "$clean" == *"[solomon"* ]] || [[ "$clean" == *"Solomon"* ]]; then
|
|
193
|
+
echo -e "${prefix}${MAGENTA} ├─ ⚖️ ${clean}${RESET}"
|
|
194
|
+
return
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
# Sonar
|
|
198
|
+
if [[ "$clean" == *"[sonar"* ]] || [[ "$clean" == *"SonarQube"* ]] || [[ "$clean" == *"Quality gate"* ]]; then
|
|
199
|
+
echo -e "${prefix}${BLUE} ├─ 🔍 ${clean}${RESET}"
|
|
200
|
+
return
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# Tester
|
|
204
|
+
if [[ "$clean" == *"[tester"* ]] || [[ "$clean" == *"Tester"* ]]; then
|
|
205
|
+
echo -e "${prefix}${CYAN} ├─ 🧪 ${clean}${RESET}"
|
|
206
|
+
return
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
# Security
|
|
210
|
+
if [[ "$clean" == *"[security"* ]] || [[ "$clean" == *"Security"* ]]; then
|
|
211
|
+
echo -e "${prefix}${CYAN} ├─ 🔒 ${clean}${RESET}"
|
|
212
|
+
return
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
# Researcher
|
|
216
|
+
if [[ "$clean" == *"[researcher"* ]] || [[ "$clean" == *"Researcher"* ]]; then
|
|
217
|
+
echo -e "${prefix}${CYAN} ├─ 🔬 ${clean}${RESET}"
|
|
218
|
+
return
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# Architect
|
|
222
|
+
if [[ "$clean" == *"[architect"* ]] || [[ "$clean" == *"Architect"* ]]; then
|
|
223
|
+
echo -e "${prefix}${CYAN} ├─ 🏗️ ${clean}${RESET}"
|
|
224
|
+
return
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
# Planner
|
|
228
|
+
if [[ "$clean" == *"[planner"* ]] || [[ "$clean" == *"Planner"* ]]; then
|
|
229
|
+
echo -e "${prefix}${CYAN} ├─ 🧠 ${clean}${RESET}"
|
|
230
|
+
return
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# Triage
|
|
234
|
+
if [[ "$clean" == *"[triage"* ]] || [[ "$clean" == *"Triage"* ]]; then
|
|
235
|
+
echo -e "${prefix}${CYAN} ├─ 📋 ${clean}${RESET}"
|
|
236
|
+
return
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
# Audit
|
|
240
|
+
if [[ "$clean" == *"[audit"* ]] || [[ "$clean" == *"Audit"* ]]; then
|
|
241
|
+
echo -e "${prefix}${CYAN} ├─ 📊 ${clean}${RESET}"
|
|
242
|
+
return
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
# Preflight
|
|
246
|
+
if [[ "$clean" == *"[preflight"* ]] || [[ "$clean" == *"Preflight"* ]]; then
|
|
247
|
+
echo -e "${prefix}${GRAY} ├─ ⚙️ ${clean}${RESET}"
|
|
248
|
+
return
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
# TDD
|
|
252
|
+
if [[ "$clean" == *"TDD"* ]] || [[ "$clean" == *"tdd"* ]]; then
|
|
253
|
+
echo -e "${prefix}${GREEN} ├─ 📋 ${clean}${RESET}"
|
|
254
|
+
return
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
# Errors / failures
|
|
258
|
+
if [[ "$clean" == *"fail"* || "$clean" == *"FAIL"* || "$clean" == *"error"* || "$clean" == *"ERROR"* ]]; then
|
|
259
|
+
echo -e "${prefix}${RED} ├─ ⚠️ ${clean}${RESET}"
|
|
260
|
+
return
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
# Standby / rate limit
|
|
264
|
+
if [[ "$clean" == *"[standby]"* ]] || [[ "$clean" == *"standby"* ]] || [[ "$clean" == *"rate limit"* ]]; then
|
|
265
|
+
echo -e "${prefix}${YELLOW} ├─ ⏸️ ${clean}${RESET}"
|
|
266
|
+
return
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
# Model fallback
|
|
270
|
+
if [[ "$clean" == *"not supported"* ]] || [[ "$clean" == *"retrying with"* ]]; then
|
|
271
|
+
echo -e "${prefix}${YELLOW} ├─ 🔄 ${clean}${RESET}"
|
|
272
|
+
return
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# Result line
|
|
276
|
+
if [[ "$clean" == *"Result:"* ]]; then
|
|
277
|
+
echo -e "${prefix}${BOLD}${GREEN}🏁 ${clean}${RESET}"
|
|
278
|
+
return
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
# Budget (verbose only — already filtered above)
|
|
282
|
+
if [[ "$clean" == *"Budget:"* ]]; then
|
|
283
|
+
echo -e "${prefix}${GRAY} ├─ 💰 ${clean}${RESET}"
|
|
284
|
+
return
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
# Agent heartbeat (verbose only)
|
|
288
|
+
if [[ "$clean" == *"heartbeat"* ]] || [[ "$clean" == *"active —"* ]] || [[ "$clean" == *"elapsed"* ]]; then
|
|
289
|
+
echo -e "${prefix}${GRAY} ├─ • ${clean}${RESET}"
|
|
290
|
+
return
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# Default
|
|
294
|
+
echo -e "${prefix} ├─ ${clean}"
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
# ── Header ──────────────────────────────────────────────
|
|
298
|
+
echo -e "${BOLD}${CYAN}kj-tail${RESET} ${DIM}v${VERSION} — ${LOG_FILE}${RESET}"
|
|
299
|
+
if [[ "$FOLLOW" == "true" ]]; then
|
|
300
|
+
echo -e "${GRAY}Ctrl+C to stop${RESET}${VERBOSE:+ ${DIM}(verbose)${RESET}}"
|
|
301
|
+
fi
|
|
302
|
+
echo ""
|
|
303
|
+
|
|
304
|
+
# ── Run ─────────────────────────────────────────────────
|
|
305
|
+
if [[ "$FOLLOW" == "false" ]]; then
|
|
306
|
+
# Snapshot mode: show full log and exit
|
|
307
|
+
while IFS= read -r line; do
|
|
308
|
+
filter_line "$line"
|
|
309
|
+
done < "$LOG_FILE"
|
|
310
|
+
exit 0
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
if [[ "$NUM_LINES" -gt 0 ]]; then
|
|
314
|
+
# Show last N lines then follow
|
|
315
|
+
tail -n "$NUM_LINES" -F "$LOG_FILE" 2>/dev/null | while IFS= read -r line; do
|
|
316
|
+
filter_line "$line"
|
|
317
|
+
done
|
|
318
|
+
else
|
|
319
|
+
# Follow only new lines
|
|
320
|
+
tail -n 0 -F "$LOG_FILE" 2>/dev/null | while IFS= read -r line; do
|
|
321
|
+
filter_line "$line"
|
|
322
|
+
done
|
|
323
|
+
fi
|