@benzotti/jedi 0.1.11 → 0.1.13

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
@@ -9218,6 +9218,7 @@ async function runMain(cmd, opts = {}) {
9218
9218
 
9219
9219
  // src/commands/init.ts
9220
9220
  import { join as join3 } from "path";
9221
+ import { existsSync as existsSync3 } from "fs";
9221
9222
 
9222
9223
  // src/utils/detect-project.ts
9223
9224
  import { existsSync } from "fs";
@@ -9249,7 +9250,7 @@ async function copyFrameworkFiles(cwd, projectType, force, ci = false) {
9249
9250
  const frameworkDest = join2(cwd, ".jdi", "framework");
9250
9251
  const glob = new Bun.Glob("**/*");
9251
9252
  for await (const file of glob.scan({ cwd: frameworkDir })) {
9252
- if (file.startsWith("adapters/") || file.startsWith("commands/"))
9253
+ if (file.startsWith("adapters/"))
9253
9254
  continue;
9254
9255
  const src2 = join2(frameworkDir, file);
9255
9256
  const dest = join2(frameworkDest, file);
@@ -9365,6 +9366,8 @@ Recognise natural language JDI intents and invoke the matching skill via the Ski
9365
9366
 
9366
9367
  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.
9367
9368
 
9369
+ Planning and implementation are separate gates \u2014 NEVER auto-proceed to implementation after plan approval.
9370
+
9368
9371
  ## Iterative Refinement
9369
9372
 
9370
9373
  After \`/jdi:create-plan\` or \`/jdi:implement-plan\` completes, the conversation continues naturally \u2014 no new command invocation needed. When the user provides feedback (e.g. "change task 2", "move this to a helper", "add error handling"), apply the changes directly, update state, and present the updated summary. When the user approves (e.g. "approved", "looks good", "lgtm"), finalise the review state. The conversation IS the feedback loop.
@@ -9387,6 +9390,8 @@ Recognise natural language JDI intents and invoke the matching skill via the Ski
9387
9390
 
9388
9391
  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.
9389
9392
 
9393
+ Planning and implementation are separate gates \u2014 NEVER auto-proceed to implementation after plan approval.
9394
+
9390
9395
  ## Iterative Refinement
9391
9396
 
9392
9397
  After \`/jdi:create-plan\` or \`/jdi:implement-plan\` completes, the conversation continues naturally \u2014 no new command invocation needed. When the user provides feedback (e.g. "change task 2", "move this to a helper", "add error handling"), apply the changes directly, update state, and present the updated summary. When the user approves (e.g. "approved", "looks good", "lgtm"), finalise the review state. The conversation IS the feedback loop.
@@ -9436,12 +9441,31 @@ var initCommand = defineCommand({
9436
9441
  ".jdi/codebase",
9437
9442
  ".jdi/reviews",
9438
9443
  ".jdi/config",
9439
- ".jdi/persistence"
9444
+ ".jdi/persistence",
9445
+ ".jdi/feedback"
9440
9446
  ];
9441
9447
  for (const dir of dirs) {
9442
9448
  await Bun.write(join3(cwd, dir, ".gitkeep"), "");
9443
9449
  }
9444
9450
  await copyFrameworkFiles(cwd, projectType, args.force, args.ci);
9451
+ const configFiles = ["state.yaml", "variables.yaml", "jdi-config.yaml"];
9452
+ for (const file of configFiles) {
9453
+ const src2 = join3(cwd, ".jdi", "framework", "config", file);
9454
+ const dest = join3(cwd, ".jdi", "config", file);
9455
+ if (existsSync3(src2) && (args.force || !existsSync3(dest))) {
9456
+ const content = await Bun.file(src2).text();
9457
+ await Bun.write(dest, content);
9458
+ }
9459
+ }
9460
+ const scaffoldFiles = ["PROJECT.yaml", "REQUIREMENTS.yaml", "ROADMAP.yaml"];
9461
+ for (const file of scaffoldFiles) {
9462
+ const src2 = join3(cwd, ".jdi", "framework", "templates", file);
9463
+ const dest = join3(cwd, ".jdi", file);
9464
+ if (existsSync3(src2) && !existsSync3(dest)) {
9465
+ const content = await Bun.file(src2).text();
9466
+ await Bun.write(dest, content);
9467
+ }
9468
+ }
9445
9469
  if (args.storage || args["storage-path"]) {
9446
9470
  const { parse, stringify } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
9447
9471
  const configPath = join3(cwd, ".jdi", "config", "jdi-config.yaml");
@@ -9483,10 +9507,10 @@ import { resolve as resolve2 } from "path";
9483
9507
  // src/utils/adapter.ts
9484
9508
  var import_yaml = __toESM(require_dist(), 1);
9485
9509
  import { join as join4 } from "path";
9486
- import { existsSync as existsSync3 } from "fs";
9510
+ import { existsSync as existsSync4 } from "fs";
9487
9511
  async function readAdapter(cwd) {
9488
9512
  const adapterPath = join4(cwd, ".jdi", "config", "adapter.yaml");
9489
- if (!existsSync3(adapterPath))
9513
+ if (!existsSync4(adapterPath))
9490
9514
  return null;
9491
9515
  const content = await Bun.file(adapterPath).text();
9492
9516
  return import_yaml.parse(content);
@@ -9619,11 +9643,11 @@ async function spawnClaude(prompt2, opts) {
9619
9643
  // src/storage/index.ts
9620
9644
  var import_yaml2 = __toESM(require_dist(), 1);
9621
9645
  import { join as join6 } from "path";
9622
- import { existsSync as existsSync5 } from "fs";
9646
+ import { existsSync as existsSync6 } from "fs";
9623
9647
 
9624
9648
  // src/storage/fs-storage.ts
9625
9649
  import { join as join5 } from "path";
9626
- import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
9650
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
9627
9651
 
9628
9652
  class FsStorage {
9629
9653
  basePath;
@@ -9632,12 +9656,12 @@ class FsStorage {
9632
9656
  }
9633
9657
  async load(key) {
9634
9658
  const filePath = join5(this.basePath, `${key}.md`);
9635
- if (!existsSync4(filePath))
9659
+ if (!existsSync5(filePath))
9636
9660
  return null;
9637
9661
  return Bun.file(filePath).text();
9638
9662
  }
9639
9663
  async save(key, content) {
9640
- if (!existsSync4(this.basePath)) {
9664
+ if (!existsSync5(this.basePath)) {
9641
9665
  mkdirSync2(this.basePath, { recursive: true });
9642
9666
  }
9643
9667
  const filePath = join5(this.basePath, `${key}.md`);
@@ -9651,7 +9675,7 @@ async function createStorage(cwd, config) {
9651
9675
  let basePath = config?.basePath;
9652
9676
  if (!config?.adapter && !config?.basePath) {
9653
9677
  const configPath = join6(cwd, ".jdi", "config", "jdi-config.yaml");
9654
- if (existsSync5(configPath)) {
9678
+ if (existsSync6(configPath)) {
9655
9679
  const content = await Bun.file(configPath).text();
9656
9680
  const parsed = import_yaml2.parse(content);
9657
9681
  if (parsed?.storage?.adapter)
@@ -9665,7 +9689,7 @@ async function createStorage(cwd, config) {
9665
9689
  return new FsStorage(resolvedPath);
9666
9690
  }
9667
9691
  const adapterPath = join6(cwd, adapter);
9668
- if (!existsSync5(adapterPath)) {
9692
+ if (!existsSync6(adapterPath)) {
9669
9693
  throw new Error(`Storage adapter not found: ${adapterPath}
9670
9694
  ` + `Set storage.adapter in .jdi/config/jdi-config.yaml to "fs" or a path to a custom adapter module.`);
9671
9695
  }
@@ -9689,14 +9713,14 @@ async function createStorage(cwd, config) {
9689
9713
 
9690
9714
  // src/utils/storage-lifecycle.ts
9691
9715
  import { join as join7 } from "path";
9692
- import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
9716
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
9693
9717
  async function loadPersistedState(cwd, storage) {
9694
9718
  let learningsPath = null;
9695
9719
  let codebaseIndexPath = null;
9696
9720
  const learnings = await storage.load("learnings");
9697
9721
  if (learnings) {
9698
9722
  const dir = join7(cwd, ".jdi", "framework", "learnings");
9699
- if (!existsSync6(dir))
9723
+ if (!existsSync7(dir))
9700
9724
  mkdirSync3(dir, { recursive: true });
9701
9725
  learningsPath = join7(dir, "_consolidated.md");
9702
9726
  await Bun.write(learningsPath, learnings);
@@ -9704,7 +9728,7 @@ async function loadPersistedState(cwd, storage) {
9704
9728
  const codebaseIndex = await storage.load("codebase-index");
9705
9729
  if (codebaseIndex) {
9706
9730
  const dir = join7(cwd, ".jdi", "codebase");
9707
- if (!existsSync6(dir))
9731
+ if (!existsSync7(dir))
9708
9732
  mkdirSync3(dir, { recursive: true });
9709
9733
  codebaseIndexPath = join7(dir, "INDEX.md");
9710
9734
  await Bun.write(codebaseIndexPath, codebaseIndex);
@@ -9715,7 +9739,7 @@ async function savePersistedState(cwd, storage) {
9715
9739
  let learningsSaved = false;
9716
9740
  let codebaseIndexSaved = false;
9717
9741
  const learningsDir = join7(cwd, ".jdi", "framework", "learnings");
9718
- if (existsSync6(learningsDir)) {
9742
+ if (existsSync7(learningsDir)) {
9719
9743
  const merged = await mergeLearningFiles(learningsDir);
9720
9744
  if (merged) {
9721
9745
  await storage.save("learnings", merged);
@@ -9723,7 +9747,7 @@ async function savePersistedState(cwd, storage) {
9723
9747
  }
9724
9748
  }
9725
9749
  const indexPath = join7(cwd, ".jdi", "codebase", "INDEX.md");
9726
- if (existsSync6(indexPath)) {
9750
+ if (existsSync7(indexPath)) {
9727
9751
  const content = await Bun.file(indexPath).text();
9728
9752
  if (content.trim()) {
9729
9753
  await storage.save("codebase-index", content);
@@ -9737,7 +9761,7 @@ async function mergeLearningFiles(dir) {
9737
9761
  const sections = [];
9738
9762
  for (const category of categories) {
9739
9763
  const filePath = join7(dir, `${category}.md`);
9740
- if (!existsSync6(filePath))
9764
+ if (!existsSync7(filePath))
9741
9765
  continue;
9742
9766
  const content = await Bun.file(filePath).text();
9743
9767
  const trimmed = content.trim();
@@ -9748,7 +9772,7 @@ async function mergeLearningFiles(dir) {
9748
9772
  sections.push(trimmed);
9749
9773
  }
9750
9774
  const consolidatedPath = join7(dir, "_consolidated.md");
9751
- if (existsSync6(consolidatedPath)) {
9775
+ if (existsSync7(consolidatedPath)) {
9752
9776
  const content = await Bun.file(consolidatedPath).text();
9753
9777
  const trimmed = content.trim();
9754
9778
  if (trimmed && !sections.some((s2) => s2.includes(trimmed))) {
@@ -9832,10 +9856,10 @@ import { resolve as resolve3 } from "path";
9832
9856
  // src/utils/state.ts
9833
9857
  var import_yaml3 = __toESM(require_dist(), 1);
9834
9858
  import { join as join8 } from "path";
9835
- import { existsSync as existsSync7 } from "fs";
9859
+ import { existsSync as existsSync8 } from "fs";
9836
9860
  async function readState(cwd) {
9837
9861
  const statePath = join8(cwd, ".jdi", "config", "state.yaml");
9838
- if (!existsSync7(statePath))
9862
+ if (!existsSync8(statePath))
9839
9863
  return null;
9840
9864
  const content = await Bun.file(statePath).text();
9841
9865
  return import_yaml3.parse(content);
@@ -10152,7 +10176,7 @@ Use --all to stage and commit all, or stage files manually.`);
10152
10176
  });
10153
10177
 
10154
10178
  // src/commands/pr.ts
10155
- import { existsSync as existsSync8 } from "fs";
10179
+ import { existsSync as existsSync9 } from "fs";
10156
10180
  import { join as join10 } from "path";
10157
10181
  async function hasGhCli() {
10158
10182
  const { exitCode } = await exec(["which", "gh"]);
@@ -10204,7 +10228,7 @@ var prCommand = defineCommand({
10204
10228
  **Plan:** ${state.position.plan_name}` : "";
10205
10229
  let template = "";
10206
10230
  const templatePath = join10(cwd, ".github", "pull_request_template.md");
10207
- if (existsSync8(templatePath)) {
10231
+ if (existsSync9(templatePath)) {
10208
10232
  template = await Bun.file(templatePath).text();
10209
10233
  }
10210
10234
  const title = branch.replace(/^(feat|fix|chore|docs|refactor|test|ci)\//, "").replace(/[-_]/g, " ").replace(/^\w/, (c3) => c3.toUpperCase());
@@ -10552,7 +10576,7 @@ var quickCommand = defineCommand({
10552
10576
  });
10553
10577
 
10554
10578
  // src/commands/worktree.ts
10555
- import { existsSync as existsSync9 } from "fs";
10579
+ import { existsSync as existsSync10 } from "fs";
10556
10580
  function slugify(name) {
10557
10581
  return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
10558
10582
  }
@@ -10586,7 +10610,7 @@ var worktreeCommand = defineCommand({
10586
10610
  }
10587
10611
  const slug = slugify(args.name);
10588
10612
  const worktreePath = `${root}/.worktrees/${slug}`;
10589
- if (existsSync9(worktreePath)) {
10613
+ if (existsSync10(worktreePath)) {
10590
10614
  consola.error(`Worktree already exists at ${worktreePath}`);
10591
10615
  return;
10592
10616
  }
@@ -10752,7 +10776,7 @@ Specify a worktree name: jdi worktree-remove <name>`);
10752
10776
 
10753
10777
  // src/commands/plan-review.ts
10754
10778
  import { resolve as resolve7 } from "path";
10755
- import { existsSync as existsSync10 } from "fs";
10779
+ import { existsSync as existsSync11 } from "fs";
10756
10780
  function parsePlanSummary(content) {
10757
10781
  const nameMatch = content.match(/^# .+?: (.+)$/m);
10758
10782
  const name = nameMatch?.[1] ?? "Unknown";
@@ -10791,7 +10815,7 @@ var planReviewCommand = defineCommand({
10791
10815
  consola.error("No plan found. Run `jdi plan` first.");
10792
10816
  return;
10793
10817
  }
10794
- if (!existsSync10(planPath)) {
10818
+ if (!existsSync11(planPath)) {
10795
10819
  consola.error(`Plan not found: ${planPath}`);
10796
10820
  return;
10797
10821
  }
@@ -10881,7 +10905,7 @@ Tasks (${tasks.length}):`);
10881
10905
 
10882
10906
  // src/commands/plan-approve.ts
10883
10907
  import { resolve as resolve8 } from "path";
10884
- import { existsSync as existsSync11 } from "fs";
10908
+ import { existsSync as existsSync12 } from "fs";
10885
10909
  var planApproveCommand = defineCommand({
10886
10910
  meta: {
10887
10911
  name: "plan-approve",
@@ -10910,7 +10934,7 @@ var planApproveCommand = defineCommand({
10910
10934
  consola.error("No plan to approve. Run `jdi plan` first.");
10911
10935
  return;
10912
10936
  }
10913
- if (!existsSync11(planPath)) {
10937
+ if (!existsSync12(planPath)) {
10914
10938
  consola.error(`Plan not found: ${planPath}`);
10915
10939
  return;
10916
10940
  }
@@ -11436,7 +11460,7 @@ Use the ClickUp ticket above as the primary requirements source.` : ``,
11436
11460
 
11437
11461
  // src/commands/setup-action.ts
11438
11462
  import { join as join12, dirname as dirname3 } from "path";
11439
- import { existsSync as existsSync12, mkdirSync as mkdirSync4 } from "fs";
11463
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4 } from "fs";
11440
11464
  var setupActionCommand = defineCommand({
11441
11465
  meta: {
11442
11466
  name: "setup-action",
@@ -11446,17 +11470,17 @@ var setupActionCommand = defineCommand({
11446
11470
  async run() {
11447
11471
  const cwd = process.cwd();
11448
11472
  const workflowDest = join12(cwd, ".github", "workflows", "jedi.yml");
11449
- if (existsSync12(workflowDest)) {
11473
+ if (existsSync13(workflowDest)) {
11450
11474
  consola.warn(`Workflow already exists at ${workflowDest}`);
11451
11475
  consola.info("Skipping workflow copy. Delete it manually to regenerate.");
11452
11476
  } else {
11453
11477
  const templatePath = join12(import.meta.dir, "../action/workflow-template.yml");
11454
- if (!existsSync12(templatePath)) {
11478
+ if (!existsSync13(templatePath)) {
11455
11479
  consola.error("Workflow template not found. Ensure @benzotti/jedi is properly installed.");
11456
11480
  process.exit(1);
11457
11481
  }
11458
11482
  const dir = dirname3(workflowDest);
11459
- if (!existsSync12(dir))
11483
+ if (!existsSync13(dir))
11460
11484
  mkdirSync4(dir, { recursive: true });
11461
11485
  const template = await Bun.file(templatePath).text();
11462
11486
  await Bun.write(workflowDest, template);
@@ -11494,7 +11518,7 @@ var setupActionCommand = defineCommand({
11494
11518
  // package.json
11495
11519
  var package_default = {
11496
11520
  name: "@benzotti/jedi",
11497
- version: "0.1.11",
11521
+ version: "0.1.13",
11498
11522
  description: "JDI - Context-efficient AI development framework for Claude Code",
11499
11523
  type: "module",
11500
11524
  bin: {
@@ -27,6 +27,6 @@ Create an implementation plan using a single planner agent (includes research).
27
27
  9. **Present summary** (name, objective, task table, files) then ask: _"Provide feedback to refine, or say **approved** to finalise."_
28
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.
29
29
 
30
- Agent base (read FIRST for cache): ./components/meta/AgentBase.md | Agent spec: ./agents/jdi-planner.md
30
+ Agent base (read FIRST for cache): .jdi/framework/components/meta/AgentBase.md | Agent spec: .jdi/framework/agents/jdi-planner.md
31
31
 
32
32
  Feature to plan: $ARGUMENTS
@@ -26,8 +26,8 @@ Execute a PLAN.md with complexity-based routing.
26
26
  11. **Present summary** (tasks completed, files changed, verification results, deviations) then ask: _"Provide feedback to adjust, or say **approved** to finalise."_
27
27
  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.
28
28
 
29
- Agent base (read FIRST for cache): ./components/meta/AgentBase.md | Agent specs: ./agents/jdi-backend.md, ./agents/jdi-frontend.md
30
- Orchestration: ./components/meta/AgentTeamsOrchestration.md | Routing: ./components/meta/ComplexityRouter.md
29
+ 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
30
+ Orchestration: .jdi/framework/components/meta/AgentTeamsOrchestration.md | Routing: .jdi/framework/components/meta/ComplexityRouter.md
31
31
 
32
32
  When spawning agents, detect project type and include a `## Project Context` block (type, tech stack, quality gates, working directory) in the spawn prompt. This saves agents 2-3 discovery tool calls.
33
33
 
@@ -13,7 +13,7 @@ Initialise the JDI slash commands in the current project.
13
13
 
14
14
  ```bash
15
15
  mkdir -p .claude/commands/jdi
16
- mkdir -p .jdi/plans .jdi/research .jdi/config
16
+ mkdir -p .jdi/plans .jdi/research .jdi/codebase .jdi/reviews .jdi/config .jdi/persistence .jdi/feedback
17
17
  ```
18
18
 
19
19
  ### Step 2: Copy Command Stubs
@@ -27,6 +27,6 @@ Remove a git worktree and clean up all associated resources.
27
27
  7. **Update state**: set `worktree.active: false`, clear `worktree.path`, `worktree.branch` in `.jdi/config/state.yaml`
28
28
  8. **Report**: what was removed
29
29
 
30
- Reference: ./hooks/jdi-worktree-cleanup.md
30
+ Reference: .jdi/framework/hooks/jdi-worktree-cleanup.md
31
31
 
32
32
  Worktree to remove: $ARGUMENTS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benzotti/jedi",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "JDI - Context-efficient AI development framework for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {