@kata-sh/cli 0.1.2 → 0.2.1

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 CHANGED
@@ -1,155 +1,304 @@
1
1
  # Kata CLI
2
2
 
3
- A terminal coding agent built on [pi](https://github.com/badlogic/pi-mono) (`@mariozechner/pi-coding-agent`). Kata CLI bundles a curated set of extensions for structured planning, browser automation, web search, subagent orchestration, and more.
3
+ A terminal coding agent that decomposes projects into milestones, slices, and tasks then executes them autonomously with structured planning, verification, and fresh context windows.
4
+
5
+ Built on [pi](https://github.com/badlogic/pi-mono) (`@mariozechner/pi-coding-agent`).
4
6
 
5
7
  ## Quick Start
6
8
 
7
9
  ```bash
8
- # From the monorepo root
9
- bun install
10
- cd apps/cli
11
- npx tsc
12
- npm run copy-themes
13
- node dist/loader.js
10
+ npx @kata-sh/cli
14
11
  ```
15
12
 
16
- Authenticate with an API key:
13
+ Or install globally:
17
14
 
18
15
  ```bash
19
- export ANTHROPIC_API_KEY=sk-ant-...
20
- node dist/loader.js
16
+ npm install -g @kata-sh/cli
17
+ kata-cli
18
+ ```
19
+
20
+ On first launch, Kata will prompt you to authenticate with an AI provider.
21
+
22
+ ## Getting Started
23
+
24
+ ### 1. Start Kata
25
+
26
+ ```bash
27
+ npx @kata-sh/cli
21
28
  ```
22
29
 
23
- ## Architecture
30
+ ### 2. Log in to a provider
31
+
32
+ ```
33
+ /login
34
+ ```
24
35
 
25
- Kata CLI is a thin wrapper around pi-coding-agent. It does not fork pi it consumes it as an npm dependency and layers on branding, config, and bundled extensions.
36
+ This opens an interactive prompt to authenticate with Anthropic, OpenAI, Google, or any supported provider. You can also set an API key directly:
26
37
 
38
+ ```bash
39
+ ANTHROPIC_API_KEY=sk-ant-... npx @kata-sh/cli
27
40
  ```
28
- apps/cli/
29
- src/
30
- loader.ts — Entry point: sets KATA_* env vars, imports cli.ts
31
- cli.ts — Calls pi-coding-agent's main()
32
- app-paths.ts — ~/.kata-cli/ path constants
33
- resource-loader.ts — Syncs bundled resources to ~/.kata-cli/agent/
34
- wizard.ts — First-run setup, env key hydration
35
- resources/
36
- KATA-WORKFLOW.md — The Kata planning methodology
37
- AGENTS.md — System prompt instructions (synced to agent dir)
38
- agents/ — Agent templates (worker, scout, researcher)
39
- extensions/ — Bundled extensions (see below)
40
- skills/ — Bundled skills
41
- pkg/
42
- package.json — piConfig shim (name: "kata", configDir: ".kata-cli")
43
- dist/ — Theme assets copied from pi-coding-agent
41
+
42
+ ### 3. Select a model
43
+
44
+ ```
45
+ /model
44
46
  ```
45
47
 
46
- ### How It Works
48
+ Pick from available models across your authenticated providers.
47
49
 
48
- 1. `loader.ts` sets `PI_PACKAGE_DIR` to `pkg/` so pi reads Kata's branding config
49
- 2. `loader.ts` sets `KATA_CODING_AGENT_DIR` so pi uses `~/.kata-cli/agent/` instead of `~/.pi/agent/`
50
- 3. `resource-loader.ts` syncs bundled extensions, agents, skills, and `AGENTS.md` to `~/.kata-cli/agent/` on every launch
51
- 4. `cli.ts` calls pi-coding-agent's `main()` — pi handles everything from there
50
+ ### 4. Start working
52
51
 
53
- ## Bundled Extensions
52
+ Tell Kata what you want to build. Kata has three modes of operation:
54
53
 
55
- | Extension | Description |
56
- |-----------|-------------|
57
- | `kata/` | Main extension: `/kata` command, auto-mode, planning, state management |
58
- | `browser-tools/` | Playwright-based browser automation |
59
- | `subagent/` | Spawns child Kata processes for parallel work |
60
- | `slash-commands/` | `/kata-run` and other slash commands |
61
- | `bg-shell/` | Background shell execution |
62
- | `context7/` | Context7 library documentation lookup |
63
- | `search-the-web/` | Web search via Brave API |
64
- | `mac-tools/` | macOS-specific utilities |
65
- | `shared/` | Shared UI components (library, not an entry point) |
54
+ **Step mode** `/kata` — human in the loop (recommended for new or risk-averse users). Kata proposes each step, you approve or redirect.
66
55
 
67
- ## The /kata Command
56
+ **Autonomous mode** — `/kata auto` — researches, plans, executes, verifies, commits, and advances through every slice until the milestone is complete.
68
57
 
69
- The main extension registers `/kata` with subcommands:
58
+ **Steering mode** two terminals for supervised autonomy:
59
+
60
+ ```
61
+ # Terminal 1: autonomous execution
62
+ /kata auto
63
+
64
+ # Terminal 2: observe and steer
65
+ /kata status — check progress
66
+ /kata discuss — discuss decisions
67
+ /kata queue — manage upcoming work
68
+
69
+ # When you need to interrupt and redirect:
70
+ # Terminal 1:
71
+ /kata stop
72
+ ```
73
+
74
+ ## How It Works
75
+
76
+ Kata breaks work into three levels:
77
+
78
+ ```
79
+ Milestone → a shippable version (4–10 slices)
80
+ Slice → one demoable vertical capability (1–7 tasks)
81
+ Task → one context-window-sized unit of work
82
+ ```
83
+
84
+ Each slice flows through phases automatically:
85
+
86
+ **Research** → **Plan** → **Execute** (per task) → **Complete** → **Reassess** → **Next Slice**
87
+
88
+ - **Research** scouts the codebase and relevant docs
89
+ - **Plan** decomposes the slice into tasks with must-haves — mechanically verifiable outcomes
90
+ - **Execute** runs each task in a fresh context window with only the relevant files pre-loaded
91
+ - **Complete** writes the summary, UAT script, marks the roadmap, and commits
92
+ - **Reassess** checks if the roadmap still makes sense given what was learned
93
+
94
+ All planning state lives in `.kata/` at the project root — human-readable markdown files that track milestones, slices, tasks, decisions, and progress.
95
+
96
+ ## Commands
97
+
98
+ ### Kata workflow
70
99
 
71
100
  | Command | Description |
72
101
  |---------|-------------|
73
- | `/kata` | Contextual wizard — smart entry point based on project state |
74
- | `/kata auto` | Start auto-mode (loops fresh sessions until milestone complete) |
75
- | `/kata stop` | Stop auto-mode gracefully |
102
+ | `/kata` | Contextual wizard — suggests next step based on project state |
103
+ | `/kata auto` | Start autonomous mode |
104
+ | `/kata stop` | Stop auto-mode after current task |
76
105
  | `/kata status` | Progress dashboard |
77
- | `/kata queue` | View/manage work queue |
106
+ | `/kata queue` | View/manage milestone queue |
78
107
  | `/kata discuss` | Discuss gray areas before planning |
79
- | `/kata prefs` | Manage preferences (global/project/status) |
108
+ | `/kata prefs` | Manage preferences (global/project) |
80
109
  | `/kata doctor` | Diagnose and fix project state |
110
+ | `/audit` | Audit the codebase against a goal, writes report to `.kata/audits/` |
81
111
 
82
- ### Project State
112
+ ### Session & model
83
113
 
84
- Kata stores planning state in `.kata/` at the project root:
114
+ | Command | Description |
115
+ |---------|-------------|
116
+ | `/login` | Authenticate with an AI provider (OAuth) |
117
+ | `/model` | Select a model |
118
+ | `/scoped-models` | Enable/disable models for `Ctrl+P` cycling |
119
+ | `/new` | Start a new session |
120
+ | `/resume` | Resume a previous session |
121
+ | `/compact` | Manually compact the session context |
122
+ | `/fork` | Create a new fork from a previous message |
123
+ | `/tree` | Navigate session tree (switch branches) |
124
+ | `/session` | Show session info and stats |
125
+
126
+ ### Utilities
127
+
128
+ | Command | Description |
129
+ |---------|-------------|
130
+ | `/mcp` | Show MCP server status and tools |
131
+ | `/gh` | GitHub helper — issues, PRs, labels, milestones, status |
132
+ | `/subagent` | List available subagents |
133
+ | `/export` | Export session to HTML file |
134
+ | `/share` | Share session as a secret GitHub gist |
135
+ | `/copy` | Copy last agent message to clipboard |
136
+ | `/hotkeys` | Show all keyboard shortcuts |
137
+ | `/create-extension` | Scaffold a new extension with interview-driven setup |
138
+ | `/create-slash-command` | Generate a new slash command from a plain-English description |
139
+
140
+ ## Preferences
141
+
142
+ Kata preferences live in `~/.kata-cli/preferences.md` (global) or `.kata-cli/preferences.md` (project-local). Manage with `/kata prefs`.
143
+
144
+ ```yaml
145
+ ---
146
+ version: 1
147
+ models:
148
+ research: claude-sonnet-4-6
149
+ planning: claude-opus-4-6
150
+ execution: claude-sonnet-4-6
151
+ completion: claude-sonnet-4-6
152
+ skill_discovery: suggest
153
+ auto_supervisor:
154
+ soft_timeout_minutes: 20
155
+ idle_timeout_minutes: 10
156
+ hard_timeout_minutes: 30
157
+ budget_ceiling: 50.00
158
+ ---
159
+ ```
160
+
161
+ | Setting | What it controls |
162
+ |---------|-----------------|
163
+ | `models.*` | Per-phase model selection (Opus for planning, Sonnet for execution, etc.) |
164
+ | `skill_discovery` | `auto` / `suggest` / `off` — how Kata finds and applies skills |
165
+ | `auto_supervisor.*` | Timeout thresholds for auto-mode supervision |
166
+ | `budget_ceiling` | USD ceiling — auto mode pauses when reached |
167
+ | `uat_dispatch` | Enable automatic UAT runs after slice completion |
168
+ | `always_use_skills` | Skills to always load when relevant |
169
+ | `skill_rules` | Situational rules for skill routing |
170
+
171
+ ## Project State
172
+
173
+ Kata stores all planning artifacts in `.kata/` at the project root:
85
174
 
86
175
  ```
87
176
  .kata/
88
- STATE.md Dashboard (read first)
89
- DECISIONS.md Append-only decisions register
90
- PROJECT.md — Project description
91
- REQUIREMENTS.md — Requirements tracking
177
+ STATE.md Quick-glance dashboard
178
+ PROJECT.md What the project is (living doc)
179
+ DECISIONS.md — Append-only architecture decisions
180
+ REQUIREMENTS.md — Requirements tracking
92
181
  milestones/
93
182
  M001/
94
- M001-ROADMAP.md Milestone plan with slices
183
+ M001-ROADMAP.md Slices with risk levels and dependencies
184
+ M001-SUMMARY.md — Milestone rollup
95
185
  slices/
96
186
  S01/
97
- S01-PLAN.md Task decomposition
187
+ S01-PLAN.md Tasks with must-haves and estimates
188
+ S01-SUMMARY.md — What was built, what changed
98
189
  tasks/
99
- T01-PLAN.md
190
+ T01-PLAN.md — Steps, verification, files touched
100
191
  T01-SUMMARY.md
101
192
  ```
102
193
 
103
- ## Config Directory
194
+ Everything is markdown. You can read it, edit it, or use it as context for other tools.
195
+
196
+ ## Bundled Tools
197
+
198
+ Kata comes with extensions for:
199
+
200
+ - **Browser automation** — Playwright-based interaction with web pages
201
+ - **Subagents** — Spawn parallel Kata processes for independent tasks
202
+ - **Background shell** — Long-running processes (servers, watchers, builds)
203
+ - **Web search** — Brave Search API for current external facts
204
+ - **Library docs** — Context7 for up-to-date framework/library documentation
205
+ - **macOS tools** — Native app automation via Accessibility APIs
206
+ - **MCP servers** — Connect to any [Model Context Protocol](https://modelcontextprotocol.io/) server
207
+
208
+ ## Bundled Agents
209
+
210
+ Three specialized subagents for delegated work:
211
+
212
+ | Agent | Role |
213
+ |-------|------|
214
+ | **Scout** | Fast codebase recon — returns compressed context for handoff |
215
+ | **Researcher** | Web research — finds and synthesizes current information |
216
+ | **Worker** | General-purpose execution in an isolated context window |
217
+
218
+ ## MCP Support
219
+
220
+ Kata integrates with MCP servers via [`pi-mcp-adapter`](https://github.com/nicobailon/pi-mcp-adapter), auto-installed on first launch. Connect to Linear, Figma, or any MCP-compatible service.
221
+
222
+ ### Adding a server
223
+
224
+ Edit `~/.kata-cli/agent/mcp.json`:
225
+
226
+ ```json
227
+ {
228
+ "mcpServers": {
229
+ "linear": {
230
+ "command": "npx",
231
+ "args": ["-y", "mcp-remote", "https://mcp.linear.app/mcp"]
232
+ }
233
+ }
234
+ }
235
+ ```
236
+
237
+ Restart Kata, then connect:
238
+
239
+ ```
240
+ mcp({ connect: "linear" })
241
+ ```
242
+
243
+ OAuth servers (Linear, etc.) open a browser window for authorization on first connect. Tokens are cached for subsequent sessions.
244
+
245
+ ### Importing existing configs
246
+
247
+ Pull in MCP configs from other tools:
248
+
249
+ ```json
250
+ {
251
+ "imports": ["claude-code", "cursor"],
252
+ "mcpServers": {}
253
+ }
254
+ ```
255
+
256
+ Supported: `cursor`, `claude-code`, `claude-desktop`, `vscode`, `windsurf`, `codex`.
104
257
 
105
- Kata uses `~/.kata-cli/` (not `~/.kata/`) to avoid collision with other Kata apps (desktop, etc.):
258
+ ### Usage
259
+
260
+ | Command | Description |
261
+ |---------|-------------|
262
+ | `mcp({ })` | Show server status |
263
+ | `mcp({ server: "name" })` | List tools from a server |
264
+ | `mcp({ search: "query" })` | Search tools across servers |
265
+ | `mcp({ tool: "name", args: '{}' })` | Call a tool |
266
+ | `/mcp` | Interactive panel |
267
+
268
+ ## Configuration
269
+
270
+ Kata stores config in `~/.kata-cli/`:
106
271
 
107
272
  ```
108
273
  ~/.kata-cli/
109
274
  agent/
110
- extensions/ Synced from src/resources/extensions/
111
- agents/ Synced from src/resources/agents/
112
- skills/ — Synced from src/resources/skills/
113
- AGENTS.md — Synced from src/resources/AGENTS.md
114
- auth.json — API keys
275
+ mcp.json MCP server configuration
276
+ auth.json Provider API keys
115
277
  settings.json — User settings
116
- models.jsonCustom model definitions
278
+ extensions/Bundled extensions (synced on launch)
279
+ skills/ — Bundled skills
117
280
  sessions/ — Session history
118
- preferences.md — Global Kata preferences
281
+ preferences.md — Global preferences
119
282
  ```
120
283
 
121
- ## Environment Variables
122
-
123
- Set by `loader.ts` before pi starts:
124
-
125
- | Variable | Purpose |
126
- |----------|---------|
127
- | `PI_PACKAGE_DIR` | Points to `pkg/` for Kata's piConfig |
128
- | `KATA_CODING_AGENT_DIR` | Tells pi to use `~/.kata-cli/agent/` |
129
- | `KATA_VERSION` | Package version for display |
130
- | `KATA_BIN_PATH` | Absolute path to loader, used by subagent |
131
- | `KATA_WORKFLOW_PATH` | Absolute path to bundled KATA-WORKFLOW.md |
132
- | `KATA_BUNDLED_EXTENSION_PATHS` | Colon-joined extension entry points for subagent |
133
-
134
284
  ## Development
135
285
 
286
+ For contributing or running from source:
287
+
136
288
  ```bash
137
- # Build
289
+ # From the monorepo root
290
+ bun install
291
+ cd apps/cli
138
292
  npx tsc
139
-
140
- # Copy theme assets (required once, or after pi-coding-agent updates)
141
293
  npm run copy-themes
142
-
143
- # Run
144
294
  node dist/loader.js
145
-
146
- # Test
147
- npm test
148
295
  ```
149
296
 
150
- ### Key Dependency
297
+ Run tests:
151
298
 
152
- `@mariozechner/pi-coding-agent` is consumed via npm (hoisted to monorepo root `node_modules/`). Never fork — run `npm update` to pick up upstream changes.
299
+ ```bash
300
+ cd apps/cli && npm test
301
+ ```
153
302
 
154
303
  ## License
155
304
 
package/dist/cli.js CHANGED
@@ -36,10 +36,30 @@ if (!settingsManager.getQuietStartup()) {
36
36
  if (!settingsManager.getCollapseChangelog()) {
37
37
  settingsManager.setCollapseChangelog(true);
38
38
  }
39
+ // Ensure pi-mcp-adapter is in the packages list so pi auto-installs it on startup.
40
+ // Bootstrap only when packages have never been configured. If users later remove the
41
+ // adapter from settings.json, that opt-out should persist.
42
+ const MCP_ADAPTER_PACKAGE = 'npm:pi-mcp-adapter';
43
+ const globalSettings = settingsManager.getGlobalSettings();
44
+ const globalPackages = [...(globalSettings.packages ?? [])];
45
+ const hasConfiguredPackages = Object.prototype.hasOwnProperty.call(globalSettings, "packages");
46
+ if (!hasConfiguredPackages && !globalPackages.includes(MCP_ADAPTER_PACKAGE)) {
47
+ settingsManager.setPackages([...globalPackages, MCP_ADAPTER_PACKAGE]);
48
+ }
49
+ await settingsManager.flush();
39
50
  const sessionManager = SessionManager.create(process.cwd(), sessionsDir);
40
51
  initResources(agentDir);
41
52
  const resourceLoader = buildResourceLoader(agentDir);
42
53
  await resourceLoader.reload();
54
+ // Inject --mcp-config flag value into the extension runtime.
55
+ // pi-mcp-adapter reads this via pi.getFlag("mcp-config") at session_start.
56
+ // Kata doesn't call pi's main() which does the two-pass argv parsing that
57
+ // normally populates flagValues, so we must do it manually here.
58
+ const mcpConfigPath = process.env.KATA_MCP_CONFIG_PATH;
59
+ if (mcpConfigPath) {
60
+ const extResult = resourceLoader.getExtensions();
61
+ extResult.runtime.flagValues.set('mcp-config', mcpConfigPath);
62
+ }
43
63
  const { session, extensionsResult } = await createAgentSession({
44
64
  authStorage,
45
65
  modelRegistry,
package/dist/loader.js CHANGED
@@ -91,5 +91,15 @@ process.env.KATA_BUNDLED_EXTENSION_PATHS = [
91
91
  join(agentDir, "extensions", "ask-user-questions.ts"),
92
92
  join(agentDir, "extensions", "get-secrets-from-user.ts"),
93
93
  ].join(":");
94
+ // KATA_MCP_CONFIG_PATH — absolute path to Kata's MCP config file.
95
+ // pi-mcp-adapter reads --mcp-config from process.argv directly (before session_start fires).
96
+ // We inject it here so the adapter uses ~/.kata-cli/agent/mcp.json instead of the
97
+ // default ~/.pi/agent/mcp.json.
98
+ const mcpConfigPath = join(agentDir, "mcp.json");
99
+ process.env.KATA_MCP_CONFIG_PATH = mcpConfigPath;
100
+ const hasMcpConfigArg = process.argv.some((arg) => arg === "--mcp-config" || arg.startsWith("--mcp-config="));
101
+ if (!hasMcpConfigArg) {
102
+ process.argv.push("--mcp-config", mcpConfigPath);
103
+ }
94
104
  // Dynamic import defers ESM evaluation — config.js will see PI_PACKAGE_DIR above
95
105
  await import("./cli.js");
@@ -2,6 +2,19 @@ import { DefaultResourceLoader } from '@mariozechner/pi-coding-agent';
2
2
  import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
+ /**
6
+ * Starter mcp.json written to agentDir on first launch.
7
+ * Uses the `imports` field so users can pull in their existing Claude/Cursor config.
8
+ * mcpServers is intentionally empty — users add their own servers here.
9
+ */
10
+ const STARTER_MCP_JSON = JSON.stringify({
11
+ imports: [],
12
+ settings: {
13
+ toolPrefix: 'server',
14
+ idleTimeout: 10,
15
+ },
16
+ mcpServers: {},
17
+ }, null, 2) + '\n';
5
18
  // Resolves to the bundled src/resources/ inside the npm package at runtime:
6
19
  // dist/resource-loader.js → .. → package root → src/resources/
7
20
  const resourcesDir = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'src', 'resources');
@@ -39,6 +52,12 @@ export function initResources(agentDir) {
39
52
  if (existsSync(srcAgentsMd)) {
40
53
  writeFileSync(destAgentsMd, readFileSync(srcAgentsMd));
41
54
  }
55
+ // Scaffold starter mcp.json — only if it doesn't exist yet.
56
+ // Never overwrite: preserve the user's MCP server configuration.
57
+ const mcpConfigPath = join(agentDir, 'mcp.json');
58
+ if (!existsSync(mcpConfigPath)) {
59
+ writeFileSync(mcpConfigPath, STARTER_MCP_JSON, 'utf-8');
60
+ }
42
61
  }
43
62
  /**
44
63
  * Constructs a DefaultResourceLoader with no additionalExtensionPaths.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kata-sh/cli",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Kata CLI coding agent",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -29,7 +29,8 @@
29
29
  "node": ">=20.6.0"
30
30
  },
31
31
  "scripts": {
32
- "build": "tsc && npm run copy-themes",
32
+ "build": "tsc && npm run copy-themes && npm run copy-changelog",
33
+ "copy-changelog": "node -e \"require('fs').cpSync('CHANGELOG.md','pkg/CHANGELOG.md')\"",
33
34
  "copy-themes": "node -e \"const{mkdirSync,cpSync}=require('fs');const{resolve}=require('path');const candidates=[resolve(__dirname,'node_modules/@mariozechner/pi-coding-agent/dist/modes/interactive/theme'),resolve(__dirname,'..','..','node_modules/@mariozechner/pi-coding-agent/dist/modes/interactive/theme')];const src=candidates.find(p=>{try{require('fs').statSync(p);return true}catch{return false}});if(!src)throw new Error('theme dir not found');mkdirSync('pkg/dist/modes/interactive/theme',{recursive:true});cpSync(src,'pkg/dist/modes/interactive/theme',{recursive:true})\"",
34
35
  "test": "node --import ./src/resources/extensions/kata/tests/resolve-ts.mjs --experimental-strip-types --test 'src/resources/extensions/kata/tests/*.test.ts' 'src/resources/extensions/kata/tests/*.test.mjs' 'src/tests/*.test.ts'",
35
36
  "dev": "tsc --watch",
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## 0.2.1
4
+
5
+ - Fix `/changelog` command — symlink `pkg/CHANGELOG.md` so Kata can find it
6
+ - Rewrite README for consumers: quick start with `npx`, getting started flow, how it works, mode comparison, full command reference
7
+
8
+ ## 0.2.0
9
+
10
+ - Add MCP (Model Context Protocol) support via `pi-mcp-adapter` — connect to any MCP server (Linear, Figma, custom tools) from Kata
11
+ - Auto-install `pi-mcp-adapter` on startup and scaffold starter `mcp.json` config
12
+ - Inject `mcp-config` flag into extension runtime for seamless MCP server discovery
13
+ - Fix inline `[]` and `{}` literal handling in preferences YAML parser
14
+ - Add comprehensive MCP documentation and setup guide to README
15
+ - Add MCP smoke tests to CI
16
+ - Install `pi-mcp-adapter` globally in CI for test coverage
17
+
18
+ ## 0.1.2
19
+
20
+ - Fix `~/.kata/` paths to `~/.kata-cli/` to avoid collision with Kata Desktop config directory
21
+
22
+ ## 0.1.1
23
+
24
+ - Rename `@kata/*` to `@kata-sh/*` npm scope
25
+ - Initial public release to npm
@@ -9,7 +9,7 @@ Kata CLI is a thin wrapper around pi-coding-agent that provides:
9
9
  - **Branded entry point**: `src/loader.ts` sets env vars and launches `src/cli.ts`
10
10
  - **Bundled extensions**: `src/resources/extensions/` contains all built-in extensions
11
11
  - **Resource syncing**: `src/resource-loader.ts` copies bundled extensions to `~/.kata-cli/agent/` on startup
12
- - **Config directory**: `~/.kata-cli/` (not `~/.kata-cli/` to avoid collision with other Kata apps)
12
+ - **Config directory**: `~/.kata-cli/` (not `~/.pi/` to avoid collision with other Kata apps)
13
13
  - **Package shim**: `pkg/package.json` provides `piConfig` with `name: "kata"` and `configDir: ".kata-cli"`
14
14
 
15
15
  ## Directory Structure
@@ -18,7 +18,7 @@ Kata CLI is a thin wrapper around pi-coding-agent that provides:
18
18
  apps/cli/
19
19
  src/
20
20
  loader.ts — Entry point, sets KATA_* env vars, imports cli.ts
21
- cli.ts — Thin wrapper that calls pi-coding-agent's main()
21
+ cli.ts — Thin wrapper that calls createAgentSession() + InteractiveMode
22
22
  app-paths.ts — Exports appRoot, agentDir, sessionsDir, authFilePath
23
23
  resource-loader.ts — Syncs bundled resources to ~/.kata-cli/agent/
24
24
  wizard.ts — First-run setup, env key hydration
@@ -55,6 +55,7 @@ Kata sets these env vars in `loader.ts` before importing `cli.ts`:
55
55
  | `KATA_BIN_PATH` | Absolute path to loader, used by subagent to spawn Kata |
56
56
  | `KATA_WORKFLOW_PATH` | Absolute path to bundled KATA-WORKFLOW.md |
57
57
  | `KATA_BUNDLED_EXTENSION_PATHS` | Colon-joined list of extension entry points |
58
+ | `KATA_MCP_CONFIG_PATH` | Absolute path to `~/.kata-cli/agent/mcp.json` (also injected as `--mcp-config` argv) |
58
59
 
59
60
  ## The /kata Command
60
61
 
@@ -99,6 +100,156 @@ Kata stores project state in `.kata/` at the project root:
99
100
  - **Copy themes**: `npm run copy-themes` (copies theme assets from pi-coding-agent)
100
101
  - **Dependencies**: Consumed via npm from `@mariozechner/pi-coding-agent` — never fork
101
102
 
103
+ ## MCP Support
104
+
105
+ Kata ships with MCP (Model Context Protocol) support via [`pi-mcp-adapter`](https://github.com/nicobailon/pi-mcp-adapter), auto-installed on first launch. One proxy `mcp` tool (~200 tokens) gives the agent on-demand access to any MCP server's tools without burning context on tool definitions.
106
+
107
+ ### How it works
108
+
109
+ There are three integration points that make MCP work in Kata:
110
+
111
+ 1. **Package seeding** (`cli.ts`): Seeds `npm:pi-mcp-adapter` into `settingsManager.getPackages()` on every startup. Pi's package manager auto-installs it globally if missing.
112
+
113
+ 2. **Config path injection** (`loader.ts` + `cli.ts`): Kata bypasses pi's `main()` and calls `createAgentSession()` directly, which means pi's two-pass argv parsing (that normally populates `runtime.flagValues`) never runs. Two things compensate:
114
+ - `loader.ts` pushes `--mcp-config ~/.kata-cli/agent/mcp.json` into `process.argv` — the adapter reads this at extension load time for `directTools` registration.
115
+ - `cli.ts` manually sets `runtime.flagValues.set('mcp-config', ...)` after `resourceLoader.reload()` — the adapter reads this via `pi.getFlag('mcp-config')` at `session_start` for the main initialization.
116
+
117
+ 3. **Config scaffolding** (`resource-loader.ts`): Creates a starter `~/.kata-cli/agent/mcp.json` on first launch. Never overwrites existing config.
118
+
119
+ ### Configuring MCP servers
120
+
121
+ Edit `~/.kata-cli/agent/mcp.json` to add servers. Servers can use **stdio** (local process) or **HTTP** (remote endpoint) transport.
122
+
123
+ #### Stdio servers (local process)
124
+
125
+ Most MCP servers run as a local process via `npx`:
126
+
127
+ ```json
128
+ {
129
+ "mcpServers": {
130
+ "my-server": {
131
+ "command": "npx",
132
+ "args": ["-y", "some-mcp-server"],
133
+ "env": {
134
+ "API_KEY": "${MY_API_KEY}"
135
+ }
136
+ }
137
+ }
138
+ }
139
+ ```
140
+
141
+ Environment variables support `${VAR}` interpolation from `process.env`.
142
+
143
+ #### HTTP servers with OAuth (e.g. Linear)
144
+
145
+ Many hosted MCP servers (Linear, Figma, etc.) use OAuth 2.1 authentication via the MCP spec. These require [`mcp-remote`](https://github.com/geelen/mcp-remote) as a stdio proxy that handles the OAuth browser flow:
146
+
147
+ ```json
148
+ {
149
+ "mcpServers": {
150
+ "linear": {
151
+ "command": "npx",
152
+ "args": ["-y", "mcp-remote", "https://mcp.linear.app/mcp"]
153
+ }
154
+ }
155
+ }
156
+ ```
157
+
158
+ On first connection, `mcp-remote` opens a browser window for OAuth consent. Tokens are cached in `~/.mcp-auth/` for subsequent sessions.
159
+
160
+ **Linear MCP setup (complete example):**
161
+
162
+ 1. Add the server to `~/.kata-cli/agent/mcp.json`:
163
+ ```json
164
+ {
165
+ "settings": { "toolPrefix": "server", "idleTimeout": 10 },
166
+ "mcpServers": {
167
+ "linear": {
168
+ "command": "npx",
169
+ "args": ["-y", "mcp-remote", "https://mcp.linear.app/mcp"]
170
+ }
171
+ }
172
+ }
173
+ ```
174
+
175
+ 2. Restart Kata.
176
+
177
+ 3. Connect the server (triggers the OAuth flow in your browser):
178
+ ```
179
+ mcp({ connect: "linear" })
180
+ ```
181
+
182
+ 4. Authorize Kata in the browser when prompted by Linear.
183
+
184
+ 5. Use Linear tools:
185
+ ```
186
+ mcp({ server: "linear" }) — list all Linear tools
187
+ mcp({ search: "issues" }) — search for issue-related tools
188
+ mcp({ tool: "linear_list_teams" }) — call a specific tool
189
+ ```
190
+
191
+ **Troubleshooting OAuth:**
192
+ - If you see `internal server error`, clear cached auth: `rm -rf ~/.mcp-auth` and reconnect.
193
+ - Make sure you're running a recent version of Node.js.
194
+ - Use `/mcp` to check server status interactively.
195
+
196
+ #### HTTP servers with bearer token auth
197
+
198
+ For servers that accept API keys or personal access tokens:
199
+
200
+ ```json
201
+ {
202
+ "mcpServers": {
203
+ "my-api": {
204
+ "url": "https://api.example.com/mcp",
205
+ "auth": "bearer",
206
+ "bearerTokenEnv": "MY_API_KEY"
207
+ }
208
+ }
209
+ }
210
+ ```
211
+
212
+ #### Importing existing configs
213
+
214
+ Pull in your existing Claude Code, Cursor, or VS Code MCP configuration:
215
+
216
+ ```json
217
+ {
218
+ "imports": ["claude-code", "cursor"],
219
+ "mcpServers": {}
220
+ }
221
+ ```
222
+
223
+ Supported sources: `cursor`, `claude-code`, `claude-desktop`, `vscode`, `windsurf`, `codex`.
224
+
225
+ ### Server lifecycle
226
+
227
+ | Mode | Behavior |
228
+ |------|----------|
229
+ | `lazy` (default) | Connect on first tool call. Disconnect after idle timeout. Cached metadata keeps search/list working offline. |
230
+ | `eager` | Connect at startup. No auto-reconnect on drop. |
231
+ | `keep-alive` | Connect at startup. Auto-reconnect via health checks. |
232
+
233
+ ### Usage
234
+
235
+ ```
236
+ mcp({ }) — show server status
237
+ mcp({ server: "linear" }) — list tools from a server
238
+ mcp({ search: "issues create" }) — search tools (space-separated words OR'd)
239
+ mcp({ describe: "linear_save_issue" }) — show tool parameters
240
+ mcp({ tool: "linear_list_teams" }) — call a tool (no args)
241
+ mcp({ tool: "linear_save_issue", args: '{"title": "Bug fix"}' }) — call with args (JSON string)
242
+ mcp({ connect: "linear" }) — force connect/reconnect a server
243
+ /mcp — interactive panel (status, tools, reconnect, OAuth)
244
+ ```
245
+
246
+ ### Known limitations
247
+
248
+ - **OAuth servers require `mcp-remote`**: The adapter doesn't implement the MCP OAuth browser flow natively. Use `mcp-remote` as a stdio proxy for any server that requires OAuth (Linear, Figma remote, etc.).
249
+ - **Figma remote MCP (`mcp.figma.com`)**: Blocks dynamic client registration — only whitelisted clients (Cursor, Claude Code, VS Code) can connect via OAuth. Use the Figma desktop app's local MCP server instead (`http://127.0.0.1:3845/mcp`), which requires Figma desktop with Dev Mode (paid plan).
250
+ - **Metadata cache path**: `pi-mcp-adapter` caches tool metadata to `~/.pi/agent/mcp-cache.json` (hardcoded). This doesn't affect functionality — just means the cache lives outside Kata's config dir.
251
+ - **OAuth token storage**: `mcp-remote` stores tokens in `~/.mcp-auth/`, separate from Kata's config dir.
252
+
102
253
  ## Key Conventions
103
254
 
104
255
  - All env var names use `KATA_` prefix (not `GSD_` or `PI_`)
@@ -106,3 +257,4 @@ Kata stores project state in `.kata/` at the project root:
106
257
  - Extensions are synced from `src/resources/extensions/` to `~/.kata-cli/agent/extensions/` on every launch
107
258
  - The `shared/` extension directory is a library, not an entry point — it's imported by other extensions
108
259
  - Branch naming for workflow: `kata/M001/S01` (milestone/slice)
260
+ - MCP config lives at `~/.kata-cli/agent/mcp.json` (not `~/.pi/agent/mcp.json`)
@@ -398,8 +398,9 @@ function parseFrontmatterBlock(frontmatter: string): KataPreferences {
398
398
  const valuePart = remainder.trim();
399
399
 
400
400
  if (valuePart === "") {
401
- const nextLine = lines[i + 1] ?? "";
402
- const nextTrimmed = nextLine.trim();
401
+ const nextNonEmptyLine =
402
+ lines.slice(i + 1).find((candidate) => candidate.trim() !== "") ?? "";
403
+ const nextTrimmed = nextNonEmptyLine.trim();
403
404
  if (nextTrimmed.startsWith("- ")) {
404
405
  const items: unknown[] = [];
405
406
  let j = i + 1;
@@ -481,9 +482,16 @@ function parseFrontmatterBlock(frontmatter: string): KataPreferences {
481
482
  current[key] = items;
482
483
  i = j - 1;
483
484
  } else {
484
- const obj: Record<string, unknown> = {};
485
- current[key] = obj;
486
- stack.push({ indent, value: obj });
485
+ // Check if the next non-empty line is actually indented deeper (a real nested block).
486
+ // If not, this key simply has no value — skip it rather than creating an empty object.
487
+ const nextIndent =
488
+ nextNonEmptyLine.match(/^\s*/)?.[0].length ?? indent;
489
+ if (nextIndent > indent) {
490
+ const obj: Record<string, unknown> = {};
491
+ current[key] = obj;
492
+ stack.push({ indent, value: obj });
493
+ }
494
+ // else: key with no value and no nested block — leave it undefined
487
495
  }
488
496
  continue;
489
497
  }
@@ -494,9 +502,13 @@ function parseFrontmatterBlock(frontmatter: string): KataPreferences {
494
502
  return root as KataPreferences;
495
503
  }
496
504
 
497
- function parseScalar(value: string): string | number | boolean {
505
+ function parseScalar(
506
+ value: string,
507
+ ): string | number | boolean | unknown[] | Record<string, never> {
498
508
  if (value === "true") return true;
499
509
  if (value === "false") return false;
510
+ if (value === "[]") return [];
511
+ if (value === "{}") return {};
500
512
  if (/^-?\d+$/.test(value)) return Number(value);
501
513
  return value.replace(/^['\"]|['\"]$/g, "");
502
514
  }
@@ -0,0 +1,53 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const resolveTsHookPath = fileURLToPath(new URL('./resolve-ts.mjs', import.meta.url));
10
+ const preferencesPath = fileURLToPath(new URL('../preferences.ts', import.meta.url));
11
+
12
+ test('loadEffectiveKataPreferences preserves blank-line-separated skill_rules lists', () => {
13
+ const tmp = mkdtempSync(join(tmpdir(), 'kata-preferences-frontmatter-'));
14
+ const kataDir = join(tmp, '.kata');
15
+ mkdirSync(kataDir, { recursive: true });
16
+ writeFileSync(
17
+ join(kataDir, 'preferences.md'),
18
+ `---
19
+ skill_rules:
20
+
21
+ - when: build
22
+ use:
23
+ - test-driven-development
24
+ ---
25
+ `,
26
+ );
27
+
28
+ const script = `
29
+ import { loadEffectiveKataPreferences } from ${JSON.stringify(preferencesPath)};
30
+ const prefs = loadEffectiveKataPreferences();
31
+ console.log(JSON.stringify(prefs?.preferences.skill_rules ?? null));
32
+ `;
33
+
34
+ const output = execFileSync(
35
+ 'node',
36
+ ['--import', resolveTsHookPath, '--experimental-strip-types', '-e', script],
37
+ {
38
+ cwd: tmp,
39
+ env: {
40
+ ...process.env,
41
+ HOME: tmp,
42
+ },
43
+ encoding: 'utf-8',
44
+ },
45
+ ).trim();
46
+
47
+ assert.deepEqual(JSON.parse(output), [
48
+ {
49
+ when: 'build',
50
+ use: ['test-driven-development'],
51
+ },
52
+ ]);
53
+ });