@hacksmith/doraval 0.2.45 → 0.2.47

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/bin/doraval.js CHANGED
@@ -599,7 +599,7 @@ var init_dist = __esm(() => {
599
599
  var require_package = __commonJS((exports, module) => {
600
600
  module.exports = {
601
601
  name: "@hacksmith/doraval",
602
- version: "0.2.45",
602
+ version: "0.2.47",
603
603
  author: "Saif",
604
604
  repository: {
605
605
  type: "git",
@@ -612,7 +612,7 @@ var require_package = __commonJS((exports, module) => {
612
612
  doraval: "bin/doraval-wrapper.js",
613
613
  dora: "bin/doraval-wrapper.js"
614
614
  },
615
- description: "The context engineering toolkit for coding agents",
615
+ description: "The context engineering toolkit for coding agent orchestrators",
616
616
  engines: {
617
617
  bun: ">=1.2.0",
618
618
  node: ">=14.18.0"
@@ -873,7 +873,8 @@ var init_skill_validate = __esm(() => {
873
873
  "agent",
874
874
  "hooks",
875
875
  "paths",
876
- "shell"
876
+ "shell",
877
+ "expected-eval"
877
878
  ]);
878
879
  SUPPORTING_DIRS = ["references", "scripts", "assets", "examples"];
879
880
  OPTIONAL_DIRS = ["references", "scripts", "assets"];
@@ -1164,85 +1165,452 @@ Check that the path points to a skill directory containing SKILL.md.`);
1164
1165
  });
1165
1166
  });
1166
1167
 
1167
- // src/cli/commands/judge.ts
1168
- var exports_judge = {};
1169
- __export(exports_judge, {
1170
- default: () => judge_default
1171
- });
1172
- var judge_default;
1173
- var init_judge = __esm(() => {
1174
- init_dist();
1175
- init_out();
1176
- judge_default = defineCommand({
1177
- meta: {
1178
- name: "judge",
1179
- description: "AI-driven qualitative assessment of a skill"
1168
+ // src/core/session-parse.ts
1169
+ function extractUserText(message) {
1170
+ if (typeof message === "string")
1171
+ return message.trim() || null;
1172
+ if (Array.isArray(message)) {
1173
+ for (const block of message) {
1174
+ if (block && typeof block === "object" && block.type === "text") {
1175
+ const text = block.text;
1176
+ if (typeof text === "string" && text.trim())
1177
+ return text.trim();
1178
+ }
1179
+ }
1180
+ }
1181
+ return null;
1182
+ }
1183
+ function parseSession(jsonlText) {
1184
+ const lines = jsonlText.split(`
1185
+ `).filter((l) => l.trim());
1186
+ const messages = [];
1187
+ for (const line of lines) {
1188
+ try {
1189
+ messages.push(JSON.parse(line));
1190
+ } catch {}
1191
+ }
1192
+ let sessionId = "";
1193
+ let sessionTitle;
1194
+ let model = "unknown";
1195
+ let agent = "claude-code";
1196
+ let cwd = "";
1197
+ let gitBranch;
1198
+ let durationMs;
1199
+ const toolCalls = [];
1200
+ const userMessages = [];
1201
+ let toolIndex = 0;
1202
+ for (const msg of messages) {
1203
+ if (!sessionId && typeof msg.sessionId === "string")
1204
+ sessionId = msg.sessionId;
1205
+ if (!cwd && typeof msg.cwd === "string")
1206
+ cwd = msg.cwd;
1207
+ if (!gitBranch && typeof msg.gitBranch === "string")
1208
+ gitBranch = msg.gitBranch;
1209
+ if (msg.type === "ai-title") {
1210
+ sessionTitle = typeof msg.aiTitle === "string" ? msg.aiTitle : undefined;
1211
+ }
1212
+ if (msg.type === "system") {
1213
+ if (typeof msg.durationMs === "number")
1214
+ durationMs = msg.durationMs;
1215
+ }
1216
+ if (msg.type === "assistant") {
1217
+ const message = msg.message;
1218
+ if (!message)
1219
+ continue;
1220
+ if (typeof message.model === "string" && message.model !== "<synthetic>") {
1221
+ model = message.model;
1222
+ agent = typeof msg.entrypoint === "string" ? msg.entrypoint === "cli" ? "claude-code" : msg.entrypoint : "claude-code";
1223
+ }
1224
+ const content = Array.isArray(message.content) ? message.content : [];
1225
+ for (const block of content) {
1226
+ if (!block || typeof block !== "object")
1227
+ continue;
1228
+ const b = block;
1229
+ if (b.type === "tool_use" && typeof b.name === "string") {
1230
+ const input = b.input ?? {};
1231
+ toolCalls.push({
1232
+ name: b.name,
1233
+ input,
1234
+ timestamp: typeof msg.timestamp === "string" ? msg.timestamp : "",
1235
+ index: toolIndex++
1236
+ });
1237
+ }
1238
+ }
1239
+ }
1240
+ if (msg.type === "user") {
1241
+ const isAttachment = typeof msg.attachment !== "undefined";
1242
+ if (isAttachment)
1243
+ continue;
1244
+ const message = msg.message;
1245
+ if (!message)
1246
+ continue;
1247
+ const text = extractUserText(message.content);
1248
+ if (text)
1249
+ userMessages.push(text);
1250
+ }
1251
+ }
1252
+ const skillsInvoked = toolCalls.filter((t) => t.name === "Skill").map((t) => typeof t.input.skill === "string" ? t.input.skill : "unknown").filter((s, i, arr) => arr.indexOf(s) === i);
1253
+ const toolCallCounts = {};
1254
+ for (const t of toolCalls) {
1255
+ toolCallCounts[t.name] = (toolCallCounts[t.name] ?? 0) + 1;
1256
+ }
1257
+ return {
1258
+ sessionId,
1259
+ sessionTitle,
1260
+ model,
1261
+ agent,
1262
+ cwd,
1263
+ gitBranch,
1264
+ toolCalls,
1265
+ toolCallCounts,
1266
+ skillsInvoked,
1267
+ userMessages,
1268
+ userTurnCount: userMessages.length,
1269
+ durationMs
1270
+ };
1271
+ }
1272
+ function truncateToolCalls(calls, maxCalls) {
1273
+ if (calls.length <= maxCalls)
1274
+ return calls;
1275
+ const skillCalls = calls.filter((c) => c.name === "Skill");
1276
+ const nonSkillCalls = calls.filter((c) => c.name !== "Skill");
1277
+ const budget = Math.max(0, maxCalls - skillCalls.length);
1278
+ if (budget === 0)
1279
+ return skillCalls;
1280
+ const head = nonSkillCalls.slice(0, Math.ceil(budget / 2));
1281
+ const tail = nonSkillCalls.slice(-Math.floor(budget / 2));
1282
+ const headSet = new Set(head.map((c) => c.index));
1283
+ const tailSet = new Set(tail.map((c) => c.index));
1284
+ const selected = new Set([...headSet, ...tailSet, ...skillCalls.map((c) => c.index)]);
1285
+ return calls.filter((c) => selected.has(c.index));
1286
+ }
1287
+
1288
+ // src/core/session-adapters.ts
1289
+ import { existsSync as existsSync3, readdirSync, readFileSync, statSync } from "fs";
1290
+ import { homedir } from "os";
1291
+ import { join } from "path";
1292
+ function cwdToProjectHash(cwd) {
1293
+ return cwd.replace(/\//g, "-");
1294
+ }
1295
+ function getAdapter() {
1296
+ return ADAPTERS.find((a) => a.detect()) ?? null;
1297
+ }
1298
+ var claudeCodeAdapter, ADAPTERS;
1299
+ var init_session_adapters = __esm(() => {
1300
+ claudeCodeAdapter = {
1301
+ agent: "claude-code",
1302
+ detect() {
1303
+ return existsSync3(join(homedir(), ".claude"));
1180
1304
  },
1181
- args: {
1182
- path: {
1183
- type: "positional",
1184
- description: "Path to skill directory",
1185
- required: true
1186
- },
1187
- for: {
1188
- type: "string",
1189
- description: 'Target a provider ("claude") or specific validator ("claude:skill")'
1190
- },
1191
- format: {
1192
- type: "string",
1193
- alias: "f",
1194
- description: "Output format (json or table)",
1195
- default: "table"
1196
- },
1197
- verbose: {
1198
- type: "boolean",
1199
- alias: "v",
1200
- description: "Show detailed diagnostics",
1201
- default: false
1305
+ findLatestSession(cwd) {
1306
+ const hash = cwdToProjectHash(cwd);
1307
+ const dir = join(homedir(), ".claude", "projects", hash);
1308
+ if (!existsSync3(dir))
1309
+ return null;
1310
+ const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => ({ name: f, path: join(dir, f), mtime: statSync(join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
1311
+ for (const file of files) {
1312
+ const content = readFileSync(file.path, "utf8");
1313
+ if (content.includes('"type":"assistant"') || content.includes('"type": "assistant"')) {
1314
+ return file.path;
1315
+ }
1316
+ }
1317
+ return files[0]?.path ?? null;
1318
+ },
1319
+ listRecentSessions(cwd, limit = 10) {
1320
+ const hash = cwdToProjectHash(cwd);
1321
+ const dir = join(homedir(), ".claude", "projects", hash);
1322
+ if (!existsSync3(dir))
1323
+ return [];
1324
+ const allFiles = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => ({ name: f, path: join(dir, f), mtime: statSync(join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
1325
+ const results = [];
1326
+ for (const file of allFiles) {
1327
+ try {
1328
+ const text = readFileSync(file.path, "utf8");
1329
+ if (!text.includes('"type":"assistant"') && !text.includes('"type": "assistant"')) {
1330
+ continue;
1331
+ }
1332
+ const prim = parseSession(text);
1333
+ results.push({
1334
+ path: file.path,
1335
+ mtime: file.mtime,
1336
+ title: prim.sessionTitle,
1337
+ skillCount: prim.skillsInvoked.length
1338
+ });
1339
+ if (results.length >= limit)
1340
+ break;
1341
+ } catch {}
1202
1342
  }
1343
+ return results;
1203
1344
  },
1204
- async run({ args }) {
1205
- ui.heading("doraval skill judge \u2014 AI-driven assessment");
1206
- ui.info(` Path: ${args.path}
1345
+ parse(path) {
1346
+ const text = readFileSync(path, "utf8");
1347
+ return parseSession(text);
1348
+ }
1349
+ };
1350
+ ADAPTERS = [claudeCodeAdapter];
1351
+ });
1352
+
1353
+ // src/core/agent-invoke.ts
1354
+ var {spawnSync } = globalThis.Bun;
1355
+ function buildAgentArgv(template, promptText) {
1356
+ const marker = "__DORA_PROMPT__";
1357
+ const substituted = template.replace("{{prompt}}", marker);
1358
+ const rawParts = substituted.split(/\s+/).filter(Boolean);
1359
+ return rawParts.map((part) => {
1360
+ let cleaned = part;
1361
+ if (cleaned.startsWith('"') && cleaned.endsWith('"'))
1362
+ cleaned = cleaned.slice(1, -1);
1363
+ if (cleaned.startsWith("'") && cleaned.endsWith("'"))
1364
+ cleaned = cleaned.slice(1, -1);
1365
+ return cleaned === marker ? promptText : cleaned;
1366
+ });
1367
+ }
1368
+ function extractCandidates(text) {
1369
+ let cleaned = text.replace(/```(?:json)?\s*([\s\S]*?)\s*```/gi, "$1").trim();
1370
+ const candidates = [];
1371
+ const allMatches = cleaned.match(/\{[\s\S]*?\}(?=\s*(?:\{|$))/g) ?? [];
1372
+ const fullMatch = cleaned.match(/\{[\s\S]*\}/);
1373
+ if (fullMatch) {
1374
+ try {
1375
+ candidates.push(JSON.parse(fullMatch[0]));
1376
+ } catch {}
1377
+ }
1378
+ for (const m of allMatches) {
1379
+ try {
1380
+ candidates.push(JSON.parse(m));
1381
+ } catch {}
1382
+ }
1383
+ if (cleaned.startsWith("{") || cleaned.startsWith("[")) {
1384
+ try {
1385
+ const direct = JSON.parse(cleaned);
1386
+ if (direct && typeof direct === "object")
1387
+ candidates.push(direct);
1388
+ } catch {}
1389
+ }
1390
+ const unwrapped = [];
1391
+ for (const c of candidates) {
1392
+ if (c.result) {
1393
+ let inner = c.result;
1394
+ if (typeof inner === "string") {
1395
+ try {
1396
+ inner = JSON.parse(inner);
1397
+ } catch {}
1398
+ }
1399
+ if (inner && typeof inner === "object")
1400
+ unwrapped.push(inner);
1401
+ }
1402
+ unwrapped.push(c);
1403
+ }
1404
+ return unwrapped;
1405
+ }
1406
+ async function invokeAgent(promptText, agentCfg, expectedKeys) {
1407
+ const template = agentCfg.prompt_template ?? '-p "{{prompt}}" --output-format json --bare';
1408
+ const extraArgs = buildAgentArgv(template, promptText);
1409
+ const shortTemplate = template.slice(0, 80);
1410
+ ui.write(` ${import_picocolors4.default.dim(`\u2192 ${agentCfg.command} ${shortTemplate}...`)}`);
1411
+ let result;
1412
+ try {
1413
+ result = spawnSync([agentCfg.command, ...extraArgs], {
1414
+ stdout: "pipe",
1415
+ stderr: "pipe",
1416
+ env: { ...process.env }
1417
+ });
1418
+ } catch (e) {
1419
+ ui.write(` ${import_picocolors4.default.yellow("\u26A0")} Failed to spawn ${agentCfg.command}: ${e.message}`);
1420
+ return null;
1421
+ }
1422
+ const stdout = result.stdout.toString().trim();
1423
+ const stderr = result.stderr.toString().trim();
1424
+ if (result.exitCode !== 0) {
1425
+ ui.write(` ${import_picocolors4.default.yellow("\u26A0")} Agent exited with code ${result.exitCode}.`);
1426
+ const out = stdout.replace(/sk-[a-zA-Z0-9_-]+/g, "sk-REDACTED").slice(0, 800);
1427
+ const err = stderr.replace(/sk-[a-zA-Z0-9_-]+/g, "sk-REDACTED").slice(0, 800);
1428
+ if (err)
1429
+ ui.write(` stderr: ${err}`);
1430
+ if (out)
1431
+ ui.write(` stdout: ${out}`);
1432
+ if (!err && !out)
1433
+ ui.write(` (no output captured)`);
1434
+ return null;
1435
+ }
1436
+ let cleaned = stdout.replace(/```(?:json)?\s*([\s\S]*?)\s*```/gi, "$1").trim();
1437
+ const unwrapped = extractCandidates(cleaned);
1438
+ for (const c of unwrapped) {
1439
+ if (expectedKeys.some((k) => (k in c)))
1440
+ return c;
1441
+ }
1442
+ if (unwrapped[0])
1443
+ return unwrapped[0];
1444
+ ui.write(` ${import_picocolors4.default.yellow("\u26A0")} Agent produced no usable JSON. stdout (700 chars): ${stdout.slice(0, 700)}`);
1445
+ return null;
1446
+ }
1447
+ var import_picocolors4;
1448
+ var init_agent_invoke = __esm(() => {
1449
+ init_out();
1450
+ import_picocolors4 = __toESM(require_picocolors(), 1);
1451
+ });
1452
+
1453
+ // src/core/session-eval.ts
1454
+ function toolCallSummary(call) {
1455
+ const inputStr = JSON.stringify(call.input).slice(0, 100);
1456
+ return `${call.name}: ${inputStr}`;
1457
+ }
1458
+ function buildEvalPrompt(primitives, skillContent, maxToolCalls) {
1459
+ const truncated = truncateToolCalls(primitives.toolCalls, maxToolCalls);
1460
+ const wasTruncated = truncated.length < primitives.toolCalls.length;
1461
+ const toolCallLines = truncated.map((c) => toolCallSummary(c)).join(`
1207
1462
  `);
1208
- ui.warn(`Not yet implemented. This command will send the skill to an LLM for qualitative review (clarity, completeness, effectiveness).
1463
+ const truncationNote = wasTruncated ? `
1464
+ [truncated: showing ${truncated.length} of ${primitives.toolCalls.length} total tool calls]` : "";
1465
+ const userMsgLines = primitives.userMessages.slice(0, 5).join(`
1466
+ ---
1209
1467
  `);
1210
- process.exit(2);
1211
- }
1468
+ return `You are evaluating whether a coding agent followed a skill's instructions during a real session.
1469
+
1470
+ SKILL CONTENT:
1471
+ ${skillContent}
1472
+
1473
+ TOOL CALL SEQUENCE (ordered):
1474
+ ${toolCallLines}${truncationNote}
1475
+
1476
+ USER MESSAGES (first 5, for familiarity inference):
1477
+ ${userMsgLines}
1478
+
1479
+ TASKS:
1480
+ 1. Extract the key actions the skill instructs (e.g. "invoke X tool", "fetch N URLs", "create tasks for each item").
1481
+ 2. For each expected action, check if the tool call sequence shows it happened.
1482
+ 3. Infer user familiarity 1-10 from the user messages:
1483
+ - 1-3: vague/brief prompts, many typos, relies on agent to figure things out
1484
+ - 4-6: clear intent but informal, some corrections
1485
+ - 7-10: precise, technical, specific file paths/function names
1486
+ 4. Determine closure:
1487
+ - "1-shot": \u22642 user turns after first Skill invocation
1488
+ - "multi-turn": >2 user turns after first Skill invocation
1489
+ - "incomplete": no end_turn signal or session appears cut off
1490
+ 5. Overall verdict: "PASS" if all critical instructions were followed, "FAIL" if any critical instruction was missed.
1491
+
1492
+ CRITICAL: Output *ONLY* the JSON object. No markdown fences, no explanations, no text before or after it. The first character of your response must be '{' and the last must be '}'.
1493
+
1494
+ Return ONLY a valid JSON object with exactly these keys:
1495
+ {
1496
+ "userFamiliarity": <number 1-10>,
1497
+ "userFamiliarityReason": "<one sentence>",
1498
+ "closure": "<1-shot|multi-turn|incomplete>",
1499
+ "userTurnsAfterSkill": <number>,
1500
+ "verdict": "<PASS|FAIL>",
1501
+ "verdictReason": "<one sentence>",
1502
+ "checklist": [
1503
+ { "instruction": "<what skill said>", "pass": <true|false>, "detail": "<optional>" }
1504
+ ]
1505
+ }`;
1506
+ }
1507
+ function makeUnknownResult(primitives, skillName, reason) {
1508
+ return {
1509
+ schemaVersion: 1,
1510
+ sessionId: primitives.sessionId,
1511
+ sessionTitle: primitives.sessionTitle,
1512
+ timestamp: new Date().toISOString(),
1513
+ agent: primitives.agent,
1514
+ model: primitives.model,
1515
+ skill: skillName,
1516
+ userFamiliarity: 0,
1517
+ userFamiliarityReason: "",
1518
+ closure: "incomplete",
1519
+ userTurnsAfterSkill: 0,
1520
+ skillsInvoked: primitives.skillsInvoked,
1521
+ toolCallCounts: primitives.toolCallCounts,
1522
+ verdict: "UNKNOWN",
1523
+ verdictReason: reason,
1524
+ checklist: []
1525
+ };
1526
+ }
1527
+ async function runEval(primitives, skillName, skillContent, agentCfg, evalCfg) {
1528
+ const prompt = buildEvalPrompt(primitives, skillContent, evalCfg.max_tool_calls);
1529
+ const raw = await invokeAgent(prompt, agentCfg, ["verdict", "checklist"]);
1530
+ if (!raw) {
1531
+ return makeUnknownResult(primitives, skillName, "LLM call failed \u2014 no response");
1532
+ }
1533
+ if (typeof raw.verdict !== "string" || !Array.isArray(raw.checklist)) {
1534
+ ui.write(` ${import_picocolors5.default.yellow("\u26A0")} Received from agent (first 800 chars): ${JSON.stringify(raw).slice(0, 800)}`);
1535
+ return makeUnknownResult(primitives, skillName, "LLM returned malformed response");
1536
+ }
1537
+ const checklist = raw.checklist.map((item) => {
1538
+ const i = item;
1539
+ return {
1540
+ instruction: typeof i.instruction === "string" ? i.instruction : "unknown",
1541
+ pass: i.pass === true,
1542
+ detail: typeof i.detail === "string" ? i.detail : undefined
1543
+ };
1212
1544
  });
1545
+ return {
1546
+ schemaVersion: 1,
1547
+ sessionId: primitives.sessionId,
1548
+ sessionTitle: primitives.sessionTitle,
1549
+ timestamp: new Date().toISOString(),
1550
+ agent: primitives.agent,
1551
+ model: primitives.model,
1552
+ skill: skillName,
1553
+ userFamiliarity: typeof raw.userFamiliarity === "number" ? raw.userFamiliarity : 0,
1554
+ userFamiliarityReason: typeof raw.userFamiliarityReason === "string" ? raw.userFamiliarityReason : "",
1555
+ closure: raw.closure ?? "incomplete",
1556
+ userTurnsAfterSkill: typeof raw.userTurnsAfterSkill === "number" ? raw.userTurnsAfterSkill : 0,
1557
+ skillsInvoked: primitives.skillsInvoked,
1558
+ toolCallCounts: primitives.toolCallCounts,
1559
+ verdict: raw.verdict === "PASS" ? "PASS" : raw.verdict === "FAIL" ? "FAIL" : "UNKNOWN",
1560
+ verdictReason: typeof raw.verdictReason === "string" ? raw.verdictReason : "",
1561
+ checklist
1562
+ };
1563
+ }
1564
+ var import_picocolors5;
1565
+ var init_session_eval = __esm(() => {
1566
+ init_agent_invoke();
1567
+ init_out();
1568
+ import_picocolors5 = __toESM(require_picocolors(), 1);
1213
1569
  });
1214
1570
 
1215
1571
  // src/core/journal-config.ts
1216
- import { existsSync as existsSync3, mkdirSync } from "fs";
1217
- import { homedir } from "os";
1218
- import { join } from "path";
1572
+ import { existsSync as existsSync4, mkdirSync } from "fs";
1573
+ import { homedir as homedir2 } from "os";
1574
+ import { join as join2 } from "path";
1219
1575
  var {YAML: YAML2 } = globalThis.Bun;
1220
1576
  function getDoravalDir() {
1221
- return process.env.DORAVAL_HOME ?? join(homedir(), ".doraval");
1577
+ return process.env.DORAVAL_HOME ?? join2(homedir2(), ".doraval");
1222
1578
  }
1223
1579
  function getConfigPath() {
1224
- return join(getDoravalDir(), "config.yml");
1580
+ return join2(getDoravalDir(), "config.yml");
1225
1581
  }
1226
1582
  function getJournalsDir() {
1227
- return join(getDoravalDir(), "journals");
1583
+ return join2(getDoravalDir(), "journals");
1228
1584
  }
1229
1585
  function getPendingDir() {
1230
- return join(getDoravalDir(), "pending");
1586
+ return join2(getDoravalDir(), "pending");
1231
1587
  }
1232
1588
  function getPendingProjectDir(project) {
1233
- return join(getPendingDir(), project);
1589
+ return join2(getPendingDir(), project);
1590
+ }
1591
+ function getEvalsDir() {
1592
+ return join2(getDoravalDir(), "evals");
1593
+ }
1594
+ function getEvalConfig(config) {
1595
+ const defaults = {
1596
+ model: "",
1597
+ api_key: undefined,
1598
+ max_tool_calls: 200,
1599
+ save_history: true
1600
+ };
1601
+ return { ...defaults, ...config?.eval ?? {} };
1234
1602
  }
1235
1603
  function ensureDoravalDirs() {
1236
1604
  const base = getDoravalDir();
1237
- for (const dir of [base, getJournalsDir(), getPendingDir()]) {
1238
- if (!existsSync3(dir)) {
1605
+ for (const dir of [base, getJournalsDir(), getPendingDir(), getEvalsDir()]) {
1606
+ if (!existsSync4(dir)) {
1239
1607
  mkdirSync(dir, { recursive: true });
1240
1608
  }
1241
1609
  }
1242
1610
  }
1243
1611
  async function readConfig() {
1244
1612
  const path = getConfigPath();
1245
- if (!existsSync3(path))
1613
+ if (!existsSync4(path))
1246
1614
  return null;
1247
1615
  const raw = await Bun.file(path).text();
1248
1616
  return YAML2.parse(raw);
@@ -1281,10 +1649,307 @@ function sanitizeProjectName(name) {
1281
1649
  }
1282
1650
  var init_journal_config = () => {};
1283
1651
 
1652
+ // src/cli/prompt.ts
1653
+ function prompt(label, fallback) {
1654
+ process.stderr.write(`${label} ${import_picocolors6.default.dim(`(${fallback})`)} `);
1655
+ const buf = new Uint8Array(1024);
1656
+ const n = __require("fs").readSync(0, buf);
1657
+ const input = new TextDecoder().decode(buf.subarray(0, n)).trim();
1658
+ return input || fallback;
1659
+ }
1660
+ var import_picocolors6;
1661
+ var init_prompt = __esm(() => {
1662
+ import_picocolors6 = __toESM(require_picocolors(), 1);
1663
+ });
1664
+
1665
+ // src/cli/commands/eval.ts
1666
+ var exports_eval = {};
1667
+ __export(exports_eval, {
1668
+ default: () => eval_default
1669
+ });
1670
+ import { join as join3, basename } from "path";
1671
+ import { existsSync as existsSync5 } from "fs";
1672
+ function renderResult(result, verbose) {
1673
+ const verdictColor = result.verdict === "PASS" ? import_picocolors7.default.green : result.verdict === "FAIL" ? import_picocolors7.default.red : import_picocolors7.default.yellow;
1674
+ const verdictSymbol = result.verdict === "PASS" ? "\u2713" : result.verdict === "FAIL" ? "\u2717" : "?";
1675
+ ui.write(`
1676
+ ${verdictColor(`[${result.verdict}]`)} ${import_picocolors7.default.bold(result.skill)}`);
1677
+ ui.write(` agent: ${result.agent}`);
1678
+ ui.write(` model: ${result.model}`);
1679
+ if (result.userFamiliarity > 0) {
1680
+ ui.write(` familiarity: ${result.userFamiliarity}/10 (${result.userFamiliarityReason})`);
1681
+ }
1682
+ ui.write(` closure: ${result.closure}${result.userTurnsAfterSkill > 0 ? ` (${result.userTurnsAfterSkill} turns)` : ""}`);
1683
+ if (result.sessionTitle) {
1684
+ ui.write(` session: ${result.sessionId.slice(0, 8)} "${result.sessionTitle}"`);
1685
+ }
1686
+ if (result.checklist.length > 0) {
1687
+ ui.write(`
1688
+ Adherence:`);
1689
+ for (const item of result.checklist) {
1690
+ const sym = item.pass ? import_picocolors7.default.green("\u2713") : import_picocolors7.default.red("\u2717");
1691
+ const detail = item.detail ? ` ${import_picocolors7.default.dim(item.detail)}` : "";
1692
+ ui.write(` ${sym} ${item.instruction}${detail}`);
1693
+ }
1694
+ const passed = result.checklist.filter((c) => c.pass).length;
1695
+ ui.write(`
1696
+ Result: ${passed}/${result.checklist.length} [${verdictColor(result.verdict)}${result.verdictReason ? ` \u2014 ${result.verdictReason}` : ""}]`);
1697
+ } else if (result.verdictReason) {
1698
+ ui.write(`
1699
+ ${verdictColor(verdictSymbol)} ${result.verdictReason}`);
1700
+ }
1701
+ }
1702
+ function selectRecentSessions(recent) {
1703
+ if (recent.length === 0)
1704
+ return [];
1705
+ if (recent.length === 1)
1706
+ return [recent[0].path];
1707
+ ui.write(`
1708
+ Recent sessions for this directory:`);
1709
+ recent.forEach((s, i) => {
1710
+ const date = new Date(s.mtime).toISOString().slice(0, 10);
1711
+ const titleStr = s.title ? ` "${s.title.slice(0, 45)}"` : "";
1712
+ const skillStr = s.skillCount > 0 ? ` (${s.skillCount} skill${s.skillCount === 1 ? "" : "s"})` : "";
1713
+ const short = basename(s.path);
1714
+ ui.write(` ${i + 1}. ${date}${titleStr}${skillStr} ${import_picocolors7.default.dim(short)}`);
1715
+ });
1716
+ const input = prompt(`
1717
+ Select session(s) (e.g. 1,3 or 2-4 or all or latest): `, "1").trim().toLowerCase();
1718
+ if (input === "all")
1719
+ return recent.map((s) => s.path);
1720
+ if (input === "latest")
1721
+ return [recent[0].path];
1722
+ if (input.includes("/") || input.endsWith(".jsonl"))
1723
+ return [input];
1724
+ const selected = new Set;
1725
+ const parts = input.split(/[\s,]+/);
1726
+ for (const part of parts) {
1727
+ if (part.includes("-")) {
1728
+ const nums = part.split("-").map((n) => parseInt(n, 10)).filter((n) => !isNaN(n));
1729
+ const start = nums[0] ?? 0;
1730
+ const end = nums[1] ?? start;
1731
+ for (let n = start;n <= end; n++) {
1732
+ const item = recent[n - 1];
1733
+ if (item)
1734
+ selected.add(item.path);
1735
+ }
1736
+ } else {
1737
+ const n = parseInt(part, 10);
1738
+ if (!isNaN(n)) {
1739
+ const item = recent[n - 1];
1740
+ if (item)
1741
+ selected.add(item.path);
1742
+ }
1743
+ }
1744
+ }
1745
+ return selected.size > 0 ? Array.from(selected) : [recent[0].path];
1746
+ }
1747
+ var import_picocolors7, eval_default;
1748
+ var init_eval = __esm(() => {
1749
+ init_dist();
1750
+ init_out();
1751
+ init_session_adapters();
1752
+ init_session_eval();
1753
+ init_journal_config();
1754
+ init_skill_validate();
1755
+ init_prompt();
1756
+ import_picocolors7 = __toESM(require_picocolors(), 1);
1757
+ eval_default = defineCommand({
1758
+ meta: {
1759
+ name: "eval",
1760
+ description: "Evaluate a real coding agent session against skill instructions"
1761
+ },
1762
+ args: {
1763
+ session: {
1764
+ type: "string",
1765
+ description: "Path to .jsonl session file(s). Supports comma/space separated values, or omit to interactively select from recent sessions."
1766
+ },
1767
+ skill: {
1768
+ type: "string",
1769
+ description: "Path to a skill directory to filter to (default: all skills in session)"
1770
+ },
1771
+ format: {
1772
+ type: "string",
1773
+ alias: "f",
1774
+ description: "Output format: table (default) or json",
1775
+ default: "table"
1776
+ },
1777
+ ci: {
1778
+ type: "boolean",
1779
+ description: "Exit with code 1 if any verdict is FAIL",
1780
+ default: false
1781
+ },
1782
+ verbose: {
1783
+ type: "boolean",
1784
+ alias: "v",
1785
+ description: "Show full checklist reasoning",
1786
+ default: false
1787
+ }
1788
+ },
1789
+ async run({ args }) {
1790
+ ui.heading("doraval eval \u2014 Session skill adherence");
1791
+ const config = await readConfig();
1792
+ const evalCfg = getEvalConfig(config);
1793
+ const agentCfg = config?.agent;
1794
+ if (!agentCfg) {
1795
+ ui.fail("No coding agent configured. Run: dora init");
1796
+ process.exit(2);
1797
+ }
1798
+ if (!evalCfg.model) {
1799
+ ui.warn("No eval.model configured for the judge LLM.");
1800
+ ui.info(" doraval will use your configured agent (" + agentCfg.command + ").");
1801
+ ui.info(" If you want to record a specific model, run: dora config set eval.model claude-3-5-sonnet-20241022");
1802
+ }
1803
+ let sessionPaths = [];
1804
+ if (args.session) {
1805
+ sessionPaths = String(args.session).split(/[\s,]+/).map((s) => s.trim()).filter(Boolean);
1806
+ } else {
1807
+ const adapter2 = getAdapter();
1808
+ if (!adapter2) {
1809
+ ui.fail("No supported coding agent detected. Is Claude Code installed?");
1810
+ process.exit(2);
1811
+ }
1812
+ let recent = adapter2.listRecentSessions(process.cwd(), 12);
1813
+ const withSkills = recent.filter((s) => s.skillCount > 0);
1814
+ if (withSkills.length > 0)
1815
+ recent = withSkills;
1816
+ if (recent.length === 0) {
1817
+ ui.fail(`No sessions with skills found for ${process.cwd()}`);
1818
+ ui.info(" Use --session <path> to specify a session file.");
1819
+ process.exit(2);
1820
+ }
1821
+ if (recent.length === 1) {
1822
+ sessionPaths = [recent[0].path];
1823
+ } else if (!process.stdout.isTTY || !process.stdin.isTTY) {
1824
+ sessionPaths = [recent[0].path];
1825
+ } else {
1826
+ sessionPaths = selectRecentSessions(recent);
1827
+ }
1828
+ }
1829
+ if (sessionPaths.length === 0) {
1830
+ ui.fail("No sessions selected.");
1831
+ process.exit(2);
1832
+ }
1833
+ const adapter = getAdapter();
1834
+ if (!adapter) {
1835
+ ui.fail("No supported coding agent detected.");
1836
+ process.exit(2);
1837
+ }
1838
+ const allResults = [];
1839
+ for (const sessionPath of sessionPaths) {
1840
+ ui.info(` Session: ${import_picocolors7.default.dim(sessionPath)}`);
1841
+ const primitives = adapter.parse(sessionPath);
1842
+ if (primitives.skillsInvoked.length === 0) {
1843
+ ui.warn(" No skills were invoked in this session.");
1844
+ continue;
1845
+ }
1846
+ let skillsToEval = primitives.skillsInvoked;
1847
+ if (args.skill) {
1848
+ skillsToEval = skillsToEval.filter((s) => s.includes(args.skill));
1849
+ if (skillsToEval.length === 0) {
1850
+ ui.warn(` No matching skills found for filter: ${args.skill}`);
1851
+ continue;
1852
+ }
1853
+ }
1854
+ ui.write(` ${import_picocolors7.default.dim("\xB7 Sending session summary (tool calls + 5 user messages) to")} ${import_picocolors7.default.dim(evalCfg.model || "configured model")}${import_picocolors7.default.dim(". Use --verbose to inspect.")}`);
1855
+ ensureDoravalDirs();
1856
+ for (const skillName of skillsToEval) {
1857
+ ui.info(`
1858
+ Evaluating: ${import_picocolors7.default.bold(skillName)}`);
1859
+ let skillContent = `Skill: ${skillName}
1860
+ (skill content not found locally \u2014 using skill name only for evaluation)`;
1861
+ const candidateDirs = [
1862
+ process.cwd(),
1863
+ join3(process.cwd(), ".claude", "skills", skillName.split(":").pop() ?? skillName),
1864
+ join3(process.cwd(), "skills", skillName.split(":").pop() ?? skillName)
1865
+ ];
1866
+ for (const dir of candidateDirs) {
1867
+ if (existsSync5(join3(dir, "SKILL.md"))) {
1868
+ const loaded = await loadSkill(dir);
1869
+ if (loaded.ok) {
1870
+ skillContent = loaded.model.content;
1871
+ break;
1872
+ }
1873
+ }
1874
+ }
1875
+ const result = await runEval(primitives, skillName, skillContent, agentCfg, evalCfg);
1876
+ allResults.push(result);
1877
+ if (evalCfg.save_history) {
1878
+ const evalPath = join3(getEvalsDir(), `${primitives.sessionId}-${Date.now()}.json`);
1879
+ await Bun.write(evalPath, JSON.stringify(result, null, 2));
1880
+ }
1881
+ }
1882
+ }
1883
+ if (args.format === "json") {
1884
+ process.stdout.write(JSON.stringify(allResults, null, 2) + `
1885
+ `);
1886
+ } else {
1887
+ for (const result of allResults) {
1888
+ renderResult(result, Boolean(args.verbose));
1889
+ }
1890
+ ui.blank();
1891
+ }
1892
+ if (args.ci && allResults.some((r) => r.verdict === "FAIL")) {
1893
+ process.exit(1);
1894
+ }
1895
+ process.exit(0);
1896
+ }
1897
+ });
1898
+ });
1899
+
1900
+ // src/cli/commands/judge.ts
1901
+ var exports_judge = {};
1902
+ __export(exports_judge, {
1903
+ default: () => judge_default
1904
+ });
1905
+ var judge_default;
1906
+ var init_judge = __esm(() => {
1907
+ init_dist();
1908
+ judge_default = defineCommand({
1909
+ meta: {
1910
+ name: "judge",
1911
+ description: "Evaluate the latest session for a skill (alias for eval --skill)"
1912
+ },
1913
+ args: {
1914
+ path: {
1915
+ type: "positional",
1916
+ description: "Path to skill directory or skill name",
1917
+ required: true
1918
+ },
1919
+ format: {
1920
+ type: "string",
1921
+ alias: "f",
1922
+ description: "Output format (json or table)",
1923
+ default: "table"
1924
+ },
1925
+ ci: {
1926
+ type: "boolean",
1927
+ description: "Exit non-zero if FAIL",
1928
+ default: false
1929
+ },
1930
+ verbose: {
1931
+ type: "boolean",
1932
+ alias: "v",
1933
+ description: "Show full checklist",
1934
+ default: false
1935
+ }
1936
+ },
1937
+ async run({ args }) {
1938
+ const evalCmd = await Promise.resolve().then(() => (init_eval(), exports_eval)).then((m) => m.default);
1939
+ const newArgs = {
1940
+ ...args,
1941
+ skill: args.path,
1942
+ session: undefined
1943
+ };
1944
+ return evalCmd.run?.({ args: newArgs });
1945
+ }
1946
+ });
1947
+ });
1948
+
1284
1949
  // src/core/journal-remote.ts
1285
- var {spawnSync } = globalThis.Bun;
1950
+ var {spawnSync: spawnSync2 } = globalThis.Bun;
1286
1951
  function hasGhCli() {
1287
- const result = spawnSync(["gh", "--version"], {
1952
+ const result = spawnSync2(["gh", "--version"], {
1288
1953
  stdout: "pipe",
1289
1954
  stderr: "pipe"
1290
1955
  });
@@ -1296,7 +1961,7 @@ function ensureGhCli() {
1296
1961
  return { ok: false, error: "GH_CLI_MISSING" };
1297
1962
  }
1298
1963
  function fetchRemoteJournalFile(repo, path) {
1299
- const result = spawnSync(["gh", "api", `repos/${repo}/contents/${path}`, "--jq", "{sha, content, encoding}"], { stdout: "pipe", stderr: "pipe" });
1964
+ const result = spawnSync2(["gh", "api", `repos/${repo}/contents/${path}`, "--jq", "{sha, content, encoding}"], { stdout: "pipe", stderr: "pipe" });
1300
1965
  if (result.exitCode !== 0) {
1301
1966
  const stderr = result.stderr.toString();
1302
1967
  if (stderr.includes("404") || stderr.includes("Not Found")) {
@@ -1336,7 +2001,7 @@ async function refreshLocalJournalFile(repo, remotePath, localPath) {
1336
2001
  return { ok: true, value: true };
1337
2002
  }
1338
2003
  function getRemoteJournalFileMeta(repo, path) {
1339
- const result = spawnSync(["gh", "api", `repos/${repo}/contents/${path}`, "--jq", "{sha, content, encoding}"], { stdout: "pipe", stderr: "pipe" });
2004
+ const result = spawnSync2(["gh", "api", `repos/${repo}/contents/${path}`, "--jq", "{sha, content, encoding}"], { stdout: "pipe", stderr: "pipe" });
1340
2005
  if (result.exitCode !== 0) {
1341
2006
  const stderr = result.stderr.toString();
1342
2007
  if (stderr.includes("404") || stderr.includes("Not Found")) {
@@ -1352,7 +2017,7 @@ function getRemoteJournalFileMeta(repo, path) {
1352
2017
  }
1353
2018
  }
1354
2019
  function getGitRemoteOwner() {
1355
- const result = spawnSync(["git", "config", "--get", "remote.origin.url"], {
2020
+ const result = spawnSync2(["git", "config", "--get", "remote.origin.url"], {
1356
2021
  stdout: "pipe",
1357
2022
  stderr: "pipe"
1358
2023
  });
@@ -1365,7 +2030,7 @@ function getGitRemoteOwner() {
1365
2030
  return match ? match[1] : null;
1366
2031
  }
1367
2032
  function ghUser() {
1368
- const result = spawnSync(["gh", "api", "user", "--jq", ".login"], {
2033
+ const result = spawnSync2(["gh", "api", "user", "--jq", ".login"], {
1369
2034
  stdout: "pipe",
1370
2035
  stderr: "pipe"
1371
2036
  });
@@ -1374,38 +2039,25 @@ function ghUser() {
1374
2039
  return result.stdout.toString().trim() || null;
1375
2040
  }
1376
2041
  function repoExists(repo) {
1377
- const result = spawnSync(["gh", "api", `repos/${repo}`, "--jq", ".full_name"], { stdout: "pipe", stderr: "pipe" });
2042
+ const result = spawnSync2(["gh", "api", `repos/${repo}`, "--jq", ".full_name"], { stdout: "pipe", stderr: "pipe" });
1378
2043
  return result.exitCode === 0 && result.stdout.toString().trim().length > 0;
1379
2044
  }
1380
2045
  var init_journal_remote = () => {};
1381
2046
 
1382
- // src/cli/prompt.ts
1383
- function prompt(label, fallback) {
1384
- process.stderr.write(`${label} ${import_picocolors4.default.dim(`(${fallback})`)} `);
1385
- const buf = new Uint8Array(1024);
1386
- const n = __require("fs").readSync(0, buf);
1387
- const input = new TextDecoder().decode(buf.subarray(0, n)).trim();
1388
- return input || fallback;
1389
- }
1390
- var import_picocolors4;
1391
- var init_prompt = __esm(() => {
1392
- import_picocolors4 = __toESM(require_picocolors(), 1);
1393
- });
1394
-
1395
2047
  // src/cli/commands/journal/init.ts
1396
2048
  var exports_init = {};
1397
2049
  __export(exports_init, {
1398
2050
  default: () => init_default
1399
2051
  });
1400
- import { basename, join as join2 } from "path";
1401
- var import_picocolors5, init_default;
2052
+ import { basename as basename2, join as join4 } from "path";
2053
+ var import_picocolors8, init_default;
1402
2054
  var init_init = __esm(() => {
1403
2055
  init_dist();
1404
2056
  init_out();
1405
2057
  init_journal_config();
1406
2058
  init_journal_remote();
1407
2059
  init_prompt();
1408
- import_picocolors5 = __toESM(require_picocolors(), 1);
2060
+ import_picocolors8 = __toESM(require_picocolors(), 1);
1409
2061
  init_default = defineCommand({
1410
2062
  meta: {
1411
2063
  name: "init",
@@ -1430,21 +2082,21 @@ var init_init = __esm(() => {
1430
2082
  },
1431
2083
  async run({ args }) {
1432
2084
  ui.write(`
1433
- ${import_picocolors5.default.bold(import_picocolors5.default.white("dora journal init"))} (or top-level ${import_picocolors5.default.dim(import_picocolors5.default.gray("dora init"))}) \u2014 Set up your journal
2085
+ ${import_picocolors8.default.bold(import_picocolors8.default.white("dora journal init"))} (or top-level ${import_picocolors8.default.dim(import_picocolors8.default.gray("dora init"))}) \u2014 Set up your journal
1434
2086
  `);
1435
2087
  const ghCheck = ensureGhCli();
1436
2088
  if (!ghCheck.ok) {
1437
- ui.write(` ${import_picocolors5.default.red("\u2717")} ${import_picocolors5.default.white("The GitHub CLI (")}${import_picocolors5.default.bold("gh")}${import_picocolors5.default.white(") is not installed.")}
2089
+ ui.write(` ${import_picocolors8.default.red("\u2717")} ${import_picocolors8.default.white("The GitHub CLI (")}${import_picocolors8.default.bold("gh")}${import_picocolors8.default.white(") is not installed.")}
1438
2090
  `);
1439
- ui.write(` doraval uses ${import_picocolors5.default.bold("gh")} to fetch and sync journal files with GitHub.
2091
+ ui.write(` doraval uses ${import_picocolors8.default.bold("gh")} to fetch and sync journal files with GitHub.
1440
2092
  `);
1441
2093
  ui.write(` Install it:
1442
2094
  `);
1443
- ui.write(` macOS: ${import_picocolors5.default.dim("brew install gh")}`);
1444
- ui.write(` Linux: ${import_picocolors5.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
1445
- ui.write(` Windows: ${import_picocolors5.default.dim("winget install --id GitHub.cli")}
2095
+ ui.write(` macOS: ${import_picocolors8.default.dim("brew install gh")}`);
2096
+ ui.write(` Linux: ${import_picocolors8.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
2097
+ ui.write(` Windows: ${import_picocolors8.default.dim("winget install --id GitHub.cli")}
1446
2098
  `);
1447
- ui.write(` Then authenticate: ${import_picocolors5.default.dim("gh auth login")}
2099
+ ui.write(` Then authenticate: ${import_picocolors8.default.dim("gh auth login")}
1448
2100
  `);
1449
2101
  process.exit(1);
1450
2102
  }
@@ -1457,46 +2109,46 @@ var init_init = __esm(() => {
1457
2109
  if (gitOwner) {
1458
2110
  defaultRepo = `${gitOwner}/${gitOwner}.md`;
1459
2111
  if (ghLogin && ghLogin !== gitOwner) {
1460
- sourceNote = ` ${import_picocolors5.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
2112
+ sourceNote = ` ${import_picocolors8.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
1461
2113
  `;
1462
2114
  } else {
1463
- sourceNote = ` ${import_picocolors5.default.dim("(from git remote)")}
2115
+ sourceNote = ` ${import_picocolors8.default.dim("(from git remote)")}
1464
2116
  `;
1465
2117
  }
1466
2118
  } else if (ghLogin) {
1467
2119
  defaultRepo = `${ghLogin}/${ghLogin}.md`;
1468
- sourceNote = ` ${import_picocolors5.default.dim("(from your active gh account)")}
2120
+ sourceNote = ` ${import_picocolors8.default.dim("(from your active gh account)")}
1469
2121
  `;
1470
2122
  } else {
1471
- ui.write(` ${import_picocolors5.default.yellow("\u26A0")} Not logged in to GitHub. Run ${import_picocolors5.default.dim("gh auth login")} first.
2123
+ ui.write(` ${import_picocolors8.default.yellow("\u26A0")} Not logged in to GitHub. Run ${import_picocolors8.default.dim("gh auth login")} first.
1472
2124
  `);
1473
2125
  process.exit(1);
1474
2126
  }
1475
2127
  const existingConfig = await readConfig();
1476
2128
  if (existingConfig?.journal.repo) {
1477
2129
  defaultRepo = existingConfig.journal.repo;
1478
- sourceNote = ` ${import_picocolors5.default.dim("(from your previous journal setup)")}
2130
+ sourceNote = ` ${import_picocolors8.default.dim("(from your previous journal setup)")}
1479
2131
  `;
1480
2132
  }
1481
- ui.write(` Journal repo ${import_picocolors5.default.dim(import_picocolors5.default.gray("(owner/name)"))}`);
2133
+ ui.write(` Journal repo ${import_picocolors8.default.dim(import_picocolors8.default.gray("(owner/name)"))}`);
1482
2134
  if (sourceNote)
1483
2135
  ui.write(sourceNote);
1484
2136
  repo = prompt(" >", defaultRepo);
1485
2137
  }
1486
2138
  let project = args.project || process.env.DORAVAL_PROJECT;
1487
2139
  if (!project) {
1488
- const defaultProject = basename(process.cwd());
2140
+ const defaultProject = basename2(process.cwd());
1489
2141
  project = prompt(" Project name", defaultProject);
1490
2142
  }
1491
2143
  project = sanitizeProjectName(project);
1492
2144
  if (!repoExists(repo)) {
1493
- ui.write(` ${import_picocolors5.default.red("\u2717")} Repository ${import_picocolors5.default.bold(import_picocolors5.default.white(repo))} not found on GitHub.
2145
+ ui.write(` ${import_picocolors8.default.red("\u2717")} Repository ${import_picocolors8.default.bold(import_picocolors8.default.white(repo))} not found on GitHub.
1494
2146
  `);
1495
2147
  ui.write(` Create it first:
1496
2148
  `);
1497
- ui.write(` ${import_picocolors5.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
2149
+ ui.write(` ${import_picocolors8.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
1498
2150
  `);
1499
- ui.write(` The repo should be private. doraval will populate it on first ${import_picocolors5.default.dim("dora journal sync")}.
2151
+ ui.write(` The repo should be private. doraval will populate it on first ${import_picocolors8.default.dim("dora journal sync")}.
1500
2152
  `);
1501
2153
  process.exit(1);
1502
2154
  }
@@ -1504,20 +2156,20 @@ var init_init = __esm(() => {
1504
2156
  const alreadyRegistered = existing?.journal.projects[project];
1505
2157
  const isRefresh = alreadyRegistered && args.refresh;
1506
2158
  if (alreadyRegistered && !isRefresh) {
1507
- ui.write(` ${import_picocolors5.default.yellow("\u26A0")} Project ${import_picocolors5.default.bold(import_picocolors5.default.white(project))} is already registered.
2159
+ ui.write(` ${import_picocolors8.default.yellow("\u26A0")} Project ${import_picocolors8.default.bold(import_picocolors8.default.white(project))} is already registered.
1508
2160
  `);
1509
- ui.write(` Repo: ${import_picocolors5.default.gray(existing.journal.repo)}`);
2161
+ ui.write(` Repo: ${import_picocolors8.default.gray(existing.journal.repo)}`);
1510
2162
  ui.write(` Remote: ${existing.journal.projects[project]?.remote_path}
1511
2163
  `);
1512
- ui.write(` To refresh local files, run: ${import_picocolors5.default.dim(import_picocolors5.default.gray(`dora journal update`))}
2164
+ ui.write(` To refresh local files, run: ${import_picocolors8.default.dim(import_picocolors8.default.gray(`dora journal update`))}
1513
2165
  ` + ` (init --refresh still works for compatibility.)
1514
- ` + ` Or remove the project from ${import_picocolors5.default.dim(import_picocolors5.default.gray("~/.doraval/config.yml"))} to fully re-initialize.
2166
+ ` + ` Or remove the project from ${import_picocolors8.default.dim(import_picocolors8.default.gray("~/.doraval/config.yml"))} to fully re-initialize.
1515
2167
  `);
1516
2168
  process.exit(0);
1517
2169
  }
1518
2170
  const journalsDir = getJournalsDir();
1519
2171
  const remotePath = `projects/${project}.md`;
1520
- const localPath = join2(journalsDir, `${project}.md`);
2172
+ const localPath = join4(journalsDir, `${project}.md`);
1521
2173
  const effectiveRepo = isRefresh && !args.repo ? existing.journal.repo : repo;
1522
2174
  const config = existing ?? {
1523
2175
  journal: { repo: effectiveRepo, projects: {} }
@@ -1529,16 +2181,16 @@ var init_init = __esm(() => {
1529
2181
  };
1530
2182
  ensureDoravalDirs();
1531
2183
  const actionLabel = isRefresh ? "Refreshing" : "Fetching";
1532
- ui.write(` ${import_picocolors5.default.dim(import_picocolors5.default.gray(`${actionLabel} journal files from`))} ${import_picocolors5.default.gray(effectiveRepo)}${import_picocolors5.default.dim(import_picocolors5.default.gray("..."))}
2184
+ ui.write(` ${import_picocolors8.default.dim(import_picocolors8.default.gray(`${actionLabel} journal files from`))} ${import_picocolors8.default.gray(effectiveRepo)}${import_picocolors8.default.dim(import_picocolors8.default.gray("..."))}
1533
2185
  `);
1534
- const globalDest = join2(journalsDir, "global.md");
2186
+ const globalDest = join4(journalsDir, "global.md");
1535
2187
  const refreshGlobalRes = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
1536
2188
  let wroteGlobal;
1537
2189
  if (!refreshGlobalRes.ok) {
1538
2190
  if (refreshGlobalRes.isNotFound) {
1539
2191
  wroteGlobal = false;
1540
2192
  } else {
1541
- ui.write(` ${import_picocolors5.default.red("\u2717")} Failed to fetch global.md from ${effectiveRepo}:`);
2193
+ ui.write(` ${import_picocolors8.default.red("\u2717")} Failed to fetch global.md from ${effectiveRepo}:`);
1542
2194
  ui.write(refreshGlobalRes.error);
1543
2195
  process.exit(1);
1544
2196
  }
@@ -1546,9 +2198,9 @@ var init_init = __esm(() => {
1546
2198
  wroteGlobal = refreshGlobalRes.value;
1547
2199
  }
1548
2200
  if (wroteGlobal) {
1549
- ui.write(` ${import_picocolors5.default.green("\u2713")} global.md`);
2201
+ ui.write(` ${import_picocolors8.default.green("\u2713")} global.md`);
1550
2202
  } else {
1551
- ui.write(` ${import_picocolors5.default.dim("\xB7")} global.md ${import_picocolors5.default.dim("(not found \u2014 will be created on first sync)")}`);
2203
+ ui.write(` ${import_picocolors8.default.dim("\xB7")} global.md ${import_picocolors8.default.dim("(not found \u2014 will be created on first sync)")}`);
1552
2204
  await Bun.write(globalDest, `# Global Journal
1553
2205
 
1554
2206
  Cross-project principles.
@@ -1560,7 +2212,7 @@ Cross-project principles.
1560
2212
  if (refreshProjectRes.isNotFound) {
1561
2213
  wroteProject = false;
1562
2214
  } else {
1563
- ui.write(` ${import_picocolors5.default.red("\u2717")} Failed to fetch ${remotePath} from ${effectiveRepo}:`);
2215
+ ui.write(` ${import_picocolors8.default.red("\u2717")} Failed to fetch ${remotePath} from ${effectiveRepo}:`);
1564
2216
  ui.write(refreshProjectRes.error);
1565
2217
  process.exit(1);
1566
2218
  }
@@ -1568,9 +2220,9 @@ Cross-project principles.
1568
2220
  wroteProject = refreshProjectRes.value;
1569
2221
  }
1570
2222
  if (wroteProject) {
1571
- ui.write(` ${import_picocolors5.default.green("\u2713")} ${remotePath}`);
2223
+ ui.write(` ${import_picocolors8.default.green("\u2713")} ${remotePath}`);
1572
2224
  } else {
1573
- ui.write(` ${import_picocolors5.default.dim("\xB7")} ${remotePath} ${import_picocolors5.default.dim("(not found \u2014 will be created on first sync)")}`);
2225
+ ui.write(` ${import_picocolors8.default.dim("\xB7")} ${remotePath} ${import_picocolors8.default.dim("(not found \u2014 will be created on first sync)")}`);
1574
2226
  await Bun.write(localPath, `# ${project} Journal
1575
2227
 
1576
2228
  Project-specific decisions.
@@ -1578,13 +2230,13 @@ Project-specific decisions.
1578
2230
  }
1579
2231
  await writeConfig(config);
1580
2232
  ui.write(`
1581
- ${import_picocolors5.default.green("\u2713")} Project ${import_picocolors5.default.bold(import_picocolors5.default.white(project))} registered to ${import_picocolors5.default.bold(import_picocolors5.default.white(repo))}.
2233
+ ${import_picocolors8.default.green("\u2713")} Project ${import_picocolors8.default.bold(import_picocolors8.default.white(project))} registered to ${import_picocolors8.default.bold(import_picocolors8.default.white(repo))}.
1582
2234
  `);
1583
- ui.write(` Config: ${import_picocolors5.default.dim(import_picocolors5.default.gray("~/.doraval/config.yml"))}`);
1584
- ui.write(` Journals: ${import_picocolors5.default.dim(import_picocolors5.default.gray("~/.doraval/journals/"))}`);
1585
- ui.write(` Pending: ${import_picocolors5.default.dim(import_picocolors5.default.gray("~/.doraval/pending/"))}
2235
+ ui.write(` Config: ${import_picocolors8.default.dim(import_picocolors8.default.gray("~/.doraval/config.yml"))}`);
2236
+ ui.write(` Journals: ${import_picocolors8.default.dim(import_picocolors8.default.gray("~/.doraval/journals/"))}`);
2237
+ ui.write(` Pending: ${import_picocolors8.default.dim(import_picocolors8.default.gray("~/.doraval/pending/"))}
1586
2238
  `);
1587
- ui.write(` Use ${import_picocolors5.default.dim(import_picocolors5.default.gray("dora journal add"))} to propose decisions and ${import_picocolors5.default.dim(import_picocolors5.default.gray("dora journal list"))} to view them.
2239
+ ui.write(` Use ${import_picocolors8.default.dim(import_picocolors8.default.gray("dora journal add"))} to propose decisions and ${import_picocolors8.default.dim(import_picocolors8.default.gray("dora journal list"))} to view them.
1588
2240
  `);
1589
2241
  process.exit(0);
1590
2242
  }
@@ -1658,15 +2310,15 @@ var exports_list = {};
1658
2310
  __export(exports_list, {
1659
2311
  default: () => list_default
1660
2312
  });
1661
- import { existsSync as existsSync4, readdirSync } from "fs";
1662
- import { join as join3 } from "path";
1663
- var import_picocolors6, list_default;
2313
+ import { existsSync as existsSync6, readdirSync as readdirSync2 } from "fs";
2314
+ import { join as join5 } from "path";
2315
+ var import_picocolors9, list_default;
1664
2316
  var init_list = __esm(() => {
1665
2317
  init_dist();
1666
2318
  init_out();
1667
2319
  init_journal_config();
1668
2320
  init_journal_parse();
1669
- import_picocolors6 = __toESM(require_picocolors(), 1);
2321
+ import_picocolors9 = __toESM(require_picocolors(), 1);
1670
2322
  list_default = defineCommand({
1671
2323
  meta: {
1672
2324
  name: "list",
@@ -1700,15 +2352,15 @@ var init_list = __esm(() => {
1700
2352
  project = sanitizeProjectName(project);
1701
2353
  }
1702
2354
  if (!project) {
1703
- ui.write(`${import_picocolors6.default.yellow("\u26A0")} ${import_picocolors6.default.yellow("No project mapping found.")}
2355
+ ui.write(`${import_picocolors9.default.yellow("\u26A0")} ${import_picocolors9.default.yellow("No project mapping found.")}
1704
2356
 
1705
- ` + `Run ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora init"))} (or ${import_picocolors6.default.dim(import_picocolors6.default.gray("doraval journal init"))}) first, or pass ${import_picocolors6.default.dim(import_picocolors6.default.gray("--project <name>"))}.`);
2357
+ ` + `Run ${import_picocolors9.default.dim(import_picocolors9.default.gray("dora init"))} (or ${import_picocolors9.default.dim(import_picocolors9.default.gray("doraval journal init"))}) first, or pass ${import_picocolors9.default.dim(import_picocolors9.default.gray("--project <name>"))}.`);
1706
2358
  process.exit(1);
1707
2359
  }
1708
2360
  const journalRepo = config?.journal.repo ?? "(unknown)";
1709
2361
  const journalsDir = getJournalsDir();
1710
- const projectFile = join3(journalsDir, `${project}.md`);
1711
- const globalFile = join3(journalsDir, "global.md");
2362
+ const projectFile = join5(journalsDir, `${project}.md`);
2363
+ const globalFile = join5(journalsDir, "global.md");
1712
2364
  let raw = "";
1713
2365
  try {
1714
2366
  raw = await Bun.file(projectFile).text();
@@ -1722,10 +2374,10 @@ var init_list = __esm(() => {
1722
2374
  let staged = [];
1723
2375
  try {
1724
2376
  const pdir = getPendingProjectDir(project);
1725
- if (existsSync4(pdir)) {
1726
- const files = readdirSync(pdir).filter((f) => f.endsWith(".md") && f !== ".gitkeep");
2377
+ if (existsSync6(pdir)) {
2378
+ const files = readdirSync2(pdir).filter((f) => f.endsWith(".md") && f !== ".gitkeep");
1727
2379
  const stagedResults = await Promise.all(files.map(async (f) => {
1728
- const txt = await Bun.file(join3(pdir, f)).text();
2380
+ const txt = await Bun.file(join5(pdir, f)).text();
1729
2381
  const parsed = parseJournalEntries(txt);
1730
2382
  return parsed.map((e) => ({ ...e, _staged: true }));
1731
2383
  }));
@@ -1737,7 +2389,7 @@ var init_list = __esm(() => {
1737
2389
  return;
1738
2390
  }
1739
2391
  ui.write(`
1740
- ${import_picocolors6.default.bold(import_picocolors6.default.white("dora journal list"))} \u2014 ${import_picocolors6.default.white(project)} ${import_picocolors6.default.dim(import_picocolors6.default.gray(`(from ${journalRepo})`))}
2392
+ ${import_picocolors9.default.bold(import_picocolors9.default.white("dora journal list"))} \u2014 ${import_picocolors9.default.white(project)} ${import_picocolors9.default.dim(import_picocolors9.default.gray(`(from ${journalRepo})`))}
1741
2393
  `);
1742
2394
  const hasStaged = staged.length > 0;
1743
2395
  const hasCommitted = allEntries.length > 0;
@@ -1751,46 +2403,46 @@ var init_list = __esm(() => {
1751
2403
  }
1752
2404
  if (dups.length > 0) {
1753
2405
  const uniqueDups = [...new Set(dups)];
1754
- ui.write(` ${import_picocolors6.default.yellow("\u26A0")} ${import_picocolors6.default.yellow("Duplicate titles in this view (clean in your journal repo + update):")} ${uniqueDups.map((t) => import_picocolors6.default.yellow(`"${t}"`)).join(", ")}
2406
+ ui.write(` ${import_picocolors9.default.yellow("\u26A0")} ${import_picocolors9.default.yellow("Duplicate titles in this view (clean in your journal repo + update):")} ${uniqueDups.map((t) => import_picocolors9.default.yellow(`"${t}"`)).join(", ")}
1755
2407
  `);
1756
2408
  }
1757
2409
  if (!hasStaged && !hasCommitted) {
1758
- ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("No active entries found for"))} ${import_picocolors6.default.bold(import_picocolors6.default.white(project))}.
2410
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("No active entries found for"))} ${import_picocolors9.default.bold(import_picocolors9.default.white(project))}.
1759
2411
  `);
1760
- ui.write(` Journal repo: ${import_picocolors6.default.dim(import_picocolors6.default.gray(journalRepo))}`);
1761
- ui.write(` Local file: ${import_picocolors6.default.dim(import_picocolors6.default.gray(projectFile))}
2412
+ ui.write(` Journal repo: ${import_picocolors9.default.dim(import_picocolors9.default.gray(journalRepo))}`);
2413
+ ui.write(` Local file: ${import_picocolors9.default.dim(import_picocolors9.default.gray(projectFile))}
1762
2414
  `);
1763
- ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("This is normal for a freshly initialized project."))}
1764
- ` + ` Use ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora journal add"))} to propose decisions.
1765
- ` + ` They will be staged locally until you run ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora journal sync"))}.
2415
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("This is normal for a freshly initialized project."))}
2416
+ ` + ` Use ${import_picocolors9.default.dim(import_picocolors9.default.gray("dora journal add"))} to propose decisions.
2417
+ ` + ` They will be staged locally until you run ${import_picocolors9.default.dim(import_picocolors9.default.gray("dora journal sync"))}.
1766
2418
  `);
1767
- ui.write(` If you expect content, try: ${import_picocolors6.default.dim(import_picocolors6.default.gray(`dora journal update`))}
2419
+ ui.write(` If you expect content, try: ${import_picocolors9.default.dim(import_picocolors9.default.gray(`dora journal update`))}
1768
2420
  `);
1769
2421
  return;
1770
2422
  }
1771
2423
  function printEntry(entry) {
1772
2424
  const pb = entry.pushback ?? 0;
1773
- let pbColor = import_picocolors6.default.green;
2425
+ let pbColor = import_picocolors9.default.green;
1774
2426
  if (pb >= 7)
1775
- pbColor = import_picocolors6.default.red;
2427
+ pbColor = import_picocolors9.default.red;
1776
2428
  else if (pb >= 4)
1777
- pbColor = import_picocolors6.default.yellow;
1778
- const tagsStr = (entry.tags || []).join(", ") || import_picocolors6.default.dim("(none)");
1779
- const statusNote = entry.status !== "active" ? import_picocolors6.default.dim(` [${entry.status}]`) : "";
1780
- const stagedNote = entry._staged ? import_picocolors6.default.dim(" (staged)") : "";
1781
- ui.write(` ${pbColor(String(pb).padStart(2))} ${import_picocolors6.default.bold(import_picocolors6.default.white(entry.title))}${statusNote}${stagedNote}`);
1782
- ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("tags:"))} ${import_picocolors6.default.gray(tagsStr)}`);
1783
- const by = entry.author?.startsWith("agent:") ? import_picocolors6.default.cyan(entry.author) : entry.author || "human";
1784
- ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("by:"))} ${import_picocolors6.default.gray(by)} ${import_picocolors6.default.dim(import_picocolors6.default.gray("on"))} ${import_picocolors6.default.gray(entry.date)}`);
2429
+ pbColor = import_picocolors9.default.yellow;
2430
+ const tagsStr = (entry.tags || []).join(", ") || import_picocolors9.default.dim("(none)");
2431
+ const statusNote = entry.status !== "active" ? import_picocolors9.default.dim(` [${entry.status}]`) : "";
2432
+ const stagedNote = entry._staged ? import_picocolors9.default.dim(" (staged)") : "";
2433
+ ui.write(` ${pbColor(String(pb).padStart(2))} ${import_picocolors9.default.bold(import_picocolors9.default.white(entry.title))}${statusNote}${stagedNote}`);
2434
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("tags:"))} ${import_picocolors9.default.gray(tagsStr)}`);
2435
+ const by = entry.author?.startsWith("agent:") ? import_picocolors9.default.cyan(entry.author) : entry.author || "human";
2436
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("by:"))} ${import_picocolors9.default.gray(by)} ${import_picocolors9.default.dim(import_picocolors9.default.gray("on"))} ${import_picocolors9.default.gray(entry.date)}`);
1785
2437
  const rat = (entry.rationale || "").replace(/\s+/g, " ").trim();
1786
2438
  if (rat) {
1787
- const preview = rat.length > 88 ? rat.slice(0, 85) + import_picocolors6.default.dim(import_picocolors6.default.gray("\u2026")) : rat;
1788
- ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray(preview))}`);
2439
+ const preview = rat.length > 88 ? rat.slice(0, 85) + import_picocolors9.default.dim(import_picocolors9.default.gray("\u2026")) : rat;
2440
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray(preview))}`);
1789
2441
  }
1790
2442
  ui.write("");
1791
2443
  }
1792
2444
  if (hasStaged) {
1793
- ui.write(` ${import_picocolors6.default.yellow("\u25CF")} ${import_picocolors6.default.bold(import_picocolors6.default.white("Staged / pending"))} (not yet in remote; run ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora journal sync"))} to publish):
2445
+ ui.write(` ${import_picocolors9.default.yellow("\u25CF")} ${import_picocolors9.default.bold(import_picocolors9.default.white("Staged / pending"))} (not yet in remote; run ${import_picocolors9.default.dim(import_picocolors9.default.gray("dora journal sync"))} to publish):
1794
2446
  `);
1795
2447
  for (const entry of staged) {
1796
2448
  printEntry(entry);
@@ -1800,7 +2452,7 @@ var init_list = __esm(() => {
1800
2452
  }
1801
2453
  if (hasCommitted) {
1802
2454
  if (hasStaged) {
1803
- ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("Committed (from local cache):"))}
2455
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Committed (from local cache):"))}
1804
2456
  `);
1805
2457
  }
1806
2458
  for (const entry of allEntries) {
@@ -1808,7 +2460,7 @@ var init_list = __esm(() => {
1808
2460
  }
1809
2461
  }
1810
2462
  const totalShown = staged.length + allEntries.length;
1811
- ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray(`${totalShown} entries shown from ${journalRepo}.`))}
2463
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray(`${totalShown} entries shown from ${journalRepo}.`))}
1812
2464
  `);
1813
2465
  process.exit(0);
1814
2466
  }
@@ -1821,8 +2473,8 @@ __export(exports_context, {
1821
2473
  generateJournalContext: () => generateJournalContext,
1822
2474
  default: () => context_default
1823
2475
  });
1824
- import { existsSync as existsSync5 } from "fs";
1825
- import { join as join4, resolve as resolvePath } from "path";
2476
+ import { existsSync as existsSync7 } from "fs";
2477
+ import { join as join6, resolve as resolvePath } from "path";
1826
2478
  function truncate(text, max = 180) {
1827
2479
  const clean = text.replace(/\s+/g, " ").trim();
1828
2480
  if (clean.length <= max)
@@ -1900,7 +2552,7 @@ function generateJournalContext(entries, project, opts = {}) {
1900
2552
  async function appendOrUpdateJournalBlock(target, contextText, project, useReference) {
1901
2553
  const absTarget = resolvePath(process.cwd(), target);
1902
2554
  let original = "";
1903
- if (existsSync5(absTarget)) {
2555
+ if (existsSync7(absTarget)) {
1904
2556
  original = await Bun.file(absTarget).text();
1905
2557
  }
1906
2558
  let blockContent;
@@ -1942,22 +2594,22 @@ async function appendOrUpdateJournalBlock(target, contextText, project, useRefer
1942
2594
  `;
1943
2595
  }
1944
2596
  await Bun.write(absTarget, updated);
1945
- const action = existsSync5(absTarget) && startIdx !== -1 ? "Updated" : "Added";
2597
+ const action = existsSync7(absTarget) && startIdx !== -1 ? "Updated" : "Added";
1946
2598
  ui.write(`
1947
- ${import_picocolors7.default.green("\u2713")} ${action} journal decisions section in ${import_picocolors7.default.white(target)}`);
2599
+ ${import_picocolors10.default.green("\u2713")} ${action} journal decisions section in ${import_picocolors10.default.white(target)}`);
1948
2600
  if (useReference) {
1949
- ui.write(` ${import_picocolors7.default.dim("Using @import references (full files will be loaded by Claude).")}`);
2601
+ ui.write(` ${import_picocolors10.default.dim("Using @import references (full files will be loaded by Claude).")}`);
1950
2602
  } else {
1951
- ui.write(` ${import_picocolors7.default.dim("Embedded compact decisions (low noise).")}`);
2603
+ ui.write(` ${import_picocolors10.default.dim("Embedded compact decisions (low noise).")}`);
1952
2604
  }
1953
2605
  }
1954
- var import_picocolors7, JOURNAL_BLOCK_START = "<!-- doraval-journal:start -->", JOURNAL_BLOCK_END = "<!-- doraval-journal:end -->", context_default;
2606
+ var import_picocolors10, JOURNAL_BLOCK_START = "<!-- doraval-journal:start -->", JOURNAL_BLOCK_END = "<!-- doraval-journal:end -->", context_default;
1955
2607
  var init_context = __esm(() => {
1956
2608
  init_dist();
1957
2609
  init_out();
1958
2610
  init_journal_config();
1959
2611
  init_journal_parse();
1960
- import_picocolors7 = __toESM(require_picocolors(), 1);
2612
+ import_picocolors10 = __toESM(require_picocolors(), 1);
1961
2613
  context_default = defineCommand({
1962
2614
  meta: {
1963
2615
  name: "context",
@@ -2029,16 +2681,16 @@ var init_context = __esm(() => {
2029
2681
  }
2030
2682
  const journalsDir = getJournalsDir();
2031
2683
  const entries = [];
2032
- const globalPath = join4(journalsDir, "global.md");
2033
- if (existsSync5(globalPath)) {
2684
+ const globalPath = join6(journalsDir, "global.md");
2685
+ if (existsSync7(globalPath)) {
2034
2686
  try {
2035
2687
  const raw = await Bun.file(globalPath).text();
2036
2688
  entries.push(...parseJournalEntries(raw));
2037
2689
  } catch {}
2038
2690
  }
2039
2691
  if (project) {
2040
- const projectPath = join4(journalsDir, `${project}.md`);
2041
- if (existsSync5(projectPath)) {
2692
+ const projectPath = join6(journalsDir, `${project}.md`);
2693
+ if (existsSync7(projectPath)) {
2042
2694
  try {
2043
2695
  const raw = await Bun.file(projectPath).text();
2044
2696
  entries.push(...parseJournalEntries(raw));
@@ -2075,17 +2727,17 @@ __export(exports_hook, {
2075
2727
  default: () => hook_default,
2076
2728
  addHook: () => addHook
2077
2729
  });
2078
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, unlinkSync, rmdirSync, readdirSync as readdirSync2 } from "fs";
2079
- import { join as join5, dirname } from "path";
2080
- import { homedir as homedir2 } from "os";
2730
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, unlinkSync, rmdirSync, readdirSync as readdirSync3 } from "fs";
2731
+ import { join as join7, dirname } from "path";
2732
+ import { homedir as homedir3 } from "os";
2081
2733
  function getGlobalSettingsPath() {
2082
- return join5(homedir2(), ".claude", "settings.json");
2734
+ return join7(homedir3(), ".claude", "settings.json");
2083
2735
  }
2084
2736
  function getLocalHooksPath() {
2085
- return join5(process.cwd(), "hooks", "hooks.json");
2737
+ return join7(process.cwd(), "hooks", "hooks.json");
2086
2738
  }
2087
2739
  async function readJson(file) {
2088
- if (!existsSync6(file))
2740
+ if (!existsSync8(file))
2089
2741
  return {};
2090
2742
  try {
2091
2743
  const raw = await Bun.file(file).text();
@@ -2096,7 +2748,7 @@ async function readJson(file) {
2096
2748
  }
2097
2749
  async function writeJson(file, data) {
2098
2750
  const dir = dirname(file);
2099
- if (!existsSync6(dir)) {
2751
+ if (!existsSync8(dir)) {
2100
2752
  mkdirSync2(dir, { recursive: true });
2101
2753
  }
2102
2754
  await Bun.write(file, JSON.stringify(data, null, 2) + `
@@ -2124,7 +2776,7 @@ async function addHook(file) {
2124
2776
  return { changed: true, path: file };
2125
2777
  }
2126
2778
  async function removeHook(file) {
2127
- if (!existsSync6(file))
2779
+ if (!existsSync8(file))
2128
2780
  return { changed: false, path: file };
2129
2781
  const original = await readJson(file);
2130
2782
  const config = JSON.parse(JSON.stringify(original));
@@ -2147,13 +2799,13 @@ async function removeHook(file) {
2147
2799
  const changed = JSON.stringify(config) !== JSON.stringify(original);
2148
2800
  if (changed) {
2149
2801
  const isEmpty = !config || Object.keys(config).length === 0;
2150
- if (isEmpty && existsSync6(file)) {
2802
+ if (isEmpty && existsSync8(file)) {
2151
2803
  try {
2152
2804
  unlinkSync(file);
2153
2805
  } catch {}
2154
2806
  try {
2155
2807
  const dir = dirname(file);
2156
- if (existsSync6(dir) && readdirSync2(dir).length === 0)
2808
+ if (existsSync8(dir) && readdirSync3(dir).length === 0)
2157
2809
  rmdirSync(dir);
2158
2810
  } catch {}
2159
2811
  } else {
@@ -2283,15 +2935,15 @@ var exports_update = {};
2283
2935
  __export(exports_update, {
2284
2936
  default: () => update_default
2285
2937
  });
2286
- import { existsSync as existsSync7 } from "fs";
2287
- import { join as join6 } from "path";
2288
- var import_picocolors8, update_default;
2938
+ import { existsSync as existsSync9 } from "fs";
2939
+ import { join as join8 } from "path";
2940
+ var import_picocolors11, update_default;
2289
2941
  var init_update = __esm(() => {
2290
2942
  init_dist();
2291
2943
  init_out();
2292
2944
  init_journal_config();
2293
2945
  init_journal_remote();
2294
- import_picocolors8 = __toESM(require_picocolors(), 1);
2946
+ import_picocolors11 = __toESM(require_picocolors(), 1);
2295
2947
  update_default = defineCommand({
2296
2948
  meta: {
2297
2949
  name: "update",
@@ -2312,30 +2964,30 @@ var init_update = __esm(() => {
2312
2964
  async run({ args }) {
2313
2965
  const ghCheck = ensureGhCli();
2314
2966
  if (!ghCheck.ok) {
2315
- ui.write(` ${import_picocolors8.default.red("\u2717")} ${import_picocolors8.default.white("The GitHub CLI (")}${import_picocolors8.default.bold("gh")}${import_picocolors8.default.white(") is not installed.")}
2967
+ ui.write(` ${import_picocolors11.default.red("\u2717")} ${import_picocolors11.default.white("The GitHub CLI (")}${import_picocolors11.default.bold("gh")}${import_picocolors11.default.white(") is not installed.")}
2316
2968
  `);
2317
- ui.write(` doraval uses ${import_picocolors8.default.bold("gh")} to fetch and sync journal files with GitHub.
2969
+ ui.write(` doraval uses ${import_picocolors11.default.bold("gh")} to fetch and sync journal files with GitHub.
2318
2970
  `);
2319
2971
  ui.write(` Install it:
2320
2972
  `);
2321
- ui.write(` macOS: ${import_picocolors8.default.dim("brew install gh")}`);
2322
- ui.write(` Linux: ${import_picocolors8.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
2323
- ui.write(` Windows: ${import_picocolors8.default.dim("winget install --id GitHub.cli")}
2973
+ ui.write(` macOS: ${import_picocolors11.default.dim("brew install gh")}`);
2974
+ ui.write(` Linux: ${import_picocolors11.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
2975
+ ui.write(` Windows: ${import_picocolors11.default.dim("winget install --id GitHub.cli")}
2324
2976
  `);
2325
- ui.write(` Then authenticate: ${import_picocolors8.default.dim("gh auth login")}
2977
+ ui.write(` Then authenticate: ${import_picocolors11.default.dim("gh auth login")}
2326
2978
  `);
2327
2979
  process.exit(1);
2328
2980
  }
2329
2981
  const config = await readConfig();
2330
2982
  if (!config?.journal.repo) {
2331
- ui.write(`${import_picocolors8.default.red("\u2717")} No journal repo configured. Run ${import_picocolors8.default.dim("dora init")} (or ${import_picocolors8.default.dim("doraval journal init")}) first.`);
2983
+ ui.write(`${import_picocolors11.default.red("\u2717")} No journal repo configured. Run ${import_picocolors11.default.dim("dora init")} (or ${import_picocolors11.default.dim("doraval journal init")}) first.`);
2332
2984
  process.exit(1);
2333
2985
  }
2334
2986
  const journalRepo = config.journal.repo;
2335
2987
  ensureDoravalDirs();
2336
2988
  const journalsDir = getJournalsDir();
2337
2989
  ui.write(`
2338
- ${import_picocolors8.default.bold(import_picocolors8.default.white("dora journal update"))} \u2014 ${import_picocolors8.default.dim(import_picocolors8.default.gray(journalRepo))}
2990
+ ${import_picocolors11.default.bold(import_picocolors11.default.white("dora journal update"))} \u2014 ${import_picocolors11.default.dim(import_picocolors11.default.gray(journalRepo))}
2339
2991
  `);
2340
2992
  const projectsToUpdate = [];
2341
2993
  if (args.all) {
@@ -2353,19 +3005,19 @@ var init_update = __esm(() => {
2353
3005
  try {
2354
3006
  projectsToUpdate.push(sanitizeProjectName(project));
2355
3007
  } catch {
2356
- ui.write(`${import_picocolors8.default.red("\u2717")} Invalid project name: ${project}`);
3008
+ ui.write(`${import_picocolors11.default.red("\u2717")} Invalid project name: ${project}`);
2357
3009
  process.exit(1);
2358
3010
  }
2359
3011
  }
2360
3012
  }
2361
- const globalLocal = join6(journalsDir, "global.md");
3013
+ const globalLocal = join8(journalsDir, "global.md");
2362
3014
  const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md", globalLocal);
2363
3015
  let gotGlobal;
2364
3016
  if (!refreshGlobalRes.ok) {
2365
3017
  if (refreshGlobalRes.isNotFound) {
2366
3018
  gotGlobal = false;
2367
3019
  } else {
2368
- ui.write(`${import_picocolors8.default.red("\u2717")} Failed to fetch global.md from ${journalRepo}:`);
3020
+ ui.write(`${import_picocolors11.default.red("\u2717")} Failed to fetch global.md from ${journalRepo}:`);
2369
3021
  ui.write(refreshGlobalRes.error);
2370
3022
  process.exit(1);
2371
3023
  }
@@ -2373,33 +3025,33 @@ var init_update = __esm(() => {
2373
3025
  gotGlobal = refreshGlobalRes.value;
2374
3026
  }
2375
3027
  if (gotGlobal) {
2376
- ui.write(` ${import_picocolors8.default.green("\u2713")} global.md`);
3028
+ ui.write(` ${import_picocolors11.default.green("\u2713")} global.md`);
2377
3029
  } else {
2378
- ui.write(` ${import_picocolors8.default.dim("\xB7")} global.md ${import_picocolors8.default.dim("(not present on remote)")}`);
3030
+ ui.write(` ${import_picocolors11.default.dim("\xB7")} global.md ${import_picocolors11.default.dim("(not present on remote)")}`);
2379
3031
  }
2380
3032
  if (projectsToUpdate.length === 0) {
2381
3033
  if (args.all) {
2382
3034
  ui.write(`
2383
- ${import_picocolors8.default.dim(import_picocolors8.default.gray("No projects registered."))}
3035
+ ${import_picocolors11.default.dim(import_picocolors11.default.gray("No projects registered."))}
2384
3036
  `);
2385
3037
  } else {
2386
3038
  ui.write(`
2387
- ${import_picocolors8.default.yellow("\u26A0")} No project mapping found.
2388
- ` + ` Run ${import_picocolors8.default.dim("dora init")} or pass ${import_picocolors8.default.dim("--project <name>")} / ${import_picocolors8.default.dim("--all")}.
3039
+ ${import_picocolors11.default.yellow("\u26A0")} No project mapping found.
3040
+ ` + ` Run ${import_picocolors11.default.dim("dora init")} or pass ${import_picocolors11.default.dim("--project <name>")} / ${import_picocolors11.default.dim("--all")}.
2389
3041
  `);
2390
3042
  }
2391
3043
  return;
2392
3044
  }
2393
3045
  for (const project of projectsToUpdate) {
2394
3046
  const remotePath = `projects/${project}.md`;
2395
- const localPath = join6(journalsDir, `${project}.md`);
3047
+ const localPath = join8(journalsDir, `${project}.md`);
2396
3048
  const refreshRes = await refreshLocalJournalFile(journalRepo, remotePath, localPath);
2397
3049
  let got;
2398
3050
  if (!refreshRes.ok) {
2399
3051
  if (refreshRes.isNotFound) {
2400
3052
  got = false;
2401
3053
  } else {
2402
- ui.write(`${import_picocolors8.default.red("\u2717")} Failed to fetch ${remotePath} from ${journalRepo}:`);
3054
+ ui.write(`${import_picocolors11.default.red("\u2717")} Failed to fetch ${remotePath} from ${journalRepo}:`);
2403
3055
  ui.write(refreshRes.error);
2404
3056
  process.exit(1);
2405
3057
  }
@@ -2407,10 +3059,10 @@ var init_update = __esm(() => {
2407
3059
  got = refreshRes.value;
2408
3060
  }
2409
3061
  if (got) {
2410
- ui.write(` ${import_picocolors8.default.green("\u2713")} ${remotePath}`);
3062
+ ui.write(` ${import_picocolors11.default.green("\u2713")} ${remotePath}`);
2411
3063
  } else {
2412
- ui.write(` ${import_picocolors8.default.dim("\xB7")} ${remotePath} ${import_picocolors8.default.dim("(not present on remote \u2014 will be created on first sync)")}`);
2413
- if (!existsSync7(localPath)) {
3064
+ ui.write(` ${import_picocolors11.default.dim("\xB7")} ${remotePath} ${import_picocolors11.default.dim("(not present on remote \u2014 will be created on first sync)")}`);
3065
+ if (!existsSync9(localPath)) {
2414
3066
  await Bun.write(localPath, `# ${project} Journal
2415
3067
 
2416
3068
  Project-specific decisions.
@@ -2420,7 +3072,7 @@ Project-specific decisions.
2420
3072
  }
2421
3073
  const summary = args.all && projectsToUpdate.length > 1 ? `${projectsToUpdate.length} projects + global` : projectsToUpdate.length === 1 ? projectsToUpdate[0] : "journals";
2422
3074
  ui.write(`
2423
- ${import_picocolors8.default.dim(import_picocolors8.default.gray("Local cache refreshed for"))} ${import_picocolors8.default.bold(import_picocolors8.default.white(summary))}.
3075
+ ${import_picocolors11.default.dim(import_picocolors11.default.gray("Local cache refreshed for"))} ${import_picocolors11.default.bold(import_picocolors11.default.white(summary))}.
2424
3076
  `);
2425
3077
  }
2426
3078
  });
@@ -2458,166 +3110,47 @@ function validateEntry(entry) {
2458
3110
  errors.push(`status must be one of: ${VALID_STATUSES.join(", ")}`);
2459
3111
  }
2460
3112
  if (!entry.title || typeof entry.title !== "string" || entry.title.trim() === "") {
2461
- errors.push("title is required");
2462
- }
2463
- return {
2464
- valid: errors.length === 0,
2465
- errors,
2466
- warnings
2467
- };
2468
- }
2469
- var CANONICAL_TAGS, VALID_STATUSES;
2470
- var init_journal_validate = __esm(() => {
2471
- CANONICAL_TAGS = [
2472
- "naming",
2473
- "cli",
2474
- "architecture",
2475
- "testing",
2476
- "ux",
2477
- "api",
2478
- "docs",
2479
- "notes"
2480
- ];
2481
- VALID_STATUSES = ["active", "superseded", "retired"];
2482
- });
2483
-
2484
- // src/cli/commands/journal/add.ts
2485
- var exports_add = {};
2486
- __export(exports_add, {
2487
- default: () => add_default,
2488
- buildAgentArgv: () => buildAgentArgv
2489
- });
2490
- import { existsSync as existsSync8 } from "fs";
2491
- import { join as join7 } from "path";
2492
- var {spawnSync: spawnSync2 } = globalThis.Bun;
2493
- function buildAgentArgv(template, promptText) {
2494
- const marker = "__DORA_PROMPT__";
2495
- const substituted = template.replace("{{prompt}}", marker);
2496
- const rawParts = substituted.split(/\s+/).filter(Boolean);
2497
- return rawParts.map((part) => {
2498
- let cleaned = part;
2499
- if (cleaned.startsWith('"') && cleaned.endsWith('"'))
2500
- cleaned = cleaned.slice(1, -1);
2501
- if (cleaned.startsWith("'") && cleaned.endsWith("'"))
2502
- cleaned = cleaned.slice(1, -1);
2503
- if (cleaned === marker) {
2504
- return promptText;
2505
- }
2506
- return cleaned;
2507
- });
2508
- }
2509
- async function invokeConfiguredAgentForEntry(decisionText, agentCfg) {
2510
- if (!agentCfg || !agentCfg.command)
2511
- return null;
2512
- const scaffold = `Raw user capture (a decision, observation, or useful note that just happened): "${decisionText}"
2513
-
2514
- Turn this into a clean journal entry. Infer the core decision or note even if the input is phrased as a todo or reminder. Be professional and concise.
2515
-
2516
- **CRITICAL INSTRUCTIONS (follow exactly):**
2517
- - Output *ONLY* a single valid JSON object. Nothing before it, nothing after it, no markdown fences, no explanations, no extra text.
2518
- - The JSON must have exactly these keys (use the suggested values as starting point but improve them):
2519
- {
2520
- "title": "Short, scannable, professional title (past tense or present perfect, max ~80 chars)",
2521
- "pushback": 4,
2522
- "tags": ["cli", "ux"],
2523
- "rationale": "2-5 sentences explaining context and implications (or the note content).",
2524
- "author": "agent:claude-code"
2525
- }
2526
-
2527
- If you cannot produce exactly this, output the JSON with the best you can and set "author" to "agent:claude-code" anyway.`;
2528
- const template = agentCfg.prompt_template || '-p "{{prompt}}" --output-format json';
2529
- const extraArgs = buildAgentArgv(template, scaffold);
2530
- const shortTemplate = (agentCfg.prompt_template || '-p "{{prompt}}" --output-format json').slice(0, 80);
2531
- ui.write(` ${import_picocolors9.default.dim(`\u2192 ${agentCfg.command} ${shortTemplate}...`)}`);
2532
- try {
2533
- const result = spawnSync2([agentCfg.command, ...extraArgs], {
2534
- stdout: "pipe",
2535
- stderr: "pipe"
2536
- });
2537
- const stdout = result.stdout.toString().trim();
2538
- const stderr = result.stderr.toString().trim();
2539
- if (result.exitCode !== 0) {
2540
- ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Configured agent (${agentCfg.command}) exited with code ${result.exitCode}. Falling back to defaults.`);
2541
- if (stderr)
2542
- ui.write(` ${import_picocolors9.default.dim("stderr:")}
2543
- ${stderr.slice(0, 800)}`);
2544
- if (stdout)
2545
- ui.write(` ${import_picocolors9.default.dim("stdout:")}
2546
- ${stdout.slice(0, 400)}`);
2547
- return null;
2548
- }
2549
- let candidates = [];
2550
- let jsonMatch = stdout.match(/\{[\s\S]*\}/);
2551
- if (jsonMatch) {
2552
- try {
2553
- candidates.push(JSON.parse(jsonMatch[0]));
2554
- } catch {}
2555
- }
2556
- const allMatches = stdout.match(/\{[\s\S]*?\}(?=\s*(?:\{|$))/g) || [];
2557
- for (const m of allMatches) {
2558
- try {
2559
- const p = JSON.parse(m);
2560
- candidates.push(p);
2561
- } catch {}
2562
- }
2563
- let parsed = null;
2564
- for (const c of candidates) {
2565
- if (c && typeof c === "object" && (c.title || c.rationale)) {
2566
- parsed = c;
2567
- break;
2568
- }
2569
- }
2570
- if (!parsed) {
2571
- for (const c of candidates) {
2572
- if (c && typeof c === "object" && c.result) {
2573
- let inner = c.result;
2574
- if (typeof inner === "string") {
2575
- try {
2576
- inner = JSON.parse(inner);
2577
- } catch {}
2578
- }
2579
- if (inner && typeof inner === "object" && (inner.title || inner.rationale)) {
2580
- parsed = inner;
2581
- break;
2582
- }
2583
- }
2584
- }
2585
- }
2586
- if (!parsed) {
2587
- parsed = candidates[0] || null;
2588
- }
2589
- if (!parsed || typeof parsed !== "object") {
2590
- ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent produced output but no usable JSON was found. Falling back.`);
2591
- ui.write(` ${import_picocolors9.default.dim("stdout (first 700 chars):")}
2592
- ${stdout.slice(0, 700)}`);
2593
- if (stderr)
2594
- ui.write(` ${import_picocolors9.default.dim("stderr:")}
2595
- ${stderr.slice(0, 500)}`);
2596
- return null;
2597
- }
2598
- if (!parsed.title && !parsed.rationale) {
2599
- ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent returned JSON without expected fields (title/rationale). Using defaults.`);
2600
- ui.write(` ${import_picocolors9.default.dim("parsed keys:")} ${Object.keys(parsed).join(", ")}`);
2601
- ui.write(` ${import_picocolors9.default.dim("stdout (truncated):")}
2602
- ${stdout.slice(0, 600)}`);
2603
- return null;
2604
- }
2605
- return parsed;
2606
- } catch (e) {
2607
- ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Failed to invoke configured agent (${agentCfg.command}): ${e.message}. Using defaults.`);
2608
- return null;
3113
+ errors.push("title is required");
2609
3114
  }
3115
+ return {
3116
+ valid: errors.length === 0,
3117
+ errors,
3118
+ warnings
3119
+ };
2610
3120
  }
3121
+ var CANONICAL_TAGS, VALID_STATUSES;
3122
+ var init_journal_validate = __esm(() => {
3123
+ CANONICAL_TAGS = [
3124
+ "naming",
3125
+ "cli",
3126
+ "architecture",
3127
+ "testing",
3128
+ "ux",
3129
+ "api",
3130
+ "docs",
3131
+ "notes"
3132
+ ];
3133
+ VALID_STATUSES = ["active", "superseded", "retired"];
3134
+ });
3135
+
3136
+ // src/cli/commands/journal/add.ts
3137
+ var exports_add = {};
3138
+ __export(exports_add, {
3139
+ default: () => add_default
3140
+ });
3141
+ import { existsSync as existsSync10 } from "fs";
3142
+ import { join as join9 } from "path";
2611
3143
  function slugify(title) {
2612
3144
  return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
2613
3145
  }
2614
- var import_picocolors9, add_default;
3146
+ var import_picocolors12, add_default;
2615
3147
  var init_add = __esm(() => {
2616
3148
  init_dist();
2617
3149
  init_out();
2618
3150
  init_journal_config();
2619
3151
  init_journal_validate();
2620
- import_picocolors9 = __toESM(require_picocolors(), 1);
3152
+ init_agent_invoke();
3153
+ import_picocolors12 = __toESM(require_picocolors(), 1);
2621
3154
  add_default = defineCommand({
2622
3155
  meta: {
2623
3156
  name: "add",
@@ -2692,9 +3225,9 @@ var init_add = __esm(() => {
2692
3225
  project = sanitizeProjectName(project);
2693
3226
  }
2694
3227
  if (!project) {
2695
- ui.write(`${import_picocolors9.default.yellow("\u26A0")} No project mapping found.
3228
+ ui.write(`${import_picocolors12.default.yellow("\u26A0")} No project mapping found.
2696
3229
 
2697
- ` + `Run ${import_picocolors9.default.dim("dora init")} (or ${import_picocolors9.default.dim("doraval journal init")}) first, or pass ${import_picocolors9.default.dim("--project <name>")}.`);
3230
+ ` + `Run ${import_picocolors12.default.dim("dora init")} (or ${import_picocolors12.default.dim("doraval journal init")}) first, or pass ${import_picocolors12.default.dim("--project <name>")}.`);
2698
3231
  process.exit(1);
2699
3232
  }
2700
3233
  let title;
@@ -2732,7 +3265,7 @@ var init_add = __esm(() => {
2732
3265
  if (parsed.date)
2733
3266
  date = String(parsed.date);
2734
3267
  } catch (e) {
2735
- ui.write(`${import_picocolors9.default.red("\u2717")} Failed to parse --json input: ${e.message}`);
3268
+ ui.write(`${import_picocolors12.default.red("\u2717")} Failed to parse --json input: ${e.message}`);
2736
3269
  process.exit(1);
2737
3270
  }
2738
3271
  }
@@ -2741,7 +3274,7 @@ var init_add = __esm(() => {
2741
3274
  if (rawMdArg && !jsonInput) {
2742
3275
  if (rawMdArg === "-" || rawMdArg === "") {
2743
3276
  rawBody = (await new Response(Bun.stdin.stream()).text()).trim();
2744
- } else if (existsSync8(rawMdArg)) {
3277
+ } else if (existsSync10(rawMdArg)) {
2745
3278
  rawBody = (await Bun.file(rawMdArg).text()).trim();
2746
3279
  } else {
2747
3280
  rawBody = rawMdArg.trim();
@@ -2756,7 +3289,7 @@ var init_add = __esm(() => {
2756
3289
  title = (headingMatch[1] ?? "").trim();
2757
3290
  rawBody = rawBody.replace(/^#+\s+(.+?)(?:\r?\n|$)/m, "").trimStart();
2758
3291
  } else {
2759
- ui.write(`${import_picocolors9.default.red("\u2717")} --raw-markdown provided without a TITLE and without a leading '# Heading' in the markdown.`);
3292
+ ui.write(`${import_picocolors12.default.red("\u2717")} --raw-markdown provided without a TITLE and without a leading '# Heading' in the markdown.`);
2760
3293
  process.exit(1);
2761
3294
  }
2762
3295
  }
@@ -2796,8 +3329,24 @@ var init_add = __esm(() => {
2796
3329
  agentCfg = fullConfigForAgent?.agent;
2797
3330
  if (agentCfg) {
2798
3331
  attemptedAgent = true;
2799
- ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("(querying your configured coding agent...)"))}`);
2800
- const agentResult = await invokeConfiguredAgentForEntry(title, agentCfg);
3332
+ ui.write(` ${import_picocolors12.default.dim(import_picocolors12.default.gray("(querying your configured coding agent...)"))}`);
3333
+ const scaffold = `Raw user capture (a decision, observation, or useful note that just happened): "${title}"
3334
+
3335
+ Turn this into a clean journal entry. Infer the core decision or note even if the input is phrased as a todo or reminder. Be professional and concise.
3336
+
3337
+ **CRITICAL INSTRUCTIONS (follow exactly):**
3338
+ - Output *ONLY* a single valid JSON object. Nothing before it, nothing after it, no markdown fences, no explanations, no extra text.
3339
+ - The JSON must have exactly these keys (use the suggested values as starting point but improve them):
3340
+ {
3341
+ "title": "Short, scannable, professional title (past tense or present perfect, max ~80 chars)",
3342
+ "pushback": 4,
3343
+ "tags": ["cli", "ux"],
3344
+ "rationale": "2-5 sentences explaining context and implications (or the note content).",
3345
+ "author": "agent:claude-code"
3346
+ }
3347
+
3348
+ If you cannot produce exactly this, output the JSON with the best you can and set "author" to "agent:claude-code" anyway.`;
3349
+ const agentResult = await invokeAgent(scaffold, agentCfg, ["title", "rationale"]);
2801
3350
  if (agentResult) {
2802
3351
  if (agentResult.title)
2803
3352
  title = String(agentResult.title).trim();
@@ -2829,18 +3378,18 @@ var init_add = __esm(() => {
2829
3378
  };
2830
3379
  const validation = validateEntry(entry);
2831
3380
  if (!validation.valid) {
2832
- ui.write(`${import_picocolors9.default.red("\u2717")} Invalid entry:
3381
+ ui.write(`${import_picocolors12.default.red("\u2717")} Invalid entry:
2833
3382
  `);
2834
3383
  for (const err of validation.errors) {
2835
- ui.write(` ${import_picocolors9.default.red("\u2022")} ${err}`);
3384
+ ui.write(` ${import_picocolors12.default.red("\u2022")} ${err}`);
2836
3385
  }
2837
3386
  process.exit(1);
2838
3387
  }
2839
3388
  for (const warn of validation.warnings) {
2840
3389
  if ((warn.includes("not supplied") || warn.includes("empty")) && attemptedAgent) {} else if (warn.includes("not supplied") || warn.includes("empty")) {
2841
- ui.write(`${import_picocolors9.default.dim("\xB7")} ${warn}`);
3390
+ ui.write(`${import_picocolors12.default.dim("\xB7")} ${warn}`);
2842
3391
  } else {
2843
- ui.write(`${import_picocolors9.default.yellow("\u26A0")} ${warn}`);
3392
+ ui.write(`${import_picocolors12.default.yellow("\u26A0")} ${warn}`);
2844
3393
  }
2845
3394
  }
2846
3395
  if (!rationale) {
@@ -2860,31 +3409,31 @@ ${rationale}
2860
3409
  `;
2861
3410
  ensureDoravalDirs();
2862
3411
  const pendingDir = getPendingProjectDir(project);
2863
- if (!existsSync8(pendingDir)) {
2864
- await Bun.write(join7(pendingDir, ".gitkeep"), "");
3412
+ if (!existsSync10(pendingDir)) {
3413
+ await Bun.write(join9(pendingDir, ".gitkeep"), "");
2865
3414
  }
2866
3415
  const slug = slugify(title);
2867
3416
  const filename = `${date}-${slug}.md`;
2868
- const filePath = join7(pendingDir, filename);
3417
+ const filePath = join9(pendingDir, filename);
2869
3418
  await Bun.write(filePath, content);
2870
3419
  ui.write(`
2871
- ${import_picocolors9.default.green("\u2713")} ${import_picocolors9.default.bold(import_picocolors9.default.white(title))}`);
2872
- ui.write(` Project: ${import_picocolors9.default.white(project)} \xB7 run ${import_picocolors9.default.dim(import_picocolors9.default.gray("dora journal sync"))} to publish
3420
+ ${import_picocolors12.default.green("\u2713")} ${import_picocolors12.default.bold(import_picocolors12.default.white(title))}`);
3421
+ ui.write(` Project: ${import_picocolors12.default.white(project)} \xB7 run ${import_picocolors12.default.dim(import_picocolors12.default.gray("dora journal sync"))} to publish
2873
3422
  `);
2874
3423
  if (args.verbose) {
2875
- const authorDisplay = author.startsWith("agent:") ? import_picocolors9.default.cyan(author) : author;
2876
- ui.write(` Pushback: ${import_picocolors9.default.white(String(pushback))}`);
2877
- ui.write(` Tags: ${import_picocolors9.default.gray(tags.join(", ") || import_picocolors9.default.dim("(none)"))}`);
3424
+ const authorDisplay = author.startsWith("agent:") ? import_picocolors12.default.cyan(author) : author;
3425
+ ui.write(` Pushback: ${import_picocolors12.default.white(String(pushback))}`);
3426
+ ui.write(` Tags: ${import_picocolors12.default.gray(tags.join(", ") || import_picocolors12.default.dim("(none)"))}`);
2878
3427
  ui.write(` Author: ${authorDisplay}`);
2879
- ui.write(` File: ${import_picocolors9.default.dim(import_picocolors9.default.gray(filePath))}
3428
+ ui.write(` File: ${import_picocolors12.default.dim(import_picocolors12.default.gray(filePath))}
2880
3429
  `);
2881
3430
  }
2882
3431
  if (isThinInput && !author.startsWith("agent:")) {
2883
3432
  if (attemptedAgent) {
2884
- ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Note: agent was called but returned no usable enrichment. Edit the pending file or re-run dora init."))}
3433
+ ui.write(` ${import_picocolors12.default.dim(import_picocolors12.default.gray("Note: agent was called but returned no usable enrichment. Edit the pending file or re-run dora init."))}
2885
3434
  `);
2886
3435
  } else {
2887
- ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Tip: run dora init to configure an agent for auto-enrichment."))}
3436
+ ui.write(` ${import_picocolors12.default.dim(import_picocolors12.default.gray("Tip: run dora init to configure an agent for auto-enrichment."))}
2888
3437
  `);
2889
3438
  }
2890
3439
  }
@@ -2898,8 +3447,8 @@ var exports_sync = {};
2898
3447
  __export(exports_sync, {
2899
3448
  default: () => sync_default
2900
3449
  });
2901
- import { readdirSync as readdirSync3, existsSync as existsSync9 } from "fs";
2902
- import { join as join8 } from "path";
3450
+ import { readdirSync as readdirSync4, existsSync as existsSync11 } from "fs";
3451
+ import { join as join10 } from "path";
2903
3452
  var {spawnSync: spawnSync3 } = globalThis.Bun;
2904
3453
  function updateGitHubFile(repo, path, content, message, sha) {
2905
3454
  const payload = {
@@ -2928,18 +3477,18 @@ function updateGitHubFile(repo, path, content, message, sha) {
2928
3477
  stderr: "pipe"
2929
3478
  });
2930
3479
  if (result.exitCode !== 0) {
2931
- ui.write(import_picocolors10.default.red(`Failed to update ${path} on ${repo}:`));
3480
+ ui.write(import_picocolors13.default.red(`Failed to update ${path} on ${repo}:`));
2932
3481
  ui.write(result.stderr.toString());
2933
3482
  process.exit(1);
2934
3483
  }
2935
3484
  }
2936
- var import_picocolors10, sync_default;
3485
+ var import_picocolors13, sync_default;
2937
3486
  var init_sync = __esm(() => {
2938
3487
  init_dist();
2939
3488
  init_out();
2940
3489
  init_journal_config();
2941
3490
  init_journal_remote();
2942
- import_picocolors10 = __toESM(require_picocolors(), 1);
3491
+ import_picocolors13 = __toESM(require_picocolors(), 1);
2943
3492
  sync_default = defineCommand({
2944
3493
  meta: {
2945
3494
  name: "sync",
@@ -2973,70 +3522,70 @@ var init_sync = __esm(() => {
2973
3522
  project = sanitizeProjectName(project);
2974
3523
  }
2975
3524
  if (!project) {
2976
- ui.write(`${import_picocolors10.default.yellow("\u26A0")} No project mapping found.
3525
+ ui.write(`${import_picocolors13.default.yellow("\u26A0")} No project mapping found.
2977
3526
 
2978
- ` + `Run ${import_picocolors10.default.dim("dora init")} (or ${import_picocolors10.default.dim("doraval journal init")}) first, or pass ${import_picocolors10.default.dim("--project <name>")}.`);
3527
+ ` + `Run ${import_picocolors13.default.dim("dora init")} (or ${import_picocolors13.default.dim("doraval journal init")}) first, or pass ${import_picocolors13.default.dim("--project <name>")}.`);
2979
3528
  process.exit(1);
2980
3529
  }
2981
3530
  if (!config?.journal.repo) {
2982
- ui.write(`${import_picocolors10.default.red("\u2717")} No journal repo configured. Run ${import_picocolors10.default.dim("dora init")} (or ${import_picocolors10.default.dim("doraval journal init")}) first.`);
3531
+ ui.write(`${import_picocolors13.default.red("\u2717")} No journal repo configured. Run ${import_picocolors13.default.dim("dora init")} (or ${import_picocolors13.default.dim("doraval journal init")}) first.`);
2983
3532
  process.exit(1);
2984
3533
  }
2985
3534
  const ghCheck = ensureGhCli();
2986
3535
  if (!ghCheck.ok) {
2987
- ui.write(` ${import_picocolors10.default.red("\u2717")} ${import_picocolors10.default.white("The GitHub CLI (")}${import_picocolors10.default.bold("gh")}${import_picocolors10.default.white(") is not installed.")}
3536
+ ui.write(` ${import_picocolors13.default.red("\u2717")} ${import_picocolors13.default.white("The GitHub CLI (")}${import_picocolors13.default.bold("gh")}${import_picocolors13.default.white(") is not installed.")}
2988
3537
  `);
2989
- ui.write(` doraval uses ${import_picocolors10.default.bold("gh")} to fetch and sync journal files with GitHub.
3538
+ ui.write(` doraval uses ${import_picocolors13.default.bold("gh")} to fetch and sync journal files with GitHub.
2990
3539
  `);
2991
3540
  ui.write(` Install it:
2992
3541
  `);
2993
- ui.write(` macOS: ${import_picocolors10.default.dim("brew install gh")}`);
2994
- ui.write(` Linux: ${import_picocolors10.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
2995
- ui.write(` Windows: ${import_picocolors10.default.dim("winget install --id GitHub.cli")}
3542
+ ui.write(` macOS: ${import_picocolors13.default.dim("brew install gh")}`);
3543
+ ui.write(` Linux: ${import_picocolors13.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
3544
+ ui.write(` Windows: ${import_picocolors13.default.dim("winget install --id GitHub.cli")}
2996
3545
  `);
2997
- ui.write(` Then authenticate: ${import_picocolors10.default.dim("gh auth login")}
3546
+ ui.write(` Then authenticate: ${import_picocolors13.default.dim("gh auth login")}
2998
3547
  `);
2999
3548
  process.exit(1);
3000
3549
  }
3001
3550
  const journalRepo = config.journal.repo;
3002
3551
  const pendingDir = getPendingProjectDir(project);
3003
3552
  ui.write(`
3004
- ${import_picocolors10.default.bold(import_picocolors10.default.white("dora journal sync"))} \u2014 ${import_picocolors10.default.white(project)}
3553
+ ${import_picocolors13.default.bold(import_picocolors13.default.white("dora journal sync"))} \u2014 ${import_picocolors13.default.white(project)}
3005
3554
  `);
3006
- ui.write(` Journal repo: ${import_picocolors10.default.dim(import_picocolors10.default.gray(journalRepo))}`);
3555
+ ui.write(` Journal repo: ${import_picocolors13.default.dim(import_picocolors13.default.gray(journalRepo))}`);
3007
3556
  ensureDoravalDirs();
3008
3557
  const journalsDir = getJournalsDir();
3009
3558
  const remoteProjectPath = `projects/${project}.md`;
3010
- const localProjectPath = join8(journalsDir, `${project}.md`);
3011
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("Refreshing local cache from remote..."))}`);
3012
- const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md", join8(journalsDir, "global.md"));
3559
+ const localProjectPath = join10(journalsDir, `${project}.md`);
3560
+ ui.write(` ${import_picocolors13.default.dim(import_picocolors13.default.gray("Refreshing local cache from remote..."))}`);
3561
+ const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md", join10(journalsDir, "global.md"));
3013
3562
  if (!refreshGlobalRes.ok) {
3014
3563
  if (!refreshGlobalRes.isNotFound) {
3015
- ui.write(import_picocolors10.default.red(`Failed to fetch global.md from ${journalRepo}:`));
3564
+ ui.write(import_picocolors13.default.red(`Failed to fetch global.md from ${journalRepo}:`));
3016
3565
  ui.write(refreshGlobalRes.error);
3017
3566
  process.exit(1);
3018
3567
  }
3019
3568
  }
3020
3569
  const gotGlobal = refreshGlobalRes.ok && refreshGlobalRes.value;
3021
3570
  if (gotGlobal) {
3022
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("\u2713 global.md"))}`);
3571
+ ui.write(` ${import_picocolors13.default.dim(import_picocolors13.default.gray("\u2713 global.md"))}`);
3023
3572
  }
3024
3573
  const refreshProjectCacheRes = await refreshLocalJournalFile(journalRepo, remoteProjectPath, localProjectPath);
3025
3574
  if (!refreshProjectCacheRes.ok) {
3026
3575
  if (!refreshProjectCacheRes.isNotFound) {
3027
- ui.write(import_picocolors10.default.red(`Failed to fetch ${remoteProjectPath} from ${journalRepo}:`));
3576
+ ui.write(import_picocolors13.default.red(`Failed to fetch ${remoteProjectPath} from ${journalRepo}:`));
3028
3577
  ui.write(refreshProjectCacheRes.error);
3029
3578
  process.exit(1);
3030
3579
  }
3031
3580
  }
3032
3581
  const gotProjectCache = refreshProjectCacheRes.ok && refreshProjectCacheRes.value;
3033
3582
  if (gotProjectCache) {
3034
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray(`\u2713 ${remoteProjectPath}`))}`);
3583
+ ui.write(` ${import_picocolors13.default.dim(import_picocolors13.default.gray(`\u2713 ${remoteProjectPath}`))}`);
3035
3584
  }
3036
- const pendingFiles = existsSync9(pendingDir) ? readdirSync3(pendingDir).filter((f) => f.endsWith(".md") && f !== ".gitkeep").sort() : [];
3585
+ const pendingFiles = existsSync11(pendingDir) ? readdirSync4(pendingDir).filter((f) => f.endsWith(".md") && f !== ".gitkeep").sort() : [];
3037
3586
  if (pendingFiles.length === 0) {
3038
3587
  ui.write(`
3039
- ${import_picocolors10.default.yellow("\u26A0")} No pending entries. Local cache is now up to date.
3588
+ ${import_picocolors13.default.yellow("\u26A0")} No pending entries. Local cache is now up to date.
3040
3589
  `);
3041
3590
  process.exit(0);
3042
3591
  }
@@ -3049,7 +3598,7 @@ var init_sync = __esm(() => {
3049
3598
  let currentFile = null;
3050
3599
  if (!metaRes.ok) {
3051
3600
  if (!metaRes.isNotFound) {
3052
- ui.write(import_picocolors10.default.red(`Failed to fetch ${remotePath} from ${journalRepo}:`));
3601
+ ui.write(import_picocolors13.default.red(`Failed to fetch ${remotePath} from ${journalRepo}:`));
3053
3602
  ui.write(metaRes.error);
3054
3603
  process.exit(1);
3055
3604
  }
@@ -3060,14 +3609,14 @@ var init_sync = __esm(() => {
3060
3609
  existingContent = Buffer.from(currentFile.content, "base64").toString("utf8");
3061
3610
  currentSha = currentFile.sha;
3062
3611
  if (args.verbose)
3063
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("Found existing remote file (sha: " + (currentSha?.slice(0, 7) ?? "") + "...)"))}`);
3612
+ ui.write(` ${import_picocolors13.default.dim(import_picocolors13.default.gray("Found existing remote file (sha: " + (currentSha?.slice(0, 7) ?? "") + "...)"))}`);
3064
3613
  } else {
3065
3614
  if (args.verbose)
3066
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("No existing file on remote \u2014 will create it"))}`);
3615
+ ui.write(` ${import_picocolors13.default.dim(import_picocolors13.default.gray("No existing file on remote \u2014 will create it"))}`);
3067
3616
  }
3068
3617
  let newEntries = "";
3069
3618
  for (const file of pendingFiles) {
3070
- const fullPath = join8(pendingDir, file);
3619
+ const fullPath = join10(pendingDir, file);
3071
3620
  const entryContent = await Bun.file(fullPath).text();
3072
3621
  newEntries += `
3073
3622
  ` + entryContent.trim() + `
@@ -3087,37 +3636,215 @@ var init_sync = __esm(() => {
3087
3636
  const commitMessage = args.message || `journal: add ${pendingFiles.length} entr${pendingFiles.length === 1 ? "y" : "ies"} for ${project}`;
3088
3637
  if (args.verbose)
3089
3638
  ui.write(`
3090
- ${import_picocolors10.default.dim(import_picocolors10.default.gray("Pushing to remote..."))}`);
3639
+ ${import_picocolors13.default.dim(import_picocolors13.default.gray("Pushing to remote..."))}`);
3091
3640
  try {
3092
3641
  updateGitHubFile(journalRepo, remotePath, newContent, commitMessage, currentSha);
3093
- ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Successfully pushed to")} ${import_picocolors10.default.white(remotePath)}`);
3642
+ ui.write(` ${import_picocolors13.default.green("\u2713")} ${import_picocolors13.default.white("Successfully pushed to")} ${import_picocolors13.default.white(remotePath)}`);
3094
3643
  } catch (err) {
3095
- ui.write(`${import_picocolors10.default.red("\u2717")} ${import_picocolors10.default.white("Failed to push to GitHub.")}`);
3644
+ ui.write(`${import_picocolors13.default.red("\u2717")} ${import_picocolors13.default.white("Failed to push to GitHub.")}`);
3096
3645
  process.exit(1);
3097
3646
  }
3098
3647
  for (const file of pendingFiles) {
3099
- const fullPath = join8(pendingDir, file);
3648
+ const fullPath = join10(pendingDir, file);
3100
3649
  try {
3101
3650
  await Bun.file(fullPath).unlink();
3102
3651
  } catch {}
3103
3652
  }
3104
- ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Cleared local pending entries")}`);
3653
+ ui.write(` ${import_picocolors13.default.green("\u2713")} ${import_picocolors13.default.white("Cleared local pending entries")}`);
3105
3654
  try {
3106
3655
  const refreshRes = await refreshLocalJournalFile(journalRepo, remotePath, localProjectPath);
3107
3656
  if (!refreshRes.ok) {
3108
3657
  if (!refreshRes.isNotFound) {
3109
- ui.write(` ${import_picocolors10.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
3658
+ ui.write(` ${import_picocolors13.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
3110
3659
  }
3111
3660
  } else if (refreshRes.value) {
3112
3661
  if (args.verbose)
3113
- ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Re-fetched")} ${import_picocolors10.default.white(project)}.md ${import_picocolors10.default.white("into local cache")}`);
3662
+ ui.write(` ${import_picocolors13.default.green("\u2713")} ${import_picocolors13.default.white("Re-fetched")} ${import_picocolors13.default.white(project)}.md ${import_picocolors13.default.white("into local cache")}`);
3114
3663
  }
3115
3664
  } catch {
3116
- ui.write(` ${import_picocolors10.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
3665
+ ui.write(` ${import_picocolors13.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
3117
3666
  }
3118
3667
  ui.write(`
3119
- ${import_picocolors10.default.green("Done!")} ${import_picocolors10.default.white(pendingFiles.length + " entr" + (pendingFiles.length === 1 ? "y" : "ies") + " published.")}
3668
+ ${import_picocolors13.default.green("Done!")} ${import_picocolors13.default.white(pendingFiles.length + " entr" + (pendingFiles.length === 1 ? "y" : "ies") + " published.")}
3669
+ `);
3670
+ process.exit(0);
3671
+ }
3672
+ });
3673
+ });
3674
+
3675
+ // src/cli/commands/eval-history.ts
3676
+ var exports_eval_history = {};
3677
+ __export(exports_eval_history, {
3678
+ default: () => eval_history_default
3679
+ });
3680
+ import { existsSync as existsSync12, readdirSync as readdirSync5 } from "fs";
3681
+ import { join as join11 } from "path";
3682
+ var import_picocolors14, eval_history_default;
3683
+ var init_eval_history = __esm(() => {
3684
+ init_dist();
3685
+ init_out();
3686
+ init_journal_config();
3687
+ import_picocolors14 = __toESM(require_picocolors(), 1);
3688
+ eval_history_default = defineCommand({
3689
+ meta: {
3690
+ name: "history",
3691
+ description: "List stored eval results"
3692
+ },
3693
+ args: {
3694
+ limit: {
3695
+ type: "string",
3696
+ description: "Maximum number of results to show (default: 20)",
3697
+ default: "20"
3698
+ },
3699
+ skill: {
3700
+ type: "string",
3701
+ description: "Filter by skill name"
3702
+ },
3703
+ format: {
3704
+ type: "string",
3705
+ alias: "f",
3706
+ description: "Output format: table (default) or json",
3707
+ default: "table"
3708
+ }
3709
+ },
3710
+ async run({ args }) {
3711
+ const evalsDir = getEvalsDir();
3712
+ if (!existsSync12(evalsDir)) {
3713
+ ui.info("No eval history yet. Run: doraval eval");
3714
+ process.exit(0);
3715
+ }
3716
+ const files = readdirSync5(evalsDir).filter((f) => f.endsWith(".json")).sort().reverse();
3717
+ const limit = parseInt(String(args.limit), 10) || 20;
3718
+ const results = [];
3719
+ for (const file of files) {
3720
+ if (results.length >= limit)
3721
+ break;
3722
+ try {
3723
+ const raw = await Bun.file(join11(evalsDir, file)).text();
3724
+ const parsed = JSON.parse(raw);
3725
+ if (parsed.schemaVersion !== 1)
3726
+ continue;
3727
+ if (args.skill && !parsed.skill.includes(args.skill))
3728
+ continue;
3729
+ results.push(parsed);
3730
+ } catch {}
3731
+ }
3732
+ if (results.length === 0) {
3733
+ ui.info("No eval results found.");
3734
+ process.exit(0);
3735
+ }
3736
+ if (args.format === "json") {
3737
+ process.stdout.write(JSON.stringify(results, null, 2) + `
3738
+ `);
3739
+ } else {
3740
+ ui.heading("doraval eval history");
3741
+ ui.write(` ${"DATE".padEnd(20)} ${"SESSION TITLE".padEnd(35)} ${"SKILL".padEnd(35)} RESULT`);
3742
+ ui.write(` ${"-".repeat(100)}`);
3743
+ for (const r of results) {
3744
+ const date = r.timestamp.slice(0, 10);
3745
+ const title = (r.sessionTitle ?? r.sessionId.slice(0, 8)).slice(0, 33).padEnd(35);
3746
+ const skill = r.skill.slice(0, 33).padEnd(35);
3747
+ const verdictColor = r.verdict === "PASS" ? import_picocolors14.default.green : r.verdict === "FAIL" ? import_picocolors14.default.red : import_picocolors14.default.yellow;
3748
+ ui.write(` ${date.padEnd(20)} ${title} ${skill} ${verdictColor(r.verdict)}`);
3749
+ }
3750
+ ui.blank();
3751
+ }
3752
+ process.exit(0);
3753
+ }
3754
+ });
3755
+ });
3756
+
3757
+ // src/cli/commands/config.ts
3758
+ var exports_config = {};
3759
+ __export(exports_config, {
3760
+ default: () => config_default
3761
+ });
3762
+ var {YAML: YAML4 } = globalThis.Bun;
3763
+ function getNestedValue(obj, keyPath) {
3764
+ const parts = keyPath.split(".");
3765
+ let current = obj;
3766
+ for (const part of parts) {
3767
+ if (current === null || typeof current !== "object")
3768
+ return;
3769
+ current = current[part];
3770
+ }
3771
+ return current;
3772
+ }
3773
+ function setNestedValue(obj, keyPath, value) {
3774
+ const parts = keyPath.split(".");
3775
+ let current = obj;
3776
+ for (let i = 0;i < parts.length - 1; i++) {
3777
+ const part = parts[i];
3778
+ if (typeof current[part] !== "object" || current[part] === null) {
3779
+ current[part] = {};
3780
+ }
3781
+ current = current[part];
3782
+ }
3783
+ current[parts[parts.length - 1]] = value;
3784
+ }
3785
+ function coerceValue(raw) {
3786
+ if (raw === "true")
3787
+ return true;
3788
+ if (raw === "false")
3789
+ return false;
3790
+ const num = Number(raw);
3791
+ if (!isNaN(num) && raw.trim() !== "")
3792
+ return num;
3793
+ return raw;
3794
+ }
3795
+ var configSet, configGet, config_default;
3796
+ var init_config = __esm(() => {
3797
+ init_dist();
3798
+ init_out();
3799
+ init_journal_config();
3800
+ configSet = defineCommand({
3801
+ meta: { name: "set", description: "Set a config value" },
3802
+ args: {
3803
+ key: { type: "positional", description: "Dot-notation key (e.g. eval.model)", required: true },
3804
+ value: { type: "positional", description: "Value to set", required: true }
3805
+ },
3806
+ async run({ args }) {
3807
+ ensureDoravalDirs();
3808
+ const config = await readConfig() ?? {
3809
+ journal: { repo: "", projects: {} }
3810
+ };
3811
+ const coerced = coerceValue(String(args.value));
3812
+ setNestedValue(config, String(args.key), coerced);
3813
+ await writeConfig(config);
3814
+ ui.success(`${args.key} = ${JSON.stringify(coerced)}`);
3815
+ process.exit(0);
3816
+ }
3817
+ });
3818
+ configGet = defineCommand({
3819
+ meta: { name: "get", description: "Get a config value (omit key to print all)" },
3820
+ args: {
3821
+ key: { type: "positional", description: "Dot-notation key (omit to print all)", required: false }
3822
+ },
3823
+ async run({ args }) {
3824
+ const config = await readConfig();
3825
+ if (!config) {
3826
+ ui.info("No config found. Run: doraval init");
3827
+ process.exit(0);
3828
+ }
3829
+ if (!args.key) {
3830
+ process.stdout.write(YAML4.stringify(config));
3831
+ process.exit(0);
3832
+ }
3833
+ const value = getNestedValue(config, String(args.key));
3834
+ if (value === undefined) {
3835
+ ui.info(`${args.key}: (not set)`);
3836
+ } else {
3837
+ process.stdout.write(`${JSON.stringify(value)}
3120
3838
  `);
3839
+ }
3840
+ process.exit(0);
3841
+ }
3842
+ });
3843
+ config_default = defineCommand({
3844
+ meta: { name: "config", description: "Get or set doraval configuration (dot-notation keys)" },
3845
+ subCommands: { set: configSet, get: configGet },
3846
+ run() {
3847
+ ui.info("Usage: doraval config set <key> <value> | doraval config get [key]");
3121
3848
  process.exit(0);
3122
3849
  }
3123
3850
  });
@@ -3175,15 +3902,15 @@ var init_spec = __esm(() => {
3175
3902
  });
3176
3903
 
3177
3904
  // src/cli/commands/claude/context.ts
3178
- import { existsSync as existsSync10, readdirSync as readdirSync4 } from "fs";
3179
- import { join as join9 } from "path";
3905
+ import { existsSync as existsSync13, readdirSync as readdirSync6 } from "fs";
3906
+ import { join as join12 } from "path";
3180
3907
  function detectContext(cwd = process.cwd()) {
3181
3908
  const claudeSpec = getProviderSpec("claude");
3182
- const hasClaudeDir = existsSync10(join9(cwd, ".claude"));
3183
- const hasPluginManifest = existsSync10(join9(cwd, claudeSpec.manifestPath));
3909
+ const hasClaudeDir = existsSync13(join12(cwd, ".claude"));
3910
+ const hasPluginManifest = existsSync13(join12(cwd, claudeSpec.manifestPath));
3184
3911
  let looseSkillFiles = [];
3185
3912
  try {
3186
- const files = readdirSync4(cwd);
3913
+ const files = readdirSync6(cwd);
3187
3914
  looseSkillFiles = files.filter((f) => {
3188
3915
  if (!f.endsWith(".md") || f.startsWith("."))
3189
3916
  return false;
@@ -3213,22 +3940,22 @@ __export(exports_new, {
3213
3940
  default: () => new_default,
3214
3941
  decidePath: () => decidePath
3215
3942
  });
3216
- import { join as join10, basename as basename2, dirname as dirname2 } from "path";
3217
- import { mkdirSync as mkdirSync3, writeFileSync, existsSync as existsSync11 } from "fs";
3943
+ import { join as join13, basename as basename3, dirname as dirname2 } from "path";
3944
+ import { mkdirSync as mkdirSync3, writeFileSync, existsSync as existsSync14 } from "fs";
3218
3945
  function decidePath(ctx, intent, providedName) {
3219
3946
  const rawName = providedName || "";
3220
3947
  let decisionPath = "standalone";
3221
3948
  let targetDir = ctx.cwd;
3222
3949
  let shouldCreateDir = false;
3223
3950
  let migrateExisting = false;
3224
- const useCurrentDirAsRoot = rawName === "." || rawName === basename2(ctx.cwd) || !rawName;
3951
+ const useCurrentDirAsRoot = rawName === "." || rawName === basename3(ctx.cwd) || !rawName;
3225
3952
  if (intent === "distribute" || intent === "self-later" && ctx.looseSkillFiles.length > 0 && !ctx.hasClaudeDir) {
3226
3953
  decisionPath = "plugin";
3227
3954
  if (useCurrentDirAsRoot) {
3228
3955
  targetDir = ctx.cwd;
3229
3956
  shouldCreateDir = false;
3230
3957
  } else {
3231
- targetDir = join10(ctx.cwd, rawName);
3958
+ targetDir = join13(ctx.cwd, rawName);
3232
3959
  shouldCreateDir = true;
3233
3960
  }
3234
3961
  migrateExisting = ctx.looseSkillFiles.length > 0;
@@ -3238,7 +3965,7 @@ function decidePath(ctx, intent, providedName) {
3238
3965
  targetDir = ctx.cwd;
3239
3966
  shouldCreateDir = false;
3240
3967
  } else {
3241
- targetDir = join10(ctx.cwd, rawName);
3968
+ targetDir = join13(ctx.cwd, rawName);
3242
3969
  shouldCreateDir = true;
3243
3970
  }
3244
3971
  } else if (decisionPath === "standalone") {
@@ -3246,7 +3973,7 @@ function decidePath(ctx, intent, providedName) {
3246
3973
  targetDir = ctx.cwd;
3247
3974
  shouldCreateDir = false;
3248
3975
  } else {
3249
- targetDir = join10(ctx.cwd, rawName);
3976
+ targetDir = join13(ctx.cwd, rawName);
3250
3977
  shouldCreateDir = true;
3251
3978
  }
3252
3979
  }
@@ -3254,7 +3981,7 @@ function decidePath(ctx, intent, providedName) {
3254
3981
  }
3255
3982
  function scaffold(decision, ctx, migrateContent) {
3256
3983
  const { targetDir, path, shouldCreateDir } = decision;
3257
- if (existsSync11(targetDir) && shouldCreateDir) {
3984
+ if (existsSync14(targetDir) && shouldCreateDir) {
3258
3985
  ui.fail("Target already exists");
3259
3986
  process.exit(1);
3260
3987
  }
@@ -3262,7 +3989,7 @@ function scaffold(decision, ctx, migrateContent) {
3262
3989
  mkdirSync3(targetDir, { recursive: true });
3263
3990
  }
3264
3991
  if (path === "plugin") {
3265
- const pluginName = basename2(targetDir);
3992
+ const pluginName = basename3(targetDir);
3266
3993
  const claudeSpec = getProviderSpec("claude");
3267
3994
  const claudeManifestDir = dirname2(claudeSpec.manifestPath);
3268
3995
  const pluginJson = {
@@ -3271,8 +3998,8 @@ function scaffold(decision, ctx, migrateContent) {
3271
3998
  version: "0.1.0",
3272
3999
  keywords: ["example-keyword", "another-keyword"]
3273
4000
  };
3274
- mkdirSync3(join10(targetDir, claudeManifestDir), { recursive: true });
3275
- writeFileSync(join10(targetDir, claudeSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
4001
+ mkdirSync3(join13(targetDir, claudeManifestDir), { recursive: true });
4002
+ writeFileSync(join13(targetDir, claudeSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
3276
4003
  const marketplaceJson = {
3277
4004
  name: pluginName,
3278
4005
  version: "0.1.0",
@@ -3283,9 +4010,9 @@ function scaffold(decision, ctx, migrateContent) {
3283
4010
  license: "MIT",
3284
4011
  keywords: ["claude-code", "skills", "plugin"]
3285
4012
  };
3286
- writeFileSync(join10(targetDir, "marketplace.json"), JSON.stringify(marketplaceJson, null, 2));
4013
+ writeFileSync(join13(targetDir, "marketplace.json"), JSON.stringify(marketplaceJson, null, 2));
3287
4014
  const demoSkillName = "doraval";
3288
- mkdirSync3(join10(targetDir, "skills", demoSkillName), { recursive: true });
4015
+ mkdirSync3(join13(targetDir, "skills", demoSkillName), { recursive: true });
3289
4016
  let skillContent;
3290
4017
  if (migrateContent) {
3291
4018
  skillContent = migrateContent;
@@ -3309,19 +4036,19 @@ When you need to check a skill or plugin:
3309
4036
 
3310
4037
  Always run \`doraval validate\` before sharing or publishing a plugin. This skill demonstrates a complete, self-referential example of using doraval inside a generated plugin.`;
3311
4038
  }
3312
- writeFileSync(join10(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
3313
- const readmePath = join10(targetDir, "README.md");
3314
- if (!existsSync11(readmePath)) {
4039
+ writeFileSync(join13(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
4040
+ const readmePath = join13(targetDir, "README.md");
4041
+ if (!existsSync14(readmePath)) {
3315
4042
  writeFileSync(readmePath, "# " + pluginName + `
3316
4043
 
3317
4044
  Claude Code plugin scaffolded by doraval.`);
3318
4045
  }
3319
4046
  } else {
3320
- mkdirSync3(join10(targetDir, ".claude", "skills", "my-skill"), { recursive: true });
4047
+ mkdirSync3(join13(targetDir, ".claude", "skills", "my-skill"), { recursive: true });
3321
4048
  const skillBody = migrateContent || `# My Skill
3322
4049
 
3323
4050
  Basic starter.`;
3324
- writeFileSync(join10(targetDir, ".claude", "skills", "my-skill", "SKILL.md"), `---
4051
+ writeFileSync(join13(targetDir, ".claude", "skills", "my-skill", "SKILL.md"), `---
3325
4052
  name: my-skill
3326
4053
  description: Starter
3327
4054
  ---
@@ -3329,14 +4056,14 @@ description: Starter
3329
4056
  ${skillBody}`);
3330
4057
  }
3331
4058
  }
3332
- var import_picocolors11, new_default;
4059
+ var import_picocolors15, new_default;
3333
4060
  var init_new = __esm(() => {
3334
4061
  init_dist();
3335
4062
  init_out();
3336
4063
  init_context2();
3337
4064
  init_prompt();
3338
4065
  init_spec();
3339
- import_picocolors11 = __toESM(require_picocolors(), 1);
4066
+ import_picocolors15 = __toESM(require_picocolors(), 1);
3340
4067
  new_default = defineCommand({
3341
4068
  meta: {
3342
4069
  name: "new",
@@ -3375,8 +4102,8 @@ var init_new = __esm(() => {
3375
4102
  }
3376
4103
  scaffold(decision, ctx, migrateContent);
3377
4104
  ui.write(`
3378
- ${import_picocolors11.default.green("\u2713")} Created ${decision.path} at ${import_picocolors11.default.bold(decision.targetDir)}`);
3379
- const cmdName = decision.path === "plugin" ? `/${basename2(decision.targetDir)}:doraval` : "/my-skill";
4105
+ ${import_picocolors15.default.green("\u2713")} Created ${decision.path} at ${import_picocolors15.default.bold(decision.targetDir)}`);
4106
+ const cmdName = decision.path === "plugin" ? `/${basename3(decision.targetDir)}:doraval` : "/my-skill";
3380
4107
  ui.info(` Command: ${cmdName}`);
3381
4108
  if (decision.path === "plugin") {
3382
4109
  const claudeSpec = getProviderSpec("claude");
@@ -3401,8 +4128,8 @@ var exports_bump = {};
3401
4128
  __export(exports_bump, {
3402
4129
  default: () => bump_default
3403
4130
  });
3404
- import { resolve as resolve4, join as join11, dirname as dirname3, relative } from "path";
3405
- import { existsSync as existsSync12, readFileSync, writeFileSync as writeFileSync2, readdirSync as readdirSync5, statSync } from "fs";
4131
+ import { resolve as resolve4, join as join14, dirname as dirname3, relative } from "path";
4132
+ import { existsSync as existsSync15, readFileSync as readFileSync2, writeFileSync as writeFileSync2, readdirSync as readdirSync7, statSync as statSync2 } from "fs";
3406
4133
  function bumpVersion(current, type) {
3407
4134
  if (/^\d+\.\d+\.\d+$/.test(type))
3408
4135
  return type;
@@ -3422,7 +4149,7 @@ function bumpVersion(current, type) {
3422
4149
  }
3423
4150
  function readJson2(p) {
3424
4151
  try {
3425
- const content = readFileSync(p, "utf8");
4152
+ const content = readFileSync2(p, "utf8");
3426
4153
  return JSON.parse(content);
3427
4154
  } catch {
3428
4155
  return null;
@@ -3480,15 +4207,15 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
3480
4207
  return results;
3481
4208
  let entries;
3482
4209
  try {
3483
- entries = readdirSync5(dir);
4210
+ entries = readdirSync7(dir);
3484
4211
  } catch {
3485
4212
  return results;
3486
4213
  }
3487
4214
  for (const entry of entries) {
3488
- const full = join11(dir, entry);
4215
+ const full = join14(dir, entry);
3489
4216
  let st;
3490
4217
  try {
3491
- st = statSync(full);
4218
+ st = statSync2(full);
3492
4219
  } catch {
3493
4220
  continue;
3494
4221
  }
@@ -3520,11 +4247,11 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
3520
4247
  }
3521
4248
  return results;
3522
4249
  }
3523
- var import_picocolors12, bump_default;
4250
+ var import_picocolors16, bump_default;
3524
4251
  var init_bump = __esm(() => {
3525
4252
  init_dist();
3526
4253
  init_out();
3527
- import_picocolors12 = __toESM(require_picocolors(), 1);
4254
+ import_picocolors16 = __toESM(require_picocolors(), 1);
3528
4255
  bump_default = defineCommand({
3529
4256
  meta: {
3530
4257
  name: "bump",
@@ -3558,7 +4285,7 @@ var init_bump = __esm(() => {
3558
4285
  }
3559
4286
  const isKnownType = ["patch", "minor", "major"].includes(rawType) || /^\d+\.\d+\.\d+$/.test(rawType);
3560
4287
  const maybePath = resolve4(rawType);
3561
- const looksLikeDir = existsSync12(maybePath) || rawType === "." || rawType.startsWith("./") || rawType.startsWith("../");
4288
+ const looksLikeDir = existsSync15(maybePath) || rawType === "." || rawType.startsWith("./") || rawType.startsWith("../");
3562
4289
  if (!isKnownType && looksLikeDir) {
3563
4290
  targetPath = rawType;
3564
4291
  rawType = "patch";
@@ -3567,7 +4294,7 @@ var init_bump = __esm(() => {
3567
4294
  process.exit(1);
3568
4295
  }
3569
4296
  const root = resolve4(targetPath);
3570
- if (!existsSync12(root)) {
4297
+ if (!existsSync15(root)) {
3571
4298
  ui.fail(`Path does not exist: ${root}`);
3572
4299
  process.exit(1);
3573
4300
  }
@@ -3633,9 +4360,9 @@ var init_bump = __esm(() => {
3633
4360
  }
3634
4361
  writeJson2(t.file, json);
3635
4362
  if (didRootUpdate && current) {
3636
- ui.success(`${t.label}: ${import_picocolors12.default.dim(current)} \u2192 ${import_picocolors12.default.green(next)}`);
4363
+ ui.success(`${t.label}: ${import_picocolors16.default.dim(current)} \u2192 ${import_picocolors16.default.green(next)}`);
3637
4364
  } else if (didRootUpdate) {
3638
- ui.success(`${t.label}: ${import_picocolors12.default.green(next)}`);
4365
+ ui.success(`${t.label}: ${import_picocolors16.default.green(next)}`);
3639
4366
  } else {
3640
4367
  ui.success(`${t.label} (no root version)`);
3641
4368
  }
@@ -3658,16 +4385,16 @@ var init_bump = __esm(() => {
3658
4385
  });
3659
4386
 
3660
4387
  // src/cli/commands/codex/context.ts
3661
- import { existsSync as existsSync13, readdirSync as readdirSync6 } from "fs";
3662
- import { join as join12 } from "path";
4388
+ import { existsSync as existsSync16, readdirSync as readdirSync8 } from "fs";
4389
+ import { join as join15 } from "path";
3663
4390
  function detectContext2(cwd = process.cwd()) {
3664
4391
  const codexSpec = getProviderSpec("codex");
3665
- const hasCodexDir = existsSync13(join12(cwd, ".codex"));
3666
- const hasPluginManifest = existsSync13(join12(cwd, codexSpec.manifestPath));
3667
- const hasMarketplace = existsSync13(join12(cwd, ".agents", "plugins", "marketplace.json")) || existsSync13(join12(cwd, codexSpec.manifestPath));
4392
+ const hasCodexDir = existsSync16(join15(cwd, ".codex"));
4393
+ const hasPluginManifest = existsSync16(join15(cwd, codexSpec.manifestPath));
4394
+ const hasMarketplace = existsSync16(join15(cwd, ".agents", "plugins", "marketplace.json")) || existsSync16(join15(cwd, codexSpec.manifestPath));
3668
4395
  let looseSkillFiles = [];
3669
4396
  try {
3670
- const files = readdirSync6(cwd);
4397
+ const files = readdirSync8(cwd);
3671
4398
  looseSkillFiles = files.filter((f) => {
3672
4399
  if (!f.endsWith(".md") || f.startsWith("."))
3673
4400
  return false;
@@ -3698,22 +4425,22 @@ __export(exports_new2, {
3698
4425
  default: () => new_default2,
3699
4426
  decidePath: () => decidePath2
3700
4427
  });
3701
- import { join as join13, basename as basename3, dirname as dirname4 } from "path";
3702
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, existsSync as existsSync14 } from "fs";
4428
+ import { join as join16, basename as basename4, dirname as dirname4 } from "path";
4429
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, existsSync as existsSync17 } from "fs";
3703
4430
  function decidePath2(ctx, intent, providedName) {
3704
4431
  const rawName = providedName || "";
3705
4432
  let decisionPath = "standalone";
3706
4433
  let targetDir = ctx.cwd;
3707
4434
  let shouldCreateDir = false;
3708
4435
  let migrateExisting = false;
3709
- const useCurrentDirAsRoot = rawName === "." || rawName === basename3(ctx.cwd) || !rawName;
4436
+ const useCurrentDirAsRoot = rawName === "." || rawName === basename4(ctx.cwd) || !rawName;
3710
4437
  if (intent === "distribute" || intent === "self-later" && ctx.looseSkillFiles.length > 0 && !ctx.hasPluginManifest) {
3711
4438
  decisionPath = "plugin";
3712
4439
  if (useCurrentDirAsRoot) {
3713
4440
  targetDir = ctx.cwd;
3714
4441
  shouldCreateDir = false;
3715
4442
  } else {
3716
- targetDir = join13(ctx.cwd, rawName);
4443
+ targetDir = join16(ctx.cwd, rawName);
3717
4444
  shouldCreateDir = true;
3718
4445
  }
3719
4446
  migrateExisting = ctx.looseSkillFiles.length > 0;
@@ -3723,7 +4450,7 @@ function decidePath2(ctx, intent, providedName) {
3723
4450
  targetDir = ctx.cwd;
3724
4451
  shouldCreateDir = false;
3725
4452
  } else {
3726
- targetDir = join13(ctx.cwd, rawName);
4453
+ targetDir = join16(ctx.cwd, rawName);
3727
4454
  shouldCreateDir = true;
3728
4455
  }
3729
4456
  } else if (decisionPath === "standalone") {
@@ -3731,7 +4458,7 @@ function decidePath2(ctx, intent, providedName) {
3731
4458
  targetDir = ctx.cwd;
3732
4459
  shouldCreateDir = false;
3733
4460
  } else {
3734
- targetDir = join13(ctx.cwd, rawName);
4461
+ targetDir = join16(ctx.cwd, rawName);
3735
4462
  shouldCreateDir = true;
3736
4463
  }
3737
4464
  }
@@ -3739,7 +4466,7 @@ function decidePath2(ctx, intent, providedName) {
3739
4466
  }
3740
4467
  function scaffold2(decision, ctx, migrateContent) {
3741
4468
  const { targetDir, path, shouldCreateDir } = decision;
3742
- if (existsSync14(targetDir) && shouldCreateDir) {
4469
+ if (existsSync17(targetDir) && shouldCreateDir) {
3743
4470
  ui.fail("Target already exists");
3744
4471
  process.exit(1);
3745
4472
  }
@@ -3747,7 +4474,7 @@ function scaffold2(decision, ctx, migrateContent) {
3747
4474
  mkdirSync4(targetDir, { recursive: true });
3748
4475
  }
3749
4476
  if (path === "plugin") {
3750
- const pluginName = basename3(targetDir);
4477
+ const pluginName = basename4(targetDir);
3751
4478
  const codexSpec = getProviderSpec("codex");
3752
4479
  const codexManifestDir = dirname4(codexSpec.manifestPath);
3753
4480
  const pluginJson = {
@@ -3762,10 +4489,10 @@ function scaffold2(decision, ctx, migrateContent) {
3762
4489
  },
3763
4490
  keywords: ["example-keyword", "another-keyword"]
3764
4491
  };
3765
- mkdirSync4(join13(targetDir, codexManifestDir), { recursive: true });
3766
- writeFileSync3(join13(targetDir, codexSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
4492
+ mkdirSync4(join16(targetDir, codexManifestDir), { recursive: true });
4493
+ writeFileSync3(join16(targetDir, codexSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
3767
4494
  const marketplaceDir = dirname4(codexSpec.marketplacePath);
3768
- mkdirSync4(join13(targetDir, marketplaceDir), { recursive: true });
4495
+ mkdirSync4(join16(targetDir, marketplaceDir), { recursive: true });
3769
4496
  const marketplaceJson = {
3770
4497
  name: "local",
3771
4498
  interface: {
@@ -3786,9 +4513,9 @@ function scaffold2(decision, ctx, migrateContent) {
3786
4513
  }
3787
4514
  ]
3788
4515
  };
3789
- writeFileSync3(join13(targetDir, codexSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
4516
+ writeFileSync3(join16(targetDir, codexSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
3790
4517
  const demoSkillName = "doraval";
3791
- mkdirSync4(join13(targetDir, "skills", demoSkillName), { recursive: true });
4518
+ mkdirSync4(join16(targetDir, "skills", demoSkillName), { recursive: true });
3792
4519
  let skillContent;
3793
4520
  if (migrateContent) {
3794
4521
  skillContent = migrateContent;
@@ -3819,19 +4546,19 @@ To test in Codex:
3819
4546
  3. Open the plugin directory, select your local marketplace, and enable the plugin.
3820
4547
  4. Invoke the demo with /${pluginName}:doraval`;
3821
4548
  }
3822
- writeFileSync3(join13(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
3823
- const readmePath = join13(targetDir, "README.md");
3824
- if (!existsSync14(readmePath)) {
4549
+ writeFileSync3(join16(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
4550
+ const readmePath = join16(targetDir, "README.md");
4551
+ if (!existsSync17(readmePath)) {
3825
4552
  writeFileSync3(readmePath, "# " + pluginName + `
3826
4553
 
3827
4554
  Codex plugin scaffolded by doraval.`);
3828
4555
  }
3829
4556
  } else {
3830
- mkdirSync4(join13(targetDir, "skills", "doraval"), { recursive: true });
4557
+ mkdirSync4(join16(targetDir, "skills", "doraval"), { recursive: true });
3831
4558
  const skillBody = migrateContent || `# My Skill
3832
4559
 
3833
4560
  Basic starter for Codex.`;
3834
- writeFileSync3(join13(targetDir, "skills", "doraval", "SKILL.md"), `---
4561
+ writeFileSync3(join16(targetDir, "skills", "doraval", "SKILL.md"), `---
3835
4562
  name: doraval
3836
4563
  description: Starter (local skill)
3837
4564
  ---
@@ -3839,14 +4566,14 @@ description: Starter (local skill)
3839
4566
  ${skillBody}`);
3840
4567
  }
3841
4568
  }
3842
- var import_picocolors13, new_default2;
4569
+ var import_picocolors17, new_default2;
3843
4570
  var init_new2 = __esm(() => {
3844
4571
  init_dist();
3845
4572
  init_out();
3846
4573
  init_context3();
3847
4574
  init_prompt();
3848
4575
  init_spec();
3849
- import_picocolors13 = __toESM(require_picocolors(), 1);
4576
+ import_picocolors17 = __toESM(require_picocolors(), 1);
3850
4577
  new_default2 = defineCommand({
3851
4578
  meta: {
3852
4579
  name: "new",
@@ -3885,8 +4612,8 @@ var init_new2 = __esm(() => {
3885
4612
  }
3886
4613
  scaffold2(decision, ctx, migrateContent);
3887
4614
  ui.write(`
3888
- ${import_picocolors13.default.green("\u2713")} Created ${decision.path} at ${import_picocolors13.default.bold(decision.targetDir)}`);
3889
- const cmdName = decision.path === "plugin" ? `/${basename3(decision.targetDir)}:doraval` : "/doraval (local skill)";
4615
+ ${import_picocolors17.default.green("\u2713")} Created ${decision.path} at ${import_picocolors17.default.bold(decision.targetDir)}`);
4616
+ const cmdName = decision.path === "plugin" ? `/${basename4(decision.targetDir)}:doraval` : "/doraval (local skill)";
3890
4617
  ui.info(` Command: ${cmdName}`);
3891
4618
  if (decision.path === "plugin") {
3892
4619
  ui.info(` Codex manifest: .codex-plugin/plugin.json`);
@@ -3907,14 +4634,14 @@ var init_new2 = __esm(() => {
3907
4634
  });
3908
4635
 
3909
4636
  // src/cli/commands/cursor/context.ts
3910
- import { existsSync as existsSync15, readdirSync as readdirSync7 } from "fs";
3911
- import { join as join14 } from "path";
4637
+ import { existsSync as existsSync18, readdirSync as readdirSync9 } from "fs";
4638
+ import { join as join17 } from "path";
3912
4639
  function detectContext3(cwd = process.cwd()) {
3913
- const hasCursorDir = existsSync15(join14(cwd, ".cursor"));
3914
- const hasPluginManifest = existsSync15(join14(cwd, ".cursor-plugin", "plugin.json"));
4640
+ const hasCursorDir = existsSync18(join17(cwd, ".cursor"));
4641
+ const hasPluginManifest = existsSync18(join17(cwd, ".cursor-plugin", "plugin.json"));
3915
4642
  let looseSkillFiles = [];
3916
4643
  try {
3917
- const files = readdirSync7(cwd);
4644
+ const files = readdirSync9(cwd);
3918
4645
  looseSkillFiles = files.filter((f) => {
3919
4646
  if (!f.endsWith(".md") || f.startsWith("."))
3920
4647
  return false;
@@ -3942,22 +4669,22 @@ __export(exports_new3, {
3942
4669
  default: () => new_default3,
3943
4670
  decidePath: () => decidePath3
3944
4671
  });
3945
- import { join as join15, basename as basename4, dirname as dirname5 } from "path";
3946
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, existsSync as existsSync16 } from "fs";
4672
+ import { join as join18, basename as basename5, dirname as dirname5 } from "path";
4673
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, existsSync as existsSync19 } from "fs";
3947
4674
  function decidePath3(ctx, intent, providedName) {
3948
4675
  const rawName = providedName || "";
3949
4676
  let decisionPath = "standalone";
3950
4677
  let targetDir = ctx.cwd;
3951
4678
  let shouldCreateDir = false;
3952
4679
  let migrateExisting = false;
3953
- const useCurrentDirAsRoot = rawName === "." || rawName === basename4(ctx.cwd) || !rawName;
4680
+ const useCurrentDirAsRoot = rawName === "." || rawName === basename5(ctx.cwd) || !rawName;
3954
4681
  if (intent === "distribute" || intent === "self-later" && ctx.looseSkillFiles.length > 0 && !ctx.hasPluginManifest) {
3955
4682
  decisionPath = "plugin";
3956
4683
  if (useCurrentDirAsRoot) {
3957
4684
  targetDir = ctx.cwd;
3958
4685
  shouldCreateDir = false;
3959
4686
  } else {
3960
- targetDir = join15(ctx.cwd, rawName);
4687
+ targetDir = join18(ctx.cwd, rawName);
3961
4688
  shouldCreateDir = true;
3962
4689
  }
3963
4690
  migrateExisting = ctx.looseSkillFiles.length > 0;
@@ -3967,7 +4694,7 @@ function decidePath3(ctx, intent, providedName) {
3967
4694
  targetDir = ctx.cwd;
3968
4695
  shouldCreateDir = false;
3969
4696
  } else {
3970
- targetDir = join15(ctx.cwd, rawName);
4697
+ targetDir = join18(ctx.cwd, rawName);
3971
4698
  shouldCreateDir = true;
3972
4699
  }
3973
4700
  } else if (decisionPath === "standalone") {
@@ -3975,7 +4702,7 @@ function decidePath3(ctx, intent, providedName) {
3975
4702
  targetDir = ctx.cwd;
3976
4703
  shouldCreateDir = false;
3977
4704
  } else {
3978
- targetDir = join15(ctx.cwd, rawName);
4705
+ targetDir = join18(ctx.cwd, rawName);
3979
4706
  shouldCreateDir = true;
3980
4707
  }
3981
4708
  }
@@ -3983,7 +4710,7 @@ function decidePath3(ctx, intent, providedName) {
3983
4710
  }
3984
4711
  function scaffold3(decision, ctx, migrateContent) {
3985
4712
  const { targetDir, path, shouldCreateDir } = decision;
3986
- if (existsSync16(targetDir) && shouldCreateDir) {
4713
+ if (existsSync19(targetDir) && shouldCreateDir) {
3987
4714
  ui.fail("Target already exists");
3988
4715
  process.exit(1);
3989
4716
  }
@@ -3991,7 +4718,7 @@ function scaffold3(decision, ctx, migrateContent) {
3991
4718
  mkdirSync5(targetDir, { recursive: true });
3992
4719
  }
3993
4720
  if (path === "plugin") {
3994
- const pluginName = basename4(targetDir);
4721
+ const pluginName = basename5(targetDir);
3995
4722
  const cursorSpec = getProviderSpec("cursor");
3996
4723
  const cursorManifestDir = dirname5(cursorSpec.manifestPath);
3997
4724
  const pluginJson = {
@@ -4002,10 +4729,10 @@ function scaffold3(decision, ctx, migrateContent) {
4002
4729
  displayName: pluginName,
4003
4730
  keywords: ["example-keyword", "another-keyword"]
4004
4731
  };
4005
- mkdirSync5(join15(targetDir, cursorManifestDir), { recursive: true });
4006
- writeFileSync4(join15(targetDir, cursorSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
4732
+ mkdirSync5(join18(targetDir, cursorManifestDir), { recursive: true });
4733
+ writeFileSync4(join18(targetDir, cursorSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
4007
4734
  const marketplaceDir = dirname5(cursorSpec.marketplacePath);
4008
- mkdirSync5(join15(targetDir, marketplaceDir), { recursive: true });
4735
+ mkdirSync5(join18(targetDir, marketplaceDir), { recursive: true });
4009
4736
  const marketplaceJson = {
4010
4737
  name: pluginName,
4011
4738
  version: "0.1.0",
@@ -4016,9 +4743,9 @@ function scaffold3(decision, ctx, migrateContent) {
4016
4743
  license: "MIT",
4017
4744
  keywords: ["cursor", "skills", "plugin"]
4018
4745
  };
4019
- writeFileSync4(join15(targetDir, cursorSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
4746
+ writeFileSync4(join18(targetDir, cursorSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
4020
4747
  const demoSkillName = "doraval";
4021
- mkdirSync5(join15(targetDir, "skills", demoSkillName), { recursive: true });
4748
+ mkdirSync5(join18(targetDir, "skills", demoSkillName), { recursive: true });
4022
4749
  let skillContent;
4023
4750
  if (migrateContent) {
4024
4751
  skillContent = migrateContent;
@@ -4047,19 +4774,19 @@ To test in Cursor:
4047
4774
  1. Open the plugin directory or add via marketplace.
4048
4775
  2. The demo skill will be available.`;
4049
4776
  }
4050
- writeFileSync4(join15(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
4051
- const readmePath = join15(targetDir, "README.md");
4052
- if (!existsSync16(readmePath)) {
4777
+ writeFileSync4(join18(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
4778
+ const readmePath = join18(targetDir, "README.md");
4779
+ if (!existsSync19(readmePath)) {
4053
4780
  writeFileSync4(readmePath, "# " + pluginName + `
4054
4781
 
4055
4782
  Cursor plugin scaffolded by doraval.`);
4056
4783
  }
4057
4784
  } else {
4058
- mkdirSync5(join15(targetDir, "skills", "doraval"), { recursive: true });
4785
+ mkdirSync5(join18(targetDir, "skills", "doraval"), { recursive: true });
4059
4786
  const skillBody = migrateContent || `# My Skill
4060
4787
 
4061
4788
  Basic starter for Cursor.`;
4062
- writeFileSync4(join15(targetDir, "skills", "doraval", "SKILL.md"), `---
4789
+ writeFileSync4(join18(targetDir, "skills", "doraval", "SKILL.md"), `---
4063
4790
  name: doraval
4064
4791
  description: Starter (local skill)
4065
4792
  ---
@@ -4067,14 +4794,14 @@ description: Starter (local skill)
4067
4794
  ${skillBody}`);
4068
4795
  }
4069
4796
  }
4070
- var import_picocolors14, new_default3;
4797
+ var import_picocolors18, new_default3;
4071
4798
  var init_new3 = __esm(() => {
4072
4799
  init_dist();
4073
4800
  init_out();
4074
4801
  init_context4();
4075
4802
  init_prompt();
4076
4803
  init_spec();
4077
- import_picocolors14 = __toESM(require_picocolors(), 1);
4804
+ import_picocolors18 = __toESM(require_picocolors(), 1);
4078
4805
  new_default3 = defineCommand({
4079
4806
  meta: {
4080
4807
  name: "new",
@@ -4113,8 +4840,8 @@ var init_new3 = __esm(() => {
4113
4840
  }
4114
4841
  scaffold3(decision, ctx, migrateContent);
4115
4842
  ui.write(`
4116
- ${import_picocolors14.default.green("\u2713")} Created ${decision.path} at ${import_picocolors14.default.bold(decision.targetDir)}`);
4117
- const cmdName = decision.path === "plugin" ? `/${basename4(decision.targetDir)}:doraval` : "/doraval (local skill)";
4843
+ ${import_picocolors18.default.green("\u2713")} Created ${decision.path} at ${import_picocolors18.default.bold(decision.targetDir)}`);
4844
+ const cmdName = decision.path === "plugin" ? `/${basename5(decision.targetDir)}:doraval` : "/doraval (local skill)";
4118
4845
  ui.info(` Command: ${cmdName}`);
4119
4846
  if (decision.path === "plugin") {
4120
4847
  ui.info(` Cursor manifest: .cursor-plugin/plugin.json`);
@@ -4134,14 +4861,14 @@ var init_new3 = __esm(() => {
4134
4861
  });
4135
4862
 
4136
4863
  // src/cli/commands/copilot/context.ts
4137
- import { existsSync as existsSync17, readdirSync as readdirSync8 } from "fs";
4138
- import { join as join16 } from "path";
4864
+ import { existsSync as existsSync20, readdirSync as readdirSync10 } from "fs";
4865
+ import { join as join19 } from "path";
4139
4866
  function detectContext4(cwd = process.cwd()) {
4140
- const hasGithubDir = existsSync17(join16(cwd, ".github"));
4141
- const hasPluginManifest = existsSync17(join16(cwd, ".github", "plugin", "plugin.json"));
4867
+ const hasGithubDir = existsSync20(join19(cwd, ".github"));
4868
+ const hasPluginManifest = existsSync20(join19(cwd, ".github", "plugin", "plugin.json"));
4142
4869
  let looseSkillFiles = [];
4143
4870
  try {
4144
- const files = readdirSync8(cwd);
4871
+ const files = readdirSync10(cwd);
4145
4872
  looseSkillFiles = files.filter((f) => {
4146
4873
  if (!f.endsWith(".md") || f.startsWith("."))
4147
4874
  return false;
@@ -4169,22 +4896,22 @@ __export(exports_new4, {
4169
4896
  default: () => new_default4,
4170
4897
  decidePath: () => decidePath4
4171
4898
  });
4172
- import { join as join17, basename as basename5, dirname as dirname6 } from "path";
4173
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5, existsSync as existsSync18 } from "fs";
4899
+ import { join as join20, basename as basename6, dirname as dirname6 } from "path";
4900
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5, existsSync as existsSync21 } from "fs";
4174
4901
  function decidePath4(ctx, intent, providedName) {
4175
4902
  const rawName = providedName || "";
4176
4903
  let decisionPath = "standalone";
4177
4904
  let targetDir = ctx.cwd;
4178
4905
  let shouldCreateDir = false;
4179
4906
  let migrateExisting = false;
4180
- const useCurrentDirAsRoot = rawName === "." || rawName === basename5(ctx.cwd) || !rawName;
4907
+ const useCurrentDirAsRoot = rawName === "." || rawName === basename6(ctx.cwd) || !rawName;
4181
4908
  if (intent === "distribute" || intent === "self-later" && ctx.looseSkillFiles.length > 0 && !ctx.hasPluginManifest) {
4182
4909
  decisionPath = "plugin";
4183
4910
  if (useCurrentDirAsRoot) {
4184
4911
  targetDir = ctx.cwd;
4185
4912
  shouldCreateDir = false;
4186
4913
  } else {
4187
- targetDir = join17(ctx.cwd, rawName);
4914
+ targetDir = join20(ctx.cwd, rawName);
4188
4915
  shouldCreateDir = true;
4189
4916
  }
4190
4917
  migrateExisting = ctx.looseSkillFiles.length > 0;
@@ -4194,7 +4921,7 @@ function decidePath4(ctx, intent, providedName) {
4194
4921
  targetDir = ctx.cwd;
4195
4922
  shouldCreateDir = false;
4196
4923
  } else {
4197
- targetDir = join17(ctx.cwd, rawName);
4924
+ targetDir = join20(ctx.cwd, rawName);
4198
4925
  shouldCreateDir = true;
4199
4926
  }
4200
4927
  } else if (decisionPath === "standalone") {
@@ -4202,7 +4929,7 @@ function decidePath4(ctx, intent, providedName) {
4202
4929
  targetDir = ctx.cwd;
4203
4930
  shouldCreateDir = false;
4204
4931
  } else {
4205
- targetDir = join17(ctx.cwd, rawName);
4932
+ targetDir = join20(ctx.cwd, rawName);
4206
4933
  shouldCreateDir = true;
4207
4934
  }
4208
4935
  }
@@ -4210,7 +4937,7 @@ function decidePath4(ctx, intent, providedName) {
4210
4937
  }
4211
4938
  function scaffold4(decision, ctx, migrateContent) {
4212
4939
  const { targetDir, path, shouldCreateDir } = decision;
4213
- if (existsSync18(targetDir) && shouldCreateDir) {
4940
+ if (existsSync21(targetDir) && shouldCreateDir) {
4214
4941
  ui.fail("Target already exists");
4215
4942
  process.exit(1);
4216
4943
  }
@@ -4218,7 +4945,7 @@ function scaffold4(decision, ctx, migrateContent) {
4218
4945
  mkdirSync6(targetDir, { recursive: true });
4219
4946
  }
4220
4947
  if (path === "plugin") {
4221
- const pluginName = basename5(targetDir);
4948
+ const pluginName = basename6(targetDir);
4222
4949
  const copilotSpec = getProviderSpec("copilot");
4223
4950
  const copilotManifestDir = dirname6(copilotSpec.manifestPath);
4224
4951
  const pluginJson = {
@@ -4229,10 +4956,10 @@ function scaffold4(decision, ctx, migrateContent) {
4229
4956
  displayName: pluginName,
4230
4957
  keywords: ["example-keyword", "another-keyword"]
4231
4958
  };
4232
- mkdirSync6(join17(targetDir, copilotManifestDir), { recursive: true });
4233
- writeFileSync5(join17(targetDir, copilotSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
4959
+ mkdirSync6(join20(targetDir, copilotManifestDir), { recursive: true });
4960
+ writeFileSync5(join20(targetDir, copilotSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
4234
4961
  const marketplaceDir = dirname6(copilotSpec.marketplacePath);
4235
- mkdirSync6(join17(targetDir, marketplaceDir), { recursive: true });
4962
+ mkdirSync6(join20(targetDir, marketplaceDir), { recursive: true });
4236
4963
  const marketplaceJson = {
4237
4964
  name: "local",
4238
4965
  plugins: [
@@ -4245,9 +4972,9 @@ function scaffold4(decision, ctx, migrateContent) {
4245
4972
  }
4246
4973
  ]
4247
4974
  };
4248
- writeFileSync5(join17(targetDir, copilotSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
4975
+ writeFileSync5(join20(targetDir, copilotSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
4249
4976
  const demoSkillName = "doraval";
4250
- mkdirSync6(join17(targetDir, "skills", demoSkillName), { recursive: true });
4977
+ mkdirSync6(join20(targetDir, "skills", demoSkillName), { recursive: true });
4251
4978
  let skillContent;
4252
4979
  if (migrateContent) {
4253
4980
  skillContent = migrateContent;
@@ -4276,19 +5003,19 @@ To test in Copilot:
4276
5003
  1. Configure the .github/plugin as local source.
4277
5004
  2. Restart/reload and invoke the skill.`;
4278
5005
  }
4279
- writeFileSync5(join17(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
4280
- const readmePath = join17(targetDir, "README.md");
4281
- if (!existsSync18(readmePath)) {
5006
+ writeFileSync5(join20(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
5007
+ const readmePath = join20(targetDir, "README.md");
5008
+ if (!existsSync21(readmePath)) {
4282
5009
  writeFileSync5(readmePath, "# " + pluginName + `
4283
5010
 
4284
5011
  Copilot plugin scaffolded by doraval.`);
4285
5012
  }
4286
5013
  } else {
4287
- mkdirSync6(join17(targetDir, "skills", "doraval"), { recursive: true });
5014
+ mkdirSync6(join20(targetDir, "skills", "doraval"), { recursive: true });
4288
5015
  const skillBody = migrateContent || `# My Skill
4289
5016
 
4290
5017
  Basic starter for Copilot.`;
4291
- writeFileSync5(join17(targetDir, "skills", "doraval", "SKILL.md"), `---
5018
+ writeFileSync5(join20(targetDir, "skills", "doraval", "SKILL.md"), `---
4292
5019
  name: doraval
4293
5020
  description: Starter (local skill)
4294
5021
  ---
@@ -4296,14 +5023,14 @@ description: Starter (local skill)
4296
5023
  ${skillBody}`);
4297
5024
  }
4298
5025
  }
4299
- var import_picocolors15, new_default4;
5026
+ var import_picocolors19, new_default4;
4300
5027
  var init_new4 = __esm(() => {
4301
5028
  init_dist();
4302
5029
  init_out();
4303
5030
  init_context5();
4304
5031
  init_prompt();
4305
5032
  init_spec();
4306
- import_picocolors15 = __toESM(require_picocolors(), 1);
5033
+ import_picocolors19 = __toESM(require_picocolors(), 1);
4307
5034
  new_default4 = defineCommand({
4308
5035
  meta: {
4309
5036
  name: "new",
@@ -4342,8 +5069,8 @@ var init_new4 = __esm(() => {
4342
5069
  }
4343
5070
  scaffold4(decision, ctx, migrateContent);
4344
5071
  ui.write(`
4345
- ${import_picocolors15.default.green("\u2713")} Created ${decision.path} at ${import_picocolors15.default.bold(decision.targetDir)}`);
4346
- const cmdName = decision.path === "plugin" ? `/${basename5(decision.targetDir)}:doraval` : "/doraval (local skill)";
5072
+ ${import_picocolors19.default.green("\u2713")} Created ${decision.path} at ${import_picocolors19.default.bold(decision.targetDir)}`);
5073
+ const cmdName = decision.path === "plugin" ? `/${basename6(decision.targetDir)}:doraval` : "/doraval (local skill)";
4347
5074
  ui.info(` Command: ${cmdName}`);
4348
5075
  if (decision.path === "plugin") {
4349
5076
  ui.info(` Copilot manifest: .github/plugin/plugin.json`);
@@ -4367,8 +5094,8 @@ var exports_ui = {};
4367
5094
  __export(exports_ui, {
4368
5095
  default: () => ui_default
4369
5096
  });
4370
- import { existsSync as existsSync19, readdirSync as readdirSync9, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync2 } from "fs";
4371
- import { join as join18 } from "path";
5097
+ import { existsSync as existsSync22, readdirSync as readdirSync11, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3 } from "fs";
5098
+ import { join as join21 } from "path";
4372
5099
  import { spawn } from "child_process";
4373
5100
  function slugify2(title) {
4374
5101
  return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
@@ -4376,8 +5103,8 @@ function slugify2(title) {
4376
5103
  async function loadAllEntries(project) {
4377
5104
  const journalsDir = getJournalsDir();
4378
5105
  const entries = [];
4379
- const globalPath = join18(journalsDir, "global.md");
4380
- if (existsSync19(globalPath)) {
5106
+ const globalPath = join21(journalsDir, "global.md");
5107
+ if (existsSync22(globalPath)) {
4381
5108
  try {
4382
5109
  const raw = await Bun.file(globalPath).text();
4383
5110
  const parsed = parseJournalEntries(raw);
@@ -4385,8 +5112,8 @@ async function loadAllEntries(project) {
4385
5112
  } catch {}
4386
5113
  }
4387
5114
  if (project) {
4388
- const projPath = join18(journalsDir, `${project}.md`);
4389
- if (existsSync19(projPath)) {
5115
+ const projPath = join21(journalsDir, `${project}.md`);
5116
+ if (existsSync22(projPath)) {
4390
5117
  try {
4391
5118
  const raw = await Bun.file(projPath).text();
4392
5119
  const parsed = parseJournalEntries(raw);
@@ -4397,10 +5124,10 @@ async function loadAllEntries(project) {
4397
5124
  const staged = [];
4398
5125
  try {
4399
5126
  const pdir = project ? getPendingProjectDir(project) : null;
4400
- if (pdir && existsSync19(pdir)) {
4401
- const files = readdirSync9(pdir).filter((f) => f.endsWith(".md") && f !== ".gitkeep");
5127
+ if (pdir && existsSync22(pdir)) {
5128
+ const files = readdirSync11(pdir).filter((f) => f.endsWith(".md") && f !== ".gitkeep");
4402
5129
  for (const f of files) {
4403
- const txt = await Bun.file(join18(pdir, f)).text();
5130
+ const txt = await Bun.file(join21(pdir, f)).text();
4404
5131
  const parsed = parseJournalEntries(txt);
4405
5132
  parsed.forEach((e) => {
4406
5133
  e._staged = true;
@@ -4416,13 +5143,13 @@ async function loadAllEntries(project) {
4416
5143
  async function writePendingEntry(project, input) {
4417
5144
  ensureDoravalDirs();
4418
5145
  const pendingDir = getPendingProjectDir(project);
4419
- if (!existsSync19(pendingDir)) {
4420
- await Bun.write(join18(pendingDir, ".gitkeep"), "");
5146
+ if (!existsSync22(pendingDir)) {
5147
+ await Bun.write(join21(pendingDir, ".gitkeep"), "");
4421
5148
  }
4422
5149
  const date = new Date().toISOString().split("T")[0];
4423
5150
  const slug = slugify2(input.title);
4424
5151
  const filename = `${date}-${slug}.md`;
4425
- const filePath = join18(pendingDir, filename);
5152
+ const filePath = join21(pendingDir, filename);
4426
5153
  const content = `## ${input.title}
4427
5154
 
4428
5155
  \`\`\`yaml
@@ -4438,6 +5165,25 @@ ${input.rationale}
4438
5165
  await Bun.write(filePath, content);
4439
5166
  return { filePath, filename };
4440
5167
  }
5168
+ async function loadEvals(limit = 30) {
5169
+ const dir = getEvalsDir();
5170
+ if (!existsSync22(dir))
5171
+ return [];
5172
+ let files = readdirSync11(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f, path: join21(dir, f) }));
5173
+ files.sort((a, b) => b.name.localeCompare(a.name));
5174
+ const results = [];
5175
+ for (const f of files.slice(0, limit)) {
5176
+ try {
5177
+ const raw = await Bun.file(f.path).text();
5178
+ const parsed = JSON.parse(raw);
5179
+ if (parsed && (parsed.schemaVersion === 1 || parsed.verdict || parsed.skill)) {
5180
+ results.push({ ...parsed, _filename: f.name });
5181
+ }
5182
+ } catch {}
5183
+ }
5184
+ results.sort((a, b) => (b.timestamp || "").localeCompare(a.timestamp || ""));
5185
+ return results.slice(0, limit);
5186
+ }
4441
5187
  async function killPort(port) {
4442
5188
  if (process.platform === "win32") {
4443
5189
  return;
@@ -4457,12 +5203,12 @@ async function killPort(port) {
4457
5203
  await new Promise((r) => setTimeout(r, 400));
4458
5204
  } catch {}
4459
5205
  }
4460
- function readPid() {
4461
- const file = getPidFile();
4462
- if (!existsSync19(file))
5206
+ function readPid(p) {
5207
+ const file = getPidFile(p);
5208
+ if (!existsSync22(file))
4463
5209
  return null;
4464
5210
  try {
4465
- const raw = readFileSync2(file, "utf8").trim();
5211
+ const raw = readFileSync3(file, "utf8").trim();
4466
5212
  const pid = parseInt(raw, 10);
4467
5213
  if (isNaN(pid))
4468
5214
  return null;
@@ -4475,14 +5221,14 @@ function readPid() {
4475
5221
  return null;
4476
5222
  }
4477
5223
  }
4478
- function writePid(pid) {
5224
+ function writePid(pid, p) {
4479
5225
  ensureDoravalDirs();
4480
- writeFileSync6(getPidFile(), String(pid) + `
5226
+ writeFileSync6(getPidFile(p), String(pid) + `
4481
5227
  `);
4482
5228
  }
4483
- function removePid() {
5229
+ function removePid(p) {
4484
5230
  try {
4485
- unlinkSync2(getPidFile());
5231
+ unlinkSync2(getPidFile(p));
4486
5232
  } catch {}
4487
5233
  }
4488
5234
  async function getDashboardHtml() {
@@ -4495,13 +5241,13 @@ async function getDashboardHtml() {
4495
5241
  return `<!doctype html><meta charset="utf-8"><body style="font-family:monospace;background:#111;color:#ddd;padding:2rem"><h1>doraval ui</h1><p>Dashboard HTML missing.</p><pre>${String(err)}</pre></body>`;
4496
5242
  }
4497
5243
  }
4498
- var import_picocolors16, DEFAULT_PORT = 3737, getPidFile = () => join18(getDoravalDir(), "ui.pid"), ui_default;
5244
+ var import_picocolors20, DEFAULT_PORT = 3737, getPidFile = (p) => join21(getDoravalDir(), `ui.${p}.pid`), ui_default;
4499
5245
  var init_ui = __esm(() => {
4500
5246
  init_journal_config();
4501
5247
  init_journal_parse();
4502
5248
  init_context();
4503
5249
  init_hook();
4504
- import_picocolors16 = __toESM(require_picocolors(), 1);
5250
+ import_picocolors20 = __toESM(require_picocolors(), 1);
4505
5251
  ui_default = {
4506
5252
  async run({ args }) {
4507
5253
  const port = Number(args.port) || DEFAULT_PORT;
@@ -4510,12 +5256,12 @@ var init_ui = __esm(() => {
4510
5256
  const showStatusOnly = !!args.status;
4511
5257
  const force = !!args.force;
4512
5258
  ensureDoravalDirs();
4513
- const existingPid = readPid();
5259
+ const existingPid = readPid(port);
4514
5260
  if (showStatusOnly) {
4515
5261
  if (existingPid) {
4516
5262
  const url2 = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`;
4517
5263
  console.error(` Dashboard running (pid ${existingPid})`);
4518
- console.error(` URL: ${import_picocolors16.default.underline(import_picocolors16.default.cyan(url2))}`);
5264
+ console.error(` URL: ${import_picocolors20.default.underline(import_picocolors20.default.cyan(url2))}`);
4519
5265
  } else {
4520
5266
  console.error(` No dashboard running.`);
4521
5267
  }
@@ -4524,7 +5270,7 @@ var init_ui = __esm(() => {
4524
5270
  if (existingPid && !force) {
4525
5271
  const url2 = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`;
4526
5272
  console.error(` Dashboard already running (pid ${existingPid}).`);
4527
- console.error(` URL: ${import_picocolors16.default.underline(import_picocolors16.default.cyan(url2))}`);
5273
+ console.error(` URL: ${import_picocolors20.default.underline(import_picocolors20.default.cyan(url2))}`);
4528
5274
  if (shouldOpen && process.stdout.isTTY) {
4529
5275
  try {
4530
5276
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
@@ -4539,7 +5285,7 @@ var init_ui = __esm(() => {
4539
5285
  process.kill(existingPid, "SIGTERM");
4540
5286
  } catch {}
4541
5287
  await new Promise((r) => setTimeout(r, 400));
4542
- removePid();
5288
+ removePid(port);
4543
5289
  } else if (!existingPid) {
4544
5290
  await killPort(port);
4545
5291
  }
@@ -4552,124 +5298,138 @@ var init_ui = __esm(() => {
4552
5298
  project = undefined;
4553
5299
  }
4554
5300
  }
4555
- const server = Bun.serve({
4556
- port,
4557
- hostname: host,
4558
- async fetch(req) {
4559
- const url2 = new URL(req.url);
4560
- if (url2.pathname === "/" || url2.pathname === "/index.html") {
4561
- const html = await getDashboardHtml();
4562
- return new Response(html, {
4563
- headers: { "content-type": "text/html; charset=utf-8" }
4564
- });
4565
- }
4566
- if (url2.pathname === "/api/status") {
4567
- return Response.json({
4568
- project: project || null,
4569
- doravalDir: getJournalsDir(),
4570
- hasConfig: !!config,
4571
- repo: config?.journal?.repo ?? null
4572
- });
4573
- }
4574
- if (url2.pathname === "/api/entries") {
4575
- const { committed, staged } = await loadAllEntries(project || null);
4576
- return Response.json({ project, committed, staged });
4577
- }
4578
- if (url2.pathname === "/api/context") {
4579
- const { committed, staged } = await loadAllEntries(project || null);
4580
- const all = [...staged, ...committed].filter((e) => (e.status || "active") === "active");
4581
- const text = generateJournalContext(all, project || null, { minPushback: 1 });
4582
- return Response.json({ text, project });
4583
- }
4584
- if (url2.pathname === "/api/hooks/status" && req.method === "GET") {
4585
- const localPath = getLocalHooksPath();
4586
- const globalPath = getGlobalSettingsPath();
4587
- const localHas = hasHook(await readJson(localPath));
4588
- const globalHas = hasHook(await readJson(globalPath));
4589
- return Response.json({
4590
- local: { enabled: localHas, path: localPath },
4591
- global: { enabled: globalHas, path: globalPath }
4592
- });
4593
- }
4594
- if (url2.pathname === "/api/hooks/enable" && req.method === "POST") {
4595
- const body = await req.json().catch(() => ({}));
4596
- const useGlobal = !!body.global;
4597
- const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
4598
- const res = await addHook(target);
4599
- return Response.json(res);
4600
- }
4601
- if (url2.pathname === "/api/hooks/disable" && req.method === "POST") {
4602
- const body = await req.json().catch(() => ({}));
4603
- const useGlobal = !!body.global;
4604
- const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
4605
- const res = await removeHook(target);
4606
- return Response.json(res);
4607
- }
4608
- if (url2.pathname === "/api/add" && req.method === "POST") {
4609
- if (!project) {
4610
- return Response.json({ error: "No project configured. Run dora init or dora journal init first." }, { status: 400 });
5301
+ let server;
5302
+ try {
5303
+ server = Bun.serve({
5304
+ port,
5305
+ hostname: host,
5306
+ async fetch(req) {
5307
+ const url2 = new URL(req.url);
5308
+ if (url2.pathname === "/" || url2.pathname === "/index.html") {
5309
+ const html = await getDashboardHtml();
5310
+ return new Response(html, {
5311
+ headers: { "content-type": "text/html; charset=utf-8" }
5312
+ });
4611
5313
  }
4612
- const body = await req.json();
4613
- const title = String(body.title || "Untitled decision").trim();
4614
- const pushback = Number(body.pushback ?? 4);
4615
- const tags = Array.isArray(body.tags) ? body.tags.map((t) => String(t).trim()).filter(Boolean) : [];
4616
- const rationale = String(body.rationale || title).trim();
4617
- try {
4618
- const result = await writePendingEntry(project, { title, pushback, tags, rationale });
4619
- return Response.json({ ok: true, ...result });
4620
- } catch (e) {
4621
- return Response.json({ error: e.message }, { status: 500 });
5314
+ if (url2.pathname === "/api/status") {
5315
+ return Response.json({
5316
+ project: project || null,
5317
+ doravalRoot: getDoravalDir(),
5318
+ doravalDir: getJournalsDir(),
5319
+ hasConfig: !!config,
5320
+ repo: config?.journal?.repo ?? null
5321
+ });
4622
5322
  }
4623
- }
4624
- if (url2.pathname === "/api/refresh" && req.method === "POST") {
4625
- const { committed, staged } = await loadAllEntries(project || null);
4626
- return Response.json({ ok: true, committed, staged });
4627
- }
4628
- if (url2.pathname === "/api/delete-staged" && req.method === "POST") {
4629
- if (!project) {
4630
- return Response.json({ error: "No project" }, { status: 400 });
5323
+ if (url2.pathname === "/api/entries") {
5324
+ const { committed, staged } = await loadAllEntries(project || null);
5325
+ return Response.json({ project, committed, staged });
5326
+ }
5327
+ if (url2.pathname === "/api/context") {
5328
+ const { committed, staged } = await loadAllEntries(project || null);
5329
+ const all = [...staged, ...committed].filter((e) => (e.status || "active") === "active");
5330
+ const text = generateJournalContext(all, project || null, { minPushback: 1 });
5331
+ return Response.json({ text, project });
5332
+ }
5333
+ if (url2.pathname === "/api/hooks/status" && req.method === "GET") {
5334
+ const localPath = getLocalHooksPath();
5335
+ const globalPath = getGlobalSettingsPath();
5336
+ const localHas = hasHook(await readJson(localPath));
5337
+ const globalHas = hasHook(await readJson(globalPath));
5338
+ return Response.json({
5339
+ local: { enabled: localHas, path: localPath },
5340
+ global: { enabled: globalHas, path: globalPath }
5341
+ });
5342
+ }
5343
+ if (url2.pathname === "/api/hooks/enable" && req.method === "POST") {
5344
+ const body = await req.json().catch(() => ({}));
5345
+ const useGlobal = !!body.global;
5346
+ const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
5347
+ const res = await addHook(target);
5348
+ return Response.json(res);
4631
5349
  }
4632
- const body = await req.json().catch(() => ({}));
4633
- const filename = body.filename;
4634
- if (!filename) {
4635
- return Response.json({ error: "filename required" }, { status: 400 });
5350
+ if (url2.pathname === "/api/hooks/disable" && req.method === "POST") {
5351
+ const body = await req.json().catch(() => ({}));
5352
+ const useGlobal = !!body.global;
5353
+ const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
5354
+ const res = await removeHook(target);
5355
+ return Response.json(res);
4636
5356
  }
4637
- const pdir = getPendingProjectDir(project);
4638
- const filePath = join18(pdir, filename);
4639
- if (existsSync19(filePath)) {
5357
+ if (url2.pathname === "/api/add" && req.method === "POST") {
5358
+ if (!project) {
5359
+ return Response.json({ error: "No project configured. Run dora init or dora journal init first." }, { status: 400 });
5360
+ }
5361
+ const body = await req.json();
5362
+ const title = String(body.title || "Untitled decision").trim();
5363
+ const pushback = Number(body.pushback ?? 4);
5364
+ const tags = Array.isArray(body.tags) ? body.tags.map((t) => String(t).trim()).filter(Boolean) : [];
5365
+ const rationale = String(body.rationale || title).trim();
4640
5366
  try {
4641
- await Bun.file(filePath).unlink();
4642
- } catch {}
4643
- return Response.json({ ok: true });
5367
+ const result = await writePendingEntry(project, { title, pushback, tags, rationale });
5368
+ return Response.json({ ok: true, ...result });
5369
+ } catch (e) {
5370
+ return Response.json({ error: e.message }, { status: 500 });
5371
+ }
4644
5372
  }
4645
- return Response.json({ error: "not found" }, { status: 404 });
4646
- }
4647
- if (url2.pathname.startsWith("/api/")) {
4648
- return Response.json({ error: "Not found" }, { status: 404 });
5373
+ if (url2.pathname === "/api/refresh" && req.method === "POST") {
5374
+ const { committed, staged } = await loadAllEntries(project || null);
5375
+ return Response.json({ ok: true, committed, staged });
5376
+ }
5377
+ if (url2.pathname === "/api/delete-staged" && req.method === "POST") {
5378
+ if (!project) {
5379
+ return Response.json({ error: "No project" }, { status: 400 });
5380
+ }
5381
+ const body = await req.json().catch(() => ({}));
5382
+ const filename = body.filename;
5383
+ if (!filename) {
5384
+ return Response.json({ error: "filename required" }, { status: 400 });
5385
+ }
5386
+ const pdir = getPendingProjectDir(project);
5387
+ const filePath = join21(pdir, filename);
5388
+ if (existsSync22(filePath)) {
5389
+ try {
5390
+ await Bun.file(filePath).unlink();
5391
+ } catch {}
5392
+ return Response.json({ ok: true });
5393
+ }
5394
+ return Response.json({ error: "not found" }, { status: 404 });
5395
+ }
5396
+ if (url2.pathname === "/api/evals") {
5397
+ const evals = await loadEvals(25);
5398
+ return Response.json({ evals });
5399
+ }
5400
+ if (url2.pathname.startsWith("/api/")) {
5401
+ return Response.json({ error: "Not found" }, { status: 404 });
5402
+ }
5403
+ return new Response("Not found", { status: 404 });
4649
5404
  }
4650
- return new Response("Not found", { status: 404 });
4651
- }
4652
- });
5405
+ });
5406
+ } catch (err) {
5407
+ removePid(port);
5408
+ console.error(` Failed to start dashboard on port ${port}: ${err?.message || err}`);
5409
+ process.exit(1);
5410
+ }
4653
5411
  const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${server.port}`;
4654
- writePid(process.pid);
5412
+ writePid(process.pid, port);
4655
5413
  const msg = `
4656
- ${import_picocolors16.default.blue("\u25C9")} dora local dashboard
4657
- ${import_picocolors16.default.dim("Project:")} ${project ? import_picocolors16.default.white(project) : import_picocolors16.default.yellow("none (run dora init)")}
4658
- ${import_picocolors16.default.dim("URL:")} ${import_picocolors16.default.underline(import_picocolors16.default.cyan(url))}
5414
+ ${import_picocolors20.default.blue("\u25C9")} dora local dashboard
5415
+ ${import_picocolors20.default.dim("Project:")} ${project ? import_picocolors20.default.white(project) : import_picocolors20.default.yellow("none (run dora init)")}
5416
+ ${import_picocolors20.default.dim("Data dir:")} ${getDoravalDir()}
5417
+ ${import_picocolors20.default.dim("URL:")} ${import_picocolors20.default.underline(import_picocolors20.default.cyan(url))}
4659
5418
 
4660
- ${import_picocolors16.default.dim("Press Ctrl+C to stop")}
5419
+ ${import_picocolors20.default.dim("Press Ctrl+C to stop")}
4661
5420
  `;
4662
5421
  console.error(msg);
5422
+ console.error(` ${import_picocolors20.default.dim("Tip:")} data location = ${getDoravalDir()} (set DORAVAL_HOME to change)`);
4663
5423
  if (shouldOpen && process.stdout.isTTY) {
4664
5424
  try {
4665
5425
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
4666
5426
  spawn(opener, [url], { stdio: "ignore", detached: true }).unref();
4667
5427
  } catch {
4668
- console.error(import_picocolors16.default.dim(` Could not auto-open. Visit ${url}`));
5428
+ console.error(import_picocolors20.default.dim(` Could not auto-open. Visit ${url}`));
4669
5429
  }
4670
5430
  }
4671
5431
  const cleanup = () => {
4672
- removePid();
5432
+ removePid(port);
4673
5433
  console.error(`
4674
5434
  Stopping dashboard...`);
4675
5435
  server.stop();
@@ -4682,7 +5442,7 @@ var init_ui = __esm(() => {
4682
5442
  });
4683
5443
 
4684
5444
  // src/validators/claude/skill.ts
4685
- import { existsSync as existsSync20 } from "fs";
5445
+ import { existsSync as existsSync23 } from "fs";
4686
5446
  import { resolve as resolve5 } from "path";
4687
5447
  var claudeSkillValidator;
4688
5448
  var init_skill = __esm(() => {
@@ -4693,7 +5453,7 @@ var init_skill = __esm(() => {
4693
5453
  name: "Claude Skill",
4694
5454
  description: "Validates SKILL.md per current Claude Code spec: frontmatter (name/description relaxed to recommended; directory name usually provides the /command), body, supporting files, dynamic injection (!`cmd`), substitutions ($ARGUMENTS, ${CLAUDE_*}), and advanced fields (allowed-tools, context, disable-model-invocation, when_to_use, etc.)",
4695
5455
  detect(dir) {
4696
- return existsSync20(resolve5(dir, "SKILL.md"));
5456
+ return existsSync23(resolve5(dir, "SKILL.md"));
4697
5457
  },
4698
5458
  async validate(dir, _opts) {
4699
5459
  const loaded = await loadSkill(dir);
@@ -4711,8 +5471,8 @@ var init_skill = __esm(() => {
4711
5471
  });
4712
5472
 
4713
5473
  // src/validators/claude/plugin.ts
4714
- import { existsSync as existsSync21, readdirSync as readdirSync10 } from "fs";
4715
- import { resolve as resolve6, join as join19 } from "path";
5474
+ import { existsSync as existsSync24, readdirSync as readdirSync12 } from "fs";
5475
+ import { resolve as resolve6, join as join22 } from "path";
4716
5476
  function levenshtein(a, b) {
4717
5477
  if (a === b)
4718
5478
  return 0;
@@ -4804,7 +5564,7 @@ var init_plugin = __esm(() => {
4804
5564
  name: "Claude Plugin",
4805
5565
  description: "Validates .claude-plugin/plugin.json manifest (complete schema per Plugins reference), component path rules (replace vs augment), .claude-plugin/ purity, default dirs, single-root-skill layout, unrecognized fields + suggestions, and structure",
4806
5566
  detect(dir) {
4807
- return existsSync21(resolve6(dir, ".claude-plugin", "plugin.json"));
5567
+ return existsSync24(resolve6(dir, ".claude-plugin", "plugin.json"));
4808
5568
  },
4809
5569
  async validate(dir, _opts) {
4810
5570
  const errors = [];
@@ -4818,7 +5578,7 @@ var init_plugin = __esm(() => {
4818
5578
  manifest = JSON.parse(raw);
4819
5579
  passes.push(".claude-plugin/plugin.json is valid JSON");
4820
5580
  } catch (err) {
4821
- if (!existsSync21(manifestPath)) {
5581
+ if (!existsSync24(manifestPath)) {
4822
5582
  errors.push(`.claude-plugin/plugin.json is missing (looked for ${manifestPath})`);
4823
5583
  warnings.push("Hint: Run `doraval claude new` (or `dora claude new`) to scaffold a new Claude plugin in this directory.");
4824
5584
  } else {
@@ -4827,7 +5587,7 @@ var init_plugin = __esm(() => {
4827
5587
  return { errors, warnings, passes };
4828
5588
  }
4829
5589
  try {
4830
- const entries = readdirSync10(dotClaudePluginDir);
5590
+ const entries = readdirSync12(dotClaudePluginDir);
4831
5591
  const unexpected = entries.filter((e) => e !== "plugin.json");
4832
5592
  if (unexpected.length > 0) {
4833
5593
  for (const e of unexpected) {
@@ -4914,7 +5674,7 @@ var init_plugin = __esm(() => {
4914
5674
  errors.push(`${field}: path "${s}" must start with "./"`);
4915
5675
  } else if (s.includes("..")) {
4916
5676
  errors.push(`${field}: path "${s}" must not use ".." (paths are confined to the plugin tree after cache copy)`);
4917
- } else if (existsSync21(resolve6(dir, s))) {
5677
+ } else if (existsSync24(resolve6(dir, s))) {
4918
5678
  passes.push(`${field}: path "${s}" exists`);
4919
5679
  } else {
4920
5680
  warnings.push(`${field}: path "${s}" does not exist on disk`);
@@ -4964,11 +5724,11 @@ var init_plugin = __esm(() => {
4964
5724
  passes.push(`dependencies: declares ${manifest.dependencies.length} plugin dependency/ies`);
4965
5725
  }
4966
5726
  const skillsDir = resolve6(dir, "skills");
4967
- if (existsSync21(skillsDir)) {
4968
- const entries = readdirSync10(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
5727
+ if (existsSync24(skillsDir)) {
5728
+ const entries = readdirSync12(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
4969
5729
  for (const e of entries) {
4970
- const md = join19(skillsDir, e.name, "SKILL.md");
4971
- if (existsSync21(md)) {
5730
+ const md = join22(skillsDir, e.name, "SKILL.md");
5731
+ if (existsSync24(md)) {
4972
5732
  passes.push(`skills/${e.name}/SKILL.md exists`);
4973
5733
  } else {
4974
5734
  errors.push(`skills/${e.name}/ is missing SKILL.md`);
@@ -4979,8 +5739,8 @@ var init_plugin = __esm(() => {
4979
5739
  }
4980
5740
  }
4981
5741
  const commandsDir = resolve6(dir, "commands");
4982
- if (existsSync21(commandsDir)) {
4983
- const mds = readdirSync10(commandsDir).filter((f) => f.endsWith(".md"));
5742
+ if (existsSync24(commandsDir)) {
5743
+ const mds = readdirSync12(commandsDir).filter((f) => f.endsWith(".md"));
4984
5744
  if (mds.length) {
4985
5745
  passes.push(`commands/ has ${mds.length} .md file(s)`);
4986
5746
  }
@@ -4989,8 +5749,8 @@ var init_plugin = __esm(() => {
4989
5749
  }
4990
5750
  }
4991
5751
  const agentsDir = resolve6(dir, "agents");
4992
- if (existsSync21(agentsDir)) {
4993
- const mds = readdirSync10(agentsDir).filter((f) => f.endsWith(".md"));
5752
+ if (existsSync24(agentsDir)) {
5753
+ const mds = readdirSync12(agentsDir).filter((f) => f.endsWith(".md"));
4994
5754
  if (mds.length) {
4995
5755
  passes.push(`agents/ has ${mds.length} .md file(s)`);
4996
5756
  }
@@ -4998,30 +5758,30 @@ var init_plugin = __esm(() => {
4998
5758
  warnings.push('agents/ co-exists with manifest "agents" \u2014 manifest replaces default (dir ignored)');
4999
5759
  }
5000
5760
  }
5001
- if (existsSync21(resolve6(dir, "output-styles"))) {
5761
+ if (existsSync24(resolve6(dir, "output-styles"))) {
5002
5762
  passes.push("output-styles/ directory present");
5003
5763
  if (manifest.outputStyles)
5004
5764
  warnings.push("output-styles/ co-exists with manifest outputStyles \u2014 manifest wins");
5005
5765
  }
5006
- if (existsSync21(resolve6(dir, "themes")))
5766
+ if (existsSync24(resolve6(dir, "themes")))
5007
5767
  passes.push("themes/ present (experimental)");
5008
- if (existsSync21(resolve6(dir, "monitors")) || manifest.experimental?.monitors) {
5768
+ if (existsSync24(resolve6(dir, "monitors")) || manifest.experimental?.monitors) {
5009
5769
  passes.push("monitors config present (experimental)");
5010
5770
  }
5011
- if (existsSync21(resolve6(dir, "bin")))
5771
+ if (existsSync24(resolve6(dir, "bin")))
5012
5772
  passes.push("bin/ present (adds executables to Bash tool $PATH)");
5013
- if (existsSync21(resolve6(dir, "settings.json")))
5773
+ if (existsSync24(resolve6(dir, "settings.json")))
5014
5774
  passes.push("settings.json present (plugin defaults for agent/statusline)");
5015
- if (existsSync21(resolve6(dir, "README.md")))
5775
+ if (existsSync24(resolve6(dir, "README.md")))
5016
5776
  passes.push("README.md present");
5017
- if (existsSync21(resolve6(dir, ".mcp.json")))
5777
+ if (existsSync24(resolve6(dir, ".mcp.json")))
5018
5778
  passes.push(".mcp.json present (validated by claude:mcp)");
5019
- if (existsSync21(resolve6(dir, ".lsp.json")))
5779
+ if (existsSync24(resolve6(dir, ".lsp.json")))
5020
5780
  passes.push(".lsp.json present (validated by claude:lsp when registered)");
5021
- if (existsSync21(resolve6(dir, "hooks/hooks.json")) || existsSync21(resolve6(dir, "hooks.json"))) {
5781
+ if (existsSync24(resolve6(dir, "hooks/hooks.json")) || existsSync24(resolve6(dir, "hooks.json"))) {
5022
5782
  passes.push("hooks config present (validated by claude:hooks)");
5023
5783
  }
5024
- if (existsSync21(resolve6(dir, "SKILL.md")) && !existsSync21(skillsDir) && manifest.skills === undefined) {
5784
+ if (existsSync24(resolve6(dir, "SKILL.md")) && !existsSync24(skillsDir) && manifest.skills === undefined) {
5025
5785
  passes.push('Root SKILL.md detected \u2014 plugin will be treated as a single-skill plugin (prefer frontmatter "name" for stable /command)');
5026
5786
  }
5027
5787
  return { errors, warnings, passes };
@@ -5030,8 +5790,8 @@ var init_plugin = __esm(() => {
5030
5790
  });
5031
5791
 
5032
5792
  // src/validators/claude/marketplace.ts
5033
- import { existsSync as existsSync22, readdirSync as readdirSync11 } from "fs";
5034
- import { resolve as resolve7, join as join20 } from "path";
5793
+ import { existsSync as existsSync25, readdirSync as readdirSync13 } from "fs";
5794
+ import { resolve as resolve7, join as join23 } from "path";
5035
5795
  var claudeMarketplaceValidator;
5036
5796
  var init_marketplace = __esm(() => {
5037
5797
  claudeMarketplaceValidator = {
@@ -5040,18 +5800,18 @@ var init_marketplace = __esm(() => {
5040
5800
  name: "Claude Plugin Marketplace",
5041
5801
  description: "Validates .claude-plugin/marketplace.json or plugins/ marketplace layouts (plugins array with sources)",
5042
5802
  detect(dir) {
5043
- if (existsSync22(resolve7(dir, ".claude-plugin", "marketplace.json")))
5803
+ if (existsSync25(resolve7(dir, ".claude-plugin", "marketplace.json")))
5044
5804
  return true;
5045
5805
  const pluginsDir = resolve7(dir, "plugins");
5046
- if (!existsSync22(pluginsDir))
5806
+ if (!existsSync25(pluginsDir))
5047
5807
  return false;
5048
5808
  try {
5049
- const entries = readdirSync11(pluginsDir, { withFileTypes: true });
5809
+ const entries = readdirSync13(pluginsDir, { withFileTypes: true });
5050
5810
  for (const entry of entries) {
5051
5811
  if (!entry.isDirectory())
5052
5812
  continue;
5053
- const hasSkills = existsSync22(join20(pluginsDir, entry.name, "skills"));
5054
- const hasManifest = existsSync22(join20(pluginsDir, entry.name, ".claude-plugin", "plugin.json"));
5813
+ const hasSkills = existsSync25(join23(pluginsDir, entry.name, "skills"));
5814
+ const hasManifest = existsSync25(join23(pluginsDir, entry.name, ".claude-plugin", "plugin.json"));
5055
5815
  if (hasSkills || hasManifest)
5056
5816
  return true;
5057
5817
  }
@@ -5063,9 +5823,9 @@ var init_marketplace = __esm(() => {
5063
5823
  const warnings = [];
5064
5824
  const passes = [];
5065
5825
  const claudeMktPath = resolve7(dir, ".claude-plugin", "marketplace.json");
5066
- const hasClaudeMkt = existsSync22(claudeMktPath);
5826
+ const hasClaudeMkt = existsSync25(claudeMktPath);
5067
5827
  const pluginsDir = resolve7(dir, "plugins");
5068
- const hasPluginsDirLayout = existsSync22(pluginsDir);
5828
+ const hasPluginsDirLayout = existsSync25(pluginsDir);
5069
5829
  if (!hasClaudeMkt && !hasPluginsDirLayout) {
5070
5830
  errors.push("Missing .claude-plugin/marketplace.json or plugins/ directory");
5071
5831
  return { errors, warnings, passes };
@@ -5110,9 +5870,9 @@ var init_marketplace = __esm(() => {
5110
5870
  const src = String(p.source);
5111
5871
  passes.push(`plugins[${i}].source: "${src}"`);
5112
5872
  const srcDir = resolve7(dir, src);
5113
- if (existsSync22(srcDir)) {
5114
- const hasManifest = existsSync22(resolve7(srcDir, ".claude-plugin", "plugin.json"));
5115
- const hasSkills = existsSync22(resolve7(srcDir, "skills"));
5873
+ if (existsSync25(srcDir)) {
5874
+ const hasManifest = existsSync25(resolve7(srcDir, ".claude-plugin", "plugin.json"));
5875
+ const hasSkills = existsSync25(resolve7(srcDir, "skills"));
5116
5876
  if (hasManifest || hasSkills) {
5117
5877
  passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
5118
5878
  } else {
@@ -5128,12 +5888,12 @@ var init_marketplace = __esm(() => {
5128
5888
  passes.push(`plugins[${i}].category: "${p.category}"`);
5129
5889
  }
5130
5890
  }
5131
- if (existsSync22(resolve7(dir, "README.md"))) {
5891
+ if (existsSync25(resolve7(dir, "README.md"))) {
5132
5892
  passes.push("README.md exists at marketplace root");
5133
5893
  } else {
5134
5894
  warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
5135
5895
  }
5136
- if (existsSync22(resolve7(dir, "LICENSE"))) {
5896
+ if (existsSync25(resolve7(dir, "LICENSE"))) {
5137
5897
  passes.push("LICENSE exists at marketplace root");
5138
5898
  } else {
5139
5899
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
@@ -5142,27 +5902,27 @@ var init_marketplace = __esm(() => {
5142
5902
  }
5143
5903
  if (hasPluginsDirLayout) {
5144
5904
  passes.push("plugins/ directory exists");
5145
- const pluginEntries = readdirSync11(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
5905
+ const pluginEntries = readdirSync13(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
5146
5906
  if (pluginEntries.length === 0) {
5147
5907
  errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
5148
5908
  return { errors, warnings, passes };
5149
5909
  }
5150
5910
  passes.push(`${pluginEntries.length} plugin(s) found`);
5151
- if (existsSync22(resolve7(dir, "README.md"))) {
5911
+ if (existsSync25(resolve7(dir, "README.md"))) {
5152
5912
  passes.push("README.md exists at marketplace root");
5153
5913
  } else {
5154
5914
  warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
5155
5915
  }
5156
- if (existsSync22(resolve7(dir, "LICENSE"))) {
5916
+ if (existsSync25(resolve7(dir, "LICENSE"))) {
5157
5917
  passes.push("LICENSE exists at marketplace root");
5158
5918
  } else {
5159
5919
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
5160
5920
  }
5161
5921
  for (const plugin of pluginEntries) {
5162
- const pluginPath = join20(pluginsDir, plugin.name);
5163
- const hasSkills = existsSync22(join20(pluginPath, "skills"));
5164
- const hasManifest = existsSync22(join20(pluginPath, ".claude-plugin", "plugin.json"));
5165
- const hasReadme = existsSync22(join20(pluginPath, "README.md"));
5922
+ const pluginPath = join23(pluginsDir, plugin.name);
5923
+ const hasSkills = existsSync25(join23(pluginPath, "skills"));
5924
+ const hasManifest = existsSync25(join23(pluginPath, ".claude-plugin", "plugin.json"));
5925
+ const hasReadme = existsSync25(join23(pluginPath, "README.md"));
5166
5926
  if (hasManifest || hasSkills) {
5167
5927
  passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
5168
5928
  } else {
@@ -5180,7 +5940,7 @@ var init_marketplace = __esm(() => {
5180
5940
  });
5181
5941
 
5182
5942
  // src/validators/claude/hooks.ts
5183
- import { existsSync as existsSync23 } from "fs";
5943
+ import { existsSync as existsSync26 } from "fs";
5184
5944
  import { resolve as resolve8 } from "path";
5185
5945
  var KNOWN_EVENTS, claudeHooksValidator;
5186
5946
  var init_hooks = __esm(() => {
@@ -5222,13 +5982,13 @@ var init_hooks = __esm(() => {
5222
5982
  name: "Claude Hooks",
5223
5983
  description: "Validates hooks/hooks.json (or root hooks.json): all lifecycle events per Plugins reference, hook group structure (matcher + hooks[]), supported hook types (command, http, mcp_tool, prompt, agent)",
5224
5984
  detect(dir) {
5225
- return existsSync23(resolve8(dir, "hooks", "hooks.json")) || existsSync23(resolve8(dir, "hooks.json"));
5985
+ return existsSync26(resolve8(dir, "hooks", "hooks.json")) || existsSync26(resolve8(dir, "hooks.json"));
5226
5986
  },
5227
5987
  async validate(dir, _opts) {
5228
5988
  const errors = [];
5229
5989
  const warnings = [];
5230
5990
  const passes = [];
5231
- const hooksPath = existsSync23(resolve8(dir, "hooks", "hooks.json")) ? resolve8(dir, "hooks", "hooks.json") : resolve8(dir, "hooks.json");
5991
+ const hooksPath = existsSync26(resolve8(dir, "hooks", "hooks.json")) ? resolve8(dir, "hooks", "hooks.json") : resolve8(dir, "hooks.json");
5232
5992
  let config;
5233
5993
  try {
5234
5994
  const raw = await Bun.file(hooksPath).text();
@@ -5294,7 +6054,7 @@ var init_hooks = __esm(() => {
5294
6054
  });
5295
6055
 
5296
6056
  // src/validators/claude/mcp.ts
5297
- import { existsSync as existsSync24 } from "fs";
6057
+ import { existsSync as existsSync27 } from "fs";
5298
6058
  import { resolve as resolve9 } from "path";
5299
6059
  var claudeMcpValidator;
5300
6060
  var init_mcp = __esm(() => {
@@ -5304,7 +6064,7 @@ var init_mcp = __esm(() => {
5304
6064
  name: "Claude MCP Config",
5305
6065
  description: "Validates .mcp.json (or inline via plugin.json mcpServers): server entries (stdio: command+args, or url), env, cwd, ${CLAUDE_PLUGIN_ROOT} etc. substitutions per Plugins reference",
5306
6066
  detect(dir) {
5307
- return existsSync24(resolve9(dir, ".mcp.json"));
6067
+ return existsSync27(resolve9(dir, ".mcp.json"));
5308
6068
  },
5309
6069
  async validate(dir, _opts) {
5310
6070
  const errors = [];
@@ -5364,8 +6124,8 @@ var init_mcp = __esm(() => {
5364
6124
  });
5365
6125
 
5366
6126
  // src/validators/claude/subagent.ts
5367
- import { existsSync as existsSync25, readdirSync as readdirSync12 } from "fs";
5368
- import { resolve as resolve10, join as join21 } from "path";
6127
+ import { existsSync as existsSync28, readdirSync as readdirSync14 } from "fs";
6128
+ import { resolve as resolve10, join as join24 } from "path";
5369
6129
  var claudeSubagentValidator;
5370
6130
  var init_subagent = __esm(() => {
5371
6131
  init_frontmatter();
@@ -5376,10 +6136,10 @@ var init_subagent = __esm(() => {
5376
6136
  description: "Validates agents/*.md (plugin subagents): frontmatter per spec (name, description, model, effort, maxTurns, tools, disallowedTools, skills, memory, background, isolation=worktree), body; warns on disallowed fields (hooks, mcpServers, permissionMode) for security",
5377
6137
  detect(dir) {
5378
6138
  const agentsDir = resolve10(dir, "agents");
5379
- if (!existsSync25(agentsDir))
6139
+ if (!existsSync28(agentsDir))
5380
6140
  return false;
5381
6141
  try {
5382
- return readdirSync12(agentsDir).some((f) => f.endsWith(".md"));
6142
+ return readdirSync14(agentsDir).some((f) => f.endsWith(".md"));
5383
6143
  } catch {
5384
6144
  return false;
5385
6145
  }
@@ -5389,7 +6149,7 @@ var init_subagent = __esm(() => {
5389
6149
  const warnings = [];
5390
6150
  const passes = [];
5391
6151
  const agentsDir = resolve10(dir, "agents");
5392
- const mdFiles = readdirSync12(agentsDir).filter((f) => f.endsWith(".md"));
6152
+ const mdFiles = readdirSync14(agentsDir).filter((f) => f.endsWith(".md"));
5393
6153
  if (mdFiles.length === 0) {
5394
6154
  errors.push("agents/ directory has no .md files");
5395
6155
  return { errors, warnings, passes };
@@ -5410,7 +6170,7 @@ var init_subagent = __esm(() => {
5410
6170
  ]);
5411
6171
  const DISALLOWED = new Set(["hooks", "mcpServers", "permissionMode"]);
5412
6172
  for (const file of mdFiles) {
5413
- const filePath = join21(agentsDir, file);
6173
+ const filePath = join24(agentsDir, file);
5414
6174
  const raw = await Bun.file(filePath).text();
5415
6175
  try {
5416
6176
  const parsed = parseFrontmatter(raw);
@@ -5456,8 +6216,8 @@ var init_subagent = __esm(() => {
5456
6216
  });
5457
6217
 
5458
6218
  // src/validators/claude/command.ts
5459
- import { existsSync as existsSync26, readdirSync as readdirSync13 } from "fs";
5460
- import { resolve as resolve11, join as join22 } from "path";
6219
+ import { existsSync as existsSync29, readdirSync as readdirSync15 } from "fs";
6220
+ import { resolve as resolve11, join as join25 } from "path";
5461
6221
  var claudeCommandValidator;
5462
6222
  var init_command = __esm(() => {
5463
6223
  init_frontmatter();
@@ -5468,10 +6228,10 @@ var init_command = __esm(() => {
5468
6228
  description: "Validates commands/ (or legacy .claude/commands/) .md files: frontmatter (including rich skill fields), description, body",
5469
6229
  detect(dir) {
5470
6230
  const commandsDir = resolve11(dir, "commands");
5471
- if (!existsSync26(commandsDir))
6231
+ if (!existsSync29(commandsDir))
5472
6232
  return false;
5473
6233
  try {
5474
- return readdirSync13(commandsDir).some((f) => f.endsWith(".md"));
6234
+ return readdirSync15(commandsDir).some((f) => f.endsWith(".md"));
5475
6235
  } catch {
5476
6236
  return false;
5477
6237
  }
@@ -5481,14 +6241,14 @@ var init_command = __esm(() => {
5481
6241
  const warnings = [];
5482
6242
  const passes = [];
5483
6243
  const commandsDir = resolve11(dir, "commands");
5484
- const mdFiles = readdirSync13(commandsDir).filter((f) => f.endsWith(".md"));
6244
+ const mdFiles = readdirSync15(commandsDir).filter((f) => f.endsWith(".md"));
5485
6245
  if (mdFiles.length === 0) {
5486
6246
  errors.push("commands/ directory has no .md files");
5487
6247
  return { errors, warnings, passes };
5488
6248
  }
5489
6249
  passes.push(`${mdFiles.length} command definition(s) found`);
5490
6250
  for (const file of mdFiles) {
5491
- const filePath = join22(commandsDir, file);
6251
+ const filePath = join25(commandsDir, file);
5492
6252
  const raw = await Bun.file(filePath).text();
5493
6253
  try {
5494
6254
  const parsed = parseFrontmatter(raw);
@@ -5517,7 +6277,7 @@ var init_command = __esm(() => {
5517
6277
  });
5518
6278
 
5519
6279
  // src/validators/claude/memory.ts
5520
- import { existsSync as existsSync27 } from "fs";
6280
+ import { existsSync as existsSync30 } from "fs";
5521
6281
  import { resolve as resolve12 } from "path";
5522
6282
  var claudeMemoryValidator;
5523
6283
  var init_memory = __esm(() => {
@@ -5527,7 +6287,7 @@ var init_memory = __esm(() => {
5527
6287
  name: "Claude CLAUDE.md",
5528
6288
  description: "Validates CLAUDE.md: non-empty, length recommendations, @path imports",
5529
6289
  detect(dir) {
5530
- return existsSync27(resolve12(dir, "CLAUDE.md"));
6290
+ return existsSync30(resolve12(dir, "CLAUDE.md"));
5531
6291
  },
5532
6292
  async validate(dir, _opts) {
5533
6293
  const errors = [];
@@ -5552,7 +6312,7 @@ var init_memory = __esm(() => {
5552
6312
  while ((match = importRegex.exec(raw)) !== null) {
5553
6313
  const importPath = match[1];
5554
6314
  const resolvedImport = resolve12(dir, importPath);
5555
- if (existsSync27(resolvedImport)) {
6315
+ if (existsSync30(resolvedImport)) {
5556
6316
  passes.push(`@import "${importPath}" exists`);
5557
6317
  } else {
5558
6318
  warnings.push(`@import "${importPath}" \u2014 file not found at ${resolvedImport}`);
@@ -5564,7 +6324,7 @@ var init_memory = __esm(() => {
5564
6324
  });
5565
6325
 
5566
6326
  // src/validators/claude/lsp.ts
5567
- import { existsSync as existsSync28 } from "fs";
6327
+ import { existsSync as existsSync31 } from "fs";
5568
6328
  import { resolve as resolve13 } from "path";
5569
6329
  var claudeLspValidator;
5570
6330
  var init_lsp = __esm(() => {
@@ -5574,7 +6334,7 @@ var init_lsp = __esm(() => {
5574
6334
  name: "Claude LSP Servers",
5575
6335
  description: "Validates .lsp.json (or plugin.json lspServers): language server configs with required command + extensionToLanguage; optional transport, env, settings, diagnostics etc. (binaries installed separately)",
5576
6336
  detect(dir) {
5577
- return existsSync28(resolve13(dir, ".lsp.json")) || existsSync28(resolve13(dir, ".claude-plugin", "plugin.json"));
6337
+ return existsSync31(resolve13(dir, ".lsp.json")) || existsSync31(resolve13(dir, ".claude-plugin", "plugin.json"));
5578
6338
  },
5579
6339
  async validate(dir, _opts) {
5580
6340
  const errors = [];
@@ -5582,7 +6342,7 @@ var init_lsp = __esm(() => {
5582
6342
  const passes = [];
5583
6343
  let cfg = null;
5584
6344
  const lspPath = resolve13(dir, ".lsp.json");
5585
- if (existsSync28(lspPath)) {
6345
+ if (existsSync31(lspPath)) {
5586
6346
  try {
5587
6347
  cfg = JSON.parse(await Bun.file(lspPath).text());
5588
6348
  passes.push(".lsp.json is valid JSON");
@@ -5592,7 +6352,7 @@ var init_lsp = __esm(() => {
5592
6352
  }
5593
6353
  } else {
5594
6354
  const manifestPath = resolve13(dir, ".claude-plugin", "plugin.json");
5595
- if (existsSync28(manifestPath)) {
6355
+ if (existsSync31(manifestPath)) {
5596
6356
  try {
5597
6357
  const m = JSON.parse(await Bun.file(manifestPath).text());
5598
6358
  if (m && m.lspServers && typeof m.lspServers === "object") {
@@ -5603,7 +6363,7 @@ var init_lsp = __esm(() => {
5603
6363
  }
5604
6364
  }
5605
6365
  if (!cfg) {
5606
- if (!existsSync28(lspPath)) {
6366
+ if (!existsSync31(lspPath)) {
5607
6367
  return { errors, warnings, passes };
5608
6368
  }
5609
6369
  }
@@ -5632,7 +6392,7 @@ var init_lsp = __esm(() => {
5632
6392
  });
5633
6393
 
5634
6394
  // src/validators/claude/monitors.ts
5635
- import { existsSync as existsSync29 } from "fs";
6395
+ import { existsSync as existsSync32 } from "fs";
5636
6396
  import { resolve as resolve14 } from "path";
5637
6397
  var claudeMonitorsValidator;
5638
6398
  var init_monitors = __esm(() => {
@@ -5642,7 +6402,7 @@ var init_monitors = __esm(() => {
5642
6402
  name: "Claude Monitors (experimental)",
5643
6403
  description: "Validates monitors/monitors.json (or experimental.monitors): array of {name, command, description, when?}; commands support ${CLAUDE_PLUGIN_*} subs. Monitors run only in interactive CLI sessions.",
5644
6404
  detect(dir) {
5645
- return existsSync29(resolve14(dir, "monitors", "monitors.json")) || existsSync29(resolve14(dir, "monitors.json")) || existsSync29(resolve14(dir, ".claude-plugin", "plugin.json"));
6405
+ return existsSync32(resolve14(dir, "monitors", "monitors.json")) || existsSync32(resolve14(dir, "monitors.json")) || existsSync32(resolve14(dir, ".claude-plugin", "plugin.json"));
5646
6406
  },
5647
6407
  async validate(dir, _opts) {
5648
6408
  const errors = [];
@@ -5654,7 +6414,7 @@ var init_monitors = __esm(() => {
5654
6414
  resolve14(dir, "monitors.json")
5655
6415
  ];
5656
6416
  for (const p of candidates) {
5657
- if (existsSync29(p)) {
6417
+ if (existsSync32(p)) {
5658
6418
  try {
5659
6419
  const parsed = JSON.parse(await Bun.file(p).text());
5660
6420
  if (Array.isArray(parsed)) {
@@ -5670,7 +6430,7 @@ var init_monitors = __esm(() => {
5670
6430
  }
5671
6431
  if (!arr) {
5672
6432
  const mp = resolve14(dir, ".claude-plugin", "plugin.json");
5673
- if (existsSync29(mp)) {
6433
+ if (existsSync32(mp)) {
5674
6434
  try {
5675
6435
  const m = JSON.parse(await Bun.file(mp).text());
5676
6436
  const exp = m?.experimental;
@@ -5723,8 +6483,8 @@ var init_monitors = __esm(() => {
5723
6483
  });
5724
6484
 
5725
6485
  // src/validators/codex/plugin.ts
5726
- import { existsSync as existsSync30, readdirSync as readdirSync14 } from "fs";
5727
- import { resolve as resolve15, join as join23 } from "path";
6486
+ import { existsSync as existsSync33, readdirSync as readdirSync16 } from "fs";
6487
+ import { resolve as resolve15, join as join26 } from "path";
5728
6488
  var NAME_REGEX3, codexPluginValidator;
5729
6489
  var init_plugin2 = __esm(() => {
5730
6490
  NAME_REGEX3 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
@@ -5734,7 +6494,7 @@ var init_plugin2 = __esm(() => {
5734
6494
  name: "Codex Plugin",
5735
6495
  description: "Validates .codex-plugin/plugin.json manifest (requires interface block and skills as directory string per Codex packaging)",
5736
6496
  detect(dir) {
5737
- return existsSync30(resolve15(dir, ".codex-plugin", "plugin.json"));
6497
+ return existsSync33(resolve15(dir, ".codex-plugin", "plugin.json"));
5738
6498
  },
5739
6499
  async validate(dir, _opts) {
5740
6500
  const errors = [];
@@ -5815,11 +6575,11 @@ var init_plugin2 = __esm(() => {
5815
6575
  warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Codex)');
5816
6576
  }
5817
6577
  const skillsDir = resolve15(dir, "skills");
5818
- if (existsSync30(skillsDir)) {
5819
- const entries = readdirSync14(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
6578
+ if (existsSync33(skillsDir)) {
6579
+ const entries = readdirSync16(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
5820
6580
  for (const e of entries) {
5821
- const md = join23(skillsDir, e.name, "SKILL.md");
5822
- if (existsSync30(md)) {
6581
+ const md = join26(skillsDir, e.name, "SKILL.md");
6582
+ if (existsSync33(md)) {
5823
6583
  passes.push(`skills/${e.name}/SKILL.md exists`);
5824
6584
  } else {
5825
6585
  errors.push(`skills/${e.name}/ is missing SKILL.md`);
@@ -5837,7 +6597,7 @@ var init_plugin2 = __esm(() => {
5837
6597
  });
5838
6598
 
5839
6599
  // src/validators/codex/marketplace.ts
5840
- import { existsSync as existsSync31 } from "fs";
6600
+ import { existsSync as existsSync34 } from "fs";
5841
6601
  import { resolve as resolve16 } from "path";
5842
6602
  var codexMarketplaceValidator;
5843
6603
  var init_marketplace2 = __esm(() => {
@@ -5847,9 +6607,9 @@ var init_marketplace2 = __esm(() => {
5847
6607
  name: "Codex Plugin Marketplace",
5848
6608
  description: "Validates .agents/plugins/marketplace.json (Codex convention: object source + policy blocks)",
5849
6609
  detect(dir) {
5850
- if (existsSync31(resolve16(dir, ".agents", "plugins", "marketplace.json")))
6610
+ if (existsSync34(resolve16(dir, ".agents", "plugins", "marketplace.json")))
5851
6611
  return true;
5852
- if (existsSync31(resolve16(dir, ".agents", "plugins", "marketplace.json")))
6612
+ if (existsSync34(resolve16(dir, ".agents", "plugins", "marketplace.json")))
5853
6613
  return true;
5854
6614
  return false;
5855
6615
  },
@@ -5858,7 +6618,7 @@ var init_marketplace2 = __esm(() => {
5858
6618
  const warnings = [];
5859
6619
  const passes = [];
5860
6620
  const marketplacePath = resolve16(dir, ".agents", "plugins", "marketplace.json");
5861
- if (!existsSync31(marketplacePath)) {
6621
+ if (!existsSync34(marketplacePath)) {
5862
6622
  errors.push("Missing .agents/plugins/marketplace.json");
5863
6623
  return { errors, warnings, passes };
5864
6624
  }
@@ -5933,12 +6693,12 @@ var init_marketplace2 = __esm(() => {
5933
6693
  passes.push(`plugins[${i}].category: "${p.category}"`);
5934
6694
  }
5935
6695
  }
5936
- if (existsSync31(resolve16(dir, "README.md"))) {
6696
+ if (existsSync34(resolve16(dir, "README.md"))) {
5937
6697
  passes.push("README.md exists at marketplace root");
5938
6698
  } else {
5939
6699
  warnings.push("No README.md at marketplace root \u2014 recommended");
5940
6700
  }
5941
- if (existsSync31(resolve16(dir, "LICENSE"))) {
6701
+ if (existsSync34(resolve16(dir, "LICENSE"))) {
5942
6702
  passes.push("LICENSE exists at marketplace root");
5943
6703
  } else {
5944
6704
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
@@ -5949,7 +6709,7 @@ var init_marketplace2 = __esm(() => {
5949
6709
  });
5950
6710
 
5951
6711
  // src/validators/codex/mcp.ts
5952
- import { existsSync as existsSync32 } from "fs";
6712
+ import { existsSync as existsSync35 } from "fs";
5953
6713
  import { resolve as resolve17 } from "path";
5954
6714
  var codexMcpValidator;
5955
6715
  var init_mcp2 = __esm(() => {
@@ -5959,7 +6719,7 @@ var init_mcp2 = __esm(() => {
5959
6719
  name: "Codex MCP Config",
5960
6720
  description: "Validates .mcp.json (or inline via plugin.json mcpServers): server entries (stdio: command+args, or url), env, cwd, substitutions per Codex MCP support",
5961
6721
  detect(dir) {
5962
- return existsSync32(resolve17(dir, ".mcp.json"));
6722
+ return existsSync35(resolve17(dir, ".mcp.json"));
5963
6723
  },
5964
6724
  async validate(dir, _opts) {
5965
6725
  const errors = [];
@@ -6019,7 +6779,7 @@ var init_mcp2 = __esm(() => {
6019
6779
  });
6020
6780
 
6021
6781
  // src/validators/codex/skill.ts
6022
- import { existsSync as existsSync33 } from "fs";
6782
+ import { existsSync as existsSync36 } from "fs";
6023
6783
  import { resolve as resolve18 } from "path";
6024
6784
  var codexSkillValidator;
6025
6785
  var init_skill2 = __esm(() => {
@@ -6030,7 +6790,7 @@ var init_skill2 = __esm(() => {
6030
6790
  name: "Codex Skill",
6031
6791
  description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Codex uses the same SKILL.md spec as other providers.",
6032
6792
  detect(dir) {
6033
- return existsSync33(resolve18(dir, "SKILL.md"));
6793
+ return existsSync36(resolve18(dir, "SKILL.md"));
6034
6794
  },
6035
6795
  async validate(dir, _opts) {
6036
6796
  const loaded = await loadSkill(dir);
@@ -6048,8 +6808,8 @@ var init_skill2 = __esm(() => {
6048
6808
  });
6049
6809
 
6050
6810
  // src/validators/cursor/plugin.ts
6051
- import { existsSync as existsSync34, readdirSync as readdirSync16 } from "fs";
6052
- import { resolve as resolve19, join as join25 } from "path";
6811
+ import { existsSync as existsSync37, readdirSync as readdirSync18 } from "fs";
6812
+ import { resolve as resolve19, join as join28 } from "path";
6053
6813
  var NAME_REGEX4, cursorPluginValidator;
6054
6814
  var init_plugin3 = __esm(() => {
6055
6815
  NAME_REGEX4 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
@@ -6059,7 +6819,7 @@ var init_plugin3 = __esm(() => {
6059
6819
  name: "Cursor Plugin",
6060
6820
  description: "Validates .cursor-plugin/plugin.json manifest (skills as directory string; mcpServers support)",
6061
6821
  detect(dir) {
6062
- return existsSync34(resolve19(dir, ".cursor-plugin", "plugin.json"));
6822
+ return existsSync37(resolve19(dir, ".cursor-plugin", "plugin.json"));
6063
6823
  },
6064
6824
  async validate(dir, _opts) {
6065
6825
  const errors = [];
@@ -6146,11 +6906,11 @@ var init_plugin3 = __esm(() => {
6146
6906
  warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Cursor)');
6147
6907
  }
6148
6908
  const skillsDir = resolve19(dir, "skills");
6149
- if (existsSync34(skillsDir)) {
6150
- const entries = readdirSync16(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
6909
+ if (existsSync37(skillsDir)) {
6910
+ const entries = readdirSync18(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
6151
6911
  for (const e of entries) {
6152
- const md = join25(skillsDir, e.name, "SKILL.md");
6153
- if (existsSync34(md)) {
6912
+ const md = join28(skillsDir, e.name, "SKILL.md");
6913
+ if (existsSync37(md)) {
6154
6914
  passes.push(`skills/${e.name}/SKILL.md exists`);
6155
6915
  } else {
6156
6916
  errors.push(`skills/${e.name}/ is missing SKILL.md`);
@@ -6161,7 +6921,7 @@ var init_plugin3 = __esm(() => {
6161
6921
  const mcpRef = manifest.mcpServers;
6162
6922
  if (mcpRef.startsWith("./") || mcpRef.startsWith("../")) {
6163
6923
  const mcpPath = resolve19(dir, mcpRef);
6164
- if (existsSync34(mcpPath)) {
6924
+ if (existsSync37(mcpPath)) {
6165
6925
  passes.push(`mcpServers file exists at ${mcpRef}`);
6166
6926
  } else {
6167
6927
  warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
@@ -6191,8 +6951,8 @@ var init_plugin3 = __esm(() => {
6191
6951
  });
6192
6952
 
6193
6953
  // src/validators/cursor/marketplace.ts
6194
- import { existsSync as existsSync35, readdirSync as readdirSync17 } from "fs";
6195
- import { resolve as resolve20, join as join26 } from "path";
6954
+ import { existsSync as existsSync38, readdirSync as readdirSync19 } from "fs";
6955
+ import { resolve as resolve20, join as join29 } from "path";
6196
6956
  var cursorMarketplaceValidator;
6197
6957
  var init_marketplace3 = __esm(() => {
6198
6958
  cursorMarketplaceValidator = {
@@ -6201,18 +6961,18 @@ var init_marketplace3 = __esm(() => {
6201
6961
  name: "Cursor Plugin Marketplace",
6202
6962
  description: "Validates .cursor-plugin/marketplace.json (string sources + metadata.pluginRoot)",
6203
6963
  detect(dir) {
6204
- if (existsSync35(resolve20(dir, ".cursor-plugin", "marketplace.json")))
6964
+ if (existsSync38(resolve20(dir, ".cursor-plugin", "marketplace.json")))
6205
6965
  return true;
6206
6966
  const pluginsDir = resolve20(dir, "plugins");
6207
- if (!existsSync35(pluginsDir))
6967
+ if (!existsSync38(pluginsDir))
6208
6968
  return false;
6209
6969
  try {
6210
- const entries = readdirSync17(pluginsDir, { withFileTypes: true });
6970
+ const entries = readdirSync19(pluginsDir, { withFileTypes: true });
6211
6971
  for (const entry of entries) {
6212
6972
  if (!entry.isDirectory())
6213
6973
  continue;
6214
- const hasSkills = existsSync35(join26(pluginsDir, entry.name, "skills"));
6215
- const hasManifest = existsSync35(join26(pluginsDir, entry.name, ".cursor-plugin", "plugin.json"));
6974
+ const hasSkills = existsSync38(join29(pluginsDir, entry.name, "skills"));
6975
+ const hasManifest = existsSync38(join29(pluginsDir, entry.name, ".cursor-plugin", "plugin.json"));
6216
6976
  if (hasSkills || hasManifest)
6217
6977
  return true;
6218
6978
  }
@@ -6224,9 +6984,9 @@ var init_marketplace3 = __esm(() => {
6224
6984
  const warnings = [];
6225
6985
  const passes = [];
6226
6986
  const cursorMktPath = resolve20(dir, ".cursor-plugin", "marketplace.json");
6227
- const hasCursorMkt = existsSync35(cursorMktPath);
6987
+ const hasCursorMkt = existsSync38(cursorMktPath);
6228
6988
  const pluginsDir = resolve20(dir, "plugins");
6229
- const hasPluginsDirLayout = existsSync35(pluginsDir);
6989
+ const hasPluginsDirLayout = existsSync38(pluginsDir);
6230
6990
  if (!hasCursorMkt && !hasPluginsDirLayout) {
6231
6991
  errors.push("Missing .cursor-plugin/marketplace.json or plugins/ directory");
6232
6992
  return { errors, warnings, passes };
@@ -6280,9 +7040,9 @@ var init_marketplace3 = __esm(() => {
6280
7040
  const src = String(p.source);
6281
7041
  passes.push(`plugins[${i}].source: "${src}"`);
6282
7042
  const srcDir = resolve20(dir, pluginRoot, src);
6283
- if (existsSync35(srcDir)) {
6284
- const hasManifest = existsSync35(resolve20(srcDir, ".cursor-plugin", "plugin.json"));
6285
- const hasSkills = existsSync35(resolve20(srcDir, "skills"));
7043
+ if (existsSync38(srcDir)) {
7044
+ const hasManifest = existsSync38(resolve20(srcDir, ".cursor-plugin", "plugin.json"));
7045
+ const hasSkills = existsSync38(resolve20(srcDir, "skills"));
6286
7046
  if (hasManifest || hasSkills) {
6287
7047
  passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
6288
7048
  } else {
@@ -6293,7 +7053,7 @@ var init_marketplace3 = __esm(() => {
6293
7053
  }
6294
7054
  } else {
6295
7055
  const implicitSrc = resolve20(dir, pluginRoot, p.name || "");
6296
- if (p.name && existsSync35(implicitSrc)) {
7056
+ if (p.name && existsSync38(implicitSrc)) {
6297
7057
  passes.push(`plugins[${i}]: implicit source via name under ${pluginRoot}`);
6298
7058
  } else {
6299
7059
  warnings.push(`plugins[${i}]: missing "source" (and no implicit dir)`);
@@ -6308,12 +7068,12 @@ var init_marketplace3 = __esm(() => {
6308
7068
  passes.push(`plugins[${i}].homepage present`);
6309
7069
  }
6310
7070
  }
6311
- if (existsSync35(resolve20(dir, "README.md"))) {
7071
+ if (existsSync38(resolve20(dir, "README.md"))) {
6312
7072
  passes.push("README.md exists at marketplace root");
6313
7073
  } else {
6314
7074
  warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
6315
7075
  }
6316
- if (existsSync35(resolve20(dir, "LICENSE"))) {
7076
+ if (existsSync38(resolve20(dir, "LICENSE"))) {
6317
7077
  passes.push("LICENSE exists at marketplace root");
6318
7078
  } else {
6319
7079
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
@@ -6322,21 +7082,21 @@ var init_marketplace3 = __esm(() => {
6322
7082
  }
6323
7083
  if (hasPluginsDirLayout) {
6324
7084
  passes.push("plugins/ directory exists");
6325
- const pluginEntries = readdirSync17(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
7085
+ const pluginEntries = readdirSync19(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
6326
7086
  if (pluginEntries.length === 0) {
6327
7087
  errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
6328
7088
  return { errors, warnings, passes };
6329
7089
  }
6330
7090
  passes.push(`${pluginEntries.length} plugin(s) found`);
6331
- if (existsSync35(resolve20(dir, "README.md"))) {
7091
+ if (existsSync38(resolve20(dir, "README.md"))) {
6332
7092
  passes.push("README.md exists at marketplace root");
6333
7093
  } else {
6334
7094
  warnings.push("No README.md at marketplace root \u2014 recommended");
6335
7095
  }
6336
7096
  for (const plugin of pluginEntries) {
6337
- const pluginPath = join26(pluginsDir, plugin.name);
6338
- const hasSkills = existsSync35(join26(pluginPath, "skills"));
6339
- const hasManifest = existsSync35(join26(pluginPath, ".cursor-plugin", "plugin.json"));
7097
+ const pluginPath = join29(pluginsDir, plugin.name);
7098
+ const hasSkills = existsSync38(join29(pluginPath, "skills"));
7099
+ const hasManifest = existsSync38(join29(pluginPath, ".cursor-plugin", "plugin.json"));
6340
7100
  if (hasManifest || hasSkills) {
6341
7101
  passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
6342
7102
  }
@@ -6349,7 +7109,7 @@ var init_marketplace3 = __esm(() => {
6349
7109
  });
6350
7110
 
6351
7111
  // src/validators/cursor/mcp.ts
6352
- import { existsSync as existsSync36 } from "fs";
7112
+ import { existsSync as existsSync39 } from "fs";
6353
7113
  import { resolve as resolve21 } from "path";
6354
7114
  var cursorMcpValidator;
6355
7115
  var init_mcp3 = __esm(() => {
@@ -6359,7 +7119,7 @@ var init_mcp3 = __esm(() => {
6359
7119
  name: "Cursor MCP Config",
6360
7120
  description: "Validates mcp.json (Cursor uses no leading dot; supports mcpServers wrapper or direct server map)",
6361
7121
  detect(dir) {
6362
- return existsSync36(resolve21(dir, "mcp.json"));
7122
+ return existsSync39(resolve21(dir, "mcp.json"));
6363
7123
  },
6364
7124
  async validate(dir, _opts) {
6365
7125
  const errors = [];
@@ -6425,7 +7185,7 @@ var init_mcp3 = __esm(() => {
6425
7185
  });
6426
7186
 
6427
7187
  // src/validators/cursor/skill.ts
6428
- import { existsSync as existsSync37 } from "fs";
7188
+ import { existsSync as existsSync40 } from "fs";
6429
7189
  import { resolve as resolve22 } from "path";
6430
7190
  var cursorSkillValidator;
6431
7191
  var init_skill3 = __esm(() => {
@@ -6436,7 +7196,7 @@ var init_skill3 = __esm(() => {
6436
7196
  name: "Cursor Skill",
6437
7197
  description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Cursor uses the same SKILL.md spec as other providers.",
6438
7198
  detect(dir) {
6439
- return existsSync37(resolve22(dir, "SKILL.md"));
7199
+ return existsSync40(resolve22(dir, "SKILL.md"));
6440
7200
  },
6441
7201
  async validate(dir, _opts) {
6442
7202
  const loaded = await loadSkill(dir);
@@ -6454,7 +7214,7 @@ var init_skill3 = __esm(() => {
6454
7214
  });
6455
7215
 
6456
7216
  // src/validators/copilot/plugin.ts
6457
- import { existsSync as existsSync38 } from "fs";
7217
+ import { existsSync as existsSync41 } from "fs";
6458
7218
  import { resolve as resolve23 } from "path";
6459
7219
  var NAME_REGEX5, copilotPluginValidator;
6460
7220
  var init_plugin4 = __esm(() => {
@@ -6465,7 +7225,7 @@ var init_plugin4 = __esm(() => {
6465
7225
  name: "Copilot Plugin",
6466
7226
  description: "Validates .github/plugin/plugin.json (skills as array of paths, mcpServers support)",
6467
7227
  detect(dir) {
6468
- return existsSync38(resolve23(dir, ".github", "plugin", "plugin.json"));
7228
+ return existsSync41(resolve23(dir, ".github", "plugin", "plugin.json"));
6469
7229
  },
6470
7230
  async validate(dir, _opts) {
6471
7231
  const errors = [];
@@ -6508,9 +7268,9 @@ var init_plugin4 = __esm(() => {
6508
7268
  }
6509
7269
  const skillDir = resolve23(dir, p);
6510
7270
  const skillMd = resolve23(skillDir, "SKILL.md");
6511
- if (existsSync38(skillMd)) {
7271
+ if (existsSync41(skillMd)) {
6512
7272
  passes.push(`skills[${i}]: ${p}/SKILL.md exists`);
6513
- } else if (existsSync38(skillDir)) {
7273
+ } else if (existsSync41(skillDir)) {
6514
7274
  warnings.push(`skills[${i}]: directory exists but no SKILL.md inside`);
6515
7275
  } else {
6516
7276
  warnings.push(`skills[${i}]: path "${p}" does not exist`);
@@ -6522,7 +7282,7 @@ var init_plugin4 = __esm(() => {
6522
7282
  passes.push(`mcpServers: "${manifest.mcpServers}"`);
6523
7283
  const mcpRef = String(manifest.mcpServers);
6524
7284
  const mcpPath = resolve23(dir, mcpRef);
6525
- if (existsSync38(mcpPath)) {
7285
+ if (existsSync41(mcpPath)) {
6526
7286
  passes.push(`mcpServers file exists at ${mcpRef}`);
6527
7287
  } else {
6528
7288
  warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
@@ -6582,7 +7342,7 @@ var init_plugin4 = __esm(() => {
6582
7342
  });
6583
7343
 
6584
7344
  // src/validators/copilot/marketplace.ts
6585
- import { existsSync as existsSync39 } from "fs";
7345
+ import { existsSync as existsSync42 } from "fs";
6586
7346
  import { resolve as resolve24 } from "path";
6587
7347
  var copilotMarketplaceValidator;
6588
7348
  var init_marketplace4 = __esm(() => {
@@ -6592,7 +7352,7 @@ var init_marketplace4 = __esm(() => {
6592
7352
  name: "Copilot Plugin Marketplace",
6593
7353
  description: "Validates .github/plugin/marketplace.json (string sources)",
6594
7354
  detect(dir) {
6595
- return existsSync39(resolve24(dir, ".github", "plugin", "marketplace.json"));
7355
+ return existsSync42(resolve24(dir, ".github", "plugin", "marketplace.json"));
6596
7356
  },
6597
7357
  async validate(dir, _opts) {
6598
7358
  const errors = [];
@@ -6642,9 +7402,9 @@ var init_marketplace4 = __esm(() => {
6642
7402
  const src = String(p.source);
6643
7403
  passes.push(`plugins[${i}].source: "${src}"`);
6644
7404
  const srcDir = resolve24(dir, src);
6645
- if (existsSync39(srcDir)) {
6646
- const hasManifest = existsSync39(resolve24(srcDir, ".github", "plugin", "plugin.json"));
6647
- const hasSkills = existsSync39(resolve24(srcDir, "skills"));
7405
+ if (existsSync42(srcDir)) {
7406
+ const hasManifest = existsSync42(resolve24(srcDir, ".github", "plugin", "plugin.json"));
7407
+ const hasSkills = existsSync42(resolve24(srcDir, "skills"));
6648
7408
  if (hasManifest || hasSkills) {
6649
7409
  passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
6650
7410
  } else {
@@ -6661,12 +7421,12 @@ var init_marketplace4 = __esm(() => {
6661
7421
  if (p.version)
6662
7422
  passes.push(`plugins[${i}].version: "${p.version}"`);
6663
7423
  }
6664
- if (existsSync39(resolve24(dir, "README.md"))) {
7424
+ if (existsSync42(resolve24(dir, "README.md"))) {
6665
7425
  passes.push("README.md exists at marketplace root");
6666
7426
  } else {
6667
7427
  warnings.push("No README.md at marketplace root \u2014 recommended");
6668
7428
  }
6669
- if (existsSync39(resolve24(dir, "LICENSE"))) {
7429
+ if (existsSync42(resolve24(dir, "LICENSE"))) {
6670
7430
  passes.push("LICENSE exists at marketplace root");
6671
7431
  } else {
6672
7432
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
@@ -6677,7 +7437,7 @@ var init_marketplace4 = __esm(() => {
6677
7437
  });
6678
7438
 
6679
7439
  // src/validators/copilot/mcp.ts
6680
- import { existsSync as existsSync40 } from "fs";
7440
+ import { existsSync as existsSync43 } from "fs";
6681
7441
  import { resolve as resolve25 } from "path";
6682
7442
  var copilotMcpValidator;
6683
7443
  var init_mcp4 = __esm(() => {
@@ -6687,7 +7447,7 @@ var init_mcp4 = __esm(() => {
6687
7447
  name: "Copilot MCP Config",
6688
7448
  description: "Validates .mcp.json (referenced via mcpServers in manifest). Supports stdio and http servers.",
6689
7449
  detect(dir) {
6690
- return existsSync40(resolve25(dir, ".mcp.json"));
7450
+ return existsSync43(resolve25(dir, ".mcp.json"));
6691
7451
  },
6692
7452
  async validate(dir, _opts) {
6693
7453
  const errors = [];
@@ -6753,7 +7513,7 @@ var init_mcp4 = __esm(() => {
6753
7513
  });
6754
7514
 
6755
7515
  // src/validators/copilot/skill.ts
6756
- import { existsSync as existsSync41 } from "fs";
7516
+ import { existsSync as existsSync44 } from "fs";
6757
7517
  import { resolve as resolve26 } from "path";
6758
7518
  var copilotSkillValidator;
6759
7519
  var init_skill4 = __esm(() => {
@@ -6764,7 +7524,7 @@ var init_skill4 = __esm(() => {
6764
7524
  name: "Copilot Skill",
6765
7525
  description: "Validates SKILL.md (shared format). Copilot supports skills referenced via array paths in the manifest.",
6766
7526
  detect(dir) {
6767
- return existsSync41(resolve26(dir, "SKILL.md"));
7527
+ return existsSync44(resolve26(dir, "SKILL.md"));
6768
7528
  },
6769
7529
  async validate(dir, _opts) {
6770
7530
  const loaded = await loadSkill(dir);
@@ -6932,7 +7692,7 @@ var init_validators = __esm(() => {
6932
7692
  // src/core/remote.ts
6933
7693
  import { spawnSync as spawnSync4 } from "child_process";
6934
7694
  import { mkdtempSync, rmSync } from "fs";
6935
- import { join as join28 } from "path";
7695
+ import { join as join31 } from "path";
6936
7696
  import { tmpdir } from "os";
6937
7697
  function parseRemoteUrl(input) {
6938
7698
  if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) {
@@ -6969,7 +7729,7 @@ function isGhAvailable() {
6969
7729
  return ghAvailable;
6970
7730
  }
6971
7731
  async function cloneToTemp(parsed) {
6972
- const tmpDir = mkdtempSync(join28(tmpdir(), "dora-"));
7732
+ const tmpDir = mkdtempSync(join31(tmpdir(), "dora-"));
6973
7733
  const cleanup = () => {
6974
7734
  try {
6975
7735
  rmSync(tmpDir, { recursive: true, force: true });
@@ -7017,15 +7777,15 @@ var exports_validate_top = {};
7017
7777
  __export(exports_validate_top, {
7018
7778
  default: () => validate_top_default
7019
7779
  });
7020
- import { existsSync as existsSync43 } from "fs";
7780
+ import { existsSync as existsSync46 } from "fs";
7021
7781
  import { resolve as resolve27 } from "path";
7022
- var import_picocolors17, validate_top_default;
7782
+ var import_picocolors21, validate_top_default;
7023
7783
  var init_validate_top = __esm(() => {
7024
7784
  init_dist();
7025
7785
  init_out();
7026
7786
  init_validators();
7027
7787
  init_remote();
7028
- import_picocolors17 = __toESM(require_picocolors(), 1);
7788
+ import_picocolors21 = __toESM(require_picocolors(), 1);
7029
7789
  validate_top_default = defineCommand({
7030
7790
  meta: {
7031
7791
  name: "validate",
@@ -7065,7 +7825,7 @@ var init_validate_top = __esm(() => {
7065
7825
  let cleanup;
7066
7826
  if (remote) {
7067
7827
  ui.info(`
7068
- Cloning ${import_picocolors17.default.dim(args.path)}...`);
7828
+ Cloning ${import_picocolors21.default.dim(args.path)}...`);
7069
7829
  try {
7070
7830
  const result = await cloneToTemp(remote);
7071
7831
  fullPath = remote.subpath ? resolve27(result.dir, remote.subpath) : result.dir;
@@ -7075,14 +7835,14 @@ var init_validate_top = __esm(() => {
7075
7835
  ui.fail(msg);
7076
7836
  process.exit(1);
7077
7837
  }
7078
- if (!existsSync43(fullPath)) {
7838
+ if (!existsSync46(fullPath)) {
7079
7839
  cleanup();
7080
7840
  ui.fail(`Subdirectory not found in repo: ${remote.subpath}`);
7081
7841
  process.exit(1);
7082
7842
  }
7083
7843
  } else {
7084
7844
  fullPath = resolve27(args.path);
7085
- if (!existsSync43(fullPath)) {
7845
+ if (!existsSync46(fullPath)) {
7086
7846
  ui.fail(`Path not found: ${args.path}
7087
7847
 
7088
7848
  Check that the path is correct and the directory exists.`);
@@ -7113,13 +7873,13 @@ Check that the path is correct and the directory exists.`);
7113
7873
  ` + `Available providers:
7114
7874
  ` + providers.map((p) => {
7115
7875
  const pvs = validators.filter((v) => v.provider === p);
7116
- return ` ${import_picocolors17.default.bold(p)}
7117
- ` + pvs.map((v) => ` \u2022 ${import_picocolors17.default.dim(v.id)} \u2014 ${v.description}`).join(`
7876
+ return ` ${import_picocolors21.default.bold(p)}
7877
+ ` + pvs.map((v) => ` \u2022 ${import_picocolors21.default.dim(v.id)} \u2014 ${v.description}`).join(`
7118
7878
  `);
7119
7879
  }).join(`
7120
7880
  `) + `
7121
7881
 
7122
- Use ${import_picocolors17.default.dim("--for <provider>")} or ${import_picocolors17.default.dim("--for <provider:type>")} to target explicitly.`);
7882
+ Use ${import_picocolors21.default.dim("--for <provider>")} or ${import_picocolors21.default.dim("--for <provider:type>")} to target explicitly.`);
7123
7883
  process.exit(1);
7124
7884
  }
7125
7885
  const allResults = [];
@@ -7140,7 +7900,7 @@ Use ${import_picocolors17.default.dim("--for <provider>")} or ${import_picocolor
7140
7900
  } else {
7141
7901
  for (const { id, name, result } of allResults) {
7142
7902
  ui.write(`
7143
- ${import_picocolors17.default.bold("dora validate")} \u2014 ${import_picocolors17.default.white(name)} ${import_picocolors17.default.dim(`(${id})`)}
7903
+ ${import_picocolors21.default.bold("dora validate")} \u2014 ${import_picocolors21.default.white(name)} ${import_picocolors21.default.dim(`(${id})`)}
7144
7904
  `);
7145
7905
  ui.info(` Path: ${args.path}
7146
7906
  `);
@@ -7155,7 +7915,7 @@ Use ${import_picocolors17.default.dim("--for <provider>")} or ${import_picocolor
7155
7915
  }
7156
7916
  if (result.errors.length === 0 && result.warnings.length === 0) {
7157
7917
  ui.write(`
7158
- ${import_picocolors17.default.green("\u2713")} ${import_picocolors17.default.white("All checks passed.")}
7918
+ ${import_picocolors21.default.green("\u2713")} ${import_picocolors21.default.white("All checks passed.")}
7159
7919
  `);
7160
7920
  } else {
7161
7921
  ui.info(`
@@ -7177,16 +7937,16 @@ var exports_init2 = {};
7177
7937
  __export(exports_init2, {
7178
7938
  default: () => init_default2
7179
7939
  });
7180
- import { basename as basename6, join as join29 } from "path";
7940
+ import { basename as basename7, join as join32 } from "path";
7181
7941
  var {spawnSync: spawnSync5 } = globalThis.Bun;
7182
- var import_picocolors18, init_default2;
7942
+ var import_picocolors22, init_default2;
7183
7943
  var init_init2 = __esm(() => {
7184
7944
  init_dist();
7185
7945
  init_out();
7186
7946
  init_journal_config();
7187
7947
  init_journal_remote();
7188
7948
  init_prompt();
7189
- import_picocolors18 = __toESM(require_picocolors(), 1);
7949
+ import_picocolors22 = __toESM(require_picocolors(), 1);
7190
7950
  init_default2 = defineCommand({
7191
7951
  meta: {
7192
7952
  name: "init",
@@ -7213,17 +7973,17 @@ var init_init2 = __esm(() => {
7213
7973
  ui.heading("dora init \u2014 Set up doraval, your journal, and the coding agent dora should use on the fly");
7214
7974
  const ghCheck = ensureGhCli();
7215
7975
  if (!ghCheck.ok) {
7216
- ui.write(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.white("The GitHub CLI (")}${import_picocolors18.default.bold("gh")}${import_picocolors18.default.white(") is not installed.")}
7976
+ ui.write(` ${import_picocolors22.default.red("\u2717")} ${import_picocolors22.default.white("The GitHub CLI (")}${import_picocolors22.default.bold("gh")}${import_picocolors22.default.white(") is not installed.")}
7217
7977
  `);
7218
- ui.info(` doraval uses ${import_picocolors18.default.bold("gh")} to fetch and sync journal files with GitHub.
7978
+ ui.info(` doraval uses ${import_picocolors22.default.bold("gh")} to fetch and sync journal files with GitHub.
7219
7979
  `);
7220
7980
  ui.info(` Install it:
7221
7981
  `);
7222
- ui.info(` macOS: ${import_picocolors18.default.dim("brew install gh")}`);
7223
- ui.info(` Linux: ${import_picocolors18.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
7224
- ui.info(` Windows: ${import_picocolors18.default.dim("winget install --id GitHub.cli")}
7982
+ ui.info(` macOS: ${import_picocolors22.default.dim("brew install gh")}`);
7983
+ ui.info(` Linux: ${import_picocolors22.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
7984
+ ui.info(` Windows: ${import_picocolors22.default.dim("winget install --id GitHub.cli")}
7225
7985
  `);
7226
- ui.info(` Then authenticate: ${import_picocolors18.default.dim("gh auth login")}
7986
+ ui.info(` Then authenticate: ${import_picocolors22.default.dim("gh auth login")}
7227
7987
  `);
7228
7988
  process.exit(1);
7229
7989
  }
@@ -7236,44 +7996,44 @@ var init_init2 = __esm(() => {
7236
7996
  if (gitOwner) {
7237
7997
  defaultRepo = `${gitOwner}/${gitOwner}.md`;
7238
7998
  if (ghLogin && ghLogin !== gitOwner) {
7239
- sourceNote = ` ${import_picocolors18.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
7999
+ sourceNote = ` ${import_picocolors22.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
7240
8000
  `;
7241
8001
  } else {
7242
- sourceNote = ` ${import_picocolors18.default.dim("(from git remote)")}
8002
+ sourceNote = ` ${import_picocolors22.default.dim("(from git remote)")}
7243
8003
  `;
7244
8004
  }
7245
8005
  } else if (ghLogin) {
7246
8006
  defaultRepo = `${ghLogin}/${ghLogin}.md`;
7247
- sourceNote = ` ${import_picocolors18.default.dim("(from your active gh account)")}
8007
+ sourceNote = ` ${import_picocolors22.default.dim("(from your active gh account)")}
7248
8008
  `;
7249
8009
  } else {
7250
- ui.warn(`Not logged in to GitHub. Run ${import_picocolors18.default.dim("gh auth login")} first.
8010
+ ui.warn(`Not logged in to GitHub. Run ${import_picocolors22.default.dim("gh auth login")} first.
7251
8011
  `);
7252
8012
  process.exit(1);
7253
8013
  }
7254
8014
  const existingConfig = await readConfig();
7255
8015
  if (existingConfig?.journal.repo) {
7256
8016
  defaultRepo = existingConfig.journal.repo;
7257
- sourceNote = ` ${import_picocolors18.default.dim("(from your previous journal setup)")}
8017
+ sourceNote = ` ${import_picocolors22.default.dim("(from your previous journal setup)")}
7258
8018
  `;
7259
8019
  }
7260
- ui.info(` Journal repo ${import_picocolors18.default.dim("(owner/name)")}`);
8020
+ ui.info(` Journal repo ${import_picocolors22.default.dim("(owner/name)")}`);
7261
8021
  if (sourceNote)
7262
8022
  ui.write(sourceNote);
7263
8023
  repo = prompt(" >", defaultRepo);
7264
8024
  }
7265
8025
  let project = args.project || process.env.DORAVAL_PROJECT;
7266
8026
  if (!project) {
7267
- const defaultProject = basename6(process.cwd());
8027
+ const defaultProject = basename7(process.cwd());
7268
8028
  project = prompt(" Project name", defaultProject);
7269
8029
  }
7270
8030
  project = sanitizeProjectName(project);
7271
8031
  if (!repoExists(repo)) {
7272
- ui.write(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.white("Repository")} ${import_picocolors18.default.bold(repo)} ${import_picocolors18.default.white("not found on GitHub.")}
8032
+ ui.write(` ${import_picocolors22.default.red("\u2717")} ${import_picocolors22.default.white("Repository")} ${import_picocolors22.default.bold(repo)} ${import_picocolors22.default.white("not found on GitHub.")}
7273
8033
  `);
7274
8034
  ui.info(` Create it first:
7275
8035
  `);
7276
- ui.info(` ${import_picocolors18.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
8036
+ ui.info(` ${import_picocolors22.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
7277
8037
  `);
7278
8038
  process.exit(1);
7279
8039
  }
@@ -7281,16 +8041,16 @@ var init_init2 = __esm(() => {
7281
8041
  const alreadyRegistered = existing?.journal.projects[project];
7282
8042
  const isRefresh = alreadyRegistered && args.refresh;
7283
8043
  if (alreadyRegistered && !isRefresh) {
7284
- ui.write(` ${import_picocolors18.default.yellow("\u26A0")} ${import_picocolors18.default.white("Project")} ${import_picocolors18.default.bold(project)} ${import_picocolors18.default.white("is already registered.")}
8044
+ ui.write(` ${import_picocolors22.default.yellow("\u26A0")} ${import_picocolors22.default.white("Project")} ${import_picocolors22.default.bold(project)} ${import_picocolors22.default.white("is already registered.")}
7285
8045
  `);
7286
8046
  ui.info(` Repo: ${existing.journal.repo}
7287
8047
  `);
7288
- ui.info(` To refresh journal files, use ${import_picocolors18.default.dim("dora journal update")} (or ${import_picocolors18.default.dim("dora init --refresh")}).
8048
+ ui.info(` To refresh journal files, use ${import_picocolors22.default.dim("dora journal update")} (or ${import_picocolors22.default.dim("dora init --refresh")}).
7289
8049
  `);
7290
8050
  }
7291
8051
  const journalsDir = getJournalsDir();
7292
8052
  const remotePath = `projects/${project}.md`;
7293
- const localPath = join29(journalsDir, `${project}.md`);
8053
+ const localPath = join32(journalsDir, `${project}.md`);
7294
8054
  const effectiveRepo = isRefresh && !args.repo ? existing.journal.repo : repo;
7295
8055
  const config = existing ?? {
7296
8056
  journal: { repo: effectiveRepo, projects: {} }
@@ -7301,9 +8061,9 @@ var init_init2 = __esm(() => {
7301
8061
  local_path: localPath
7302
8062
  };
7303
8063
  ensureDoravalDirs();
7304
- ui.write(` ${import_picocolors18.default.dim(import_picocolors18.default.gray("Fetching journal files from"))} ${import_picocolors18.default.gray(effectiveRepo)}${import_picocolors18.default.dim(import_picocolors18.default.gray("..."))}
8064
+ ui.write(` ${import_picocolors22.default.dim(import_picocolors22.default.gray("Fetching journal files from"))} ${import_picocolors22.default.gray(effectiveRepo)}${import_picocolors22.default.dim(import_picocolors22.default.gray("..."))}
7305
8065
  `);
7306
- const globalDest = join29(journalsDir, "global.md");
8066
+ const globalDest = join32(journalsDir, "global.md");
7307
8067
  const refreshGlobalRes = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
7308
8068
  let wroteGlobal;
7309
8069
  if (!refreshGlobalRes.ok) {
@@ -7320,7 +8080,7 @@ var init_init2 = __esm(() => {
7320
8080
  if (wroteGlobal) {
7321
8081
  ui.success("global.md");
7322
8082
  } else {
7323
- ui.write(` ${import_picocolors18.default.dim("\xB7")} global.md ${import_picocolors18.default.dim("(not found \u2014 will be created on first sync)")}`);
8083
+ ui.write(` ${import_picocolors22.default.dim("\xB7")} global.md ${import_picocolors22.default.dim("(not found \u2014 will be created on first sync)")}`);
7324
8084
  await Bun.write(globalDest, `# Global Journal
7325
8085
 
7326
8086
  Cross-project principles.
@@ -7342,7 +8102,7 @@ Cross-project principles.
7342
8102
  if (wroteProject) {
7343
8103
  ui.success(remotePath);
7344
8104
  } else {
7345
- ui.write(` ${import_picocolors18.default.dim("\xB7")} ${remotePath} ${import_picocolors18.default.dim("(not found \u2014 will be created on first sync)")}`);
8105
+ ui.write(` ${import_picocolors22.default.dim("\xB7")} ${remotePath} ${import_picocolors22.default.dim("(not found \u2014 will be created on first sync)")}`);
7346
8106
  await Bun.write(localPath, `# ${project} Journal
7347
8107
 
7348
8108
  Project-specific decisions.
@@ -7350,13 +8110,13 @@ Project-specific decisions.
7350
8110
  }
7351
8111
  await writeConfig(config);
7352
8112
  ui.write(`
7353
- ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Journal ready for project")} ${import_picocolors18.default.bold(import_picocolors18.default.white(project))}.
8113
+ ${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.white("Journal ready for project")} ${import_picocolors22.default.bold(import_picocolors22.default.white(project))}.
7354
8114
  `);
7355
8115
  const existingAgent = (await readConfig())?.agent;
7356
8116
  if (existingAgent?.command) {
7357
- ui.write(` ${import_picocolors18.default.bold(import_picocolors18.default.white("Coding agent (already configured)"))}
8117
+ ui.write(` ${import_picocolors22.default.bold(import_picocolors22.default.white("Coding agent (already configured)"))}
7358
8118
  `);
7359
- ui.write(` Current: ${import_picocolors18.default.dim(import_picocolors18.default.gray(existingAgent.command))} template: ${import_picocolors18.default.dim(import_picocolors18.default.gray(existingAgent.prompt_template || "(default)"))}
8119
+ ui.write(` Current: ${import_picocolors22.default.dim(import_picocolors22.default.gray(existingAgent.command))} template: ${import_picocolors22.default.dim(import_picocolors22.default.gray(existingAgent.prompt_template || "(default)"))}
7360
8120
  `);
7361
8121
  const change = prompt(" Reconfigure / change the coding agent for on-the-fly enrichment? (y/N)", "n");
7362
8122
  if (!/^y/i.test(String(change))) {
@@ -7366,16 +8126,16 @@ Project-specific decisions.
7366
8126
  if (existingAgent)
7367
8127
  cfg.agent = existingAgent;
7368
8128
  await writeConfig(cfg);
7369
- ui.write(` ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Try:")} ${import_picocolors18.default.dim(import_picocolors18.default.gray('dora journal add "short decision"'))}
8129
+ ui.write(` ${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.white("Try:")} ${import_picocolors22.default.dim(import_picocolors22.default.gray('dora journal add "short decision"'))}
7370
8130
  `);
7371
8131
  process.exit(0);
7372
8132
  return;
7373
8133
  }
7374
8134
  ui.blank();
7375
8135
  } else {
7376
- ui.write(` ${import_picocolors18.default.bold(import_picocolors18.default.white("Coding agent for journal add"))}
8136
+ ui.write(` ${import_picocolors22.default.bold(import_picocolors22.default.white("Coding agent for journal add"))}
7377
8137
  `);
7378
- ui.info(` When configured, ${import_picocolors18.default.dim(import_picocolors18.default.gray('dora journal add ".."'))} will use your agent to enrich entries with tags and rationale automatically.
8138
+ ui.info(` When configured, ${import_picocolors22.default.dim(import_picocolors22.default.gray('dora journal add ".."'))} will use your agent to enrich entries with tags and rationale automatically.
7379
8139
  `);
7380
8140
  }
7381
8141
  const common = [
@@ -7394,7 +8154,7 @@ Project-specific decisions.
7394
8154
  }
7395
8155
  }
7396
8156
  let agentCmd = detected || "claude";
7397
- ui.write(` Detected / default agent command: ${import_picocolors18.default.dim(import_picocolors18.default.gray(agentCmd))}`);
8157
+ ui.write(` Detected / default agent command: ${import_picocolors22.default.dim(import_picocolors22.default.gray(agentCmd))}`);
7398
8158
  agentCmd = prompt(" Agent command (the binary you run for prompts)", agentCmd);
7399
8159
  let template = detected ? common.find((c) => c.name === detected)?.template || '-p "{{prompt}}" --output-format json' : '-p "{{prompt}}" --output-format json';
7400
8160
  ui.info(` Prompt template (use {{prompt}} placeholder):`);
@@ -7406,11 +8166,36 @@ Project-specific decisions.
7406
8166
  };
7407
8167
  await writeConfig(finalConfig);
7408
8168
  ui.write(`
7409
- ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Agent configured.")}
8169
+ ${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.white("Agent configured.")}
7410
8170
  `);
7411
- ui.info(` Re-run ${import_picocolors18.default.dim(import_picocolors18.default.gray("dora init"))} anytime to change it.
8171
+ ui.info(` Re-run ${import_picocolors22.default.dim(import_picocolors22.default.gray("dora init"))} anytime to change it.
8172
+ `);
8173
+ ui.write(`
8174
+ ${import_picocolors22.default.bold("Step 3: Eval configuration (doraval eval)")}
7412
8175
  `);
7413
- ui.info(` Next: ${import_picocolors18.default.dim(import_picocolors18.default.gray('dora journal add ".."'))}, ${import_picocolors18.default.dim(import_picocolors18.default.gray("dora journal list"))}, or ${import_picocolors18.default.dim(import_picocolors18.default.gray("dora journal update"))}.
8176
+ const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
8177
+ const hasOpenAI = !!process.env.OPENAI_API_KEY;
8178
+ if (hasAnthropic || hasOpenAI) {
8179
+ ui.success("API key found in environment \u2014 will be used for eval.");
8180
+ } else {
8181
+ ui.warn("No ANTHROPIC_API_KEY or OPENAI_API_KEY set. Set the right one before running doraval eval.");
8182
+ }
8183
+ const evalModelAnswer = await prompt(` Which model should doraval eval use? ${import_picocolors22.default.dim("(e.g. claude-sonnet-4-6 or gpt-4o, press Enter to skip)")} `, "");
8184
+ if (evalModelAnswer.trim()) {
8185
+ const updatedConfig2 = await readConfig();
8186
+ if (updatedConfig2) {
8187
+ updatedConfig2.eval = {
8188
+ model: evalModelAnswer.trim(),
8189
+ max_tool_calls: 200,
8190
+ save_history: true
8191
+ };
8192
+ await writeConfig(updatedConfig2);
8193
+ ui.success(`eval.model set to ${evalModelAnswer.trim()}`);
8194
+ }
8195
+ } else {
8196
+ ui.dim(" Skipped. Run: dora config set eval.model <model-name>");
8197
+ }
8198
+ ui.info(` Next: ${import_picocolors22.default.dim(import_picocolors22.default.gray('dora journal add ".."'))}, ${import_picocolors22.default.dim(import_picocolors22.default.gray("dora journal list"))}, or ${import_picocolors22.default.dim(import_picocolors22.default.gray("dora journal update"))}.
7414
8199
  `);
7415
8200
  process.exit(0);
7416
8201
  }
@@ -7419,7 +8204,7 @@ Project-specific decisions.
7419
8204
 
7420
8205
  // src/core/update.ts
7421
8206
  import { resolve as resolve28 } from "path";
7422
- import { homedir as homedir3 } from "os";
8207
+ import { homedir as homedir4 } from "os";
7423
8208
  function normalizePath(p) {
7424
8209
  return p.replace(/\\/g, "/").replace(/\/+$/, "");
7425
8210
  }
@@ -7604,7 +8389,7 @@ async function writeMarker(marker) {
7604
8389
  }
7605
8390
  var MARKER_PATH;
7606
8391
  var init_update2 = __esm(() => {
7607
- MARKER_PATH = resolve28(homedir3(), ".doraval", "install.json");
8392
+ MARKER_PATH = resolve28(homedir4(), ".doraval", "install.json");
7608
8393
  });
7609
8394
 
7610
8395
  // src/cli/commands/update.ts
@@ -7613,7 +8398,7 @@ __export(exports_update2, {
7613
8398
  default: () => update_default2
7614
8399
  });
7615
8400
  import { spawnSync as spawnSync6 } from "child_process";
7616
- import { homedir as homedir4 } from "os";
8401
+ import { homedir as homedir5 } from "os";
7617
8402
  import { fileURLToPath } from "url";
7618
8403
  import { realpath, access } from "fs/promises";
7619
8404
  async function confirmUpdate() {
@@ -7659,7 +8444,7 @@ var init_update3 = __esm(() => {
7659
8444
  entrypoint,
7660
8445
  argv: process.argv,
7661
8446
  env: process.env,
7662
- homeDir: homedir4(),
8447
+ homeDir: homedir5(),
7663
8448
  realpath: (p) => realpath(p),
7664
8449
  exists: async (p) => {
7665
8450
  try {
@@ -7750,12 +8535,12 @@ var exports_providers = {};
7750
8535
  __export(exports_providers, {
7751
8536
  default: () => providers_default
7752
8537
  });
7753
- var import_picocolors19, providers_default;
8538
+ var import_picocolors23, providers_default;
7754
8539
  var init_providers2 = __esm(() => {
7755
8540
  init_dist();
7756
8541
  init_out();
7757
8542
  init_spec();
7758
- import_picocolors19 = __toESM(require_picocolors(), 1);
8543
+ import_picocolors23 = __toESM(require_picocolors(), 1);
7759
8544
  providers_default = defineCommand({
7760
8545
  meta: {
7761
8546
  name: "providers",
@@ -7780,7 +8565,7 @@ var init_providers2 = __esm(() => {
7780
8565
  for (const id of supportedProviders) {
7781
8566
  const spec = getProviderSpec(id);
7782
8567
  ui.write(`
7783
- ${import_picocolors19.default.bold(id)} \u2014 ${spec.name}`);
8568
+ ${import_picocolors23.default.bold(id)} \u2014 ${spec.name}`);
7784
8569
  ui.info(` Manifest: ${spec.manifestPath}`);
7785
8570
  ui.info(` Marketplace: ${spec.marketplacePath}`);
7786
8571
  ui.info(` MCP: ${spec.mcpFilename}`);
@@ -7812,6 +8597,8 @@ var init_completion = __esm(() => {
7812
8597
  "skill",
7813
8598
  "journal",
7814
8599
  "ui",
8600
+ "eval",
8601
+ "config",
7815
8602
  "claude",
7816
8603
  "codex",
7817
8604
  "cursor",
@@ -7821,6 +8608,8 @@ var init_completion = __esm(() => {
7821
8608
  subCommands = {
7822
8609
  skill: ["validate", "drift", "judge"],
7823
8610
  journal: ["init", "list", "context", "hook", "update", "add", "sync"],
8611
+ eval: ["history"],
8612
+ config: ["set", "get"],
7824
8613
  hook: ["enable", "disable", "status"],
7825
8614
  claude: ["new", "bump"],
7826
8615
  codex: ["new", "bump"],
@@ -7855,6 +8644,8 @@ _doraval_completions() {
7855
8644
  case "$prev" in
7856
8645
  skill) COMPREPLY=( $(compgen -W "${subCommands.skill.join(" ")}" -- "$cur") ) ;;
7857
8646
  journal) COMPREPLY=( $(compgen -W "${subCommands.journal.join(" ")}" -- "$cur") ) ;;
8647
+ eval) COMPREPLY=( $(compgen -W "${subCommands.eval.join(" ")}" -- "$cur") ) ;;
8648
+ config) COMPREPLY=( $(compgen -W "${subCommands.config.join(" ")}" -- "$cur") ) ;;
7858
8649
  hook) COMPREPLY=( $(compgen -W "${subCommands.hook.join(" ")}" -- "$cur") ) ;;
7859
8650
  ui) COMPREPLY=( $(compgen -W "${uiFlags.join(" ")}" -- "$cur") ) ;;
7860
8651
  claude|codex|cursor|copilot) COMPREPLY=( $(compgen -W "${subCommands.claude.join(" ")}" -- "$cur") ) ;;
@@ -7869,7 +8660,7 @@ complete -F _doraval_completions doraval
7869
8660
 
7870
8661
  _doraval() {
7871
8662
  local -a commands sub
7872
- commands=(validate init bump update providers skill journal ui claude codex cursor copilot)
8663
+ commands=(validate init bump update providers skill journal ui eval config claude codex cursor copilot)
7873
8664
  _arguments -C \\
7874
8665
  '1: :->cmd' \\
7875
8666
  '*::arg:->args'
@@ -7886,6 +8677,12 @@ _doraval() {
7886
8677
  journal)
7887
8678
  _describe 'subcommand' (init list context hook update add sync)
7888
8679
  ;;
8680
+ eval)
8681
+ _describe 'subcommand' (history)
8682
+ ;;
8683
+ config)
8684
+ _describe 'subcommand' (set get)
8685
+ ;;
7889
8686
  hook)
7890
8687
  _describe 'subcommand' (enable disable status)
7891
8688
  ;;
@@ -7905,10 +8702,12 @@ _doraval "$@"
7905
8702
  } else if (shell === "fish") {
7906
8703
  console.log(`# doraval fish completion
7907
8704
  complete -c doraval -f
7908
- complete -c doraval -n '__fish_use_subcommand' -a 'validate init bump update providers skill journal ui claude codex cursor copilot'
8705
+ complete -c doraval -n '__fish_use_subcommand' -a 'validate init bump update providers skill journal ui eval config claude codex cursor copilot'
7909
8706
 
7910
8707
  complete -c doraval -n '__fish_seen_subcommand_from skill' -a 'validate drift judge'
7911
8708
  complete -c doraval -n '__fish_seen_subcommand_from journal' -a 'init list context hook update add sync'
8709
+ complete -c doraval -n '__fish_seen_subcommand_from eval' -a 'history'
8710
+ complete -c doraval -n '__fish_seen_subcommand_from config' -a 'set get'
7912
8711
  complete -c doraval -n '__fish_seen_subcommand_from hook' -a 'enable disable status'
7913
8712
  complete -c doraval -n '__fish_seen_subcommand_from ui' -l port -d 'Port'
7914
8713
  complete -c doraval -n '__fish_seen_subcommand_from ui' -l open -d 'Open browser'
@@ -7930,7 +8729,7 @@ complete -c doraval -n '__fish_seen_subcommand_from claude codex cursor copilot'
7930
8729
  // src/cli/index.ts
7931
8730
  init_dist();
7932
8731
  var import__package = __toESM(require_package(), 1);
7933
- var import_picocolors20 = __toESM(require_picocolors(), 1);
8732
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
7934
8733
  var skill = defineCommand({
7935
8734
  meta: {
7936
8735
  name: "skill",
@@ -7969,6 +8768,20 @@ var journal = defineCommand({
7969
8768
  showUsage(journal);
7970
8769
  }
7971
8770
  });
8771
+ var evalCmd = defineCommand({
8772
+ meta: {
8773
+ name: "eval",
8774
+ description: "Evaluate a coding agent session against skill instructions"
8775
+ },
8776
+ subCommands: {
8777
+ history: () => Promise.resolve().then(() => (init_eval_history(), exports_eval_history)).then((m) => m.default)
8778
+ },
8779
+ async run(ctx) {
8780
+ const evalMain = await Promise.resolve().then(() => (init_eval(), exports_eval)).then((m) => m.default);
8781
+ return evalMain.run?.(ctx);
8782
+ }
8783
+ });
8784
+ var config = () => Promise.resolve().then(() => (init_config(), exports_config)).then((m) => m.default);
7972
8785
  var claude = defineCommand({
7973
8786
  meta: {
7974
8787
  name: "claude",
@@ -8085,7 +8898,7 @@ var main = defineCommand({
8085
8898
  meta: {
8086
8899
  name: "doraval",
8087
8900
  version: import__package.default.version,
8088
- description: "The context engineering toolkit for coding agents"
8901
+ description: "The context engineering toolkit for coding agent orchestrators"
8089
8902
  },
8090
8903
  subCommands: {
8091
8904
  validate: () => Promise.resolve().then(() => (init_validate_top(), exports_validate_top)).then((m) => m.default),
@@ -8096,6 +8909,8 @@ var main = defineCommand({
8096
8909
  completion: () => Promise.resolve().then(() => (init_completion(), exports_completion)).then((m) => m.default),
8097
8910
  skill: () => Promise.resolve(skill),
8098
8911
  journal: () => Promise.resolve(journal),
8912
+ eval: () => Promise.resolve(evalCmd),
8913
+ config,
8099
8914
  claude: () => Promise.resolve(claude),
8100
8915
  codex: () => Promise.resolve(codex),
8101
8916
  cursor: () => Promise.resolve(cursor),
@@ -8108,7 +8923,7 @@ var main = defineCommand({
8108
8923
  return;
8109
8924
  if (process.stdout.isTTY) {
8110
8925
  console.error(`
8111
- ` + import_picocolors20.default.blue(doraemonArt) + `
8926
+ ` + import_picocolors24.default.blue(doraemonArt) + `
8112
8927
  `);
8113
8928
  }
8114
8929
  showUsage(main);