@chit-run/cli 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/chit.js +250 -110
  2. package/package.json +1 -1
package/dist/chit.js CHANGED
@@ -970,6 +970,7 @@ function validateLoopRecord(raw) {
970
970
  scope: str2(o, "scope", ctx),
971
971
  task: str2(o, "task", ctx),
972
972
  repo: str2(o, "repo", ctx),
973
+ repoKey: str2(o, "repoKey", ctx),
973
974
  startedAt: str2(o, "startedAt", ctx),
974
975
  maxIterations: int2(o, "maxIterations", ctx, 1)
975
976
  };
@@ -988,8 +989,11 @@ function validateLoopRecord(raw) {
988
989
  checkDurationMs: int2(o, "checkDurationMs", ctx, 0),
989
990
  at: str2(o, "at", ctx)
990
991
  };
991
- if (o.detailsRef !== undefined)
992
- rec.detailsRef = str2(o, "detailsRef", ctx);
992
+ if (o.workspaceWarnings !== undefined) {
993
+ rec.workspaceWarnings = stringArray2(o, "workspaceWarnings", ctx);
994
+ }
995
+ if (o.auditRef !== undefined)
996
+ rec.auditRef = str2(o, "auditRef", ctx);
993
997
  const usage = optUsage2(o, ctx);
994
998
  if (usage !== undefined)
995
999
  rec.usage = usage;
@@ -9964,11 +9968,11 @@ var init_dist = __esm(() => {
9964
9968
 
9965
9969
  // ../studio/src/server/audit.ts
9966
9970
  import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
9967
- import { homedir as homedir5 } from "os";
9968
- import { join as join8 } from "path";
9971
+ import { homedir as homedir6 } from "os";
9972
+ import { join as join9 } from "path";
9969
9973
  function defaultAuditDir2() {
9970
- const xdg = process.env.XDG_STATE_HOME || join8(homedir5(), ".local", "state");
9971
- return join8(xdg, "chit", "audit");
9974
+ const xdg = process.env.XDG_STATE_HOME || join9(homedir6(), ".local", "state");
9975
+ return join9(xdg, "chit", "audit");
9972
9976
  }
9973
9977
  function blobRefs(e) {
9974
9978
  switch (e.type) {
@@ -9987,8 +9991,8 @@ function blobRefs(e) {
9987
9991
  function readAuditRun(auditDir, runId, includeBlobs) {
9988
9992
  if (!SAFE_RUN_ID2.test(runId))
9989
9993
  return { kind: "invalid-id" };
9990
- const runDir = join8(auditDir, "runs", runId);
9991
- const eventsPath = join8(runDir, "events.jsonl");
9994
+ const runDir = join9(auditDir, "runs", runId);
9995
+ const eventsPath = join9(runDir, "events.jsonl");
9992
9996
  if (!existsSync8(eventsPath))
9993
9997
  return { kind: "not-found" };
9994
9998
  let events2;
@@ -10001,13 +10005,13 @@ function readAuditRun(auditDir, runId, includeBlobs) {
10001
10005
  }
10002
10006
  if (!includeBlobs)
10003
10007
  return { kind: "ok", events: events2 };
10004
- const blobsDir = join8(runDir, "blobs");
10008
+ const blobsDir = join9(runDir, "blobs");
10005
10009
  const blobs = {};
10006
10010
  for (const e of events2) {
10007
10011
  for (const ref of blobRefs(e)) {
10008
10012
  if (!SHA256_HEX2.test(ref) || ref in blobs)
10009
10013
  continue;
10010
- const blobPath = join8(blobsDir, ref);
10014
+ const blobPath = join9(blobsDir, ref);
10011
10015
  if (existsSync8(blobPath))
10012
10016
  blobs[ref] = readFileSync9(blobPath, "utf-8");
10013
10017
  }
@@ -10094,7 +10098,7 @@ var init_paths = __esm(() => {
10094
10098
 
10095
10099
  // ../studio/src/server/discovery.ts
10096
10100
  import { readdirSync as readdirSync3, readFileSync as readFileSync10 } from "fs";
10097
- import { basename, join as join9, relative } from "path";
10101
+ import { basename, join as join10, relative } from "path";
10098
10102
  function safeParseChit(absolutePath) {
10099
10103
  try {
10100
10104
  const raw2 = JSON.parse(readFileSync10(absolutePath, "utf-8"));
@@ -10124,7 +10128,7 @@ function discover(opts) {
10124
10128
  continue;
10125
10129
  if (!entry.name.endsWith(".json"))
10126
10130
  continue;
10127
- const absolutePath = join9(opts.cwd, entry.name);
10131
+ const absolutePath = join10(opts.cwd, entry.name);
10128
10132
  if (!safeParseChit(absolutePath))
10129
10133
  continue;
10130
10134
  candidates.push({
@@ -10149,14 +10153,14 @@ var init_discovery = __esm(() => {
10149
10153
  });
10150
10154
 
10151
10155
  // ../studio/src/server/docs.ts
10152
- import { createHash as createHash5 } from "crypto";
10156
+ import { createHash as createHash6 } from "crypto";
10153
10157
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
10154
10158
  import { basename as basename2, relative as relative2 } from "path";
10155
10159
  function canonicalize(draft) {
10156
10160
  return JSON.stringify(draft, null, "\t");
10157
10161
  }
10158
10162
  function hashRaw(raw2) {
10159
- return createHash5("sha256").update(raw2, "utf-8").digest("hex");
10163
+ return createHash6("sha256").update(raw2, "utf-8").digest("hex");
10160
10164
  }
10161
10165
 
10162
10166
  class DocStore {
@@ -10363,10 +10367,7 @@ var init_docs = __esm(() => {
10363
10367
 
10364
10368
  // ../studio/src/server/loops.ts
10365
10369
  import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync12 } from "fs";
10366
- import { join as join10 } from "path";
10367
- function loopsDir2(cwd) {
10368
- return join10(cwd, ".chit", "loops");
10369
- }
10370
+ import { join as join11 } from "path";
10370
10371
  function summarize(loopId, records) {
10371
10372
  const header = records[0];
10372
10373
  if (header?.type !== "loop")
@@ -10383,26 +10384,8 @@ function summarize(loopId, records) {
10383
10384
  startedAt: header.startedAt
10384
10385
  };
10385
10386
  }
10386
- function listLoops(cwd) {
10387
- const dir = loopsDir2(cwd);
10388
- if (!existsSync10(dir))
10389
- return [];
10390
- const summaries = [];
10391
- for (const name of readdirSync4(dir)) {
10392
- if (!name.endsWith(".jsonl"))
10393
- continue;
10394
- const loopId = name.slice(0, -".jsonl".length);
10395
- const result = readLoop2(cwd, loopId);
10396
- if (result.kind === "ok")
10397
- summaries.push(summarize(loopId, result.records));
10398
- }
10399
- summaries.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
10400
- return summaries;
10401
- }
10402
- function readLoop2(cwd, loopId) {
10403
- if (!SAFE_LOOP_ID2.test(loopId))
10404
- return { kind: "invalid-id" };
10405
- const path = join10(loopsDir2(cwd), `${loopId}.jsonl`);
10387
+ function readLoopFrom(dir, loopId) {
10388
+ const path = join11(dir, `${loopId}.jsonl`);
10406
10389
  if (!existsSync10(path))
10407
10390
  return { kind: "not-found" };
10408
10391
  try {
@@ -10418,6 +10401,30 @@ function readLoop2(cwd, loopId) {
10418
10401
  throw e;
10419
10402
  }
10420
10403
  }
10404
+ function listLoops(loopsDir) {
10405
+ if (!loopsDir || !existsSync10(loopsDir))
10406
+ return [];
10407
+ const summaries = [];
10408
+ for (const name of readdirSync4(loopsDir)) {
10409
+ if (!name.endsWith(".jsonl"))
10410
+ continue;
10411
+ const loopId = name.slice(0, -".jsonl".length);
10412
+ if (!SAFE_LOOP_ID2.test(loopId))
10413
+ continue;
10414
+ const result = readLoopFrom(loopsDir, loopId);
10415
+ if (result.kind === "ok")
10416
+ summaries.push(summarize(loopId, result.records));
10417
+ }
10418
+ summaries.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
10419
+ return summaries;
10420
+ }
10421
+ function readLoop2(loopsDir, loopId) {
10422
+ if (!SAFE_LOOP_ID2.test(loopId))
10423
+ return { kind: "invalid-id" };
10424
+ if (!loopsDir)
10425
+ return { kind: "not-found" };
10426
+ return readLoopFrom(loopsDir, loopId);
10427
+ }
10421
10428
  var SAFE_LOOP_ID2;
10422
10429
  var init_loops = __esm(() => {
10423
10430
  init_src();
@@ -10456,7 +10463,7 @@ __export(exports_server, {
10456
10463
  PathError: () => PathError
10457
10464
  });
10458
10465
  import { existsSync as existsSync11 } from "fs";
10459
- import { join as join11 } from "path";
10466
+ import { join as join12 } from "path";
10460
10467
  async function startStudio(opts) {
10461
10468
  const hostname3 = opts.hostname ?? "127.0.0.1";
10462
10469
  const requestedPort = opts.port ?? 0;
@@ -10474,7 +10481,8 @@ async function startStudio(opts) {
10474
10481
  store,
10475
10482
  allowedHosts,
10476
10483
  clientDistDir,
10477
- lifecycle: opts.lifecycle
10484
+ lifecycle: opts.lifecycle,
10485
+ loopsDir: opts.loopsDir
10478
10486
  });
10479
10487
  const server2 = Bun.serve({
10480
10488
  port: requestedPort,
@@ -10508,7 +10516,7 @@ function buildApp(opts) {
10508
10516
  const asset = c.req.param("asset");
10509
10517
  if (!CLIENT_ASSETS.has(asset))
10510
10518
  return c.text("not found", 404);
10511
- const path = join11(opts.clientDistDir, asset);
10519
+ const path = join12(opts.clientDistDir, asset);
10512
10520
  if (!existsSync11(path)) {
10513
10521
  return c.text(`client bundle missing at ${path}. Run: bun run studio:build`, 503);
10514
10522
  }
@@ -10589,10 +10597,10 @@ function buildApp(opts) {
10589
10597
  return c.json(result);
10590
10598
  });
10591
10599
  app.get("/api/loops", (c) => {
10592
- return c.json(listLoops(opts.cwd));
10600
+ return c.json(listLoops(opts.loopsDir));
10593
10601
  });
10594
10602
  app.get("/api/loops/:loopId", (c) => {
10595
- const result = readLoop2(opts.cwd, c.req.param("loopId"));
10603
+ const result = readLoop2(opts.loopsDir, c.req.param("loopId"));
10596
10604
  if (result.kind === "not-found")
10597
10605
  return c.text("not found", 404);
10598
10606
  if (result.kind === "invalid-id")
@@ -10684,15 +10692,15 @@ var init_server = __esm(() => {
10684
10692
  init_loops();
10685
10693
  init_token();
10686
10694
  init_paths();
10687
- CLIENT_DIST = join11(import.meta.dir, "..", "..", "dist", "client");
10695
+ CLIENT_DIST = join12(import.meta.dir, "..", "..", "dist", "client");
10688
10696
  CLIENT_ASSETS = new Set(["index.js", "index.css"]);
10689
10697
  });
10690
10698
 
10691
10699
  // src/cli/run.ts
10692
10700
  init_src();
10693
10701
  import { readFileSync as readFileSync13 } from "fs";
10694
- import { homedir as homedir6 } from "os";
10695
- import { basename as basename3, dirname as dirname2, join as join12 } from "path";
10702
+ import { homedir as homedir7 } from "os";
10703
+ import { basename as basename3, dirname as dirname2, join as join13 } from "path";
10696
10704
 
10697
10705
  // src/adapters/sanitize.ts
10698
10706
  var SENSITIVE_KEY = /key|token|secret|password|auth/i;
@@ -11596,6 +11604,45 @@ function wrapAdaptersWithAudit(adapters, recorder) {
11596
11604
  return out;
11597
11605
  }
11598
11606
 
11607
+ // src/loops/location.ts
11608
+ import { spawnSync } from "child_process";
11609
+ import { createHash as createHash2 } from "crypto";
11610
+ import { realpathSync } from "fs";
11611
+ import { homedir as homedir3 } from "os";
11612
+ import { join as join3 } from "path";
11613
+ function gitTopLevel(cwd) {
11614
+ try {
11615
+ const out = spawnSync("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
11616
+ encoding: "utf-8",
11617
+ stdio: ["ignore", "pipe", "ignore"]
11618
+ });
11619
+ if (out.status !== 0)
11620
+ return;
11621
+ const top = out.stdout.trim();
11622
+ return top || undefined;
11623
+ } catch {
11624
+ return;
11625
+ }
11626
+ }
11627
+ function repoRoot(cwd) {
11628
+ const base = gitTopLevel(cwd) ?? cwd;
11629
+ try {
11630
+ return realpathSync(base);
11631
+ } catch {
11632
+ return base;
11633
+ }
11634
+ }
11635
+ function repoKey(cwd) {
11636
+ return createHash2("sha256").update(repoRoot(cwd)).digest("hex").slice(0, 16);
11637
+ }
11638
+ function loopStateDir() {
11639
+ const xdg = process.env.XDG_STATE_HOME || join3(homedir3(), ".local", "state");
11640
+ return join3(xdg, "chit", "loops");
11641
+ }
11642
+ function loopLogDir(cwd) {
11643
+ return join3(loopStateDir(), repoKey(cwd));
11644
+ }
11645
+
11599
11646
  // src/runtime/render.ts
11600
11647
  import { existsSync as existsSync3 } from "fs";
11601
11648
  import { isAbsolute, resolve } from "path";
@@ -11818,7 +11865,7 @@ async function executeManifest(manifest, options) {
11818
11865
  }
11819
11866
 
11820
11867
  // src/sessions/fingerprint.ts
11821
- import { createHash as createHash2 } from "crypto";
11868
+ import { createHash as createHash3 } from "crypto";
11822
11869
  function computeFingerprint(input) {
11823
11870
  const { agent, participant } = input;
11824
11871
  const baseUrl = agent.env?.ANTHROPIC_BASE_URL ?? agent.env?.OPENAI_BASE_URL ?? agent.env?.OLLAMA_HOST ?? "";
@@ -11834,7 +11881,7 @@ function computeFingerprint(input) {
11834
11881
  session: participant.session,
11835
11882
  permissions: participant.permissions
11836
11883
  });
11837
- return createHash2("sha256").update(material).digest("hex").slice(0, 16);
11884
+ return createHash3("sha256").update(material).digest("hex").slice(0, 16);
11838
11885
  }
11839
11886
 
11840
11887
  // src/sessions/coordinator.ts
@@ -11886,7 +11933,7 @@ function buildSessionAdapter(inner, manifestId, scope, trackedFingerprints, stor
11886
11933
  }
11887
11934
 
11888
11935
  // src/sessions/store.ts
11889
- import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
11936
+ import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
11890
11937
  import {
11891
11938
  closeSync,
11892
11939
  existsSync as existsSync4,
@@ -11898,8 +11945,8 @@ import {
11898
11945
  writeFileSync as writeFileSync2,
11899
11946
  writeSync
11900
11947
  } from "fs";
11901
- import { homedir as homedir3 } from "os";
11902
- import { dirname, join as join3 } from "path";
11948
+ import { homedir as homedir4 } from "os";
11949
+ import { dirname, join as join4 } from "path";
11903
11950
  function isObject4(v) {
11904
11951
  return v !== null && typeof v === "object" && !Array.isArray(v);
11905
11952
  }
@@ -11916,8 +11963,8 @@ function sleepSync(ms) {
11916
11963
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
11917
11964
  }
11918
11965
  function defaultSessionDir() {
11919
- const xdg = process.env.XDG_STATE_HOME || join3(homedir3(), ".local", "state");
11920
- return join3(xdg, "chit", "sessions");
11966
+ const xdg = process.env.XDG_STATE_HOME || join4(homedir4(), ".local", "state");
11967
+ return join4(xdg, "chit", "sessions");
11921
11968
  }
11922
11969
 
11923
11970
  class FileSessionStore {
@@ -12012,16 +12059,16 @@ class FileSessionStore {
12012
12059
  }
12013
12060
  filePath(key) {
12014
12061
  const readable = `${safeSegment(key.scope)}--${safeSegment(key.manifestId)}`;
12015
- const hash = createHash3("sha256").update(`${key.scope}${HASH_SEP}${key.manifestId}`).digest("hex").slice(0, 12);
12016
- return join3(this.baseDir, `${readable}--${hash}.json`);
12062
+ const hash = createHash4("sha256").update(`${key.scope}${HASH_SEP}${key.manifestId}`).digest("hex").slice(0, 12);
12063
+ return join4(this.baseDir, `${readable}--${hash}.json`);
12017
12064
  }
12018
12065
  }
12019
12066
 
12020
12067
  // src/surfaces/claude-skill.ts
12021
12068
  init_src();
12022
- import { createHash as createHash4, randomBytes } from "crypto";
12069
+ import { createHash as createHash5, randomBytes } from "crypto";
12023
12070
  import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
12024
- import { join as join4, resolve as resolvePath } from "path";
12071
+ import { join as join5, resolve as resolvePath } from "path";
12025
12072
  class SurfaceInstallError extends Error {
12026
12073
  constructor(message) {
12027
12074
  super(message);
@@ -12083,7 +12130,7 @@ ${formatEnforcementGaps(gaps)}
12083
12130
  Pass allowUnenforcedPermissions=true to install anyway; the generated skill will warn on every run.`);
12084
12131
  }
12085
12132
  const installName = opts.overrideName ?? manifest.id;
12086
- const skillDir = join4(outputDir, installName);
12133
+ const skillDir = join5(outputDir, installName);
12087
12134
  if (existsSync5(skillDir)) {
12088
12135
  if (!opts.force) {
12089
12136
  throw new SurfaceInstallError(`skill directory already exists: ${skillDir}
@@ -12093,9 +12140,9 @@ Pass force=true to remove and replace it, or use overrideName="<id>" to install
12093
12140
  rmSync3(skillDir, { recursive: true, force: true });
12094
12141
  }
12095
12142
  mkdirSync3(skillDir, { recursive: true });
12096
- const skillMdPath = join4(skillDir, "SKILL.md");
12097
- const manifestPath = join4(skillDir, "manifest.json");
12098
- const markerPath = join4(skillDir, INSTALL_MARKER_FILENAME);
12143
+ const skillMdPath = join5(skillDir, "SKILL.md");
12144
+ const manifestPath = join5(skillDir, "manifest.json");
12145
+ const markerPath = join5(skillDir, INSTALL_MARKER_FILENAME);
12099
12146
  const manifestJson = `${JSON.stringify(rawJson, null, 2)}
12100
12147
  `;
12101
12148
  writeFileSync3(manifestPath, manifestJson);
@@ -12115,7 +12162,7 @@ Pass force=true to remove and replace it, or use overrideName="<id>" to install
12115
12162
  manifestId: manifest.id,
12116
12163
  runtimePath,
12117
12164
  installedAt: new Date().toISOString(),
12118
- manifestHash: createHash4("sha256").update(manifestJson).digest("hex")
12165
+ manifestHash: createHash5("sha256").update(manifestJson).digest("hex")
12119
12166
  };
12120
12167
  writeFileSync3(markerPath, `${JSON.stringify(marker, null, 2)}
12121
12168
  `);
@@ -12182,10 +12229,10 @@ function escapeFrontmatter(s) {
12182
12229
  // src/surfaces/lifecycle.ts
12183
12230
  init_src();
12184
12231
  import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync as rmSync4, statSync as statSync2 } from "fs";
12185
- import { homedir as homedir4 } from "os";
12186
- import { join as join5 } from "path";
12232
+ import { homedir as homedir5 } from "os";
12233
+ import { join as join6 } from "path";
12187
12234
  function defaultSkillsDir() {
12188
- return join5(homedir4(), ".claude", "skills");
12235
+ return join6(homedir5(), ".claude", "skills");
12189
12236
  }
12190
12237
 
12191
12238
  class LifecycleError extends Error {
@@ -12200,7 +12247,7 @@ function listInstalled(parentDir) {
12200
12247
  const out = [];
12201
12248
  const entries = readdirSync2(parentDir);
12202
12249
  for (const name of entries) {
12203
- const skillDir = join5(parentDir, name);
12250
+ const skillDir = join6(parentDir, name);
12204
12251
  let stat;
12205
12252
  try {
12206
12253
  stat = statSync2(skillDir);
@@ -12209,7 +12256,7 @@ function listInstalled(parentDir) {
12209
12256
  }
12210
12257
  if (!stat.isDirectory())
12211
12258
  continue;
12212
- const markerPath = join5(skillDir, INSTALL_MARKER_FILENAME);
12259
+ const markerPath = join6(skillDir, INSTALL_MARKER_FILENAME);
12213
12260
  if (!existsSync6(markerPath))
12214
12261
  continue;
12215
12262
  let raw;
@@ -12229,14 +12276,14 @@ function uninstall(parentDir, name) {
12229
12276
  if (!VALID_INSTALL_NAME_RE.test(name)) {
12230
12277
  throw new LifecycleError(`install name "${name}" is invalid: must be kebab-case (lowercase letters, digits, hyphens; must start with a letter). Path-traversal sequences like ".." or "/" are rejected.`);
12231
12278
  }
12232
- const skillDir = join5(parentDir, name);
12279
+ const skillDir = join6(parentDir, name);
12233
12280
  if (!existsSync6(skillDir)) {
12234
12281
  throw new LifecycleError(`no install at ${skillDir}`);
12235
12282
  }
12236
12283
  if (!statSync2(skillDir).isDirectory()) {
12237
12284
  throw new LifecycleError(`${skillDir} is not a directory`);
12238
12285
  }
12239
- const markerPath = join5(skillDir, INSTALL_MARKER_FILENAME);
12286
+ const markerPath = join6(skillDir, INSTALL_MARKER_FILENAME);
12240
12287
  if (!existsSync6(markerPath)) {
12241
12288
  throw new LifecycleError(`refusing to uninstall ${skillDir}: no install marker (${INSTALL_MARKER_FILENAME}) present. ` + `This directory was not created by chit (or was created by a pre-marker version). ` + `If you're sure this is yours, remove it manually with rm -rf.`);
12242
12289
  }
@@ -35061,8 +35108,7 @@ import { resolve as resolve2 } from "path";
35061
35108
  // src/loops/log-store.ts
35062
35109
  init_src();
35063
35110
  import { appendFileSync as appendFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
35064
- import { join as join6 } from "path";
35065
-
35111
+ import { join as join7 } from "path";
35066
35112
  class LoopStoreError extends Error {
35067
35113
  }
35068
35114
  var SAFE_LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
@@ -35070,14 +35116,14 @@ var realClock2 = () => Date.now();
35070
35116
  function iso(ms) {
35071
35117
  return new Date(ms).toISOString();
35072
35118
  }
35073
- function loopsDir(cwd) {
35074
- return join6(cwd, ".chit", "loops");
35075
- }
35076
- function loopPath(cwd, loopId) {
35119
+ function safeId(loopId) {
35077
35120
  if (!SAFE_LOOP_ID.test(loopId)) {
35078
35121
  throw new LoopStoreError(`invalid loop id ${JSON.stringify(loopId)}`);
35079
35122
  }
35080
- return join6(loopsDir(cwd), `${loopId}.jsonl`);
35123
+ return loopId;
35124
+ }
35125
+ function loopPath(cwd, loopId) {
35126
+ return join7(loopLogDir(cwd), `${safeId(loopId)}.jsonl`);
35081
35127
  }
35082
35128
  function readRecords(path, loopId) {
35083
35129
  if (!existsSync7(path)) {
@@ -35096,14 +35142,15 @@ function startLoop(cwd, opts) {
35096
35142
  if (existsSync7(path) && !opts.force) {
35097
35143
  throw new LoopStoreError(`loop log already exists at ${path} (pass force to overwrite)`);
35098
35144
  }
35099
- mkdirSync4(loopsDir(cwd), { recursive: true });
35145
+ mkdirSync4(loopLogDir(cwd), { recursive: true });
35100
35146
  const header = {
35101
35147
  type: "loop",
35102
35148
  schema: 1,
35103
35149
  loopId,
35104
35150
  scope: opts.scope,
35105
35151
  task: opts.task,
35106
- repo: cwd,
35152
+ repo: repoRoot(cwd),
35153
+ repoKey: repoKey(cwd),
35107
35154
  startedAt: iso((opts.clock ?? realClock2)()),
35108
35155
  maxIterations: opts.maxIterations
35109
35156
  };
@@ -35134,8 +35181,11 @@ function appendIteration(cwd, loopId, opts) {
35134
35181
  checkDurationMs: opts.checkDurationMs,
35135
35182
  at: iso((opts.clock ?? realClock2)())
35136
35183
  };
35137
- if (opts.detailsRef !== undefined)
35138
- rec.detailsRef = opts.detailsRef;
35184
+ if (opts.workspaceWarnings !== undefined && opts.workspaceWarnings.length > 0) {
35185
+ rec.workspaceWarnings = opts.workspaceWarnings;
35186
+ }
35187
+ if (opts.auditRef !== undefined)
35188
+ rec.auditRef = opts.auditRef;
35139
35189
  if (opts.usage !== undefined)
35140
35190
  rec.usage = opts.usage;
35141
35191
  appendFileSync2(path, `${serializeLoopRecord(rec)}
@@ -35172,7 +35222,7 @@ function readLoop(cwd, loopId) {
35172
35222
  var DEFAULT_CONVERGE_MANIFEST = {
35173
35223
  schema: 1,
35174
35224
  id: "converge",
35175
- description: "Autonomous convergence: a write-capable Claude implements a slice, then a read-only Codex reviews the diff and returns proceed/revise/block. Drive it in a loop with the `chit converge` CLI driver, or stepwise from the MCP - one chit_start per iteration, same scope so both agents keep their thread, feeding the prior review back in via inputs.prior_review. The human sequences and checkpoints (inspect the diff each round, stop if it goes sideways); chit runs the agents. Run against an isolated worktree, not the main checkout.",
35225
+ description: "Autonomous convergence: a write-capable Claude implements a slice, then a read-only Codex reviews the diff and returns proceed/revise/block. Drive it in a loop with the `chit converge` CLI driver, or stepwise from the MCP - one chit_run_start per iteration, same scope so both agents keep their thread, feeding the prior review back in via inputs.prior_review. The human sequences and checkpoints (inspect the diff each round, stop if it goes sideways); chit runs the agents. Run against an isolated worktree, not the main checkout.",
35176
35226
  inputs: {
35177
35227
  task: { type: "string" },
35178
35228
  prior_review: { type: "string", optional: true }
@@ -35189,7 +35239,7 @@ var DEFAULT_CONVERGE_MANIFEST = {
35189
35239
  },
35190
35240
  reviewer: {
35191
35241
  agent: "codex",
35192
- role: "You are a skeptical implementation reviewer for a convergence loop. Claude just edited the repository at your cwd. Inspect the current git diff and the changed files, verify the work against the task, and run non-mutating checks if useful. Do not edit. Do not agree for the sake of agreeing. Use prior context from this scope. Cite file:line and command results.",
35242
+ role: "You are a skeptical implementation reviewer for a convergence loop. Claude just edited the repository at your cwd. Inspect the current git diff and the changed files, and verify the work against the task. Base your verdict on the TASK changes. Untracked generated build artifacts (e.g. __pycache__, *.pyc) are workspace hygiene, not task changes: note them at most as a minor aside and do NOT revise solely because of them. chit keeps its own control-plane state outside the repo, so it never appears in the diff. Run non-mutating checks if useful. Do not edit. Do not agree for the sake of agreeing. Use prior context from this scope. Cite file:line and command results.",
35193
35243
  session: "per_scope",
35194
35244
  permissions: { filesystem: "read_only" }
35195
35245
  }
@@ -35238,6 +35288,32 @@ findingCount is the integer number of findings; checksRun is a short human strin
35238
35288
  output: "out"
35239
35289
  };
35240
35290
 
35291
+ // src/cli/workspace.ts
35292
+ function isChitOwned(path) {
35293
+ return path === ".chit" || path.startsWith(".chit/");
35294
+ }
35295
+ function isGeneratedArtifact(path) {
35296
+ const base = path.slice(path.lastIndexOf("/") + 1);
35297
+ return path === "__pycache__" || path.startsWith("__pycache__/") || path.includes("/__pycache__/") || path.endsWith(".pyc") || path.endsWith(".pyo") || base === ".DS_Store";
35298
+ }
35299
+ function classifyWorkspace(snap) {
35300
+ const changed = new Set;
35301
+ for (const f of snap.tracked) {
35302
+ if (!isChitOwned(f))
35303
+ changed.add(f);
35304
+ }
35305
+ const workspaceWarnings = [];
35306
+ for (const f of snap.untracked) {
35307
+ if (isChitOwned(f))
35308
+ continue;
35309
+ if (isGeneratedArtifact(f))
35310
+ workspaceWarnings.push(`untracked generated artifact: ${f}`);
35311
+ else
35312
+ changed.add(f);
35313
+ }
35314
+ return { changedFiles: [...changed], workspaceWarnings };
35315
+ }
35316
+
35241
35317
  // src/cli/converge.ts
35242
35318
  var defaultIO = {
35243
35319
  out: (s) => process.stdout.write(s),
@@ -35330,13 +35406,14 @@ function gitLines(cwd, args) {
35330
35406
  return [];
35331
35407
  }
35332
35408
  }
35333
- function gitChangedFiles(cwd) {
35334
- const all = [
35335
- ...gitLines(cwd, ["diff", "--name-only"]),
35336
- ...gitLines(cwd, ["diff", "--cached", "--name-only"]),
35337
- ...gitLines(cwd, ["ls-files", "--others", "--exclude-standard"])
35338
- ];
35339
- return [...new Set(all)];
35409
+ function gitWorkspace(cwd) {
35410
+ return classifyWorkspace({
35411
+ tracked: [
35412
+ ...gitLines(cwd, ["diff", "--name-only"]),
35413
+ ...gitLines(cwd, ["diff", "--cached", "--name-only"])
35414
+ ],
35415
+ untracked: gitLines(cwd, ["ls-files", "--others", "--exclude-standard"])
35416
+ });
35340
35417
  }
35341
35418
 
35342
35419
  class ConvergeExecuteError extends Error {
@@ -35366,17 +35443,18 @@ async function runConvergeIteration(ctx) {
35366
35443
  const reviewText = result.outputs.review ?? "";
35367
35444
  const review = parseReview(reviewText);
35368
35445
  const usage = sumTraceUsage(result.trace);
35369
- const changedFiles = gitChangedFiles(ctx.cwd);
35446
+ const { changedFiles, workspaceWarnings } = gitWorkspace(ctx.cwd);
35370
35447
  appendIteration(ctx.cwd, ctx.loopId, {
35371
35448
  implementSummary: capSummary(result.outputs.implement ?? ""),
35372
35449
  changedFiles,
35450
+ workspaceWarnings,
35373
35451
  checksRun: review.checksRun,
35374
35452
  verdict: review.verdict,
35375
35453
  findingCount: review.findingCount,
35376
35454
  decision: review.verdict,
35377
35455
  checkDurationMs: reviewDurationMs(result.trace),
35378
35456
  ...usage && { usage },
35379
- ...result.auditRunId && { detailsRef: `audit:${result.auditRunId}` }
35457
+ ...result.auditRunId && { auditRef: result.auditRunId }
35380
35458
  });
35381
35459
  const stopStatus = review.verdict === "proceed" ? "converged" : review.verdict === "block" ? "blocked" : undefined;
35382
35460
  return {
@@ -35386,6 +35464,7 @@ async function runConvergeIteration(ctx) {
35386
35464
  checksRun: review.checksRun,
35387
35465
  decision: review.verdict,
35388
35466
  changedFiles,
35467
+ workspaceWarnings,
35389
35468
  ...usage && { usage },
35390
35469
  ...result.auditRunId && { auditRunId: result.auditRunId },
35391
35470
  reviewText,
@@ -35512,8 +35591,8 @@ var CONVERGE_HELP = `chit converge --task <text> --scope <id> [options]
35512
35591
  adapter cannot enforce (emits a warning each run).
35513
35592
  Default off: such a manifest is refused before running.
35514
35593
 
35515
- Runs the implement/check loop to convergence and records it under
35516
- .chit/loops/<loopId>.jsonl. Stops at the reviewer's verdict: proceed ->
35594
+ Runs the implement/check loop to convergence and records it under chit's
35595
+ state dir (keyed by repo, not in the worktree). Stops at the reviewer's verdict: proceed ->
35517
35596
  converged, block -> blocked, else revise and retry up to the budget. An
35518
35597
  unparseable verdict is treated as block (never an implicit proceed).
35519
35598
  `;
@@ -35764,6 +35843,7 @@ async function runNextIteration(session, signal) {
35764
35843
  findingCount: iter.findingCount,
35765
35844
  checksRun: iter.checksRun,
35766
35845
  changedFiles: iter.changedFiles,
35846
+ workspaceWarnings: iter.workspaceWarnings,
35767
35847
  ...iter.usage !== undefined && { usage: iter.usage },
35768
35848
  ...iter.auditRunId !== undefined && { auditRunId: iter.auditRunId },
35769
35849
  ...session.terminalStatus !== undefined && { stopStatus: session.terminalStatus }
@@ -35842,6 +35922,9 @@ class ConvergeStore {
35842
35922
  if (this.sessions.has(loopId))
35843
35923
  this.lastTouched.set(loopId, now);
35844
35924
  }
35925
+ list() {
35926
+ return [...this.sessions.values()];
35927
+ }
35845
35928
  sweep(now) {
35846
35929
  const evicted = [];
35847
35930
  for (const [loopId, session] of this.sessions) {
@@ -35875,7 +35958,7 @@ function startRun(runId, opts) {
35875
35958
  }
35876
35959
  const needsScope = Object.values(manifest.participants).some((p) => p.session === "per_scope");
35877
35960
  if (needsScope && opts.scope === undefined) {
35878
- throw new RuntimeError(`manifest "${manifest.id}" has per_scope participant(s); a scope is required (pass scope to chit_start)`);
35961
+ throw new RuntimeError(`manifest "${manifest.id}" has per_scope participant(s); a scope is required (pass scope to chit_run_start)`);
35879
35962
  }
35880
35963
  const baseAdapters = {};
35881
35964
  for (const p of Object.values(manifest.participants)) {
@@ -36078,6 +36161,9 @@ class RunStore {
36078
36161
  if (this.runs.has(runId))
36079
36162
  this.lastTouched.set(runId, now);
36080
36163
  }
36164
+ list() {
36165
+ return [...this.runs.values()];
36166
+ }
36081
36167
  sweep(now) {
36082
36168
  const evicted = [];
36083
36169
  for (const [runId, run] of this.runs) {
@@ -36098,6 +36184,39 @@ class RunStore {
36098
36184
  }
36099
36185
  }
36100
36186
 
36187
+ // src/surfaces/mcp/status.ts
36188
+ function summarizeRunForStatus(run) {
36189
+ const complete = isComplete(run);
36190
+ return {
36191
+ run_id: run.runId,
36192
+ manifest: run.manifest.id,
36193
+ complete,
36194
+ ready: complete ? [] : readySteps(run),
36195
+ audited: run.recorder !== undefined && run.recorder.lastError === undefined
36196
+ };
36197
+ }
36198
+ function byNewest(items) {
36199
+ return [...items].sort((a, b) => b.startedAtMs - a.startedAtMs);
36200
+ }
36201
+ function recentRuns(auditStore, recentLimit) {
36202
+ if (recentLimit === 0)
36203
+ return [];
36204
+ try {
36205
+ return listAudit(auditStore, recentLimit);
36206
+ } catch {
36207
+ return [];
36208
+ }
36209
+ }
36210
+ function buildStatus(runs, convergeSessions, auditStore, recentLimit) {
36211
+ return {
36212
+ active: {
36213
+ runs: byNewest(runs.list()).map(summarizeRunForStatus),
36214
+ loops: byNewest(convergeSessions.list()).map(describeConverge)
36215
+ },
36216
+ recent: recentRuns(auditStore, recentLimit)
36217
+ };
36218
+ }
36219
+
36101
36220
  // src/surfaces/mcp/server.ts
36102
36221
  var runs = new RunStore;
36103
36222
  var controllers = new Map;
@@ -36140,7 +36259,7 @@ function describeRun(run) {
36140
36259
  audit: run.recorder && run.recorder.lastError === undefined ? { runId: run.runId } : undefined
36141
36260
  };
36142
36261
  }
36143
- server.registerTool("chit_start", {
36262
+ server.registerTool("chit_run_start", {
36144
36263
  description: "Start a stepwise run of a chit manifest. Returns a run_id and the steps ready to run. chit owns the declared order; only ready steps can be run. Then call chit_run_step for each ready step.",
36145
36264
  inputSchema: {
36146
36265
  manifest_path: exports_external.string().describe("Path to the manifest .json (absolute, or relative to cwd)"),
@@ -36176,7 +36295,7 @@ server.registerTool("chit_start", {
36176
36295
  runs.add(run, Date.now());
36177
36296
  return jsonResult(describeRun(run));
36178
36297
  });
36179
- server.registerTool("chit_next", {
36298
+ server.registerTool("chit_run_next", {
36180
36299
  description: "List the steps ready to run next for a run, or report that the run is complete.",
36181
36300
  inputSchema: { run_id: exports_external.string() }
36182
36301
  }, async ({ run_id }) => {
@@ -36235,7 +36354,7 @@ server.registerTool("chit_run_step", {
36235
36354
  runs.touch(run_id, Date.now());
36236
36355
  }
36237
36356
  });
36238
- server.registerTool("chit_cancel", {
36357
+ server.registerTool("chit_run_cancel", {
36239
36358
  description: "Cancel a step that is currently running: aborts its controller, which kills the agent's child process and settles the step as cancelled (terminal, blocks dependents). Returns cancelled:true if it stopped a running step, or a reason (already_done | not_running) otherwise. Use after interrupting a long step.",
36240
36359
  inputSchema: { run_id: exports_external.string(), step_id: exports_external.string() }
36241
36360
  }, async ({ run_id, step_id }) => {
@@ -36252,7 +36371,7 @@ server.registerTool("chit_cancel", {
36252
36371
  ...describeRun(run)
36253
36372
  });
36254
36373
  });
36255
- server.registerTool("chit_trace", {
36374
+ server.registerTool("chit_run_trace", {
36256
36375
  description: "Return the transcript of a run so far: each step's status, participant, agent, elapsed, and output.",
36257
36376
  inputSchema: { run_id: exports_external.string() }
36258
36377
  }, async ({ run_id }) => {
@@ -36310,7 +36429,7 @@ Pass allow_unenforced_permissions=true to run anyway.`
36310
36429
  return { ok: true, execute: buildExecute(manifest, registry3, scope, cwd), warnings };
36311
36430
  }
36312
36431
  server.registerTool("chit_converge_start", {
36313
- description: "Start an autonomous converge loop (a write-capable implementer slices the task, a read-only reviewer checks the diff) driven one iteration at a time. Returns a loop_id and the next action. Then call chit_converge_next per iteration. Records to .chit/loops/<loop_id>.jsonl, identical to `chit converge`.",
36432
+ description: "Start an autonomous converge loop (a write-capable implementer slices the task, a read-only reviewer checks the diff) driven one iteration at a time. Returns a loop_id and the next action. Then call chit_converge_next per iteration. Records the loop under chit's state dir (keyed by repo), identical to `chit converge`.",
36314
36433
  inputSchema: {
36315
36434
  task: exports_external.string().describe("The slice to converge on"),
36316
36435
  scope: exports_external.string().describe("Session scope id; both agents keep their thread across iterations"),
@@ -36415,6 +36534,7 @@ server.registerTool("chit_converge_next", {
36415
36534
  findingCount: result.findingCount,
36416
36535
  checksRun: result.checksRun,
36417
36536
  changedFiles: result.changedFiles,
36537
+ workspaceWarnings: result.workspaceWarnings,
36418
36538
  ...result.usage && { usage: result.usage },
36419
36539
  ...result.auditRunId && { auditRunId: result.auditRunId },
36420
36540
  ...result.stopStatus && { stopStatus: result.stopStatus },
@@ -36480,6 +36600,14 @@ server.registerTool("chit_audit_show", {
36480
36600
  return errorResult(e.message);
36481
36601
  }
36482
36602
  });
36603
+ server.registerTool("chit_status", {
36604
+ description: "Operator overview: the stepwise runs and converge loops live in THIS server right now (each loop with its status and next action), plus a compact list of recently audited runs (newest first). Read-only; answers 'what is active and what should I do next?'. Active state is per-session (a new session starts empty, and idle runs are evicted); recent state is durable. Drill into one item with chit_converge_status/chit_run_trace, or chit_audit_show for a run's receipt.",
36605
+ inputSchema: {
36606
+ recent_limit: exports_external.number().int().min(0).default(5).describe("How many recently audited runs to include (newest first). Default 5; 0 for none.")
36607
+ }
36608
+ }, async ({ recent_limit }) => {
36609
+ return jsonResult(buildStatus(runs, convergeSessions, auditStore, recent_limit));
36610
+ });
36483
36611
  async function startMcpServer() {
36484
36612
  await server.connect(new StdioServerTransport);
36485
36613
  }
@@ -36710,7 +36838,7 @@ ${AUDIT_HELP}`);
36710
36838
  // src/cli/doctor.ts
36711
36839
  import { randomUUID as randomUUID3 } from "crypto";
36712
36840
  import { mkdirSync as mkdirSync5, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
36713
- import { join as join7 } from "path";
36841
+ import { join as join8 } from "path";
36714
36842
  var defaultIO3 = {
36715
36843
  out: (s) => process.stdout.write(s),
36716
36844
  err: (s) => process.stderr.write(s)
@@ -36779,7 +36907,7 @@ function checkGitRepo(deps) {
36779
36907
  function checkAuditDir(deps) {
36780
36908
  try {
36781
36909
  mkdirSync5(deps.auditDir, { recursive: true });
36782
- const probeFile = join7(deps.auditDir, `.doctor-${randomUUID3()}`);
36910
+ const probeFile = join8(deps.auditDir, `.doctor-${randomUUID3()}`);
36783
36911
  writeFileSync5(probeFile, "ok");
36784
36912
  rmSync5(probeFile);
36785
36913
  return { name: "audit dir", status: "pass", detail: `writable (${deps.auditDir})` };
@@ -36912,12 +37040,13 @@ var ALLOWED = {
36912
37040
  "loop-id",
36913
37041
  "summary",
36914
37042
  "changed-files",
37043
+ "workspace-warnings",
36915
37044
  "checks-run",
36916
37045
  "verdict",
36917
37046
  "finding-count",
36918
37047
  "decision",
36919
37048
  "duration-ms",
36920
- "details-ref"
37049
+ "audit-ref"
36921
37050
  ],
36922
37051
  bools: []
36923
37052
  },
@@ -36929,7 +37058,8 @@ var LOOP_LOG_HELP = `chit loop-log <start|append|stop|show> [flags]
36929
37058
  start --scope <s> --task <t> --max-iterations <n> [--cwd <dir>] [--loop-id <id>] [--force]
36930
37059
  append --loop-id <id> --summary <t> --changed-files <json> --checks-run <t>
36931
37060
  --verdict <proceed|revise|block> --finding-count <n>
36932
- --decision <proceed|revise|block> --duration-ms <n> [--details-ref <r>] [--cwd <dir>]
37061
+ --decision <proceed|revise|block> --duration-ms <n>
37062
+ [--workspace-warnings <json>] [--audit-ref <r>] [--cwd <dir>]
36933
37063
  stop --loop-id <id> --status <converged|blocked|max-iterations|needs-decision> --reason <t> [--cwd <dir>]
36934
37064
  show --loop-id <id> [--json] [--cwd <dir>]
36935
37065
 
@@ -36979,15 +37109,15 @@ function intFlag(p, key, verb) {
36979
37109
  }
36980
37110
  return n;
36981
37111
  }
36982
- function parseChangedFiles(raw) {
37112
+ function parseStringArray(raw, flag) {
36983
37113
  let parsed;
36984
37114
  try {
36985
37115
  parsed = JSON.parse(raw);
36986
37116
  } catch {
36987
- throw new UsageError3("--changed-files must be a JSON array of strings");
37117
+ throw new UsageError3(`--${flag} must be a JSON array of strings`);
36988
37118
  }
36989
37119
  if (!Array.isArray(parsed) || parsed.some((e) => typeof e !== "string")) {
36990
- throw new UsageError3("--changed-files must be a JSON array of strings");
37120
+ throw new UsageError3(`--${flag} must be a JSON array of strings`);
36991
37121
  }
36992
37122
  return parsed;
36993
37123
  }
@@ -37006,6 +37136,11 @@ function renderLoop(records) {
37006
37136
  lines.push(` ${r.n}. ${r.implementSummary}`);
37007
37137
  lines.push(` ${r.changedFiles.length} files \xB7 check ${r.verdict.toUpperCase()} \xB7 ` + `${Math.round(r.checkDurationMs / 1000)}s \xB7 ${r.findingCount} findings`);
37008
37138
  lines.push(` decide ${r.decision}`);
37139
+ if (r.workspaceWarnings && r.workspaceWarnings.length > 0) {
37140
+ lines.push(` workspace: ${r.workspaceWarnings.length} warning(s)`);
37141
+ for (const w of r.workspaceWarnings)
37142
+ lines.push(` - ${w}`);
37143
+ }
37009
37144
  } else if (r.type === "stop") {
37010
37145
  lines.push("");
37011
37146
  lines.push(` stopped: ${r.status} (${r.reason}) \xB7 ${r.iterations} iters \xB7 ` + `${Math.round(r.totalElapsedMs / 1000)}s`);
@@ -37037,15 +37172,19 @@ function runLoopLog(argv, io = defaultIO4) {
37037
37172
  return 0;
37038
37173
  }
37039
37174
  if (verb === "append") {
37175
+ const warningsRaw = p.flags["workspace-warnings"];
37040
37176
  const res = appendIteration(cwd, req(p, "loop-id", verb), {
37041
37177
  implementSummary: req(p, "summary", verb),
37042
- changedFiles: parseChangedFiles(req(p, "changed-files", verb)),
37178
+ changedFiles: parseStringArray(req(p, "changed-files", verb), "changed-files"),
37179
+ ...warningsRaw !== undefined && {
37180
+ workspaceWarnings: parseStringArray(warningsRaw, "workspace-warnings")
37181
+ },
37043
37182
  checksRun: req(p, "checks-run", verb),
37044
37183
  verdict: req(p, "verdict", verb),
37045
37184
  findingCount: intFlag(p, "finding-count", verb),
37046
37185
  decision: req(p, "decision", verb),
37047
37186
  checkDurationMs: intFlag(p, "duration-ms", verb),
37048
- detailsRef: p.flags["details-ref"]
37187
+ auditRef: p.flags["audit-ref"]
37049
37188
  });
37050
37189
  io.out(`${JSON.stringify(res)}
37051
37190
  `);
@@ -37643,7 +37782,7 @@ ${HELP}`);
37643
37782
  `);
37644
37783
  return 2;
37645
37784
  }
37646
- const outputDir = args.outputDir ?? join12(homedir6(), ".claude", "skills");
37785
+ const outputDir = args.outputDir ?? join13(homedir7(), ".claude", "skills");
37647
37786
  const runtimePath = args.runtimePath ?? defaultRuntimePath();
37648
37787
  try {
37649
37788
  const result = installClaudeSkill({
@@ -37810,7 +37949,8 @@ async function runStudio(args) {
37810
37949
  cwd: process.cwd(),
37811
37950
  explicitPath: args.manifestPath,
37812
37951
  registry: registry3,
37813
- lifecycle: buildStudioLifecycle()
37952
+ lifecycle: buildStudioLifecycle(),
37953
+ loopsDir: loopLogDir(process.cwd())
37814
37954
  });
37815
37955
  } catch (e) {
37816
37956
  if (e instanceof PathError2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chit-run/cli",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Versioned, cross-vendor agent routines with an audit trail. Stop being the glue between your agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",