@hacksmith/doraval 0.2.46 → 0.2.48

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.46",
602
+ version: "0.2.48",
603
603
  author: "Saif",
604
604
  repository: {
605
605
  type: "git",
@@ -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
  });
@@ -2484,140 +3136,21 @@ var init_journal_validate = __esm(() => {
2484
3136
  // src/cli/commands/journal/add.ts
2485
3137
  var exports_add = {};
2486
3138
  __export(exports_add, {
2487
- default: () => add_default,
2488
- buildAgentArgv: () => buildAgentArgv
3139
+ default: () => add_default
2489
3140
  });
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;
2609
- }
2610
- }
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;
@@ -4459,10 +5205,10 @@ async function killPort(port) {
4459
5205
  }
4460
5206
  function readPid(p) {
4461
5207
  const file = getPidFile(p);
4462
- if (!existsSync19(file))
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;
@@ -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 = (p) => join18(getDoravalDir(), `ui.${p}.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;
@@ -4515,7 +5261,7 @@ var init_ui = __esm(() => {
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";
@@ -4568,6 +5314,7 @@ var init_ui = __esm(() => {
4568
5314
  if (url2.pathname === "/api/status") {
4569
5315
  return Response.json({
4570
5316
  project: project || null,
5317
+ doravalRoot: getDoravalDir(),
4571
5318
  doravalDir: getJournalsDir(),
4572
5319
  hasConfig: !!config,
4573
5320
  repo: config?.journal?.repo ?? null
@@ -4637,8 +5384,8 @@ var init_ui = __esm(() => {
4637
5384
  return Response.json({ error: "filename required" }, { status: 400 });
4638
5385
  }
4639
5386
  const pdir = getPendingProjectDir(project);
4640
- const filePath = join18(pdir, filename);
4641
- if (existsSync19(filePath)) {
5387
+ const filePath = join21(pdir, filename);
5388
+ if (existsSync22(filePath)) {
4642
5389
  try {
4643
5390
  await Bun.file(filePath).unlink();
4644
5391
  } catch {}
@@ -4646,6 +5393,10 @@ var init_ui = __esm(() => {
4646
5393
  }
4647
5394
  return Response.json({ error: "not found" }, { status: 404 });
4648
5395
  }
5396
+ if (url2.pathname === "/api/evals") {
5397
+ const evals = await loadEvals(25);
5398
+ return Response.json({ evals });
5399
+ }
4649
5400
  if (url2.pathname.startsWith("/api/")) {
4650
5401
  return Response.json({ error: "Not found" }, { status: 404 });
4651
5402
  }
@@ -4660,19 +5411,21 @@ var init_ui = __esm(() => {
4660
5411
  const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${server.port}`;
4661
5412
  writePid(process.pid, port);
4662
5413
  const msg = `
4663
- ${import_picocolors16.default.blue("\u25C9")} dora local dashboard
4664
- ${import_picocolors16.default.dim("Project:")} ${project ? import_picocolors16.default.white(project) : import_picocolors16.default.yellow("none (run dora init)")}
4665
- ${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))}
4666
5418
 
4667
- ${import_picocolors16.default.dim("Press Ctrl+C to stop")}
5419
+ ${import_picocolors20.default.dim("Press Ctrl+C to stop")}
4668
5420
  `;
4669
5421
  console.error(msg);
5422
+ console.error(` ${import_picocolors20.default.dim("Tip:")} data location = ${getDoravalDir()} (set DORAVAL_HOME to change)`);
4670
5423
  if (shouldOpen && process.stdout.isTTY) {
4671
5424
  try {
4672
5425
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
4673
5426
  spawn(opener, [url], { stdio: "ignore", detached: true }).unref();
4674
5427
  } catch {
4675
- 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}`));
4676
5429
  }
4677
5430
  }
4678
5431
  const cleanup = () => {
@@ -4689,7 +5442,7 @@ var init_ui = __esm(() => {
4689
5442
  });
4690
5443
 
4691
5444
  // src/validators/claude/skill.ts
4692
- import { existsSync as existsSync20 } from "fs";
5445
+ import { existsSync as existsSync23 } from "fs";
4693
5446
  import { resolve as resolve5 } from "path";
4694
5447
  var claudeSkillValidator;
4695
5448
  var init_skill = __esm(() => {
@@ -4700,7 +5453,7 @@ var init_skill = __esm(() => {
4700
5453
  name: "Claude Skill",
4701
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.)",
4702
5455
  detect(dir) {
4703
- return existsSync20(resolve5(dir, "SKILL.md"));
5456
+ return existsSync23(resolve5(dir, "SKILL.md"));
4704
5457
  },
4705
5458
  async validate(dir, _opts) {
4706
5459
  const loaded = await loadSkill(dir);
@@ -4718,8 +5471,8 @@ var init_skill = __esm(() => {
4718
5471
  });
4719
5472
 
4720
5473
  // src/validators/claude/plugin.ts
4721
- import { existsSync as existsSync21, readdirSync as readdirSync10 } from "fs";
4722
- 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";
4723
5476
  function levenshtein(a, b) {
4724
5477
  if (a === b)
4725
5478
  return 0;
@@ -4811,7 +5564,7 @@ var init_plugin = __esm(() => {
4811
5564
  name: "Claude Plugin",
4812
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",
4813
5566
  detect(dir) {
4814
- return existsSync21(resolve6(dir, ".claude-plugin", "plugin.json"));
5567
+ return existsSync24(resolve6(dir, ".claude-plugin", "plugin.json"));
4815
5568
  },
4816
5569
  async validate(dir, _opts) {
4817
5570
  const errors = [];
@@ -4825,7 +5578,7 @@ var init_plugin = __esm(() => {
4825
5578
  manifest = JSON.parse(raw);
4826
5579
  passes.push(".claude-plugin/plugin.json is valid JSON");
4827
5580
  } catch (err) {
4828
- if (!existsSync21(manifestPath)) {
5581
+ if (!existsSync24(manifestPath)) {
4829
5582
  errors.push(`.claude-plugin/plugin.json is missing (looked for ${manifestPath})`);
4830
5583
  warnings.push("Hint: Run `doraval claude new` (or `dora claude new`) to scaffold a new Claude plugin in this directory.");
4831
5584
  } else {
@@ -4834,7 +5587,7 @@ var init_plugin = __esm(() => {
4834
5587
  return { errors, warnings, passes };
4835
5588
  }
4836
5589
  try {
4837
- const entries = readdirSync10(dotClaudePluginDir);
5590
+ const entries = readdirSync12(dotClaudePluginDir);
4838
5591
  const unexpected = entries.filter((e) => e !== "plugin.json");
4839
5592
  if (unexpected.length > 0) {
4840
5593
  for (const e of unexpected) {
@@ -4921,7 +5674,7 @@ var init_plugin = __esm(() => {
4921
5674
  errors.push(`${field}: path "${s}" must start with "./"`);
4922
5675
  } else if (s.includes("..")) {
4923
5676
  errors.push(`${field}: path "${s}" must not use ".." (paths are confined to the plugin tree after cache copy)`);
4924
- } else if (existsSync21(resolve6(dir, s))) {
5677
+ } else if (existsSync24(resolve6(dir, s))) {
4925
5678
  passes.push(`${field}: path "${s}" exists`);
4926
5679
  } else {
4927
5680
  warnings.push(`${field}: path "${s}" does not exist on disk`);
@@ -4971,11 +5724,11 @@ var init_plugin = __esm(() => {
4971
5724
  passes.push(`dependencies: declares ${manifest.dependencies.length} plugin dependency/ies`);
4972
5725
  }
4973
5726
  const skillsDir = resolve6(dir, "skills");
4974
- if (existsSync21(skillsDir)) {
4975
- 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());
4976
5729
  for (const e of entries) {
4977
- const md = join19(skillsDir, e.name, "SKILL.md");
4978
- if (existsSync21(md)) {
5730
+ const md = join22(skillsDir, e.name, "SKILL.md");
5731
+ if (existsSync24(md)) {
4979
5732
  passes.push(`skills/${e.name}/SKILL.md exists`);
4980
5733
  } else {
4981
5734
  errors.push(`skills/${e.name}/ is missing SKILL.md`);
@@ -4986,8 +5739,8 @@ var init_plugin = __esm(() => {
4986
5739
  }
4987
5740
  }
4988
5741
  const commandsDir = resolve6(dir, "commands");
4989
- if (existsSync21(commandsDir)) {
4990
- const mds = readdirSync10(commandsDir).filter((f) => f.endsWith(".md"));
5742
+ if (existsSync24(commandsDir)) {
5743
+ const mds = readdirSync12(commandsDir).filter((f) => f.endsWith(".md"));
4991
5744
  if (mds.length) {
4992
5745
  passes.push(`commands/ has ${mds.length} .md file(s)`);
4993
5746
  }
@@ -4996,8 +5749,8 @@ var init_plugin = __esm(() => {
4996
5749
  }
4997
5750
  }
4998
5751
  const agentsDir = resolve6(dir, "agents");
4999
- if (existsSync21(agentsDir)) {
5000
- const mds = readdirSync10(agentsDir).filter((f) => f.endsWith(".md"));
5752
+ if (existsSync24(agentsDir)) {
5753
+ const mds = readdirSync12(agentsDir).filter((f) => f.endsWith(".md"));
5001
5754
  if (mds.length) {
5002
5755
  passes.push(`agents/ has ${mds.length} .md file(s)`);
5003
5756
  }
@@ -5005,30 +5758,30 @@ var init_plugin = __esm(() => {
5005
5758
  warnings.push('agents/ co-exists with manifest "agents" \u2014 manifest replaces default (dir ignored)');
5006
5759
  }
5007
5760
  }
5008
- if (existsSync21(resolve6(dir, "output-styles"))) {
5761
+ if (existsSync24(resolve6(dir, "output-styles"))) {
5009
5762
  passes.push("output-styles/ directory present");
5010
5763
  if (manifest.outputStyles)
5011
5764
  warnings.push("output-styles/ co-exists with manifest outputStyles \u2014 manifest wins");
5012
5765
  }
5013
- if (existsSync21(resolve6(dir, "themes")))
5766
+ if (existsSync24(resolve6(dir, "themes")))
5014
5767
  passes.push("themes/ present (experimental)");
5015
- if (existsSync21(resolve6(dir, "monitors")) || manifest.experimental?.monitors) {
5768
+ if (existsSync24(resolve6(dir, "monitors")) || manifest.experimental?.monitors) {
5016
5769
  passes.push("monitors config present (experimental)");
5017
5770
  }
5018
- if (existsSync21(resolve6(dir, "bin")))
5771
+ if (existsSync24(resolve6(dir, "bin")))
5019
5772
  passes.push("bin/ present (adds executables to Bash tool $PATH)");
5020
- if (existsSync21(resolve6(dir, "settings.json")))
5773
+ if (existsSync24(resolve6(dir, "settings.json")))
5021
5774
  passes.push("settings.json present (plugin defaults for agent/statusline)");
5022
- if (existsSync21(resolve6(dir, "README.md")))
5775
+ if (existsSync24(resolve6(dir, "README.md")))
5023
5776
  passes.push("README.md present");
5024
- if (existsSync21(resolve6(dir, ".mcp.json")))
5777
+ if (existsSync24(resolve6(dir, ".mcp.json")))
5025
5778
  passes.push(".mcp.json present (validated by claude:mcp)");
5026
- if (existsSync21(resolve6(dir, ".lsp.json")))
5779
+ if (existsSync24(resolve6(dir, ".lsp.json")))
5027
5780
  passes.push(".lsp.json present (validated by claude:lsp when registered)");
5028
- 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"))) {
5029
5782
  passes.push("hooks config present (validated by claude:hooks)");
5030
5783
  }
5031
- if (existsSync21(resolve6(dir, "SKILL.md")) && !existsSync21(skillsDir) && manifest.skills === undefined) {
5784
+ if (existsSync24(resolve6(dir, "SKILL.md")) && !existsSync24(skillsDir) && manifest.skills === undefined) {
5032
5785
  passes.push('Root SKILL.md detected \u2014 plugin will be treated as a single-skill plugin (prefer frontmatter "name" for stable /command)');
5033
5786
  }
5034
5787
  return { errors, warnings, passes };
@@ -5037,8 +5790,8 @@ var init_plugin = __esm(() => {
5037
5790
  });
5038
5791
 
5039
5792
  // src/validators/claude/marketplace.ts
5040
- import { existsSync as existsSync22, readdirSync as readdirSync11 } from "fs";
5041
- 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";
5042
5795
  var claudeMarketplaceValidator;
5043
5796
  var init_marketplace = __esm(() => {
5044
5797
  claudeMarketplaceValidator = {
@@ -5047,18 +5800,18 @@ var init_marketplace = __esm(() => {
5047
5800
  name: "Claude Plugin Marketplace",
5048
5801
  description: "Validates .claude-plugin/marketplace.json or plugins/ marketplace layouts (plugins array with sources)",
5049
5802
  detect(dir) {
5050
- if (existsSync22(resolve7(dir, ".claude-plugin", "marketplace.json")))
5803
+ if (existsSync25(resolve7(dir, ".claude-plugin", "marketplace.json")))
5051
5804
  return true;
5052
5805
  const pluginsDir = resolve7(dir, "plugins");
5053
- if (!existsSync22(pluginsDir))
5806
+ if (!existsSync25(pluginsDir))
5054
5807
  return false;
5055
5808
  try {
5056
- const entries = readdirSync11(pluginsDir, { withFileTypes: true });
5809
+ const entries = readdirSync13(pluginsDir, { withFileTypes: true });
5057
5810
  for (const entry of entries) {
5058
5811
  if (!entry.isDirectory())
5059
5812
  continue;
5060
- const hasSkills = existsSync22(join20(pluginsDir, entry.name, "skills"));
5061
- 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"));
5062
5815
  if (hasSkills || hasManifest)
5063
5816
  return true;
5064
5817
  }
@@ -5070,9 +5823,9 @@ var init_marketplace = __esm(() => {
5070
5823
  const warnings = [];
5071
5824
  const passes = [];
5072
5825
  const claudeMktPath = resolve7(dir, ".claude-plugin", "marketplace.json");
5073
- const hasClaudeMkt = existsSync22(claudeMktPath);
5826
+ const hasClaudeMkt = existsSync25(claudeMktPath);
5074
5827
  const pluginsDir = resolve7(dir, "plugins");
5075
- const hasPluginsDirLayout = existsSync22(pluginsDir);
5828
+ const hasPluginsDirLayout = existsSync25(pluginsDir);
5076
5829
  if (!hasClaudeMkt && !hasPluginsDirLayout) {
5077
5830
  errors.push("Missing .claude-plugin/marketplace.json or plugins/ directory");
5078
5831
  return { errors, warnings, passes };
@@ -5117,9 +5870,9 @@ var init_marketplace = __esm(() => {
5117
5870
  const src = String(p.source);
5118
5871
  passes.push(`plugins[${i}].source: "${src}"`);
5119
5872
  const srcDir = resolve7(dir, src);
5120
- if (existsSync22(srcDir)) {
5121
- const hasManifest = existsSync22(resolve7(srcDir, ".claude-plugin", "plugin.json"));
5122
- 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"));
5123
5876
  if (hasManifest || hasSkills) {
5124
5877
  passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
5125
5878
  } else {
@@ -5135,12 +5888,12 @@ var init_marketplace = __esm(() => {
5135
5888
  passes.push(`plugins[${i}].category: "${p.category}"`);
5136
5889
  }
5137
5890
  }
5138
- if (existsSync22(resolve7(dir, "README.md"))) {
5891
+ if (existsSync25(resolve7(dir, "README.md"))) {
5139
5892
  passes.push("README.md exists at marketplace root");
5140
5893
  } else {
5141
5894
  warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
5142
5895
  }
5143
- if (existsSync22(resolve7(dir, "LICENSE"))) {
5896
+ if (existsSync25(resolve7(dir, "LICENSE"))) {
5144
5897
  passes.push("LICENSE exists at marketplace root");
5145
5898
  } else {
5146
5899
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
@@ -5149,27 +5902,27 @@ var init_marketplace = __esm(() => {
5149
5902
  }
5150
5903
  if (hasPluginsDirLayout) {
5151
5904
  passes.push("plugins/ directory exists");
5152
- const pluginEntries = readdirSync11(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
5905
+ const pluginEntries = readdirSync13(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
5153
5906
  if (pluginEntries.length === 0) {
5154
5907
  errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
5155
5908
  return { errors, warnings, passes };
5156
5909
  }
5157
5910
  passes.push(`${pluginEntries.length} plugin(s) found`);
5158
- if (existsSync22(resolve7(dir, "README.md"))) {
5911
+ if (existsSync25(resolve7(dir, "README.md"))) {
5159
5912
  passes.push("README.md exists at marketplace root");
5160
5913
  } else {
5161
5914
  warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
5162
5915
  }
5163
- if (existsSync22(resolve7(dir, "LICENSE"))) {
5916
+ if (existsSync25(resolve7(dir, "LICENSE"))) {
5164
5917
  passes.push("LICENSE exists at marketplace root");
5165
5918
  } else {
5166
5919
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
5167
5920
  }
5168
5921
  for (const plugin of pluginEntries) {
5169
- const pluginPath = join20(pluginsDir, plugin.name);
5170
- const hasSkills = existsSync22(join20(pluginPath, "skills"));
5171
- const hasManifest = existsSync22(join20(pluginPath, ".claude-plugin", "plugin.json"));
5172
- 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"));
5173
5926
  if (hasManifest || hasSkills) {
5174
5927
  passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
5175
5928
  } else {
@@ -5187,7 +5940,7 @@ var init_marketplace = __esm(() => {
5187
5940
  });
5188
5941
 
5189
5942
  // src/validators/claude/hooks.ts
5190
- import { existsSync as existsSync23 } from "fs";
5943
+ import { existsSync as existsSync26 } from "fs";
5191
5944
  import { resolve as resolve8 } from "path";
5192
5945
  var KNOWN_EVENTS, claudeHooksValidator;
5193
5946
  var init_hooks = __esm(() => {
@@ -5229,13 +5982,13 @@ var init_hooks = __esm(() => {
5229
5982
  name: "Claude Hooks",
5230
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)",
5231
5984
  detect(dir) {
5232
- 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"));
5233
5986
  },
5234
5987
  async validate(dir, _opts) {
5235
5988
  const errors = [];
5236
5989
  const warnings = [];
5237
5990
  const passes = [];
5238
- 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");
5239
5992
  let config;
5240
5993
  try {
5241
5994
  const raw = await Bun.file(hooksPath).text();
@@ -5301,7 +6054,7 @@ var init_hooks = __esm(() => {
5301
6054
  });
5302
6055
 
5303
6056
  // src/validators/claude/mcp.ts
5304
- import { existsSync as existsSync24 } from "fs";
6057
+ import { existsSync as existsSync27 } from "fs";
5305
6058
  import { resolve as resolve9 } from "path";
5306
6059
  var claudeMcpValidator;
5307
6060
  var init_mcp = __esm(() => {
@@ -5311,7 +6064,7 @@ var init_mcp = __esm(() => {
5311
6064
  name: "Claude MCP Config",
5312
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",
5313
6066
  detect(dir) {
5314
- return existsSync24(resolve9(dir, ".mcp.json"));
6067
+ return existsSync27(resolve9(dir, ".mcp.json"));
5315
6068
  },
5316
6069
  async validate(dir, _opts) {
5317
6070
  const errors = [];
@@ -5371,8 +6124,8 @@ var init_mcp = __esm(() => {
5371
6124
  });
5372
6125
 
5373
6126
  // src/validators/claude/subagent.ts
5374
- import { existsSync as existsSync25, readdirSync as readdirSync12 } from "fs";
5375
- 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";
5376
6129
  var claudeSubagentValidator;
5377
6130
  var init_subagent = __esm(() => {
5378
6131
  init_frontmatter();
@@ -5383,10 +6136,10 @@ var init_subagent = __esm(() => {
5383
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",
5384
6137
  detect(dir) {
5385
6138
  const agentsDir = resolve10(dir, "agents");
5386
- if (!existsSync25(agentsDir))
6139
+ if (!existsSync28(agentsDir))
5387
6140
  return false;
5388
6141
  try {
5389
- return readdirSync12(agentsDir).some((f) => f.endsWith(".md"));
6142
+ return readdirSync14(agentsDir).some((f) => f.endsWith(".md"));
5390
6143
  } catch {
5391
6144
  return false;
5392
6145
  }
@@ -5396,7 +6149,7 @@ var init_subagent = __esm(() => {
5396
6149
  const warnings = [];
5397
6150
  const passes = [];
5398
6151
  const agentsDir = resolve10(dir, "agents");
5399
- const mdFiles = readdirSync12(agentsDir).filter((f) => f.endsWith(".md"));
6152
+ const mdFiles = readdirSync14(agentsDir).filter((f) => f.endsWith(".md"));
5400
6153
  if (mdFiles.length === 0) {
5401
6154
  errors.push("agents/ directory has no .md files");
5402
6155
  return { errors, warnings, passes };
@@ -5417,7 +6170,7 @@ var init_subagent = __esm(() => {
5417
6170
  ]);
5418
6171
  const DISALLOWED = new Set(["hooks", "mcpServers", "permissionMode"]);
5419
6172
  for (const file of mdFiles) {
5420
- const filePath = join21(agentsDir, file);
6173
+ const filePath = join24(agentsDir, file);
5421
6174
  const raw = await Bun.file(filePath).text();
5422
6175
  try {
5423
6176
  const parsed = parseFrontmatter(raw);
@@ -5463,8 +6216,8 @@ var init_subagent = __esm(() => {
5463
6216
  });
5464
6217
 
5465
6218
  // src/validators/claude/command.ts
5466
- import { existsSync as existsSync26, readdirSync as readdirSync13 } from "fs";
5467
- 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";
5468
6221
  var claudeCommandValidator;
5469
6222
  var init_command = __esm(() => {
5470
6223
  init_frontmatter();
@@ -5475,10 +6228,10 @@ var init_command = __esm(() => {
5475
6228
  description: "Validates commands/ (or legacy .claude/commands/) .md files: frontmatter (including rich skill fields), description, body",
5476
6229
  detect(dir) {
5477
6230
  const commandsDir = resolve11(dir, "commands");
5478
- if (!existsSync26(commandsDir))
6231
+ if (!existsSync29(commandsDir))
5479
6232
  return false;
5480
6233
  try {
5481
- return readdirSync13(commandsDir).some((f) => f.endsWith(".md"));
6234
+ return readdirSync15(commandsDir).some((f) => f.endsWith(".md"));
5482
6235
  } catch {
5483
6236
  return false;
5484
6237
  }
@@ -5488,14 +6241,14 @@ var init_command = __esm(() => {
5488
6241
  const warnings = [];
5489
6242
  const passes = [];
5490
6243
  const commandsDir = resolve11(dir, "commands");
5491
- const mdFiles = readdirSync13(commandsDir).filter((f) => f.endsWith(".md"));
6244
+ const mdFiles = readdirSync15(commandsDir).filter((f) => f.endsWith(".md"));
5492
6245
  if (mdFiles.length === 0) {
5493
6246
  errors.push("commands/ directory has no .md files");
5494
6247
  return { errors, warnings, passes };
5495
6248
  }
5496
6249
  passes.push(`${mdFiles.length} command definition(s) found`);
5497
6250
  for (const file of mdFiles) {
5498
- const filePath = join22(commandsDir, file);
6251
+ const filePath = join25(commandsDir, file);
5499
6252
  const raw = await Bun.file(filePath).text();
5500
6253
  try {
5501
6254
  const parsed = parseFrontmatter(raw);
@@ -5524,7 +6277,7 @@ var init_command = __esm(() => {
5524
6277
  });
5525
6278
 
5526
6279
  // src/validators/claude/memory.ts
5527
- import { existsSync as existsSync27 } from "fs";
6280
+ import { existsSync as existsSync30 } from "fs";
5528
6281
  import { resolve as resolve12 } from "path";
5529
6282
  var claudeMemoryValidator;
5530
6283
  var init_memory = __esm(() => {
@@ -5534,7 +6287,7 @@ var init_memory = __esm(() => {
5534
6287
  name: "Claude CLAUDE.md",
5535
6288
  description: "Validates CLAUDE.md: non-empty, length recommendations, @path imports",
5536
6289
  detect(dir) {
5537
- return existsSync27(resolve12(dir, "CLAUDE.md"));
6290
+ return existsSync30(resolve12(dir, "CLAUDE.md"));
5538
6291
  },
5539
6292
  async validate(dir, _opts) {
5540
6293
  const errors = [];
@@ -5559,7 +6312,7 @@ var init_memory = __esm(() => {
5559
6312
  while ((match = importRegex.exec(raw)) !== null) {
5560
6313
  const importPath = match[1];
5561
6314
  const resolvedImport = resolve12(dir, importPath);
5562
- if (existsSync27(resolvedImport)) {
6315
+ if (existsSync30(resolvedImport)) {
5563
6316
  passes.push(`@import "${importPath}" exists`);
5564
6317
  } else {
5565
6318
  warnings.push(`@import "${importPath}" \u2014 file not found at ${resolvedImport}`);
@@ -5571,7 +6324,7 @@ var init_memory = __esm(() => {
5571
6324
  });
5572
6325
 
5573
6326
  // src/validators/claude/lsp.ts
5574
- import { existsSync as existsSync28 } from "fs";
6327
+ import { existsSync as existsSync31 } from "fs";
5575
6328
  import { resolve as resolve13 } from "path";
5576
6329
  var claudeLspValidator;
5577
6330
  var init_lsp = __esm(() => {
@@ -5581,7 +6334,7 @@ var init_lsp = __esm(() => {
5581
6334
  name: "Claude LSP Servers",
5582
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)",
5583
6336
  detect(dir) {
5584
- 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"));
5585
6338
  },
5586
6339
  async validate(dir, _opts) {
5587
6340
  const errors = [];
@@ -5589,7 +6342,7 @@ var init_lsp = __esm(() => {
5589
6342
  const passes = [];
5590
6343
  let cfg = null;
5591
6344
  const lspPath = resolve13(dir, ".lsp.json");
5592
- if (existsSync28(lspPath)) {
6345
+ if (existsSync31(lspPath)) {
5593
6346
  try {
5594
6347
  cfg = JSON.parse(await Bun.file(lspPath).text());
5595
6348
  passes.push(".lsp.json is valid JSON");
@@ -5599,7 +6352,7 @@ var init_lsp = __esm(() => {
5599
6352
  }
5600
6353
  } else {
5601
6354
  const manifestPath = resolve13(dir, ".claude-plugin", "plugin.json");
5602
- if (existsSync28(manifestPath)) {
6355
+ if (existsSync31(manifestPath)) {
5603
6356
  try {
5604
6357
  const m = JSON.parse(await Bun.file(manifestPath).text());
5605
6358
  if (m && m.lspServers && typeof m.lspServers === "object") {
@@ -5610,7 +6363,7 @@ var init_lsp = __esm(() => {
5610
6363
  }
5611
6364
  }
5612
6365
  if (!cfg) {
5613
- if (!existsSync28(lspPath)) {
6366
+ if (!existsSync31(lspPath)) {
5614
6367
  return { errors, warnings, passes };
5615
6368
  }
5616
6369
  }
@@ -5639,7 +6392,7 @@ var init_lsp = __esm(() => {
5639
6392
  });
5640
6393
 
5641
6394
  // src/validators/claude/monitors.ts
5642
- import { existsSync as existsSync29 } from "fs";
6395
+ import { existsSync as existsSync32 } from "fs";
5643
6396
  import { resolve as resolve14 } from "path";
5644
6397
  var claudeMonitorsValidator;
5645
6398
  var init_monitors = __esm(() => {
@@ -5649,7 +6402,7 @@ var init_monitors = __esm(() => {
5649
6402
  name: "Claude Monitors (experimental)",
5650
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.",
5651
6404
  detect(dir) {
5652
- 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"));
5653
6406
  },
5654
6407
  async validate(dir, _opts) {
5655
6408
  const errors = [];
@@ -5661,7 +6414,7 @@ var init_monitors = __esm(() => {
5661
6414
  resolve14(dir, "monitors.json")
5662
6415
  ];
5663
6416
  for (const p of candidates) {
5664
- if (existsSync29(p)) {
6417
+ if (existsSync32(p)) {
5665
6418
  try {
5666
6419
  const parsed = JSON.parse(await Bun.file(p).text());
5667
6420
  if (Array.isArray(parsed)) {
@@ -5677,7 +6430,7 @@ var init_monitors = __esm(() => {
5677
6430
  }
5678
6431
  if (!arr) {
5679
6432
  const mp = resolve14(dir, ".claude-plugin", "plugin.json");
5680
- if (existsSync29(mp)) {
6433
+ if (existsSync32(mp)) {
5681
6434
  try {
5682
6435
  const m = JSON.parse(await Bun.file(mp).text());
5683
6436
  const exp = m?.experimental;
@@ -5730,8 +6483,8 @@ var init_monitors = __esm(() => {
5730
6483
  });
5731
6484
 
5732
6485
  // src/validators/codex/plugin.ts
5733
- import { existsSync as existsSync30, readdirSync as readdirSync14 } from "fs";
5734
- 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";
5735
6488
  var NAME_REGEX3, codexPluginValidator;
5736
6489
  var init_plugin2 = __esm(() => {
5737
6490
  NAME_REGEX3 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
@@ -5741,7 +6494,7 @@ var init_plugin2 = __esm(() => {
5741
6494
  name: "Codex Plugin",
5742
6495
  description: "Validates .codex-plugin/plugin.json manifest (requires interface block and skills as directory string per Codex packaging)",
5743
6496
  detect(dir) {
5744
- return existsSync30(resolve15(dir, ".codex-plugin", "plugin.json"));
6497
+ return existsSync33(resolve15(dir, ".codex-plugin", "plugin.json"));
5745
6498
  },
5746
6499
  async validate(dir, _opts) {
5747
6500
  const errors = [];
@@ -5822,11 +6575,11 @@ var init_plugin2 = __esm(() => {
5822
6575
  warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Codex)');
5823
6576
  }
5824
6577
  const skillsDir = resolve15(dir, "skills");
5825
- if (existsSync30(skillsDir)) {
5826
- 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());
5827
6580
  for (const e of entries) {
5828
- const md = join23(skillsDir, e.name, "SKILL.md");
5829
- if (existsSync30(md)) {
6581
+ const md = join26(skillsDir, e.name, "SKILL.md");
6582
+ if (existsSync33(md)) {
5830
6583
  passes.push(`skills/${e.name}/SKILL.md exists`);
5831
6584
  } else {
5832
6585
  errors.push(`skills/${e.name}/ is missing SKILL.md`);
@@ -5844,7 +6597,7 @@ var init_plugin2 = __esm(() => {
5844
6597
  });
5845
6598
 
5846
6599
  // src/validators/codex/marketplace.ts
5847
- import { existsSync as existsSync31 } from "fs";
6600
+ import { existsSync as existsSync34 } from "fs";
5848
6601
  import { resolve as resolve16 } from "path";
5849
6602
  var codexMarketplaceValidator;
5850
6603
  var init_marketplace2 = __esm(() => {
@@ -5854,9 +6607,9 @@ var init_marketplace2 = __esm(() => {
5854
6607
  name: "Codex Plugin Marketplace",
5855
6608
  description: "Validates .agents/plugins/marketplace.json (Codex convention: object source + policy blocks)",
5856
6609
  detect(dir) {
5857
- if (existsSync31(resolve16(dir, ".agents", "plugins", "marketplace.json")))
6610
+ if (existsSync34(resolve16(dir, ".agents", "plugins", "marketplace.json")))
5858
6611
  return true;
5859
- if (existsSync31(resolve16(dir, ".agents", "plugins", "marketplace.json")))
6612
+ if (existsSync34(resolve16(dir, ".agents", "plugins", "marketplace.json")))
5860
6613
  return true;
5861
6614
  return false;
5862
6615
  },
@@ -5865,7 +6618,7 @@ var init_marketplace2 = __esm(() => {
5865
6618
  const warnings = [];
5866
6619
  const passes = [];
5867
6620
  const marketplacePath = resolve16(dir, ".agents", "plugins", "marketplace.json");
5868
- if (!existsSync31(marketplacePath)) {
6621
+ if (!existsSync34(marketplacePath)) {
5869
6622
  errors.push("Missing .agents/plugins/marketplace.json");
5870
6623
  return { errors, warnings, passes };
5871
6624
  }
@@ -5940,12 +6693,12 @@ var init_marketplace2 = __esm(() => {
5940
6693
  passes.push(`plugins[${i}].category: "${p.category}"`);
5941
6694
  }
5942
6695
  }
5943
- if (existsSync31(resolve16(dir, "README.md"))) {
6696
+ if (existsSync34(resolve16(dir, "README.md"))) {
5944
6697
  passes.push("README.md exists at marketplace root");
5945
6698
  } else {
5946
6699
  warnings.push("No README.md at marketplace root \u2014 recommended");
5947
6700
  }
5948
- if (existsSync31(resolve16(dir, "LICENSE"))) {
6701
+ if (existsSync34(resolve16(dir, "LICENSE"))) {
5949
6702
  passes.push("LICENSE exists at marketplace root");
5950
6703
  } else {
5951
6704
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
@@ -5956,7 +6709,7 @@ var init_marketplace2 = __esm(() => {
5956
6709
  });
5957
6710
 
5958
6711
  // src/validators/codex/mcp.ts
5959
- import { existsSync as existsSync32 } from "fs";
6712
+ import { existsSync as existsSync35 } from "fs";
5960
6713
  import { resolve as resolve17 } from "path";
5961
6714
  var codexMcpValidator;
5962
6715
  var init_mcp2 = __esm(() => {
@@ -5966,7 +6719,7 @@ var init_mcp2 = __esm(() => {
5966
6719
  name: "Codex MCP Config",
5967
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",
5968
6721
  detect(dir) {
5969
- return existsSync32(resolve17(dir, ".mcp.json"));
6722
+ return existsSync35(resolve17(dir, ".mcp.json"));
5970
6723
  },
5971
6724
  async validate(dir, _opts) {
5972
6725
  const errors = [];
@@ -6026,7 +6779,7 @@ var init_mcp2 = __esm(() => {
6026
6779
  });
6027
6780
 
6028
6781
  // src/validators/codex/skill.ts
6029
- import { existsSync as existsSync33 } from "fs";
6782
+ import { existsSync as existsSync36 } from "fs";
6030
6783
  import { resolve as resolve18 } from "path";
6031
6784
  var codexSkillValidator;
6032
6785
  var init_skill2 = __esm(() => {
@@ -6037,7 +6790,7 @@ var init_skill2 = __esm(() => {
6037
6790
  name: "Codex Skill",
6038
6791
  description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Codex uses the same SKILL.md spec as other providers.",
6039
6792
  detect(dir) {
6040
- return existsSync33(resolve18(dir, "SKILL.md"));
6793
+ return existsSync36(resolve18(dir, "SKILL.md"));
6041
6794
  },
6042
6795
  async validate(dir, _opts) {
6043
6796
  const loaded = await loadSkill(dir);
@@ -6055,8 +6808,8 @@ var init_skill2 = __esm(() => {
6055
6808
  });
6056
6809
 
6057
6810
  // src/validators/cursor/plugin.ts
6058
- import { existsSync as existsSync34, readdirSync as readdirSync16 } from "fs";
6059
- 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";
6060
6813
  var NAME_REGEX4, cursorPluginValidator;
6061
6814
  var init_plugin3 = __esm(() => {
6062
6815
  NAME_REGEX4 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
@@ -6066,7 +6819,7 @@ var init_plugin3 = __esm(() => {
6066
6819
  name: "Cursor Plugin",
6067
6820
  description: "Validates .cursor-plugin/plugin.json manifest (skills as directory string; mcpServers support)",
6068
6821
  detect(dir) {
6069
- return existsSync34(resolve19(dir, ".cursor-plugin", "plugin.json"));
6822
+ return existsSync37(resolve19(dir, ".cursor-plugin", "plugin.json"));
6070
6823
  },
6071
6824
  async validate(dir, _opts) {
6072
6825
  const errors = [];
@@ -6153,11 +6906,11 @@ var init_plugin3 = __esm(() => {
6153
6906
  warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Cursor)');
6154
6907
  }
6155
6908
  const skillsDir = resolve19(dir, "skills");
6156
- if (existsSync34(skillsDir)) {
6157
- 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());
6158
6911
  for (const e of entries) {
6159
- const md = join25(skillsDir, e.name, "SKILL.md");
6160
- if (existsSync34(md)) {
6912
+ const md = join28(skillsDir, e.name, "SKILL.md");
6913
+ if (existsSync37(md)) {
6161
6914
  passes.push(`skills/${e.name}/SKILL.md exists`);
6162
6915
  } else {
6163
6916
  errors.push(`skills/${e.name}/ is missing SKILL.md`);
@@ -6168,7 +6921,7 @@ var init_plugin3 = __esm(() => {
6168
6921
  const mcpRef = manifest.mcpServers;
6169
6922
  if (mcpRef.startsWith("./") || mcpRef.startsWith("../")) {
6170
6923
  const mcpPath = resolve19(dir, mcpRef);
6171
- if (existsSync34(mcpPath)) {
6924
+ if (existsSync37(mcpPath)) {
6172
6925
  passes.push(`mcpServers file exists at ${mcpRef}`);
6173
6926
  } else {
6174
6927
  warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
@@ -6198,8 +6951,8 @@ var init_plugin3 = __esm(() => {
6198
6951
  });
6199
6952
 
6200
6953
  // src/validators/cursor/marketplace.ts
6201
- import { existsSync as existsSync35, readdirSync as readdirSync17 } from "fs";
6202
- 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";
6203
6956
  var cursorMarketplaceValidator;
6204
6957
  var init_marketplace3 = __esm(() => {
6205
6958
  cursorMarketplaceValidator = {
@@ -6208,18 +6961,18 @@ var init_marketplace3 = __esm(() => {
6208
6961
  name: "Cursor Plugin Marketplace",
6209
6962
  description: "Validates .cursor-plugin/marketplace.json (string sources + metadata.pluginRoot)",
6210
6963
  detect(dir) {
6211
- if (existsSync35(resolve20(dir, ".cursor-plugin", "marketplace.json")))
6964
+ if (existsSync38(resolve20(dir, ".cursor-plugin", "marketplace.json")))
6212
6965
  return true;
6213
6966
  const pluginsDir = resolve20(dir, "plugins");
6214
- if (!existsSync35(pluginsDir))
6967
+ if (!existsSync38(pluginsDir))
6215
6968
  return false;
6216
6969
  try {
6217
- const entries = readdirSync17(pluginsDir, { withFileTypes: true });
6970
+ const entries = readdirSync19(pluginsDir, { withFileTypes: true });
6218
6971
  for (const entry of entries) {
6219
6972
  if (!entry.isDirectory())
6220
6973
  continue;
6221
- const hasSkills = existsSync35(join26(pluginsDir, entry.name, "skills"));
6222
- 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"));
6223
6976
  if (hasSkills || hasManifest)
6224
6977
  return true;
6225
6978
  }
@@ -6231,9 +6984,9 @@ var init_marketplace3 = __esm(() => {
6231
6984
  const warnings = [];
6232
6985
  const passes = [];
6233
6986
  const cursorMktPath = resolve20(dir, ".cursor-plugin", "marketplace.json");
6234
- const hasCursorMkt = existsSync35(cursorMktPath);
6987
+ const hasCursorMkt = existsSync38(cursorMktPath);
6235
6988
  const pluginsDir = resolve20(dir, "plugins");
6236
- const hasPluginsDirLayout = existsSync35(pluginsDir);
6989
+ const hasPluginsDirLayout = existsSync38(pluginsDir);
6237
6990
  if (!hasCursorMkt && !hasPluginsDirLayout) {
6238
6991
  errors.push("Missing .cursor-plugin/marketplace.json or plugins/ directory");
6239
6992
  return { errors, warnings, passes };
@@ -6287,9 +7040,9 @@ var init_marketplace3 = __esm(() => {
6287
7040
  const src = String(p.source);
6288
7041
  passes.push(`plugins[${i}].source: "${src}"`);
6289
7042
  const srcDir = resolve20(dir, pluginRoot, src);
6290
- if (existsSync35(srcDir)) {
6291
- const hasManifest = existsSync35(resolve20(srcDir, ".cursor-plugin", "plugin.json"));
6292
- 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"));
6293
7046
  if (hasManifest || hasSkills) {
6294
7047
  passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
6295
7048
  } else {
@@ -6300,7 +7053,7 @@ var init_marketplace3 = __esm(() => {
6300
7053
  }
6301
7054
  } else {
6302
7055
  const implicitSrc = resolve20(dir, pluginRoot, p.name || "");
6303
- if (p.name && existsSync35(implicitSrc)) {
7056
+ if (p.name && existsSync38(implicitSrc)) {
6304
7057
  passes.push(`plugins[${i}]: implicit source via name under ${pluginRoot}`);
6305
7058
  } else {
6306
7059
  warnings.push(`plugins[${i}]: missing "source" (and no implicit dir)`);
@@ -6315,12 +7068,12 @@ var init_marketplace3 = __esm(() => {
6315
7068
  passes.push(`plugins[${i}].homepage present`);
6316
7069
  }
6317
7070
  }
6318
- if (existsSync35(resolve20(dir, "README.md"))) {
7071
+ if (existsSync38(resolve20(dir, "README.md"))) {
6319
7072
  passes.push("README.md exists at marketplace root");
6320
7073
  } else {
6321
7074
  warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
6322
7075
  }
6323
- if (existsSync35(resolve20(dir, "LICENSE"))) {
7076
+ if (existsSync38(resolve20(dir, "LICENSE"))) {
6324
7077
  passes.push("LICENSE exists at marketplace root");
6325
7078
  } else {
6326
7079
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
@@ -6329,21 +7082,21 @@ var init_marketplace3 = __esm(() => {
6329
7082
  }
6330
7083
  if (hasPluginsDirLayout) {
6331
7084
  passes.push("plugins/ directory exists");
6332
- const pluginEntries = readdirSync17(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
7085
+ const pluginEntries = readdirSync19(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
6333
7086
  if (pluginEntries.length === 0) {
6334
7087
  errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
6335
7088
  return { errors, warnings, passes };
6336
7089
  }
6337
7090
  passes.push(`${pluginEntries.length} plugin(s) found`);
6338
- if (existsSync35(resolve20(dir, "README.md"))) {
7091
+ if (existsSync38(resolve20(dir, "README.md"))) {
6339
7092
  passes.push("README.md exists at marketplace root");
6340
7093
  } else {
6341
7094
  warnings.push("No README.md at marketplace root \u2014 recommended");
6342
7095
  }
6343
7096
  for (const plugin of pluginEntries) {
6344
- const pluginPath = join26(pluginsDir, plugin.name);
6345
- const hasSkills = existsSync35(join26(pluginPath, "skills"));
6346
- 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"));
6347
7100
  if (hasManifest || hasSkills) {
6348
7101
  passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
6349
7102
  }
@@ -6356,7 +7109,7 @@ var init_marketplace3 = __esm(() => {
6356
7109
  });
6357
7110
 
6358
7111
  // src/validators/cursor/mcp.ts
6359
- import { existsSync as existsSync36 } from "fs";
7112
+ import { existsSync as existsSync39 } from "fs";
6360
7113
  import { resolve as resolve21 } from "path";
6361
7114
  var cursorMcpValidator;
6362
7115
  var init_mcp3 = __esm(() => {
@@ -6366,7 +7119,7 @@ var init_mcp3 = __esm(() => {
6366
7119
  name: "Cursor MCP Config",
6367
7120
  description: "Validates mcp.json (Cursor uses no leading dot; supports mcpServers wrapper or direct server map)",
6368
7121
  detect(dir) {
6369
- return existsSync36(resolve21(dir, "mcp.json"));
7122
+ return existsSync39(resolve21(dir, "mcp.json"));
6370
7123
  },
6371
7124
  async validate(dir, _opts) {
6372
7125
  const errors = [];
@@ -6432,7 +7185,7 @@ var init_mcp3 = __esm(() => {
6432
7185
  });
6433
7186
 
6434
7187
  // src/validators/cursor/skill.ts
6435
- import { existsSync as existsSync37 } from "fs";
7188
+ import { existsSync as existsSync40 } from "fs";
6436
7189
  import { resolve as resolve22 } from "path";
6437
7190
  var cursorSkillValidator;
6438
7191
  var init_skill3 = __esm(() => {
@@ -6443,7 +7196,7 @@ var init_skill3 = __esm(() => {
6443
7196
  name: "Cursor Skill",
6444
7197
  description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Cursor uses the same SKILL.md spec as other providers.",
6445
7198
  detect(dir) {
6446
- return existsSync37(resolve22(dir, "SKILL.md"));
7199
+ return existsSync40(resolve22(dir, "SKILL.md"));
6447
7200
  },
6448
7201
  async validate(dir, _opts) {
6449
7202
  const loaded = await loadSkill(dir);
@@ -6461,7 +7214,7 @@ var init_skill3 = __esm(() => {
6461
7214
  });
6462
7215
 
6463
7216
  // src/validators/copilot/plugin.ts
6464
- import { existsSync as existsSync38 } from "fs";
7217
+ import { existsSync as existsSync41 } from "fs";
6465
7218
  import { resolve as resolve23 } from "path";
6466
7219
  var NAME_REGEX5, copilotPluginValidator;
6467
7220
  var init_plugin4 = __esm(() => {
@@ -6472,7 +7225,7 @@ var init_plugin4 = __esm(() => {
6472
7225
  name: "Copilot Plugin",
6473
7226
  description: "Validates .github/plugin/plugin.json (skills as array of paths, mcpServers support)",
6474
7227
  detect(dir) {
6475
- return existsSync38(resolve23(dir, ".github", "plugin", "plugin.json"));
7228
+ return existsSync41(resolve23(dir, ".github", "plugin", "plugin.json"));
6476
7229
  },
6477
7230
  async validate(dir, _opts) {
6478
7231
  const errors = [];
@@ -6515,9 +7268,9 @@ var init_plugin4 = __esm(() => {
6515
7268
  }
6516
7269
  const skillDir = resolve23(dir, p);
6517
7270
  const skillMd = resolve23(skillDir, "SKILL.md");
6518
- if (existsSync38(skillMd)) {
7271
+ if (existsSync41(skillMd)) {
6519
7272
  passes.push(`skills[${i}]: ${p}/SKILL.md exists`);
6520
- } else if (existsSync38(skillDir)) {
7273
+ } else if (existsSync41(skillDir)) {
6521
7274
  warnings.push(`skills[${i}]: directory exists but no SKILL.md inside`);
6522
7275
  } else {
6523
7276
  warnings.push(`skills[${i}]: path "${p}" does not exist`);
@@ -6529,7 +7282,7 @@ var init_plugin4 = __esm(() => {
6529
7282
  passes.push(`mcpServers: "${manifest.mcpServers}"`);
6530
7283
  const mcpRef = String(manifest.mcpServers);
6531
7284
  const mcpPath = resolve23(dir, mcpRef);
6532
- if (existsSync38(mcpPath)) {
7285
+ if (existsSync41(mcpPath)) {
6533
7286
  passes.push(`mcpServers file exists at ${mcpRef}`);
6534
7287
  } else {
6535
7288
  warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
@@ -6589,7 +7342,7 @@ var init_plugin4 = __esm(() => {
6589
7342
  });
6590
7343
 
6591
7344
  // src/validators/copilot/marketplace.ts
6592
- import { existsSync as existsSync39 } from "fs";
7345
+ import { existsSync as existsSync42 } from "fs";
6593
7346
  import { resolve as resolve24 } from "path";
6594
7347
  var copilotMarketplaceValidator;
6595
7348
  var init_marketplace4 = __esm(() => {
@@ -6599,7 +7352,7 @@ var init_marketplace4 = __esm(() => {
6599
7352
  name: "Copilot Plugin Marketplace",
6600
7353
  description: "Validates .github/plugin/marketplace.json (string sources)",
6601
7354
  detect(dir) {
6602
- return existsSync39(resolve24(dir, ".github", "plugin", "marketplace.json"));
7355
+ return existsSync42(resolve24(dir, ".github", "plugin", "marketplace.json"));
6603
7356
  },
6604
7357
  async validate(dir, _opts) {
6605
7358
  const errors = [];
@@ -6649,9 +7402,9 @@ var init_marketplace4 = __esm(() => {
6649
7402
  const src = String(p.source);
6650
7403
  passes.push(`plugins[${i}].source: "${src}"`);
6651
7404
  const srcDir = resolve24(dir, src);
6652
- if (existsSync39(srcDir)) {
6653
- const hasManifest = existsSync39(resolve24(srcDir, ".github", "plugin", "plugin.json"));
6654
- 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"));
6655
7408
  if (hasManifest || hasSkills) {
6656
7409
  passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
6657
7410
  } else {
@@ -6668,12 +7421,12 @@ var init_marketplace4 = __esm(() => {
6668
7421
  if (p.version)
6669
7422
  passes.push(`plugins[${i}].version: "${p.version}"`);
6670
7423
  }
6671
- if (existsSync39(resolve24(dir, "README.md"))) {
7424
+ if (existsSync42(resolve24(dir, "README.md"))) {
6672
7425
  passes.push("README.md exists at marketplace root");
6673
7426
  } else {
6674
7427
  warnings.push("No README.md at marketplace root \u2014 recommended");
6675
7428
  }
6676
- if (existsSync39(resolve24(dir, "LICENSE"))) {
7429
+ if (existsSync42(resolve24(dir, "LICENSE"))) {
6677
7430
  passes.push("LICENSE exists at marketplace root");
6678
7431
  } else {
6679
7432
  warnings.push("No LICENSE at marketplace root \u2014 recommended");
@@ -6684,7 +7437,7 @@ var init_marketplace4 = __esm(() => {
6684
7437
  });
6685
7438
 
6686
7439
  // src/validators/copilot/mcp.ts
6687
- import { existsSync as existsSync40 } from "fs";
7440
+ import { existsSync as existsSync43 } from "fs";
6688
7441
  import { resolve as resolve25 } from "path";
6689
7442
  var copilotMcpValidator;
6690
7443
  var init_mcp4 = __esm(() => {
@@ -6694,7 +7447,7 @@ var init_mcp4 = __esm(() => {
6694
7447
  name: "Copilot MCP Config",
6695
7448
  description: "Validates .mcp.json (referenced via mcpServers in manifest). Supports stdio and http servers.",
6696
7449
  detect(dir) {
6697
- return existsSync40(resolve25(dir, ".mcp.json"));
7450
+ return existsSync43(resolve25(dir, ".mcp.json"));
6698
7451
  },
6699
7452
  async validate(dir, _opts) {
6700
7453
  const errors = [];
@@ -6760,7 +7513,7 @@ var init_mcp4 = __esm(() => {
6760
7513
  });
6761
7514
 
6762
7515
  // src/validators/copilot/skill.ts
6763
- import { existsSync as existsSync41 } from "fs";
7516
+ import { existsSync as existsSync44 } from "fs";
6764
7517
  import { resolve as resolve26 } from "path";
6765
7518
  var copilotSkillValidator;
6766
7519
  var init_skill4 = __esm(() => {
@@ -6771,7 +7524,7 @@ var init_skill4 = __esm(() => {
6771
7524
  name: "Copilot Skill",
6772
7525
  description: "Validates SKILL.md (shared format). Copilot supports skills referenced via array paths in the manifest.",
6773
7526
  detect(dir) {
6774
- return existsSync41(resolve26(dir, "SKILL.md"));
7527
+ return existsSync44(resolve26(dir, "SKILL.md"));
6775
7528
  },
6776
7529
  async validate(dir, _opts) {
6777
7530
  const loaded = await loadSkill(dir);
@@ -6939,7 +7692,7 @@ var init_validators = __esm(() => {
6939
7692
  // src/core/remote.ts
6940
7693
  import { spawnSync as spawnSync4 } from "child_process";
6941
7694
  import { mkdtempSync, rmSync } from "fs";
6942
- import { join as join28 } from "path";
7695
+ import { join as join31 } from "path";
6943
7696
  import { tmpdir } from "os";
6944
7697
  function parseRemoteUrl(input) {
6945
7698
  if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) {
@@ -6976,7 +7729,7 @@ function isGhAvailable() {
6976
7729
  return ghAvailable;
6977
7730
  }
6978
7731
  async function cloneToTemp(parsed) {
6979
- const tmpDir = mkdtempSync(join28(tmpdir(), "dora-"));
7732
+ const tmpDir = mkdtempSync(join31(tmpdir(), "dora-"));
6980
7733
  const cleanup = () => {
6981
7734
  try {
6982
7735
  rmSync(tmpDir, { recursive: true, force: true });
@@ -7024,15 +7777,15 @@ var exports_validate_top = {};
7024
7777
  __export(exports_validate_top, {
7025
7778
  default: () => validate_top_default
7026
7779
  });
7027
- import { existsSync as existsSync43 } from "fs";
7780
+ import { existsSync as existsSync46 } from "fs";
7028
7781
  import { resolve as resolve27 } from "path";
7029
- var import_picocolors17, validate_top_default;
7782
+ var import_picocolors21, validate_top_default;
7030
7783
  var init_validate_top = __esm(() => {
7031
7784
  init_dist();
7032
7785
  init_out();
7033
7786
  init_validators();
7034
7787
  init_remote();
7035
- import_picocolors17 = __toESM(require_picocolors(), 1);
7788
+ import_picocolors21 = __toESM(require_picocolors(), 1);
7036
7789
  validate_top_default = defineCommand({
7037
7790
  meta: {
7038
7791
  name: "validate",
@@ -7072,7 +7825,7 @@ var init_validate_top = __esm(() => {
7072
7825
  let cleanup;
7073
7826
  if (remote) {
7074
7827
  ui.info(`
7075
- Cloning ${import_picocolors17.default.dim(args.path)}...`);
7828
+ Cloning ${import_picocolors21.default.dim(args.path)}...`);
7076
7829
  try {
7077
7830
  const result = await cloneToTemp(remote);
7078
7831
  fullPath = remote.subpath ? resolve27(result.dir, remote.subpath) : result.dir;
@@ -7082,14 +7835,14 @@ var init_validate_top = __esm(() => {
7082
7835
  ui.fail(msg);
7083
7836
  process.exit(1);
7084
7837
  }
7085
- if (!existsSync43(fullPath)) {
7838
+ if (!existsSync46(fullPath)) {
7086
7839
  cleanup();
7087
7840
  ui.fail(`Subdirectory not found in repo: ${remote.subpath}`);
7088
7841
  process.exit(1);
7089
7842
  }
7090
7843
  } else {
7091
7844
  fullPath = resolve27(args.path);
7092
- if (!existsSync43(fullPath)) {
7845
+ if (!existsSync46(fullPath)) {
7093
7846
  ui.fail(`Path not found: ${args.path}
7094
7847
 
7095
7848
  Check that the path is correct and the directory exists.`);
@@ -7120,13 +7873,13 @@ Check that the path is correct and the directory exists.`);
7120
7873
  ` + `Available providers:
7121
7874
  ` + providers.map((p) => {
7122
7875
  const pvs = validators.filter((v) => v.provider === p);
7123
- return ` ${import_picocolors17.default.bold(p)}
7124
- ` + 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(`
7125
7878
  `);
7126
7879
  }).join(`
7127
7880
  `) + `
7128
7881
 
7129
- 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.`);
7130
7883
  process.exit(1);
7131
7884
  }
7132
7885
  const allResults = [];
@@ -7147,7 +7900,7 @@ Use ${import_picocolors17.default.dim("--for <provider>")} or ${import_picocolor
7147
7900
  } else {
7148
7901
  for (const { id, name, result } of allResults) {
7149
7902
  ui.write(`
7150
- ${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})`)}
7151
7904
  `);
7152
7905
  ui.info(` Path: ${args.path}
7153
7906
  `);
@@ -7162,7 +7915,7 @@ Use ${import_picocolors17.default.dim("--for <provider>")} or ${import_picocolor
7162
7915
  }
7163
7916
  if (result.errors.length === 0 && result.warnings.length === 0) {
7164
7917
  ui.write(`
7165
- ${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.")}
7166
7919
  `);
7167
7920
  } else {
7168
7921
  ui.info(`
@@ -7184,16 +7937,16 @@ var exports_init2 = {};
7184
7937
  __export(exports_init2, {
7185
7938
  default: () => init_default2
7186
7939
  });
7187
- import { basename as basename6, join as join29 } from "path";
7940
+ import { basename as basename7, join as join32 } from "path";
7188
7941
  var {spawnSync: spawnSync5 } = globalThis.Bun;
7189
- var import_picocolors18, init_default2;
7942
+ var import_picocolors22, init_default2;
7190
7943
  var init_init2 = __esm(() => {
7191
7944
  init_dist();
7192
7945
  init_out();
7193
7946
  init_journal_config();
7194
7947
  init_journal_remote();
7195
7948
  init_prompt();
7196
- import_picocolors18 = __toESM(require_picocolors(), 1);
7949
+ import_picocolors22 = __toESM(require_picocolors(), 1);
7197
7950
  init_default2 = defineCommand({
7198
7951
  meta: {
7199
7952
  name: "init",
@@ -7220,17 +7973,17 @@ var init_init2 = __esm(() => {
7220
7973
  ui.heading("dora init \u2014 Set up doraval, your journal, and the coding agent dora should use on the fly");
7221
7974
  const ghCheck = ensureGhCli();
7222
7975
  if (!ghCheck.ok) {
7223
- 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.")}
7224
7977
  `);
7225
- 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.
7226
7979
  `);
7227
7980
  ui.info(` Install it:
7228
7981
  `);
7229
- ui.info(` macOS: ${import_picocolors18.default.dim("brew install gh")}`);
7230
- ui.info(` Linux: ${import_picocolors18.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
7231
- 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")}
7232
7985
  `);
7233
- ui.info(` Then authenticate: ${import_picocolors18.default.dim("gh auth login")}
7986
+ ui.info(` Then authenticate: ${import_picocolors22.default.dim("gh auth login")}
7234
7987
  `);
7235
7988
  process.exit(1);
7236
7989
  }
@@ -7243,44 +7996,44 @@ var init_init2 = __esm(() => {
7243
7996
  if (gitOwner) {
7244
7997
  defaultRepo = `${gitOwner}/${gitOwner}.md`;
7245
7998
  if (ghLogin && ghLogin !== gitOwner) {
7246
- 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 + ")")}
7247
8000
  `;
7248
8001
  } else {
7249
- sourceNote = ` ${import_picocolors18.default.dim("(from git remote)")}
8002
+ sourceNote = ` ${import_picocolors22.default.dim("(from git remote)")}
7250
8003
  `;
7251
8004
  }
7252
8005
  } else if (ghLogin) {
7253
8006
  defaultRepo = `${ghLogin}/${ghLogin}.md`;
7254
- sourceNote = ` ${import_picocolors18.default.dim("(from your active gh account)")}
8007
+ sourceNote = ` ${import_picocolors22.default.dim("(from your active gh account)")}
7255
8008
  `;
7256
8009
  } else {
7257
- 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.
7258
8011
  `);
7259
8012
  process.exit(1);
7260
8013
  }
7261
8014
  const existingConfig = await readConfig();
7262
8015
  if (existingConfig?.journal.repo) {
7263
8016
  defaultRepo = existingConfig.journal.repo;
7264
- sourceNote = ` ${import_picocolors18.default.dim("(from your previous journal setup)")}
8017
+ sourceNote = ` ${import_picocolors22.default.dim("(from your previous journal setup)")}
7265
8018
  `;
7266
8019
  }
7267
- ui.info(` Journal repo ${import_picocolors18.default.dim("(owner/name)")}`);
8020
+ ui.info(` Journal repo ${import_picocolors22.default.dim("(owner/name)")}`);
7268
8021
  if (sourceNote)
7269
8022
  ui.write(sourceNote);
7270
8023
  repo = prompt(" >", defaultRepo);
7271
8024
  }
7272
8025
  let project = args.project || process.env.DORAVAL_PROJECT;
7273
8026
  if (!project) {
7274
- const defaultProject = basename6(process.cwd());
8027
+ const defaultProject = basename7(process.cwd());
7275
8028
  project = prompt(" Project name", defaultProject);
7276
8029
  }
7277
8030
  project = sanitizeProjectName(project);
7278
8031
  if (!repoExists(repo)) {
7279
- 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.")}
7280
8033
  `);
7281
8034
  ui.info(` Create it first:
7282
8035
  `);
7283
- 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"`)}
7284
8037
  `);
7285
8038
  process.exit(1);
7286
8039
  }
@@ -7288,16 +8041,16 @@ var init_init2 = __esm(() => {
7288
8041
  const alreadyRegistered = existing?.journal.projects[project];
7289
8042
  const isRefresh = alreadyRegistered && args.refresh;
7290
8043
  if (alreadyRegistered && !isRefresh) {
7291
- 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.")}
7292
8045
  `);
7293
8046
  ui.info(` Repo: ${existing.journal.repo}
7294
8047
  `);
7295
- 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")}).
7296
8049
  `);
7297
8050
  }
7298
8051
  const journalsDir = getJournalsDir();
7299
8052
  const remotePath = `projects/${project}.md`;
7300
- const localPath = join29(journalsDir, `${project}.md`);
8053
+ const localPath = join32(journalsDir, `${project}.md`);
7301
8054
  const effectiveRepo = isRefresh && !args.repo ? existing.journal.repo : repo;
7302
8055
  const config = existing ?? {
7303
8056
  journal: { repo: effectiveRepo, projects: {} }
@@ -7308,9 +8061,9 @@ var init_init2 = __esm(() => {
7308
8061
  local_path: localPath
7309
8062
  };
7310
8063
  ensureDoravalDirs();
7311
- 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("..."))}
7312
8065
  `);
7313
- const globalDest = join29(journalsDir, "global.md");
8066
+ const globalDest = join32(journalsDir, "global.md");
7314
8067
  const refreshGlobalRes = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
7315
8068
  let wroteGlobal;
7316
8069
  if (!refreshGlobalRes.ok) {
@@ -7327,7 +8080,7 @@ var init_init2 = __esm(() => {
7327
8080
  if (wroteGlobal) {
7328
8081
  ui.success("global.md");
7329
8082
  } else {
7330
- 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)")}`);
7331
8084
  await Bun.write(globalDest, `# Global Journal
7332
8085
 
7333
8086
  Cross-project principles.
@@ -7349,7 +8102,7 @@ Cross-project principles.
7349
8102
  if (wroteProject) {
7350
8103
  ui.success(remotePath);
7351
8104
  } else {
7352
- 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)")}`);
7353
8106
  await Bun.write(localPath, `# ${project} Journal
7354
8107
 
7355
8108
  Project-specific decisions.
@@ -7357,13 +8110,13 @@ Project-specific decisions.
7357
8110
  }
7358
8111
  await writeConfig(config);
7359
8112
  ui.write(`
7360
- ${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))}.
7361
8114
  `);
7362
8115
  const existingAgent = (await readConfig())?.agent;
7363
8116
  if (existingAgent?.command) {
7364
- 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)"))}
7365
8118
  `);
7366
- 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)"))}
7367
8120
  `);
7368
8121
  const change = prompt(" Reconfigure / change the coding agent for on-the-fly enrichment? (y/N)", "n");
7369
8122
  if (!/^y/i.test(String(change))) {
@@ -7373,16 +8126,16 @@ Project-specific decisions.
7373
8126
  if (existingAgent)
7374
8127
  cfg.agent = existingAgent;
7375
8128
  await writeConfig(cfg);
7376
- 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"'))}
7377
8130
  `);
7378
8131
  process.exit(0);
7379
8132
  return;
7380
8133
  }
7381
8134
  ui.blank();
7382
8135
  } else {
7383
- 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"))}
7384
8137
  `);
7385
- 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.
7386
8139
  `);
7387
8140
  }
7388
8141
  const common = [
@@ -7401,7 +8154,7 @@ Project-specific decisions.
7401
8154
  }
7402
8155
  }
7403
8156
  let agentCmd = detected || "claude";
7404
- 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))}`);
7405
8158
  agentCmd = prompt(" Agent command (the binary you run for prompts)", agentCmd);
7406
8159
  let template = detected ? common.find((c) => c.name === detected)?.template || '-p "{{prompt}}" --output-format json' : '-p "{{prompt}}" --output-format json';
7407
8160
  ui.info(` Prompt template (use {{prompt}} placeholder):`);
@@ -7413,11 +8166,36 @@ Project-specific decisions.
7413
8166
  };
7414
8167
  await writeConfig(finalConfig);
7415
8168
  ui.write(`
7416
- ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Agent configured.")}
8169
+ ${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.white("Agent configured.")}
7417
8170
  `);
7418
- 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)")}
7419
8175
  `);
7420
- 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"))}.
7421
8199
  `);
7422
8200
  process.exit(0);
7423
8201
  }
@@ -7426,7 +8204,7 @@ Project-specific decisions.
7426
8204
 
7427
8205
  // src/core/update.ts
7428
8206
  import { resolve as resolve28 } from "path";
7429
- import { homedir as homedir3 } from "os";
8207
+ import { homedir as homedir4 } from "os";
7430
8208
  function normalizePath(p) {
7431
8209
  return p.replace(/\\/g, "/").replace(/\/+$/, "");
7432
8210
  }
@@ -7611,7 +8389,7 @@ async function writeMarker(marker) {
7611
8389
  }
7612
8390
  var MARKER_PATH;
7613
8391
  var init_update2 = __esm(() => {
7614
- MARKER_PATH = resolve28(homedir3(), ".doraval", "install.json");
8392
+ MARKER_PATH = resolve28(homedir4(), ".doraval", "install.json");
7615
8393
  });
7616
8394
 
7617
8395
  // src/cli/commands/update.ts
@@ -7620,7 +8398,7 @@ __export(exports_update2, {
7620
8398
  default: () => update_default2
7621
8399
  });
7622
8400
  import { spawnSync as spawnSync6 } from "child_process";
7623
- import { homedir as homedir4 } from "os";
8401
+ import { homedir as homedir5 } from "os";
7624
8402
  import { fileURLToPath } from "url";
7625
8403
  import { realpath, access } from "fs/promises";
7626
8404
  async function confirmUpdate() {
@@ -7666,7 +8444,7 @@ var init_update3 = __esm(() => {
7666
8444
  entrypoint,
7667
8445
  argv: process.argv,
7668
8446
  env: process.env,
7669
- homeDir: homedir4(),
8447
+ homeDir: homedir5(),
7670
8448
  realpath: (p) => realpath(p),
7671
8449
  exists: async (p) => {
7672
8450
  try {
@@ -7757,12 +8535,12 @@ var exports_providers = {};
7757
8535
  __export(exports_providers, {
7758
8536
  default: () => providers_default
7759
8537
  });
7760
- var import_picocolors19, providers_default;
8538
+ var import_picocolors23, providers_default;
7761
8539
  var init_providers2 = __esm(() => {
7762
8540
  init_dist();
7763
8541
  init_out();
7764
8542
  init_spec();
7765
- import_picocolors19 = __toESM(require_picocolors(), 1);
8543
+ import_picocolors23 = __toESM(require_picocolors(), 1);
7766
8544
  providers_default = defineCommand({
7767
8545
  meta: {
7768
8546
  name: "providers",
@@ -7787,7 +8565,7 @@ var init_providers2 = __esm(() => {
7787
8565
  for (const id of supportedProviders) {
7788
8566
  const spec = getProviderSpec(id);
7789
8567
  ui.write(`
7790
- ${import_picocolors19.default.bold(id)} \u2014 ${spec.name}`);
8568
+ ${import_picocolors23.default.bold(id)} \u2014 ${spec.name}`);
7791
8569
  ui.info(` Manifest: ${spec.manifestPath}`);
7792
8570
  ui.info(` Marketplace: ${spec.marketplacePath}`);
7793
8571
  ui.info(` MCP: ${spec.mcpFilename}`);
@@ -7819,6 +8597,8 @@ var init_completion = __esm(() => {
7819
8597
  "skill",
7820
8598
  "journal",
7821
8599
  "ui",
8600
+ "eval",
8601
+ "config",
7822
8602
  "claude",
7823
8603
  "codex",
7824
8604
  "cursor",
@@ -7828,6 +8608,8 @@ var init_completion = __esm(() => {
7828
8608
  subCommands = {
7829
8609
  skill: ["validate", "drift", "judge"],
7830
8610
  journal: ["init", "list", "context", "hook", "update", "add", "sync"],
8611
+ eval: ["history"],
8612
+ config: ["set", "get"],
7831
8613
  hook: ["enable", "disable", "status"],
7832
8614
  claude: ["new", "bump"],
7833
8615
  codex: ["new", "bump"],
@@ -7862,6 +8644,8 @@ _doraval_completions() {
7862
8644
  case "$prev" in
7863
8645
  skill) COMPREPLY=( $(compgen -W "${subCommands.skill.join(" ")}" -- "$cur") ) ;;
7864
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") ) ;;
7865
8649
  hook) COMPREPLY=( $(compgen -W "${subCommands.hook.join(" ")}" -- "$cur") ) ;;
7866
8650
  ui) COMPREPLY=( $(compgen -W "${uiFlags.join(" ")}" -- "$cur") ) ;;
7867
8651
  claude|codex|cursor|copilot) COMPREPLY=( $(compgen -W "${subCommands.claude.join(" ")}" -- "$cur") ) ;;
@@ -7876,7 +8660,7 @@ complete -F _doraval_completions doraval
7876
8660
 
7877
8661
  _doraval() {
7878
8662
  local -a commands sub
7879
- 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)
7880
8664
  _arguments -C \\
7881
8665
  '1: :->cmd' \\
7882
8666
  '*::arg:->args'
@@ -7893,6 +8677,12 @@ _doraval() {
7893
8677
  journal)
7894
8678
  _describe 'subcommand' (init list context hook update add sync)
7895
8679
  ;;
8680
+ eval)
8681
+ _describe 'subcommand' (history)
8682
+ ;;
8683
+ config)
8684
+ _describe 'subcommand' (set get)
8685
+ ;;
7896
8686
  hook)
