@chamba/claude-extras 0.2.1 → 0.3.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 CHANGED
@@ -117,6 +117,60 @@ records why — the config still drives every other editor through the MCP tool.
117
117
  - **Why does `extreme` become `max` in Claude Code?** Claude Code's effort scale tops
118
118
  out at `max`; `extreme` is chamba's name for "the ceiling".
119
119
 
120
+ ## Multi-repo worktrees + the `/ticket` flow
121
+
122
+ If you work in a **workspace of several repos** (a parent dir with N git repos), chamba
123
+ can create an isolated worktree per repo for a ticket, reuse or fork the branch, copy
124
+ git-ignored `.env*` files, and write a `.code-workspace` — all driven by config.
125
+
126
+ ```bash
127
+ npx @chamba/claude-extras config worktrees init # interactive setup
128
+ npx @chamba/claude-extras config worktrees show # inspect the resolved policy
129
+ ```
130
+
131
+ This writes a `worktrees` block to `~/.chamba/config.json` (or per project in
132
+ `./.chamba/config.json`):
133
+
134
+ ```json
135
+ {
136
+ "version": 1,
137
+ "worktrees": {
138
+ "layout": "sibling",
139
+ "root": "WORKTREES",
140
+ "branchPrefix": "ticket/",
141
+ "baseBranch": "main",
142
+ "copyEnvFiles": true,
143
+ "editorWorkspace": "code-workspace",
144
+ "repos": ["api", "web", "functions"]
145
+ }
146
+ }
147
+ ```
148
+
149
+ - **layout** — `sibling` puts everything under `<workspace>/WORKTREES/<ticket>/<repo>`;
150
+ `nested` puts a worktree under each repo.
151
+ - **repos** — omit to autodetect the workspace's git repos.
152
+ - **command** — escape hatch: set it to your own script (e.g.
153
+ `"./ticket-create.sh {ticket} {repos}"`) and chamba shells out instead of using the
154
+ built-in. Migrate from a bespoke script to config whenever you want.
155
+
156
+ Then, in Claude Code:
157
+
158
+ ```
159
+ /ticket TICKET-123
160
+ ```
161
+
162
+ `/ticket` runs the full orchestrator-worker flow: create worktrees →
163
+ `chamba_load_context` → delegate the plan to the **planner** subagent →
164
+ `chamba_review_plan` + the **reviewer** subagent → delegate code to **implementer** and
165
+ tests to **tester** (all inside the worktrees) → `chamba_summarize_to_vault`. It runs to
166
+ the end and stops for your review. It **never commits, merges or pushes** — you review,
167
+ commit and send to code review by hand. Each worker runs with the model + effort you
168
+ configured above.
169
+
170
+ > **Security:** `copyEnvFiles` copies secrets into the worktree directories. Add your
171
+ > `worktrees.root` (e.g. `WORKTREES/`) to `.gitignore` so they're never committed. It's
172
+ > off by default.
173
+
120
174
  ## License
121
175
 
122
176
  MIT
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: planner
3
+ description: Produces a detailed, reviewable implementation plan for a ticket, decomposed by repo
4
+ ---
5
+
6
+ You are the **planner**. You receive a ticket and the loaded context (workspace
7
+ map + relevant notes). Produce a concrete, reviewable plan — do not write code.
8
+
9
+ - Decompose the work into subtasks, grouped by repo when it spans several.
10
+ - For each subtask: the goal, the files likely touched, and which worker does it.
11
+ - State explicit acceptance criteria and how they'll be verified (tests).
12
+ - Call out risks and any sensitive areas (auth, payments, migrations, data).
13
+ - Keep it tight and concrete enough that an implementer can execute it without
14
+ guessing. If the ticket is ambiguous, state the assumptions you made instead of
15
+ inventing scope.
16
+
17
+ Return the plan as structured markdown. The orchestrator runs it through
18
+ `chamba_review_plan` and the reviewer subagent before any code is written.
@@ -6,15 +6,17 @@ argument-hint: <task>
6
6
  You are orchestrating this task with chamba's MCP tools: **$ARGUMENTS**
7
7
 
8
8
  Follow the orchestrator-worker flow. chamba provides context, plan validation,
9
- worktrees and vault writing; you do the reasoning and the code.
9
+ worktrees and vault writing; you orchestrate and delegate to the subagents (which
10
+ run with the model + effort you configured via `chamba-config`).
10
11
 
11
12
  1. Call `chamba_load_context` with the task to pull workspace + relevant notes.
12
- 2. Call `chamba_generate_plan` to get a plan template, then fill it in concretely
13
- (goal, acceptance criteria, subtasks with workers, risks, files).
14
- 3. Call `chamba_review_plan` with your plan. If `approved` is false, fix the
15
- reported issues and review again (max 3 rounds).
13
+ 2. Delegate to the **planner** subagent to produce the plan (goal, acceptance
14
+ criteria, subtasks with workers, risks, files).
15
+ 3. Call `chamba_review_plan` with the plan and have the **reviewer** subagent
16
+ audit it. If not approved, fix the issues and review again (max 3 rounds).
16
17
  4. Show me the approved plan and wait for my go-ahead.
