cleargate 0.11.3 → 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.js CHANGED
@@ -9,8 +9,9 @@ import {
9
9
  AcquireError,
10
10
  acquireAccessToken,
11
11
  getMembershipState,
12
- loadConfig
13
- } from "./chunk-Q3BTSXCK.js";
12
+ loadConfig,
13
+ saveConfig
14
+ } from "./chunk-WFNLCTY5.js";
14
15
  import {
15
16
  createTokenStore
16
17
  } from "./chunk-4V4QABOJ.js";
@@ -21,7 +22,7 @@ import { Command } from "commander";
21
22
  // package.json
22
23
  var package_default = {
23
24
  name: "cleargate",
24
- version: "0.11.3",
25
+ version: "0.11.5",
25
26
  private: false,
26
27
  type: "module",
27
28
  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.",
@@ -633,7 +634,8 @@ async function joinHandler(opts) {
633
634
  if (UUID_V4_RE.test(opts.inviteUrl)) {
634
635
  token = opts.inviteUrl;
635
636
  const cfg = loadConfig({
636
- flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag }
637
+ flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag },
638
+ ...opts.configPath !== void 0 ? { configPath: opts.configPath } : {}
637
639
  });
638
640
  if (!cfg.mcpUrl) {
639
641
  stderr(
@@ -970,9 +972,15 @@ async function joinHandler(opts) {
970
972
  try {
971
973
  const store = await (opts.createStore ?? createTokenStore)();
972
974
  await store.save(opts.profile, refreshToken);
975
+ saveConfig(
976
+ { mcpUrl: baseUrl },
977
+ opts.configPath !== void 0 ? { configPath: opts.configPath } : {}
978
+ );
973
979
  stdout(`joined project '${projectName}' as '${hostname3()}'
974
980
  `);
975
981
  stdout(`refresh token saved to ${store.backend}.
982
+ `);
983
+ stdout(`mcp_url ${baseUrl} saved to ~/.cleargate/config.json.
976
984
  `);
977
985
  } catch (err) {
978
986
  stderr(
@@ -2430,6 +2438,61 @@ function resolveScaffoldRoot(opts) {
2430
2438
  };
2431
2439
  }
2432
2440
 
2441
+ // src/lib/session-load-delta.ts
2442
+ function canonicalize(value) {
2443
+ if (value === null || typeof value !== "object") {
2444
+ return JSON.stringify(value);
2445
+ }
2446
+ if (Array.isArray(value)) {
2447
+ return "[" + value.map(canonicalize).join(",") + "]";
2448
+ }
2449
+ const obj = value;
2450
+ const sortedKeys = Object.keys(obj).sort();
2451
+ const pairs = sortedKeys.map((k) => JSON.stringify(k) + ":" + canonicalize(obj[k]));
2452
+ return "{" + pairs.join(",") + "}";
2453
+ }
2454
+ var HOOK_EVENTS = ["PreToolUse", "PostToolUse", "SessionStart", "SubagentStop"];
2455
+ function extractSettingsHooksBlock(settings) {
2456
+ const hooks = settings["hooks"] ?? {};
2457
+ const extracted = {};
2458
+ for (const event of HOOK_EVENTS) {
2459
+ if (Object.prototype.hasOwnProperty.call(hooks, event)) {
2460
+ extracted[event] = hooks[event];
2461
+ }
2462
+ }
2463
+ return extracted;
2464
+ }
2465
+ function extractMcpCleargateEntry(mcp2) {
2466
+ const servers = mcp2["mcpServers"] ?? {};
2467
+ return servers["cleargate"] ?? null;
2468
+ }
2469
+ function extractSessionLoadDelta(filePath, oldContent, newContent) {
2470
+ const normalized = filePath.replace(/\\/g, "/");
2471
+ if (normalized === ".claude/settings.json") {
2472
+ try {
2473
+ const oldSettings = JSON.parse(oldContent);
2474
+ const newSettings = JSON.parse(newContent);
2475
+ const oldHooks = extractSettingsHooksBlock(oldSettings);
2476
+ const newHooks = extractSettingsHooksBlock(newSettings);
2477
+ return canonicalize(oldHooks) !== canonicalize(newHooks);
2478
+ } catch {
2479
+ return true;
2480
+ }
2481
+ }
2482
+ if (normalized === ".mcp.json") {
2483
+ try {
2484
+ const oldMcp = JSON.parse(oldContent);
2485
+ const newMcp = JSON.parse(newContent);
2486
+ const oldEntry = extractMcpCleargateEntry(oldMcp);
2487
+ const newEntry = extractMcpCleargateEntry(newMcp);
2488
+ return canonicalize(oldEntry) !== canonicalize(newEntry);
2489
+ } catch {
2490
+ return true;
2491
+ }
2492
+ }
2493
+ return true;
2494
+ }
2495
+
2433
2496
  // src/commands/init.ts
2434
2497
  var HOOK_ADDITION = {
2435
2498
  hooks: {
@@ -2605,10 +2668,17 @@ async function initHandler(opts = {}) {
2605
2668
  }
2606
2669
  }
2607
2670
  const mergedSettings = mergeSettings(existingSettings, HOOK_ADDITION);
2671
+ const mergedSettingsContent = JSON.stringify(mergedSettings, null, 2) + "\n";
2672
+ const existingSettingsContent = existingSettings !== null ? JSON.stringify(existingSettings, null, 2) + "\n" : "{}";
2608
2673
  fs15.mkdirSync(path15.dirname(settingsPath), { recursive: true });
2609
- writeAtomic(settingsPath, JSON.stringify(mergedSettings, null, 2) + "\n");
2610
- stdout(`[cleargate init] Updated .claude/settings.json: merged PostToolUse hook \u2014 restart Claude Code if already open.
2674
+ writeAtomic(settingsPath, mergedSettingsContent);
2675
+ if (extractSessionLoadDelta(".claude/settings.json", existingSettingsContent, mergedSettingsContent)) {
2676
+ stdout(`[cleargate init] Updated .claude/settings.json: merged PostToolUse hook \u2014 restart Claude Code if already open.
2677
+ `);
2678
+ } else {
2679
+ stdout(`[cleargate init] .claude/settings.json unchanged (hooks block already current)
2611
2680
  `);
2681
+ }
2612
2682
  const claudeMdPath = path15.join(cwd, "CLAUDE.md");
2613
2683
  const claudeMdSrcPath = path15.join(payloadDir, "CLAUDE.md");
2614
2684
  let claudeMdBlock;
@@ -7775,7 +7845,16 @@ function removeClearGateHooks(settings) {
7775
7845
  // src/lib/merge-ui.ts
7776
7846
  import { createPatch } from "diff";
7777
7847
  function renderInlineDiff(ours, theirs, filePath) {
7778
- return createPatch(filePath, ours, theirs, "installed", "upstream");
7848
+ const patch = createPatch(filePath, ours, theirs, "installed", "upstream");
7849
+ const hasHunkLines = patch.split("\n").filter((l) => l.startsWith("+") || l.startsWith("-")).filter((l) => !l.startsWith("+++") && !l.startsWith("---")).length > 0;
7850
+ if (!hasHunkLines) {
7851
+ const ourBytes = Buffer.byteLength(ours, "utf-8");
7852
+ const theirBytes = Buffer.byteLength(theirs, "utf-8");
7853
+ const byteNote = ourBytes !== theirBytes ? `${Math.abs(theirBytes - ourBytes)} bytes changed` : "same byte count";
7854
+ return patch + `(whitespace/EOL-only differences \u2014 ${byteNote})
7855
+ `;
7856
+ }
7857
+ return patch;
7779
7858
  }
7780
7859
  async function promptMergeChoice(opts) {
7781
7860
  const { path: filePath, state: state2, ours, theirs } = opts;
@@ -8070,7 +8149,15 @@ async function upgradeHandler(flags, cli) {
8070
8149
  let count = 0;
8071
8150
  for (const item of workItems) {
8072
8151
  const state2 = classify(item.entry.sha256, item.installSha, item.currentSha, item.entry.tier);
8073
- stdout(`[dry-run] ${item.entry.path} action=${item.action} state=${state2}`);
8152
+ const projectedPostSha = item.entry.sha256;
8153
+ const projectedPostState = classify(
8154
+ item.entry.sha256,
8155
+ item.entry.sha256,
8156
+ projectedPostSha,
8157
+ item.entry.tier
8158
+ );
8159
+ const stateLabel = state2 !== projectedPostState ? `state=${state2} \u2192 ${projectedPostState}` : `state=${state2}`;
8160
+ stdout(`[dry-run] ${item.entry.path} action=${item.action} ${stateLabel}`);
8074
8161
  count++;
8075
8162
  }
8076
8163
  stdout(`[dry-run] ${count} files planned. No changes made.`);
@@ -8082,6 +8169,15 @@ async function upgradeHandler(flags, cli) {
8082
8169
  const sessionRestartFiles = [];
8083
8170
  for (const item of workItems) {
8084
8171
  const { entry, currentSha, installSha, action } = item;
8172
+ let preMutationContent = null;
8173
+ if (SESSION_LOAD_PATHS.has(entry.path)) {
8174
+ const targetPath = path36.join(cwd, entry.path);
8175
+ try {
8176
+ preMutationContent = await fsp.readFile(targetPath, "utf-8");
8177
+ } catch {
8178
+ preMutationContent = "";
8179
+ }
8180
+ }
8085
8181
  switch (action) {
8086
8182
  case "skip": {
8087
8183
  stdout(`[skip] ${entry.path} policy=${entry.overwrite_policy}`);
@@ -8112,8 +8208,17 @@ async function upgradeHandler(flags, cli) {
8112
8208
  current_sha: postSha,
8113
8209
  package_sha: entry.sha256
8114
8210
  };
8115
- if (SESSION_LOAD_PATHS.has(entry.path) && postSha !== currentSha) {
8116
- sessionRestartFiles.push(entry.path);
8211
+ if (SESSION_LOAD_PATHS.has(entry.path) && preMutationContent !== null) {
8212
+ const targetPath = path36.join(cwd, entry.path);
8213
+ let postMutationContent;
8214
+ try {
8215
+ postMutationContent = await fsp.readFile(targetPath, "utf-8");
8216
+ } catch {
8217
+ postMutationContent = "";
8218
+ }
8219
+ if (extractSessionLoadDelta(entry.path, preMutationContent, postMutationContent)) {
8220
+ sessionRestartFiles.push(entry.path);
8221
+ }
8117
8222
  }
8118
8223
  }
8119
8224
  await writeDriftState(cwd, driftMap, { lastRefreshed: now.toISOString() });
@@ -11022,7 +11127,7 @@ program.command("init").description("initialise a repo with ClearGate scaffold (
11022
11127
  await initHandler({ force: opts.force ?? false, yes: opts.yes ?? false, pin: opts.pin, fromSource: opts.fromSource });
11023
11128
  });
11024
11129
  program.command("whoami").description("print the currently authenticated agent identity").option("--json", "CR-011: emit membership state as JSON (no network call)").action(async (opts) => {
11025
- const { whoamiHandler } = await import("./whoami-W4U6DPVG.js");
11130
+ const { whoamiHandler } = await import("./whoami-GQTFZHFQ.js");
11026
11131
  const parentOpts = program.opts();
11027
11132
  await whoamiHandler({
11028
11133
  profile: parentOpts.profile,