7897
8687
  _describe 'subcommand' (enable disable status)
7898
8688
  ;;
@@ -7912,10 +8702,12 @@ _doraval "$@"
7912
8702
  } else if (shell === "fish") {
7913
8703
  console.log(`# doraval fish completion
7914
8704
  complete -c doraval -f
7915
- 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'
7916
8706
 
7917
8707
  complete -c doraval -n '__fish_seen_subcommand_from skill' -a 'validate drift judge'
7918
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'
7919
8711
  complete -c doraval -n '__fish_seen_subcommand_from hook' -a 'enable disable status'
7920
8712
  complete -c doraval -n '__fish_seen_subcommand_from ui' -l port -d 'Port'
7921
8713
  complete -c doraval -n '__fish_seen_subcommand_from ui' -l open -d 'Open browser'
@@ -7937,7 +8729,7 @@ complete -c doraval -n '__fish_seen_subcommand_from claude codex cursor copilot'
7937
8729
  // src/cli/index.ts
7938
8730
  init_dist();
7939
8731
  var import__package = __toESM(require_package(), 1);
7940
- var import_picocolors20 = __toESM(require_picocolors(), 1);
8732
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
7941
8733
  var skill = defineCommand({
7942
8734
  meta: {
7943
8735
  name: "skill",
@@ -7976,6 +8768,20 @@ var journal = defineCommand({
7976
8768
  showUsage(journal);
7977
8769
  }
7978
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);
7979
8785
  var claude = defineCommand({
7980
8786
  meta: {
7981
8787
  name: "claude",
@@ -8103,6 +8909,8 @@ var main = defineCommand({
8103
8909
  completion: () => Promise.resolve().then(() => (init_completion(), exports_completion)).then((m) => m.default),
8104
8910
  skill: () => Promise.resolve(skill),
8105
8911
  journal: () => Promise.resolve(journal),
8912
+ eval: () => Promise.resolve(evalCmd),
8913
+ config,
8106
8914
  claude: () => Promise.resolve(claude),
8107
8915
  codex: () => Promise.resolve(codex),
8108
8916
  cursor: () => Promise.resolve(cursor),
@@ -8115,7 +8923,7 @@ var main = defineCommand({
8115
8923
  return;
8116
8924
  if (process.stdout.isTTY) {
8117
8925
  console.error(`
8118
- ` + import_picocolors20.default.blue(doraemonArt) + `
8926
+ ` + import_picocolors24.default.blue(doraemonArt) + `
8119
8927
  `);
8120
8928
  }
8121
8929
  showUsage(main);