@benzotti/jedi 0.1.3 → 0.1.4

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
@@ -1,6 +1,35 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
+ var __create = Object.create;
4
+ var __getProtoOf = Object.getPrototypeOf;
3
5
  var __defProp = Object.defineProperty;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
13
+ var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
21
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
22
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
+ for (let key of __getOwnPropNames(mod))
24
+ if (!__hasOwnProp.call(to, key))
25
+ __defProp(to, key, {
26
+ get: __accessProp.bind(mod, key),
27
+ enumerable: true
28
+ });
29
+ if (canCache)
30
+ cache.set(mod, to);
31
+ return to;
32
+ };
4
33
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
5
34
  var __returnValue = (v) => v;
6
35
  function __exportSetter(name, newValue) {
@@ -7676,6 +7705,55 @@ var require_public_api = __commonJS((exports) => {
7676
7705
  exports.stringify = stringify;
7677
7706
  });
7678
7707
 
7708
+ // node_modules/yaml/dist/index.js
7709
+ var require_dist = __commonJS((exports) => {
7710
+ var composer = require_composer();
7711
+ var Document = require_Document();
7712
+ var Schema = require_Schema();
7713
+ var errors = require_errors();
7714
+ var Alias = require_Alias();
7715
+ var identity = require_identity();
7716
+ var Pair = require_Pair();
7717
+ var Scalar = require_Scalar();
7718
+ var YAMLMap = require_YAMLMap();
7719
+ var YAMLSeq = require_YAMLSeq();
7720
+ var cst = require_cst();
7721
+ var lexer = require_lexer();
7722
+ var lineCounter = require_line_counter();
7723
+ var parser = require_parser();
7724
+ var publicApi = require_public_api();
7725
+ var visit = require_visit();
7726
+ exports.Composer = composer.Composer;
7727
+ exports.Document = Document.Document;
7728
+ exports.Schema = Schema.Schema;
7729
+ exports.YAMLError = errors.YAMLError;
7730
+ exports.YAMLParseError = errors.YAMLParseError;
7731
+ exports.YAMLWarning = errors.YAMLWarning;
7732
+ exports.Alias = Alias.Alias;
7733
+ exports.isAlias = identity.isAlias;
7734
+ exports.isCollection = identity.isCollection;
7735
+ exports.isDocument = identity.isDocument;
7736
+ exports.isMap = identity.isMap;
7737
+ exports.isNode = identity.isNode;
7738
+ exports.isPair = identity.isPair;
7739
+ exports.isScalar = identity.isScalar;
7740
+ exports.isSeq = identity.isSeq;
7741
+ exports.Pair = Pair.Pair;
7742
+ exports.Scalar = Scalar.Scalar;
7743
+ exports.YAMLMap = YAMLMap.YAMLMap;
7744
+ exports.YAMLSeq = YAMLSeq.YAMLSeq;
7745
+ exports.CST = cst;
7746
+ exports.Lexer = lexer.Lexer;
7747
+ exports.LineCounter = lineCounter.LineCounter;
7748
+ exports.Parser = parser.Parser;
7749
+ exports.parse = publicApi.parse;
7750
+ exports.parseAllDocuments = publicApi.parseAllDocuments;
7751
+ exports.parseDocument = publicApi.parseDocument;
7752
+ exports.stringify = publicApi.stringify;
7753
+ exports.visit = visit.visit;
7754
+ exports.visitAsync = visit.visitAsync;
7755
+ });
7756
+
7679
7757
  // src/utils/git.ts
7680
7758
  var exports_git = {};
7681
7759
  __export(exports_git, {
@@ -9266,30 +9344,73 @@ var initCommand = defineCommand({
9266
9344
  type: "boolean",
9267
9345
  description: "Overwrite existing files",
9268
9346
  default: false
9347
+ },
9348
+ ci: {
9349
+ type: "boolean",
9350
+ description: "Headless CI mode (no prompts, JSON output)",
9351
+ default: false
9352
+ },
9353
+ storage: {
9354
+ type: "string",
9355
+ description: "Storage adapter to configure (default: fs)"
9356
+ },
9357
+ "storage-path": {
9358
+ type: "string",
9359
+ description: "Storage base path (default: .jdi/persistence/)"
9269
9360
  }
9270
9361
  },
9271
9362
  async run({ args }) {
9272
9363
  const cwd = process.cwd();
9273
9364
  const projectType = await detectProjectType(cwd);
9274
- consola.info(`Detected project type: ${projectType}`);
9275
- consola.start("Initialising JDI...");
9365
+ if (!args.ci) {
9366
+ consola.info(`Detected project type: ${projectType}`);
9367
+ consola.start("Initialising JDI...");
9368
+ }
9276
9369
  const dirs = [
9277
9370
  ".claude/commands/jdi",
9278
9371
  ".jdi/plans",
9279
9372
  ".jdi/research",
9280
9373
  ".jdi/codebase",
9281
9374
  ".jdi/reviews",
9282
- ".jdi/config"
9375
+ ".jdi/config",
9376
+ ".jdi/persistence"
9283
9377
  ];
9284
9378
  for (const dir of dirs) {
9285
9379
  await Bun.write(join3(cwd, dir, ".gitkeep"), "");
9286
9380
  }
9287
9381
  await copyFrameworkFiles(cwd, projectType, args.force);
9288
- consola.success("JDI initialised successfully!");
9289
- consola.info("");
9290
- consola.info("Get started:");
9291
- consola.info(' /jdi:create-plan "your feature"');
9292
- consola.info(' /jdi:quick "small fix"');
9382
+ if (args.storage || args["storage-path"]) {
9383
+ const { parse, stringify } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
9384
+ const configPath = join3(cwd, ".jdi", "config", "jdi-config.yaml");
9385
+ let config = {};
9386
+ try {
9387
+ const existing = await Bun.file(configPath).text();
9388
+ config = parse(existing) ?? {};
9389
+ } catch {}
9390
+ config.storage = {
9391
+ adapter: args.storage ?? "fs",
9392
+ base_path: args["storage-path"] ?? ".jdi/persistence/"
9393
+ };
9394
+ await Bun.write(configPath, stringify(config));
9395
+ }
9396
+ if (args.ci) {
9397
+ const result = {
9398
+ status: "initialised",
9399
+ project_type: projectType,
9400
+ cwd,
9401
+ storage: {
9402
+ adapter: args.storage ?? "fs",
9403
+ base_path: args["storage-path"] ?? ".jdi/persistence/"
9404
+ }
9405
+ };
9406
+ console.log(JSON.stringify(result));
9407
+ } else {
9408
+ consola.success("JDI initialised successfully!");
9409
+ consola.info("");
9410
+ consola.info("Get started:");
9411
+ consola.info(' /jdi:create-plan "your feature"');
9412
+ consola.info(' /jdi:quick "small fix"');
9413
+ }
9293
9414
  }
9294
9415
  });
9295
9416
 
@@ -9297,62 +9418,15 @@ var initCommand = defineCommand({
9297
9418
  import { resolve as resolve2 } from "path";
9298
9419
 
9299
9420
  // src/utils/adapter.ts
9421
+ var import_yaml = __toESM(require_dist(), 1);
9300
9422
  import { join as join4 } from "path";
9301
9423
  import { existsSync as existsSync3 } from "fs";
9302
-
9303
- // node_modules/yaml/dist/index.js
9304
- var composer = require_composer();
9305
- var Document = require_Document();
9306
- var Schema = require_Schema();
9307
- var errors = require_errors();
9308
- var Alias = require_Alias();
9309
- var identity = require_identity();
9310
- var Pair = require_Pair();
9311
- var Scalar = require_Scalar();
9312
- var YAMLMap = require_YAMLMap();
9313
- var YAMLSeq = require_YAMLSeq();
9314
- var cst = require_cst();
9315
- var lexer = require_lexer();
9316
- var lineCounter = require_line_counter();
9317
- var parser = require_parser();
9318
- var publicApi = require_public_api();
9319
- var visit = require_visit();
9320
- var $Composer = composer.Composer;
9321
- var $Document = Document.Document;
9322
- var $Schema = Schema.Schema;
9323
- var $YAMLError = errors.YAMLError;
9324
- var $YAMLParseError = errors.YAMLParseError;
9325
- var $YAMLWarning = errors.YAMLWarning;
9326
- var $Alias = Alias.Alias;
9327
- var $isAlias = identity.isAlias;
9328
- var $isCollection = identity.isCollection;
9329
- var $isDocument = identity.isDocument;
9330
- var $isMap = identity.isMap;
9331
- var $isNode = identity.isNode;
9332
- var $isPair = identity.isPair;
9333
- var $isScalar = identity.isScalar;
9334
- var $isSeq = identity.isSeq;
9335
- var $Pair = Pair.Pair;
9336
- var $Scalar = Scalar.Scalar;
9337
- var $YAMLMap = YAMLMap.YAMLMap;
9338
- var $YAMLSeq = YAMLSeq.YAMLSeq;
9339
- var $Lexer = lexer.Lexer;
9340
- var $LineCounter = lineCounter.LineCounter;
9341
- var $Parser = parser.Parser;
9342
- var $parse = publicApi.parse;
9343
- var $parseAllDocuments = publicApi.parseAllDocuments;
9344
- var $parseDocument = publicApi.parseDocument;
9345
- var $stringify = publicApi.stringify;
9346
- var $visit = visit.visit;
9347
- var $visitAsync = visit.visitAsync;
9348
-
9349
- // src/utils/adapter.ts
9350
9424
  async function readAdapter(cwd) {
9351
9425
  const adapterPath = join4(cwd, ".jdi", "config", "adapter.yaml");
9352
9426
  if (!existsSync3(adapterPath))
9353
9427
  return null;
9354
9428
  const content = await Bun.file(adapterPath).text();
9355
- return $parse(content);
9429
+ return import_yaml.parse(content);
9356
9430
  }
9357
9431
 
9358
9432
  // src/utils/claude.ts
@@ -9479,6 +9553,152 @@ async function spawnClaude(prompt2, opts) {
9479
9553
  return { exitCode };
9480
9554
  }
9481
9555
 
9556
+ // src/storage/index.ts
9557
+ var import_yaml2 = __toESM(require_dist(), 1);
9558
+ import { join as join6 } from "path";
9559
+ import { existsSync as existsSync5 } from "fs";
9560
+
9561
+ // src/storage/fs-storage.ts
9562
+ import { join as join5 } from "path";
9563
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
9564
+
9565
+ class FsStorage {
9566
+ basePath;
9567
+ constructor(basePath = ".jdi/persistence") {
9568
+ this.basePath = basePath;
9569
+ }
9570
+ async load(key) {
9571
+ const filePath = join5(this.basePath, `${key}.md`);
9572
+ if (!existsSync4(filePath))
9573
+ return null;
9574
+ return Bun.file(filePath).text();
9575
+ }
9576
+ async save(key, content) {
9577
+ if (!existsSync4(this.basePath)) {
9578
+ mkdirSync2(this.basePath, { recursive: true });
9579
+ }
9580
+ const filePath = join5(this.basePath, `${key}.md`);
9581
+ await Bun.write(filePath, content);
9582
+ }
9583
+ }
9584
+
9585
+ // src/storage/index.ts
9586
+ async function createStorage(cwd, config) {
9587
+ let adapter = config?.adapter ?? "fs";
9588
+ let basePath = config?.basePath;
9589
+ if (!config?.adapter && !config?.basePath) {
9590
+ const configPath = join6(cwd, ".jdi", "config", "jdi-config.yaml");
9591
+ if (existsSync5(configPath)) {
9592
+ const content = await Bun.file(configPath).text();
9593
+ const parsed = import_yaml2.parse(content);
9594
+ if (parsed?.storage?.adapter)
9595
+ adapter = parsed.storage.adapter;
9596
+ if (parsed?.storage?.base_path)
9597
+ basePath = parsed.storage.base_path;
9598
+ }
9599
+ }
9600
+ if (adapter === "fs") {
9601
+ const resolvedPath = basePath ? join6(cwd, basePath) : join6(cwd, ".jdi", "persistence");
9602
+ return new FsStorage(resolvedPath);
9603
+ }
9604
+ const adapterPath = join6(cwd, adapter);
9605
+ if (!existsSync5(adapterPath)) {
9606
+ throw new Error(`Storage adapter not found: ${adapterPath}
9607
+ ` + `Set storage.adapter in .jdi/config/jdi-config.yaml to "fs" or a path to a custom adapter module.`);
9608
+ }
9609
+ try {
9610
+ const mod = await import(adapterPath);
9611
+ const AdapterClass = mod.default ?? mod.Storage ?? mod[Object.keys(mod)[0]];
9612
+ if (!AdapterClass || typeof AdapterClass !== "function") {
9613
+ throw new Error(`Storage adapter at ${adapterPath} must export a class as default export.`);
9614
+ }
9615
+ const instance = new AdapterClass({ basePath, cwd });
9616
+ if (typeof instance.load !== "function" || typeof instance.save !== "function") {
9617
+ throw new Error(`Storage adapter at ${adapterPath} must implement JediStorage (load and save methods).`);
9618
+ }
9619
+ return instance;
9620
+ } catch (err) {
9621
+ if (err.message?.includes("Storage adapter"))
9622
+ throw err;
9623
+ throw new Error(`Failed to load storage adapter from ${adapterPath}: ${err.message}`);
9624
+ }
9625
+ }
9626
+
9627
+ // src/utils/storage-lifecycle.ts
9628
+ import { join as join7 } from "path";
9629
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
9630
+ async function loadPersistedState(cwd, storage) {
9631
+ let learningsPath = null;
9632
+ let codebaseIndexPath = null;
9633
+ const learnings = await storage.load("learnings");
9634
+ if (learnings) {
9635
+ const dir = join7(cwd, ".jdi", "framework", "learnings");
9636
+ if (!existsSync6(dir))
9637
+ mkdirSync3(dir, { recursive: true });
9638
+ learningsPath = join7(dir, "_consolidated.md");
9639
+ await Bun.write(learningsPath, learnings);
9640
+ }
9641
+ const codebaseIndex = await storage.load("codebase-index");
9642
+ if (codebaseIndex) {
9643
+ const dir = join7(cwd, ".jdi", "codebase");
9644
+ if (!existsSync6(dir))
9645
+ mkdirSync3(dir, { recursive: true });
9646
+ codebaseIndexPath = join7(dir, "INDEX.md");
9647
+ await Bun.write(codebaseIndexPath, codebaseIndex);
9648
+ }
9649
+ return { learningsPath, codebaseIndexPath };
9650
+ }
9651
+ async function savePersistedState(cwd, storage) {
9652
+ let learningsSaved = false;
9653
+ let codebaseIndexSaved = false;
9654
+ const learningsDir = join7(cwd, ".jdi", "framework", "learnings");
9655
+ if (existsSync6(learningsDir)) {
9656
+ const merged = await mergeLearningFiles(learningsDir);
9657
+ if (merged) {
9658
+ await storage.save("learnings", merged);
9659
+ learningsSaved = true;
9660
+ }
9661
+ }
9662
+ const indexPath = join7(cwd, ".jdi", "codebase", "INDEX.md");
9663
+ if (existsSync6(indexPath)) {
9664
+ const content = await Bun.file(indexPath).text();
9665
+ if (content.trim()) {
9666
+ await storage.save("codebase-index", content);
9667
+ codebaseIndexSaved = true;
9668
+ }
9669
+ }
9670
+ return { learningsSaved, codebaseIndexSaved };
9671
+ }
9672
+ async function mergeLearningFiles(dir) {
9673
+ const categories = ["general", "backend", "frontend", "testing", "devops"];
9674
+ const sections = [];
9675
+ for (const category of categories) {
9676
+ const filePath = join7(dir, `${category}.md`);
9677
+ if (!existsSync6(filePath))
9678
+ continue;
9679
+ const content = await Bun.file(filePath).text();
9680
+ const trimmed = content.trim();
9681
+ const lines = trimmed.split(`
9682
+ `).filter((l2) => l2.trim() && !l2.startsWith("#") && !l2.startsWith("<!--"));
9683
+ if (lines.length === 0)
9684
+ continue;
9685
+ sections.push(trimmed);
9686
+ }
9687
+ const consolidatedPath = join7(dir, "_consolidated.md");
9688
+ if (existsSync6(consolidatedPath)) {
9689
+ const content = await Bun.file(consolidatedPath).text();
9690
+ const trimmed = content.trim();
9691
+ if (trimmed && !sections.some((s2) => s2.includes(trimmed))) {
9692
+ sections.push(trimmed);
9693
+ }
9694
+ }
9695
+ return sections.length > 0 ? sections.join(`
9696
+
9697
+ ---
9698
+
9699
+ `) : null;
9700
+ }
9701
+
9482
9702
  // src/commands/plan.ts
9483
9703
  var planCommand = defineCommand({
9484
9704
  meta: {
@@ -9531,7 +9751,10 @@ var planCommand = defineCommand({
9531
9751
  } else if (args.print) {
9532
9752
  console.log(prompt2);
9533
9753
  } else {
9754
+ const storage = await createStorage(cwd);
9755
+ await loadPersistedState(cwd, storage);
9534
9756
  const { exitCode } = await spawnClaude(prompt2, { cwd });
9757
+ await savePersistedState(cwd, storage);
9535
9758
  if (exitCode !== 0) {
9536
9759
  consola.error(`Claude exited with code ${exitCode}`);
9537
9760
  process.exit(exitCode);
@@ -9544,18 +9767,19 @@ var planCommand = defineCommand({
9544
9767
  import { resolve as resolve3 } from "path";
9545
9768
 
9546
9769
  // src/utils/state.ts
9547
- import { join as join5 } from "path";
9548
- import { existsSync as existsSync4 } from "fs";
9770
+ var import_yaml3 = __toESM(require_dist(), 1);
9771
+ import { join as join8 } from "path";
9772
+ import { existsSync as existsSync7 } from "fs";
9549
9773
  async function readState(cwd) {
9550
- const statePath = join5(cwd, ".jdi", "config", "state.yaml");
9551
- if (!existsSync4(statePath))
9774
+ const statePath = join8(cwd, ".jdi", "config", "state.yaml");
9775
+ if (!existsSync7(statePath))
9552
9776
  return null;
9553
9777
  const content = await Bun.file(statePath).text();
9554
- return $parse(content);
9778
+ return import_yaml3.parse(content);
9555
9779
  }
9556
9780
  async function writeState(cwd, state) {
9557
- const statePath = join5(cwd, ".jdi", "config", "state.yaml");
9558
- await Bun.write(statePath, $stringify(state));
9781
+ const statePath = join8(cwd, ".jdi", "config", "state.yaml");
9782
+ await Bun.write(statePath, import_yaml3.stringify(state));
9559
9783
  }
9560
9784
 
9561
9785
  // src/commands/implement.ts
@@ -9643,7 +9867,10 @@ Override: --single (force single-agent mode)` : "";
9643
9867
  } else if (args.print) {
9644
9868
  console.log(prompt2);
9645
9869
  } else {
9870
+ const storage = await createStorage(cwd);
9871
+ await loadPersistedState(cwd, storage);
9646
9872
  const { exitCode } = await spawnClaude(prompt2, { cwd });
9873
+ await savePersistedState(cwd, storage);
9647
9874
  if (exitCode !== 0) {
9648
9875
  consola.error(`Claude exited with code ${exitCode}`);
9649
9876
  process.exit(exitCode);
@@ -9699,15 +9926,15 @@ var statusCommand = defineCommand({
9699
9926
  import { relative } from "path";
9700
9927
 
9701
9928
  // src/utils/resolve-components.ts
9702
- import { join as join6, basename } from "path";
9929
+ import { join as join9, basename } from "path";
9703
9930
  import { homedir } from "os";
9704
9931
  async function resolveComponents(cwd) {
9705
9932
  const components = [];
9706
9933
  const seen = new Set;
9707
9934
  const sources = [
9708
- { dir: join6(cwd, ".jdi", "framework", "components"), source: "project" },
9709
- { dir: join6(homedir(), ".jdi", "components"), source: "user" },
9710
- { dir: join6(import.meta.dir, "../framework/components"), source: "builtin" }
9935
+ { dir: join9(cwd, ".jdi", "framework", "components"), source: "project" },
9936
+ { dir: join9(homedir(), ".jdi", "components"), source: "user" },
9937
+ { dir: join9(import.meta.dir, "../framework/components"), source: "builtin" }
9711
9938
  ];
9712
9939
  for (const { dir, source } of sources) {
9713
9940
  try {
@@ -9716,7 +9943,7 @@ async function resolveComponents(cwd) {
9716
9943
  const name = basename(file, ".md");
9717
9944
  if (!seen.has(name)) {
9718
9945
  seen.add(name);
9719
- components.push({ name, path: join6(dir, file), source });
9946
+ components.push({ name, path: join9(dir, file), source });
9720
9947
  }
9721
9948
  }
9722
9949
  } catch {}
@@ -9862,8 +10089,8 @@ Use --all to stage and commit all, or stage files manually.`);
9862
10089
  });
9863
10090
 
9864
10091
  // src/commands/pr.ts
9865
- import { existsSync as existsSync5 } from "fs";
9866
- import { join as join7 } from "path";
10092
+ import { existsSync as existsSync8 } from "fs";
10093
+ import { join as join10 } from "path";
9867
10094
  async function hasGhCli() {
9868
10095
  const { exitCode } = await exec(["which", "gh"]);
9869
10096
  return exitCode === 0;
@@ -9913,8 +10140,8 @@ var prCommand = defineCommand({
9913
10140
 
9914
10141
  **Plan:** ${state.position.plan_name}` : "";
9915
10142
  let template = "";
9916
- const templatePath = join7(cwd, ".github", "pull_request_template.md");
9917
- if (existsSync5(templatePath)) {
10143
+ const templatePath = join10(cwd, ".github", "pull_request_template.md");
10144
+ if (existsSync8(templatePath)) {
9918
10145
  template = await Bun.file(templatePath).text();
9919
10146
  }
9920
10147
  const title = branch.replace(/^(feat|fix|chore|docs|refactor|test|ci)\//, "").replace(/[-_]/g, " ").replace(/^\w/, (c3) => c3.toUpperCase());
@@ -10262,7 +10489,7 @@ var quickCommand = defineCommand({
10262
10489
  });
10263
10490
 
10264
10491
  // src/commands/worktree.ts
10265
- import { existsSync as existsSync6 } from "fs";
10492
+ import { existsSync as existsSync9 } from "fs";
10266
10493
  function slugify(name) {
10267
10494
  return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
10268
10495
  }
@@ -10296,7 +10523,7 @@ var worktreeCommand = defineCommand({
10296
10523
  }
10297
10524
  const slug = slugify(args.name);
10298
10525
  const worktreePath = `${root}/.worktrees/${slug}`;
10299
- if (existsSync6(worktreePath)) {
10526
+ if (existsSync9(worktreePath)) {
10300
10527
  consola.error(`Worktree already exists at ${worktreePath}`);
10301
10528
  return;
10302
10529
  }
@@ -10462,7 +10689,7 @@ Specify a worktree name: jdi worktree-remove <name>`);
10462
10689
 
10463
10690
  // src/commands/plan-review.ts
10464
10691
  import { resolve as resolve7 } from "path";
10465
- import { existsSync as existsSync7 } from "fs";
10692
+ import { existsSync as existsSync10 } from "fs";
10466
10693
  function parsePlanSummary(content) {
10467
10694
  const nameMatch = content.match(/^# .+?: (.+)$/m);
10468
10695
  const name = nameMatch?.[1] ?? "Unknown";
@@ -10501,7 +10728,7 @@ var planReviewCommand = defineCommand({
10501
10728
  consola.error("No plan found. Run `jdi plan` first.");
10502
10729
  return;
10503
10730
  }
10504
- if (!existsSync7(planPath)) {
10731
+ if (!existsSync10(planPath)) {
10505
10732
  consola.error(`Plan not found: ${planPath}`);
10506
10733
  return;
10507
10734
  }
@@ -10591,7 +10818,7 @@ Tasks (${tasks.length}):`);
10591
10818
 
10592
10819
  // src/commands/plan-approve.ts
10593
10820
  import { resolve as resolve8 } from "path";
10594
- import { existsSync as existsSync8 } from "fs";
10821
+ import { existsSync as existsSync11 } from "fs";
10595
10822
  var planApproveCommand = defineCommand({
10596
10823
  meta: {
10597
10824
  name: "plan-approve",
@@ -10620,7 +10847,7 @@ var planApproveCommand = defineCommand({
10620
10847
  consola.error("No plan to approve. Run `jdi plan` first.");
10621
10848
  return;
10622
10849
  }
10623
- if (!existsSync8(planPath)) {
10850
+ if (!existsSync11(planPath)) {
10624
10851
  consola.error(`Plan not found: ${planPath}`);
10625
10852
  return;
10626
10853
  }
@@ -10644,15 +10871,576 @@ var planApproveCommand = defineCommand({
10644
10871
  consola.info("Say 'implement this' in Claude Code or run `/jdi:implement-plan` to execute.");
10645
10872
  }
10646
10873
  });
10874
+
10875
+ // src/commands/action.ts
10876
+ import { resolve as resolve9 } from "path";
10877
+
10878
+ // src/utils/clickup.ts
10879
+ var CLICKUP_URL_PATTERNS = [
10880
+ /app\.clickup\.com\/t\/([a-z0-9]+)/i,
10881
+ /sharing\.clickup\.com\/t\/([a-z0-9]+)/i,
10882
+ /clickup\.com\/t\/([a-z0-9]+)/i
10883
+ ];
10884
+ function extractClickUpId(text) {
10885
+ for (const pattern of CLICKUP_URL_PATTERNS) {
10886
+ const match = text.match(pattern);
10887
+ if (match)
10888
+ return match[1];
10889
+ }
10890
+ return null;
10891
+ }
10892
+ async function fetchClickUpTicket(taskId) {
10893
+ const token = process.env.CLICKUP_API_TOKEN;
10894
+ if (!token)
10895
+ return null;
10896
+ try {
10897
+ const res = await fetch(`https://api.clickup.com/api/v2/task/${taskId}`, { headers: { Authorization: token } });
10898
+ if (!res.ok)
10899
+ return null;
10900
+ const data = await res.json();
10901
+ const acceptanceCriteria = [];
10902
+ if (data.checklists) {
10903
+ for (const checklist of data.checklists) {
10904
+ for (const item of checklist.items ?? []) {
10905
+ acceptanceCriteria.push(item.name);
10906
+ }
10907
+ }
10908
+ }
10909
+ const subtasks = (data.subtasks ?? []).map((st) => ({
10910
+ name: st.name,
10911
+ status: st.status?.status ?? "unknown"
10912
+ }));
10913
+ const priorityMap = {
10914
+ 1: "urgent",
10915
+ 2: "high",
10916
+ 3: "normal",
10917
+ 4: "low"
10918
+ };
10919
+ return {
10920
+ id: data.id,
10921
+ name: data.name,
10922
+ description: data.description ?? "",
10923
+ status: data.status?.status ?? "unknown",
10924
+ priority: priorityMap[data.priority?.id] ?? "normal",
10925
+ acceptanceCriteria,
10926
+ subtasks
10927
+ };
10928
+ } catch {
10929
+ return null;
10930
+ }
10931
+ }
10932
+ function formatTicketAsContext(ticket) {
10933
+ const lines = [
10934
+ `## ClickUp Ticket: ${ticket.name}`,
10935
+ `- **ID:** ${ticket.id}`,
10936
+ `- **Status:** ${ticket.status}`,
10937
+ `- **Priority:** ${ticket.priority}`,
10938
+ ``,
10939
+ `### Description`,
10940
+ ticket.description || "_No description_"
10941
+ ];
10942
+ if (ticket.acceptanceCriteria.length > 0) {
10943
+ lines.push(``, `### Acceptance Criteria`);
10944
+ for (const ac of ticket.acceptanceCriteria) {
10945
+ lines.push(`- [ ] ${ac}`);
10946
+ }
10947
+ }
10948
+ if (ticket.subtasks.length > 0) {
10949
+ lines.push(``, `### Subtasks`);
10950
+ for (const st of ticket.subtasks) {
10951
+ const check = st.status === "complete" ? "x" : " ";
10952
+ lines.push(`- [${check}] ${st.name}`);
10953
+ }
10954
+ }
10955
+ return lines.join(`
10956
+ `);
10957
+ }
10958
+
10959
+ // src/utils/github.ts
10960
+ async function postGitHubComment(repo, issueNumber, body) {
10961
+ await exec([
10962
+ "gh",
10963
+ "api",
10964
+ `repos/${repo}/issues/${issueNumber}/comments`,
10965
+ "-f",
10966
+ `body=${body}`
10967
+ ]);
10968
+ }
10969
+ async function reactToComment(repo, commentId, reaction) {
10970
+ await exec([
10971
+ "gh",
10972
+ "api",
10973
+ `repos/${repo}/issues/comments/${commentId}/reactions`,
10974
+ "-f",
10975
+ `content=${reaction}`
10976
+ ]);
10977
+ }
10978
+ async function fetchCommentThread(repo, issueNumber) {
10979
+ const { stdout: stdout2, exitCode } = await exec([
10980
+ "gh",
10981
+ "api",
10982
+ `repos/${repo}/issues/${issueNumber}/comments`,
10983
+ "--paginate",
10984
+ "--jq",
10985
+ `.[] | {id: .id, author: .user.login, body: .body, createdAt: .created_at}`
10986
+ ]);
10987
+ if (exitCode !== 0 || !stdout2.trim())
10988
+ return [];
10989
+ const comments = [];
10990
+ for (const line of stdout2.trim().split(`
10991
+ `)) {
10992
+ if (!line.trim())
10993
+ continue;
10994
+ try {
10995
+ const parsed = JSON.parse(line);
10996
+ comments.push({
10997
+ id: parsed.id,
10998
+ author: parsed.author,
10999
+ body: parsed.body,
11000
+ createdAt: parsed.createdAt,
11001
+ isJedi: parsed.body.includes("Powered by [@benzotti/jedi]") || parsed.body.includes("Jedi plan") || parsed.body.includes("Jedi quick") || parsed.body.includes("Jedi implement")
11002
+ });
11003
+ } catch {}
11004
+ }
11005
+ return comments;
11006
+ }
11007
+ function buildConversationContext(thread, currentCommentId) {
11008
+ const jediSegments = [];
11009
+ let inJediConversation = false;
11010
+ for (const comment of thread) {
11011
+ if (comment.id === currentCommentId)
11012
+ break;
11013
+ if (comment.body.includes("@jedi")) {
11014
+ inJediConversation = true;
11015
+ jediSegments.push(comment);
11016
+ } else if (inJediConversation) {
11017
+ jediSegments.push(comment);
11018
+ if (comment.isJedi) {}
11019
+ }
11020
+ }
11021
+ const previousJediRuns = jediSegments.filter((c3) => c3.isJedi).length;
11022
+ const isFollowUp = previousJediRuns > 0;
11023
+ if (jediSegments.length === 0) {
11024
+ return { history: "", previousJediRuns: 0, isFollowUp: false };
11025
+ }
11026
+ const lines = ["## Previous Conversation", ""];
11027
+ for (const comment of jediSegments) {
11028
+ const role = comment.isJedi ? "Jedi" : `@${comment.author}`;
11029
+ let body = comment.body;
11030
+ if (comment.isJedi && body.length > 2000) {
11031
+ body = body.slice(0, 2000) + `
11032
+
11033
+ ... (truncated)`;
11034
+ }
11035
+ lines.push(`**${role}** (${comment.createdAt}):`);
11036
+ lines.push(body);
11037
+ lines.push("");
11038
+ }
11039
+ return { history: lines.join(`
11040
+ `), previousJediRuns, isFollowUp };
11041
+ }
11042
+ function formatResultComment(command, success, summary) {
11043
+ const status = success ? "Completed" : "Failed";
11044
+ const icon = success ? "white_check_mark" : "x";
11045
+ return [
11046
+ `### :${icon}: Jedi ${command} \u2014 ${status}`,
11047
+ ``,
11048
+ `<details>`,
11049
+ `<summary>Details</summary>`,
11050
+ ``,
11051
+ summary,
11052
+ ``,
11053
+ `</details>`,
11054
+ ``,
11055
+ `---`,
11056
+ `_Powered by [@benzotti/jedi](https://github.com/zottiben/jedi)_`
11057
+ ].join(`
11058
+ `);
11059
+ }
11060
+
11061
+ // src/commands/action.ts
11062
+ function parseComment(comment, isFollowUp) {
11063
+ const match = comment.match(/@jedi\s+(.+)/is);
11064
+ if (!match) {
11065
+ if (isFollowUp) {
11066
+ return {
11067
+ command: "plan",
11068
+ description: comment.trim(),
11069
+ clickUpUrl: null,
11070
+ fullFlow: false,
11071
+ isFeedback: true
11072
+ };
11073
+ }
11074
+ return null;
11075
+ }
11076
+ const body = match[1].trim();
11077
+ const clickUpMatch = body.match(/(https?:\/\/[^\s]*clickup\.com\/t\/[a-z0-9]+)/i);
11078
+ const clickUpUrl = clickUpMatch ? clickUpMatch[1] : null;
11079
+ const description = body.replace(/(https?:\/\/[^\s]*clickup\.com\/t\/[a-z0-9]+)/i, "").replace(/\s+/g, " ").trim();
11080
+ const lower = body.toLowerCase();
11081
+ if (lower.startsWith("plan ")) {
11082
+ return { command: "plan", description, clickUpUrl, fullFlow: false, isFeedback: false };
11083
+ }
11084
+ if (lower.startsWith("implement")) {
11085
+ return { command: "implement", description, clickUpUrl, fullFlow: false, isFeedback: false };
11086
+ }
11087
+ if (lower.startsWith("quick ")) {
11088
+ return { command: "quick", description, clickUpUrl, fullFlow: false, isFeedback: false };
11089
+ }
11090
+ if (lower.startsWith("review")) {
11091
+ return { command: "review", description, clickUpUrl, fullFlow: false, isFeedback: false };
11092
+ }
11093
+ if (lower.startsWith("feedback")) {
11094
+ return { command: "feedback", description, clickUpUrl, fullFlow: false, isFeedback: false };
11095
+ }
11096
+ if (lower.startsWith("do ")) {
11097
+ if (clickUpUrl) {
11098
+ return { command: "plan", description, clickUpUrl, fullFlow: true, isFeedback: false };
11099
+ }
11100
+ return { command: "quick", description, clickUpUrl, fullFlow: false, isFeedback: false };
11101
+ }
11102
+ if (/^(approved?|lgtm|looks?\s*good|ship\s*it)/i.test(lower)) {
11103
+ return {
11104
+ command: "plan",
11105
+ description: body,
11106
+ clickUpUrl: null,
11107
+ fullFlow: false,
11108
+ isFeedback: true
11109
+ };
11110
+ }
11111
+ if (isFollowUp) {
11112
+ return {
11113
+ command: "plan",
11114
+ description: body,
11115
+ clickUpUrl: null,
11116
+ fullFlow: false,
11117
+ isFeedback: true
11118
+ };
11119
+ }
11120
+ return { command: "plan", description, clickUpUrl, fullFlow: false, isFeedback: false };
11121
+ }
11122
+ var actionCommand = defineCommand({
11123
+ meta: {
11124
+ name: "action",
11125
+ description: "GitHub Action entry point \u2014 parse @jedi comment and run workflow"
11126
+ },
11127
+ args: {
11128
+ comment: {
11129
+ type: "positional",
11130
+ description: "The raw comment body containing @jedi mention",
11131
+ required: true
11132
+ },
11133
+ "comment-id": {
11134
+ type: "string",
11135
+ description: "GitHub comment ID for reactions"
11136
+ },
11137
+ "pr-number": {
11138
+ type: "string",
11139
+ description: "PR number (if triggered from a PR)"
11140
+ },
11141
+ "issue-number": {
11142
+ type: "string",
11143
+ description: "Issue number (if triggered from an issue)"
11144
+ },
11145
+ repo: {
11146
+ type: "string",
11147
+ description: "Repository in owner/repo format"
11148
+ }
11149
+ },
11150
+ async run({ args }) {
11151
+ const cwd = process.cwd();
11152
+ const repo = args.repo ?? process.env.GITHUB_REPOSITORY;
11153
+ const commentId = args["comment-id"] ? Number(args["comment-id"]) : null;
11154
+ const issueNumber = Number(args["pr-number"] ?? args["issue-number"] ?? 0);
11155
+ let conversationHistory = "";
11156
+ let isFollowUp = false;
11157
+ if (repo && issueNumber) {
11158
+ const thread = await fetchCommentThread(repo, issueNumber);
11159
+ const context = buildConversationContext(thread, commentId ?? 0);
11160
+ conversationHistory = context.history;
11161
+ isFollowUp = context.isFollowUp;
11162
+ if (isFollowUp) {
11163
+ consola.info(`Continuing conversation (${context.previousJediRuns} previous Jedi run(s))`);
11164
+ }
11165
+ }
11166
+ const intent = parseComment(args.comment, isFollowUp);
11167
+ if (!intent) {
11168
+ consola.error("Could not parse @jedi intent from comment");
11169
+ process.exit(1);
11170
+ }
11171
+ consola.info(`Parsed intent: ${intent.isFeedback ? "feedback on previous" : intent.command}${intent.fullFlow ? " (full flow)" : ""}`);
11172
+ if (repo && commentId) {
11173
+ await reactToComment(repo, commentId, "eyes").catch(() => {});
11174
+ }
11175
+ const storage = await createStorage(cwd);
11176
+ const { learningsPath, codebaseIndexPath } = await loadPersistedState(cwd, storage);
11177
+ let ticketContext = "";
11178
+ if (intent.clickUpUrl) {
11179
+ const taskId = extractClickUpId(intent.clickUpUrl);
11180
+ if (taskId) {
11181
+ consola.info(`Fetching ClickUp ticket: ${taskId}`);
11182
+ const ticket = await fetchClickUpTicket(taskId);
11183
+ if (ticket) {
11184
+ ticketContext = formatTicketAsContext(ticket);
11185
+ consola.success(`Loaded ticket: ${ticket.name}`);
11186
+ }
11187
+ }
11188
+ }
11189
+ const projectType = await detectProjectType(cwd);
11190
+ const adapter = await readAdapter(cwd);
11191
+ const techStack = adapter?.tech_stack ? Object.entries(adapter.tech_stack).map(([k2, v2]) => `${k2}: ${v2}`).join(", ") : projectType;
11192
+ const contextLines = [
11193
+ `## Project Context`,
11194
+ `- Type: ${projectType}`,
11195
+ `- Tech stack: ${techStack}`,
11196
+ `- Working directory: ${cwd}`
11197
+ ];
11198
+ if (learningsPath) {
11199
+ contextLines.push(`- Learnings: ${learningsPath}`);
11200
+ }
11201
+ if (codebaseIndexPath) {
11202
+ contextLines.push(`- Codebase index: ${codebaseIndexPath}`);
11203
+ }
11204
+ if (ticketContext) {
11205
+ contextLines.push(``, ticketContext);
11206
+ }
11207
+ const baseProtocol = resolve9(cwd, ".jdi/framework/components/meta/AgentBase.md");
11208
+ let prompt2;
11209
+ if (intent.isFeedback) {
11210
+ prompt2 = [
11211
+ `Read ${baseProtocol} for the base agent protocol.`,
11212
+ ``,
11213
+ ...contextLines,
11214
+ ``,
11215
+ conversationHistory,
11216
+ ``,
11217
+ `## Current Feedback`,
11218
+ `The user has provided feedback on previous Jedi work:`,
11219
+ ``,
11220
+ `> ${intent.description}`,
11221
+ ``,
11222
+ `## Instructions`,
11223
+ `Review the previous conversation above. The user is iterating on work Jedi already started.`,
11224
+ ``,
11225
+ `- If the feedback is an **approval** ("approved", "lgtm", "looks good", "ship it"), finalise the current work \u2014 create commits and/or a PR as appropriate.`,
11226
+ `- If the feedback is a **refinement** ("change task 2", "use a different approach", "add error handling"), apply the requested changes to the existing plan or implementation. Present an updated summary when done.`,
11227
+ `- If the feedback is a **question**, answer it with full context from the conversation.`,
11228
+ ``,
11229
+ `Read state.yaml and any existing plan files to understand what was previously done. Apply changes incrementally \u2014 do not restart from scratch.`
11230
+ ].join(`
11231
+ `);
11232
+ } else {
11233
+ const agentSpec = resolve9(cwd, `.jdi/framework/agents/jdi-planner.md`);
11234
+ const historyBlock = conversationHistory ? `
11235
+ ${conversationHistory}
11236
+
11237
+ The above is prior conversation on this issue for context.
11238
+ ` : "";
11239
+ switch (intent.command) {
11240
+ case "plan":
11241
+ prompt2 = [
11242
+ `Read ${baseProtocol} for the base agent protocol.`,
11243
+ `You are jdi-planner. Read ${agentSpec} for your full specification.`,
11244
+ ``,
11245
+ ...contextLines,
11246
+ historyBlock,
11247
+ `## Task`,
11248
+ `Create an implementation plan for: ${intent.description}`,
11249
+ ticketContext ? `
11250
+ Use the ClickUp ticket above as the primary requirements source.` : ``,
11251
+ ``,
11252
+ `Follow the planning workflow in your spec. Present the plan summary when complete and ask for feedback. The user will respond via another GitHub comment.`
11253
+ ].join(`
11254
+ `);
11255
+ break;
11256
+ case "implement":
11257
+ prompt2 = [
11258
+ `Read ${baseProtocol} for the base agent protocol.`,
11259
+ `Read ${resolve9(cwd, ".jdi/framework/components/meta/ComplexityRouter.md")} for complexity routing rules.`,
11260
+ `Read ${resolve9(cwd, ".jdi/framework/components/meta/AgentTeamsOrchestration.md")} for Agent Teams orchestration (if needed).`,
11261
+ ``,
11262
+ ...contextLines,
11263
+ historyBlock,
11264
+ `## Task`,
11265
+ `Execute the current implementation plan. Read state.yaml for the active plan path.`,
11266
+ `Follow the implement-plan orchestration. Present a summary when complete and ask for feedback.`
11267
+ ].join(`
11268
+ `);
11269
+ break;
11270
+ case "quick":
11271
+ prompt2 = [
11272
+ `Read ${baseProtocol} for the base agent protocol.`,
11273
+ ``,
11274
+ ...contextLines,
11275
+ historyBlock,
11276
+ `## Task`,
11277
+ `Make this quick change: ${intent.description}`,
11278
+ `Keep changes minimal and focused. Commit when done. Present what you changed.`
11279
+ ].join(`
11280
+ `);
11281
+ break;
11282
+ case "review":
11283
+ prompt2 = [
11284
+ `Read ${baseProtocol} for the base agent protocol.`,
11285
+ ``,
11286
+ ...contextLines,
11287
+ historyBlock,
11288
+ `## Task`,
11289
+ `Review the current PR. Post line-level review comments via gh api.`
11290
+ ].join(`
11291
+ `);
11292
+ break;
11293
+ case "feedback":
11294
+ prompt2 = [
11295
+ `Read ${baseProtocol} for the base agent protocol.`,
11296
+ ``,
11297
+ ...contextLines,
11298
+ historyBlock,
11299
+ `## Task`,
11300
+ `Address PR feedback comments. Read comments via gh api, make changes, reply to each.`
11301
+ ].join(`
11302
+ `);
11303
+ break;
11304
+ default:
11305
+ prompt2 = [
11306
+ `Read ${baseProtocol} for the base agent protocol.`,
11307
+ ``,
11308
+ ...contextLines,
11309
+ historyBlock,
11310
+ `## Task`,
11311
+ intent.description
11312
+ ].join(`
11313
+ `);
11314
+ }
11315
+ }
11316
+ let success = true;
11317
+ try {
11318
+ const { exitCode } = await spawnClaude(prompt2, {
11319
+ cwd,
11320
+ permissionMode: "bypassPermissions"
11321
+ });
11322
+ if (exitCode !== 0) {
11323
+ success = false;
11324
+ consola.error(`Claude exited with code ${exitCode}`);
11325
+ }
11326
+ if (intent.fullFlow && success) {
11327
+ consola.info("Full flow: now running implement...");
11328
+ const implementPrompt = [
11329
+ `Read ${baseProtocol} for the base agent protocol.`,
11330
+ `Read ${resolve9(cwd, ".jdi/framework/components/meta/ComplexityRouter.md")} for complexity routing rules.`,
11331
+ `Read ${resolve9(cwd, ".jdi/framework/components/meta/AgentTeamsOrchestration.md")} for Agent Teams orchestration (if needed).`,
11332
+ ``,
11333
+ ...contextLines,
11334
+ ``,
11335
+ `## Task`,
11336
+ `Execute the most recently created implementation plan in .jdi/plans/.`,
11337
+ `Follow the implement-plan orchestration. Present a summary when complete.`
11338
+ ].join(`
11339
+ `);
11340
+ const implResult = await spawnClaude(implementPrompt, {
11341
+ cwd,
11342
+ permissionMode: "bypassPermissions"
11343
+ });
11344
+ if (implResult.exitCode !== 0) {
11345
+ success = false;
11346
+ }
11347
+ }
11348
+ } catch (err) {
11349
+ success = false;
11350
+ consola.error("Execution failed:", err);
11351
+ }
11352
+ const saved = await savePersistedState(cwd, storage);
11353
+ if (saved.learningsSaved)
11354
+ consola.info("Learnings persisted to storage");
11355
+ if (saved.codebaseIndexSaved)
11356
+ consola.info("Codebase index persisted to storage");
11357
+ if (repo && issueNumber) {
11358
+ const actionLabel = intent.isFeedback ? "feedback" : intent.command;
11359
+ const summary = success ? `Executed \`${actionLabel}\` successfully.${saved.learningsSaved ? " Learnings updated." : ""}` : `Execution of \`${actionLabel}\` failed. Check workflow logs for details.`;
11360
+ const commentBody = formatResultComment(actionLabel, success, summary);
11361
+ await postGitHubComment(repo, issueNumber, commentBody).catch((err) => {
11362
+ consola.error("Failed to post result comment:", err);
11363
+ });
11364
+ }
11365
+ if (repo && commentId) {
11366
+ const reaction = success ? "+1" : "-1";
11367
+ await reactToComment(repo, commentId, reaction).catch(() => {});
11368
+ }
11369
+ if (!success)
11370
+ process.exit(1);
11371
+ }
11372
+ });
11373
+
11374
+ // src/commands/setup-action.ts
11375
+ import { join as join12, dirname as dirname3 } from "path";
11376
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4 } from "fs";
11377
+ var setupActionCommand = defineCommand({
11378
+ meta: {
11379
+ name: "setup-action",
11380
+ description: "Set up the Jedi GitHub Action in your repository"
11381
+ },
11382
+ args: {},
11383
+ async run() {
11384
+ const cwd = process.cwd();
11385
+ const workflowDest = join12(cwd, ".github", "workflows", "jedi.yml");
11386
+ if (existsSync12(workflowDest)) {
11387
+ consola.warn(`Workflow already exists at ${workflowDest}`);
11388
+ consola.info("Skipping workflow copy. Delete it manually to regenerate.");
11389
+ } else {
11390
+ const templatePath = join12(import.meta.dir, "../../action/workflow-template.yml");
11391
+ if (!existsSync12(templatePath)) {
11392
+ consola.error("Workflow template not found. Ensure @benzotti/jedi is properly installed.");
11393
+ process.exit(1);
11394
+ }
11395
+ const dir = dirname3(workflowDest);
11396
+ if (!existsSync12(dir))
11397
+ mkdirSync4(dir, { recursive: true });
11398
+ const template = await Bun.file(templatePath).text();
11399
+ await Bun.write(workflowDest, template);
11400
+ consola.success(`Created ${workflowDest}`);
11401
+ }
11402
+ consola.info("");
11403
+ consola.box([
11404
+ "Jedi GitHub Action Setup",
11405
+ "",
11406
+ "Uses: anthropics/claude-code-action@v1",
11407
+ "Trigger: @jedi in issue/PR comments",
11408
+ "",
11409
+ "Required secrets (set via GitHub UI or CLI):",
11410
+ "",
11411
+ " gh secret set ANTHROPIC_API_KEY --body '<your-key>'",
11412
+ "",
11413
+ "Optional secrets:",
11414
+ "",
11415
+ " gh secret set CLICKUP_API_TOKEN --body '<your-token>'",
11416
+ "",
11417
+ "Usage: Comment on any issue or PR with:",
11418
+ "",
11419
+ " @jedi plan <description>",
11420
+ " @jedi quick <small fix>",
11421
+ " @jedi do <clickup-ticket-url>",
11422
+ " @jedi review",
11423
+ " @jedi feedback",
11424
+ "",
11425
+ "Conversation: Reply to Jedi with feedback to iterate,",
11426
+ "or say 'approved' to finalise."
11427
+ ].join(`
11428
+ `));
11429
+ }
11430
+ });
10647
11431
  // package.json
10648
11432
  var package_default = {
10649
11433
  name: "@benzotti/jedi",
10650
- version: "0.1.3",
11434
+ version: "0.1.4",
10651
11435
  description: "JDI - Context-efficient AI development framework for Claude Code",
10652
11436
  type: "module",
10653
11437
  bin: {
10654
11438
  jdi: "./dist/index.js"
10655
11439
  },
11440
+ exports: {
11441
+ ".": "./dist/index.js",
11442
+ "./storage": "./src/storage/index.ts"
11443
+ },
10656
11444
  scripts: {
10657
11445
  build: "bun build src/index.ts --outdir dist --target bun",
10658
11446
  dev: "bun run src/index.ts",
@@ -10669,7 +11457,8 @@ var package_default = {
10669
11457
  },
10670
11458
  files: [
10671
11459
  "dist",
10672
- "framework"
11460
+ "framework",
11461
+ "action"
10673
11462
  ],
10674
11463
  repository: {
10675
11464
  type: "git",
@@ -10702,7 +11491,9 @@ var main = defineCommand({
10702
11491
  worktree: worktreeCommand,
10703
11492
  "worktree-remove": worktreeRemoveCommand,
10704
11493
  "plan-review": planReviewCommand,
10705
- "plan-approve": planApproveCommand
11494
+ "plan-approve": planApproveCommand,
11495
+ action: actionCommand,
11496
+ "setup-action": setupActionCommand
10706
11497
  }
10707
11498
  });
10708
11499
  runMain(main);