@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.
- package/dist/chit.js +250 -110
- 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.
|
|
992
|
-
rec.
|
|
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
|
|
9968
|
-
import { join as
|
|
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 ||
|
|
9971
|
-
return
|
|
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 =
|
|
9991
|
-
const eventsPath =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
10387
|
-
const
|
|
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
|
|
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 =
|
|
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.
|
|
10600
|
+
return c.json(listLoops(opts.loopsDir));
|
|
10593
10601
|
});
|
|
10594
10602
|
app.get("/api/loops/:loopId", (c) => {
|
|
10595
|
-
const result = readLoop2(opts.
|
|
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 =
|
|
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
|
|
10695
|
-
import { basename as basename3, dirname as dirname2, join as
|
|
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
|
|
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
|
|
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
|
|
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
|
|
11902
|
-
import { dirname, join as
|
|
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 ||
|
|
11920
|
-
return
|
|
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 =
|
|
12016
|
-
return
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
12097
|
-
const manifestPath =
|
|
12098
|
-
const markerPath =
|
|
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:
|
|
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
|
|
12186
|
-
import { join as
|
|
12232
|
+
import { homedir as homedir5 } from "os";
|
|
12233
|
+
import { join as join6 } from "path";
|
|
12187
12234
|
function defaultSkillsDir() {
|
|
12188
|
-
return
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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.
|
|
35138
|
-
rec.
|
|
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
|
|
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
|
|
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
|
|
35334
|
-
|
|
35335
|
-
|
|
35336
|
-
|
|
35337
|
-
|
|
35338
|
-
|
|
35339
|
-
|
|
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 =
|
|
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 && {
|
|
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
|
-
.
|
|
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
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
"
|
|
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>
|
|
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
|
|
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(
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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 ??
|
|
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) {
|