17
18
  5. If this is a git repo, call `chamba_create_worktree` per worker for isolation.
18
- 6. Implement the change, write/extend tests, and run them.
19
+ 6. Delegate implementation to the **implementer** subagent (in its worktree) and
20
+ the tests to the **tester** subagent; run them.
19
21
  7. When done, call `chamba_summarize_to_vault` with a summary of what changed.
20
22
  8. Leave any worktree branches open — do not merge. Tell me the merge command.
@@ -0,0 +1,27 @@
1
+ ---
2
+ description: Resolve a ticket end-to-end in isolated worktrees, delegating to chamba's agents
3
+ argument-hint: <ticket> [repo ...]
4
+ ---
5
+
6
+ You are orchestrating ticket **$ARGUMENTS** end-to-end. chamba provides the
7
+ worktrees, context, plan validation and vault memory; you delegate the thinking
8
+ and the code to the configured subagents. **Run to the end and stop only for my
9
+ final review** — do not pause for approval mid-way.
10
+
11
+ 1. Create isolated worktrees: call `chamba_create_worktrees` with the ticket id
12
+ (and the repos if I named any). ALL work happens INSIDE these worktrees —
13
+ never edit the main checkouts. Note the branch and per-repo worktree paths.
14
+ 2. Call `chamba_load_context` with the ticket to pull the workspace map + relevant
15
+ Obsidian notes.
16
+ 3. Delegate to the **planner** subagent to produce the detailed plan. Run it
17
+ through `chamba_review_plan`; if not approved, fix and re-review (max 3 rounds).
18
+ Also have the **reviewer** subagent audit it. Do all of this WITHOUT stopping to
19
+ ask me — the heuristic review + the reviewer agent are the quality gate.
20
+ 4. For each subtask/repo, delegate implementation to the **implementer** subagent,
21
+ working only in that repo's worktree. Delegate the tests to the **tester**
22
+ subagent and run them.
23
+ 5. Call `chamba_summarize_to_vault` with a summary of what changed.
24
+ 6. STOP and report for my review: per repo, what changed and the test results; the
25
+ `.code-workspace` to open; and the suggested commit + `git merge --no-ff`
26
+ commands. Do NOT commit, merge or push — I review, commit and send to my
27
+ company's code review by hand.
package/dist/cli.js CHANGED
@@ -56,6 +56,19 @@ var ConfigStore = class {
56
56
  await this.write(parsed.value);
57
57
  return parsed.value;
58
58
  }
59
+ /** Merge a patch into the `worktrees` block and persist (validated). */
60
+ async setWorktrees(patch) {
61
+ const current = await this.read();
62
+ const next = {
63
+ ...current,
64
+ version: 1,
65
+ worktrees: { ...current.worktrees ?? {}, ...patch }
66
+ };
67
+ const parsed = parseChambaConfig(next);
68
+ if (!parsed.ok) throw new ConfigError(parsed.error);
69
+ await this.write(parsed.value);
70
+ return parsed.value;
71
+ }
59
72
  /** Write the compiled defaults as the full config. */
