@benzotti/jedi 0.1.32 → 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
@@ -9355,7 +9355,7 @@ Recognise natural language JDI intents and invoke the matching skill via the Ski
9355
9355
 
9356
9356
  Extract flags from context: "in a worktree" \u2192 \`--worktree\`, "lightweight" \u2192 \`--worktree-lightweight\`, "single agent" \u2192 \`--single\`, "use teams" \u2192 \`--team\`. If the intent is unclear, ask. Never guess.
9357
9357
 
9358
- Planning and implementation are separate gates \u2014 NEVER auto-proceed to implementation after plan approval.
9358
+ Planning and implementation are separate human-gated phases \u2014 NEVER auto-proceed to implementation after plan approval. When a plan is approved, STOP and wait for an explicit implementation request.
9359
9359
 
9360
9360
  ## Iterative Refinement
9361
9361
 
@@ -9379,7 +9379,7 @@ Recognise natural language JDI intents and invoke the matching skill via the Ski
9379
9379
 
9380
9380
  Extract flags from context: "in a worktree" \u2192 \`--worktree\`, "lightweight" \u2192 \`--worktree-lightweight\`, "single agent" \u2192 \`--single\`, "use teams" \u2192 \`--team\`. If the intent is unclear, ask. Never guess.
9381
9381
 
9382
- Planning and implementation are separate gates \u2014 NEVER auto-proceed to implementation after plan approval.
9382
+ Planning and implementation are separate human-gated phases \u2014 NEVER auto-proceed to implementation after plan approval. When a plan is approved, STOP and wait for an explicit implementation request.
9383
9383
 
9384
9384
  ## Iterative Refinement
9385
9385
 
@@ -9975,6 +9975,9 @@ function buildReviewPrompt(ctx, prNum, meta, diff) {
9975
9975
  ``,
9976
9976
  meta,
9977
9977
  ``,
9978
+ ...buildLearningsBlock(ctx.techStack),
9979
+ `Cross-reference learnings against every change \u2014 flag violations and praise adherence.`,
9980
+ ``,
9978
9981
  `## Diff`,
9979
9982
  "```diff",
9980
9983
  diff,
@@ -9992,6 +9995,7 @@ function buildReviewPrompt(ctx, prNum, meta, diff) {
9992
9995
  `- Does it follow the project's existing patterns?`,
9993
9996
  `- Are naming conventions consistent?`,
9994
9997
  `- Is the code well-organised?`,
9998
+ `- Does it follow the team's documented learnings?`,
9995
9999
  ``,
9996
10000
  `### Security`,
9997
10001
  `- Any injection risks (SQL, XSS, command)?`,
@@ -10080,6 +10084,72 @@ async function writeState(cwd, state) {
10080
10084
  }
10081
10085
 
10082
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
+ }
10100
+ async function transitionToPlanReady(cwd, planPath, planName) {
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
+ }
10118
+ state.position = {
10119
+ ...state.position,
10120
+ ...phase != null ? { phase } : {},
10121
+ plan: planNumber ?? planPath,
10122
+ plan_name: planName,
10123
+ status: "planning"
10124
+ };
10125
+ state.current_plan = {
10126
+ ...state.current_plan,
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
10136
+ };
10137
+ state.review = {
10138
+ ...state.review,
10139
+ status: "in_review",
10140
+ scope: "plan"
10141
+ };
10142
+ await updateSessionActivity(cwd, state);
10143
+ }
10144
+ async function transitionToApproved(cwd) {
10145
+ const state = await readState(cwd) ?? {};
10146
+ state.review = {
10147
+ ...state.review,
10148
+ status: "approved",
10149
+ approved_at: new Date().toISOString()
10150
+ };
10151
+ await updateSessionActivity(cwd, state);
10152
+ }
10083
10153
  async function transitionToExecuting(cwd, taskId, taskName) {
10084
10154
  const state = await readState(cwd) ?? {};
10085
10155
  state.position = {
@@ -10098,6 +10168,24 @@ async function transitionToComplete(cwd) {
10098
10168
  };
10099
10169
  await updateSessionActivity(cwd, state);
10100
10170
  }
10171
+ async function advanceTask(cwd, completedTaskId) {
10172
+ const state = await readState(cwd) ?? {};
10173
+ if (state.current_plan) {
10174
+ const completed = state.current_plan.completed_tasks ?? [];
10175
+ if (!completed.includes(completedTaskId)) {
10176
+ completed.push(completedTaskId);
10177
+ }
10178
+ state.current_plan.completed_tasks = completed;
10179
+ const tasks = state.current_plan.tasks ?? [];
10180
+ const nextIndex = completed.length;
10181
+ state.current_plan.current_task_index = nextIndex < tasks.length ? nextIndex : null;
10182
+ }
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 };
10185
+ }
10186
+ state.progress.tasks_completed = (state.progress.tasks_completed ?? 0) + 1;
10187
+ await updateSessionActivity(cwd, state);
10188
+ }
10101
10189
  async function updateSessionActivity(cwd, state) {
10102
10190
  state.session = {
10103
10191
  ...state.session,
@@ -10278,15 +10366,15 @@ var statusCommand = defineCommand({
10278
10366
  import { relative } from "path";
10279
10367
 
10280
10368
  // src/utils/resolve-components.ts
10281
- import { join as join9, basename } from "path";
10369
+ import { join as join10, basename } from "path";
10282
10370
  import { homedir } from "os";
10283
10371
  async function resolveComponents(cwd) {
10284
10372
  const components = [];
10285
10373
  const seen = new Set;
10286
10374
  const sources = [
10287
- { dir: join9(cwd, ".jdi", "framework", "components"), source: "project" },
10288
- { dir: join9(homedir(), ".jdi", "components"), source: "user" },
10289
- { 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" }
10290
10378
  ];
10291
10379
  for (const { dir, source } of sources) {
10292
10380
  try {
@@ -10295,7 +10383,7 @@ async function resolveComponents(cwd) {
10295
10383
  const name = basename(file, ".md");
10296
10384
  if (!seen.has(name)) {
10297
10385
  seen.add(name);
10298
- components.push({ name, path: join9(dir, file), source });
10386
+ components.push({ name, path: join10(dir, file), source });
10299
10387
  }
10300
10388
  }
10301
10389
  } catch {}
@@ -10441,8 +10529,8 @@ Use --all to stage and commit all, or stage files manually.`);
10441
10529
  });
10442
10530
 
10443
10531
  // src/commands/pr.ts
10444
- import { existsSync as existsSync9 } from "fs";
10445
- import { join as join10 } from "path";
10532
+ import { existsSync as existsSync10 } from "fs";
10533
+ import { join as join11 } from "path";
10446
10534
  async function hasGhCli() {
10447
10535
  const { exitCode } = await exec(["which", "gh"]);
10448
10536
  return exitCode === 0;
@@ -10493,8 +10581,8 @@ var prCommand = defineCommand({
10493
10581
  let verificationChecks = [];
10494
10582
  const planPath = state?.current_plan?.path;
10495
10583
  if (planPath) {
10496
- const fullPlanPath = join10(cwd, planPath);
10497
- if (existsSync9(fullPlanPath)) {
10584
+ const fullPlanPath = join11(cwd, planPath);
10585
+ if (existsSync10(fullPlanPath)) {
10498
10586
  try {
10499
10587
  const planContent = await Bun.file(fullPlanPath).text();
10500
10588
  const nameMatch = planContent.match(/^#\s+(.+)/m);
@@ -10517,8 +10605,8 @@ ${taskLines.map((l2) => `- ${l2.split("|").slice(2, 3).join("").trim()}`).join(`
10517
10605
  }
10518
10606
  }
10519
10607
  let template = "";
10520
- const templatePath = join10(cwd, ".github", "pull_request_template.md");
10521
- if (existsSync9(templatePath)) {
10608
+ const templatePath = join11(cwd, ".github", "pull_request_template.md");
10609
+ if (existsSync10(templatePath)) {
10522
10610
  template = await Bun.file(templatePath).text();
10523
10611
  }
10524
10612
  const title = branch.replace(/^(feat|fix|chore|docs|refactor|test|ci)\//, "").replace(/[-_]/g, " ").replace(/^\w/, (c3) => c3.toUpperCase());
@@ -10621,7 +10709,8 @@ ${data.body}` : ""
10621
10709
  meta = metaResult.stdout;
10622
10710
  }
10623
10711
  }
10624
- const prompt2 = buildReviewPrompt({ cwd: process.cwd(), projectType: "", techStack: "", qualityGates: "", learningsPath: null, codebaseIndexPath: null, adapter: null }, String(prNum), meta, diffResult.stdout);
10712
+ const ctx = await gatherPromptContext(process.cwd());
10713
+ const prompt2 = buildReviewPrompt(ctx, String(prNum), meta, diffResult.stdout);
10625
10714
  if (args.output) {
10626
10715
  await Bun.write(resolve7(process.cwd(), args.output), prompt2);
10627
10716
  consola.success(`Review prompt written to ${args.output}`);
@@ -10813,7 +10902,7 @@ var quickCommand = defineCommand({
10813
10902
  });
10814
10903
 
10815
10904
  // src/commands/worktree.ts
10816
- import { existsSync as existsSync10 } from "fs";
10905
+ import { existsSync as existsSync11 } from "fs";
10817
10906
  function slugify(name) {
10818
10907
  return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
10819
10908
  }
@@ -10847,7 +10936,7 @@ var worktreeCommand = defineCommand({
10847
10936
  }
10848
10937
  const slug = slugify(args.name);
10849
10938
  const worktreePath = `${root}/.worktrees/${slug}`;
10850
- if (existsSync10(worktreePath)) {
10939
+ if (existsSync11(worktreePath)) {
10851
10940
  consola.error(`Worktree already exists at ${worktreePath}`);
10852
10941
  return;
10853
10942
  }
@@ -11013,7 +11102,7 @@ Specify a worktree name: jdi worktree-remove <name>`);
11013
11102
 
11014
11103
  // src/commands/plan-review.ts
11015
11104
  import { resolve as resolve9 } from "path";
11016
- import { existsSync as existsSync11 } from "fs";
11105
+ import { existsSync as existsSync12 } from "fs";
11017
11106
  function parsePlanSummary(content) {
11018
11107
  const nameMatch = content.match(/^# .+?: (.+)$/m);
11019
11108
  const name = nameMatch?.[1] ?? "Unknown";
@@ -11052,7 +11141,7 @@ var planReviewCommand = defineCommand({
11052
11141
  consola.error("No plan found. Run `jdi plan` first.");
11053
11142
  return;
11054
11143
  }
11055
- if (!existsSync11(planPath)) {
11144
+ if (!existsSync12(planPath)) {
11056
11145
  consola.error(`Plan not found: ${planPath}`);
11057
11146
  return;
11058
11147
  }
@@ -11142,7 +11231,7 @@ Tasks (${tasks.length}):`);
11142
11231
 
11143
11232
  // src/commands/plan-approve.ts
11144
11233
  import { resolve as resolve10 } from "path";
11145
- import { existsSync as existsSync12 } from "fs";
11234
+ import { existsSync as existsSync13 } from "fs";
11146
11235
  var planApproveCommand = defineCommand({
11147
11236
  meta: {
11148
11237
  name: "plan-approve",
@@ -11171,7 +11260,7 @@ var planApproveCommand = defineCommand({
11171
11260
  consola.error("No plan to approve. Run `jdi plan` first.");
11172
11261
  return;
11173
11262
  }
11174
- if (!existsSync12(planPath)) {
11263
+ if (!existsSync13(planPath)) {
11175
11264
  consola.error(`Plan not found: ${planPath}`);
11176
11265
  return;
11177
11266
  }
@@ -11648,16 +11737,16 @@ Say **\`Hey Jedi implement\`** when you're ready to go.`;
11648
11737
  return;
11649
11738
  }
11650
11739
  if (intent.command === "ping") {
11651
- const { existsSync: existsSync13 } = await import("fs");
11652
- const { join: join12 } = await import("path");
11653
- const frameworkExists = existsSync13(join12(cwd, ".jdi/framework"));
11654
- const claudeMdExists = existsSync13(join12(cwd, ".claude/CLAUDE.md"));
11655
- const stateExists = existsSync13(join12(cwd, ".jdi/config/state.yaml"));
11656
- 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"));
11657
11746
  let version = "unknown";
11658
11747
  try {
11659
- const pkgPath = join12(cwd, "node_modules/@benzotti/jedi/package.json");
11660
- if (existsSync13(pkgPath)) {
11748
+ const pkgPath = join13(cwd, "node_modules/@benzotti/jedi/package.json");
11749
+ if (existsSync14(pkgPath)) {
11661
11750
  const pkg = JSON.parse(await Bun.file(pkgPath).text());
11662
11751
  version = pkg.version;
11663
11752
  }
@@ -11773,11 +11862,12 @@ Say **\`Hey Jedi implement\`** when you're ready to go.`;
11773
11862
  `## Instructions`,
11774
11863
  `Read state.yaml and existing plan files. Apply feedback incrementally \u2014 do not restart from scratch.`,
11775
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.`,
11776
11866
  ``,
11777
11867
  `## Response Format (MANDATORY)`,
11778
- `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:`,
11779
11869
  `\`<details><summary>View full plan</summary> ... </details>\``,
11780
- `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.`,
11781
11871
  `End with: "Any changes before implementation?"`
11782
11872
  ].join(`
11783
11873
  `);
@@ -11810,8 +11900,14 @@ Use the ClickUp ticket above as the primary requirements source.` : ``,
11810
11900
  `## Learnings`,
11811
11901
  `Before planning, read .jdi/persistence/learnings.md and .jdi/framework/learnings/ if they exist. Apply any team preferences found.`,
11812
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
+ ``,
11813
11909
  `## Response Format`,
11814
- `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):`,
11815
11911
  ``,
11816
11912
  `1-2 sentence summary of the approach.`,
11817
11913
  ``,
@@ -11828,16 +11924,7 @@ Use the ClickUp ticket above as the primary requirements source.` : ``,
11828
11924
  `|------|------|------|------|------|`,
11829
11925
  `| T1 | {name} | {S|M|L} | auto | 1 |`,
11830
11926
  ``,
11831
- `### T1 \u2014 {Task Name}`,
11832
- `**Objective:** {what this achieves}`,
11833
- ``,
11834
- `**Steps:**`,
11835
- `1. {step}`,
11836
- ``,
11837
- `**Done when:** {completion criterion}`,
11838
- ``,
11839
- `---`,
11840
- `(repeat for each task)`,
11927
+ `(For each task, show a brief 1-2 line summary \u2014 full details are in the task files)`,
11841
11928
  ``,
11842
11929
  `### Verification`,
11843
11930
  `- [ ] {check 1}`,
@@ -12036,8 +12123,8 @@ Use the ClickUp ticket above as the primary requirements source.` : ``,
12036
12123
  });
12037
12124
 
12038
12125
  // src/commands/setup-action.ts
12039
- import { join as join12, dirname as dirname3 } from "path";
12040
- 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";
12041
12128
  var setupActionCommand = defineCommand({
12042
12129
  meta: {
12043
12130
  name: "setup-action",
@@ -12046,18 +12133,18 @@ var setupActionCommand = defineCommand({
12046
12133
  args: {},
12047
12134
  async run() {
12048
12135
  const cwd = process.cwd();
12049
- const workflowDest = join12(cwd, ".github", "workflows", "jedi.yml");
12050
- if (existsSync13(workflowDest)) {
12136
+ const workflowDest = join13(cwd, ".github", "workflows", "jedi.yml");
12137
+ if (existsSync14(workflowDest)) {
12051
12138
  consola.warn(`Workflow already exists at ${workflowDest}`);
12052
12139
  consola.info("Skipping workflow copy. Delete it manually to regenerate.");
12053
12140
  } else {
12054
- const templatePath = join12(import.meta.dir, "../action/workflow-template.yml");
12055
- if (!existsSync13(templatePath)) {
12141
+ const templatePath = join13(import.meta.dir, "../action/workflow-template.yml");
12142
+ if (!existsSync14(templatePath)) {
12056
12143
  consola.error("Workflow template not found. Ensure @benzotti/jedi is properly installed.");
12057
12144
  process.exit(1);
12058
12145
  }
12059
12146
  const dir = dirname3(workflowDest);
12060
- if (!existsSync13(dir))
12147
+ if (!existsSync14(dir))
12061
12148
  mkdirSync4(dir, { recursive: true });
12062
12149
  const template = await Bun.file(templatePath).text();
12063
12150
  await Bun.write(workflowDest, template);
@@ -12093,10 +12180,114 @@ var setupActionCommand = defineCommand({
12093
12180
  `));
12094
12181
  }
12095
12182
  });
12183
+
12184
+ // src/commands/state.ts
12185
+ var planReadyCommand = defineCommand({
12186
+ meta: {
12187
+ name: "plan-ready",
12188
+ description: "Transition state after plan creation"
12189
+ },
12190
+ args: {
12191
+ "plan-path": {
12192
+ type: "string",
12193
+ description: "Path to the plan file",
12194
+ required: true
12195
+ },
12196
+ "plan-name": {
12197
+ type: "string",
12198
+ description: "Human-readable plan name",
12199
+ required: true
12200
+ }
12201
+ },
12202
+ async run({ args }) {
12203
+ const cwd = process.cwd();
12204
+ await transitionToPlanReady(cwd, args["plan-path"], args["plan-name"]);
12205
+ consola.success(`State \u2192 plan-ready (${args["plan-name"]})`);
12206
+ }
12207
+ });
12208
+ var approvedCommand = defineCommand({
12209
+ meta: {
12210
+ name: "approved",
12211
+ description: "Transition state after plan approval"
12212
+ },
12213
+ async run() {
12214
+ const cwd = process.cwd();
12215
+ await transitionToApproved(cwd);
12216
+ consola.success("State \u2192 approved");
12217
+ }
12218
+ });
12219
+ var executingCommand = defineCommand({
12220
+ meta: {
12221
+ name: "executing",
12222
+ description: "Transition state when implementation starts"
12223
+ },
12224
+ args: {
12225
+ "task-id": {
12226
+ type: "string",
12227
+ description: "Current task ID",
12228
+ required: false
12229
+ },
12230
+ "task-name": {
12231
+ type: "string",
12232
+ description: "Current task name",
12233
+ required: false
12234
+ }
12235
+ },
12236
+ async run({ args }) {
12237
+ const cwd = process.cwd();
12238
+ await transitionToExecuting(cwd, args["task-id"], args["task-name"]);
12239
+ consola.success("State \u2192 executing");
12240
+ }
12241
+ });
12242
+ var completeCommand = defineCommand({
12243
+ meta: {
12244
+ name: "complete",
12245
+ description: "Transition state after implementation finishes"
12246
+ },
12247
+ async run() {
12248
+ const cwd = process.cwd();
12249
+ await transitionToComplete(cwd);
12250
+ consola.success("State \u2192 complete");
12251
+ }
12252
+ });
12253
+ var advanceTaskCommand = defineCommand({
12254
+ meta: {
12255
+ name: "advance-task",
12256
+ description: "Mark a task as completed and advance to next"
12257
+ },
12258
+ args: {
12259
+ "task-id": {
12260
+ type: "positional",
12261
+ description: "ID of the completed task",
12262
+ required: true
12263
+ }
12264
+ },
12265
+ async run({ args }) {
12266
+ const cwd = process.cwd();
12267
+ await advanceTask(cwd, args["task-id"]);
12268
+ const state = await readState(cwd);
12269
+ const completed = state?.current_plan?.completed_tasks?.length ?? 0;
12270
+ const total = state?.current_plan?.tasks?.length ?? 0;
12271
+ consola.success(`Task ${args["task-id"]} completed (${completed}/${total})`);
12272
+ }
12273
+ });
12274
+ var stateCommand = defineCommand({
12275
+ meta: {
12276
+ name: "state",
12277
+ description: "Manage JDI state transitions"
12278
+ },
12279
+ subCommands: {
12280
+ "plan-ready": planReadyCommand,
12281
+ approved: approvedCommand,
12282
+ executing: executingCommand,
12283
+ complete: completeCommand,
12284
+ "advance-task": advanceTaskCommand
12285
+ }
12286
+ });
12096
12287
  // package.json
12097
12288
  var package_default = {
12098
12289
  name: "@benzotti/jedi",
12099
- version: "0.1.32",
12290
+ version: "0.1.34",
12100
12291
  description: "JDI - Context-efficient AI development framework for Claude Code",
12101
12292
  type: "module",
12102
12293
  bin: {
@@ -12158,7 +12349,8 @@ var main = defineCommand({
12158
12349
  "plan-review": planReviewCommand,
12159
12350
  "plan-approve": planApproveCommand,
12160
12351
  action: actionCommand,
12161
- "setup-action": setupActionCommand
12352
+ "setup-action": setupActionCommand,
12353
+ state: stateCommand
12162
12354
  }
12163
12355
  });
12164
12356
  runMain(main);
@@ -15,14 +15,16 @@ You create executable implementation plans with proper task sizing, dependency m
15
15
 
16
16
  ## CRITICAL: Scope Discipline
17
17
 
18
- You MUST only plan what was explicitly requested. Never infer, assume, or add extras.
18
+ Do not add unrelated extras (tooling, testing, linting, CI) unless the user explicitly requests them. But you MUST thoroughly investigate the full scope of what WAS requested — including implicit requirements.
19
19
 
20
20
  **Rules:**
21
- 1. **Only include what was asked for.** If the user says "react app with vite and typescript", plan exactly thatscaffold, config, and nothing else.
22
- 2. **Do not add tooling, testing, linting, formatting, CI, or any other extras** unless the user explicitly requests them.
23
- 3. **Do not make subjective decisions.** If something is ambiguous (e.g. folder structure, routing library, state management), list it as an open question and ask the userdo not guess.
24
- 4. **Suggest optional additions separately.** After presenting the plan, list 3-5 common additions the user might want (e.g. "Would you also like: testing (Vitest)? linting (ESLint)? formatting (Prettier)?"). These are suggestions, NOT part of the plan.
25
- 5. **Same request = same plan.** Two identical requests must produce structurally identical plans. Achieve this by following the templates exactly and not improvising.
21
+ 1. **Do not add unrelated extras.** If the user says "react app with vite and typescript", plan scaffold and config not linting, CI, or testing unless asked.
22
+ 2. **DO investigate the full scope of the request.** "Scope discipline" means no unrelated additions — it does NOT mean ignoring requirements that are clearly implied by the request. If the user asks for a UI view with specific columns, you must verify those columns exist in the backend response, and plan to add them if they don't.
23
+ 3. **When reference PRs/tickets are provided, analyse them thoroughly.** Read the actual diff, files changed, patterns used, columns/fields added, routes created, and data flow. The user provides reference PRs so you follow the same pattern extract the full pattern, don't just skim.
24
+ 4. **When the user says "backend is already done", verify it.** Read the actual API endpoint, check what fields it returns, and confirm they match the frontend requirements. If there's a gap, include it in the plan.
25
+ 5. **Do not make subjective decisions.** If something is ambiguous (e.g. folder structure, routing library, state management), list it as an open question and ask the user — do not guess.
26
+ 6. **Suggest optional additions separately.** After presenting the plan, list 3-5 common additions the user might want. These are suggestions, NOT part of the plan.
27
+ 7. **Same request = same plan.** Two identical requests must produce structurally identical plans. Achieve this by following the templates exactly and not improvising.
26
28
 
27
29
  ## CRITICAL: Read Learnings First
28
30
 
@@ -35,13 +37,18 @@ Before planning, ALWAYS:
35
37
 
36
38
  You MUST write files using Write/Edit tools. Returning plan content as text is NOT acceptable.
37
39
 
38
- Required files:
39
- 1. `.jdi/plans/{phase}-{plan}-{slug}.plan.md` (index file)
40
- 2. `.jdi/plans/{phase}-{plan}-{slug}.T{n}.md` (one per task)
41
- 3. `.jdi/config/state.yaml`
42
- 4. `.jdi/config/variables.yaml`
43
- 5. `.jdi/ROADMAP.yaml` (add plan entry)
44
- 6. `.jdi/REQUIREMENTS.yaml` (add traceability)
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)
45
+ 3. `.jdi/config/variables.yaml`
46
+ 4. `.jdi/ROADMAP.yaml` (add plan entry)
47
+ 5. `.jdi/REQUIREMENTS.yaml` (add traceability)
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
+
51
+ **Do NOT manually edit `.jdi/config/state.yaml`** — state transitions are handled via CLI commands (e.g. `npx jdi state plan-ready`).
45
52
 
46
53
  ## File Naming
47
54
 
@@ -87,6 +94,15 @@ Never use time estimates. Use S/M/L sizing in task manifests and plan summaries.
87
94
  4. Research: standard stack, architecture patterns, common pitfalls
88
95
  5. Findings feed directly into planning (no separate RESEARCH.md)
89
96
 
97
+ ### Step 0b: Reference Analysis (when provided)
98
+
99
+ If the user provides reference PRs, tickets, or example implementations:
100
+
101
+ 1. **Reference PRs**: Fetch each PR's diff (`gh pr diff {number}`), list of changed files (`gh pr view {number} --json files`), and description. Analyse the **complete pattern**: what files were changed, what columns/fields were added, what routes were created, what data transformations were applied. The reference PR defines the pattern you must follow — extract every detail.
102
+ 2. **Existing backend/API work**: When the user states "backend is already done" or implies API endpoints exist, verify by reading the actual route files, controllers, and response shapes. Confirm the API returns all fields the frontend will need. If fields are missing, include them in the plan.
103
+ 3. **ClickUp/ticket context**: If a ticket URL is provided, read the ticket's description, acceptance criteria, and any attached specifications. Cross-reference against what the plan covers.
104
+ 4. **Data requirements for UI work**: When planning a view/page/table, explicitly list every column/field the UI needs, verify each one exists in the API response, and plan to add any that are missing (both backend and frontend).
105
+
90
106
  ### Step 1: Discovery
91
107
 
92
108
  <JDI:TaskBreakdown source="requirements" />
@@ -94,6 +110,8 @@ Never use time estimates. Use S/M/L sizing in task manifests and plan summaries.
94
110
  #### Mandatory Verification (never skip)
95
111
  - **Bug fixes**: Grep the symptom across entire codebase. Trace every occurrence through all layers. Do not stop at first match.
96
112
  - **API boundaries**: Read backend route, controller, and request validation (or frontend consumer). Never assume endpoint fields.
113
+ - **UI views/tables**: List every column from the requirements. Verify each column's data source exists in the backend response. Plan to add missing fields end-to-end (backend + frontend).
114
+ - **Reference PR patterns**: If reference PRs were provided, verify the plan covers every layer those PRs touched (routes, controllers, types, components, hooks, etc.).
97
115
 
98
116
  ### Step 2: Scope Estimation
99
117
  If >4 tasks or >3 hours, split into multiple plans.
@@ -133,12 +151,9 @@ Types: `checkpoint:human-verify`, `checkpoint:decision`, `checkpoint:human-actio
133
151
 
134
152
  ### Step 7: Generate Plan Document and Update Scaffolding (WRITE FILES)
135
153
 
136
- <JDI:StateUpdate />
137
-
138
- #### 7-pre: Update State Files
139
- Read `.jdi/config/state.yaml` (create from template if missing). Update: `position.phase`, `position.plan`, `position.status` → `"planning"`, `progress.plans_total`, `progress.tasks_total`, `current_plan.path`, `current_plan.tasks`. Each task entry must include `file:` field pointing to the task file path.
154
+ **Do NOT manually edit `.jdi/config/state.yaml`** — use `npx jdi state` CLI commands for transitions. Only record decisions, deviations, or blockers via `<JDI:StateUpdate />`.
140
155
 
141
- #### 7-pre-b: Update Variables
156
+ #### 7-pre: Update Variables
142
157
  Read `.jdi/config/variables.yaml` (create from template if missing). Update: `feature.name`, `feature.description`, `feature.type`.
143
158
 
144
159
  #### 7a: Write Plan Files (Split Format)
@@ -20,12 +20,18 @@ 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
25
- 7. Update state.yaml: status `"plan_ready"`, worktree state if active
26
- 8. Initialise review: `review.status` → `"draft"`, `review.revision` → 1, `review.scope` → `"plan"`
27
- 9. **Present summary** (name, objective, task table, files) then ask: _"Provide feedback to refine, or say **approved** to finalise."_
28
- 10. **Review loop**: approval → update state to `"approved"`, confirm. Feedback → revise plan in-place, increment revision, re-present summary. Repeat until approved. This is natural conversation — no separate command needed.
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
+ 7. **Update state via CLI** do NOT manually edit state.yaml. Run:
26
+ ```bash
27
+ npx jdi state plan-ready --plan-path ".jdi/plans/{plan-file}" --plan-name "{plan name}"
28
+ ```
29
+ 8. **Present summary** (name, objective, task table, files) then ask: _"Provide feedback to refine, or say **approved** to finalise."_
30
+ 9. **Review loop**: Feedback → revise plan in-place, increment revision, re-present summary. Repeat until approved. Approval → run `npx jdi state approved`, then **STOP**.
31
+
32
+ ## HARD STOP — Planning Gate
33
+
34
+ After the user approves the plan, your work is **DONE**. Output: _"Plan approved and finalised. Run `/jdi:implement-plan` when ready to execute."_ Then **STOP completely**. Do NOT invoke `/jdi:implement-plan`, do NOT spawn implementation agents, do NOT begin writing source code. Planning and implementation are separate human-gated phases.
29
35
 
30
36
  Agent base (read FIRST for cache): .jdi/framework/components/meta/AgentBase.md | Agent spec: .jdi/framework/agents/jdi-planner.md
31
37
 
@@ -15,18 +15,18 @@ Execute a PLAN.md with complexity-based routing.
15
15
 
16
16
  1. Read codebase context (`.jdi/codebase/SUMMARY.md` if exists)
17
17
  2. Read plan index file and state.yaml — parse frontmatter for tasks, deps, waves, tech_stack
18
- 3. **Format detection:** If frontmatter contains `task_files:`, this is a split plan task details are in separate files. If absent, legacy monolithic plan all tasks inline.
19
- 4. **Complexity routing** (`<JDI:ComplexityRouter />`): Simple (≤3 tasks, single stack/wave) single agent. Complex Agent Teams swarm. Override: `--team` / `--single`
20
- 5. **Tech routing**: PHP jdi-backend | TS/Reactjdi-frontend | Full-stackboth
21
- 6. Execute:
18
+ 3. **Read learnings:** Always read `.jdi/framework/learnings/general.md`. Then read domain-specific learnings based on tech_stack from plan frontmatter: PHP `backend.md`, TS/React `frontend.md`. Follow any conventions foundlearnings override defaults.
19
+ 4. **Format detection:** If frontmatter contains `task_files:`, this is a split plan task details are in separate files. If absent, legacy monolithic plan — all tasks inline.
20
+ 5. **Complexity routing** (`<JDI:ComplexityRouter />`): Simple (≤3 tasks, single stack/wave)single agent. ComplexAgent Teams swarm. Override: `--team` / `--single`
21
+ 6. **Tech routing**: PHP → jdi-backend | TS/React → jdi-frontend | Full-stack → both
22
+ 7. Execute:
22
23
  - **Single agent:** Pass `PLAN: {index-path}`. For split plans, agent reads task files one at a time via `file:` field in state.yaml.
23
24
  - **Agent Teams:** For split plans, pass `TASK_FILE: {task-file-path}` in each agent's spawn prompt so they load only their assigned task file(s). For legacy plans, pass `PLAN: {plan-path}` as before.
24
- 7. Collect and execute deferred ops (files, commits)
25
- 8. Run verification (tests, lint, typecheck)
26
- 9. Cleanup update state
27
- 10. Initialise review: `review.status` → `"draft"`, `review.revision` → 1, `review.scope` → `"implementation"`
25
+ 8. Collect and execute deferred ops (files, commits)
26
+ 9. Run verification (tests, lint, typecheck)
27
+ 10. **Update state via CLI** — do NOT manually edit state.yaml. Run `npx jdi state executing` before execution and `npx jdi state complete` after. Use `npx jdi state advance-task {task-id}` after each task completes.
28
28
  11. **Present summary** (tasks completed, files changed, verification results, deviations) then ask: _"Provide feedback to adjust, or say **approved** to finalise."_
29
- 12. **Review loop**: approval → update state to `"complete"`, suggest commit/PR. Feedback → apply code changes, run tests, increment revision, re-present. Repeat until approved. Natural conversation — no separate command needed.
29
+ 12. **Review loop**: Feedback → apply code changes, run tests, increment revision, re-present. Approval suggest commit/PR. Natural conversation — no separate command needed.
30
30
 
31
31
  Agent base (read FIRST for cache): .jdi/framework/components/meta/AgentBase.md | Agent specs: .jdi/framework/agents/jdi-backend.md, .jdi/framework/agents/jdi-frontend.md
32
32
  Orchestration: .jdi/framework/components/meta/AgentTeamsOrchestration.md | Routing: .jdi/framework/components/meta/ComplexityRouter.md
@@ -5,11 +5,29 @@ description: "JDI: Review pull request"
5
5
 
6
6
  # /jdi:pr-review
7
7
 
8
+ Review a pull request with learnings-aware analysis.
9
+
10
+ ## Flags
11
+
12
+ - `--no-comments` — Do not post comments to GitHub. Write review to `.jdi/reviews/PR-{number}-review.md` instead.
13
+
8
14
  ## Delegation
9
15
 
16
+ Parse flags from $ARGUMENTS. Map `--no-comments` to `post="false"`.
17
+
10
18
  Use Task tool with subagent_type="general-purpose" and prompt:
11
19
 
12
20
  Read ./.jdi/framework/components/meta/AgentBase.md for the base protocol.
21
+
22
+ Read learnings before reviewing — these represent the team's coding standards and MUST be cross-referenced during review:
23
+ - Always read: `.jdi/framework/learnings/general.md`
24
+ - For PHP/Laravel PRs: also read `.jdi/framework/learnings/backend.md`
25
+ - For React/TypeScript PRs: also read `.jdi/framework/learnings/frontend.md`
26
+ - For test changes: also read `.jdi/framework/learnings/testing.md`
27
+ - For CI/Docker changes: also read `.jdi/framework/learnings/devops.md`
28
+ Apply learnings as additional review criteria — flag violations and praise adherence.
29
+
13
30
  Read ./.jdi/framework/components/quality/PRReview.md for review instructions.
31
+ {If --no-comments flag was present: Include `post="false"` parameter — invoke as `<JDI:PRReview post="false" />`}
14
32
 
15
33
  Review PR: $ARGUMENTS
@@ -6,7 +6,13 @@ description: Record decisions, deviations, and blockers in state.yaml
6
6
 
7
7
  # StateUpdate
8
8
 
9
- > **Status transitions are handled by the TypeScript framework.** Do NOT update `position`, `progress`, `current_plan`, `review`, or `session` fields in state.yaml the CLI manages these automatically.
9
+ > **Status transitions are handled via CLI commands.** Do NOT manually edit `position`, `progress`, `current_plan`, `review`, or `session` fields in state.yaml. Use these commands instead:
10
+ >
11
+ > - `npx jdi state plan-ready --plan-path "{path}" --plan-name "{name}"`
12
+ > - `npx jdi state approved`
13
+ > - `npx jdi state executing`
14
+ > - `npx jdi state complete`
15
+ > - `npx jdi state advance-task {task-id}`
10
16
 
11
17
  Use state.yaml ONLY to record decisions, deviations, or blockers:
12
18
 
@@ -74,9 +74,20 @@ If context provided:
74
74
 
75
75
  Read each changed file in its entirety (not just the diff). **NEVER** use limit/offset.
76
76
 
77
+ ### Step 5b: Cross-Reference Learnings (MANDATORY)
78
+
79
+ If learnings files were loaded (via the command stub or agent prompt), cross-reference every changed file against the team's learnings:
80
+
81
+ 1. For each finding from the review checklist, check if a learning exists that addresses it — cite the learning in your comment.
82
+ 2. Flag any code that **violates** a documented learning (e.g. a learning says "always use path aliases" but the PR uses relative imports).
83
+ 3. **Praise** code that follows learnings the team has documented — this reinforces good patterns.
84
+ 4. If no learnings were loaded, skip this step (but note it in your review summary as a gap).
85
+
86
+ Learnings-based findings should use the same severity classification as other findings. A violation of a documented team convention is at minimum a **minor** finding.
87
+
77
88
  ### Step 6: Perform Code Review
78
89
 
79
- Apply <JDI:PRReview:Checklist /> to analyse each change.
90
+ Apply <JDI:PRReview:Checklist /> to analyse each change. Include learnings violations alongside standard checklist findings.
80
91
 
81
92
  ### Step 7: Categorise Findings (Internal)
82
93
 
@@ -30,13 +30,31 @@ When you learn something new from a review or feedback, update the appropriate c
30
30
 
31
31
  ## Scope Discipline
32
32
 
33
- Only do what was explicitly requested. Do not add extras, tooling, or features the user did not ask for.
33
+ Do not add unrelated extras, tooling, or features the user did not ask for. But DO investigate the full scope of what was requested — including implicit requirements that are clearly part of the ask (e.g. if a UI view needs columns, verify the backend provides them).
34
34
  If something is ambiguous, ask — do not guess.
35
35
  NEVER use time estimates (minutes, hours, etc). Use S/M/L t-shirt sizing for all task and plan sizing.
36
36
  Follow response templates exactly as instructed in the prompt — do not improvise the layout or structure.
37
37
 
38
- ## Approval Gate
38
+ ## Approval Gate — HARD STOP
39
39
 
40
- Planning and implementation are separate gates NEVER auto-proceed to implementation after planning or plan refinement.
41
- When the user provides refinement feedback on a plan, ONLY update the plan files in `.jdi/plans/`. Do NOT implement code.
42
- Implementation only happens when the user explicitly approves ("approved", "lgtm", "looks good", "ship it") or explicitly requests implementation ("implement", "build", "execute").
40
+ Planning and implementation are **separate human-gated phases**. NEVER auto-proceed to implementation after planning or plan refinement.
41
+
42
+ - When the user says "approved" / "lgtm" / "looks good" to a **plan**: this means the plan is finalised. It does NOT mean "go implement it." Finalise the plan review, output _"Plan approved. Run `/jdi:implement-plan` when ready."_, then **STOP**.
43
+ - When the user provides refinement feedback on a plan, ONLY update the plan files in `.jdi/plans/`. Do NOT implement code.
44
+ - Implementation ONLY happens when the user explicitly requests it: "implement", "build", "execute", or `/jdi:implement-plan`.
45
+
46
+ ## State Management
47
+
48
+ Do NOT manually edit `.jdi/config/state.yaml` for status transitions. Use the CLI instead:
49
+
50
+ - `npx jdi state plan-ready --plan-path "{path}" --plan-name "{name}"` — after plan creation
51
+ - `npx jdi state approved` — after plan approval
52
+ - `npx jdi state executing` — before implementation starts
53
+ - `npx jdi state complete` — after implementation finishes
54
+ - `npx jdi state advance-task {task-id}` — after each task completes
55
+
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.32",
3
+ "version": "0.1.34",
4
4
  "description": "JDI - Context-efficient AI development framework for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {