@hacksmith/doraval 0.2.17 → 0.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/doraval.js +368 -219
  2. package/package.json +2 -1
package/bin/doraval.js CHANGED
@@ -698,8 +698,8 @@ function parseFrontmatter(raw) {
698
698
  if (!match) {
699
699
  return { data: {}, content: raw };
700
700
  }
701
- const data = YAML.parse(match[1]);
702
- return { data: data ?? {}, content: match[2] };
701
+ const data = YAML.parse(match[1]) ?? {};
702
+ return { data, content: match[2] ?? "" };
703
703
  }
704
704
  var FRONTMATTER_RE;
705
705
  var init_frontmatter = __esm(() => {
@@ -1216,33 +1216,19 @@ function hasGhCli() {
1216
1216
  });
1217
1217
  return result.exitCode === 0;
1218
1218
  }
1219
- function ensureGhCliOrExit() {
1219
+ function ensureGhCli() {
1220
1220
  if (hasGhCli())
1221
- return;
1222
- ui.write(` ${import_picocolors4.default.red("\u2717")} ${import_picocolors4.default.white("The GitHub CLI (")}${import_picocolors4.default.bold("gh")}${import_picocolors4.default.white(") is not installed.")}
1223
- `);
1224
- ui.info(` doraval uses ${import_picocolors4.default.bold("gh")} to fetch and sync journal files with GitHub.
1225
- `);
1226
- ui.info(` Install it:
1227
- `);
1228
- ui.info(` macOS: ${import_picocolors4.default.dim("brew install gh")}`);
1229
- ui.info(` Linux: ${import_picocolors4.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
1230
- ui.info(` Windows: ${import_picocolors4.default.dim("winget install --id GitHub.cli")}
1231
- `);
1232
- ui.info(` Then authenticate: ${import_picocolors4.default.dim("gh auth login")}
1233
- `);
1234
- process.exit(1);
1221
+ return { ok: true, value: true };
1222
+ return { ok: false, error: "GH_CLI_MISSING" };
1235
1223
  }
1236
1224
  function fetchRemoteJournalFile(repo, path) {
1237
1225
  const result = spawnSync(["gh", "api", `repos/${repo}/contents/${path}`, "--jq", "{sha, content, encoding}"], { stdout: "pipe", stderr: "pipe" });
1238
1226
  if (result.exitCode !== 0) {
1239
1227
  const stderr = result.stderr.toString();
1240
1228
  if (stderr.includes("404") || stderr.includes("Not Found")) {
1241
- return null;
1229
+ return { ok: false, error: "not found", isNotFound: true };
1242
1230
  }
1243
- ui.fail(`Failed to fetch ${path} from ${repo}:`);
1244
- ui.info(stderr);
1245
- process.exit(1);
1231
+ return { ok: false, error: stderr };
1246
1232
  }
1247
1233
  try {
1248
1234
  const parsed = JSON.parse(result.stdout.toString());
@@ -1253,38 +1239,42 @@ function fetchRemoteJournalFile(repo, path) {
1253
1239
  decoded = parsed.content;
1254
1240
  }
1255
1241
  return {
1256
- content: decoded,
1257
- sha: parsed.sha
1242
+ ok: true,
1243
+ value: {
1244
+ content: decoded,
1245
+ sha: parsed.sha
1246
+ }
1258
1247
  };
1259
1248
  } catch {
1260
- ui.fail(`Unexpected response when fetching ${path} from ${repo}`);
1261
- process.exit(1);
1249
+ return { ok: false, error: `Unexpected response when fetching ${path} from ${repo}` };
1262
1250
  }
1263
1251
  }
1264
1252
  async function refreshLocalJournalFile(repo, remotePath, localPath) {
1265
- const remote = fetchRemoteJournalFile(repo, remotePath);
1266
- if (!remote) {
1267
- return false;
1253
+ const res = fetchRemoteJournalFile(repo, remotePath);
1254
+ if (!res.ok) {
1255
+ if (res.isNotFound) {
1256
+ return { ok: true, value: false };
1257
+ }
1258
+ return { ok: false, error: res.error };
1268
1259
  }
1260
+ const remote = res.value;
1269
1261
  await Bun.write(localPath, remote.content);
1270
- return true;
1262
+ return { ok: true, value: true };
1271
1263
  }
1272
1264
  function getRemoteJournalFileMeta(repo, path) {
1273
1265
  const result = spawnSync(["gh", "api", `repos/${repo}/contents/${path}`, "--jq", "{sha, content, encoding}"], { stdout: "pipe", stderr: "pipe" });
1274
1266
  if (result.exitCode !== 0) {
1275
1267
  const stderr = result.stderr.toString();
1276
1268
  if (stderr.includes("404") || stderr.includes("Not Found")) {
1277
- return null;
1269
+ return { ok: false, error: "not found", isNotFound: true };
1278
1270
  }
1279
- ui.fail(`Failed to fetch ${path} from ${repo}:`);
1280
- ui.info(stderr);
1281
- process.exit(1);
1271
+ return { ok: false, error: stderr };
1282
1272
  }
1283
1273
  try {
1284
- return JSON.parse(result.stdout.toString());
1274
+ const parsed = JSON.parse(result.stdout.toString());
1275
+ return { ok: true, value: parsed };
1285
1276
  } catch {
1286
- ui.fail(`Unexpected response when fetching ${path} from ${repo}`);
1287
- process.exit(1);
1277
+ return { ok: false, error: `Unexpected response when fetching ${path} from ${repo}` };
1288
1278
  }
1289
1279
  }
1290
1280
  function getGitRemoteOwner() {
@@ -1313,23 +1303,19 @@ function repoExists(repo) {
1313
1303
  const result = spawnSync(["gh", "api", `repos/${repo}`, "--jq", ".full_name"], { stdout: "pipe", stderr: "pipe" });
1314
1304
  return result.exitCode === 0 && result.stdout.toString().trim().length > 0;
1315
1305
  }
1316
- var import_picocolors4;
1317
- var init_journal_remote = __esm(() => {
1318
- init_out();
1319
- import_picocolors4 = __toESM(require_picocolors(), 1);
1320
- });
1306
+ var init_journal_remote = () => {};
1321
1307
 
1322
1308
  // src/cli/prompt.ts
1323
1309
  function prompt(label, fallback) {
1324
- process.stderr.write(`${label} ${import_picocolors5.default.dim(`(${fallback})`)} `);
1310
+ process.stderr.write(`${label} ${import_picocolors4.default.dim(`(${fallback})`)} `);
1325
1311
  const buf = new Uint8Array(1024);
1326
1312
  const n = __require("fs").readSync(0, buf);
1327
1313
  const input = new TextDecoder().decode(buf.subarray(0, n)).trim();
1328
1314
  return input || fallback;
1329
1315
  }
1330
- var import_picocolors5;
1316
+ var import_picocolors4;
1331
1317
  var init_prompt = __esm(() => {
1332
- import_picocolors5 = __toESM(require_picocolors(), 1);
1318
+ import_picocolors4 = __toESM(require_picocolors(), 1);
1333
1319
  });
1334
1320
 
1335
1321
  // src/cli/commands/journal/init.ts
@@ -1338,14 +1324,14 @@ __export(exports_init, {
1338
1324
  default: () => init_default
1339
1325
  });
1340
1326
  import { basename, join as join2 } from "path";
1341
- var import_picocolors6, init_default;
1327
+ var import_picocolors5, init_default;
1342
1328
  var init_init = __esm(() => {
1343
1329
  init_dist();
1344
1330
  init_out();
1345
1331
  init_journal_config();
1346
1332
  init_journal_remote();
1347
1333
  init_prompt();
1348
- import_picocolors6 = __toESM(require_picocolors(), 1);
1334
+ import_picocolors5 = __toESM(require_picocolors(), 1);
1349
1335
  init_default = defineCommand({
1350
1336
  meta: {
1351
1337
  name: "init",
@@ -1370,9 +1356,24 @@ var init_init = __esm(() => {
1370
1356
  },
1371
1357
  async run({ args }) {
1372
1358
  ui.write(`
1373
- ${import_picocolors6.default.bold(import_picocolors6.default.white("dora journal init"))} (or top-level ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora init"))}) \u2014 Set up your journal
1359
+ ${import_picocolors5.default.bold(import_picocolors5.default.white("dora journal init"))} (or top-level ${import_picocolors5.default.dim(import_picocolors5.default.gray("dora init"))}) \u2014 Set up your journal
1360
+ `);
1361
+ const ghCheck = ensureGhCli();
1362
+ if (!ghCheck.ok) {
1363
+ ui.write(` ${import_picocolors5.default.red("\u2717")} ${import_picocolors5.default.white("The GitHub CLI (")}${import_picocolors5.default.bold("gh")}${import_picocolors5.default.white(") is not installed.")}
1364
+ `);
1365
+ ui.write(` doraval uses ${import_picocolors5.default.bold("gh")} to fetch and sync journal files with GitHub.
1366
+ `);
1367
+ ui.write(` Install it:
1374
1368
  `);
1375
- ensureGhCliOrExit();
1369
+ ui.write(` macOS: ${import_picocolors5.default.dim("brew install gh")}`);
1370
+ ui.write(` Linux: ${import_picocolors5.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
1371
+ ui.write(` Windows: ${import_picocolors5.default.dim("winget install --id GitHub.cli")}
1372
+ `);
1373
+ ui.write(` Then authenticate: ${import_picocolors5.default.dim("gh auth login")}
1374
+ `);
1375
+ process.exit(1);
1376
+ }
1376
1377
  let repo = args.repo || process.env.DORAVAL_JOURNAL_REPO;
1377
1378
  if (!repo) {
1378
1379
  const gitOwner = getGitRemoteOwner();
@@ -1382,28 +1383,28 @@ var init_init = __esm(() => {
1382
1383
  if (gitOwner) {
1383
1384
  defaultRepo = `${gitOwner}/${gitOwner}.md`;
1384
1385
  if (ghLogin && ghLogin !== gitOwner) {
1385
- sourceNote = ` ${import_picocolors6.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
1386
+ sourceNote = ` ${import_picocolors5.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
1386
1387
  `;
1387
1388
  } else {
1388
- sourceNote = ` ${import_picocolors6.default.dim("(from git remote)")}
1389
+ sourceNote = ` ${import_picocolors5.default.dim("(from git remote)")}
1389
1390
  `;
1390
1391
  }
1391
1392
  } else if (ghLogin) {
1392
1393
  defaultRepo = `${ghLogin}/${ghLogin}.md`;
1393
- sourceNote = ` ${import_picocolors6.default.dim("(from your active gh account)")}
1394
+ sourceNote = ` ${import_picocolors5.default.dim("(from your active gh account)")}
1394
1395
  `;
1395
1396
  } else {
1396
- ui.write(` ${import_picocolors6.default.yellow("\u26A0")} Not logged in to GitHub. Run ${import_picocolors6.default.dim("gh auth login")} first.
1397
+ ui.write(` ${import_picocolors5.default.yellow("\u26A0")} Not logged in to GitHub. Run ${import_picocolors5.default.dim("gh auth login")} first.
1397
1398
  `);
1398
1399
  process.exit(1);
1399
1400
  }
1400
1401
  const existingConfig = await readConfig();
1401
1402
  if (existingConfig?.journal.repo) {
1402
1403
  defaultRepo = existingConfig.journal.repo;
1403
- sourceNote = ` ${import_picocolors6.default.dim("(from your previous journal setup)")}
1404
+ sourceNote = ` ${import_picocolors5.default.dim("(from your previous journal setup)")}
1404
1405
  `;
1405
1406
  }
1406
- ui.write(` Journal repo ${import_picocolors6.default.dim(import_picocolors6.default.gray("(owner/name)"))}`);
1407
+ ui.write(` Journal repo ${import_picocolors5.default.dim(import_picocolors5.default.gray("(owner/name)"))}`);
1407
1408
  if (sourceNote)
1408
1409
  ui.write(sourceNote);
1409
1410
  repo = prompt(" >", defaultRepo);
@@ -1415,13 +1416,13 @@ var init_init = __esm(() => {
1415
1416
  }
1416
1417
  project = sanitizeProjectName(project);
1417
1418
  if (!repoExists(repo)) {
1418
- ui.write(` ${import_picocolors6.default.red("\u2717")} Repository ${import_picocolors6.default.bold(import_picocolors6.default.white(repo))} not found on GitHub.
1419
+ ui.write(` ${import_picocolors5.default.red("\u2717")} Repository ${import_picocolors5.default.bold(import_picocolors5.default.white(repo))} not found on GitHub.
1419
1420
  `);
1420
1421
  ui.write(` Create it first:
1421
1422
  `);
1422
- ui.write(` ${import_picocolors6.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
1423
+ ui.write(` ${import_picocolors5.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
1423
1424
  `);
1424
- ui.write(` The repo should be private. doraval will populate it on first ${import_picocolors6.default.dim("dora journal sync")}.
1425
+ ui.write(` The repo should be private. doraval will populate it on first ${import_picocolors5.default.dim("dora journal sync")}.
1425
1426
  `);
1426
1427
  process.exit(1);
1427
1428
  }
@@ -1429,14 +1430,14 @@ var init_init = __esm(() => {
1429
1430
  const alreadyRegistered = existing?.journal.projects[project];
1430
1431
  const isRefresh = alreadyRegistered && args.refresh;
1431
1432
  if (alreadyRegistered && !isRefresh) {
1432
- ui.write(` ${import_picocolors6.default.yellow("\u26A0")} Project ${import_picocolors6.default.bold(import_picocolors6.default.white(project))} is already registered.
1433
+ ui.write(` ${import_picocolors5.default.yellow("\u26A0")} Project ${import_picocolors5.default.bold(import_picocolors5.default.white(project))} is already registered.
1433
1434
  `);
1434
- ui.write(` Repo: ${import_picocolors6.default.gray(existing.journal.repo)}`);
1435
- ui.write(` Remote: ${existing.journal.projects[project].remote_path}
1435
+ ui.write(` Repo: ${import_picocolors5.default.gray(existing.journal.repo)}`);
1436
+ ui.write(` Remote: ${existing.journal.projects[project]?.remote_path}
1436
1437
  `);
1437
- ui.write(` To refresh local files, run: ${import_picocolors6.default.dim(import_picocolors6.default.gray(`dora journal update`))}
1438
+ ui.write(` To refresh local files, run: ${import_picocolors5.default.dim(import_picocolors5.default.gray(`dora journal update`))}
1438
1439
  ` + ` (init --refresh still works for compatibility.)
1439
- ` + ` Or remove the project from ${import_picocolors6.default.dim(import_picocolors6.default.gray("~/.doraval/config.yml"))} to fully re-initialize.
1440
+ ` + ` Or remove the project from ${import_picocolors5.default.dim(import_picocolors5.default.gray("~/.doraval/config.yml"))} to fully re-initialize.
1440
1441
  `);
1441
1442
  process.exit(0);
1442
1443
  }
@@ -1454,24 +1455,48 @@ var init_init = __esm(() => {
1454
1455
  };
1455
1456
  ensureDoravalDirs();
1456
1457
  const actionLabel = isRefresh ? "Refreshing" : "Fetching";
1457
- ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray(`${actionLabel} journal files from`))} ${import_picocolors6.default.gray(effectiveRepo)}${import_picocolors6.default.dim(import_picocolors6.default.gray("..."))}
1458
+ ui.write(` ${import_picocolors5.default.dim(import_picocolors5.default.gray(`${actionLabel} journal files from`))} ${import_picocolors5.default.gray(effectiveRepo)}${import_picocolors5.default.dim(import_picocolors5.default.gray("..."))}
1458
1459
  `);
1459
1460
  const globalDest = join2(journalsDir, "global.md");
1460
- const wroteGlobal = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
1461
+ const refreshGlobalRes = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
1462
+ let wroteGlobal;
1463
+ if (!refreshGlobalRes.ok) {
1464
+ if (refreshGlobalRes.isNotFound) {
1465
+ wroteGlobal = false;
1466
+ } else {
1467
+ ui.write(` ${import_picocolors5.default.red("\u2717")} Failed to fetch global.md from ${effectiveRepo}:`);
1468
+ ui.write(refreshGlobalRes.error);
1469
+ process.exit(1);
1470
+ }
1471
+ } else {
1472
+ wroteGlobal = refreshGlobalRes.value;
1473
+ }
1461
1474
  if (wroteGlobal) {
1462
- ui.write(` ${import_picocolors6.default.green("\u2713")} global.md`);
1475
+ ui.write(` ${import_picocolors5.default.green("\u2713")} global.md`);
1463
1476
  } else {
1464
- ui.write(` ${import_picocolors6.default.dim("\xB7")} global.md ${import_picocolors6.default.dim("(not found \u2014 will be created on first sync)")}`);
1477
+ ui.write(` ${import_picocolors5.default.dim("\xB7")} global.md ${import_picocolors5.default.dim("(not found \u2014 will be created on first sync)")}`);
1465
1478
  await Bun.write(globalDest, `# Global Journal
1466
1479
 
1467
1480
  Cross-project principles.
1468
1481
  `);
1469
1482
  }
1470
- const wroteProject = await refreshLocalJournalFile(effectiveRepo, remotePath, localPath);
1483
+ const refreshProjectRes = await refreshLocalJournalFile(effectiveRepo, remotePath, localPath);
1484
+ let wroteProject;
1485
+ if (!refreshProjectRes.ok) {
1486
+ if (refreshProjectRes.isNotFound) {
1487
+ wroteProject = false;
1488
+ } else {
1489
+ ui.write(` ${import_picocolors5.default.red("\u2717")} Failed to fetch ${remotePath} from ${effectiveRepo}:`);
1490
+ ui.write(refreshProjectRes.error);
1491
+ process.exit(1);
1492
+ }
1493
+ } else {
1494
+ wroteProject = refreshProjectRes.value;
1495
+ }
1471
1496
  if (wroteProject) {
1472
- ui.write(` ${import_picocolors6.default.green("\u2713")} ${remotePath}`);
1497
+ ui.write(` ${import_picocolors5.default.green("\u2713")} ${remotePath}`);
1473
1498
  } else {
1474
- ui.write(` ${import_picocolors6.default.dim("\xB7")} ${remotePath} ${import_picocolors6.default.dim("(not found \u2014 will be created on first sync)")}`);
1499
+ ui.write(` ${import_picocolors5.default.dim("\xB7")} ${remotePath} ${import_picocolors5.default.dim("(not found \u2014 will be created on first sync)")}`);
1475
1500
  await Bun.write(localPath, `# ${project} Journal
1476
1501
 
1477
1502
  Project-specific decisions.
@@ -1479,13 +1504,13 @@ Project-specific decisions.
1479
1504
  }
1480
1505
  await writeConfig(config);
1481
1506
  ui.write(`
1482
- ${import_picocolors6.default.green("\u2713")} Project ${import_picocolors6.default.bold(import_picocolors6.default.white(project))} registered to ${import_picocolors6.default.bold(import_picocolors6.default.white(repo))}.
1507
+ ${import_picocolors5.default.green("\u2713")} Project ${import_picocolors5.default.bold(import_picocolors5.default.white(project))} registered to ${import_picocolors5.default.bold(import_picocolors5.default.white(repo))}.
1483
1508
  `);
1484
- ui.write(` Config: ${import_picocolors6.default.dim(import_picocolors6.default.gray("~/.doraval/config.yml"))}`);
1485
- ui.write(` Journals: ${import_picocolors6.default.dim(import_picocolors6.default.gray("~/.doraval/journals/"))}`);
1486
- ui.write(` Pending: ${import_picocolors6.default.dim(import_picocolors6.default.gray("~/.doraval/pending/"))}
1509
+ ui.write(` Config: ${import_picocolors5.default.dim(import_picocolors5.default.gray("~/.doraval/config.yml"))}`);
1510
+ ui.write(` Journals: ${import_picocolors5.default.dim(import_picocolors5.default.gray("~/.doraval/journals/"))}`);
1511
+ ui.write(` Pending: ${import_picocolors5.default.dim(import_picocolors5.default.gray("~/.doraval/pending/"))}
1487
1512
  `);
1488
- ui.write(` Use ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora journal add"))} to propose decisions and ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora journal list"))} to view them.
1513
+ ui.write(` Use ${import_picocolors5.default.dim(import_picocolors5.default.gray("dora journal add"))} to propose decisions and ${import_picocolors5.default.dim(import_picocolors5.default.gray("dora journal list"))} to view them.
1489
1514
  `);
1490
1515
  process.exit(0);
1491
1516
  }
@@ -1536,8 +1561,8 @@ function parseJournalEntriesWithWarnings(raw) {
1536
1561
  const pushback = Number(meta.pushback);
1537
1562
  const tags = Array.isArray(meta.tags) ? meta.tags : Array.isArray(meta.scope) ? meta.scope : [];
1538
1563
  const author = typeof meta.author === "string" ? meta.author : "human";
1539
- const date = typeof meta.date === "string" ? meta.date : "";
1540
- const status = meta.status || "active";
1564
+ const date = (typeof meta.date === "string" ? meta.date : "") ?? "";
1565
+ const status = (meta.status || "active") ?? "active";
1541
1566
  const superseded_by = typeof meta.superseded_by === "string" ? meta.superseded_by : undefined;
1542
1567
  entries.push({
1543
1568
  title,
@@ -1561,13 +1586,13 @@ __export(exports_list, {
1561
1586
  });
1562
1587
  import { existsSync as existsSync4, readdirSync } from "fs";
1563
1588
  import { join as join3 } from "path";
1564
- var import_picocolors7, list_default;
1589
+ var import_picocolors6, list_default;
1565
1590
  var init_list = __esm(() => {
1566
1591
  init_dist();
1567
1592
  init_out();
1568
1593
  init_journal_config();
1569
1594
  init_journal_parse();
1570
- import_picocolors7 = __toESM(require_picocolors(), 1);
1595
+ import_picocolors6 = __toESM(require_picocolors(), 1);
1571
1596
  list_default = defineCommand({
1572
1597
  meta: {
1573
1598
  name: "list",
@@ -1601,9 +1626,9 @@ var init_list = __esm(() => {
1601
1626
  project = sanitizeProjectName(project);
1602
1627
  }
1603
1628
  if (!project) {
1604
- ui.write(`${import_picocolors7.default.yellow("\u26A0")} ${import_picocolors7.default.yellow("No project mapping found.")}
1629
+ ui.write(`${import_picocolors6.default.yellow("\u26A0")} ${import_picocolors6.default.yellow("No project mapping found.")}
1605
1630
 
1606
- ` + `Run ${import_picocolors7.default.dim(import_picocolors7.default.gray("dora init"))} (or ${import_picocolors7.default.dim(import_picocolors7.default.gray("doraval journal init"))}) first, or pass ${import_picocolors7.default.dim(import_picocolors7.default.gray("--project <name>"))}.`);
1631
+ ` + `Run ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora init"))} (or ${import_picocolors6.default.dim(import_picocolors6.default.gray("doraval journal init"))}) first, or pass ${import_picocolors6.default.dim(import_picocolors6.default.gray("--project <name>"))}.`);
1607
1632
  process.exit(1);
1608
1633
  }
1609
1634
  const journalRepo = config?.journal.repo ?? "(unknown)";
@@ -1640,7 +1665,7 @@ var init_list = __esm(() => {
1640
1665
  return;
1641
1666
  }
1642
1667
  ui.write(`
1643
- ${import_picocolors7.default.bold(import_picocolors7.default.white("dora journal list"))} \u2014 ${import_picocolors7.default.white(project)} ${import_picocolors7.default.dim(import_picocolors7.default.gray(`(from ${journalRepo})`))}
1668
+ ${import_picocolors6.default.bold(import_picocolors6.default.white("dora journal list"))} \u2014 ${import_picocolors6.default.white(project)} ${import_picocolors6.default.dim(import_picocolors6.default.gray(`(from ${journalRepo})`))}
1644
1669
  `);
1645
1670
  const hasStaged = staged.length > 0;
1646
1671
  const hasCommitted = allEntries.length > 0;
@@ -1654,46 +1679,46 @@ var init_list = __esm(() => {
1654
1679
  }
1655
1680
  if (dups.length > 0) {
1656
1681
  const uniqueDups = [...new Set(dups)];
1657
- ui.write(` ${import_picocolors7.default.yellow("\u26A0")} ${import_picocolors7.default.yellow("Duplicate titles in this view (clean in your journal repo + update):")} ${uniqueDups.map((t) => import_picocolors7.default.yellow(`"${t}"`)).join(", ")}
1682
+ ui.write(` ${import_picocolors6.default.yellow("\u26A0")} ${import_picocolors6.default.yellow("Duplicate titles in this view (clean in your journal repo + update):")} ${uniqueDups.map((t) => import_picocolors6.default.yellow(`"${t}"`)).join(", ")}
1658
1683
  `);
1659
1684
  }
1660
1685
  if (!hasStaged && !hasCommitted) {
1661
- ui.write(` ${import_picocolors7.default.dim(import_picocolors7.default.gray("No active entries found for"))} ${import_picocolors7.default.bold(import_picocolors7.default.white(project))}.
1686
+ ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("No active entries found for"))} ${import_picocolors6.default.bold(import_picocolors6.default.white(project))}.
1662
1687
  `);
1663
- ui.write(` Journal repo: ${import_picocolors7.default.dim(import_picocolors7.default.gray(journalRepo))}`);
1664
- ui.write(` Local file: ${import_picocolors7.default.dim(import_picocolors7.default.gray(projectFile))}
1688
+ ui.write(` Journal repo: ${import_picocolors6.default.dim(import_picocolors6.default.gray(journalRepo))}`);
1689
+ ui.write(` Local file: ${import_picocolors6.default.dim(import_picocolors6.default.gray(projectFile))}
1665
1690
  `);
1666
- ui.write(` ${import_picocolors7.default.dim(import_picocolors7.default.gray("This is normal for a freshly initialized project."))}
1667
- ` + ` Use ${import_picocolors7.default.dim(import_picocolors7.default.gray("dora journal add"))} to propose decisions.
1668
- ` + ` They will be staged locally until you run ${import_picocolors7.default.dim(import_picocolors7.default.gray("dora journal sync"))}.
1691
+ ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("This is normal for a freshly initialized project."))}
1692
+ ` + ` Use ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora journal add"))} to propose decisions.
1693
+ ` + ` They will be staged locally until you run ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora journal sync"))}.
1669
1694
  `);
1670
- ui.write(` If you expect content, try: ${import_picocolors7.default.dim(import_picocolors7.default.gray(`dora journal update`))}
1695
+ ui.write(` If you expect content, try: ${import_picocolors6.default.dim(import_picocolors6.default.gray(`dora journal update`))}
1671
1696
  `);
1672
1697
  return;
1673
1698
  }
1674
1699
  function printEntry(entry) {
1675
1700
  const pb = entry.pushback ?? 0;
1676
- let pbColor = import_picocolors7.default.green;
1701
+ let pbColor = import_picocolors6.default.green;
1677
1702
  if (pb >= 7)
1678
- pbColor = import_picocolors7.default.red;
1703
+ pbColor = import_picocolors6.default.red;
1679
1704
  else if (pb >= 4)
1680
- pbColor = import_picocolors7.default.yellow;
1681
- const tagsStr = (entry.tags || []).join(", ") || import_picocolors7.default.dim("(none)");
1682
- const statusNote = entry.status !== "active" ? import_picocolors7.default.dim(` [${entry.status}]`) : "";
1683
- const stagedNote = entry._staged ? import_picocolors7.default.dim(" (staged)") : "";
1684
- ui.write(` ${pbColor(String(pb).padStart(2))} ${import_picocolors7.default.bold(import_picocolors7.default.white(entry.title))}${statusNote}${stagedNote}`);
1685
- ui.write(` ${import_picocolors7.default.dim(import_picocolors7.default.gray("tags:"))} ${import_picocolors7.default.gray(tagsStr)}`);
1686
- const by = entry.author?.startsWith("agent:") ? import_picocolors7.default.cyan(entry.author) : entry.author || "human";
1687
- ui.write(` ${import_picocolors7.default.dim(import_picocolors7.default.gray("by:"))} ${import_picocolors7.default.gray(by)} ${import_picocolors7.default.dim(import_picocolors7.default.gray("on"))} ${import_picocolors7.default.gray(entry.date)}`);
1705
+ pbColor = import_picocolors6.default.yellow;
1706
+ const tagsStr = (entry.tags || []).join(", ") || import_picocolors6.default.dim("(none)");
1707
+ const statusNote = entry.status !== "active" ? import_picocolors6.default.dim(` [${entry.status}]`) : "";
1708
+ const stagedNote = entry._staged ? import_picocolors6.default.dim(" (staged)") : "";
1709
+ ui.write(` ${pbColor(String(pb).padStart(2))} ${import_picocolors6.default.bold(import_picocolors6.default.white(entry.title))}${statusNote}${stagedNote}`);
1710
+ ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("tags:"))} ${import_picocolors6.default.gray(tagsStr)}`);
1711
+ const by = entry.author?.startsWith("agent:") ? import_picocolors6.default.cyan(entry.author) : entry.author || "human";
1712
+ ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("by:"))} ${import_picocolors6.default.gray(by)} ${import_picocolors6.default.dim(import_picocolors6.default.gray("on"))} ${import_picocolors6.default.gray(entry.date)}`);
1688
1713
  const rat = (entry.rationale || "").replace(/\s+/g, " ").trim();
1689
1714
  if (rat) {
1690
- const preview = rat.length > 88 ? rat.slice(0, 85) + import_picocolors7.default.dim(import_picocolors7.default.gray("\u2026")) : rat;
1691
- ui.write(` ${import_picocolors7.default.dim(import_picocolors7.default.gray(preview))}`);
1715
+ const preview = rat.length > 88 ? rat.slice(0, 85) + import_picocolors6.default.dim(import_picocolors6.default.gray("\u2026")) : rat;
1716
+ ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray(preview))}`);
1692
1717
  }
1693
1718
  ui.write("");
1694
1719
  }
1695
1720
  if (hasStaged) {
1696
- ui.write(` ${import_picocolors7.default.yellow("\u25CF")} ${import_picocolors7.default.bold(import_picocolors7.default.white("Staged / pending"))} (not yet in remote; run ${import_picocolors7.default.dim(import_picocolors7.default.gray("dora journal sync"))} to publish):
1721
+ ui.write(` ${import_picocolors6.default.yellow("\u25CF")} ${import_picocolors6.default.bold(import_picocolors6.default.white("Staged / pending"))} (not yet in remote; run ${import_picocolors6.default.dim(import_picocolors6.default.gray("dora journal sync"))} to publish):
1697
1722
  `);
1698
1723
  for (const entry of staged) {
1699
1724
  printEntry(entry);
@@ -1703,7 +1728,7 @@ var init_list = __esm(() => {
1703
1728
  }
1704
1729
  if (hasCommitted) {
1705
1730
  if (hasStaged) {
1706
- ui.write(` ${import_picocolors7.default.dim(import_picocolors7.default.gray("Committed (from local cache):"))}
1731
+ ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray("Committed (from local cache):"))}
1707
1732
  `);
1708
1733
  }
1709
1734
  for (const entry of allEntries) {
@@ -1711,7 +1736,7 @@ var init_list = __esm(() => {
1711
1736
  }
1712
1737
  }
1713
1738
  const totalShown = staged.length + allEntries.length;
1714
- ui.write(` ${import_picocolors7.default.dim(import_picocolors7.default.gray(`${totalShown} entries shown from ${journalRepo}.`))}
1739
+ ui.write(` ${import_picocolors6.default.dim(import_picocolors6.default.gray(`${totalShown} entries shown from ${journalRepo}.`))}
1715
1740
  `);
1716
1741
  process.exit(0);
1717
1742
  }
@@ -1725,13 +1750,13 @@ __export(exports_update, {
1725
1750
  });
1726
1751
  import { existsSync as existsSync5 } from "fs";
1727
1752
  import { join as join4 } from "path";
1728
- var import_picocolors8, update_default;
1753
+ var import_picocolors7, update_default;
1729
1754
  var init_update = __esm(() => {
1730
1755
  init_dist();
1731
1756
  init_out();
1732
1757
  init_journal_config();
1733
1758
  init_journal_remote();
1734
- import_picocolors8 = __toESM(require_picocolors(), 1);
1759
+ import_picocolors7 = __toESM(require_picocolors(), 1);
1735
1760
  update_default = defineCommand({
1736
1761
  meta: {
1737
1762
  name: "update",
@@ -1750,17 +1775,32 @@ var init_update = __esm(() => {
1750
1775
  }
1751
1776
  },
1752
1777
  async run({ args }) {
1753
- ensureGhCliOrExit();
1778
+ const ghCheck = ensureGhCli();
1779
+ if (!ghCheck.ok) {
1780
+ ui.write(` ${import_picocolors7.default.red("\u2717")} ${import_picocolors7.default.white("The GitHub CLI (")}${import_picocolors7.default.bold("gh")}${import_picocolors7.default.white(") is not installed.")}
1781
+ `);
1782
+ ui.write(` doraval uses ${import_picocolors7.default.bold("gh")} to fetch and sync journal files with GitHub.
1783
+ `);
1784
+ ui.write(` Install it:
1785
+ `);
1786
+ ui.write(` macOS: ${import_picocolors7.default.dim("brew install gh")}`);
1787
+ ui.write(` Linux: ${import_picocolors7.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
1788
+ ui.write(` Windows: ${import_picocolors7.default.dim("winget install --id GitHub.cli")}
1789
+ `);
1790
+ ui.write(` Then authenticate: ${import_picocolors7.default.dim("gh auth login")}
1791
+ `);
1792
+ process.exit(1);
1793
+ }
1754
1794
  const config = await readConfig();
1755
1795
  if (!config?.journal.repo) {
1756
- ui.write(`${import_picocolors8.default.red("\u2717")} No journal repo configured. Run ${import_picocolors8.default.dim("dora init")} (or ${import_picocolors8.default.dim("doraval journal init")}) first.`);
1796
+ ui.write(`${import_picocolors7.default.red("\u2717")} No journal repo configured. Run ${import_picocolors7.default.dim("dora init")} (or ${import_picocolors7.default.dim("doraval journal init")}) first.`);
1757
1797
  process.exit(1);
1758
1798
  }
1759
1799
  const journalRepo = config.journal.repo;
1760
1800
  ensureDoravalDirs();
1761
1801
  const journalsDir = getJournalsDir();
1762
1802
  ui.write(`
1763
- ${import_picocolors8.default.bold(import_picocolors8.default.white("dora journal update"))} \u2014 ${import_picocolors8.default.dim(import_picocolors8.default.gray(journalRepo))}
1803
+ ${import_picocolors7.default.bold(import_picocolors7.default.white("dora journal update"))} \u2014 ${import_picocolors7.default.dim(import_picocolors7.default.gray(journalRepo))}
1764
1804
  `);
1765
1805
  const projectsToUpdate = [];
1766
1806
  if (args.all) {
@@ -1778,27 +1818,39 @@ var init_update = __esm(() => {
1778
1818
  try {
1779
1819
  projectsToUpdate.push(sanitizeProjectName(project));
1780
1820
  } catch {
1781
- ui.write(`${import_picocolors8.default.red("\u2717")} Invalid project name: ${project}`);
1821
+ ui.write(`${import_picocolors7.default.red("\u2717")} Invalid project name: ${project}`);
1782
1822
  process.exit(1);
1783
1823
  }
1784
1824
  }
1785
1825
  }
1786
1826
  const globalLocal = join4(journalsDir, "global.md");
1787
- const gotGlobal = await refreshLocalJournalFile(journalRepo, "global.md", globalLocal);
1827
+ const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md", globalLocal);
1828
+ let gotGlobal;
1829
+ if (!refreshGlobalRes.ok) {
1830
+ if (refreshGlobalRes.isNotFound) {
1831
+ gotGlobal = false;
1832
+ } else {
1833
+ ui.write(`${import_picocolors7.default.red("\u2717")} Failed to fetch global.md from ${journalRepo}:`);
1834
+ ui.write(refreshGlobalRes.error);
1835
+ process.exit(1);
1836
+ }
1837
+ } else {
1838
+ gotGlobal = refreshGlobalRes.value;
1839
+ }
1788
1840
  if (gotGlobal) {
1789
- ui.write(` ${import_picocolors8.default.green("\u2713")} global.md`);
1841
+ ui.write(` ${import_picocolors7.default.green("\u2713")} global.md`);
1790
1842
  } else {
1791
- ui.write(` ${import_picocolors8.default.dim("\xB7")} global.md ${import_picocolors8.default.dim("(not present on remote)")}`);
1843
+ ui.write(` ${import_picocolors7.default.dim("\xB7")} global.md ${import_picocolors7.default.dim("(not present on remote)")}`);
1792
1844
  }
1793
1845
  if (projectsToUpdate.length === 0) {
1794
1846
  if (args.all) {
1795
1847
  ui.write(`
1796
- ${import_picocolors8.default.dim(import_picocolors8.default.gray("No projects registered."))}
1848
+ ${import_picocolors7.default.dim(import_picocolors7.default.gray("No projects registered."))}
1797
1849
  `);
1798
1850
  } else {
1799
1851
  ui.write(`
1800
- ${import_picocolors8.default.yellow("\u26A0")} No project mapping found.
1801
- ` + ` Run ${import_picocolors8.default.dim("dora init")} or pass ${import_picocolors8.default.dim("--project <name>")} / ${import_picocolors8.default.dim("--all")}.
1852
+ ${import_picocolors7.default.yellow("\u26A0")} No project mapping found.
1853
+ ` + ` Run ${import_picocolors7.default.dim("dora init")} or pass ${import_picocolors7.default.dim("--project <name>")} / ${import_picocolors7.default.dim("--all")}.
1802
1854
  `);
1803
1855
  }
1804
1856
  return;
@@ -1806,11 +1858,23 @@ var init_update = __esm(() => {
1806
1858
  for (const project of projectsToUpdate) {
1807
1859
  const remotePath = `projects/${project}.md`;
1808
1860
  const localPath = join4(journalsDir, `${project}.md`);
1809
- const got = await refreshLocalJournalFile(journalRepo, remotePath, localPath);
1861
+ const refreshRes = await refreshLocalJournalFile(journalRepo, remotePath, localPath);
1862
+ let got;
1863
+ if (!refreshRes.ok) {
1864
+ if (refreshRes.isNotFound) {
1865
+ got = false;
1866
+ } else {
1867
+ ui.write(`${import_picocolors7.default.red("\u2717")} Failed to fetch ${remotePath} from ${journalRepo}:`);
1868
+ ui.write(refreshRes.error);
1869
+ process.exit(1);
1870
+ }
1871
+ } else {
1872
+ got = refreshRes.value;
1873
+ }
1810
1874
  if (got) {
1811
- ui.write(` ${import_picocolors8.default.green("\u2713")} ${remotePath}`);
1875
+ ui.write(` ${import_picocolors7.default.green("\u2713")} ${remotePath}`);
1812
1876
  } else {
1813
- ui.write(` ${import_picocolors8.default.dim("\xB7")} ${remotePath} ${import_picocolors8.default.dim("(not present on remote \u2014 will be created on first sync)")}`);
1877
+ ui.write(` ${import_picocolors7.default.dim("\xB7")} ${remotePath} ${import_picocolors7.default.dim("(not present on remote \u2014 will be created on first sync)")}`);
1814
1878
  if (!existsSync5(localPath)) {
1815
1879
  await Bun.write(localPath, `# ${project} Journal
1816
1880
 
@@ -1821,7 +1885,7 @@ Project-specific decisions.
1821
1885
  }
1822
1886
  const summary = args.all && projectsToUpdate.length > 1 ? `${projectsToUpdate.length} projects + global` : projectsToUpdate.length === 1 ? projectsToUpdate[0] : "journals";
1823
1887
  ui.write(`
1824
- ${import_picocolors8.default.dim(import_picocolors8.default.gray("Local cache refreshed for"))} ${import_picocolors8.default.bold(import_picocolors8.default.white(summary))}.
1888
+ ${import_picocolors7.default.dim(import_picocolors7.default.gray("Local cache refreshed for"))} ${import_picocolors7.default.bold(import_picocolors7.default.white(summary))}.
1825
1889
  `);
1826
1890
  }
1827
1891
  });
@@ -1929,7 +1993,7 @@ If you cannot produce exactly this, output the JSON with the best you can and se
1929
1993
  const template = agentCfg.prompt_template || '-p "{{prompt}}" --output-format json';
1930
1994
  const extraArgs = buildAgentArgv(template, scaffold);
1931
1995
  const shortTemplate = (agentCfg.prompt_template || '-p "{{prompt}}" --output-format json').slice(0, 80);
1932
- ui.write(` ${import_picocolors9.default.dim(`\u2192 ${agentCfg.command} ${shortTemplate}...`)}`);
1996
+ ui.write(` ${import_picocolors8.default.dim(`\u2192 ${agentCfg.command} ${shortTemplate}...`)}`);
1933
1997
  try {
1934
1998
  const result = spawnSync2([agentCfg.command, ...extraArgs], {
1935
1999
  stdout: "pipe",
@@ -1938,12 +2002,12 @@ If you cannot produce exactly this, output the JSON with the best you can and se
1938
2002
  const stdout = result.stdout.toString().trim();
1939
2003
  const stderr = result.stderr.toString().trim();
1940
2004
  if (result.exitCode !== 0) {
1941
- ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Configured agent (${agentCfg.command}) exited with code ${result.exitCode}. Falling back to defaults.`);
2005
+ ui.write(` ${import_picocolors8.default.yellow("\u26A0")} Configured agent (${agentCfg.command}) exited with code ${result.exitCode}. Falling back to defaults.`);
1942
2006
  if (stderr)
1943
- ui.write(` ${import_picocolors9.default.dim("stderr:")}
2007
+ ui.write(` ${import_picocolors8.default.dim("stderr:")}
1944
2008
  ${stderr.slice(0, 800)}`);
1945
2009
  if (stdout)
1946
- ui.write(` ${import_picocolors9.default.dim("stdout:")}
2010
+ ui.write(` ${import_picocolors8.default.dim("stdout:")}
1947
2011
  ${stdout.slice(0, 400)}`);
1948
2012
  return null;
1949
2013
  }
@@ -1988,37 +2052,37 @@ ${stdout.slice(0, 400)}`);
1988
2052
  parsed = candidates[0] || null;
1989
2053
  }
1990
2054
  if (!parsed || typeof parsed !== "object") {
1991
- ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent produced output but no usable JSON was found. Falling back.`);
1992
- ui.write(` ${import_picocolors9.default.dim("stdout (first 700 chars):")}
2055
+ ui.write(` ${import_picocolors8.default.yellow("\u26A0")} Agent produced output but no usable JSON was found. Falling back.`);
2056
+ ui.write(` ${import_picocolors8.default.dim("stdout (first 700 chars):")}
1993
2057
  ${stdout.slice(0, 700)}`);
1994
2058
  if (stderr)
1995
- ui.write(` ${import_picocolors9.default.dim("stderr:")}
2059
+ ui.write(` ${import_picocolors8.default.dim("stderr:")}
1996
2060
  ${stderr.slice(0, 500)}`);
1997
2061
  return null;
1998
2062
  }
1999
2063
  if (!parsed.title && !parsed.rationale) {
2000
- ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent returned JSON without expected fields (title/rationale). Using defaults.`);
2001
- ui.write(` ${import_picocolors9.default.dim("parsed keys:")} ${Object.keys(parsed).join(", ")}`);
2002
- ui.write(` ${import_picocolors9.default.dim("stdout (truncated):")}
2064
+ ui.write(` ${import_picocolors8.default.yellow("\u26A0")} Agent returned JSON without expected fields (title/rationale). Using defaults.`);
2065
+ ui.write(` ${import_picocolors8.default.dim("parsed keys:")} ${Object.keys(parsed).join(", ")}`);
2066
+ ui.write(` ${import_picocolors8.default.dim("stdout (truncated):")}
2003
2067
  ${stdout.slice(0, 600)}`);
2004
2068
  return null;
2005
2069
  }
2006
2070
  return parsed;
2007
2071
  } catch (e) {
2008
- ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Failed to invoke configured agent (${agentCfg.command}): ${e.message}. Using defaults.`);
2072
+ ui.write(` ${import_picocolors8.default.yellow("\u26A0")} Failed to invoke configured agent (${agentCfg.command}): ${e.message}. Using defaults.`);
2009
2073
  return null;
2010
2074
  }
2011
2075
  }
2012
2076
  function slugify(title) {
2013
2077
  return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
2014
2078
  }
2015
- var import_picocolors9, add_default;
2079
+ var import_picocolors8, add_default;
2016
2080
  var init_add = __esm(() => {
2017
2081
  init_dist();
2018
2082
  init_out();
2019
2083
  init_journal_config();
2020
2084
  init_journal_validate();
2021
- import_picocolors9 = __toESM(require_picocolors(), 1);
2085
+ import_picocolors8 = __toESM(require_picocolors(), 1);
2022
2086
  add_default = defineCommand({
2023
2087
  meta: {
2024
2088
  name: "add",
@@ -2031,7 +2095,7 @@ var init_add = __esm(() => {
2031
2095
  required: false
2032
2096
  },
2033
2097
  pushback: {
2034
- type: "number",
2098
+ type: "string",
2035
2099
  alias: "b",
2036
2100
  description: "Pushback intensity (1-10). Optional \u2014 defaults are applied (or supplied by --json / on-the-fly agent).",
2037
2101
  required: false
@@ -2093,15 +2157,15 @@ var init_add = __esm(() => {
2093
2157
  project = sanitizeProjectName(project);
2094
2158
  }
2095
2159
  if (!project) {
2096
- ui.write(`${import_picocolors9.default.yellow("\u26A0")} No project mapping found.
2160
+ ui.write(`${import_picocolors8.default.yellow("\u26A0")} No project mapping found.
2097
2161
 
2098
- ` + `Run ${import_picocolors9.default.dim("dora init")} (or ${import_picocolors9.default.dim("doraval journal init")}) first, or pass ${import_picocolors9.default.dim("--project <name>")}.`);
2162
+ ` + `Run ${import_picocolors8.default.dim("dora init")} (or ${import_picocolors8.default.dim("doraval journal init")}) first, or pass ${import_picocolors8.default.dim("--project <name>")}.`);
2099
2163
  process.exit(1);
2100
2164
  }
2101
2165
  let title;
2102
2166
  let pushback;
2103
2167
  let tags = [];
2104
- let author = args.author || "human";
2168
+ let author = String(args.author || "human");
2105
2169
  let status = args.status || "active";
2106
2170
  let rationale;
2107
2171
  let date = new Date().toISOString().split("T")[0];
@@ -2133,7 +2197,7 @@ var init_add = __esm(() => {
2133
2197
  if (parsed.date)
2134
2198
  date = String(parsed.date);
2135
2199
  } catch (e) {
2136
- ui.write(`${import_picocolors9.default.red("\u2717")} Failed to parse --json input: ${e.message}`);
2200
+ ui.write(`${import_picocolors8.default.red("\u2717")} Failed to parse --json input: ${e.message}`);
2137
2201
  process.exit(1);
2138
2202
  }
2139
2203
  }
@@ -2154,10 +2218,10 @@ var init_add = __esm(() => {
2154
2218
  if (!title && rawBody) {
2155
2219
  const headingMatch = rawBody.match(/^#+\s+(.+?)(?:\r?\n|$)/m);
2156
2220
  if (headingMatch) {
2157
- title = headingMatch[1].trim();
2221
+ title = (headingMatch[1] ?? "").trim();
2158
2222
  rawBody = rawBody.replace(/^#+\s+(.+?)(?:\r?\n|$)/m, "").trimStart();
2159
2223
  } else {
2160
- ui.write(`${import_picocolors9.default.red("\u2717")} --raw-markdown provided without a TITLE and without a leading '# Heading' in the markdown.`);
2224
+ ui.write(`${import_picocolors8.default.red("\u2717")} --raw-markdown provided without a TITLE and without a leading '# Heading' in the markdown.`);
2161
2225
  process.exit(1);
2162
2226
  }
2163
2227
  }
@@ -2197,7 +2261,7 @@ var init_add = __esm(() => {
2197
2261
  agentCfg = fullConfigForAgent?.agent;
2198
2262
  if (agentCfg) {
2199
2263
  attemptedAgent = true;
2200
- ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("(querying your configured coding agent...)"))}`);
2264
+ ui.write(` ${import_picocolors8.default.dim(import_picocolors8.default.gray("(querying your configured coding agent...)"))}`);
2201
2265
  const agentResult = await invokeConfiguredAgentForEntry(title, agentCfg);
2202
2266
  if (agentResult) {
2203
2267
  if (agentResult.title)
@@ -2230,18 +2294,18 @@ var init_add = __esm(() => {
2230
2294
  };
2231
2295
  const validation = validateEntry(entry);
2232
2296
  if (!validation.valid) {
2233
- ui.write(`${import_picocolors9.default.red("\u2717")} Invalid entry:
2297
+ ui.write(`${import_picocolors8.default.red("\u2717")} Invalid entry:
2234
2298
  `);
2235
2299
  for (const err of validation.errors) {
2236
- ui.write(` ${import_picocolors9.default.red("\u2022")} ${err}`);
2300
+ ui.write(` ${import_picocolors8.default.red("\u2022")} ${err}`);
2237
2301
  }
2238
2302
  process.exit(1);
2239
2303
  }
2240
2304
  for (const warn of validation.warnings) {
2241
2305
  if ((warn.includes("not supplied") || warn.includes("empty")) && attemptedAgent) {} else if (warn.includes("not supplied") || warn.includes("empty")) {
2242
- ui.write(`${import_picocolors9.default.dim("\xB7")} ${warn}`);
2306
+ ui.write(`${import_picocolors8.default.dim("\xB7")} ${warn}`);
2243
2307
  } else {
2244
- ui.write(`${import_picocolors9.default.yellow("\u26A0")} ${warn}`);
2308
+ ui.write(`${import_picocolors8.default.yellow("\u26A0")} ${warn}`);
2245
2309
  }
2246
2310
  }
2247
2311
  if (!rationale) {
@@ -2269,23 +2333,23 @@ ${rationale}
2269
2333
  const filePath = join5(pendingDir, filename);
2270
2334
  await Bun.write(filePath, content);
2271
2335
  ui.write(`
2272
- ${import_picocolors9.default.green("\u2713")} ${import_picocolors9.default.bold(import_picocolors9.default.white(title))}`);
2273
- ui.write(` Project: ${import_picocolors9.default.white(project)} \xB7 run ${import_picocolors9.default.dim(import_picocolors9.default.gray("dora journal sync"))} to publish
2336
+ ${import_picocolors8.default.green("\u2713")} ${import_picocolors8.default.bold(import_picocolors8.default.white(title))}`);
2337
+ ui.write(` Project: ${import_picocolors8.default.white(project)} \xB7 run ${import_picocolors8.default.dim(import_picocolors8.default.gray("dora journal sync"))} to publish
2274
2338
  `);
2275
2339
  if (args.verbose) {
2276
- const authorDisplay = author.startsWith("agent:") ? import_picocolors9.default.cyan(author) : author;
2277
- ui.write(` Pushback: ${import_picocolors9.default.white(String(pushback))}`);
2278
- ui.write(` Tags: ${import_picocolors9.default.gray(tags.join(", ") || import_picocolors9.default.dim("(none)"))}`);
2340
+ const authorDisplay = author.startsWith("agent:") ? import_picocolors8.default.cyan(author) : author;
2341
+ ui.write(` Pushback: ${import_picocolors8.default.white(String(pushback))}`);
2342
+ ui.write(` Tags: ${import_picocolors8.default.gray(tags.join(", ") || import_picocolors8.default.dim("(none)"))}`);
2279
2343
  ui.write(` Author: ${authorDisplay}`);
2280
- ui.write(` File: ${import_picocolors9.default.dim(import_picocolors9.default.gray(filePath))}
2344
+ ui.write(` File: ${import_picocolors8.default.dim(import_picocolors8.default.gray(filePath))}
2281
2345
  `);
2282
2346
  }
2283
2347
  if (isThinInput && !author.startsWith("agent:")) {
2284
2348
  if (attemptedAgent) {
2285
- ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Note: agent was called but returned no usable enrichment. Edit the pending file or re-run dora init."))}
2349
+ ui.write(` ${import_picocolors8.default.dim(import_picocolors8.default.gray("Note: agent was called but returned no usable enrichment. Edit the pending file or re-run dora init."))}
2286
2350
  `);
2287
2351
  } else {
2288
- ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Tip: run dora init to configure an agent for auto-enrichment."))}
2352
+ ui.write(` ${import_picocolors8.default.dim(import_picocolors8.default.gray("Tip: run dora init to configure an agent for auto-enrichment."))}
2289
2353
  `);
2290
2354
  }
2291
2355
  }
@@ -2329,18 +2393,18 @@ function updateGitHubFile(repo, path, content, message, sha) {
2329
2393
  stderr: "pipe"
2330
2394
  });
2331
2395
  if (result.exitCode !== 0) {
2332
- ui.write(import_picocolors10.default.red(`Failed to update ${path} on ${repo}:`));
2396
+ ui.write(import_picocolors9.default.red(`Failed to update ${path} on ${repo}:`));
2333
2397
  ui.write(result.stderr.toString());
2334
2398
  process.exit(1);
2335
2399
  }
2336
2400
  }
2337
- var import_picocolors10, sync_default;
2401
+ var import_picocolors9, sync_default;
2338
2402
  var init_sync = __esm(() => {
2339
2403
  init_dist();
2340
2404
  init_out();
2341
2405
  init_journal_config();
2342
2406
  init_journal_remote();
2343
- import_picocolors10 = __toESM(require_picocolors(), 1);
2407
+ import_picocolors9 = __toESM(require_picocolors(), 1);
2344
2408
  sync_default = defineCommand({
2345
2409
  meta: {
2346
2410
  name: "sync",
@@ -2374,56 +2438,97 @@ var init_sync = __esm(() => {
2374
2438
  project = sanitizeProjectName(project);
2375
2439
  }
2376
2440
  if (!project) {
2377
- ui.write(`${import_picocolors10.default.yellow("\u26A0")} No project mapping found.
2441
+ ui.write(`${import_picocolors9.default.yellow("\u26A0")} No project mapping found.
2378
2442
 
2379
- ` + `Run ${import_picocolors10.default.dim("dora init")} (or ${import_picocolors10.default.dim("doraval journal init")}) first, or pass ${import_picocolors10.default.dim("--project <name>")}.`);
2443
+ ` + `Run ${import_picocolors9.default.dim("dora init")} (or ${import_picocolors9.default.dim("doraval journal init")}) first, or pass ${import_picocolors9.default.dim("--project <name>")}.`);
2380
2444
  process.exit(1);
2381
2445
  }
2382
2446
  if (!config?.journal.repo) {
2383
- ui.write(`${import_picocolors10.default.red("\u2717")} No journal repo configured. Run ${import_picocolors10.default.dim("dora init")} (or ${import_picocolors10.default.dim("doraval journal init")}) first.`);
2447
+ ui.write(`${import_picocolors9.default.red("\u2717")} No journal repo configured. Run ${import_picocolors9.default.dim("dora init")} (or ${import_picocolors9.default.dim("doraval journal init")}) first.`);
2448
+ process.exit(1);
2449
+ }
2450
+ const ghCheck = ensureGhCli();
2451
+ if (!ghCheck.ok) {
2452
+ ui.write(` ${import_picocolors9.default.red("\u2717")} ${import_picocolors9.default.white("The GitHub CLI (")}${import_picocolors9.default.bold("gh")}${import_picocolors9.default.white(") is not installed.")}
2453
+ `);
2454
+ ui.write(` doraval uses ${import_picocolors9.default.bold("gh")} to fetch and sync journal files with GitHub.
2455
+ `);
2456
+ ui.write(` Install it:
2457
+ `);
2458
+ ui.write(` macOS: ${import_picocolors9.default.dim("brew install gh")}`);
2459
+ ui.write(` Linux: ${import_picocolors9.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
2460
+ ui.write(` Windows: ${import_picocolors9.default.dim("winget install --id GitHub.cli")}
2461
+ `);
2462
+ ui.write(` Then authenticate: ${import_picocolors9.default.dim("gh auth login")}
2463
+ `);
2384
2464
  process.exit(1);
2385
2465
  }
2386
- ensureGhCliOrExit();
2387
2466
  const journalRepo = config.journal.repo;
2388
2467
  const pendingDir = getPendingProjectDir(project);
2389
2468
  ui.write(`
2390
- ${import_picocolors10.default.bold(import_picocolors10.default.white("dora journal sync"))} \u2014 ${import_picocolors10.default.white(project)}
2469
+ ${import_picocolors9.default.bold(import_picocolors9.default.white("dora journal sync"))} \u2014 ${import_picocolors9.default.white(project)}
2391
2470
  `);
2392
- ui.write(` Journal repo: ${import_picocolors10.default.dim(import_picocolors10.default.gray(journalRepo))}`);
2471
+ ui.write(` Journal repo: ${import_picocolors9.default.dim(import_picocolors9.default.gray(journalRepo))}`);
2393
2472
  ensureDoravalDirs();
2394
2473
  const journalsDir = getJournalsDir();
2395
2474
  const remoteProjectPath = `projects/${project}.md`;
2396
2475
  const localProjectPath = join6(journalsDir, `${project}.md`);
2397
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("Refreshing local cache from remote..."))}`);
2398
- const gotGlobal = await refreshLocalJournalFile(journalRepo, "global.md", join6(journalsDir, "global.md"));
2476
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Refreshing local cache from remote..."))}`);
2477
+ const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md", join6(journalsDir, "global.md"));
2478
+ if (!refreshGlobalRes.ok) {
2479
+ if (!refreshGlobalRes.isNotFound) {
2480
+ ui.write(import_picocolors9.default.red(`Failed to fetch global.md from ${journalRepo}:`));
2481
+ ui.write(refreshGlobalRes.error);
2482
+ process.exit(1);
2483
+ }
2484
+ }
2485
+ const gotGlobal = refreshGlobalRes.ok && refreshGlobalRes.value;
2399
2486
  if (gotGlobal) {
2400
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("\u2713 global.md"))}`);
2487
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("\u2713 global.md"))}`);
2401
2488
  }
2402
- const gotProjectCache = await refreshLocalJournalFile(journalRepo, remoteProjectPath, localProjectPath);
2489
+ const refreshProjectCacheRes = await refreshLocalJournalFile(journalRepo, remoteProjectPath, localProjectPath);
2490
+ if (!refreshProjectCacheRes.ok) {
2491
+ if (!refreshProjectCacheRes.isNotFound) {
2492
+ ui.write(import_picocolors9.default.red(`Failed to fetch ${remoteProjectPath} from ${journalRepo}:`));
2493
+ ui.write(refreshProjectCacheRes.error);
2494
+ process.exit(1);
2495
+ }
2496
+ }
2497
+ const gotProjectCache = refreshProjectCacheRes.ok && refreshProjectCacheRes.value;
2403
2498
  if (gotProjectCache) {
2404
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray(`\u2713 ${remoteProjectPath}`))}`);
2499
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray(`\u2713 ${remoteProjectPath}`))}`);
2405
2500
  }
2406
2501
  const pendingFiles = existsSync7(pendingDir) ? readdirSync2(pendingDir).filter((f) => f.endsWith(".md") && f !== ".gitkeep").sort() : [];
2407
2502
  if (pendingFiles.length === 0) {
2408
2503
  ui.write(`
2409
- ${import_picocolors10.default.yellow("\u26A0")} No pending entries. Local cache is now up to date.
2504
+ ${import_picocolors9.default.yellow("\u26A0")} No pending entries. Local cache is now up to date.
2410
2505
  `);
2411
2506
  process.exit(0);
2412
2507
  }
2413
2508
  ui.write(` Found ${pendingFiles.length} pending entr${pendingFiles.length === 1 ? "y" : "ies"}
2414
2509
  `);
2415
2510
  const remotePath = `projects/${project}.md`;
2416
- const currentFile = getRemoteJournalFileMeta(journalRepo, remotePath);
2511
+ const metaRes = getRemoteJournalFileMeta(journalRepo, remotePath);
2417
2512
  let existingContent = "";
2418
2513
  let currentSha;
2514
+ let currentFile = null;
2515
+ if (!metaRes.ok) {
2516
+ if (!metaRes.isNotFound) {
2517
+ ui.write(import_picocolors9.default.red(`Failed to fetch ${remotePath} from ${journalRepo}:`));
2518
+ ui.write(metaRes.error);
2519
+ process.exit(1);
2520
+ }
2521
+ } else {
2522
+ currentFile = metaRes.value;
2523
+ }
2419
2524
  if (currentFile) {
2420
2525
  existingContent = Buffer.from(currentFile.content, "base64").toString("utf8");
2421
2526
  currentSha = currentFile.sha;
2422
2527
  if (args.verbose)
2423
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("Found existing remote file (sha: " + currentSha.slice(0, 7) + "...)"))}`);
2528
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Found existing remote file (sha: " + (currentSha?.slice(0, 7) ?? "") + "...)"))}`);
2424
2529
  } else {
2425
2530
  if (args.verbose)
2426
- ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("No existing file on remote \u2014 will create it"))}`);
2531
+ ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("No existing file on remote \u2014 will create it"))}`);
2427
2532
  }
2428
2533
  let newEntries = "";
2429
2534
  for (const file of pendingFiles) {
@@ -2447,12 +2552,12 @@ var init_sync = __esm(() => {
2447
2552
  const commitMessage = args.message || `journal: add ${pendingFiles.length} entr${pendingFiles.length === 1 ? "y" : "ies"} for ${project}`;
2448
2553
  if (args.verbose)
2449
2554
  ui.write(`
2450
- ${import_picocolors10.default.dim(import_picocolors10.default.gray("Pushing to remote..."))}`);
2555
+ ${import_picocolors9.default.dim(import_picocolors9.default.gray("Pushing to remote..."))}`);
2451
2556
  try {
2452
2557
  updateGitHubFile(journalRepo, remotePath, newContent, commitMessage, currentSha);
2453
- ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Successfully pushed to")} ${import_picocolors10.default.white(remotePath)}`);
2558
+ ui.write(` ${import_picocolors9.default.green("\u2713")} ${import_picocolors9.default.white("Successfully pushed to")} ${import_picocolors9.default.white(remotePath)}`);
2454
2559
  } catch (err) {
2455
- ui.write(`${import_picocolors10.default.red("\u2717")} ${import_picocolors10.default.white("Failed to push to GitHub.")}`);
2560
+ ui.write(`${import_picocolors9.default.red("\u2717")} ${import_picocolors9.default.white("Failed to push to GitHub.")}`);
2456
2561
  process.exit(1);
2457
2562
  }
2458
2563
  for (const file of pendingFiles) {
@@ -2461,18 +2566,22 @@ var init_sync = __esm(() => {
2461
2566
  await Bun.file(fullPath).unlink();
2462
2567
  } catch {}
2463
2568
  }
2464
- ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Cleared local pending entries")}`);
2569
+ ui.write(` ${import_picocolors9.default.green("\u2713")} ${import_picocolors9.default.white("Cleared local pending entries")}`);
2465
2570
  try {
2466
- const wrote = await refreshLocalJournalFile(journalRepo, remotePath, localProjectPath);
2467
- if (wrote) {
2571
+ const refreshRes = await refreshLocalJournalFile(journalRepo, remotePath, localProjectPath);
2572
+ if (!refreshRes.ok) {
2573
+ if (!refreshRes.isNotFound) {
2574
+ ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
2575
+ }
2576
+ } else if (refreshRes.value) {
2468
2577
  if (args.verbose)
2469
- ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Re-fetched")} ${import_picocolors10.default.white(project)}.md ${import_picocolors10.default.white("into local cache")}`);
2578
+ ui.write(` ${import_picocolors9.default.green("\u2713")} ${import_picocolors9.default.white("Re-fetched")} ${import_picocolors9.default.white(project)}.md ${import_picocolors9.default.white("into local cache")}`);
2470
2579
  }
2471
2580
  } catch {
2472
- ui.write(` ${import_picocolors10.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
2581
+ ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
2473
2582
  }
2474
2583
  ui.write(`
2475
- ${import_picocolors10.default.green("Done!")} ${import_picocolors10.default.white(pendingFiles.length + " entr" + (pendingFiles.length === 1 ? "y" : "ies") + " published.")}
2584
+ ${import_picocolors9.default.green("Done!")} ${import_picocolors9.default.white(pendingFiles.length + " entr" + (pendingFiles.length === 1 ? "y" : "ies") + " published.")}
2476
2585
  `);
2477
2586
  process.exit(0);
2478
2587
  }
@@ -3090,13 +3199,13 @@ __export(exports_validate_top, {
3090
3199
  });
3091
3200
  import { existsSync as existsSync17 } from "fs";
3092
3201
  import { resolve as resolve11 } from "path";
3093
- var import_picocolors11, validate_top_default;
3202
+ var import_picocolors10, validate_top_default;
3094
3203
  var init_validate_top = __esm(() => {
3095
3204
  init_dist();
3096
3205
  init_out();
3097
3206
  init_validators();
3098
3207
  init_remote();
3099
- import_picocolors11 = __toESM(require_picocolors(), 1);
3208
+ import_picocolors10 = __toESM(require_picocolors(), 1);
3100
3209
  validate_top_default = defineCommand({
3101
3210
  meta: {
3102
3211
  name: "validate",
@@ -3136,7 +3245,7 @@ var init_validate_top = __esm(() => {
3136
3245
  let cleanup;
3137
3246
  if (remote) {
3138
3247
  ui.info(`
3139
- Cloning ${import_picocolors11.default.dim(args.path)}...`);
3248
+ Cloning ${import_picocolors10.default.dim(args.path)}...`);
3140
3249
  try {
3141
3250
  const result = await cloneToTemp(remote);
3142
3251
  fullPath = remote.subpath ? resolve11(result.dir, remote.subpath) : result.dir;
@@ -3184,13 +3293,13 @@ Check that the path is correct and the directory exists.`);
3184
3293
  ` + `Available providers:
3185
3294
  ` + providers.map((p) => {
3186
3295
  const pvs = validators.filter((v) => v.provider === p);
3187
- return ` ${import_picocolors11.default.bold(p)}
3188
- ` + pvs.map((v) => ` \u2022 ${import_picocolors11.default.dim(v.id)} \u2014 ${v.description}`).join(`
3296
+ return ` ${import_picocolors10.default.bold(p)}
3297
+ ` + pvs.map((v) => ` \u2022 ${import_picocolors10.default.dim(v.id)} \u2014 ${v.description}`).join(`
3189
3298
  `);
3190
3299
  }).join(`
3191
3300
  `) + `
3192
3301
 
3193
- Use ${import_picocolors11.default.dim("--for <provider>")} or ${import_picocolors11.default.dim("--for <provider:type>")} to target explicitly.`);
3302
+ Use ${import_picocolors10.default.dim("--for <provider>")} or ${import_picocolors10.default.dim("--for <provider:type>")} to target explicitly.`);
3194
3303
  process.exit(1);
3195
3304
  }
3196
3305
  const allResults = [];
@@ -3211,7 +3320,7 @@ Use ${import_picocolors11.default.dim("--for <provider>")} or ${import_picocolor
3211
3320
  } else {
3212
3321
  for (const { id, name, result } of allResults) {
3213
3322
  ui.write(`
3214
- ${import_picocolors11.default.bold("dora validate")} \u2014 ${import_picocolors11.default.white(name)} ${import_picocolors11.default.dim(`(${id})`)}
3323
+ ${import_picocolors10.default.bold("dora validate")} \u2014 ${import_picocolors10.default.white(name)} ${import_picocolors10.default.dim(`(${id})`)}
3215
3324
  `);
3216
3325
  ui.info(` Path: ${args.path}
3217
3326
  `);
@@ -3226,7 +3335,7 @@ Use ${import_picocolors11.default.dim("--for <provider>")} or ${import_picocolor
3226
3335
  }
3227
3336
  if (result.errors.length === 0 && result.warnings.length === 0) {
3228
3337
  ui.write(`
3229
- ${import_picocolors11.default.green("\u2713")} ${import_picocolors11.default.white("All checks passed.")}
3338
+ ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("All checks passed.")}
3230
3339
  `);
3231
3340
  } else {
3232
3341
  ui.info(`
@@ -3250,14 +3359,14 @@ __export(exports_init2, {
3250
3359
  });
3251
3360
  import { basename as basename2, join as join12 } from "path";
3252
3361
  var {spawnSync: spawnSync5 } = globalThis.Bun;
3253
- var import_picocolors12, init_default2;
3362
+ var import_picocolors11, init_default2;
3254
3363
  var init_init2 = __esm(() => {
3255
3364
  init_dist();
3256
3365
  init_out();
3257
3366
  init_journal_config();
3258
3367
  init_journal_remote();
3259
3368
  init_prompt();
3260
- import_picocolors12 = __toESM(require_picocolors(), 1);
3369
+ import_picocolors11 = __toESM(require_picocolors(), 1);
3261
3370
  init_default2 = defineCommand({
3262
3371
  meta: {
3263
3372
  name: "init",
@@ -3282,7 +3391,22 @@ var init_init2 = __esm(() => {
3282
3391
  },
3283
3392
  async run({ args }) {
3284
3393
  ui.heading("dora init \u2014 Set up doraval, your journal, and the coding agent dora should use on the fly");
3285
- ensureGhCliOrExit();
3394
+ const ghCheck = ensureGhCli();
3395
+ if (!ghCheck.ok) {
3396
+ 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.")}
3397
+ `);
3398
+ ui.info(` doraval uses ${import_picocolors11.default.bold("gh")} to fetch and sync journal files with GitHub.
3399
+ `);
3400
+ ui.info(` Install it:
3401
+ `);
3402
+ ui.info(` macOS: ${import_picocolors11.default.dim("brew install gh")}`);
3403
+ ui.info(` Linux: ${import_picocolors11.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
3404
+ ui.info(` Windows: ${import_picocolors11.default.dim("winget install --id GitHub.cli")}
3405
+ `);
3406
+ ui.info(` Then authenticate: ${import_picocolors11.default.dim("gh auth login")}
3407
+ `);
3408
+ process.exit(1);
3409
+ }
3286
3410
  let repo = args.repo || process.env.DORAVAL_JOURNAL_REPO;
3287
3411
  if (!repo) {
3288
3412
  const gitOwner = getGitRemoteOwner();
@@ -3292,28 +3416,28 @@ var init_init2 = __esm(() => {
3292
3416
  if (gitOwner) {
3293
3417
  defaultRepo = `${gitOwner}/${gitOwner}.md`;
3294
3418
  if (ghLogin && ghLogin !== gitOwner) {
3295
- sourceNote = ` ${import_picocolors12.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
3419
+ sourceNote = ` ${import_picocolors11.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
3296
3420
  `;
3297
3421
  } else {
3298
- sourceNote = ` ${import_picocolors12.default.dim("(from git remote)")}
3422
+ sourceNote = ` ${import_picocolors11.default.dim("(from git remote)")}
3299
3423
  `;
3300
3424
  }
3301
3425
  } else if (ghLogin) {
3302
3426
  defaultRepo = `${ghLogin}/${ghLogin}.md`;
3303
- sourceNote = ` ${import_picocolors12.default.dim("(from your active gh account)")}
3427
+ sourceNote = ` ${import_picocolors11.default.dim("(from your active gh account)")}
3304
3428
  `;
3305
3429
  } else {
3306
- ui.warn(`Not logged in to GitHub. Run ${import_picocolors12.default.dim("gh auth login")} first.
3430
+ ui.warn(`Not logged in to GitHub. Run ${import_picocolors11.default.dim("gh auth login")} first.
3307
3431
  `);
3308
3432
  process.exit(1);
3309
3433
  }
3310
3434
  const existingConfig = await readConfig();
3311
3435
  if (existingConfig?.journal.repo) {
3312
3436
  defaultRepo = existingConfig.journal.repo;
3313
- sourceNote = ` ${import_picocolors12.default.dim("(from your previous journal setup)")}
3437
+ sourceNote = ` ${import_picocolors11.default.dim("(from your previous journal setup)")}
3314
3438
  `;
3315
3439
  }
3316
- ui.info(` Journal repo ${import_picocolors12.default.dim("(owner/name)")}`);
3440
+ ui.info(` Journal repo ${import_picocolors11.default.dim("(owner/name)")}`);
3317
3441
  if (sourceNote)
3318
3442
  ui.write(sourceNote);
3319
3443
  repo = prompt(" >", defaultRepo);
@@ -3325,11 +3449,11 @@ var init_init2 = __esm(() => {
3325
3449
  }
3326
3450
  project = sanitizeProjectName(project);
3327
3451
  if (!repoExists(repo)) {
3328
- ui.write(` ${import_picocolors12.default.red("\u2717")} ${import_picocolors12.default.white("Repository")} ${import_picocolors12.default.bold(repo)} ${import_picocolors12.default.white("not found on GitHub.")}
3452
+ ui.write(` ${import_picocolors11.default.red("\u2717")} ${import_picocolors11.default.white("Repository")} ${import_picocolors11.default.bold(repo)} ${import_picocolors11.default.white("not found on GitHub.")}
3329
3453
  `);
3330
3454
  ui.info(` Create it first:
3331
3455
  `);
3332
- ui.info(` ${import_picocolors12.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
3456
+ ui.info(` ${import_picocolors11.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
3333
3457
  `);
3334
3458
  process.exit(1);
3335
3459
  }
@@ -3337,11 +3461,11 @@ var init_init2 = __esm(() => {
3337
3461
  const alreadyRegistered = existing?.journal.projects[project];
3338
3462
  const isRefresh = alreadyRegistered && args.refresh;
3339
3463
  if (alreadyRegistered && !isRefresh) {
3340
- ui.write(` ${import_picocolors12.default.yellow("\u26A0")} ${import_picocolors12.default.white("Project")} ${import_picocolors12.default.bold(project)} ${import_picocolors12.default.white("is already registered.")}
3464
+ ui.write(` ${import_picocolors11.default.yellow("\u26A0")} ${import_picocolors11.default.white("Project")} ${import_picocolors11.default.bold(project)} ${import_picocolors11.default.white("is already registered.")}
3341
3465
  `);
3342
3466
  ui.info(` Repo: ${existing.journal.repo}
3343
3467
  `);
3344
- ui.info(` To refresh journal files, use ${import_picocolors12.default.dim("dora journal update")} (or ${import_picocolors12.default.dim("dora init --refresh")}).
3468
+ ui.info(` To refresh journal files, use ${import_picocolors11.default.dim("dora journal update")} (or ${import_picocolors11.default.dim("dora init --refresh")}).
3345
3469
  `);
3346
3470
  }
3347
3471
  const journalsDir = getJournalsDir();
@@ -3357,24 +3481,48 @@ var init_init2 = __esm(() => {
3357
3481
  local_path: localPath
3358
3482
  };
3359
3483
  ensureDoravalDirs();
3360
- ui.write(` ${import_picocolors12.default.dim(import_picocolors12.default.gray("Fetching journal files from"))} ${import_picocolors12.default.gray(effectiveRepo)}${import_picocolors12.default.dim(import_picocolors12.default.gray("..."))}
3484
+ ui.write(` ${import_picocolors11.default.dim(import_picocolors11.default.gray("Fetching journal files from"))} ${import_picocolors11.default.gray(effectiveRepo)}${import_picocolors11.default.dim(import_picocolors11.default.gray("..."))}
3361
3485
  `);
3362
3486
  const globalDest = join12(journalsDir, "global.md");
3363
- const wroteGlobal = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
3487
+ const refreshGlobalRes = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
3488
+ let wroteGlobal;
3489
+ if (!refreshGlobalRes.ok) {
3490
+ if (refreshGlobalRes.isNotFound) {
3491
+ wroteGlobal = false;
3492
+ } else {
3493
+ ui.fail(`Failed to fetch global.md from ${effectiveRepo}:`);
3494
+ ui.info(refreshGlobalRes.error);
3495
+ process.exit(1);
3496
+ }
3497
+ } else {
3498
+ wroteGlobal = refreshGlobalRes.value;
3499
+ }
3364
3500
  if (wroteGlobal) {
3365
3501
  ui.success("global.md");
3366
3502
  } else {
3367
- ui.write(` ${import_picocolors12.default.dim("\xB7")} global.md ${import_picocolors12.default.dim("(not found \u2014 will be created on first sync)")}`);
3503
+ ui.write(` ${import_picocolors11.default.dim("\xB7")} global.md ${import_picocolors11.default.dim("(not found \u2014 will be created on first sync)")}`);
3368
3504
  await Bun.write(globalDest, `# Global Journal
3369
3505
 
3370
3506
  Cross-project principles.
3371
3507
  `);
3372
3508
  }
3373
- const wroteProject = await refreshLocalJournalFile(effectiveRepo, remotePath, localPath);
3509
+ const refreshProjectRes = await refreshLocalJournalFile(effectiveRepo, remotePath, localPath);
3510
+ let wroteProject;
3511
+ if (!refreshProjectRes.ok) {
3512
+ if (refreshProjectRes.isNotFound) {
3513
+ wroteProject = false;
3514
+ } else {
3515
+ ui.fail(`Failed to fetch ${remotePath} from ${effectiveRepo}:`);
3516
+ ui.info(refreshProjectRes.error);
3517
+ process.exit(1);
3518
+ }
3519
+ } else {
3520
+ wroteProject = refreshProjectRes.value;
3521
+ }
3374
3522
  if (wroteProject) {
3375
3523
  ui.success(remotePath);
3376
3524
  } else {
3377
- ui.write(` ${import_picocolors12.default.dim("\xB7")} ${remotePath} ${import_picocolors12.default.dim("(not found \u2014 will be created on first sync)")}`);
3525
+ ui.write(` ${import_picocolors11.default.dim("\xB7")} ${remotePath} ${import_picocolors11.default.dim("(not found \u2014 will be created on first sync)")}`);
3378
3526
  await Bun.write(localPath, `# ${project} Journal
3379
3527
 
3380
3528
  Project-specific decisions.
@@ -3382,13 +3530,13 @@ Project-specific decisions.
3382
3530
  }
3383
3531
  await writeConfig(config);
3384
3532
  ui.write(`
3385
- ${import_picocolors12.default.green("\u2713")} ${import_picocolors12.default.white("Journal ready for project")} ${import_picocolors12.default.bold(import_picocolors12.default.white(project))}.
3533
+ ${import_picocolors11.default.green("\u2713")} ${import_picocolors11.default.white("Journal ready for project")} ${import_picocolors11.default.bold(import_picocolors11.default.white(project))}.
3386
3534
  `);
3387
3535
  const existingAgent = (await readConfig())?.agent;
3388
3536
  if (existingAgent?.command) {
3389
- ui.write(` ${import_picocolors12.default.bold(import_picocolors12.default.white("Coding agent (already configured)"))}
3537
+ ui.write(` ${import_picocolors11.default.bold(import_picocolors11.default.white("Coding agent (already configured)"))}
3390
3538
  `);
3391
- ui.write(` Current: ${import_picocolors12.default.dim(import_picocolors12.default.gray(existingAgent.command))} template: ${import_picocolors12.default.dim(import_picocolors12.default.gray(existingAgent.prompt_template || "(default)"))}
3539
+ ui.write(` Current: ${import_picocolors11.default.dim(import_picocolors11.default.gray(existingAgent.command))} template: ${import_picocolors11.default.dim(import_picocolors11.default.gray(existingAgent.prompt_template || "(default)"))}
3392
3540
  `);
3393
3541
  const change = prompt(" Reconfigure / change the coding agent for on-the-fly enrichment? (y/N)", "n");
3394
3542
  if (!/^y/i.test(String(change))) {
@@ -3398,16 +3546,16 @@ Project-specific decisions.
3398
3546
  if (existingAgent)
3399
3547
  cfg.agent = existingAgent;
3400
3548
  await writeConfig(cfg);
3401
- ui.write(` ${import_picocolors12.default.green("\u2713")} ${import_picocolors12.default.white("Try:")} ${import_picocolors12.default.dim(import_picocolors12.default.gray('dora journal add "short decision"'))}
3549
+ ui.write(` ${import_picocolors11.default.green("\u2713")} ${import_picocolors11.default.white("Try:")} ${import_picocolors11.default.dim(import_picocolors11.default.gray('dora journal add "short decision"'))}
3402
3550
  `);
3403
3551
  process.exit(0);
3404
3552
  return;
3405
3553
  }
3406
3554
  ui.blank();
3407
3555
  } else {
3408
- ui.write(` ${import_picocolors12.default.bold(import_picocolors12.default.white("Coding agent for journal add"))}
3556
+ ui.write(` ${import_picocolors11.default.bold(import_picocolors11.default.white("Coding agent for journal add"))}
3409
3557
  `);
3410
- ui.info(` When configured, ${import_picocolors12.default.dim(import_picocolors12.default.gray('dora journal add ".."'))} will use your agent to enrich entries with tags and rationale automatically.
3558
+ ui.info(` When configured, ${import_picocolors11.default.dim(import_picocolors11.default.gray('dora journal add ".."'))} will use your agent to enrich entries with tags and rationale automatically.
3411
3559
  `);
3412
3560
  }
3413
3561
  const common = [
@@ -3426,7 +3574,7 @@ Project-specific decisions.
3426
3574
  }
3427
3575
  }
3428
3576
  let agentCmd = detected || "claude";
3429
- ui.write(` Detected / default agent command: ${import_picocolors12.default.dim(import_picocolors12.default.gray(agentCmd))}`);
3577
+ ui.write(` Detected / default agent command: ${import_picocolors11.default.dim(import_picocolors11.default.gray(agentCmd))}`);
3430
3578
  agentCmd = prompt(" Agent command (the binary you run for prompts)", agentCmd);
3431
3579
  let template = detected ? common.find((c) => c.name === detected)?.template || '-p "{{prompt}}" --output-format json' : '-p "{{prompt}}" --output-format json';
3432
3580
  ui.info(` Prompt template (use {{prompt}} placeholder):`);
@@ -3438,11 +3586,11 @@ Project-specific decisions.
3438
3586
  };
3439
3587
  await writeConfig(finalConfig);
3440
3588
  ui.write(`
3441
- ${import_picocolors12.default.green("\u2713")} ${import_picocolors12.default.white("Agent configured.")}
3589
+ ${import_picocolors11.default.green("\u2713")} ${import_picocolors11.default.white("Agent configured.")}
3442
3590
  `);
3443
- ui.info(` Re-run ${import_picocolors12.default.dim(import_picocolors12.default.gray("dora init"))} anytime to change it.
3591
+ ui.info(` Re-run ${import_picocolors11.default.dim(import_picocolors11.default.gray("dora init"))} anytime to change it.
3444
3592
  `);
3445
- ui.info(` Next: ${import_picocolors12.default.dim(import_picocolors12.default.gray('dora journal add ".."'))}, ${import_picocolors12.default.dim(import_picocolors12.default.gray("dora journal list"))}, or ${import_picocolors12.default.dim(import_picocolors12.default.gray("dora journal update"))}.
3593
+ ui.info(` Next: ${import_picocolors11.default.dim(import_picocolors11.default.gray('dora journal add ".."'))}, ${import_picocolors11.default.dim(import_picocolors11.default.gray("dora journal list"))}, or ${import_picocolors11.default.dim(import_picocolors11.default.gray("dora journal update"))}.
3446
3594
  `);
3447
3595
  process.exit(0);
3448
3596
  }
@@ -3454,7 +3602,7 @@ init_dist();
3454
3602
  // package.json
3455
3603
  var package_default = {
3456
3604
  name: "@hacksmith/doraval",
3457
- version: "0.2.17",
3605
+ version: "0.2.20",
3458
3606
  author: "Saif",
3459
3607
  repository: {
3460
3608
  type: "git",
@@ -3498,6 +3646,7 @@ var package_default = {
3498
3646
  build: "bun build ./src/cli/index.ts --outfile ./bin/doraval.js --target bun",
3499
3647
  dev: "bun run ./src/cli/index.ts",
3500
3648
  test: "bun test",
3649
+ typecheck: "bunx tsc --noEmit --skipLibCheck",
3501
3650
  prepublishOnly: `bun run build && node -e "const p=require('./package.json'),j=require('./jsr.json');if(p.version!==j.version){console.error('Version mismatch: package.json='+p.version+' jsr.json='+j.version);process.exit(1)}"`,
3502
3651
  bump: "bun run scripts/bump.ts",
3503
3652
  release: "bun run scripts/release.ts",
@@ -3514,7 +3663,7 @@ var package_default = {
3514
3663
  };
3515
3664
 
3516
3665
  // src/cli/index.ts
3517
- var import_picocolors13 = __toESM(require_picocolors(), 1);
3666
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
3518
3667
  var skill = defineCommand({
3519
3668
  meta: {
3520
3669
  name: "skill",
@@ -3571,7 +3720,7 @@ var main = defineCommand({
3571
3720
  },
3572
3721
  run() {
3573
3722
  console.log(`
3574
- ` + import_picocolors13.default.blue(doraemonArt) + `
3723
+ ` + import_picocolors12.default.blue(doraemonArt) + `
3575
3724
  `);
3576
3725
  showUsage(main);
3577
3726
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hacksmith/doraval",
3
- "version": "0.2.17",
3
+ "version": "0.2.20",
4
4
  "author": "Saif",
5
5
  "repository": {
6
6
  "type": "git",
@@ -44,6 +44,7 @@
44
44
  "build": "bun build ./src/cli/index.ts --outfile ./bin/doraval.js --target bun",
45
45
  "dev": "bun run ./src/cli/index.ts",
46
46
  "test": "bun test",
47
+ "typecheck": "bunx tsc --noEmit --skipLibCheck",
47
48
  "prepublishOnly": "bun run build && node -e \"const p=require('./package.json'),j=require('./jsr.json');if(p.version!==j.version){console.error('Version mismatch: package.json='+p.version+' jsr.json='+j.version);process.exit(1)}\"",
48
49
  "bump": "bun run scripts/bump.ts",
49
50
  "release": "bun run scripts/release.ts",