@hacksmith/doraval 0.2.45 → 0.2.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -4
- package/bin/doraval.js +1666 -851
- package/bin/ui/index.html +156 -5
- package/package.json +2 -2
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.
|
|
602
|
+
version: "0.2.47",
|
|
603
603
|
author: "Saif",
|
|
604
604
|
repository: {
|
|
605
605
|
type: "git",
|
|
@@ -612,7 +612,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
612
612
|
doraval: "bin/doraval-wrapper.js",
|
|
613
613
|
dora: "bin/doraval-wrapper.js"
|
|
614
614
|
},
|
|
615
|
-
description: "The context engineering toolkit for coding
|
|
615
|
+
description: "The context engineering toolkit for coding agent orchestrators",
|
|
616
616
|
engines: {
|
|
617
617
|
bun: ">=1.2.0",
|
|
618
618
|
node: ">=14.18.0"
|
|
@@ -873,7 +873,8 @@ var init_skill_validate = __esm(() => {
|
|
|
873
873
|
"agent",
|
|
874
874
|
"hooks",
|
|
875
875
|
"paths",
|
|
876
|
-
"shell"
|
|
876
|
+
"shell",
|
|
877
|
+
"expected-eval"
|
|
877
878
|
]);
|
|
878
879
|
SUPPORTING_DIRS = ["references", "scripts", "assets", "examples"];
|
|
879
880
|
OPTIONAL_DIRS = ["references", "scripts", "assets"];
|
|
@@ -1164,85 +1165,452 @@ Check that the path points to a skill directory containing SKILL.md.`);
|
|
|
1164
1165
|
});
|
|
1165
1166
|
});
|
|
1166
1167
|
|
|
1167
|
-
// src/
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
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
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
},
|
|
1187
|
-
for
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
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
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ??
|
|
1577
|
+
return process.env.DORAVAL_HOME ?? join2(homedir2(), ".doraval");
|
|
1222
1578
|
}
|
|
1223
1579
|
function getConfigPath() {
|
|
1224
|
-
return
|
|
1580
|
+
return join2(getDoravalDir(), "config.yml");
|
|
1225
1581
|
}
|
|
1226
1582
|
function getJournalsDir() {
|
|
1227
|
-
return
|
|
1583
|
+
return join2(getDoravalDir(), "journals");
|
|
1228
1584
|
}
|
|
1229
1585
|
function getPendingDir() {
|
|
1230
|
-
return
|
|
1586
|
+
return join2(getDoravalDir(), "pending");
|
|
1231
1587
|
}
|
|
1232
1588
|
function getPendingProjectDir(project) {
|
|
1233
|
-
return
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
1401
|
-
var
|
|
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
|
-
|
|
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
|
-
${
|
|
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(` ${
|
|
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 ${
|
|
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: ${
|
|
1444
|
-
ui.write(` Linux: ${
|
|
1445
|
-
ui.write(` Windows: ${
|
|
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: ${
|
|
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 = ` ${
|
|
2112
|
+
sourceNote = ` ${import_picocolors8.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
|
|
1461
2113
|
`;
|
|
1462
2114
|
} else {
|
|
1463
|
-
sourceNote = ` ${
|
|
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 = ` ${
|
|
2120
|
+
sourceNote = ` ${import_picocolors8.default.dim("(from your active gh account)")}
|
|
1469
2121
|
`;
|
|
1470
2122
|
} else {
|
|
1471
|
-
ui.write(` ${
|
|
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 = ` ${
|
|
2130
|
+
sourceNote = ` ${import_picocolors8.default.dim("(from your previous journal setup)")}
|
|
1479
2131
|
`;
|
|
1480
2132
|
}
|
|
1481
|
-
ui.write(` Journal repo ${
|
|
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 =
|
|
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(` ${
|
|
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(` ${
|
|
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 ${
|
|
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(` ${
|
|
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: ${
|
|
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: ${
|
|
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 ${
|
|
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 =
|
|
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(` ${
|
|
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 =
|
|
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(` ${
|
|
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(` ${
|
|
2201
|
+
ui.write(` ${import_picocolors8.default.green("\u2713")} global.md`);
|
|
1550
2202
|
} else {
|
|
1551
|
-
ui.write(` ${
|
|
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(` ${
|
|
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(` ${
|
|
2223
|
+
ui.write(` ${import_picocolors8.default.green("\u2713")} ${remotePath}`);
|
|
1572
2224
|
} else {
|
|
1573
|
-
ui.write(` ${
|
|
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
|
-
${
|
|
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: ${
|
|
1584
|
-
ui.write(` Journals: ${
|
|
1585
|
-
ui.write(` 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 ${
|
|
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
|
|
1662
|
-
import { join as
|
|
1663
|
-
var
|
|
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
|
-
|
|
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(`${
|
|
2355
|
+
ui.write(`${import_picocolors9.default.yellow("\u26A0")} ${import_picocolors9.default.yellow("No project mapping found.")}
|
|
1704
2356
|
|
|
1705
|
-
` + `Run ${
|
|
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 =
|
|
1711
|
-
const globalFile =
|
|
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 (
|
|
1726
|
-
const files =
|
|
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(
|
|
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
|
-
${
|
|
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(` ${
|
|
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(` ${
|
|
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: ${
|
|
1761
|
-
ui.write(` Local file: ${
|
|
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(` ${
|
|
1764
|
-
` + ` Use ${
|
|
1765
|
-
` + ` They will be staged locally until you run ${
|
|
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: ${
|
|
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 =
|
|
2425
|
+
let pbColor = import_picocolors9.default.green;
|
|
1774
2426
|
if (pb >= 7)
|
|
1775
|
-
pbColor =
|
|
2427
|
+
pbColor = import_picocolors9.default.red;
|
|
1776
2428
|
else if (pb >= 4)
|
|
1777
|
-
pbColor =
|
|
1778
|
-
const tagsStr = (entry.tags || []).join(", ") ||
|
|
1779
|
-
const statusNote = entry.status !== "active" ?
|
|
1780
|
-
const stagedNote = entry._staged ?
|
|
1781
|
-
ui.write(` ${pbColor(String(pb).padStart(2))} ${
|
|
1782
|
-
ui.write(` ${
|
|
1783
|
-
const by = entry.author?.startsWith("agent:") ?
|
|
1784
|
-
ui.write(` ${
|
|
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) +
|
|
1788
|
-
ui.write(` ${
|
|
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(` ${
|
|
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(` ${
|
|
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(` ${
|
|
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
|
|
1825
|
-
import { join as
|
|
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 (
|
|
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 =
|
|
2597
|
+
const action = existsSync7(absTarget) && startIdx !== -1 ? "Updated" : "Added";
|
|
1946
2598
|
ui.write(`
|
|
1947
|
-
${
|
|
2599
|
+
${import_picocolors10.default.green("\u2713")} ${action} journal decisions section in ${import_picocolors10.default.white(target)}`);
|
|
1948
2600
|
if (useReference) {
|
|
1949
|
-
ui.write(` ${
|
|
2601
|
+
ui.write(` ${import_picocolors10.default.dim("Using @import references (full files will be loaded by Claude).")}`);
|
|
1950
2602
|
} else {
|
|
1951
|
-
ui.write(` ${
|
|
2603
|
+
ui.write(` ${import_picocolors10.default.dim("Embedded compact decisions (low noise).")}`);
|
|
1952
2604
|
}
|
|
1953
2605
|
}
|
|
1954
|
-
var
|
|
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
|
-
|
|
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 =
|
|
2033
|
-
if (
|
|
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 =
|
|
2041
|
-
if (
|
|
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
|
|
2079
|
-
import { join as
|
|
2080
|
-
import { homedir as
|
|
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
|
|
2734
|
+
return join7(homedir3(), ".claude", "settings.json");
|
|
2083
2735
|
}
|
|
2084
2736
|
function getLocalHooksPath() {
|
|
2085
|
-
return
|
|
2737
|
+
return join7(process.cwd(), "hooks", "hooks.json");
|
|
2086
2738
|
}
|
|
2087
2739
|
async function readJson(file) {
|
|
2088
|
-
if (!
|
|
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 (!
|
|
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 (!
|
|
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 &&
|
|
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 (
|
|
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
|
|
2287
|
-
import { join as
|
|
2288
|
-
var
|
|
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
|
-
|
|
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(` ${
|
|
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 ${
|
|
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: ${
|
|
2322
|
-
ui.write(` Linux: ${
|
|
2323
|
-
ui.write(` Windows: ${
|
|
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: ${
|
|
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(`${
|
|
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
|
-
${
|
|
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(`${
|
|
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 =
|
|
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(`${
|
|
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(` ${
|
|
3028
|
+
ui.write(` ${import_picocolors11.default.green("\u2713")} global.md`);
|
|
2377
3029
|
} else {
|
|
2378
|
-
ui.write(` ${
|
|
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
|
-
${
|
|
3035
|
+
${import_picocolors11.default.dim(import_picocolors11.default.gray("No projects registered."))}
|
|
2384
3036
|
`);
|
|
2385
3037
|
} else {
|
|
2386
3038
|
ui.write(`
|
|
2387
|
-
${
|
|
2388
|
-
` + ` Run ${
|
|
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 =
|
|
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(`${
|
|
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(` ${
|
|
3062
|
+
ui.write(` ${import_picocolors11.default.green("\u2713")} ${remotePath}`);
|
|
2411
3063
|
} else {
|
|
2412
|
-
ui.write(` ${
|
|
2413
|
-
if (!
|
|
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
|
-
${
|
|
3075
|
+
${import_picocolors11.default.dim(import_picocolors11.default.gray("Local cache refreshed for"))} ${import_picocolors11.default.bold(import_picocolors11.default.white(summary))}.
|
|
2424
3076
|
`);
|
|
2425
3077
|
}
|
|
2426
3078
|
});
|
|
@@ -2458,166 +3110,47 @@ function validateEntry(entry) {
|
|
|
2458
3110
|
errors.push(`status must be one of: ${VALID_STATUSES.join(", ")}`);
|
|
2459
3111
|
}
|
|
2460
3112
|
if (!entry.title || typeof entry.title !== "string" || entry.title.trim() === "") {
|
|
2461
|
-
errors.push("title is required");
|
|
2462
|
-
}
|
|
2463
|
-
return {
|
|
2464
|
-
valid: errors.length === 0,
|
|
2465
|
-
errors,
|
|
2466
|
-
warnings
|
|
2467
|
-
};
|
|
2468
|
-
}
|
|
2469
|
-
var CANONICAL_TAGS, VALID_STATUSES;
|
|
2470
|
-
var init_journal_validate = __esm(() => {
|
|
2471
|
-
CANONICAL_TAGS = [
|
|
2472
|
-
"naming",
|
|
2473
|
-
"cli",
|
|
2474
|
-
"architecture",
|
|
2475
|
-
"testing",
|
|
2476
|
-
"ux",
|
|
2477
|
-
"api",
|
|
2478
|
-
"docs",
|
|
2479
|
-
"notes"
|
|
2480
|
-
];
|
|
2481
|
-
VALID_STATUSES = ["active", "superseded", "retired"];
|
|
2482
|
-
});
|
|
2483
|
-
|
|
2484
|
-
// src/cli/commands/journal/add.ts
|
|
2485
|
-
var exports_add = {};
|
|
2486
|
-
__export(exports_add, {
|
|
2487
|
-
default: () => add_default,
|
|
2488
|
-
buildAgentArgv: () => buildAgentArgv
|
|
2489
|
-
});
|
|
2490
|
-
import { existsSync as existsSync8 } from "fs";
|
|
2491
|
-
import { join as join7 } from "path";
|
|
2492
|
-
var {spawnSync: spawnSync2 } = globalThis.Bun;
|
|
2493
|
-
function buildAgentArgv(template, promptText) {
|
|
2494
|
-
const marker = "__DORA_PROMPT__";
|
|
2495
|
-
const substituted = template.replace("{{prompt}}", marker);
|
|
2496
|
-
const rawParts = substituted.split(/\s+/).filter(Boolean);
|
|
2497
|
-
return rawParts.map((part) => {
|
|
2498
|
-
let cleaned = part;
|
|
2499
|
-
if (cleaned.startsWith('"') && cleaned.endsWith('"'))
|
|
2500
|
-
cleaned = cleaned.slice(1, -1);
|
|
2501
|
-
if (cleaned.startsWith("'") && cleaned.endsWith("'"))
|
|
2502
|
-
cleaned = cleaned.slice(1, -1);
|
|
2503
|
-
if (cleaned === marker) {
|
|
2504
|
-
return promptText;
|
|
2505
|
-
}
|
|
2506
|
-
return cleaned;
|
|
2507
|
-
});
|
|
2508
|
-
}
|
|
2509
|
-
async function invokeConfiguredAgentForEntry(decisionText, agentCfg) {
|
|
2510
|
-
if (!agentCfg || !agentCfg.command)
|
|
2511
|
-
return null;
|
|
2512
|
-
const scaffold = `Raw user capture (a decision, observation, or useful note that just happened): "${decisionText}"
|
|
2513
|
-
|
|
2514
|
-
Turn this into a clean journal entry. Infer the core decision or note even if the input is phrased as a todo or reminder. Be professional and concise.
|
|
2515
|
-
|
|
2516
|
-
**CRITICAL INSTRUCTIONS (follow exactly):**
|
|
2517
|
-
- Output *ONLY* a single valid JSON object. Nothing before it, nothing after it, no markdown fences, no explanations, no extra text.
|
|
2518
|
-
- The JSON must have exactly these keys (use the suggested values as starting point but improve them):
|
|
2519
|
-
{
|
|
2520
|
-
"title": "Short, scannable, professional title (past tense or present perfect, max ~80 chars)",
|
|
2521
|
-
"pushback": 4,
|
|
2522
|
-
"tags": ["cli", "ux"],
|
|
2523
|
-
"rationale": "2-5 sentences explaining context and implications (or the note content).",
|
|
2524
|
-
"author": "agent:claude-code"
|
|
2525
|
-
}
|
|
2526
|
-
|
|
2527
|
-
If you cannot produce exactly this, output the JSON with the best you can and set "author" to "agent:claude-code" anyway.`;
|
|
2528
|
-
const template = agentCfg.prompt_template || '-p "{{prompt}}" --output-format json';
|
|
2529
|
-
const extraArgs = buildAgentArgv(template, scaffold);
|
|
2530
|
-
const shortTemplate = (agentCfg.prompt_template || '-p "{{prompt}}" --output-format json').slice(0, 80);
|
|
2531
|
-
ui.write(` ${import_picocolors9.default.dim(`\u2192 ${agentCfg.command} ${shortTemplate}...`)}`);
|
|
2532
|
-
try {
|
|
2533
|
-
const result = spawnSync2([agentCfg.command, ...extraArgs], {
|
|
2534
|
-
stdout: "pipe",
|
|
2535
|
-
stderr: "pipe"
|
|
2536
|
-
});
|
|
2537
|
-
const stdout = result.stdout.toString().trim();
|
|
2538
|
-
const stderr = result.stderr.toString().trim();
|
|
2539
|
-
if (result.exitCode !== 0) {
|
|
2540
|
-
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Configured agent (${agentCfg.command}) exited with code ${result.exitCode}. Falling back to defaults.`);
|
|
2541
|
-
if (stderr)
|
|
2542
|
-
ui.write(` ${import_picocolors9.default.dim("stderr:")}
|
|
2543
|
-
${stderr.slice(0, 800)}`);
|
|
2544
|
-
if (stdout)
|
|
2545
|
-
ui.write(` ${import_picocolors9.default.dim("stdout:")}
|
|
2546
|
-
${stdout.slice(0, 400)}`);
|
|
2547
|
-
return null;
|
|
2548
|
-
}
|
|
2549
|
-
let candidates = [];
|
|
2550
|
-
let jsonMatch = stdout.match(/\{[\s\S]*\}/);
|
|
2551
|
-
if (jsonMatch) {
|
|
2552
|
-
try {
|
|
2553
|
-
candidates.push(JSON.parse(jsonMatch[0]));
|
|
2554
|
-
} catch {}
|
|
2555
|
-
}
|
|
2556
|
-
const allMatches = stdout.match(/\{[\s\S]*?\}(?=\s*(?:\{|$))/g) || [];
|
|
2557
|
-
for (const m of allMatches) {
|
|
2558
|
-
try {
|
|
2559
|
-
const p = JSON.parse(m);
|
|
2560
|
-
candidates.push(p);
|
|
2561
|
-
} catch {}
|
|
2562
|
-
}
|
|
2563
|
-
let parsed = null;
|
|
2564
|
-
for (const c of candidates) {
|
|
2565
|
-
if (c && typeof c === "object" && (c.title || c.rationale)) {
|
|
2566
|
-
parsed = c;
|
|
2567
|
-
break;
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2570
|
-
if (!parsed) {
|
|
2571
|
-
for (const c of candidates) {
|
|
2572
|
-
if (c && typeof c === "object" && c.result) {
|
|
2573
|
-
let inner = c.result;
|
|
2574
|
-
if (typeof inner === "string") {
|
|
2575
|
-
try {
|
|
2576
|
-
inner = JSON.parse(inner);
|
|
2577
|
-
} catch {}
|
|
2578
|
-
}
|
|
2579
|
-
if (inner && typeof inner === "object" && (inner.title || inner.rationale)) {
|
|
2580
|
-
parsed = inner;
|
|
2581
|
-
break;
|
|
2582
|
-
}
|
|
2583
|
-
}
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
if (!parsed) {
|
|
2587
|
-
parsed = candidates[0] || null;
|
|
2588
|
-
}
|
|
2589
|
-
if (!parsed || typeof parsed !== "object") {
|
|
2590
|
-
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent produced output but no usable JSON was found. Falling back.`);
|
|
2591
|
-
ui.write(` ${import_picocolors9.default.dim("stdout (first 700 chars):")}
|
|
2592
|
-
${stdout.slice(0, 700)}`);
|
|
2593
|
-
if (stderr)
|
|
2594
|
-
ui.write(` ${import_picocolors9.default.dim("stderr:")}
|
|
2595
|
-
${stderr.slice(0, 500)}`);
|
|
2596
|
-
return null;
|
|
2597
|
-
}
|
|
2598
|
-
if (!parsed.title && !parsed.rationale) {
|
|
2599
|
-
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent returned JSON without expected fields (title/rationale). Using defaults.`);
|
|
2600
|
-
ui.write(` ${import_picocolors9.default.dim("parsed keys:")} ${Object.keys(parsed).join(", ")}`);
|
|
2601
|
-
ui.write(` ${import_picocolors9.default.dim("stdout (truncated):")}
|
|
2602
|
-
${stdout.slice(0, 600)}`);
|
|
2603
|
-
return null;
|
|
2604
|
-
}
|
|
2605
|
-
return parsed;
|
|
2606
|
-
} catch (e) {
|
|
2607
|
-
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Failed to invoke configured agent (${agentCfg.command}): ${e.message}. Using defaults.`);
|
|
2608
|
-
return null;
|
|
3113
|
+
errors.push("title is required");
|
|
2609
3114
|
}
|
|
3115
|
+
return {
|
|
3116
|
+
valid: errors.length === 0,
|
|
3117
|
+
errors,
|
|
3118
|
+
warnings
|
|
3119
|
+
};
|
|
2610
3120
|
}
|
|
3121
|
+
var CANONICAL_TAGS, VALID_STATUSES;
|
|
3122
|
+
var init_journal_validate = __esm(() => {
|
|
3123
|
+
CANONICAL_TAGS = [
|
|
3124
|
+
"naming",
|
|
3125
|
+
"cli",
|
|
3126
|
+
"architecture",
|
|
3127
|
+
"testing",
|
|
3128
|
+
"ux",
|
|
3129
|
+
"api",
|
|
3130
|
+
"docs",
|
|
3131
|
+
"notes"
|
|
3132
|
+
];
|
|
3133
|
+
VALID_STATUSES = ["active", "superseded", "retired"];
|
|
3134
|
+
});
|
|
3135
|
+
|
|
3136
|
+
// src/cli/commands/journal/add.ts
|
|
3137
|
+
var exports_add = {};
|
|
3138
|
+
__export(exports_add, {
|
|
3139
|
+
default: () => add_default
|
|
3140
|
+
});
|
|
3141
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3142
|
+
import { join as join9 } from "path";
|
|
2611
3143
|
function slugify(title) {
|
|
2612
3144
|
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
|
|
2613
3145
|
}
|
|
2614
|
-
var
|
|
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
|
-
|
|
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(`${
|
|
3228
|
+
ui.write(`${import_picocolors12.default.yellow("\u26A0")} No project mapping found.
|
|
2696
3229
|
|
|
2697
|
-
` + `Run ${
|
|
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(`${
|
|
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 (
|
|
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(`${
|
|
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(` ${
|
|
2800
|
-
const
|
|
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(`${
|
|
3381
|
+
ui.write(`${import_picocolors12.default.red("\u2717")} Invalid entry:
|
|
2833
3382
|
`);
|
|
2834
3383
|
for (const err of validation.errors) {
|
|
2835
|
-
ui.write(` ${
|
|
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(`${
|
|
3390
|
+
ui.write(`${import_picocolors12.default.dim("\xB7")} ${warn}`);
|
|
2842
3391
|
} else {
|
|
2843
|
-
ui.write(`${
|
|
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 (!
|
|
2864
|
-
await Bun.write(
|
|
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 =
|
|
3417
|
+
const filePath = join9(pendingDir, filename);
|
|
2869
3418
|
await Bun.write(filePath, content);
|
|
2870
3419
|
ui.write(`
|
|
2871
|
-
${
|
|
2872
|
-
ui.write(` Project: ${
|
|
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:") ?
|
|
2876
|
-
ui.write(` Pushback: ${
|
|
2877
|
-
ui.write(` Tags: ${
|
|
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: ${
|
|
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(` ${
|
|
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(` ${
|
|
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
|
|
2902
|
-
import { join as
|
|
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(
|
|
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
|
|
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
|
-
|
|
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(`${
|
|
3525
|
+
ui.write(`${import_picocolors13.default.yellow("\u26A0")} No project mapping found.
|
|
2977
3526
|
|
|
2978
|
-
` + `Run ${
|
|
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(`${
|
|
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(` ${
|
|
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 ${
|
|
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: ${
|
|
2994
|
-
ui.write(` Linux: ${
|
|
2995
|
-
ui.write(` Windows: ${
|
|
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: ${
|
|
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
|
-
${
|
|
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: ${
|
|
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 =
|
|
3011
|
-
ui.write(` ${
|
|
3012
|
-
const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "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(
|
|
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(` ${
|
|
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(
|
|
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(` ${
|
|
3583
|
+
ui.write(` ${import_picocolors13.default.dim(import_picocolors13.default.gray(`\u2713 ${remoteProjectPath}`))}`);
|
|
3035
3584
|
}
|
|
3036
|
-
const pendingFiles =
|
|
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
|
-
${
|
|
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(
|
|
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(` ${
|
|
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(` ${
|
|
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 =
|
|
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
|
-
${
|
|
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(` ${
|
|
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(`${
|
|
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 =
|
|
3648
|
+
const fullPath = join10(pendingDir, file);
|
|
3100
3649
|
try {
|
|
3101
3650
|
await Bun.file(fullPath).unlink();
|
|
3102
3651
|
} catch {}
|
|
3103
3652
|
}
|
|
3104
|
-
ui.write(` ${
|
|
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(` ${
|
|
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(` ${
|
|
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(` ${
|
|
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
|
-
${
|
|
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
|
|
3179
|
-
import { join as
|
|
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 =
|
|
3183
|
-
const hasPluginManifest =
|
|
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 =
|
|
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
|
|
3217
|
-
import { mkdirSync as mkdirSync3, writeFileSync, existsSync as
|
|
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 ===
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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(
|
|
3275
|
-
writeFileSync(
|
|
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(
|
|
4013
|
+
writeFileSync(join13(targetDir, "marketplace.json"), JSON.stringify(marketplaceJson, null, 2));
|
|
3287
4014
|
const demoSkillName = "doraval";
|
|
3288
|
-
mkdirSync3(
|
|
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(
|
|
3313
|
-
const readmePath =
|
|
3314
|
-
if (!
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
${
|
|
3379
|
-
const cmdName = decision.path === "plugin" ? `/${
|
|
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
|
|
3405
|
-
import { existsSync as
|
|
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 =
|
|
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 =
|
|
4210
|
+
entries = readdirSync7(dir);
|
|
3484
4211
|
} catch {
|
|
3485
4212
|
return results;
|
|
3486
4213
|
}
|
|
3487
4214
|
for (const entry of entries) {
|
|
3488
|
-
const full =
|
|
4215
|
+
const full = join14(dir, entry);
|
|
3489
4216
|
let st;
|
|
3490
4217
|
try {
|
|
3491
|
-
st =
|
|
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
|
|
4250
|
+
var import_picocolors16, bump_default;
|
|
3524
4251
|
var init_bump = __esm(() => {
|
|
3525
4252
|
init_dist();
|
|
3526
4253
|
init_out();
|
|
3527
|
-
|
|
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 =
|
|
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 (!
|
|
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}: ${
|
|
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}: ${
|
|
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
|
|
3662
|
-
import { join as
|
|
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 =
|
|
3666
|
-
const hasPluginManifest =
|
|
3667
|
-
const hasMarketplace =
|
|
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 =
|
|
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
|
|
3702
|
-
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, existsSync as
|
|
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 ===
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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(
|
|
3766
|
-
writeFileSync3(
|
|
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(
|
|
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(
|
|
4516
|
+
writeFileSync3(join16(targetDir, codexSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
|
|
3790
4517
|
const demoSkillName = "doraval";
|
|
3791
|
-
mkdirSync4(
|
|
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(
|
|
3823
|
-
const readmePath =
|
|
3824
|
-
if (!
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
${
|
|
3889
|
-
const cmdName = decision.path === "plugin" ? `/${
|
|
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
|
|
3911
|
-
import { join as
|
|
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 =
|
|
3914
|
-
const hasPluginManifest =
|
|
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 =
|
|
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
|
|
3946
|
-
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, existsSync as
|
|
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 ===
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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(
|
|
4006
|
-
writeFileSync4(
|
|
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(
|
|
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(
|
|
4746
|
+
writeFileSync4(join18(targetDir, cursorSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
|
|
4020
4747
|
const demoSkillName = "doraval";
|
|
4021
|
-
mkdirSync5(
|
|
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(
|
|
4051
|
-
const readmePath =
|
|
4052
|
-
if (!
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
${
|
|
4117
|
-
const cmdName = decision.path === "plugin" ? `/${
|
|
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
|
|
4138
|
-
import { join as
|
|
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 =
|
|
4141
|
-
const hasPluginManifest =
|
|
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 =
|
|
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
|
|
4173
|
-
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5, existsSync as
|
|
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 ===
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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(
|
|
4233
|
-
writeFileSync5(
|
|
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(
|
|
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(
|
|
4975
|
+
writeFileSync5(join20(targetDir, copilotSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
|
|
4249
4976
|
const demoSkillName = "doraval";
|
|
4250
|
-
mkdirSync6(
|
|
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(
|
|
4280
|
-
const readmePath =
|
|
4281
|
-
if (!
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
${
|
|
4346
|
-
const cmdName = decision.path === "plugin" ? `/${
|
|
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
|
|
4371
|
-
import { join as
|
|
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 =
|
|
4380
|
-
if (
|
|
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 =
|
|
4389
|
-
if (
|
|
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 &&
|
|
4401
|
-
const files =
|
|
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(
|
|
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 (!
|
|
4420
|
-
await Bun.write(
|
|
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 =
|
|
5152
|
+
const filePath = join21(pendingDir, filename);
|
|
4426
5153
|
const content = `## ${input.title}
|
|
4427
5154
|
|
|
4428
5155
|
\`\`\`yaml
|
|
@@ -4438,6 +5165,25 @@ ${input.rationale}
|
|
|
4438
5165
|
await Bun.write(filePath, content);
|
|
4439
5166
|
return { filePath, filename };
|
|
4440
5167
|
}
|
|
5168
|
+
async function loadEvals(limit = 30) {
|
|
5169
|
+
const dir = getEvalsDir();
|
|
5170
|
+
if (!existsSync22(dir))
|
|
5171
|
+
return [];
|
|
5172
|
+
let files = readdirSync11(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f, path: join21(dir, f) }));
|
|
5173
|
+
files.sort((a, b) => b.name.localeCompare(a.name));
|
|
5174
|
+
const results = [];
|
|
5175
|
+
for (const f of files.slice(0, limit)) {
|
|
5176
|
+
try {
|
|
5177
|
+
const raw = await Bun.file(f.path).text();
|
|
5178
|
+
const parsed = JSON.parse(raw);
|
|
5179
|
+
if (parsed && (parsed.schemaVersion === 1 || parsed.verdict || parsed.skill)) {
|
|
5180
|
+
results.push({ ...parsed, _filename: f.name });
|
|
5181
|
+
}
|
|
5182
|
+
} catch {}
|
|
5183
|
+
}
|
|
5184
|
+
results.sort((a, b) => (b.timestamp || "").localeCompare(a.timestamp || ""));
|
|
5185
|
+
return results.slice(0, limit);
|
|
5186
|
+
}
|
|
4441
5187
|
async function killPort(port) {
|
|
4442
5188
|
if (process.platform === "win32") {
|
|
4443
5189
|
return;
|
|
@@ -4457,12 +5203,12 @@ async function killPort(port) {
|
|
|
4457
5203
|
await new Promise((r) => setTimeout(r, 400));
|
|
4458
5204
|
} catch {}
|
|
4459
5205
|
}
|
|
4460
|
-
function readPid() {
|
|
4461
|
-
const file = getPidFile();
|
|
4462
|
-
if (!
|
|
5206
|
+
function readPid(p) {
|
|
5207
|
+
const file = getPidFile(p);
|
|
5208
|
+
if (!existsSync22(file))
|
|
4463
5209
|
return null;
|
|
4464
5210
|
try {
|
|
4465
|
-
const raw =
|
|
5211
|
+
const raw = readFileSync3(file, "utf8").trim();
|
|
4466
5212
|
const pid = parseInt(raw, 10);
|
|
4467
5213
|
if (isNaN(pid))
|
|
4468
5214
|
return null;
|
|
@@ -4475,14 +5221,14 @@ function readPid() {
|
|
|
4475
5221
|
return null;
|
|
4476
5222
|
}
|
|
4477
5223
|
}
|
|
4478
|
-
function writePid(pid) {
|
|
5224
|
+
function writePid(pid, p) {
|
|
4479
5225
|
ensureDoravalDirs();
|
|
4480
|
-
writeFileSync6(getPidFile(), String(pid) + `
|
|
5226
|
+
writeFileSync6(getPidFile(p), String(pid) + `
|
|
4481
5227
|
`);
|
|
4482
5228
|
}
|
|
4483
|
-
function removePid() {
|
|
5229
|
+
function removePid(p) {
|
|
4484
5230
|
try {
|
|
4485
|
-
unlinkSync2(getPidFile());
|
|
5231
|
+
unlinkSync2(getPidFile(p));
|
|
4486
5232
|
} catch {}
|
|
4487
5233
|
}
|
|
4488
5234
|
async function getDashboardHtml() {
|
|
@@ -4495,13 +5241,13 @@ async function getDashboardHtml() {
|
|
|
4495
5241
|
return `<!doctype html><meta charset="utf-8"><body style="font-family:monospace;background:#111;color:#ddd;padding:2rem"><h1>doraval ui</h1><p>Dashboard HTML missing.</p><pre>${String(err)}</pre></body>`;
|
|
4496
5242
|
}
|
|
4497
5243
|
}
|
|
4498
|
-
var
|
|
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
|
-
|
|
5250
|
+
import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
4505
5251
|
ui_default = {
|
|
4506
5252
|
async run({ args }) {
|
|
4507
5253
|
const port = Number(args.port) || DEFAULT_PORT;
|
|
@@ -4510,12 +5256,12 @@ var init_ui = __esm(() => {
|
|
|
4510
5256
|
const showStatusOnly = !!args.status;
|
|
4511
5257
|
const force = !!args.force;
|
|
4512
5258
|
ensureDoravalDirs();
|
|
4513
|
-
const existingPid = readPid();
|
|
5259
|
+
const existingPid = readPid(port);
|
|
4514
5260
|
if (showStatusOnly) {
|
|
4515
5261
|
if (existingPid) {
|
|
4516
5262
|
const url2 = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`;
|
|
4517
5263
|
console.error(` Dashboard running (pid ${existingPid})`);
|
|
4518
|
-
console.error(` URL: ${
|
|
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: ${
|
|
5273
|
+
console.error(` URL: ${import_picocolors20.default.underline(import_picocolors20.default.cyan(url2))}`);
|
|
4528
5274
|
if (shouldOpen && process.stdout.isTTY) {
|
|
4529
5275
|
try {
|
|
4530
5276
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
@@ -4539,7 +5285,7 @@ var init_ui = __esm(() => {
|
|
|
4539
5285
|
process.kill(existingPid, "SIGTERM");
|
|
4540
5286
|
} catch {}
|
|
4541
5287
|
await new Promise((r) => setTimeout(r, 400));
|
|
4542
|
-
removePid();
|
|
5288
|
+
removePid(port);
|
|
4543
5289
|
} else if (!existingPid) {
|
|
4544
5290
|
await killPort(port);
|
|
4545
5291
|
}
|
|
@@ -4552,124 +5298,138 @@ var init_ui = __esm(() => {
|
|
|
4552
5298
|
project = undefined;
|
|
4553
5299
|
}
|
|
4554
5300
|
}
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
const
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
return Response.json({
|
|
4568
|
-
project: project || null,
|
|
4569
|
-
doravalDir: getJournalsDir(),
|
|
4570
|
-
hasConfig: !!config,
|
|
4571
|
-
repo: config?.journal?.repo ?? null
|
|
4572
|
-
});
|
|
4573
|
-
}
|
|
4574
|
-
if (url2.pathname === "/api/entries") {
|
|
4575
|
-
const { committed, staged } = await loadAllEntries(project || null);
|
|
4576
|
-
return Response.json({ project, committed, staged });
|
|
4577
|
-
}
|
|
4578
|
-
if (url2.pathname === "/api/context") {
|
|
4579
|
-
const { committed, staged } = await loadAllEntries(project || null);
|
|
4580
|
-
const all = [...staged, ...committed].filter((e) => (e.status || "active") === "active");
|
|
4581
|
-
const text = generateJournalContext(all, project || null, { minPushback: 1 });
|
|
4582
|
-
return Response.json({ text, project });
|
|
4583
|
-
}
|
|
4584
|
-
if (url2.pathname === "/api/hooks/status" && req.method === "GET") {
|
|
4585
|
-
const localPath = getLocalHooksPath();
|
|
4586
|
-
const globalPath = getGlobalSettingsPath();
|
|
4587
|
-
const localHas = hasHook(await readJson(localPath));
|
|
4588
|
-
const globalHas = hasHook(await readJson(globalPath));
|
|
4589
|
-
return Response.json({
|
|
4590
|
-
local: { enabled: localHas, path: localPath },
|
|
4591
|
-
global: { enabled: globalHas, path: globalPath }
|
|
4592
|
-
});
|
|
4593
|
-
}
|
|
4594
|
-
if (url2.pathname === "/api/hooks/enable" && req.method === "POST") {
|
|
4595
|
-
const body = await req.json().catch(() => ({}));
|
|
4596
|
-
const useGlobal = !!body.global;
|
|
4597
|
-
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
4598
|
-
const res = await addHook(target);
|
|
4599
|
-
return Response.json(res);
|
|
4600
|
-
}
|
|
4601
|
-
if (url2.pathname === "/api/hooks/disable" && req.method === "POST") {
|
|
4602
|
-
const body = await req.json().catch(() => ({}));
|
|
4603
|
-
const useGlobal = !!body.global;
|
|
4604
|
-
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
4605
|
-
const res = await removeHook(target);
|
|
4606
|
-
return Response.json(res);
|
|
4607
|
-
}
|
|
4608
|
-
if (url2.pathname === "/api/add" && req.method === "POST") {
|
|
4609
|
-
if (!project) {
|
|
4610
|
-
return Response.json({ error: "No project configured. Run dora init or dora journal init first." }, { status: 400 });
|
|
5301
|
+
let server;
|
|
5302
|
+
try {
|
|
5303
|
+
server = Bun.serve({
|
|
5304
|
+
port,
|
|
5305
|
+
hostname: host,
|
|
5306
|
+
async fetch(req) {
|
|
5307
|
+
const url2 = new URL(req.url);
|
|
5308
|
+
if (url2.pathname === "/" || url2.pathname === "/index.html") {
|
|
5309
|
+
const html = await getDashboardHtml();
|
|
5310
|
+
return new Response(html, {
|
|
5311
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
5312
|
+
});
|
|
4611
5313
|
}
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
} catch (e) {
|
|
4621
|
-
return Response.json({ error: e.message }, { status: 500 });
|
|
5314
|
+
if (url2.pathname === "/api/status") {
|
|
5315
|
+
return Response.json({
|
|
5316
|
+
project: project || null,
|
|
5317
|
+
doravalRoot: getDoravalDir(),
|
|
5318
|
+
doravalDir: getJournalsDir(),
|
|
5319
|
+
hasConfig: !!config,
|
|
5320
|
+
repo: config?.journal?.repo ?? null
|
|
5321
|
+
});
|
|
4622
5322
|
}
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
5323
|
+
if (url2.pathname === "/api/entries") {
|
|
5324
|
+
const { committed, staged } = await loadAllEntries(project || null);
|
|
5325
|
+
return Response.json({ project, committed, staged });
|
|
5326
|
+
}
|
|
5327
|
+
if (url2.pathname === "/api/context") {
|
|
5328
|
+
const { committed, staged } = await loadAllEntries(project || null);
|
|
5329
|
+
const all = [...staged, ...committed].filter((e) => (e.status || "active") === "active");
|
|
5330
|
+
const text = generateJournalContext(all, project || null, { minPushback: 1 });
|
|
5331
|
+
return Response.json({ text, project });
|
|
5332
|
+
}
|
|
5333
|
+
if (url2.pathname === "/api/hooks/status" && req.method === "GET") {
|
|
5334
|
+
const localPath = getLocalHooksPath();
|
|
5335
|
+
const globalPath = getGlobalSettingsPath();
|
|
5336
|
+
const localHas = hasHook(await readJson(localPath));
|
|
5337
|
+
const globalHas = hasHook(await readJson(globalPath));
|
|
5338
|
+
return Response.json({
|
|
5339
|
+
local: { enabled: localHas, path: localPath },
|
|
5340
|
+
global: { enabled: globalHas, path: globalPath }
|
|
5341
|
+
});
|
|
5342
|
+
}
|
|
5343
|
+
if (url2.pathname === "/api/hooks/enable" && req.method === "POST") {
|
|
5344
|
+
const body = await req.json().catch(() => ({}));
|
|
5345
|
+
const useGlobal = !!body.global;
|
|
5346
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
5347
|
+
const res = await addHook(target);
|
|
5348
|
+
return Response.json(res);
|
|
4631
5349
|
}
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
5350
|
+
if (url2.pathname === "/api/hooks/disable" && req.method === "POST") {
|
|
5351
|
+
const body = await req.json().catch(() => ({}));
|
|
5352
|
+
const useGlobal = !!body.global;
|
|
5353
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
5354
|
+
const res = await removeHook(target);
|
|
5355
|
+
return Response.json(res);
|
|
4636
5356
|
}
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
5357
|
+
if (url2.pathname === "/api/add" && req.method === "POST") {
|
|
5358
|
+
if (!project) {
|
|
5359
|
+
return Response.json({ error: "No project configured. Run dora init or dora journal init first." }, { status: 400 });
|
|
5360
|
+
}
|
|
5361
|
+
const body = await req.json();
|
|
5362
|
+
const title = String(body.title || "Untitled decision").trim();
|
|
5363
|
+
const pushback = Number(body.pushback ?? 4);
|
|
5364
|
+
const tags = Array.isArray(body.tags) ? body.tags.map((t) => String(t).trim()).filter(Boolean) : [];
|
|
5365
|
+
const rationale = String(body.rationale || title).trim();
|
|
4640
5366
|
try {
|
|
4641
|
-
await
|
|
4642
|
-
|
|
4643
|
-
|
|
5367
|
+
const result = await writePendingEntry(project, { title, pushback, tags, rationale });
|
|
5368
|
+
return Response.json({ ok: true, ...result });
|
|
5369
|
+
} catch (e) {
|
|
5370
|
+
return Response.json({ error: e.message }, { status: 500 });
|
|
5371
|
+
}
|
|
4644
5372
|
}
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
5373
|
+
if (url2.pathname === "/api/refresh" && req.method === "POST") {
|
|
5374
|
+
const { committed, staged } = await loadAllEntries(project || null);
|
|
5375
|
+
return Response.json({ ok: true, committed, staged });
|
|
5376
|
+
}
|
|
5377
|
+
if (url2.pathname === "/api/delete-staged" && req.method === "POST") {
|
|
5378
|
+
if (!project) {
|
|
5379
|
+
return Response.json({ error: "No project" }, { status: 400 });
|
|
5380
|
+
}
|
|
5381
|
+
const body = await req.json().catch(() => ({}));
|
|
5382
|
+
const filename = body.filename;
|
|
5383
|
+
if (!filename) {
|
|
5384
|
+
return Response.json({ error: "filename required" }, { status: 400 });
|
|
5385
|
+
}
|
|
5386
|
+
const pdir = getPendingProjectDir(project);
|
|
5387
|
+
const filePath = join21(pdir, filename);
|
|
5388
|
+
if (existsSync22(filePath)) {
|
|
5389
|
+
try {
|
|
5390
|
+
await Bun.file(filePath).unlink();
|
|
5391
|
+
} catch {}
|
|
5392
|
+
return Response.json({ ok: true });
|
|
5393
|
+
}
|
|
5394
|
+
return Response.json({ error: "not found" }, { status: 404 });
|
|
5395
|
+
}
|
|
5396
|
+
if (url2.pathname === "/api/evals") {
|
|
5397
|
+
const evals = await loadEvals(25);
|
|
5398
|
+
return Response.json({ evals });
|
|
5399
|
+
}
|
|
5400
|
+
if (url2.pathname.startsWith("/api/")) {
|
|
5401
|
+
return Response.json({ error: "Not found" }, { status: 404 });
|
|
5402
|
+
}
|
|
5403
|
+
return new Response("Not found", { status: 404 });
|
|
4649
5404
|
}
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
5405
|
+
});
|
|
5406
|
+
} catch (err) {
|
|
5407
|
+
removePid(port);
|
|
5408
|
+
console.error(` Failed to start dashboard on port ${port}: ${err?.message || err}`);
|
|
5409
|
+
process.exit(1);
|
|
5410
|
+
}
|
|
4653
5411
|
const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${server.port}`;
|
|
4654
|
-
writePid(process.pid);
|
|
5412
|
+
writePid(process.pid, port);
|
|
4655
5413
|
const msg = `
|
|
4656
|
-
${
|
|
4657
|
-
${
|
|
4658
|
-
${
|
|
5414
|
+
${import_picocolors20.default.blue("\u25C9")} dora local dashboard
|
|
5415
|
+
${import_picocolors20.default.dim("Project:")} ${project ? import_picocolors20.default.white(project) : import_picocolors20.default.yellow("none (run dora init)")}
|
|
5416
|
+
${import_picocolors20.default.dim("Data dir:")} ${getDoravalDir()}
|
|
5417
|
+
${import_picocolors20.default.dim("URL:")} ${import_picocolors20.default.underline(import_picocolors20.default.cyan(url))}
|
|
4659
5418
|
|
|
4660
|
-
${
|
|
5419
|
+
${import_picocolors20.default.dim("Press Ctrl+C to stop")}
|
|
4661
5420
|
`;
|
|
4662
5421
|
console.error(msg);
|
|
5422
|
+
console.error(` ${import_picocolors20.default.dim("Tip:")} data location = ${getDoravalDir()} (set DORAVAL_HOME to change)`);
|
|
4663
5423
|
if (shouldOpen && process.stdout.isTTY) {
|
|
4664
5424
|
try {
|
|
4665
5425
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
4666
5426
|
spawn(opener, [url], { stdio: "ignore", detached: true }).unref();
|
|
4667
5427
|
} catch {
|
|
4668
|
-
console.error(
|
|
5428
|
+
console.error(import_picocolors20.default.dim(` Could not auto-open. Visit ${url}`));
|
|
4669
5429
|
}
|
|
4670
5430
|
}
|
|
4671
5431
|
const cleanup = () => {
|
|
4672
|
-
removePid();
|
|
5432
|
+
removePid(port);
|
|
4673
5433
|
console.error(`
|
|
4674
5434
|
Stopping dashboard...`);
|
|
4675
5435
|
server.stop();
|
|
@@ -4682,7 +5442,7 @@ var init_ui = __esm(() => {
|
|
|
4682
5442
|
});
|
|
4683
5443
|
|
|
4684
5444
|
// src/validators/claude/skill.ts
|
|
4685
|
-
import { existsSync as
|
|
5445
|
+
import { existsSync as existsSync23 } from "fs";
|
|
4686
5446
|
import { resolve as resolve5 } from "path";
|
|
4687
5447
|
var claudeSkillValidator;
|
|
4688
5448
|
var init_skill = __esm(() => {
|
|
@@ -4693,7 +5453,7 @@ var init_skill = __esm(() => {
|
|
|
4693
5453
|
name: "Claude Skill",
|
|
4694
5454
|
description: "Validates SKILL.md per current Claude Code spec: frontmatter (name/description relaxed to recommended; directory name usually provides the /command), body, supporting files, dynamic injection (!`cmd`), substitutions ($ARGUMENTS, ${CLAUDE_*}), and advanced fields (allowed-tools, context, disable-model-invocation, when_to_use, etc.)",
|
|
4695
5455
|
detect(dir) {
|
|
4696
|
-
return
|
|
5456
|
+
return existsSync23(resolve5(dir, "SKILL.md"));
|
|
4697
5457
|
},
|
|
4698
5458
|
async validate(dir, _opts) {
|
|
4699
5459
|
const loaded = await loadSkill(dir);
|
|
@@ -4711,8 +5471,8 @@ var init_skill = __esm(() => {
|
|
|
4711
5471
|
});
|
|
4712
5472
|
|
|
4713
5473
|
// src/validators/claude/plugin.ts
|
|
4714
|
-
import { existsSync as
|
|
4715
|
-
import { resolve as resolve6, join as
|
|
5474
|
+
import { existsSync as existsSync24, readdirSync as readdirSync12 } from "fs";
|
|
5475
|
+
import { resolve as resolve6, join as join22 } from "path";
|
|
4716
5476
|
function levenshtein(a, b) {
|
|
4717
5477
|
if (a === b)
|
|
4718
5478
|
return 0;
|
|
@@ -4804,7 +5564,7 @@ var init_plugin = __esm(() => {
|
|
|
4804
5564
|
name: "Claude Plugin",
|
|
4805
5565
|
description: "Validates .claude-plugin/plugin.json manifest (complete schema per Plugins reference), component path rules (replace vs augment), .claude-plugin/ purity, default dirs, single-root-skill layout, unrecognized fields + suggestions, and structure",
|
|
4806
5566
|
detect(dir) {
|
|
4807
|
-
return
|
|
5567
|
+
return existsSync24(resolve6(dir, ".claude-plugin", "plugin.json"));
|
|
4808
5568
|
},
|
|
4809
5569
|
async validate(dir, _opts) {
|
|
4810
5570
|
const errors = [];
|
|
@@ -4818,7 +5578,7 @@ var init_plugin = __esm(() => {
|
|
|
4818
5578
|
manifest = JSON.parse(raw);
|
|
4819
5579
|
passes.push(".claude-plugin/plugin.json is valid JSON");
|
|
4820
5580
|
} catch (err) {
|
|
4821
|
-
if (!
|
|
5581
|
+
if (!existsSync24(manifestPath)) {
|
|
4822
5582
|
errors.push(`.claude-plugin/plugin.json is missing (looked for ${manifestPath})`);
|
|
4823
5583
|
warnings.push("Hint: Run `doraval claude new` (or `dora claude new`) to scaffold a new Claude plugin in this directory.");
|
|
4824
5584
|
} else {
|
|
@@ -4827,7 +5587,7 @@ var init_plugin = __esm(() => {
|
|
|
4827
5587
|
return { errors, warnings, passes };
|
|
4828
5588
|
}
|
|
4829
5589
|
try {
|
|
4830
|
-
const entries =
|
|
5590
|
+
const entries = readdirSync12(dotClaudePluginDir);
|
|
4831
5591
|
const unexpected = entries.filter((e) => e !== "plugin.json");
|
|
4832
5592
|
if (unexpected.length > 0) {
|
|
4833
5593
|
for (const e of unexpected) {
|
|
@@ -4914,7 +5674,7 @@ var init_plugin = __esm(() => {
|
|
|
4914
5674
|
errors.push(`${field}: path "${s}" must start with "./"`);
|
|
4915
5675
|
} else if (s.includes("..")) {
|
|
4916
5676
|
errors.push(`${field}: path "${s}" must not use ".." (paths are confined to the plugin tree after cache copy)`);
|
|
4917
|
-
} else if (
|
|
5677
|
+
} else if (existsSync24(resolve6(dir, s))) {
|
|
4918
5678
|
passes.push(`${field}: path "${s}" exists`);
|
|
4919
5679
|
} else {
|
|
4920
5680
|
warnings.push(`${field}: path "${s}" does not exist on disk`);
|
|
@@ -4964,11 +5724,11 @@ var init_plugin = __esm(() => {
|
|
|
4964
5724
|
passes.push(`dependencies: declares ${manifest.dependencies.length} plugin dependency/ies`);
|
|
4965
5725
|
}
|
|
4966
5726
|
const skillsDir = resolve6(dir, "skills");
|
|
4967
|
-
if (
|
|
4968
|
-
const entries =
|
|
5727
|
+
if (existsSync24(skillsDir)) {
|
|
5728
|
+
const entries = readdirSync12(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
4969
5729
|
for (const e of entries) {
|
|
4970
|
-
const md =
|
|
4971
|
-
if (
|
|
5730
|
+
const md = join22(skillsDir, e.name, "SKILL.md");
|
|
5731
|
+
if (existsSync24(md)) {
|
|
4972
5732
|
passes.push(`skills/${e.name}/SKILL.md exists`);
|
|
4973
5733
|
} else {
|
|
4974
5734
|
errors.push(`skills/${e.name}/ is missing SKILL.md`);
|
|
@@ -4979,8 +5739,8 @@ var init_plugin = __esm(() => {
|
|
|
4979
5739
|
}
|
|
4980
5740
|
}
|
|
4981
5741
|
const commandsDir = resolve6(dir, "commands");
|
|
4982
|
-
if (
|
|
4983
|
-
const mds =
|
|
5742
|
+
if (existsSync24(commandsDir)) {
|
|
5743
|
+
const mds = readdirSync12(commandsDir).filter((f) => f.endsWith(".md"));
|
|
4984
5744
|
if (mds.length) {
|
|
4985
5745
|
passes.push(`commands/ has ${mds.length} .md file(s)`);
|
|
4986
5746
|
}
|
|
@@ -4989,8 +5749,8 @@ var init_plugin = __esm(() => {
|
|
|
4989
5749
|
}
|
|
4990
5750
|
}
|
|
4991
5751
|
const agentsDir = resolve6(dir, "agents");
|
|
4992
|
-
if (
|
|
4993
|
-
const mds =
|
|
5752
|
+
if (existsSync24(agentsDir)) {
|
|
5753
|
+
const mds = readdirSync12(agentsDir).filter((f) => f.endsWith(".md"));
|
|
4994
5754
|
if (mds.length) {
|
|
4995
5755
|
passes.push(`agents/ has ${mds.length} .md file(s)`);
|
|
4996
5756
|
}
|
|
@@ -4998,30 +5758,30 @@ var init_plugin = __esm(() => {
|
|
|
4998
5758
|
warnings.push('agents/ co-exists with manifest "agents" \u2014 manifest replaces default (dir ignored)');
|
|
4999
5759
|
}
|
|
5000
5760
|
}
|
|
5001
|
-
if (
|
|
5761
|
+
if (existsSync24(resolve6(dir, "output-styles"))) {
|
|
5002
5762
|
passes.push("output-styles/ directory present");
|
|
5003
5763
|
if (manifest.outputStyles)
|
|
5004
5764
|
warnings.push("output-styles/ co-exists with manifest outputStyles \u2014 manifest wins");
|
|
5005
5765
|
}
|
|
5006
|
-
if (
|
|
5766
|
+
if (existsSync24(resolve6(dir, "themes")))
|
|
5007
5767
|
passes.push("themes/ present (experimental)");
|
|
5008
|
-
if (
|
|
5768
|
+
if (existsSync24(resolve6(dir, "monitors")) || manifest.experimental?.monitors) {
|
|
5009
5769
|
passes.push("monitors config present (experimental)");
|
|
5010
5770
|
}
|
|
5011
|
-
if (
|
|
5771
|
+
if (existsSync24(resolve6(dir, "bin")))
|
|
5012
5772
|
passes.push("bin/ present (adds executables to Bash tool $PATH)");
|
|
5013
|
-
if (
|
|
5773
|
+
if (existsSync24(resolve6(dir, "settings.json")))
|
|
5014
5774
|
passes.push("settings.json present (plugin defaults for agent/statusline)");
|
|
5015
|
-
if (
|
|
5775
|
+
if (existsSync24(resolve6(dir, "README.md")))
|
|
5016
5776
|
passes.push("README.md present");
|
|
5017
|
-
if (
|
|
5777
|
+
if (existsSync24(resolve6(dir, ".mcp.json")))
|
|
5018
5778
|
passes.push(".mcp.json present (validated by claude:mcp)");
|
|
5019
|
-
if (
|
|
5779
|
+
if (existsSync24(resolve6(dir, ".lsp.json")))
|
|
5020
5780
|
passes.push(".lsp.json present (validated by claude:lsp when registered)");
|
|
5021
|
-
if (
|
|
5781
|
+
if (existsSync24(resolve6(dir, "hooks/hooks.json")) || existsSync24(resolve6(dir, "hooks.json"))) {
|
|
5022
5782
|
passes.push("hooks config present (validated by claude:hooks)");
|
|
5023
5783
|
}
|
|
5024
|
-
if (
|
|
5784
|
+
if (existsSync24(resolve6(dir, "SKILL.md")) && !existsSync24(skillsDir) && manifest.skills === undefined) {
|
|
5025
5785
|
passes.push('Root SKILL.md detected \u2014 plugin will be treated as a single-skill plugin (prefer frontmatter "name" for stable /command)');
|
|
5026
5786
|
}
|
|
5027
5787
|
return { errors, warnings, passes };
|
|
@@ -5030,8 +5790,8 @@ var init_plugin = __esm(() => {
|
|
|
5030
5790
|
});
|
|
5031
5791
|
|
|
5032
5792
|
// src/validators/claude/marketplace.ts
|
|
5033
|
-
import { existsSync as
|
|
5034
|
-
import { resolve as resolve7, join as
|
|
5793
|
+
import { existsSync as existsSync25, readdirSync as readdirSync13 } from "fs";
|
|
5794
|
+
import { resolve as resolve7, join as join23 } from "path";
|
|
5035
5795
|
var claudeMarketplaceValidator;
|
|
5036
5796
|
var init_marketplace = __esm(() => {
|
|
5037
5797
|
claudeMarketplaceValidator = {
|
|
@@ -5040,18 +5800,18 @@ var init_marketplace = __esm(() => {
|
|
|
5040
5800
|
name: "Claude Plugin Marketplace",
|
|
5041
5801
|
description: "Validates .claude-plugin/marketplace.json or plugins/ marketplace layouts (plugins array with sources)",
|
|
5042
5802
|
detect(dir) {
|
|
5043
|
-
if (
|
|
5803
|
+
if (existsSync25(resolve7(dir, ".claude-plugin", "marketplace.json")))
|
|
5044
5804
|
return true;
|
|
5045
5805
|
const pluginsDir = resolve7(dir, "plugins");
|
|
5046
|
-
if (!
|
|
5806
|
+
if (!existsSync25(pluginsDir))
|
|
5047
5807
|
return false;
|
|
5048
5808
|
try {
|
|
5049
|
-
const entries =
|
|
5809
|
+
const entries = readdirSync13(pluginsDir, { withFileTypes: true });
|
|
5050
5810
|
for (const entry of entries) {
|
|
5051
5811
|
if (!entry.isDirectory())
|
|
5052
5812
|
continue;
|
|
5053
|
-
const hasSkills =
|
|
5054
|
-
const hasManifest =
|
|
5813
|
+
const hasSkills = existsSync25(join23(pluginsDir, entry.name, "skills"));
|
|
5814
|
+
const hasManifest = existsSync25(join23(pluginsDir, entry.name, ".claude-plugin", "plugin.json"));
|
|
5055
5815
|
if (hasSkills || hasManifest)
|
|
5056
5816
|
return true;
|
|
5057
5817
|
}
|
|
@@ -5063,9 +5823,9 @@ var init_marketplace = __esm(() => {
|
|
|
5063
5823
|
const warnings = [];
|
|
5064
5824
|
const passes = [];
|
|
5065
5825
|
const claudeMktPath = resolve7(dir, ".claude-plugin", "marketplace.json");
|
|
5066
|
-
const hasClaudeMkt =
|
|
5826
|
+
const hasClaudeMkt = existsSync25(claudeMktPath);
|
|
5067
5827
|
const pluginsDir = resolve7(dir, "plugins");
|
|
5068
|
-
const hasPluginsDirLayout =
|
|
5828
|
+
const hasPluginsDirLayout = existsSync25(pluginsDir);
|
|
5069
5829
|
if (!hasClaudeMkt && !hasPluginsDirLayout) {
|
|
5070
5830
|
errors.push("Missing .claude-plugin/marketplace.json or plugins/ directory");
|
|
5071
5831
|
return { errors, warnings, passes };
|
|
@@ -5110,9 +5870,9 @@ var init_marketplace = __esm(() => {
|
|
|
5110
5870
|
const src = String(p.source);
|
|
5111
5871
|
passes.push(`plugins[${i}].source: "${src}"`);
|
|
5112
5872
|
const srcDir = resolve7(dir, src);
|
|
5113
|
-
if (
|
|
5114
|
-
const hasManifest =
|
|
5115
|
-
const hasSkills =
|
|
5873
|
+
if (existsSync25(srcDir)) {
|
|
5874
|
+
const hasManifest = existsSync25(resolve7(srcDir, ".claude-plugin", "plugin.json"));
|
|
5875
|
+
const hasSkills = existsSync25(resolve7(srcDir, "skills"));
|
|
5116
5876
|
if (hasManifest || hasSkills) {
|
|
5117
5877
|
passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
|
|
5118
5878
|
} else {
|
|
@@ -5128,12 +5888,12 @@ var init_marketplace = __esm(() => {
|
|
|
5128
5888
|
passes.push(`plugins[${i}].category: "${p.category}"`);
|
|
5129
5889
|
}
|
|
5130
5890
|
}
|
|
5131
|
-
if (
|
|
5891
|
+
if (existsSync25(resolve7(dir, "README.md"))) {
|
|
5132
5892
|
passes.push("README.md exists at marketplace root");
|
|
5133
5893
|
} else {
|
|
5134
5894
|
warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
|
|
5135
5895
|
}
|
|
5136
|
-
if (
|
|
5896
|
+
if (existsSync25(resolve7(dir, "LICENSE"))) {
|
|
5137
5897
|
passes.push("LICENSE exists at marketplace root");
|
|
5138
5898
|
} else {
|
|
5139
5899
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -5142,27 +5902,27 @@ var init_marketplace = __esm(() => {
|
|
|
5142
5902
|
}
|
|
5143
5903
|
if (hasPluginsDirLayout) {
|
|
5144
5904
|
passes.push("plugins/ directory exists");
|
|
5145
|
-
const pluginEntries =
|
|
5905
|
+
const pluginEntries = readdirSync13(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5146
5906
|
if (pluginEntries.length === 0) {
|
|
5147
5907
|
errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
|
|
5148
5908
|
return { errors, warnings, passes };
|
|
5149
5909
|
}
|
|
5150
5910
|
passes.push(`${pluginEntries.length} plugin(s) found`);
|
|
5151
|
-
if (
|
|
5911
|
+
if (existsSync25(resolve7(dir, "README.md"))) {
|
|
5152
5912
|
passes.push("README.md exists at marketplace root");
|
|
5153
5913
|
} else {
|
|
5154
5914
|
warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
|
|
5155
5915
|
}
|
|
5156
|
-
if (
|
|
5916
|
+
if (existsSync25(resolve7(dir, "LICENSE"))) {
|
|
5157
5917
|
passes.push("LICENSE exists at marketplace root");
|
|
5158
5918
|
} else {
|
|
5159
5919
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
5160
5920
|
}
|
|
5161
5921
|
for (const plugin of pluginEntries) {
|
|
5162
|
-
const pluginPath =
|
|
5163
|
-
const hasSkills =
|
|
5164
|
-
const hasManifest =
|
|
5165
|
-
const hasReadme =
|
|
5922
|
+
const pluginPath = join23(pluginsDir, plugin.name);
|
|
5923
|
+
const hasSkills = existsSync25(join23(pluginPath, "skills"));
|
|
5924
|
+
const hasManifest = existsSync25(join23(pluginPath, ".claude-plugin", "plugin.json"));
|
|
5925
|
+
const hasReadme = existsSync25(join23(pluginPath, "README.md"));
|
|
5166
5926
|
if (hasManifest || hasSkills) {
|
|
5167
5927
|
passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
|
|
5168
5928
|
} else {
|
|
@@ -5180,7 +5940,7 @@ var init_marketplace = __esm(() => {
|
|
|
5180
5940
|
});
|
|
5181
5941
|
|
|
5182
5942
|
// src/validators/claude/hooks.ts
|
|
5183
|
-
import { existsSync as
|
|
5943
|
+
import { existsSync as existsSync26 } from "fs";
|
|
5184
5944
|
import { resolve as resolve8 } from "path";
|
|
5185
5945
|
var KNOWN_EVENTS, claudeHooksValidator;
|
|
5186
5946
|
var init_hooks = __esm(() => {
|
|
@@ -5222,13 +5982,13 @@ var init_hooks = __esm(() => {
|
|
|
5222
5982
|
name: "Claude Hooks",
|
|
5223
5983
|
description: "Validates hooks/hooks.json (or root hooks.json): all lifecycle events per Plugins reference, hook group structure (matcher + hooks[]), supported hook types (command, http, mcp_tool, prompt, agent)",
|
|
5224
5984
|
detect(dir) {
|
|
5225
|
-
return
|
|
5985
|
+
return existsSync26(resolve8(dir, "hooks", "hooks.json")) || existsSync26(resolve8(dir, "hooks.json"));
|
|
5226
5986
|
},
|
|
5227
5987
|
async validate(dir, _opts) {
|
|
5228
5988
|
const errors = [];
|
|
5229
5989
|
const warnings = [];
|
|
5230
5990
|
const passes = [];
|
|
5231
|
-
const hooksPath =
|
|
5991
|
+
const hooksPath = existsSync26(resolve8(dir, "hooks", "hooks.json")) ? resolve8(dir, "hooks", "hooks.json") : resolve8(dir, "hooks.json");
|
|
5232
5992
|
let config;
|
|
5233
5993
|
try {
|
|
5234
5994
|
const raw = await Bun.file(hooksPath).text();
|
|
@@ -5294,7 +6054,7 @@ var init_hooks = __esm(() => {
|
|
|
5294
6054
|
});
|
|
5295
6055
|
|
|
5296
6056
|
// src/validators/claude/mcp.ts
|
|
5297
|
-
import { existsSync as
|
|
6057
|
+
import { existsSync as existsSync27 } from "fs";
|
|
5298
6058
|
import { resolve as resolve9 } from "path";
|
|
5299
6059
|
var claudeMcpValidator;
|
|
5300
6060
|
var init_mcp = __esm(() => {
|
|
@@ -5304,7 +6064,7 @@ var init_mcp = __esm(() => {
|
|
|
5304
6064
|
name: "Claude MCP Config",
|
|
5305
6065
|
description: "Validates .mcp.json (or inline via plugin.json mcpServers): server entries (stdio: command+args, or url), env, cwd, ${CLAUDE_PLUGIN_ROOT} etc. substitutions per Plugins reference",
|
|
5306
6066
|
detect(dir) {
|
|
5307
|
-
return
|
|
6067
|
+
return existsSync27(resolve9(dir, ".mcp.json"));
|
|
5308
6068
|
},
|
|
5309
6069
|
async validate(dir, _opts) {
|
|
5310
6070
|
const errors = [];
|
|
@@ -5364,8 +6124,8 @@ var init_mcp = __esm(() => {
|
|
|
5364
6124
|
});
|
|
5365
6125
|
|
|
5366
6126
|
// src/validators/claude/subagent.ts
|
|
5367
|
-
import { existsSync as
|
|
5368
|
-
import { resolve as resolve10, join as
|
|
6127
|
+
import { existsSync as existsSync28, readdirSync as readdirSync14 } from "fs";
|
|
6128
|
+
import { resolve as resolve10, join as join24 } from "path";
|
|
5369
6129
|
var claudeSubagentValidator;
|
|
5370
6130
|
var init_subagent = __esm(() => {
|
|
5371
6131
|
init_frontmatter();
|
|
@@ -5376,10 +6136,10 @@ var init_subagent = __esm(() => {
|
|
|
5376
6136
|
description: "Validates agents/*.md (plugin subagents): frontmatter per spec (name, description, model, effort, maxTurns, tools, disallowedTools, skills, memory, background, isolation=worktree), body; warns on disallowed fields (hooks, mcpServers, permissionMode) for security",
|
|
5377
6137
|
detect(dir) {
|
|
5378
6138
|
const agentsDir = resolve10(dir, "agents");
|
|
5379
|
-
if (!
|
|
6139
|
+
if (!existsSync28(agentsDir))
|
|
5380
6140
|
return false;
|
|
5381
6141
|
try {
|
|
5382
|
-
return
|
|
6142
|
+
return readdirSync14(agentsDir).some((f) => f.endsWith(".md"));
|
|
5383
6143
|
} catch {
|
|
5384
6144
|
return false;
|
|
5385
6145
|
}
|
|
@@ -5389,7 +6149,7 @@ var init_subagent = __esm(() => {
|
|
|
5389
6149
|
const warnings = [];
|
|
5390
6150
|
const passes = [];
|
|
5391
6151
|
const agentsDir = resolve10(dir, "agents");
|
|
5392
|
-
const mdFiles =
|
|
6152
|
+
const mdFiles = readdirSync14(agentsDir).filter((f) => f.endsWith(".md"));
|
|
5393
6153
|
if (mdFiles.length === 0) {
|
|
5394
6154
|
errors.push("agents/ directory has no .md files");
|
|
5395
6155
|
return { errors, warnings, passes };
|
|
@@ -5410,7 +6170,7 @@ var init_subagent = __esm(() => {
|
|
|
5410
6170
|
]);
|
|
5411
6171
|
const DISALLOWED = new Set(["hooks", "mcpServers", "permissionMode"]);
|
|
5412
6172
|
for (const file of mdFiles) {
|
|
5413
|
-
const filePath =
|
|
6173
|
+
const filePath = join24(agentsDir, file);
|
|
5414
6174
|
const raw = await Bun.file(filePath).text();
|
|
5415
6175
|
try {
|
|
5416
6176
|
const parsed = parseFrontmatter(raw);
|
|
@@ -5456,8 +6216,8 @@ var init_subagent = __esm(() => {
|
|
|
5456
6216
|
});
|
|
5457
6217
|
|
|
5458
6218
|
// src/validators/claude/command.ts
|
|
5459
|
-
import { existsSync as
|
|
5460
|
-
import { resolve as resolve11, join as
|
|
6219
|
+
import { existsSync as existsSync29, readdirSync as readdirSync15 } from "fs";
|
|
6220
|
+
import { resolve as resolve11, join as join25 } from "path";
|
|
5461
6221
|
var claudeCommandValidator;
|
|
5462
6222
|
var init_command = __esm(() => {
|
|
5463
6223
|
init_frontmatter();
|
|
@@ -5468,10 +6228,10 @@ var init_command = __esm(() => {
|
|
|
5468
6228
|
description: "Validates commands/ (or legacy .claude/commands/) .md files: frontmatter (including rich skill fields), description, body",
|
|
5469
6229
|
detect(dir) {
|
|
5470
6230
|
const commandsDir = resolve11(dir, "commands");
|
|
5471
|
-
if (!
|
|
6231
|
+
if (!existsSync29(commandsDir))
|
|
5472
6232
|
return false;
|
|
5473
6233
|
try {
|
|
5474
|
-
return
|
|
6234
|
+
return readdirSync15(commandsDir).some((f) => f.endsWith(".md"));
|
|
5475
6235
|
} catch {
|
|
5476
6236
|
return false;
|
|
5477
6237
|
}
|
|
@@ -5481,14 +6241,14 @@ var init_command = __esm(() => {
|
|
|
5481
6241
|
const warnings = [];
|
|
5482
6242
|
const passes = [];
|
|
5483
6243
|
const commandsDir = resolve11(dir, "commands");
|
|
5484
|
-
const mdFiles =
|
|
6244
|
+
const mdFiles = readdirSync15(commandsDir).filter((f) => f.endsWith(".md"));
|
|
5485
6245
|
if (mdFiles.length === 0) {
|
|
5486
6246
|
errors.push("commands/ directory has no .md files");
|
|
5487
6247
|
return { errors, warnings, passes };
|
|
5488
6248
|
}
|
|
5489
6249
|
passes.push(`${mdFiles.length} command definition(s) found`);
|
|
5490
6250
|
for (const file of mdFiles) {
|
|
5491
|
-
const filePath =
|
|
6251
|
+
const filePath = join25(commandsDir, file);
|
|
5492
6252
|
const raw = await Bun.file(filePath).text();
|
|
5493
6253
|
try {
|
|
5494
6254
|
const parsed = parseFrontmatter(raw);
|
|
@@ -5517,7 +6277,7 @@ var init_command = __esm(() => {
|
|
|
5517
6277
|
});
|
|
5518
6278
|
|
|
5519
6279
|
// src/validators/claude/memory.ts
|
|
5520
|
-
import { existsSync as
|
|
6280
|
+
import { existsSync as existsSync30 } from "fs";
|
|
5521
6281
|
import { resolve as resolve12 } from "path";
|
|
5522
6282
|
var claudeMemoryValidator;
|
|
5523
6283
|
var init_memory = __esm(() => {
|
|
@@ -5527,7 +6287,7 @@ var init_memory = __esm(() => {
|
|
|
5527
6287
|
name: "Claude CLAUDE.md",
|
|
5528
6288
|
description: "Validates CLAUDE.md: non-empty, length recommendations, @path imports",
|
|
5529
6289
|
detect(dir) {
|
|
5530
|
-
return
|
|
6290
|
+
return existsSync30(resolve12(dir, "CLAUDE.md"));
|
|
5531
6291
|
},
|
|
5532
6292
|
async validate(dir, _opts) {
|
|
5533
6293
|
const errors = [];
|
|
@@ -5552,7 +6312,7 @@ var init_memory = __esm(() => {
|
|
|
5552
6312
|
while ((match = importRegex.exec(raw)) !== null) {
|
|
5553
6313
|
const importPath = match[1];
|
|
5554
6314
|
const resolvedImport = resolve12(dir, importPath);
|
|
5555
|
-
if (
|
|
6315
|
+
if (existsSync30(resolvedImport)) {
|
|
5556
6316
|
passes.push(`@import "${importPath}" exists`);
|
|
5557
6317
|
} else {
|
|
5558
6318
|
warnings.push(`@import "${importPath}" \u2014 file not found at ${resolvedImport}`);
|
|
@@ -5564,7 +6324,7 @@ var init_memory = __esm(() => {
|
|
|
5564
6324
|
});
|
|
5565
6325
|
|
|
5566
6326
|
// src/validators/claude/lsp.ts
|
|
5567
|
-
import { existsSync as
|
|
6327
|
+
import { existsSync as existsSync31 } from "fs";
|
|
5568
6328
|
import { resolve as resolve13 } from "path";
|
|
5569
6329
|
var claudeLspValidator;
|
|
5570
6330
|
var init_lsp = __esm(() => {
|
|
@@ -5574,7 +6334,7 @@ var init_lsp = __esm(() => {
|
|
|
5574
6334
|
name: "Claude LSP Servers",
|
|
5575
6335
|
description: "Validates .lsp.json (or plugin.json lspServers): language server configs with required command + extensionToLanguage; optional transport, env, settings, diagnostics etc. (binaries installed separately)",
|
|
5576
6336
|
detect(dir) {
|
|
5577
|
-
return
|
|
6337
|
+
return existsSync31(resolve13(dir, ".lsp.json")) || existsSync31(resolve13(dir, ".claude-plugin", "plugin.json"));
|
|
5578
6338
|
},
|
|
5579
6339
|
async validate(dir, _opts) {
|
|
5580
6340
|
const errors = [];
|
|
@@ -5582,7 +6342,7 @@ var init_lsp = __esm(() => {
|
|
|
5582
6342
|
const passes = [];
|
|
5583
6343
|
let cfg = null;
|
|
5584
6344
|
const lspPath = resolve13(dir, ".lsp.json");
|
|
5585
|
-
if (
|
|
6345
|
+
if (existsSync31(lspPath)) {
|
|
5586
6346
|
try {
|
|
5587
6347
|
cfg = JSON.parse(await Bun.file(lspPath).text());
|
|
5588
6348
|
passes.push(".lsp.json is valid JSON");
|
|
@@ -5592,7 +6352,7 @@ var init_lsp = __esm(() => {
|
|
|
5592
6352
|
}
|
|
5593
6353
|
} else {
|
|
5594
6354
|
const manifestPath = resolve13(dir, ".claude-plugin", "plugin.json");
|
|
5595
|
-
if (
|
|
6355
|
+
if (existsSync31(manifestPath)) {
|
|
5596
6356
|
try {
|
|
5597
6357
|
const m = JSON.parse(await Bun.file(manifestPath).text());
|
|
5598
6358
|
if (m && m.lspServers && typeof m.lspServers === "object") {
|
|
@@ -5603,7 +6363,7 @@ var init_lsp = __esm(() => {
|
|
|
5603
6363
|
}
|
|
5604
6364
|
}
|
|
5605
6365
|
if (!cfg) {
|
|
5606
|
-
if (!
|
|
6366
|
+
if (!existsSync31(lspPath)) {
|
|
5607
6367
|
return { errors, warnings, passes };
|
|
5608
6368
|
}
|
|
5609
6369
|
}
|
|
@@ -5632,7 +6392,7 @@ var init_lsp = __esm(() => {
|
|
|
5632
6392
|
});
|
|
5633
6393
|
|
|
5634
6394
|
// src/validators/claude/monitors.ts
|
|
5635
|
-
import { existsSync as
|
|
6395
|
+
import { existsSync as existsSync32 } from "fs";
|
|
5636
6396
|
import { resolve as resolve14 } from "path";
|
|
5637
6397
|
var claudeMonitorsValidator;
|
|
5638
6398
|
var init_monitors = __esm(() => {
|
|
@@ -5642,7 +6402,7 @@ var init_monitors = __esm(() => {
|
|
|
5642
6402
|
name: "Claude Monitors (experimental)",
|
|
5643
6403
|
description: "Validates monitors/monitors.json (or experimental.monitors): array of {name, command, description, when?}; commands support ${CLAUDE_PLUGIN_*} subs. Monitors run only in interactive CLI sessions.",
|
|
5644
6404
|
detect(dir) {
|
|
5645
|
-
return
|
|
6405
|
+
return existsSync32(resolve14(dir, "monitors", "monitors.json")) || existsSync32(resolve14(dir, "monitors.json")) || existsSync32(resolve14(dir, ".claude-plugin", "plugin.json"));
|
|
5646
6406
|
},
|
|
5647
6407
|
async validate(dir, _opts) {
|
|
5648
6408
|
const errors = [];
|
|
@@ -5654,7 +6414,7 @@ var init_monitors = __esm(() => {
|
|
|
5654
6414
|
resolve14(dir, "monitors.json")
|
|
5655
6415
|
];
|
|
5656
6416
|
for (const p of candidates) {
|
|
5657
|
-
if (
|
|
6417
|
+
if (existsSync32(p)) {
|
|
5658
6418
|
try {
|
|
5659
6419
|
const parsed = JSON.parse(await Bun.file(p).text());
|
|
5660
6420
|
if (Array.isArray(parsed)) {
|
|
@@ -5670,7 +6430,7 @@ var init_monitors = __esm(() => {
|
|
|
5670
6430
|
}
|
|
5671
6431
|
if (!arr) {
|
|
5672
6432
|
const mp = resolve14(dir, ".claude-plugin", "plugin.json");
|
|
5673
|
-
if (
|
|
6433
|
+
if (existsSync32(mp)) {
|
|
5674
6434
|
try {
|
|
5675
6435
|
const m = JSON.parse(await Bun.file(mp).text());
|
|
5676
6436
|
const exp = m?.experimental;
|
|
@@ -5723,8 +6483,8 @@ var init_monitors = __esm(() => {
|
|
|
5723
6483
|
});
|
|
5724
6484
|
|
|
5725
6485
|
// src/validators/codex/plugin.ts
|
|
5726
|
-
import { existsSync as
|
|
5727
|
-
import { resolve as resolve15, join as
|
|
6486
|
+
import { existsSync as existsSync33, readdirSync as readdirSync16 } from "fs";
|
|
6487
|
+
import { resolve as resolve15, join as join26 } from "path";
|
|
5728
6488
|
var NAME_REGEX3, codexPluginValidator;
|
|
5729
6489
|
var init_plugin2 = __esm(() => {
|
|
5730
6490
|
NAME_REGEX3 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
@@ -5734,7 +6494,7 @@ var init_plugin2 = __esm(() => {
|
|
|
5734
6494
|
name: "Codex Plugin",
|
|
5735
6495
|
description: "Validates .codex-plugin/plugin.json manifest (requires interface block and skills as directory string per Codex packaging)",
|
|
5736
6496
|
detect(dir) {
|
|
5737
|
-
return
|
|
6497
|
+
return existsSync33(resolve15(dir, ".codex-plugin", "plugin.json"));
|
|
5738
6498
|
},
|
|
5739
6499
|
async validate(dir, _opts) {
|
|
5740
6500
|
const errors = [];
|
|
@@ -5815,11 +6575,11 @@ var init_plugin2 = __esm(() => {
|
|
|
5815
6575
|
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Codex)');
|
|
5816
6576
|
}
|
|
5817
6577
|
const skillsDir = resolve15(dir, "skills");
|
|
5818
|
-
if (
|
|
5819
|
-
const entries =
|
|
6578
|
+
if (existsSync33(skillsDir)) {
|
|
6579
|
+
const entries = readdirSync16(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5820
6580
|
for (const e of entries) {
|
|
5821
|
-
const md =
|
|
5822
|
-
if (
|
|
6581
|
+
const md = join26(skillsDir, e.name, "SKILL.md");
|
|
6582
|
+
if (existsSync33(md)) {
|
|
5823
6583
|
passes.push(`skills/${e.name}/SKILL.md exists`);
|
|
5824
6584
|
} else {
|
|
5825
6585
|
errors.push(`skills/${e.name}/ is missing SKILL.md`);
|
|
@@ -5837,7 +6597,7 @@ var init_plugin2 = __esm(() => {
|
|
|
5837
6597
|
});
|
|
5838
6598
|
|
|
5839
6599
|
// src/validators/codex/marketplace.ts
|
|
5840
|
-
import { existsSync as
|
|
6600
|
+
import { existsSync as existsSync34 } from "fs";
|
|
5841
6601
|
import { resolve as resolve16 } from "path";
|
|
5842
6602
|
var codexMarketplaceValidator;
|
|
5843
6603
|
var init_marketplace2 = __esm(() => {
|
|
@@ -5847,9 +6607,9 @@ var init_marketplace2 = __esm(() => {
|
|
|
5847
6607
|
name: "Codex Plugin Marketplace",
|
|
5848
6608
|
description: "Validates .agents/plugins/marketplace.json (Codex convention: object source + policy blocks)",
|
|
5849
6609
|
detect(dir) {
|
|
5850
|
-
if (
|
|
6610
|
+
if (existsSync34(resolve16(dir, ".agents", "plugins", "marketplace.json")))
|
|
5851
6611
|
return true;
|
|
5852
|
-
if (
|
|
6612
|
+
if (existsSync34(resolve16(dir, ".agents", "plugins", "marketplace.json")))
|
|
5853
6613
|
return true;
|
|
5854
6614
|
return false;
|
|
5855
6615
|
},
|
|
@@ -5858,7 +6618,7 @@ var init_marketplace2 = __esm(() => {
|
|
|
5858
6618
|
const warnings = [];
|
|
5859
6619
|
const passes = [];
|
|
5860
6620
|
const marketplacePath = resolve16(dir, ".agents", "plugins", "marketplace.json");
|
|
5861
|
-
if (!
|
|
6621
|
+
if (!existsSync34(marketplacePath)) {
|
|
5862
6622
|
errors.push("Missing .agents/plugins/marketplace.json");
|
|
5863
6623
|
return { errors, warnings, passes };
|
|
5864
6624
|
}
|
|
@@ -5933,12 +6693,12 @@ var init_marketplace2 = __esm(() => {
|
|
|
5933
6693
|
passes.push(`plugins[${i}].category: "${p.category}"`);
|
|
5934
6694
|
}
|
|
5935
6695
|
}
|
|
5936
|
-
if (
|
|
6696
|
+
if (existsSync34(resolve16(dir, "README.md"))) {
|
|
5937
6697
|
passes.push("README.md exists at marketplace root");
|
|
5938
6698
|
} else {
|
|
5939
6699
|
warnings.push("No README.md at marketplace root \u2014 recommended");
|
|
5940
6700
|
}
|
|
5941
|
-
if (
|
|
6701
|
+
if (existsSync34(resolve16(dir, "LICENSE"))) {
|
|
5942
6702
|
passes.push("LICENSE exists at marketplace root");
|
|
5943
6703
|
} else {
|
|
5944
6704
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -5949,7 +6709,7 @@ var init_marketplace2 = __esm(() => {
|
|
|
5949
6709
|
});
|
|
5950
6710
|
|
|
5951
6711
|
// src/validators/codex/mcp.ts
|
|
5952
|
-
import { existsSync as
|
|
6712
|
+
import { existsSync as existsSync35 } from "fs";
|
|
5953
6713
|
import { resolve as resolve17 } from "path";
|
|
5954
6714
|
var codexMcpValidator;
|
|
5955
6715
|
var init_mcp2 = __esm(() => {
|
|
@@ -5959,7 +6719,7 @@ var init_mcp2 = __esm(() => {
|
|
|
5959
6719
|
name: "Codex MCP Config",
|
|
5960
6720
|
description: "Validates .mcp.json (or inline via plugin.json mcpServers): server entries (stdio: command+args, or url), env, cwd, substitutions per Codex MCP support",
|
|
5961
6721
|
detect(dir) {
|
|
5962
|
-
return
|
|
6722
|
+
return existsSync35(resolve17(dir, ".mcp.json"));
|
|
5963
6723
|
},
|
|
5964
6724
|
async validate(dir, _opts) {
|
|
5965
6725
|
const errors = [];
|
|
@@ -6019,7 +6779,7 @@ var init_mcp2 = __esm(() => {
|
|
|
6019
6779
|
});
|
|
6020
6780
|
|
|
6021
6781
|
// src/validators/codex/skill.ts
|
|
6022
|
-
import { existsSync as
|
|
6782
|
+
import { existsSync as existsSync36 } from "fs";
|
|
6023
6783
|
import { resolve as resolve18 } from "path";
|
|
6024
6784
|
var codexSkillValidator;
|
|
6025
6785
|
var init_skill2 = __esm(() => {
|
|
@@ -6030,7 +6790,7 @@ var init_skill2 = __esm(() => {
|
|
|
6030
6790
|
name: "Codex Skill",
|
|
6031
6791
|
description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Codex uses the same SKILL.md spec as other providers.",
|
|
6032
6792
|
detect(dir) {
|
|
6033
|
-
return
|
|
6793
|
+
return existsSync36(resolve18(dir, "SKILL.md"));
|
|
6034
6794
|
},
|
|
6035
6795
|
async validate(dir, _opts) {
|
|
6036
6796
|
const loaded = await loadSkill(dir);
|
|
@@ -6048,8 +6808,8 @@ var init_skill2 = __esm(() => {
|
|
|
6048
6808
|
});
|
|
6049
6809
|
|
|
6050
6810
|
// src/validators/cursor/plugin.ts
|
|
6051
|
-
import { existsSync as
|
|
6052
|
-
import { resolve as resolve19, join as
|
|
6811
|
+
import { existsSync as existsSync37, readdirSync as readdirSync18 } from "fs";
|
|
6812
|
+
import { resolve as resolve19, join as join28 } from "path";
|
|
6053
6813
|
var NAME_REGEX4, cursorPluginValidator;
|
|
6054
6814
|
var init_plugin3 = __esm(() => {
|
|
6055
6815
|
NAME_REGEX4 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
@@ -6059,7 +6819,7 @@ var init_plugin3 = __esm(() => {
|
|
|
6059
6819
|
name: "Cursor Plugin",
|
|
6060
6820
|
description: "Validates .cursor-plugin/plugin.json manifest (skills as directory string; mcpServers support)",
|
|
6061
6821
|
detect(dir) {
|
|
6062
|
-
return
|
|
6822
|
+
return existsSync37(resolve19(dir, ".cursor-plugin", "plugin.json"));
|
|
6063
6823
|
},
|
|
6064
6824
|
async validate(dir, _opts) {
|
|
6065
6825
|
const errors = [];
|
|
@@ -6146,11 +6906,11 @@ var init_plugin3 = __esm(() => {
|
|
|
6146
6906
|
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Cursor)');
|
|
6147
6907
|
}
|
|
6148
6908
|
const skillsDir = resolve19(dir, "skills");
|
|
6149
|
-
if (
|
|
6150
|
-
const entries =
|
|
6909
|
+
if (existsSync37(skillsDir)) {
|
|
6910
|
+
const entries = readdirSync18(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
6151
6911
|
for (const e of entries) {
|
|
6152
|
-
const md =
|
|
6153
|
-
if (
|
|
6912
|
+
const md = join28(skillsDir, e.name, "SKILL.md");
|
|
6913
|
+
if (existsSync37(md)) {
|
|
6154
6914
|
passes.push(`skills/${e.name}/SKILL.md exists`);
|
|
6155
6915
|
} else {
|
|
6156
6916
|
errors.push(`skills/${e.name}/ is missing SKILL.md`);
|
|
@@ -6161,7 +6921,7 @@ var init_plugin3 = __esm(() => {
|
|
|
6161
6921
|
const mcpRef = manifest.mcpServers;
|
|
6162
6922
|
if (mcpRef.startsWith("./") || mcpRef.startsWith("../")) {
|
|
6163
6923
|
const mcpPath = resolve19(dir, mcpRef);
|
|
6164
|
-
if (
|
|
6924
|
+
if (existsSync37(mcpPath)) {
|
|
6165
6925
|
passes.push(`mcpServers file exists at ${mcpRef}`);
|
|
6166
6926
|
} else {
|
|
6167
6927
|
warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
|
|
@@ -6191,8 +6951,8 @@ var init_plugin3 = __esm(() => {
|
|
|
6191
6951
|
});
|
|
6192
6952
|
|
|
6193
6953
|
// src/validators/cursor/marketplace.ts
|
|
6194
|
-
import { existsSync as
|
|
6195
|
-
import { resolve as resolve20, join as
|
|
6954
|
+
import { existsSync as existsSync38, readdirSync as readdirSync19 } from "fs";
|
|
6955
|
+
import { resolve as resolve20, join as join29 } from "path";
|
|
6196
6956
|
var cursorMarketplaceValidator;
|
|
6197
6957
|
var init_marketplace3 = __esm(() => {
|
|
6198
6958
|
cursorMarketplaceValidator = {
|
|
@@ -6201,18 +6961,18 @@ var init_marketplace3 = __esm(() => {
|
|
|
6201
6961
|
name: "Cursor Plugin Marketplace",
|
|
6202
6962
|
description: "Validates .cursor-plugin/marketplace.json (string sources + metadata.pluginRoot)",
|
|
6203
6963
|
detect(dir) {
|
|
6204
|
-
if (
|
|
6964
|
+
if (existsSync38(resolve20(dir, ".cursor-plugin", "marketplace.json")))
|
|
6205
6965
|
return true;
|
|
6206
6966
|
const pluginsDir = resolve20(dir, "plugins");
|
|
6207
|
-
if (!
|
|
6967
|
+
if (!existsSync38(pluginsDir))
|
|
6208
6968
|
return false;
|
|
6209
6969
|
try {
|
|
6210
|
-
const entries =
|
|
6970
|
+
const entries = readdirSync19(pluginsDir, { withFileTypes: true });
|
|
6211
6971
|
for (const entry of entries) {
|
|
6212
6972
|
if (!entry.isDirectory())
|
|
6213
6973
|
continue;
|
|
6214
|
-
const hasSkills =
|
|
6215
|
-
const hasManifest =
|
|
6974
|
+
const hasSkills = existsSync38(join29(pluginsDir, entry.name, "skills"));
|
|
6975
|
+
const hasManifest = existsSync38(join29(pluginsDir, entry.name, ".cursor-plugin", "plugin.json"));
|
|
6216
6976
|
if (hasSkills || hasManifest)
|
|
6217
6977
|
return true;
|
|
6218
6978
|
}
|
|
@@ -6224,9 +6984,9 @@ var init_marketplace3 = __esm(() => {
|
|
|
6224
6984
|
const warnings = [];
|
|
6225
6985
|
const passes = [];
|
|
6226
6986
|
const cursorMktPath = resolve20(dir, ".cursor-plugin", "marketplace.json");
|
|
6227
|
-
const hasCursorMkt =
|
|
6987
|
+
const hasCursorMkt = existsSync38(cursorMktPath);
|
|
6228
6988
|
const pluginsDir = resolve20(dir, "plugins");
|
|
6229
|
-
const hasPluginsDirLayout =
|
|
6989
|
+
const hasPluginsDirLayout = existsSync38(pluginsDir);
|
|
6230
6990
|
if (!hasCursorMkt && !hasPluginsDirLayout) {
|
|
6231
6991
|
errors.push("Missing .cursor-plugin/marketplace.json or plugins/ directory");
|
|
6232
6992
|
return { errors, warnings, passes };
|
|
@@ -6280,9 +7040,9 @@ var init_marketplace3 = __esm(() => {
|
|
|
6280
7040
|
const src = String(p.source);
|
|
6281
7041
|
passes.push(`plugins[${i}].source: "${src}"`);
|
|
6282
7042
|
const srcDir = resolve20(dir, pluginRoot, src);
|
|
6283
|
-
if (
|
|
6284
|
-
const hasManifest =
|
|
6285
|
-
const hasSkills =
|
|
7043
|
+
if (existsSync38(srcDir)) {
|
|
7044
|
+
const hasManifest = existsSync38(resolve20(srcDir, ".cursor-plugin", "plugin.json"));
|
|
7045
|
+
const hasSkills = existsSync38(resolve20(srcDir, "skills"));
|
|
6286
7046
|
if (hasManifest || hasSkills) {
|
|
6287
7047
|
passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
|
|
6288
7048
|
} else {
|
|
@@ -6293,7 +7053,7 @@ var init_marketplace3 = __esm(() => {
|
|
|
6293
7053
|
}
|
|
6294
7054
|
} else {
|
|
6295
7055
|
const implicitSrc = resolve20(dir, pluginRoot, p.name || "");
|
|
6296
|
-
if (p.name &&
|
|
7056
|
+
if (p.name && existsSync38(implicitSrc)) {
|
|
6297
7057
|
passes.push(`plugins[${i}]: implicit source via name under ${pluginRoot}`);
|
|
6298
7058
|
} else {
|
|
6299
7059
|
warnings.push(`plugins[${i}]: missing "source" (and no implicit dir)`);
|
|
@@ -6308,12 +7068,12 @@ var init_marketplace3 = __esm(() => {
|
|
|
6308
7068
|
passes.push(`plugins[${i}].homepage present`);
|
|
6309
7069
|
}
|
|
6310
7070
|
}
|
|
6311
|
-
if (
|
|
7071
|
+
if (existsSync38(resolve20(dir, "README.md"))) {
|
|
6312
7072
|
passes.push("README.md exists at marketplace root");
|
|
6313
7073
|
} else {
|
|
6314
7074
|
warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
|
|
6315
7075
|
}
|
|
6316
|
-
if (
|
|
7076
|
+
if (existsSync38(resolve20(dir, "LICENSE"))) {
|
|
6317
7077
|
passes.push("LICENSE exists at marketplace root");
|
|
6318
7078
|
} else {
|
|
6319
7079
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -6322,21 +7082,21 @@ var init_marketplace3 = __esm(() => {
|
|
|
6322
7082
|
}
|
|
6323
7083
|
if (hasPluginsDirLayout) {
|
|
6324
7084
|
passes.push("plugins/ directory exists");
|
|
6325
|
-
const pluginEntries =
|
|
7085
|
+
const pluginEntries = readdirSync19(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
6326
7086
|
if (pluginEntries.length === 0) {
|
|
6327
7087
|
errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
|
|
6328
7088
|
return { errors, warnings, passes };
|
|
6329
7089
|
}
|
|
6330
7090
|
passes.push(`${pluginEntries.length} plugin(s) found`);
|
|
6331
|
-
if (
|
|
7091
|
+
if (existsSync38(resolve20(dir, "README.md"))) {
|
|
6332
7092
|
passes.push("README.md exists at marketplace root");
|
|
6333
7093
|
} else {
|
|
6334
7094
|
warnings.push("No README.md at marketplace root \u2014 recommended");
|
|
6335
7095
|
}
|
|
6336
7096
|
for (const plugin of pluginEntries) {
|
|
6337
|
-
const pluginPath =
|
|
6338
|
-
const hasSkills =
|
|
6339
|
-
const hasManifest =
|
|
7097
|
+
const pluginPath = join29(pluginsDir, plugin.name);
|
|
7098
|
+
const hasSkills = existsSync38(join29(pluginPath, "skills"));
|
|
7099
|
+
const hasManifest = existsSync38(join29(pluginPath, ".cursor-plugin", "plugin.json"));
|
|
6340
7100
|
if (hasManifest || hasSkills) {
|
|
6341
7101
|
passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
|
|
6342
7102
|
}
|
|
@@ -6349,7 +7109,7 @@ var init_marketplace3 = __esm(() => {
|
|
|
6349
7109
|
});
|
|
6350
7110
|
|
|
6351
7111
|
// src/validators/cursor/mcp.ts
|
|
6352
|
-
import { existsSync as
|
|
7112
|
+
import { existsSync as existsSync39 } from "fs";
|
|
6353
7113
|
import { resolve as resolve21 } from "path";
|
|
6354
7114
|
var cursorMcpValidator;
|
|
6355
7115
|
var init_mcp3 = __esm(() => {
|
|
@@ -6359,7 +7119,7 @@ var init_mcp3 = __esm(() => {
|
|
|
6359
7119
|
name: "Cursor MCP Config",
|
|
6360
7120
|
description: "Validates mcp.json (Cursor uses no leading dot; supports mcpServers wrapper or direct server map)",
|
|
6361
7121
|
detect(dir) {
|
|
6362
|
-
return
|
|
7122
|
+
return existsSync39(resolve21(dir, "mcp.json"));
|
|
6363
7123
|
},
|
|
6364
7124
|
async validate(dir, _opts) {
|
|
6365
7125
|
const errors = [];
|
|
@@ -6425,7 +7185,7 @@ var init_mcp3 = __esm(() => {
|
|
|
6425
7185
|
});
|
|
6426
7186
|
|
|
6427
7187
|
// src/validators/cursor/skill.ts
|
|
6428
|
-
import { existsSync as
|
|
7188
|
+
import { existsSync as existsSync40 } from "fs";
|
|
6429
7189
|
import { resolve as resolve22 } from "path";
|
|
6430
7190
|
var cursorSkillValidator;
|
|
6431
7191
|
var init_skill3 = __esm(() => {
|
|
@@ -6436,7 +7196,7 @@ var init_skill3 = __esm(() => {
|
|
|
6436
7196
|
name: "Cursor Skill",
|
|
6437
7197
|
description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Cursor uses the same SKILL.md spec as other providers.",
|
|
6438
7198
|
detect(dir) {
|
|
6439
|
-
return
|
|
7199
|
+
return existsSync40(resolve22(dir, "SKILL.md"));
|
|
6440
7200
|
},
|
|
6441
7201
|
async validate(dir, _opts) {
|
|
6442
7202
|
const loaded = await loadSkill(dir);
|
|
@@ -6454,7 +7214,7 @@ var init_skill3 = __esm(() => {
|
|
|
6454
7214
|
});
|
|
6455
7215
|
|
|
6456
7216
|
// src/validators/copilot/plugin.ts
|
|
6457
|
-
import { existsSync as
|
|
7217
|
+
import { existsSync as existsSync41 } from "fs";
|
|
6458
7218
|
import { resolve as resolve23 } from "path";
|
|
6459
7219
|
var NAME_REGEX5, copilotPluginValidator;
|
|
6460
7220
|
var init_plugin4 = __esm(() => {
|
|
@@ -6465,7 +7225,7 @@ var init_plugin4 = __esm(() => {
|
|
|
6465
7225
|
name: "Copilot Plugin",
|
|
6466
7226
|
description: "Validates .github/plugin/plugin.json (skills as array of paths, mcpServers support)",
|
|
6467
7227
|
detect(dir) {
|
|
6468
|
-
return
|
|
7228
|
+
return existsSync41(resolve23(dir, ".github", "plugin", "plugin.json"));
|
|
6469
7229
|
},
|
|
6470
7230
|
async validate(dir, _opts) {
|
|
6471
7231
|
const errors = [];
|
|
@@ -6508,9 +7268,9 @@ var init_plugin4 = __esm(() => {
|
|
|
6508
7268
|
}
|
|
6509
7269
|
const skillDir = resolve23(dir, p);
|
|
6510
7270
|
const skillMd = resolve23(skillDir, "SKILL.md");
|
|
6511
|
-
if (
|
|
7271
|
+
if (existsSync41(skillMd)) {
|
|
6512
7272
|
passes.push(`skills[${i}]: ${p}/SKILL.md exists`);
|
|
6513
|
-
} else if (
|
|
7273
|
+
} else if (existsSync41(skillDir)) {
|
|
6514
7274
|
warnings.push(`skills[${i}]: directory exists but no SKILL.md inside`);
|
|
6515
7275
|
} else {
|
|
6516
7276
|
warnings.push(`skills[${i}]: path "${p}" does not exist`);
|
|
@@ -6522,7 +7282,7 @@ var init_plugin4 = __esm(() => {
|
|
|
6522
7282
|
passes.push(`mcpServers: "${manifest.mcpServers}"`);
|
|
6523
7283
|
const mcpRef = String(manifest.mcpServers);
|
|
6524
7284
|
const mcpPath = resolve23(dir, mcpRef);
|
|
6525
|
-
if (
|
|
7285
|
+
if (existsSync41(mcpPath)) {
|
|
6526
7286
|
passes.push(`mcpServers file exists at ${mcpRef}`);
|
|
6527
7287
|
} else {
|
|
6528
7288
|
warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
|
|
@@ -6582,7 +7342,7 @@ var init_plugin4 = __esm(() => {
|
|
|
6582
7342
|
});
|
|
6583
7343
|
|
|
6584
7344
|
// src/validators/copilot/marketplace.ts
|
|
6585
|
-
import { existsSync as
|
|
7345
|
+
import { existsSync as existsSync42 } from "fs";
|
|
6586
7346
|
import { resolve as resolve24 } from "path";
|
|
6587
7347
|
var copilotMarketplaceValidator;
|
|
6588
7348
|
var init_marketplace4 = __esm(() => {
|
|
@@ -6592,7 +7352,7 @@ var init_marketplace4 = __esm(() => {
|
|
|
6592
7352
|
name: "Copilot Plugin Marketplace",
|
|
6593
7353
|
description: "Validates .github/plugin/marketplace.json (string sources)",
|
|
6594
7354
|
detect(dir) {
|
|
6595
|
-
return
|
|
7355
|
+
return existsSync42(resolve24(dir, ".github", "plugin", "marketplace.json"));
|
|
6596
7356
|
},
|
|
6597
7357
|
async validate(dir, _opts) {
|
|
6598
7358
|
const errors = [];
|
|
@@ -6642,9 +7402,9 @@ var init_marketplace4 = __esm(() => {
|
|
|
6642
7402
|
const src = String(p.source);
|
|
6643
7403
|
passes.push(`plugins[${i}].source: "${src}"`);
|
|
6644
7404
|
const srcDir = resolve24(dir, src);
|
|
6645
|
-
if (
|
|
6646
|
-
const hasManifest =
|
|
6647
|
-
const hasSkills =
|
|
7405
|
+
if (existsSync42(srcDir)) {
|
|
7406
|
+
const hasManifest = existsSync42(resolve24(srcDir, ".github", "plugin", "plugin.json"));
|
|
7407
|
+
const hasSkills = existsSync42(resolve24(srcDir, "skills"));
|
|
6648
7408
|
if (hasManifest || hasSkills) {
|
|
6649
7409
|
passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
|
|
6650
7410
|
} else {
|
|
@@ -6661,12 +7421,12 @@ var init_marketplace4 = __esm(() => {
|
|
|
6661
7421
|
if (p.version)
|
|
6662
7422
|
passes.push(`plugins[${i}].version: "${p.version}"`);
|
|
6663
7423
|
}
|
|
6664
|
-
if (
|
|
7424
|
+
if (existsSync42(resolve24(dir, "README.md"))) {
|
|
6665
7425
|
passes.push("README.md exists at marketplace root");
|
|
6666
7426
|
} else {
|
|
6667
7427
|
warnings.push("No README.md at marketplace root \u2014 recommended");
|
|
6668
7428
|
}
|
|
6669
|
-
if (
|
|
7429
|
+
if (existsSync42(resolve24(dir, "LICENSE"))) {
|
|
6670
7430
|
passes.push("LICENSE exists at marketplace root");
|
|
6671
7431
|
} else {
|
|
6672
7432
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -6677,7 +7437,7 @@ var init_marketplace4 = __esm(() => {
|
|
|
6677
7437
|
});
|
|
6678
7438
|
|
|
6679
7439
|
// src/validators/copilot/mcp.ts
|
|
6680
|
-
import { existsSync as
|
|
7440
|
+
import { existsSync as existsSync43 } from "fs";
|
|
6681
7441
|
import { resolve as resolve25 } from "path";
|
|
6682
7442
|
var copilotMcpValidator;
|
|
6683
7443
|
var init_mcp4 = __esm(() => {
|
|
@@ -6687,7 +7447,7 @@ var init_mcp4 = __esm(() => {
|
|
|
6687
7447
|
name: "Copilot MCP Config",
|
|
6688
7448
|
description: "Validates .mcp.json (referenced via mcpServers in manifest). Supports stdio and http servers.",
|
|
6689
7449
|
detect(dir) {
|
|
6690
|
-
return
|
|
7450
|
+
return existsSync43(resolve25(dir, ".mcp.json"));
|
|
6691
7451
|
},
|
|
6692
7452
|
async validate(dir, _opts) {
|
|
6693
7453
|
const errors = [];
|
|
@@ -6753,7 +7513,7 @@ var init_mcp4 = __esm(() => {
|
|
|
6753
7513
|
});
|
|
6754
7514
|
|
|
6755
7515
|
// src/validators/copilot/skill.ts
|
|
6756
|
-
import { existsSync as
|
|
7516
|
+
import { existsSync as existsSync44 } from "fs";
|
|
6757
7517
|
import { resolve as resolve26 } from "path";
|
|
6758
7518
|
var copilotSkillValidator;
|
|
6759
7519
|
var init_skill4 = __esm(() => {
|
|
@@ -6764,7 +7524,7 @@ var init_skill4 = __esm(() => {
|
|
|
6764
7524
|
name: "Copilot Skill",
|
|
6765
7525
|
description: "Validates SKILL.md (shared format). Copilot supports skills referenced via array paths in the manifest.",
|
|
6766
7526
|
detect(dir) {
|
|
6767
|
-
return
|
|
7527
|
+
return existsSync44(resolve26(dir, "SKILL.md"));
|
|
6768
7528
|
},
|
|
6769
7529
|
async validate(dir, _opts) {
|
|
6770
7530
|
const loaded = await loadSkill(dir);
|
|
@@ -6932,7 +7692,7 @@ var init_validators = __esm(() => {
|
|
|
6932
7692
|
// src/core/remote.ts
|
|
6933
7693
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6934
7694
|
import { mkdtempSync, rmSync } from "fs";
|
|
6935
|
-
import { join as
|
|
7695
|
+
import { join as join31 } from "path";
|
|
6936
7696
|
import { tmpdir } from "os";
|
|
6937
7697
|
function parseRemoteUrl(input) {
|
|
6938
7698
|
if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) {
|
|
@@ -6969,7 +7729,7 @@ function isGhAvailable() {
|
|
|
6969
7729
|
return ghAvailable;
|
|
6970
7730
|
}
|
|
6971
7731
|
async function cloneToTemp(parsed) {
|
|
6972
|
-
const tmpDir = mkdtempSync(
|
|
7732
|
+
const tmpDir = mkdtempSync(join31(tmpdir(), "dora-"));
|
|
6973
7733
|
const cleanup = () => {
|
|
6974
7734
|
try {
|
|
6975
7735
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
@@ -7017,15 +7777,15 @@ var exports_validate_top = {};
|
|
|
7017
7777
|
__export(exports_validate_top, {
|
|
7018
7778
|
default: () => validate_top_default
|
|
7019
7779
|
});
|
|
7020
|
-
import { existsSync as
|
|
7780
|
+
import { existsSync as existsSync46 } from "fs";
|
|
7021
7781
|
import { resolve as resolve27 } from "path";
|
|
7022
|
-
var
|
|
7782
|
+
var import_picocolors21, validate_top_default;
|
|
7023
7783
|
var init_validate_top = __esm(() => {
|
|
7024
7784
|
init_dist();
|
|
7025
7785
|
init_out();
|
|
7026
7786
|
init_validators();
|
|
7027
7787
|
init_remote();
|
|
7028
|
-
|
|
7788
|
+
import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
7029
7789
|
validate_top_default = defineCommand({
|
|
7030
7790
|
meta: {
|
|
7031
7791
|
name: "validate",
|
|
@@ -7065,7 +7825,7 @@ var init_validate_top = __esm(() => {
|
|
|
7065
7825
|
let cleanup;
|
|
7066
7826
|
if (remote) {
|
|
7067
7827
|
ui.info(`
|
|
7068
|
-
Cloning ${
|
|
7828
|
+
Cloning ${import_picocolors21.default.dim(args.path)}...`);
|
|
7069
7829
|
try {
|
|
7070
7830
|
const result = await cloneToTemp(remote);
|
|
7071
7831
|
fullPath = remote.subpath ? resolve27(result.dir, remote.subpath) : result.dir;
|
|
@@ -7075,14 +7835,14 @@ var init_validate_top = __esm(() => {
|
|
|
7075
7835
|
ui.fail(msg);
|
|
7076
7836
|
process.exit(1);
|
|
7077
7837
|
}
|
|
7078
|
-
if (!
|
|
7838
|
+
if (!existsSync46(fullPath)) {
|
|
7079
7839
|
cleanup();
|
|
7080
7840
|
ui.fail(`Subdirectory not found in repo: ${remote.subpath}`);
|
|
7081
7841
|
process.exit(1);
|
|
7082
7842
|
}
|
|
7083
7843
|
} else {
|
|
7084
7844
|
fullPath = resolve27(args.path);
|
|
7085
|
-
if (!
|
|
7845
|
+
if (!existsSync46(fullPath)) {
|
|
7086
7846
|
ui.fail(`Path not found: ${args.path}
|
|
7087
7847
|
|
|
7088
7848
|
Check that the path is correct and the directory exists.`);
|
|
@@ -7113,13 +7873,13 @@ Check that the path is correct and the directory exists.`);
|
|
|
7113
7873
|
` + `Available providers:
|
|
7114
7874
|
` + providers.map((p) => {
|
|
7115
7875
|
const pvs = validators.filter((v) => v.provider === p);
|
|
7116
|
-
return ` ${
|
|
7117
|
-
` + pvs.map((v) => ` \u2022 ${
|
|
7876
|
+
return ` ${import_picocolors21.default.bold(p)}
|
|
7877
|
+
` + pvs.map((v) => ` \u2022 ${import_picocolors21.default.dim(v.id)} \u2014 ${v.description}`).join(`
|
|
7118
7878
|
`);
|
|
7119
7879
|
}).join(`
|
|
7120
7880
|
`) + `
|
|
7121
7881
|
|
|
7122
|
-
Use ${
|
|
7882
|
+
Use ${import_picocolors21.default.dim("--for <provider>")} or ${import_picocolors21.default.dim("--for <provider:type>")} to target explicitly.`);
|
|
7123
7883
|
process.exit(1);
|
|
7124
7884
|
}
|
|
7125
7885
|
const allResults = [];
|
|
@@ -7140,7 +7900,7 @@ Use ${import_picocolors17.default.dim("--for <provider>")} or ${import_picocolor
|
|
|
7140
7900
|
} else {
|
|
7141
7901
|
for (const { id, name, result } of allResults) {
|
|
7142
7902
|
ui.write(`
|
|
7143
|
-
${
|
|
7903
|
+
${import_picocolors21.default.bold("dora validate")} \u2014 ${import_picocolors21.default.white(name)} ${import_picocolors21.default.dim(`(${id})`)}
|
|
7144
7904
|
`);
|
|
7145
7905
|
ui.info(` Path: ${args.path}
|
|
7146
7906
|
`);
|
|
@@ -7155,7 +7915,7 @@ Use ${import_picocolors17.default.dim("--for <provider>")} or ${import_picocolor
|
|
|
7155
7915
|
}
|
|
7156
7916
|
if (result.errors.length === 0 && result.warnings.length === 0) {
|
|
7157
7917
|
ui.write(`
|
|
7158
|
-
${
|
|
7918
|
+
${import_picocolors21.default.green("\u2713")} ${import_picocolors21.default.white("All checks passed.")}
|
|
7159
7919
|
`);
|
|
7160
7920
|
} else {
|
|
7161
7921
|
ui.info(`
|
|
@@ -7177,16 +7937,16 @@ var exports_init2 = {};
|
|
|
7177
7937
|
__export(exports_init2, {
|
|
7178
7938
|
default: () => init_default2
|
|
7179
7939
|
});
|
|
7180
|
-
import { basename as
|
|
7940
|
+
import { basename as basename7, join as join32 } from "path";
|
|
7181
7941
|
var {spawnSync: spawnSync5 } = globalThis.Bun;
|
|
7182
|
-
var
|
|
7942
|
+
var import_picocolors22, init_default2;
|
|
7183
7943
|
var init_init2 = __esm(() => {
|
|
7184
7944
|
init_dist();
|
|
7185
7945
|
init_out();
|
|
7186
7946
|
init_journal_config();
|
|
7187
7947
|
init_journal_remote();
|
|
7188
7948
|
init_prompt();
|
|
7189
|
-
|
|
7949
|
+
import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
7190
7950
|
init_default2 = defineCommand({
|
|
7191
7951
|
meta: {
|
|
7192
7952
|
name: "init",
|
|
@@ -7213,17 +7973,17 @@ var init_init2 = __esm(() => {
|
|
|
7213
7973
|
ui.heading("dora init \u2014 Set up doraval, your journal, and the coding agent dora should use on the fly");
|
|
7214
7974
|
const ghCheck = ensureGhCli();
|
|
7215
7975
|
if (!ghCheck.ok) {
|
|
7216
|
-
ui.write(` ${
|
|
7976
|
+
ui.write(` ${import_picocolors22.default.red("\u2717")} ${import_picocolors22.default.white("The GitHub CLI (")}${import_picocolors22.default.bold("gh")}${import_picocolors22.default.white(") is not installed.")}
|
|
7217
7977
|
`);
|
|
7218
|
-
ui.info(` doraval uses ${
|
|
7978
|
+
ui.info(` doraval uses ${import_picocolors22.default.bold("gh")} to fetch and sync journal files with GitHub.
|
|
7219
7979
|
`);
|
|
7220
7980
|
ui.info(` Install it:
|
|
7221
7981
|
`);
|
|
7222
|
-
ui.info(` macOS: ${
|
|
7223
|
-
ui.info(` Linux: ${
|
|
7224
|
-
ui.info(` Windows: ${
|
|
7982
|
+
ui.info(` macOS: ${import_picocolors22.default.dim("brew install gh")}`);
|
|
7983
|
+
ui.info(` Linux: ${import_picocolors22.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
|
|
7984
|
+
ui.info(` Windows: ${import_picocolors22.default.dim("winget install --id GitHub.cli")}
|
|
7225
7985
|
`);
|
|
7226
|
-
ui.info(` Then authenticate: ${
|
|
7986
|
+
ui.info(` Then authenticate: ${import_picocolors22.default.dim("gh auth login")}
|
|
7227
7987
|
`);
|
|
7228
7988
|
process.exit(1);
|
|
7229
7989
|
}
|
|
@@ -7236,44 +7996,44 @@ var init_init2 = __esm(() => {
|
|
|
7236
7996
|
if (gitOwner) {
|
|
7237
7997
|
defaultRepo = `${gitOwner}/${gitOwner}.md`;
|
|
7238
7998
|
if (ghLogin && ghLogin !== gitOwner) {
|
|
7239
|
-
sourceNote = ` ${
|
|
7999
|
+
sourceNote = ` ${import_picocolors22.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
|
|
7240
8000
|
`;
|
|
7241
8001
|
} else {
|
|
7242
|
-
sourceNote = ` ${
|
|
8002
|
+
sourceNote = ` ${import_picocolors22.default.dim("(from git remote)")}
|
|
7243
8003
|
`;
|
|
7244
8004
|
}
|
|
7245
8005
|
} else if (ghLogin) {
|
|
7246
8006
|
defaultRepo = `${ghLogin}/${ghLogin}.md`;
|
|
7247
|
-
sourceNote = ` ${
|
|
8007
|
+
sourceNote = ` ${import_picocolors22.default.dim("(from your active gh account)")}
|
|
7248
8008
|
`;
|
|
7249
8009
|
} else {
|
|
7250
|
-
ui.warn(`Not logged in to GitHub. Run ${
|
|
8010
|
+
ui.warn(`Not logged in to GitHub. Run ${import_picocolors22.default.dim("gh auth login")} first.
|
|
7251
8011
|
`);
|
|
7252
8012
|
process.exit(1);
|
|
7253
8013
|
}
|
|
7254
8014
|
const existingConfig = await readConfig();
|
|
7255
8015
|
if (existingConfig?.journal.repo) {
|
|
7256
8016
|
defaultRepo = existingConfig.journal.repo;
|
|
7257
|
-
sourceNote = ` ${
|
|
8017
|
+
sourceNote = ` ${import_picocolors22.default.dim("(from your previous journal setup)")}
|
|
7258
8018
|
`;
|
|
7259
8019
|
}
|
|
7260
|
-
ui.info(` Journal repo ${
|
|
8020
|
+
ui.info(` Journal repo ${import_picocolors22.default.dim("(owner/name)")}`);
|
|
7261
8021
|
if (sourceNote)
|
|
7262
8022
|
ui.write(sourceNote);
|
|
7263
8023
|
repo = prompt(" >", defaultRepo);
|
|
7264
8024
|
}
|
|
7265
8025
|
let project = args.project || process.env.DORAVAL_PROJECT;
|
|
7266
8026
|
if (!project) {
|
|
7267
|
-
const defaultProject =
|
|
8027
|
+
const defaultProject = basename7(process.cwd());
|
|
7268
8028
|
project = prompt(" Project name", defaultProject);
|
|
7269
8029
|
}
|
|
7270
8030
|
project = sanitizeProjectName(project);
|
|
7271
8031
|
if (!repoExists(repo)) {
|
|
7272
|
-
ui.write(` ${
|
|
8032
|
+
ui.write(` ${import_picocolors22.default.red("\u2717")} ${import_picocolors22.default.white("Repository")} ${import_picocolors22.default.bold(repo)} ${import_picocolors22.default.white("not found on GitHub.")}
|
|
7273
8033
|
`);
|
|
7274
8034
|
ui.info(` Create it first:
|
|
7275
8035
|
`);
|
|
7276
|
-
ui.info(` ${
|
|
8036
|
+
ui.info(` ${import_picocolors22.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
|
|
7277
8037
|
`);
|
|
7278
8038
|
process.exit(1);
|
|
7279
8039
|
}
|
|
@@ -7281,16 +8041,16 @@ var init_init2 = __esm(() => {
|
|
|
7281
8041
|
const alreadyRegistered = existing?.journal.projects[project];
|
|
7282
8042
|
const isRefresh = alreadyRegistered && args.refresh;
|
|
7283
8043
|
if (alreadyRegistered && !isRefresh) {
|
|
7284
|
-
ui.write(` ${
|
|
8044
|
+
ui.write(` ${import_picocolors22.default.yellow("\u26A0")} ${import_picocolors22.default.white("Project")} ${import_picocolors22.default.bold(project)} ${import_picocolors22.default.white("is already registered.")}
|
|
7285
8045
|
`);
|
|
7286
8046
|
ui.info(` Repo: ${existing.journal.repo}
|
|
7287
8047
|
`);
|
|
7288
|
-
ui.info(` To refresh journal files, use ${
|
|
8048
|
+
ui.info(` To refresh journal files, use ${import_picocolors22.default.dim("dora journal update")} (or ${import_picocolors22.default.dim("dora init --refresh")}).
|
|
7289
8049
|
`);
|
|
7290
8050
|
}
|
|
7291
8051
|
const journalsDir = getJournalsDir();
|
|
7292
8052
|
const remotePath = `projects/${project}.md`;
|
|
7293
|
-
const localPath =
|
|
8053
|
+
const localPath = join32(journalsDir, `${project}.md`);
|
|
7294
8054
|
const effectiveRepo = isRefresh && !args.repo ? existing.journal.repo : repo;
|
|
7295
8055
|
const config = existing ?? {
|
|
7296
8056
|
journal: { repo: effectiveRepo, projects: {} }
|
|
@@ -7301,9 +8061,9 @@ var init_init2 = __esm(() => {
|
|
|
7301
8061
|
local_path: localPath
|
|
7302
8062
|
};
|
|
7303
8063
|
ensureDoravalDirs();
|
|
7304
|
-
ui.write(` ${
|
|
8064
|
+
ui.write(` ${import_picocolors22.default.dim(import_picocolors22.default.gray("Fetching journal files from"))} ${import_picocolors22.default.gray(effectiveRepo)}${import_picocolors22.default.dim(import_picocolors22.default.gray("..."))}
|
|
7305
8065
|
`);
|
|
7306
|
-
const globalDest =
|
|
8066
|
+
const globalDest = join32(journalsDir, "global.md");
|
|
7307
8067
|
const refreshGlobalRes = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
|
|
7308
8068
|
let wroteGlobal;
|
|
7309
8069
|
if (!refreshGlobalRes.ok) {
|
|
@@ -7320,7 +8080,7 @@ var init_init2 = __esm(() => {
|
|
|
7320
8080
|
if (wroteGlobal) {
|
|
7321
8081
|
ui.success("global.md");
|
|
7322
8082
|
} else {
|
|
7323
|
-
ui.write(` ${
|
|
8083
|
+
ui.write(` ${import_picocolors22.default.dim("\xB7")} global.md ${import_picocolors22.default.dim("(not found \u2014 will be created on first sync)")}`);
|
|
7324
8084
|
await Bun.write(globalDest, `# Global Journal
|
|
7325
8085
|
|
|
7326
8086
|
Cross-project principles.
|
|
@@ -7342,7 +8102,7 @@ Cross-project principles.
|
|
|
7342
8102
|
if (wroteProject) {
|
|
7343
8103
|
ui.success(remotePath);
|
|
7344
8104
|
} else {
|
|
7345
|
-
ui.write(` ${
|
|
8105
|
+
ui.write(` ${import_picocolors22.default.dim("\xB7")} ${remotePath} ${import_picocolors22.default.dim("(not found \u2014 will be created on first sync)")}`);
|
|
7346
8106
|
await Bun.write(localPath, `# ${project} Journal
|
|
7347
8107
|
|
|
7348
8108
|
Project-specific decisions.
|
|
@@ -7350,13 +8110,13 @@ Project-specific decisions.
|
|
|
7350
8110
|
}
|
|
7351
8111
|
await writeConfig(config);
|
|
7352
8112
|
ui.write(`
|
|
7353
|
-
${
|
|
8113
|
+
${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.white("Journal ready for project")} ${import_picocolors22.default.bold(import_picocolors22.default.white(project))}.
|
|
7354
8114
|
`);
|
|
7355
8115
|
const existingAgent = (await readConfig())?.agent;
|
|
7356
8116
|
if (existingAgent?.command) {
|
|
7357
|
-
ui.write(` ${
|
|
8117
|
+
ui.write(` ${import_picocolors22.default.bold(import_picocolors22.default.white("Coding agent (already configured)"))}
|
|
7358
8118
|
`);
|
|
7359
|
-
ui.write(` Current: ${
|
|
8119
|
+
ui.write(` Current: ${import_picocolors22.default.dim(import_picocolors22.default.gray(existingAgent.command))} template: ${import_picocolors22.default.dim(import_picocolors22.default.gray(existingAgent.prompt_template || "(default)"))}
|
|
7360
8120
|
`);
|
|
7361
8121
|
const change = prompt(" Reconfigure / change the coding agent for on-the-fly enrichment? (y/N)", "n");
|
|
7362
8122
|
if (!/^y/i.test(String(change))) {
|
|
@@ -7366,16 +8126,16 @@ Project-specific decisions.
|
|
|
7366
8126
|
if (existingAgent)
|
|
7367
8127
|
cfg.agent = existingAgent;
|
|
7368
8128
|
await writeConfig(cfg);
|
|
7369
|
-
ui.write(` ${
|
|
8129
|
+
ui.write(` ${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.white("Try:")} ${import_picocolors22.default.dim(import_picocolors22.default.gray('dora journal add "short decision"'))}
|
|
7370
8130
|
`);
|
|
7371
8131
|
process.exit(0);
|
|
7372
8132
|
return;
|
|
7373
8133
|
}
|
|
7374
8134
|
ui.blank();
|
|
7375
8135
|
} else {
|
|
7376
|
-
ui.write(` ${
|
|
8136
|
+
ui.write(` ${import_picocolors22.default.bold(import_picocolors22.default.white("Coding agent for journal add"))}
|
|
7377
8137
|
`);
|
|
7378
|
-
ui.info(` When configured, ${
|
|
8138
|
+
ui.info(` When configured, ${import_picocolors22.default.dim(import_picocolors22.default.gray('dora journal add ".."'))} will use your agent to enrich entries with tags and rationale automatically.
|
|
7379
8139
|
`);
|
|
7380
8140
|
}
|
|
7381
8141
|
const common = [
|
|
@@ -7394,7 +8154,7 @@ Project-specific decisions.
|
|
|
7394
8154
|
}
|
|
7395
8155
|
}
|
|
7396
8156
|
let agentCmd = detected || "claude";
|
|
7397
|
-
ui.write(` Detected / default agent command: ${
|
|
8157
|
+
ui.write(` Detected / default agent command: ${import_picocolors22.default.dim(import_picocolors22.default.gray(agentCmd))}`);
|
|
7398
8158
|
agentCmd = prompt(" Agent command (the binary you run for prompts)", agentCmd);
|
|
7399
8159
|
let template = detected ? common.find((c) => c.name === detected)?.template || '-p "{{prompt}}" --output-format json' : '-p "{{prompt}}" --output-format json';
|
|
7400
8160
|
ui.info(` Prompt template (use {{prompt}} placeholder):`);
|
|
@@ -7406,11 +8166,36 @@ Project-specific decisions.
|
|
|
7406
8166
|
};
|
|
7407
8167
|
await writeConfig(finalConfig);
|
|
7408
8168
|
ui.write(`
|
|
7409
|
-
${
|
|
8169
|
+
${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.white("Agent configured.")}
|
|
7410
8170
|
`);
|
|
7411
|
-
ui.info(` Re-run ${
|
|
8171
|
+
ui.info(` Re-run ${import_picocolors22.default.dim(import_picocolors22.default.gray("dora init"))} anytime to change it.
|
|
8172
|
+
`);
|
|
8173
|
+
ui.write(`
|
|
8174
|
+
${import_picocolors22.default.bold("Step 3: Eval configuration (doraval eval)")}
|
|
7412
8175
|
`);
|
|
7413
|
-
|
|
8176
|
+
const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
|
|
8177
|
+
const hasOpenAI = !!process.env.OPENAI_API_KEY;
|
|
8178
|
+
if (hasAnthropic || hasOpenAI) {
|
|
8179
|
+
ui.success("API key found in environment \u2014 will be used for eval.");
|
|
8180
|
+
} else {
|
|
8181
|
+
ui.warn("No ANTHROPIC_API_KEY or OPENAI_API_KEY set. Set the right one before running doraval eval.");
|
|
8182
|
+
}
|
|
8183
|
+
const evalModelAnswer = await prompt(` Which model should doraval eval use? ${import_picocolors22.default.dim("(e.g. claude-sonnet-4-6 or gpt-4o, press Enter to skip)")} `, "");
|
|
8184
|
+
if (evalModelAnswer.trim()) {
|
|
8185
|
+
const updatedConfig2 = await readConfig();
|
|
8186
|
+
if (updatedConfig2) {
|
|
8187
|
+
updatedConfig2.eval = {
|
|
8188
|
+
model: evalModelAnswer.trim(),
|
|
8189
|
+
max_tool_calls: 200,
|
|
8190
|
+
save_history: true
|
|
8191
|
+
};
|
|
8192
|
+
await writeConfig(updatedConfig2);
|
|
8193
|
+
ui.success(`eval.model set to ${evalModelAnswer.trim()}`);
|
|
8194
|
+
}
|
|
8195
|
+
} else {
|
|
8196
|
+
ui.dim(" Skipped. Run: dora config set eval.model <model-name>");
|
|
8197
|
+
}
|
|
8198
|
+
ui.info(` Next: ${import_picocolors22.default.dim(import_picocolors22.default.gray('dora journal add ".."'))}, ${import_picocolors22.default.dim(import_picocolors22.default.gray("dora journal list"))}, or ${import_picocolors22.default.dim(import_picocolors22.default.gray("dora journal update"))}.
|
|
7414
8199
|
`);
|
|
7415
8200
|
process.exit(0);
|
|
7416
8201
|
}
|
|
@@ -7419,7 +8204,7 @@ Project-specific decisions.
|
|
|
7419
8204
|
|
|
7420
8205
|
// src/core/update.ts
|
|
7421
8206
|
import { resolve as resolve28 } from "path";
|
|
7422
|
-
import { homedir as
|
|
8207
|
+
import { homedir as homedir4 } from "os";
|
|
7423
8208
|
function normalizePath(p) {
|
|
7424
8209
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
7425
8210
|
}
|
|
@@ -7604,7 +8389,7 @@ async function writeMarker(marker) {
|
|
|
7604
8389
|
}
|
|
7605
8390
|
var MARKER_PATH;
|
|
7606
8391
|
var init_update2 = __esm(() => {
|
|
7607
|
-
MARKER_PATH = resolve28(
|
|
8392
|
+
MARKER_PATH = resolve28(homedir4(), ".doraval", "install.json");
|
|
7608
8393
|
});
|
|
7609
8394
|
|
|
7610
8395
|
// src/cli/commands/update.ts
|
|
@@ -7613,7 +8398,7 @@ __export(exports_update2, {
|
|
|
7613
8398
|
default: () => update_default2
|
|
7614
8399
|
});
|
|
7615
8400
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
7616
|
-
import { homedir as
|
|
8401
|
+
import { homedir as homedir5 } from "os";
|
|
7617
8402
|
import { fileURLToPath } from "url";
|
|
7618
8403
|
import { realpath, access } from "fs/promises";
|
|
7619
8404
|
async function confirmUpdate() {
|
|
@@ -7659,7 +8444,7 @@ var init_update3 = __esm(() => {
|
|
|
7659
8444
|
entrypoint,
|
|
7660
8445
|
argv: process.argv,
|
|
7661
8446
|
env: process.env,
|
|
7662
|
-
homeDir:
|
|
8447
|
+
homeDir: homedir5(),
|
|
7663
8448
|
realpath: (p) => realpath(p),
|
|
7664
8449
|
exists: async (p) => {
|
|
7665
8450
|
try {
|
|
@@ -7750,12 +8535,12 @@ var exports_providers = {};
|
|
|
7750
8535
|
__export(exports_providers, {
|
|
7751
8536
|
default: () => providers_default
|
|
7752
8537
|
});
|
|
7753
|
-
var
|
|
8538
|
+
var import_picocolors23, providers_default;
|
|
7754
8539
|
var init_providers2 = __esm(() => {
|
|
7755
8540
|
init_dist();
|
|
7756
8541
|
init_out();
|
|
7757
8542
|
init_spec();
|
|
7758
|
-
|
|
8543
|
+
import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
7759
8544
|
providers_default = defineCommand({
|
|
7760
8545
|
meta: {
|
|
7761
8546
|
name: "providers",
|
|
@@ -7780,7 +8565,7 @@ var init_providers2 = __esm(() => {
|
|
|
7780
8565
|
for (const id of supportedProviders) {
|
|
7781
8566
|
const spec = getProviderSpec(id);
|
|
7782
8567
|
ui.write(`
|
|
7783
|
-
${
|
|
8568
|
+
${import_picocolors23.default.bold(id)} \u2014 ${spec.name}`);
|
|
7784
8569
|
ui.info(` Manifest: ${spec.manifestPath}`);
|
|
7785
8570
|
ui.info(` Marketplace: ${spec.marketplacePath}`);
|
|
7786
8571
|
ui.info(` MCP: ${spec.mcpFilename}`);
|
|
@@ -7812,6 +8597,8 @@ var init_completion = __esm(() => {
|
|
|
7812
8597
|
"skill",
|
|
7813
8598
|
"journal",
|
|
7814
8599
|
"ui",
|
|
8600
|
+
"eval",
|
|
8601
|
+
"config",
|
|
7815
8602
|
"claude",
|
|
7816
8603
|
"codex",
|
|
7817
8604
|
"cursor",
|
|
@@ -7821,6 +8608,8 @@ var init_completion = __esm(() => {
|
|
|
7821
8608
|
subCommands = {
|
|
7822
8609
|
skill: ["validate", "drift", "judge"],
|
|
7823
8610
|
journal: ["init", "list", "context", "hook", "update", "add", "sync"],
|
|
8611
|
+
eval: ["history"],
|
|
8612
|
+
config: ["set", "get"],
|
|
7824
8613
|
hook: ["enable", "disable", "status"],
|
|
7825
8614
|
claude: ["new", "bump"],
|
|
7826
8615
|
codex: ["new", "bump"],
|
|
@@ -7855,6 +8644,8 @@ _doraval_completions() {
|
|
|
7855
8644
|
case "$prev" in
|
|
7856
8645
|
skill) COMPREPLY=( $(compgen -W "${subCommands.skill.join(" ")}" -- "$cur") ) ;;
|
|
7857
8646
|
journal) COMPREPLY=( $(compgen -W "${subCommands.journal.join(" ")}" -- "$cur") ) ;;
|
|
8647
|
+
eval) COMPREPLY=( $(compgen -W "${subCommands.eval.join(" ")}" -- "$cur") ) ;;
|
|
8648
|
+
config) COMPREPLY=( $(compgen -W "${subCommands.config.join(" ")}" -- "$cur") ) ;;
|
|
7858
8649
|
hook) COMPREPLY=( $(compgen -W "${subCommands.hook.join(" ")}" -- "$cur") ) ;;
|
|
7859
8650
|
ui) COMPREPLY=( $(compgen -W "${uiFlags.join(" ")}" -- "$cur") ) ;;
|
|
7860
8651
|
claude|codex|cursor|copilot) COMPREPLY=( $(compgen -W "${subCommands.claude.join(" ")}" -- "$cur") ) ;;
|
|
@@ -7869,7 +8660,7 @@ complete -F _doraval_completions doraval
|
|
|
7869
8660
|
|
|
7870
8661
|
_doraval() {
|
|
7871
8662
|
local -a commands sub
|
|
7872
|
-
commands=(validate init bump update providers skill journal ui claude codex cursor copilot)
|
|
8663
|
+
commands=(validate init bump update providers skill journal ui eval config claude codex cursor copilot)
|
|
7873
8664
|
_arguments -C \\
|
|
7874
8665
|
'1: :->cmd' \\
|
|
7875
8666
|
'*::arg:->args'
|
|
@@ -7886,6 +8677,12 @@ _doraval() {
|
|
|
7886
8677
|
journal)
|
|
7887
8678
|
_describe 'subcommand' (init list context hook update add sync)
|
|
7888
8679
|
;;
|
|
8680
|
+
eval)
|
|
8681
|
+
_describe 'subcommand' (history)
|
|
8682
|
+
;;
|
|
8683
|
+
config)
|
|
8684
|
+
_describe 'subcommand' (set get)
|
|
8685
|
+
;;
|
|
7889
8686
|
hook)
|
|
7890
8687
|
_describe 'subcommand' (enable disable status)
|
|
7891
8688
|
;;
|
|
@@ -7905,10 +8702,12 @@ _doraval "$@"
|
|
|
7905
8702
|
} else if (shell === "fish") {
|
|
7906
8703
|
console.log(`# doraval fish completion
|
|
7907
8704
|
complete -c doraval -f
|
|
7908
|
-
complete -c doraval -n '__fish_use_subcommand' -a 'validate init bump update providers skill journal ui claude codex cursor copilot'
|
|
8705
|
+
complete -c doraval -n '__fish_use_subcommand' -a 'validate init bump update providers skill journal ui eval config claude codex cursor copilot'
|
|
7909
8706
|
|
|
7910
8707
|
complete -c doraval -n '__fish_seen_subcommand_from skill' -a 'validate drift judge'
|
|
7911
8708
|
complete -c doraval -n '__fish_seen_subcommand_from journal' -a 'init list context hook update add sync'
|
|
8709
|
+
complete -c doraval -n '__fish_seen_subcommand_from eval' -a 'history'
|
|
8710
|
+
complete -c doraval -n '__fish_seen_subcommand_from config' -a 'set get'
|
|
7912
8711
|
complete -c doraval -n '__fish_seen_subcommand_from hook' -a 'enable disable status'
|
|
7913
8712
|
complete -c doraval -n '__fish_seen_subcommand_from ui' -l port -d 'Port'
|
|
7914
8713
|
complete -c doraval -n '__fish_seen_subcommand_from ui' -l open -d 'Open browser'
|
|
@@ -7930,7 +8729,7 @@ complete -c doraval -n '__fish_seen_subcommand_from claude codex cursor copilot'
|
|
|
7930
8729
|
// src/cli/index.ts
|
|
7931
8730
|
init_dist();
|
|
7932
8731
|
var import__package = __toESM(require_package(), 1);
|
|
7933
|
-
var
|
|
8732
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
7934
8733
|
var skill = defineCommand({
|
|
7935
8734
|
meta: {
|
|
7936
8735
|
name: "skill",
|
|
@@ -7969,6 +8768,20 @@ var journal = defineCommand({
|
|
|
7969
8768
|
showUsage(journal);
|
|
7970
8769
|
}
|
|
7971
8770
|
});
|
|
8771
|
+
var evalCmd = defineCommand({
|
|
8772
|
+
meta: {
|
|
8773
|
+
name: "eval",
|
|
8774
|
+
description: "Evaluate a coding agent session against skill instructions"
|
|
8775
|
+
},
|
|
8776
|
+
subCommands: {
|
|
8777
|
+
history: () => Promise.resolve().then(() => (init_eval_history(), exports_eval_history)).then((m) => m.default)
|
|
8778
|
+
},
|
|
8779
|
+
async run(ctx) {
|
|
8780
|
+
const evalMain = await Promise.resolve().then(() => (init_eval(), exports_eval)).then((m) => m.default);
|
|
8781
|
+
return evalMain.run?.(ctx);
|
|
8782
|
+
}
|
|
8783
|
+
});
|
|
8784
|
+
var config = () => Promise.resolve().then(() => (init_config(), exports_config)).then((m) => m.default);
|
|
7972
8785
|
var claude = defineCommand({
|
|
7973
8786
|
meta: {
|
|
7974
8787
|
name: "claude",
|
|
@@ -8085,7 +8898,7 @@ var main = defineCommand({
|
|
|
8085
8898
|
meta: {
|
|
8086
8899
|
name: "doraval",
|
|
8087
8900
|
version: import__package.default.version,
|
|
8088
|
-
description: "The context engineering toolkit for coding
|
|
8901
|
+
description: "The context engineering toolkit for coding agent orchestrators"
|
|
8089
8902
|
},
|
|
8090
8903
|
subCommands: {
|
|
8091
8904
|
validate: () => Promise.resolve().then(() => (init_validate_top(), exports_validate_top)).then((m) => m.default),
|
|
@@ -8096,6 +8909,8 @@ var main = defineCommand({
|
|
|
8096
8909
|
completion: () => Promise.resolve().then(() => (init_completion(), exports_completion)).then((m) => m.default),
|
|
8097
8910
|
skill: () => Promise.resolve(skill),
|
|
8098
8911
|
journal: () => Promise.resolve(journal),
|
|
8912
|
+
eval: () => Promise.resolve(evalCmd),
|
|
8913
|
+
config,
|
|
8099
8914
|
claude: () => Promise.resolve(claude),
|
|
8100
8915
|
codex: () => Promise.resolve(codex),
|
|
8101
8916
|
cursor: () => Promise.resolve(cursor),
|
|
@@ -8108,7 +8923,7 @@ var main = defineCommand({
|
|
|
8108
8923
|
return;
|
|
8109
8924
|
if (process.stdout.isTTY) {
|
|
8110
8925
|
console.error(`
|
|
8111
|
-
` +
|
|
8926
|
+
` + import_picocolors24.default.blue(doraemonArt) + `
|
|
8112
8927
|
`);
|
|
8113
8928
|
}
|
|
8114
8929
|
showUsage(main);
|