@benzotti/jedi 0.1.33 → 0.1.34

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/dist/index.js CHANGED
@@ -10084,17 +10084,55 @@ async function writeState(cwd, state) {
10084
10084
  }
10085
10085
 
10086
10086
  // src/utils/state-handlers.ts
10087
+ var import_yaml4 = __toESM(require_dist(), 1);
10088
+ import { join as join9 } from "path";
10089
+ import { existsSync as existsSync9 } from "fs";
10090
+ function parseFrontmatter(content) {
10091
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
10092
+ if (!match)
10093
+ return null;
10094
+ try {
10095
+ return import_yaml4.parse(match[1]);
10096
+ } catch {
10097
+ return null;
10098
+ }
10099
+ }
10087
10100
  async function transitionToPlanReady(cwd, planPath, planName) {
10088
10101
  const state = await readState(cwd) ?? {};
10102
+ const fullPlanPath = planPath.startsWith("/") ? planPath : join9(cwd, planPath);
10103
+ let phase;
10104
+ let planNumber;
10105
+ let taskFiles = [];
10106
+ if (existsSync9(fullPlanPath)) {
10107
+ const content = await Bun.file(fullPlanPath).text();
10108
+ const fm = parseFrontmatter(content);
10109
+ if (fm) {
10110
+ if (fm.phase != null)
10111
+ phase = Number(fm.phase);
10112
+ if (fm.plan != null)
10113
+ planNumber = String(fm.plan);
10114
+ if (Array.isArray(fm.task_files))
10115
+ taskFiles = fm.task_files;
10116
+ }
10117
+ }
10089
10118
  state.position = {
10090
10119
  ...state.position,
10091
- plan: planPath,
10120
+ ...phase != null ? { phase } : {},
10121
+ plan: planNumber ?? planPath,
10092
10122
  plan_name: planName,
10093
10123
  status: "planning"
10094
10124
  };
10095
10125
  state.current_plan = {
10096
10126
  ...state.current_plan,
10097
- path: planPath
10127
+ path: planPath,
10128
+ tasks: taskFiles,
10129
+ completed_tasks: [],
10130
+ current_task_index: taskFiles.length > 0 ? 0 : null
10131
+ };
10132
+ state.progress = {
10133
+ ...state.progress,
10134
+ tasks_total: taskFiles.length,
10135
+ tasks_completed: 0
10098
10136
  };
10099
10137
  state.review = {
10100
10138
  ...state.review,
@@ -10142,9 +10180,10 @@ async function advanceTask(cwd, completedTaskId) {
10142
10180
  const nextIndex = completed.length;
10143
10181
  state.current_plan.current_task_index = nextIndex < tasks.length ? nextIndex : null;
10144
10182
  }
10145
- if (state.progress) {
10146
- state.progress.tasks_completed = (state.progress.tasks_completed ?? 0) + 1;
10183
+ if (!state.progress) {
10184
+ state.progress = { phases_total: 0, phases_completed: 0, plans_total: 0, plans_completed: 0, tasks_total: 0, tasks_completed: 0 };
10147
10185
  }
10186
+ state.progress.tasks_completed = (state.progress.tasks_completed ?? 0) + 1;
10148
10187
  await updateSessionActivity(cwd, state);
10149
10188
  }
10150
10189
  async function updateSessionActivity(cwd, state) {
@@ -10327,15 +10366,15 @@ var statusCommand = defineCommand({
10327
10366
  import { relative } from "path";
10328
10367
 
10329
10368
  // src/utils/resolve-components.ts
10330
- import { join as join9, basename } from "path";
10369
+ import { join as join10, basename } from "path";
10331
10370
  import { homedir } from "os";
10332
10371
  async function resolveComponents(cwd) {
10333
10372
  const components = [];
10334
10373
  const seen = new Set;
10335
10374
  const sources = [
10336
- { dir: join9(cwd, ".jdi", "framework", "components"), source: "project" },
10337
- { dir: join9(homedir(), ".jdi", "components"), source: "user" },
10338
- { dir: join9(import.meta.dir, "../framework/components"), source: "builtin" }
10375
+ { dir: join10(cwd, ".jdi", "framework", "components"), source: "project" },
10376
+ { dir: join10(homedir(), ".jdi", "components"), source: "user" },
10377
+ { dir: join10(import.meta.dir, "../framework/components"), source: "builtin" }
10339
10378
  ];
10340
10379
  for (const { dir, source } of sources) {
10341
10380
  try {
@@ -10344,7 +10383,7 @@ async function resolveComponents(cwd) {
10344
10383
  const name = basename(file, ".md");
10345
10384
  if (!seen.has(name)) {
10346
10385
  seen.add(name);
10347
- components.push({ name, path: join9(dir, file), source });
10386
+ components.push({ name, path: join10(dir, file), source });
10348
10387
  }
10349
10388
  }
10350
10389
  } catch {}
@@ -10490,8 +10529,8 @@ Use --all to stage and commit all, or stage files manually.`);
10490
10529
  });
10491
10530
 
10492
10531
  // src/commands/pr.ts
10493
- import { existsSync as existsSync9 } from "fs";
10494
- import { join as join10 } from "path";
10532
+ import { existsSync as existsSync10 } from "fs";
10533
+ import { join as join11 } from "path";
10495
10534
  async function hasGhCli() {
10496
10535
  const { exitCode } = await exec(["which", "gh"]);
10497
10536
  return exitCode === 0;
@@ -10542,8 +10581,8 @@ var prCommand = defineCommand({
10542
10581
  let verificationChecks = [];
10543
10582
  const planPath = state?.current_plan?.path;
10544
10583
  if (planPath) {
10545
- const fullPlanPath = join10(cwd, planPath);
10546
- if (existsSync9(fullPlanPath)) {
10584
+ const fullPlanPath = join11(cwd, planPath);
10585
+ if (existsSync10(fullPlanPath)) {
10547
10586
  try {
10548
10587
  const planContent = await Bun.file(fullPlanPath).text();
10549
10588
  const nameMatch = planContent.match(/^#\s+(.+)/m);
@@ -10566,8 +10605,8 @@ ${taskLines.map((l2) => `- ${l2.split("|").slice(2, 3).join("").trim()}`).join(`
10566
10605
  }
10567
10606
  }
10568
10607
  let template = "";
10569
- const templatePath = join10(cwd, ".github", "pull_request_template.md");
10570
- if (existsSync9(templatePath)) {
10608
+ const templatePath = join11(cwd, ".github", "pull_request_template.md");
10609
+ if (existsSync10(templatePath)) {
10571
10610
  template = await Bun.file(templatePath).text();
10572
10611
  }
10573
10612
  const title = branch.replace(/^(feat|fix|chore|docs|refactor|test|ci)\//, "").replace(/[-_]/g, " ").replace(/^\w/, (c3) => c3.toUpperCase());
@@ -10863,7 +10902,7 @@ var quickCommand = defineCommand({
10863
10902
  });
10864
10903
 
10865
10904
  // src/commands/worktree.ts
10866
- import { existsSync as existsSync10 } from "fs";
10905
+ import { existsSync as existsSync11 } from "fs";
10867
10906
  function slugify(name) {
10868
10907
  return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
10869
10908
  }
@@ -10897,7 +10936,7 @@ var worktreeCommand = defineCommand({
10897
10936
  }
10898
10937
  const slug = slugify(args.name);
10899
10938
  const worktreePath = `${root}/.worktrees/${slug}`;
10900
- if (existsSync10(worktreePath)) {
10939
+ if (existsSync11(worktreePath)) {
10901
10940
  consola.error(`Worktree already exists at ${worktreePath}`);
10902
10941
  return;
10903
10942
  }
@@ -11063,7 +11102,7 @@ Specify a worktree name: jdi worktree-remove <name>`);
11063
11102
 
11064
11103
  // src/commands/plan-review.ts
11065
11104
  import { resolve as resolve9 } from "path";
11066
- import { existsSync as existsSync11 } from "fs";
11105
+ import { existsSync as existsSync12 } from "fs";
11067
11106
  function parsePlanSummary(content) {
11068
11107
  const nameMatch = content.match(/^# .+?: (.+)$/m);
11069
11108
  const name = nameMatch?.[1] ?? "Unknown";
@@ -11102,7 +11141,7 @@ var planReviewCommand = defineCommand({
11102
11141
  consola.error("No plan found. Run `jdi plan` first.");
11103
11142
  return;
11104
11143
  }
11105
- if (!existsSync11(planPath)) {
11144
+ if (!existsSync12(planPath)) {
11106
11145
  consola.error(`Plan not found: ${planPath}`);
11107
11146
  return;
11108
11147
  }
@@ -11192,7 +11231,7 @@ Tasks (${tasks.length}):`);
11192
11231
 
11193
11232
  // src/commands/plan-approve.ts
11194
11233
  import { resolve as resolve10 } from "path";
11195
- import { existsSync as existsSync12 } from "fs";
11234
+ import { existsSync as existsSync13 } from "fs";
11196
11235
  var planApproveCommand = defineCommand({
11197
11236
  meta: {
11198
11237
  name: "plan-approve",
@@ -11221,7 +11260,7 @@ var planApproveCommand = defineCommand({
11221
11260
  consola.error("No plan to approve. Run `jdi plan` first.");
11222
11261
  return;
11223
11262
  }
11224
- if (!existsSync12(planPath)) {
11263
+ if (!existsSync13(planPath)) {
11225
11264
  consola.error(`Plan not found: ${planPath}`);
11226
11265
  return;
11227
11266
  }
@@ -11698,16 +11737,16 @@ Say **\`Hey Jedi implement\`** when you're ready to go.`;
11698
11737
  return;
11699
11738
  }
11700
11739
  if (intent.command === "ping") {
11701
- const { existsSync: existsSync13 } = await import("fs");
11702
- const { join: join12 } = await import("path");
11703
- const frameworkExists = existsSync13(join12(cwd, ".jdi/framework"));
11704
- const claudeMdExists = existsSync13(join12(cwd, ".claude/CLAUDE.md"));
11705
- const stateExists = existsSync13(join12(cwd, ".jdi/config/state.yaml"));
11706
- const learningsExists = existsSync13(join12(cwd, ".jdi/persistence/learnings.md"));
11740
+ const { existsSync: existsSync14 } = await import("fs");
11741
+ const { join: join13 } = await import("path");
11742
+ const frameworkExists = existsSync14(join13(cwd, ".jdi/framework"));
11743
+ const claudeMdExists = existsSync14(join13(cwd, ".claude/CLAUDE.md"));
11744
+ const stateExists = existsSync14(join13(cwd, ".jdi/config/state.yaml"));
11745
+ const learningsExists = existsSync14(join13(cwd, ".jdi/persistence/learnings.md"));
11707
11746
  let version = "unknown";
11708
11747
  try {
11709
- const pkgPath = join12(cwd, "node_modules/@benzotti/jedi/package.json");
11710
- if (existsSync13(pkgPath)) {
11748
+ const pkgPath = join13(cwd, "node_modules/@benzotti/jedi/package.json");
11749
+ if (existsSync14(pkgPath)) {
11711
11750
  const pkg = JSON.parse(await Bun.file(pkgPath).text());
11712
11751
  version = pkg.version;
11713
11752
  }
@@ -11823,11 +11862,12 @@ Say **\`Hey Jedi implement\`** when you're ready to go.`;
11823
11862
  `## Instructions`,
11824
11863
  `Read state.yaml and existing plan files. Apply feedback incrementally \u2014 do not restart from scratch.`,
11825
11864
  `If the feedback is a question, answer it conversationally. If it implies a plan change, update the plan.`,
11865
+ `Maintain the SPLIT plan format: update the index file and individual task files (.T{n}.md) separately.`,
11826
11866
  ``,
11827
11867
  `## Response Format (MANDATORY)`,
11828
- `1-2 sentence summary of what changed. Then the full updated plan in a collapsible block:`,
11868
+ `1-2 sentence summary of what changed. Then the updated plan summary in a collapsible block:`,
11829
11869
  `\`<details><summary>View full plan</summary> ... </details>\``,
11830
- `Use the same plan structure as the initial plan (tasks table, per-task details, verification).`,
11870
+ `Show the tasks manifest table and brief summaries \u2014 full task details are in the task files.`,
11831
11871
  `End with: "Any changes before implementation?"`
11832
11872
  ].join(`
11833
11873
  `);
@@ -11860,8 +11900,14 @@ Use the ClickUp ticket above as the primary requirements source.` : ``,
11860
11900
  `## Learnings`,
11861
11901
  `Before planning, read .jdi/persistence/learnings.md and .jdi/framework/learnings/ if they exist. Apply any team preferences found.`,
11862
11902
  ``,
11903
+ `## Plan File Format`,
11904
+ `CRITICAL: You MUST write plan files in SPLIT format as defined in your spec (Step 7a):`,
11905
+ `1. Write the index file: \`.jdi/plans/{phase}-{plan}-{slug}.plan.md\` \u2014 contains frontmatter with \`task_files:\` list and a manifest table only (NO inline task details)`,
11906
+ `2. Write each task as a separate file: \`.jdi/plans/{phase}-{plan}-{slug}.T{n}.md\` \u2014 one file per task with full implementation details`,
11907
+ `This split format is MANDATORY \u2014 it reduces token usage by letting agents load only their assigned task.`,
11908
+ ``,
11863
11909
  `## Response Format`,
11864
- `Follow the planning workflow in your spec to write plan files. Then respond with EXACTLY this structure (no deviations, no meta-commentary like "You are now active as..." or "Plan created"):`,
11910
+ `After writing the split plan files, respond with EXACTLY this structure (no deviations, no meta-commentary):`,
11865
11911
  ``,
11866
11912
  `1-2 sentence summary of the approach.`,
11867
11913
  ``,
@@ -11878,16 +11924,7 @@ Use the ClickUp ticket above as the primary requirements source.` : ``,
11878
11924
  `|------|------|------|------|------|`,
11879
11925
  `| T1 | {name} | {S|M|L} | auto | 1 |`,
11880
11926
  ``,
11881
- `### T1 \u2014 {Task Name}`,
11882
- `**Objective:** {what this achieves}`,
11883
- ``,
11884
- `**Steps:**`,
11885
- `1. {step}`,
11886
- ``,
11887
- `**Done when:** {completion criterion}`,
11888
- ``,
11889
- `---`,
11890
- `(repeat for each task)`,
11927
+ `(For each task, show a brief 1-2 line summary \u2014 full details are in the task files)`,
11891
11928
  ``,
11892
11929
  `### Verification`,
11893
11930
  `- [ ] {check 1}`,
@@ -12086,8 +12123,8 @@ Use the ClickUp ticket above as the primary requirements source.` : ``,
12086
12123
  });
12087
12124
 
12088
12125
  // src/commands/setup-action.ts
12089
- import { join as join12, dirname as dirname3 } from "path";
12090
- import { existsSync as existsSync13, mkdirSync as mkdirSync4 } from "fs";
12126
+ import { join as join13, dirname as dirname3 } from "path";
12127
+ import { existsSync as existsSync14, mkdirSync as mkdirSync4 } from "fs";
12091
12128
  var setupActionCommand = defineCommand({
12092
12129
  meta: {
12093
12130
  name: "setup-action",
@@ -12096,18 +12133,18 @@ var setupActionCommand = defineCommand({
12096
12133
  args: {},
12097
12134
  async run() {
12098
12135
  const cwd = process.cwd();
12099
- const workflowDest = join12(cwd, ".github", "workflows", "jedi.yml");
12100
- if (existsSync13(workflowDest)) {
12136
+ const workflowDest = join13(cwd, ".github", "workflows", "jedi.yml");
12137
+ if (existsSync14(workflowDest)) {
12101
12138
  consola.warn(`Workflow already exists at ${workflowDest}`);
12102
12139
  consola.info("Skipping workflow copy. Delete it manually to regenerate.");
12103
12140
  } else {
12104
- const templatePath = join12(import.meta.dir, "../action/workflow-template.yml");
12105
- if (!existsSync13(templatePath)) {
12141
+ const templatePath = join13(import.meta.dir, "../action/workflow-template.yml");
12142
+ if (!existsSync14(templatePath)) {
12106
12143
  consola.error("Workflow template not found. Ensure @benzotti/jedi is properly installed.");
12107
12144
  process.exit(1);
12108
12145
  }
12109
12146
  const dir = dirname3(workflowDest);
12110
- if (!existsSync13(dir))
12147
+ if (!existsSync14(dir))
12111
12148
  mkdirSync4(dir, { recursive: true });
12112
12149
  const template = await Bun.file(templatePath).text();
12113
12150
  await Bun.write(workflowDest, template);
@@ -12250,7 +12287,7 @@ var stateCommand = defineCommand({
12250
12287
  // package.json
12251
12288
  var package_default = {
12252
12289
  name: "@benzotti/jedi",
12253
- version: "0.1.33",
12290
+ version: "0.1.34",
12254
12291
  description: "JDI - Context-efficient AI development framework for Claude Code",
12255
12292
  type: "module",
12256
12293
  bin: {
@@ -37,13 +37,17 @@ Before planning, ALWAYS:
37
37
 
38
38
  You MUST write files using Write/Edit tools. Returning plan content as text is NOT acceptable.
39
39
 
40
- Required files:
41
- 1. `.jdi/plans/{phase}-{plan}-{slug}.plan.md` (index file)
42
- 2. `.jdi/plans/{phase}-{plan}-{slug}.T{n}.md` (one per task)
40
+ **SANDBOX OVERRIDE:** Plan file creation is an EXCEPTION to AgentBase sandbox rules. You MUST use the Write tool to create plan files directly — do NOT defer them to `files_to_create`. Plan files WILL persist when written via the Write tool. This override applies ONLY to `.jdi/plans/` and `.jdi/config/` files listed below.
41
+
42
+ Required files (SPLIT FORMAT — one file per task):
43
+ 1. `.jdi/plans/{phase}-{plan}-{slug}.plan.md` (index file — manifest table only, NO inline task details)
44
+ 2. `.jdi/plans/{phase}-{plan}-{slug}.T{n}.md` (one per task — full implementation details)
43
45
  3. `.jdi/config/variables.yaml`
44
46
  4. `.jdi/ROADMAP.yaml` (add plan entry)
45
47
  5. `.jdi/REQUIREMENTS.yaml` (add traceability)
46
48
 
49
+ The split format is MANDATORY. Each task MUST be a separate `.T{n}.md` file. The index file contains ONLY the frontmatter (with `task_files:` list) and a manifest table — NEVER inline task implementation details.
50
+
47
51
  **Do NOT manually edit `.jdi/config/state.yaml`** — state transitions are handled via CLI commands (e.g. `npx jdi state plan-ready`).
48
52
 
49
53
  ## File Naming
@@ -20,8 +20,8 @@ Create an implementation plan using a single planner agent (includes research).
20
20
  2. Read codebase context (`.jdi/codebase/SUMMARY.md` if exists)
21
21
  3. Read scaffolding (.jdi/PROJECT.yaml, REQUIREMENTS.yaml, ROADMAP.yaml) — create from templates if missing
22
22
  4. Quick Mode Detection — suggest /jdi:quick for trivial tasks
23
- 5. Spawn `jdi-planner` agent (subagent_type="general-purpose") — creates PLAN.md with tasks, deps, waves
24
- 6. Collect and execute deferred ops
23
+ 5. Spawn `jdi-planner` agent (subagent_type="general-purpose") — creates split plan files (index .plan.md + per-task .T{n}.md files). The planner MUST write these files directly via Write tool (sandbox override for plan files).
24
+ 6. Collect and execute deferred ops — if the agent returned `files_to_create`, create them now using Write tool. Verify split plan files exist: index `.plan.md` + individual `.T{n}.md` task files.
25
25
  7. **Update state via CLI** — do NOT manually edit state.yaml. Run:
26
26
  ```bash
27
27
  npx jdi state plan-ready --plan-path ".jdi/plans/{plan-file}" --plan-name "{plan name}"
@@ -54,3 +54,7 @@ Do NOT manually edit `.jdi/config/state.yaml` for status transitions. Use the CL
54
54
  - `npx jdi state advance-task {task-id}` — after each task completes
55
55
 
56
56
  You may only append to `decisions`, `deviations`, or `blockers` arrays in state.yaml directly via `<JDI:StateUpdate />`.
57
+
58
+ ## Self-Testing (Jedi development only)
59
+
60
+ If the current project is the Jedi framework itself (`@benzotti/jedi`), run `bun test` after modifying prompt builders, action commands, or framework files. This catches regressions in split format references, learnings inclusion, and framework invariants.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benzotti/jedi",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "description": "JDI - Context-efficient AI development framework for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {