claude-nomad 0.32.3 → 0.32.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.32.4](https://github.com/funkadelic/claude-nomad/compare/v0.32.3...v0.32.4) (2026-05-30)
4
+
5
+
6
+ ### Fixed
7
+
8
+ * **push:** honor drop-wins for allow in recovery dispatch ([#194](https://github.com/funkadelic/claude-nomad/issues/194)) ([9731ecf](https://github.com/funkadelic/claude-nomad/commit/9731ecf9b3d65a6f10fd3e7fc8e520f839640b25))
9
+
3
10
  ## [0.32.3](https://github.com/funkadelic/claude-nomad/compare/v0.32.2...v0.32.3) (2026-05-30)
4
11
 
5
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-nomad",
3
- "version": "0.32.3",
3
+ "version": "0.32.4",
4
4
  "type": "module",
5
5
  "description": "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
6
6
  "keywords": [
@@ -46,64 +46,65 @@ export async function collectActions(
46
46
  const sid = sessionIdFromFinding(f);
47
47
  const header =
48
48
  `\nFinding: ${f.RuleID} in ${f.File} line ${f.StartLine}` +
49
- (sid !== null ? ` (session: ${sid})` : '') +
49
+ (sid === null ? '' : ` (session: ${sid})`) +
50
50
  '\n [R]edact [A]llow [D]rop session [S]kip (default)\n';
51
51
  actions.set(findingKey(f), parseAction(await prompt(header + '> ')));
52
52
  }
53
53
  return actions;
54
54
  }
55
55
 
56
+ /**
57
+ * Loop-invariant context for `dispatchOne`, built once by `dispatchActions`
58
+ * before iterating findings. Bundling these keeps `dispatchOne` to two
59
+ * parameters. The `redactedSids` and `droppedSids` sets are mutated in place so
60
+ * per-session de-duplication is maintained across the caller's loop.
61
+ */
62
+ type DispatchCtx = {
63
+ findings: Finding[];
64
+ actions: Map<string, FindingAction>;
65
+ ts: string;
66
+ map: PathMap;
67
+ nowMs: () => number;
68
+ scan: (p: string) => Finding[] | null;
69
+ drop: (sid: string, map: PathMap) => boolean;
70
+ redactedSids: Set<string>;
71
+ droppedSids: Set<string>;
72
+ };
73
+
56
74
  /**
57
75
  * Apply one finding's triaged action against local state. Extracted from
58
76
  * `dispatchActions` so each function stays under the cognitive-complexity gate.
59
- * `redactedSids` and `droppedSids` are mutated in place so per-session
60
- * de-duplication is maintained across the caller's loop. Drop wins: once a
61
- * session id appears in `droppedSids`, subsequent redact or allow actions for
62
- * findings in that session are skipped.
77
+ * Drop wins: once a session id appears in `ctx.droppedSids`, subsequent redact
78
+ * or allow actions for findings in that session are skipped.
63
79
  *
64
80
  * @param f The finding to act on.
65
- * @param findings Full findings list (passed to `applyRedact` for per-session redaction).
66
- * @param actions The action map returned by `collectActions`.
67
- * @param ts Backup timestamp.
68
- * @param map Parsed path-map.
69
- * @param nowMs Injectable clock.
70
- * @param scan Injectable scan function for `applyRedact`.
71
- * @param drop Injectable staged-copy remover for the Drop action.
72
- * @param redactedSids Set of already-redacted session ids (mutated in place).
73
- * @param droppedSids Set of already-dropped session ids (mutated in place).
81
+ * @param ctx Loop-invariant dispatch context (see `DispatchCtx`).
74
82
  */
75
- function dispatchOne(
76
- f: Finding,
77
- findings: Finding[],
78
- actions: Map<string, FindingAction>,
79
- ts: string,
80
- map: PathMap,
81
- nowMs: () => number,
82
- scan: (p: string) => Finding[] | null,
83
- drop: (sid: string, map: PathMap) => boolean,
84
- redactedSids: Set<string>,
85
- droppedSids: Set<string>,
86
- ): void {
87
- const action = actions.get(findingKey(f)) ?? 'skip';
83
+ function dispatchOne(f: Finding, ctx: DispatchCtx): void {
84
+ const action = ctx.actions.get(findingKey(f)) ?? 'skip';
88
85
  if (action === 'skip') return;
86
+ const sid = sessionIdFromFinding(f);
87
+ // Drop wins: a dropped session short-circuits every later action for it,
88
+ // including allow, so a stale fingerprint is never written for content that
89
+ // was held back from the push.
90
+ if (sid !== null && ctx.droppedSids.has(sid)) return;
89
91
  if (action === 'allow') {
90
92
  applyAllow(f);
91
93
  return;
92
94
  }
93
- const sid = sessionIdFromFinding(f);
94
95
  if (sid === null) return;
95
- if (droppedSids.has(sid)) return;
96
96
  if (action === 'drop') {
97
- droppedSids.add(sid);
98
- if (drop(sid, map)) {
97
+ ctx.droppedSids.add(sid);
98
+ if (ctx.drop(sid, ctx.map)) {
99
99
  log(
100
100
  `dropped session ${sid} from this push (local transcript kept; the secret remains in your local copy)`,
101
101
  );
102
102
  }
103
103
  return;
104
104
  }
105
- if (action === 'redact' && !redactedSids.has(sid)) {
106
- if (applyRedact(f, findings, ts, map, nowMs, scan)) redactedSids.add(sid);
105
+ if (action === 'redact' && !ctx.redactedSids.has(sid)) {
106
+ if (applyRedact(f, ctx.findings, ctx.ts, ctx.map, ctx.nowMs, ctx.scan))
107
+ ctx.redactedSids.add(sid);
107
108
  }
108
109
  }
109
110
 
@@ -130,10 +131,19 @@ export function dispatchActions(
130
131
  scan: (p: string) => Finding[] | null = scanFile,
131
132
  drop: (sid: string, map: PathMap) => boolean = dropSessionFromStaged,
132
133
  ): void {
133
- const redactedSids = new Set<string>();
134
- const droppedSids = new Set<string>();
134
+ const ctx: DispatchCtx = {
135
+ findings,
136
+ actions,
137
+ ts,
138
+ map,
139
+ nowMs,
140
+ scan,
141
+ drop,
142
+ redactedSids: new Set<string>(),
143
+ droppedSids: new Set<string>(),
144
+ };
135
145
  for (const f of findings) {
136
- dispatchOne(f, findings, actions, ts, map, nowMs, scan, drop, redactedSids, droppedSids);
146
+ dispatchOne(f, ctx);
137
147
  }
138
148
  }
139
149
 
@@ -153,7 +153,8 @@ export function cmdRedact(
153
153
  }
154
154
 
155
155
  if (findings.length === 0) {
156
- log(`no findings${rule !== undefined ? ` for rule ${rule}` : ''} in session ${id}`);
156
+ const ruleClause = rule === undefined ? '' : ` for rule ${rule}`;
157
+ log(`no findings${ruleClause} in session ${id}`);
157
158
  return;
158
159
  }
159
160