60
73
  async reset() {
61
74
  await this.write({ version: 1, defaults: DEFAULT_CONFIG.defaults });
@@ -71,6 +84,7 @@ import { joinPath, loadConfig } from "@chamba/core";
71
84
  // src/agent-frontmatter.ts
72
85
  import { getModel } from "@chamba/core";
73
86
  var AGENT_ROLE_BY_FILE = {
87
+ "planner.md": "planner",
74
88
  "implementer.md": "implementer",
75
89
  "reviewer.md": "reviewer",
76
90
  "tester.md": "tester"
@@ -269,11 +283,12 @@ function asRecord(value) {
269
283
  import {
270
284
  AGENT_ROLES,
271
285
  DEFAULT_CONFIG as DEFAULT_CONFIG2,
286
+ DEFAULT_WORKTREE_CONFIG,
272
287
  EFFORT_LEVELS,
273
288
  MODEL_CATALOG,
274
289
  ROLE_DESCRIPTIONS
275
290
  } from "@chamba/core";
276
- import { confirm, select } from "@inquirer/prompts";
291
+ import { confirm, input, select } from "@inquirer/prompts";
277
292
  function defaultConfigFile() {
278
293
  return { version: 1, defaults: DEFAULT_CONFIG2.defaults };
279
294
  }
@@ -298,6 +313,42 @@ function effortChoices() {
298
313
  function isCancellation(e) {
299
314
  return e instanceof Error && e.name === "ExitPromptError";
300
315
  }
316
+ async function runWorktreesWizard() {
317
+ const d = DEFAULT_WORKTREE_CONFIG;
318
+ const layout = await select({
319
+ message: "Worktree layout",
320
+ choices: [
321
+ {
322
+ name: "sibling \u2014 one folder per ticket (<root>/<ticket>/<repo>)",
323
+ value: "sibling"
324
+ },
325
+ { name: "nested \u2014 a worktree under each repo", value: "nested" }
326
+ ],
327
+ default: d.layout
328
+ });
329
+ const root = await input({
330
+ message: "Worktree root directory",
331
+ default: layout === "sibling" ? "WORKTREES" : ".chamba/worktrees"
332
+ });
333
+ const branchPrefix = await input({ message: "Branch prefix", default: "ticket/" });
334
+ const baseBranch = await input({ message: "Base branch to fork from", default: d.baseBranch });
335
+ const copyEnvFiles = await confirm({
336
+ message: "Copy git-ignored .env* files into worktrees?",
337
+ default: false
338
+ });
339
+ const editor = await confirm({
340
+ message: "Generate a .code-workspace per ticket?",
341
+ default: true
342
+ });
343
+ return {
344
+ layout,
345
+ root,
346
+ branchPrefix,
347
+ baseBranch,
348
+ copyEnvFiles,
349
+ editorWorkspace: editor ? "code-workspace" : null
350
+ };
351
+ }
301
352
  async function runWizard(opts = {}) {
302
353
  if (opts.nonInteractive) return defaultConfigFile();
303
354
  try {
@@ -379,6 +430,19 @@ async function cmdSet(store, args) {
379
430
  const effortNote = args.effort ? ` with effort ${args.effort}` : "";
380
431
  return `Set ${args.role} \u2192 ${args.model}${effortNote}. Run 'config apply' to regenerate subagents.`;
381
432
  }
433
+ function formatWorktrees(w) {
434
+ return [
435
+ "worktrees config:",
436
+ ` layout ${w.layout}`,
437
+ ` root ${w.root}`,
438
+ ` branchPrefix ${w.branchPrefix}`,
439
+ ` baseBranch ${w.baseBranch}`,
440
+ ` copyEnvFiles ${w.copyEnvFiles}`,
441
+ ` editorWorkspace ${w.editorWorkspace ?? "(none)"}`,
442
+ ` repos ${w.repos ? w.repos.join(", ") : "(autodetect)"}`,
443
+ ` command ${w.command ?? "(none)"}`
444
+ ].join("\n");
445
+ }
382
446
  function buildInstaller(fs) {
383
447
  const home = homedir();
384
448
  const assetsDir = fileURLToPath(new URL("../assets", import.meta.url));
@@ -394,7 +458,7 @@ function parseEffortFlag(rest) {
394
458
  const i = rest.indexOf("--effort");
395
459
  return i >= 0 ? rest[i + 1] : void 0;
396
460
  }
397
- var USAGE = "Usage: chamba-install config <show|models|set|reset|wizard|apply|edit>\n show resolved config + where each value comes from\n models list the available models\n set <role> <model> [--effort <level>] change one role\n reset [--yes] restore defaults\n wizard [--defaults] (re)run the interactive wizard\n apply regenerate ~/.claude/agents from the config\n edit open the config in $EDITOR\n";
461
+ var USAGE = "Usage: chamba-install config <show|models|set|reset|wizard|apply|edit>\n show resolved config + where each value comes from\n models list the available models\n set <role> <model> [--effort <level>] change one role\n reset [--yes] restore defaults\n wizard [--defaults] (re)run the interactive wizard\n worktrees <show|init> show or configure the multi-repo worktree policy\n apply regenerate ~/.claude/agents from the config\n edit open the config in $EDITOR\n";
398
462
  async function runConfigCommand(args) {
399
463
  const [command, ...rest] = args;
400
464
  const fs = new NodeFilesystem();
@@ -447,6 +511,30 @@ async function runConfigCommand(args) {
447
511
  `);
448
512
  return;
449
513
  }
514
+ case "worktrees": {
515
+ const sub = rest[0];
516
+ if (sub === "show") {
517
+ const { worktrees } = await loadConfig2(fs, {
518
+ globalPath: globalConfigPath(),
519
+ projectPath: projectConfigPath()
520
+ });
521
+ process.stdout.write(`${formatWorktrees(worktrees)}
522
+ `);
523
+ return;
524
+ }
525
+ if (sub === "init") {
526
+ const patch = await runWorktreesWizard();
527
+ await store.setWorktrees(patch);
528
+ process.stdout.write(
529
+ `Wrote worktrees config to ${store.filePath}.${patch.copyEnvFiles ? ` Tip: add '${patch.root}/' to your .gitignore.` : ""}
530
+ `
531
+ );
532
+ return;
533
+ }
534
+ process.stderr.write("Usage: config worktrees <show|init>\n");
535
+ process.exitCode = sub ? 1 : 0;
536
+ return;
537
+ }
450
538
  case "apply": {
451
539
  const result = await buildInstaller(fs).applyConfig();
452
540
  process.stdout.write(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chamba/claude-extras",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Optional Claude Code extras for chamba: slash commands, subagents and hooks installer",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,8 +31,8 @@
31
31
  ],
32
32
  "dependencies": {
33
33
  "@inquirer/prompts": "^7.0.0",
34
- "@chamba/core": "0.2.1",
35
- "@chamba/adapters": "0.2.1"
34
+ "@chamba/adapters": "0.3.0",
35
+ "@chamba/core": "0.3.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^22.0.0",