cleargate 0.11.4 → 0.11.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -114,6 +114,40 @@ function requireMcpUrl(cfg) {
114
114
  }
115
115
  return cfg.mcpUrl;
116
116
  }
117
+ function saveConfig(updates, opts = {}) {
118
+ const home = os.homedir();
119
+ if (!home) {
120
+ throw new Error("Cannot determine home directory.");
121
+ }
122
+ const configPath = opts.configPath ?? path2.join(home, ".cleargate", "config.json");
123
+ const dir = path2.dirname(configPath);
124
+ let existing = {};
125
+ try {
126
+ const raw = fs2.readFileSync(configPath, "utf8");
127
+ const parsed = JSON.parse(raw);
128
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
129
+ existing = parsed;
130
+ }
131
+ } catch (err) {
132
+ if (!(err instanceof Error && "code" in err && err.code === "ENOENT")) {
133
+ }
134
+ }
135
+ const merged = { ...existing };
136
+ for (const [k, v] of Object.entries(updates)) {
137
+ if (v !== void 0) merged[k] = v;
138
+ }
139
+ fs2.mkdirSync(dir, { recursive: true, mode: 448 });
140
+ try {
141
+ fs2.chmodSync(dir, 448);
142
+ } catch {
143
+ }
144
+ const tmpPath = path2.join(dir, ".config.json.tmp");
145
+ const json = JSON.stringify(merged, null, 2) + "\n";
146
+ fs2.writeFileSync(tmpPath, json, { mode: 384 });
147
+ fs2.chmodSync(tmpPath, 384);
148
+ fs2.renameSync(tmpPath, configPath);
149
+ fs2.chmodSync(configPath, 384);
150
+ }
117
151
  var fs2, os, path2, import_zod, ConfigSchema;
118
152
  var init_config = __esm({
119
153
  "src/config.ts"() {
@@ -383,6 +417,41 @@ var init_membership = __esm({
383
417
  });
384
418
 
385
419
  // src/auth/acquire.ts
420
+ function defaultDiskCachePath(env = process.env) {
421
+ const override = env["CLEARGATE_DISK_CACHE_PATH"];
422
+ if (override === "off") return null;
423
+ if (typeof override === "string" && override.length > 0) return override;
424
+ const home = os9.homedir();
425
+ if (!home) return null;
426
+ return path45.join(home, ".cleargate", "access-token.json");
427
+ }
428
+ function readDiskCache(filePath) {
429
+ try {
430
+ const raw = fs42.readFileSync(filePath, "utf8");
431
+ const parsed = JSON.parse(raw);
432
+ if (parsed !== null && typeof parsed === "object" && parsed.version === 1 && typeof parsed.entries === "object" && parsed.entries !== null) {
433
+ return parsed;
434
+ }
435
+ } catch {
436
+ }
437
+ return { version: 1, entries: {} };
438
+ }
439
+ function writeDiskCache(filePath, data) {
440
+ const dir = path45.dirname(filePath);
441
+ try {
442
+ fs42.mkdirSync(dir, { recursive: true, mode: 448 });
443
+ try {
444
+ fs42.chmodSync(dir, 448);
445
+ } catch {
446
+ }
447
+ const tmpPath = path45.join(dir, ".access-token.json.tmp");
448
+ fs42.writeFileSync(tmpPath, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
449
+ fs42.chmodSync(tmpPath, 384);
450
+ fs42.renameSync(tmpPath, filePath);
451
+ fs42.chmodSync(filePath, 384);
452
+ } catch {
453
+ }
454
+ }
386
455
  function decodeJwtPayload2(token) {
387
456
  try {
388
457
  const parts = token.split(".");
@@ -408,6 +477,15 @@ async function acquireAccessToken(opts) {
408
477
  return cached.accessToken;
409
478
  }
410
479
  }
480
+ const diskCachePath = opts.diskCachePath === void 0 ? defaultDiskCachePath() : opts.diskCachePath;
481
+ if (!opts.forceRefresh && diskCachePath) {
482
+ const file = readDiskCache(diskCachePath);
483
+ const entry = file.entries[cacheKey];
484
+ if (entry && nowFn() < entry.expiresAtMs) {
485
+ CACHE.set(cacheKey, entry);
486
+ return entry.accessToken;
487
+ }
488
+ }
411
489
  const store = await (opts.createStore ?? createTokenStore)();
412
490
  const stored = await store.load(opts.profile);
413
491
  if (!stored) {
@@ -456,15 +534,24 @@ async function acquireAccessToken(opts) {
456
534
  const exp = payload?.exp;
457
535
  if (typeof exp === "number" && Number.isFinite(exp)) {
458
536
  const expiresAtMs = (exp - 60) * 1e3;
459
- CACHE.set(cacheKey, { accessToken, expiresAtMs });
537
+ const entry = { accessToken, expiresAtMs };
538
+ CACHE.set(cacheKey, entry);
539
+ if (diskCachePath) {
540
+ const file = readDiskCache(diskCachePath);
541
+ file.entries[cacheKey] = entry;
542
+ writeDiskCache(diskCachePath, file);
543
+ }
460
544
  }
461
545
  return accessToken;
462
546
  }
463
- var CACHE, AcquireError;
547
+ var fs42, os9, path45, CACHE, AcquireError;
464
548
  var init_acquire = __esm({
465
549
  "src/auth/acquire.ts"() {
466
550
  "use strict";
467
551
  init_cjs_shims();
552
+ fs42 = __toESM(require("fs"), 1);
553
+ os9 = __toESM(require("os"), 1);
554
+ path45 = __toESM(require("path"), 1);
468
555
  init_factory();
469
556
  CACHE = /* @__PURE__ */ new Map();
470
557
  AcquireError = class extends Error {
@@ -696,7 +783,7 @@ var import_commander = require("commander");
696
783
  // package.json
697
784
  var package_default = {
698
785
  name: "cleargate",
699
- version: "0.11.4",
786
+ version: "0.11.5",
700
787
  private: false,
701
788
  type: "module",
702
789
  description: "Planning framework for Claude Code agents \u2014 sprint/epic/story protocol, five-role agent team (architect/developer/qa/devops/reporter), Karpathy-style awareness wiki.",
@@ -1314,7 +1401,8 @@ async function joinHandler(opts) {
1314
1401
  if (UUID_V4_RE.test(opts.inviteUrl)) {
1315
1402
  token = opts.inviteUrl;
1316
1403
  const cfg = loadConfig({
1317
- flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag }
1404
+ flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag },
1405
+ ...opts.configPath !== void 0 ? { configPath: opts.configPath } : {}
1318
1406
  });
1319
1407
  if (!cfg.mcpUrl) {
1320
1408
  stderr(
@@ -1651,9 +1739,15 @@ async function joinHandler(opts) {
1651
1739
  try {
1652
1740
  const store = await (opts.createStore ?? createTokenStore)();
1653
1741
  await store.save(opts.profile, refreshToken);
1742
+ saveConfig(
1743
+ { mcpUrl: baseUrl },
1744
+ opts.configPath !== void 0 ? { configPath: opts.configPath } : {}
1745
+ );
1654
1746
  stdout(`joined project '${projectName}' as '${hostname3()}'
1655
1747
  `);
1656
1748
  stdout(`refresh token saved to ${store.backend}.
1749
+ `);
1750
+ stdout(`mcp_url ${baseUrl} saved to ~/.cleargate/config.json.
1657
1751
  `);
1658
1752
  } catch (err) {
1659
1753
  stderr(
@@ -9618,7 +9712,7 @@ async function uninstallHandler(opts) {
9618
9712
  // src/commands/sync.ts
9619
9713
  init_cjs_shims();
9620
9714
  var fsPromises8 = __toESM(require("fs/promises"), 1);
9621
- var path50 = __toESM(require("path"), 1);
9715
+ var path51 = __toESM(require("path"), 1);
9622
9716
 
9623
9717
  // src/lib/sync-log.ts
9624
9718
  init_cjs_shims();
@@ -9938,12 +10032,12 @@ init_config();
9938
10032
  // src/lib/intake.ts
9939
10033
  init_cjs_shims();
9940
10034
  var fsPromises4 = __toESM(require("fs/promises"), 1);
9941
- var path46 = __toESM(require("path"), 1);
10035
+ var path47 = __toESM(require("path"), 1);
9942
10036
 
9943
10037
  // src/lib/slug.ts
9944
10038
  init_cjs_shims();
9945
10039
  var fsPromises3 = __toESM(require("fs/promises"), 1);
9946
- var path45 = __toESM(require("path"), 1);
10040
+ var path46 = __toESM(require("path"), 1);
9947
10041
  function slugify(title, max = 40) {
9948
10042
  const normalized = title.normalize("NFKD").replace(new RegExp("\\p{M}", "gu"), "");
9949
10043
  const lowered = normalized.toLowerCase();
@@ -9958,8 +10052,8 @@ function slugify(title, max = 40) {
9958
10052
  var PROPOSAL_ID_RE = /^proposal_id:\s*"?PROP-(\d+)"?/m;
9959
10053
  async function nextProposalId(projectRoot) {
9960
10054
  const dirs = [
9961
- path45.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
9962
- path45.join(projectRoot, ".cleargate", "delivery", "archive")
10055
+ path46.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
10056
+ path46.join(projectRoot, ".cleargate", "delivery", "archive")
9963
10057
  ];
9964
10058
  let maxN = 0;
9965
10059
  for (const dir of dirs) {
@@ -9971,7 +10065,7 @@ async function nextProposalId(projectRoot) {
9971
10065
  }
9972
10066
  for (const entry of entries) {
9973
10067
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
9974
- const fullPath = path45.join(dir, entry.name);
10068
+ const fullPath = path46.join(dir, entry.name);
9975
10069
  try {
9976
10070
  const raw = await fsPromises3.readFile(fullPath, "utf8");
9977
10071
  const fmEnd = extractFrontmatterBlock(raw);
@@ -9989,8 +10083,8 @@ async function nextProposalId(projectRoot) {
9989
10083
  }
9990
10084
  async function findByRemoteId(projectRoot, remoteId) {
9991
10085
  const dirs = [
9992
- path45.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
9993
- path45.join(projectRoot, ".cleargate", "delivery", "archive")
10086
+ path46.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
10087
+ path46.join(projectRoot, ".cleargate", "delivery", "archive")
9994
10088
  ];
9995
10089
  const escaped = remoteId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9996
10090
  const re = new RegExp(`^remote_id:\\s*"?${escaped}"?\\s*$`, "m");
@@ -10003,7 +10097,7 @@ async function findByRemoteId(projectRoot, remoteId) {
10003
10097
  }
10004
10098
  for (const entry of entries) {
10005
10099
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
10006
- const fullPath = path45.join(dir, entry.name);
10100
+ const fullPath = path46.join(dir, entry.name);
10007
10101
  try {
10008
10102
  const raw = await fsPromises3.readFile(fullPath, "utf8");
10009
10103
  const fm = extractFrontmatterBlock(raw);
@@ -10040,7 +10134,7 @@ async function runIntakeBranch(opts) {
10040
10134
  labelFilter = "cleargate:proposal",
10041
10135
  now = () => (/* @__PURE__ */ new Date()).toISOString()
10042
10136
  } = opts;
10043
- const pendingSyncDir = path46.join(projectRoot, ".cleargate", "delivery", "pending-sync");
10137
+ const pendingSyncDir = path47.join(projectRoot, ".cleargate", "delivery", "pending-sync");
10044
10138
  let remoteItems = [];
10045
10139
  try {
10046
10140
  remoteItems = await mcp2.call(
@@ -10071,7 +10165,7 @@ async function runIntakeBranch(opts) {
10071
10165
  const slug2 = slugify(item.title ?? "untitled");
10072
10166
  const num2 = proposalId2.replace("PROP-", "");
10073
10167
  const filename2 = `PROPOSAL-${num2}-remote-${slug2}.md`;
10074
- const targetPath2 = path46.join(pendingSyncDir, filename2);
10168
+ const targetPath2 = path47.join(pendingSyncDir, filename2);
10075
10169
  createdItems.push({
10076
10170
  proposalId: proposalId2,
10077
10171
  remoteId: item.remote_id,
@@ -10084,7 +10178,7 @@ async function runIntakeBranch(opts) {
10084
10178
  const num = proposalId.replace("PROP-", "");
10085
10179
  const slug = slugify(item.title ?? "untitled");
10086
10180
  const filename = `PROPOSAL-${num}-remote-${slug}.md`;
10087
- const targetPath = path46.join(pendingSyncDir, filename);
10181
+ const targetPath = path47.join(pendingSyncDir, filename);
10088
10182
  const nowTs = now();
10089
10183
  const fm = {
10090
10184
  proposal_id: proposalId,
@@ -10176,8 +10270,8 @@ path/to/new/file.ext - {Explanation of purpose}
10176
10270
  }
10177
10271
  async function hasAnyRemoteAuthored(projectRoot) {
10178
10272
  const dirs = [
10179
- path46.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
10180
- path46.join(projectRoot, ".cleargate", "delivery", "archive")
10273
+ path47.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
10274
+ path47.join(projectRoot, ".cleargate", "delivery", "archive")
10181
10275
  ];
10182
10276
  for (const dir of dirs) {
10183
10277
  let entries;
@@ -10188,7 +10282,7 @@ async function hasAnyRemoteAuthored(projectRoot) {
10188
10282
  }
10189
10283
  for (const entry of entries) {
10190
10284
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
10191
- const fullPath = path46.join(dir, entry.name);
10285
+ const fullPath = path47.join(dir, entry.name);
10192
10286
  try {
10193
10287
  const raw = await fsPromises4.readFile(fullPath, "utf8");
10194
10288
  const fmEnd = raw.indexOf("\n---", 4);
@@ -10206,9 +10300,9 @@ async function hasAnyRemoteAuthored(projectRoot) {
10206
10300
 
10207
10301
  // src/lib/active-criteria.ts
10208
10302
  init_cjs_shims();
10209
- var fs42 = __toESM(require("fs"), 1);
10303
+ var fs43 = __toESM(require("fs"), 1);
10210
10304
  var fsPromises5 = __toESM(require("fs/promises"), 1);
10211
- var path47 = __toESM(require("path"), 1);
10305
+ var path48 = __toESM(require("path"), 1);
10212
10306
  async function resolveActiveItems(projectRoot, localItems, nowFn = () => (/* @__PURE__ */ new Date()).toISOString()) {
10213
10307
  const active = /* @__PURE__ */ new Set();
10214
10308
  const now = Date.parse(nowFn());
@@ -10233,7 +10327,7 @@ async function resolveInSprintIds(projectRoot) {
10233
10327
  const ids = /* @__PURE__ */ new Set();
10234
10328
  try {
10235
10329
  const sprintDir = resolveActiveSprintDir(projectRoot);
10236
- const sprintId = path47.basename(sprintDir);
10330
+ const sprintId = path48.basename(sprintDir);
10237
10331
  if (sprintId === "_off-sprint") return ids;
10238
10332
  const sprintFile = await findSprintFile2(projectRoot, sprintId);
10239
10333
  if (!sprintFile) return ids;
@@ -10248,14 +10342,14 @@ async function resolveInSprintIds(projectRoot) {
10248
10342
  return ids;
10249
10343
  }
10250
10344
  async function findSprintFile2(projectRoot, sprintId) {
10251
- const pendingSync = path47.join(projectRoot, ".cleargate", "delivery", "pending-sync");
10252
- const archive = path47.join(projectRoot, ".cleargate", "delivery", "archive");
10345
+ const pendingSync = path48.join(projectRoot, ".cleargate", "delivery", "pending-sync");
10346
+ const archive = path48.join(projectRoot, ".cleargate", "delivery", "archive");
10253
10347
  for (const dir of [pendingSync, archive]) {
10254
10348
  try {
10255
- const entries = fs42.readdirSync(dir, { withFileTypes: true });
10349
+ const entries = fs43.readdirSync(dir, { withFileTypes: true });
10256
10350
  for (const entry of entries) {
10257
10351
  if (entry.isFile() && entry.name.startsWith(sprintId) && entry.name.endsWith(".md")) {
10258
- return path47.join(dir, entry.name);
10352
+ return path48.join(dir, entry.name);
10259
10353
  }
10260
10354
  }
10261
10355
  } catch {
@@ -10267,12 +10361,12 @@ async function findSprintFile2(projectRoot, sprintId) {
10267
10361
  // src/lib/comments-cache.ts
10268
10362
  init_cjs_shims();
10269
10363
  var fsPromises6 = __toESM(require("fs/promises"), 1);
10270
- var path48 = __toESM(require("path"), 1);
10364
+ var path49 = __toESM(require("path"), 1);
10271
10365
  function cacheDir(projectRoot) {
10272
- return path48.join(projectRoot, ".cleargate", ".comments-cache");
10366
+ return path49.join(projectRoot, ".cleargate", ".comments-cache");
10273
10367
  }
10274
10368
  function cachePath(projectRoot, remoteId) {
10275
- return path48.join(cacheDir(projectRoot), `${remoteId}.json`);
10369
+ return path49.join(cacheDir(projectRoot), `${remoteId}.json`);
10276
10370
  }
10277
10371
  async function writeCommentCache(projectRoot, remoteId, comments) {
10278
10372
  const dir = cacheDir(projectRoot);
@@ -10287,7 +10381,7 @@ async function writeCommentCache(projectRoot, remoteId, comments) {
10287
10381
  // src/lib/wiki-comments-render.ts
10288
10382
  init_cjs_shims();
10289
10383
  var fsPromises7 = __toESM(require("fs/promises"), 1);
10290
- var path49 = __toESM(require("path"), 1);
10384
+ var path50 = __toESM(require("path"), 1);
10291
10385
  var START = "<!-- cleargate:comments:start -->";
10292
10386
  var END = "<!-- cleargate:comments:end -->";
10293
10387
  function resolveBucket(fm) {
@@ -10332,7 +10426,7 @@ async function renderCommentsSection(opts) {
10332
10426
  const bucket = resolveBucket(localItem.fm);
10333
10427
  const primaryId = getPrimaryId(localItem.fm);
10334
10428
  if (!bucket || !primaryId) return;
10335
- const wikiPath = path49.join(
10429
+ const wikiPath = path50.join(
10336
10430
  projectRoot,
10337
10431
  ".cleargate",
10338
10432
  "wiki",
@@ -10368,7 +10462,7 @@ async function renderCommentsSection(opts) {
10368
10462
  await writeAtomic4(wikiPath, updated);
10369
10463
  }
10370
10464
  async function writeAtomic4(filePath, content) {
10371
- await fsPromises7.mkdir(path49.dirname(filePath), { recursive: true });
10465
+ await fsPromises7.mkdir(path50.dirname(filePath), { recursive: true });
10372
10466
  const tmpPath = `${filePath}.tmp.${Date.now()}`;
10373
10467
  await fsPromises7.writeFile(tmpPath, content, "utf8");
10374
10468
  await fsPromises7.rename(tmpPath, filePath);
@@ -10380,11 +10474,11 @@ async function syncCheckHandler(opts = {}) {
10380
10474
  const env = opts.env ?? process.env;
10381
10475
  const stdout = opts.stdout ?? ((s) => process.stdout.write(s));
10382
10476
  const nowFn = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
10383
- const markerPath = path50.join(projectRoot, ".cleargate", ".sync-marker.json");
10477
+ const markerPath = path51.join(projectRoot, ".cleargate", ".sync-marker.json");
10384
10478
  const updateMarker = async (nowIso2) => {
10385
10479
  try {
10386
10480
  const content = JSON.stringify({ last_check: nowIso2 });
10387
- await fsPromises8.mkdir(path50.dirname(markerPath), { recursive: true });
10481
+ await fsPromises8.mkdir(path51.dirname(markerPath), { recursive: true });
10388
10482
  const tmpPath = `${markerPath}.tmp.${Date.now()}`;
10389
10483
  await fsPromises8.writeFile(tmpPath, content, "utf8");
10390
10484
  await fsPromises8.rename(tmpPath, markerPath);
@@ -10467,7 +10561,7 @@ async function syncHandler(opts = {}) {
10467
10561
  const nowFn = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
10468
10562
  const identity = resolveIdentity(projectRoot);
10469
10563
  const sprintRoot = resolveActiveSprintDir(projectRoot);
10470
- const sprintId = path50.basename(sprintRoot);
10564
+ const sprintId = path51.basename(sprintRoot);
10471
10565
  let mcp2;
10472
10566
  if (opts.mcp) {
10473
10567
  mcp2 = opts.mcp;
@@ -10528,7 +10622,7 @@ async function syncHandler(opts = {}) {
10528
10622
  exit(2);
10529
10623
  return;
10530
10624
  }
10531
- const wikiMetaPath = path50.join(projectRoot, ".cleargate", "wiki", "meta.json");
10625
+ const wikiMetaPath = path51.join(projectRoot, ".cleargate", "wiki", "meta.json");
10532
10626
  let lastRemoteSync = "1970-01-01T00:00:00.000Z";
10533
10627
  try {
10534
10628
  const metaRaw = await fsPromises8.readFile(wikiMetaPath, "utf8");
@@ -10769,7 +10863,7 @@ async function syncHandler(opts = {}) {
10769
10863
  };
10770
10864
  await appendSyncLog(sprintRoot, entry);
10771
10865
  }
10772
- const conflictsFile = path50.join(projectRoot, ".cleargate", ".conflicts.json");
10866
+ const conflictsFile = path51.join(projectRoot, ".cleargate", ".conflicts.json");
10773
10867
  const conflictsContent = {
10774
10868
  generated_at: nowFn(),
10775
10869
  sprint_id: sprintId,
@@ -10777,7 +10871,7 @@ async function syncHandler(opts = {}) {
10777
10871
  };
10778
10872
  await writeAtomic5(conflictsFile, JSON.stringify(conflictsContent, null, 2) + "\n");
10779
10873
  try {
10780
- await fsPromises8.mkdir(path50.dirname(wikiMetaPath), { recursive: true });
10874
+ await fsPromises8.mkdir(path51.dirname(wikiMetaPath), { recursive: true });
10781
10875
  let meta = {};
10782
10876
  try {
10783
10877
  const raw = await fsPromises8.readFile(wikiMetaPath, "utf8");
@@ -10818,13 +10912,13 @@ async function applyPull(item, localPath, fm, actorEmail, nowFn) {
10818
10912
  await writeAtomic5(localPath, newContent);
10819
10913
  }
10820
10914
  async function writeAtomic5(filePath, content) {
10821
- await fsPromises8.mkdir(path50.dirname(filePath), { recursive: true });
10915
+ await fsPromises8.mkdir(path51.dirname(filePath), { recursive: true });
10822
10916
  const tmpPath = `${filePath}.tmp.${Date.now()}`;
10823
10917
  await fsPromises8.writeFile(tmpPath, content, "utf8");
10824
10918
  await fsPromises8.rename(tmpPath, filePath);
10825
10919
  }
10826
10920
  async function scanLocalItems(projectRoot) {
10827
- const pendingSync = path50.join(projectRoot, ".cleargate", "delivery", "pending-sync");
10921
+ const pendingSync = path51.join(projectRoot, ".cleargate", "delivery", "pending-sync");
10828
10922
  const results = [];
10829
10923
  let entries;
10830
10924
  try {
@@ -10834,7 +10928,7 @@ async function scanLocalItems(projectRoot) {
10834
10928
  }
10835
10929
  for (const entry of entries) {
10836
10930
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
10837
- const fullPath = path50.join(pendingSync, entry.name);
10931
+ const fullPath = path51.join(pendingSync, entry.name);
10838
10932
  try {
10839
10933
  const raw = await fsPromises8.readFile(fullPath, "utf8");
10840
10934
  const { fm, body } = parseFrontmatter(raw);
@@ -10862,7 +10956,7 @@ init_config();
10862
10956
  // src/lib/sync/work-items.ts
10863
10957
  init_cjs_shims();
10864
10958
  var fsPromises9 = __toESM(require("fs/promises"), 1);
10865
- var path51 = __toESM(require("path"), 1);
10959
+ var path52 = __toESM(require("path"), 1);
10866
10960
  var import_node_crypto2 = require("crypto");
10867
10961
  var BATCH_SIZE = 100;
10868
10962
  var ATTRIBUTION_FIELDS = /* @__PURE__ */ new Set([
@@ -10910,8 +11004,8 @@ function getItemId2(fm) {
10910
11004
  }
10911
11005
  async function walkDeliveryDirs(projectRoot) {
10912
11006
  const dirs = [
10913
- path51.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
10914
- path51.join(projectRoot, ".cleargate", "delivery", "archive")
11007
+ path52.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
11008
+ path52.join(projectRoot, ".cleargate", "delivery", "archive")
10915
11009
  ];
10916
11010
  const results = [];
10917
11011
  for (const dir of dirs) {
@@ -10923,7 +11017,7 @@ async function walkDeliveryDirs(projectRoot) {
10923
11017
  }
10924
11018
  for (const entry of entries) {
10925
11019
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
10926
- const fullPath = path51.join(dir, entry.name);
11020
+ const fullPath = path52.join(dir, entry.name);
10927
11021
  try {
10928
11022
  const raw = await fsPromises9.readFile(fullPath, "utf8");
10929
11023
  const { fm, body } = parseFrontmatter(raw);
@@ -10948,7 +11042,7 @@ async function walkDeliveryDirs(projectRoot) {
10948
11042
  return results;
10949
11043
  }
10950
11044
  async function writeAtomic6(filePath, content) {
10951
- await fsPromises9.mkdir(path51.dirname(filePath), { recursive: true });
11045
+ await fsPromises9.mkdir(path52.dirname(filePath), { recursive: true });
10952
11046
  const tmpPath = `${filePath}.tmp.${Date.now()}`;
10953
11047
  await fsPromises9.writeFile(tmpPath, content, "utf8");
10954
11048
  await fsPromises9.rename(tmpPath, filePath);
@@ -11015,9 +11109,9 @@ async function syncWorkItems(opts) {
11015
11109
 
11016
11110
  // src/lib/admin-url.ts
11017
11111
  init_cjs_shims();
11018
- var fs43 = __toESM(require("fs"), 1);
11019
- var os9 = __toESM(require("os"), 1);
11020
- var path52 = __toESM(require("path"), 1);
11112
+ var fs44 = __toESM(require("fs"), 1);
11113
+ var os10 = __toESM(require("os"), 1);
11114
+ var path53 = __toESM(require("path"), 1);
11021
11115
  var DEFAULT_BASE = "https://admin.cleargate.soula.ge/";
11022
11116
  function adminUrl(urlPath, opts) {
11023
11117
  const env = opts?.env ?? process.env;
@@ -11038,10 +11132,10 @@ function adminUrl(urlPath, opts) {
11038
11132
  return base;
11039
11133
  }
11040
11134
  function readLocalConfig() {
11041
- const home = os9.homedir();
11135
+ const home = os10.homedir();
11042
11136
  if (!home) return null;
11043
- const configPath = path52.join(home, ".cleargate", "config.json");
11044
- const raw = fs43.readFileSync(configPath, "utf8");
11137
+ const configPath = path53.join(home, ".cleargate", "config.json");
11138
+ const raw = fs44.readFileSync(configPath, "utf8");
11045
11139
  return JSON.parse(raw);
11046
11140
  }
11047
11141
 
@@ -11156,7 +11250,7 @@ async function syncWorkItemsHandler(opts = {}) {
11156
11250
  // src/commands/pull.ts
11157
11251
  init_cjs_shims();
11158
11252
  var fsPromises10 = __toESM(require("fs/promises"), 1);
11159
- var path53 = __toESM(require("path"), 1);
11253
+ var path54 = __toESM(require("path"), 1);
11160
11254
  init_acquire();
11161
11255
  init_config();
11162
11256
  async function pullHandler(idOrRemoteId, opts = {}) {
@@ -11271,7 +11365,7 @@ async function pullHandler(idOrRemoteId, opts = {}) {
11271
11365
  result: "ok"
11272
11366
  };
11273
11367
  await appendSyncLog(sprintRoot, entry);
11274
- stdout(`pull: ${remoteId} applied to ${path53.relative(projectRoot, localPath)}
11368
+ stdout(`pull: ${remoteId} applied to ${path54.relative(projectRoot, localPath)}
11275
11369
  `);
11276
11370
  if (opts.comments) {
11277
11371
  const comments = await mcp2.call(
@@ -11294,7 +11388,7 @@ async function resolveRemoteId(idOrRemoteId, projectRoot) {
11294
11388
  if (/^[A-Z]+-\d+/.test(idOrRemoteId)) {
11295
11389
  return idOrRemoteId;
11296
11390
  }
11297
- const pendingSync = path53.join(projectRoot, ".cleargate", "delivery", "pending-sync");
11391
+ const pendingSync = path54.join(projectRoot, ".cleargate", "delivery", "pending-sync");
11298
11392
  let entries;
11299
11393
  try {
11300
11394
  entries = await fsPromises10.readdir(pendingSync, { withFileTypes: true });
@@ -11304,7 +11398,7 @@ async function resolveRemoteId(idOrRemoteId, projectRoot) {
11304
11398
  for (const entry of entries) {
11305
11399
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
11306
11400
  try {
11307
- const raw = await fsPromises10.readFile(path53.join(pendingSync, entry.name), "utf8");
11401
+ const raw = await fsPromises10.readFile(path54.join(pendingSync, entry.name), "utf8");
11308
11402
  const { fm } = parseFrontmatter(raw);
11309
11403
  for (const key of ["story_id", "epic_id", "proposal_id", "cr_id", "bug_id"]) {
11310
11404
  if (fm[key] === idOrRemoteId && typeof fm["remote_id"] === "string") {
@@ -11317,7 +11411,7 @@ async function resolveRemoteId(idOrRemoteId, projectRoot) {
11317
11411
  return null;
11318
11412
  }
11319
11413
  async function findLocalFile(remoteId, projectRoot) {
11320
- const pendingSync = path53.join(projectRoot, ".cleargate", "delivery", "pending-sync");
11414
+ const pendingSync = path54.join(projectRoot, ".cleargate", "delivery", "pending-sync");
11321
11415
  let entries;
11322
11416
  try {
11323
11417
  entries = await fsPromises10.readdir(pendingSync, { withFileTypes: true });
@@ -11326,7 +11420,7 @@ async function findLocalFile(remoteId, projectRoot) {
11326
11420
  }
11327
11421
  for (const entry of entries) {
11328
11422
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
11329
- const fullPath = path53.join(pendingSync, entry.name);
11423
+ const fullPath = path54.join(pendingSync, entry.name);
11330
11424
  try {
11331
11425
  const raw = await fsPromises10.readFile(fullPath, "utf8");
11332
11426
  const { fm } = parseFrontmatter(raw);
@@ -11337,7 +11431,7 @@ async function findLocalFile(remoteId, projectRoot) {
11337
11431
  return null;
11338
11432
  }
11339
11433
  async function writeAtomic7(filePath, content) {
11340
- await fsPromises10.mkdir(path53.dirname(filePath), { recursive: true });
11434
+ await fsPromises10.mkdir(path54.dirname(filePath), { recursive: true });
11341
11435
  const tmpPath = `${filePath}.tmp.${Date.now()}`;
11342
11436
  await fsPromises10.writeFile(tmpPath, content, "utf8");
11343
11437
  await fsPromises10.rename(tmpPath, filePath);
@@ -11353,7 +11447,7 @@ function getItemId3(fm) {
11353
11447
  // src/commands/push.ts
11354
11448
  init_cjs_shims();
11355
11449
  var fsPromises11 = __toESM(require("fs/promises"), 1);
11356
- var path54 = __toESM(require("path"), 1);
11450
+ var path55 = __toESM(require("path"), 1);
11357
11451
  init_acquire();
11358
11452
  init_config();
11359
11453
  async function pushHandler(fileOrId, opts = {}) {
@@ -11429,7 +11523,7 @@ async function pushHandler(fileOrId, opts = {}) {
11429
11523
  }
11430
11524
  async function handlePush(filePath, ctx) {
11431
11525
  const { projectRoot, identity, sprintRoot, nowFn, resolveMcp, stdout, stderr, exit } = ctx;
11432
- const resolvedPath = path54.isAbsolute(filePath) ? filePath : path54.resolve(projectRoot, filePath);
11526
+ const resolvedPath = path55.isAbsolute(filePath) ? filePath : path55.resolve(projectRoot, filePath);
11433
11527
  let rawContent;
11434
11528
  try {
11435
11529
  rawContent = await fsPromises11.readFile(resolvedPath, "utf8");
@@ -11555,8 +11649,8 @@ async function handleRevert(idOrRemoteId, ctx) {
11555
11649
  void localPath;
11556
11650
  }
11557
11651
  async function resolveLocalItem(idOrRemoteId, projectRoot) {
11558
- const pendingSync = path54.join(projectRoot, ".cleargate", "delivery", "pending-sync");
11559
- const archive = path54.join(projectRoot, ".cleargate", "delivery", "archive");
11652
+ const pendingSync = path55.join(projectRoot, ".cleargate", "delivery", "pending-sync");
11653
+ const archive = path55.join(projectRoot, ".cleargate", "delivery", "archive");
11560
11654
  for (const dir of [pendingSync, archive]) {
11561
11655
  let entries;
11562
11656
  try {
@@ -11566,7 +11660,7 @@ async function resolveLocalItem(idOrRemoteId, projectRoot) {
11566
11660
  }
11567
11661
  for (const entry of entries) {
11568
11662
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
11569
- const fullPath = path54.join(dir, entry.name);
11663
+ const fullPath = path55.join(dir, entry.name);
11570
11664
  try {
11571
11665
  const raw = await fsPromises11.readFile(fullPath, "utf8");
11572
11666
  const { fm } = parseFrontmatter(raw);
@@ -11585,7 +11679,7 @@ async function resolveLocalItem(idOrRemoteId, projectRoot) {
11585
11679
  return null;
11586
11680
  }
11587
11681
  async function writeAtomic8(filePath, content) {
11588
- await fsPromises11.mkdir(path54.dirname(filePath), { recursive: true });
11682
+ await fsPromises11.mkdir(path55.dirname(filePath), { recursive: true });
11589
11683
  const tmpPath = `${filePath}.tmp.${Date.now()}`;
11590
11684
  await fsPromises11.writeFile(tmpPath, content, "utf8");
11591
11685
  await fsPromises11.rename(tmpPath, filePath);
@@ -11614,7 +11708,7 @@ function getItemType2(fm) {
11614
11708
  // src/commands/conflicts.ts
11615
11709
  init_cjs_shims();
11616
11710
  var fsPromises12 = __toESM(require("fs/promises"), 1);
11617
- var path55 = __toESM(require("path"), 1);
11711
+ var path56 = __toESM(require("path"), 1);
11618
11712
  init_acquire();
11619
11713
  init_config();
11620
11714
  var RESOLUTION_HINTS = {
@@ -11652,7 +11746,7 @@ async function conflictsHandler(opts = {}) {
11652
11746
  }
11653
11747
  }
11654
11748
  }
11655
- const conflictsFile = path55.join(projectRoot, ".cleargate", ".conflicts.json");
11749
+ const conflictsFile = path56.join(projectRoot, ".cleargate", ".conflicts.json");
11656
11750
  let data;
11657
11751
  try {
11658
11752
  const raw = await fsPromises12.readFile(conflictsFile, "utf8");
@@ -11740,24 +11834,24 @@ function formatEntry(entry) {
11740
11834
 
11741
11835
  // src/commands/admin-login.ts
11742
11836
  init_cjs_shims();
11743
- var fs44 = __toESM(require("fs"), 1);
11744
- var path56 = __toESM(require("path"), 1);
11745
- var os10 = __toESM(require("os"), 1);
11837
+ var fs45 = __toESM(require("fs"), 1);
11838
+ var path57 = __toESM(require("path"), 1);
11839
+ var os11 = __toESM(require("os"), 1);
11746
11840
  var DEFAULT_MCP_URL = "http://localhost:3000";
11747
11841
  function resolveMcpUrl(mcpUrlFlag, env) {
11748
11842
  return (mcpUrlFlag ?? (env ?? process.env)["CLEARGATE_MCP_URL"] ?? DEFAULT_MCP_URL).replace(/\/$/, "");
11749
11843
  }
11750
11844
  function resolveAuthFilePath(opts) {
11751
11845
  if (opts.authFilePath) return opts.authFilePath;
11752
- const homedirFn = opts.homedir ?? os10.homedir;
11753
- return path56.join(homedirFn(), ".cleargate", "admin-auth.json");
11846
+ const homedirFn = opts.homedir ?? os11.homedir;
11847
+ return path57.join(homedirFn(), ".cleargate", "admin-auth.json");
11754
11848
  }
11755
11849
  function writeAdminAuth(filePath, token) {
11756
- const dir = path56.dirname(filePath);
11757
- fs44.mkdirSync(dir, { recursive: true });
11850
+ const dir = path57.dirname(filePath);
11851
+ fs45.mkdirSync(dir, { recursive: true });
11758
11852
  const payload = JSON.stringify({ version: 1, token }, null, 2);
11759
- fs44.writeFileSync(filePath, payload, { encoding: "utf8", mode: 384 });
11760
- fs44.chmodSync(filePath, 384);
11853
+ fs45.writeFileSync(filePath, payload, { encoding: "utf8", mode: 384 });
11854
+ fs45.chmodSync(filePath, 384);
11761
11855
  }
11762
11856
  async function adminLoginHandler(opts = {}) {
11763
11857
  const fetchFn = opts.fetch ?? globalThis.fetch;
@@ -11867,8 +11961,8 @@ async function adminLoginHandler(opts = {}) {
11867
11961
 
11868
11962
  // src/commands/hotfix.ts
11869
11963
  init_cjs_shims();
11870
- var fs45 = __toESM(require("fs"), 1);
11871
- var path57 = __toESM(require("path"), 1);
11964
+ var fs46 = __toESM(require("fs"), 1);
11965
+ var path58 = __toESM(require("path"), 1);
11872
11966
  function defaultExit4(code) {
11873
11967
  return process.exit(code);
11874
11968
  }
@@ -11878,7 +11972,7 @@ function maxHotfixId(pendingDir) {
11878
11972
  let max = 0;
11879
11973
  let entries;
11880
11974
  try {
11881
- entries = fs45.readdirSync(pendingDir);
11975
+ entries = fs46.readdirSync(pendingDir);
11882
11976
  } catch {
11883
11977
  return 0;
11884
11978
  }
@@ -11892,13 +11986,13 @@ function maxHotfixId(pendingDir) {
11892
11986
  return max;
11893
11987
  }
11894
11988
  function countActiveHotfixes(repoRoot) {
11895
- const pendingDir = path57.join(repoRoot, ".cleargate", "delivery", "pending-sync");
11896
- const archiveDir = path57.join(repoRoot, ".cleargate", "delivery", "archive");
11989
+ const pendingDir = path58.join(repoRoot, ".cleargate", "delivery", "pending-sync");
11990
+ const archiveDir = path58.join(repoRoot, ".cleargate", "delivery", "archive");
11897
11991
  const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1e3;
11898
11992
  let count = 0;
11899
11993
  let pendingEntries = [];
11900
11994
  try {
11901
- pendingEntries = fs45.readdirSync(pendingDir);
11995
+ pendingEntries = fs46.readdirSync(pendingDir);
11902
11996
  } catch {
11903
11997
  }
11904
11998
  for (const entry of pendingEntries) {
@@ -11906,13 +12000,13 @@ function countActiveHotfixes(repoRoot) {
11906
12000
  }
11907
12001
  let archiveEntries = [];
11908
12002
  try {
11909
- archiveEntries = fs45.readdirSync(archiveDir);
12003
+ archiveEntries = fs46.readdirSync(archiveDir);
11910
12004
  } catch {
11911
12005
  }
11912
12006
  for (const entry of archiveEntries) {
11913
12007
  if (entry.startsWith("HOTFIX-") && entry.endsWith(".md")) {
11914
12008
  try {
11915
- const stat = fs45.statSync(path57.join(archiveDir, entry));
12009
+ const stat = fs46.statSync(path58.join(archiveDir, entry));
11916
12010
  if (stat.mtimeMs >= sevenDaysAgo) count++;
11917
12011
  } catch {
11918
12012
  }
@@ -11921,7 +12015,7 @@ function countActiveHotfixes(repoRoot) {
11921
12015
  return count;
11922
12016
  }
11923
12017
  function resolveTemplatePath(repoRoot) {
11924
- return path57.join(repoRoot, ".cleargate", "templates", "hotfix.md");
12018
+ return path58.join(repoRoot, ".cleargate", "templates", "hotfix.md");
11925
12019
  }
11926
12020
  function hotfixNewHandler(opts, cli) {
11927
12021
  const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
@@ -11940,14 +12034,14 @@ function hotfixNewHandler(opts, cli) {
11940
12034
  );
11941
12035
  return exitFn(1);
11942
12036
  }
11943
- const pendingDir = path57.join(repoRoot, ".cleargate", "delivery", "pending-sync");
12037
+ const pendingDir = path58.join(repoRoot, ".cleargate", "delivery", "pending-sync");
11944
12038
  const maxId = maxHotfixId(pendingDir);
11945
12039
  const nextId = maxId + 1;
11946
12040
  const idStr = `HOTFIX-${String(nextId).padStart(3, "0")}`;
11947
12041
  const templatePath = resolveTemplatePath(repoRoot);
11948
12042
  let templateContent;
11949
12043
  try {
11950
- templateContent = fs45.readFileSync(templatePath, "utf8");
12044
+ templateContent = fs46.readFileSync(templatePath, "utf8");
11951
12045
  } catch {
11952
12046
  stderrFn(`[cleargate hotfix new] template not found: ${templatePath}`);
11953
12047
  return exitFn(2);
@@ -11955,10 +12049,10 @@ function hotfixNewHandler(opts, cli) {
11955
12049
  const content = templateContent.replace(/\{ID\}/g, idStr).replace(/\{SLUG\}/g, opts.slug).replace(/\{ISO\}/g, now);
11956
12050
  const fileSlug = opts.slug.replace(/-/g, "_");
11957
12051
  const fileName = `${idStr}_${fileSlug}.md`;
11958
- const outPath = path57.join(pendingDir, fileName);
12052
+ const outPath = path58.join(pendingDir, fileName);
11959
12053
  try {
11960
- fs45.mkdirSync(pendingDir, { recursive: true });
11961
- fs45.writeFileSync(outPath, content, "utf8");
12054
+ fs46.mkdirSync(pendingDir, { recursive: true });
12055
+ fs46.writeFileSync(outPath, content, "utf8");
11962
12056
  } catch (err) {
11963
12057
  const msg = err instanceof Error ? err.message : String(err);
11964
12058
  stderrFn(`[cleargate hotfix new] write failed: ${msg}`);