claude-nomad 0.32.2 → 0.32.3

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.3](https://github.com/funkadelic/claude-nomad/compare/v0.32.2...v0.32.3) (2026-05-30)
4
+
5
+
6
+ ### Fixed
7
+
8
+ * **push:** hard-block sensitive never-sync files under extras ([#191](https://github.com/funkadelic/claude-nomad/issues/191)) ([6509387](https://github.com/funkadelic/claude-nomad/commit/6509387b5724d13e8aa2122eb99cdd80e58da2ee))
9
+
3
10
  ## [0.32.2](https://github.com/funkadelic/claude-nomad/compare/v0.32.1...v0.32.2) (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.2",
3
+ "version": "0.32.3",
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": [
@@ -1,4 +1,10 @@
1
- import { NEVER_SYNC, PUSH_ALLOWED_STATIC, SUPPORTED_EXTRAS, type PathMap } from './config.ts';
1
+ import {
2
+ ALWAYS_NEVER_SYNC,
3
+ NEVER_SYNC,
4
+ PUSH_ALLOWED_STATIC,
5
+ SUPPORTED_EXTRAS,
6
+ type PathMap,
7
+ } from './config.ts';
2
8
  import { isValidSharedDir } from './config.sharedDirs.guard.ts';
3
9
  import { fail, NomadFatal } from './utils.ts';
4
10
 
@@ -22,17 +28,18 @@ function isAllowed(path: string, allowed: readonly string[]): boolean {
22
28
  }
23
29
 
24
30
  /**
25
- * True when any path segment matches a `NEVER_SYNC` entry (hard-block list).
26
- * Scope exception (Pitfall 6): paths beginning with `shared/extras/` are
27
- * exempt. The segment list was authored against `~/.claude/` semantics for
28
- * ephemeral Claude Code state (`todos/`, `shell-snapshots/`, etc.); inside
29
- * the extras tree, `.planning/todos/` is a meaningful GSD-managed path. The
30
- * narrowed scope preserves the original hard-block for all other surface.
31
+ * True when any path segment matches a hard-block entry. Outside the extras
32
+ * tree the full `NEVER_SYNC` set applies. Inside `shared/extras/` only the
33
+ * `ALWAYS_NEVER_SYNC` subset applies (Pitfall 6): the broader set was authored
34
+ * against `~/.claude/` semantics for ephemeral state, so `.planning/todos/` and
35
+ * similar legitimate GSD content must pass, but genuinely-sensitive host-local
36
+ * files (`.credentials.json`, `settings.local.json`, `.claude.json`, ...) stay
37
+ * blocked even when nested inside a synced extras dir.
31
38
  */
32
39
  function isNeverSync(path: string): boolean {
33
- if (path.startsWith('shared/extras/')) return false;
40
+ const blockSet = path.startsWith('shared/extras/') ? ALWAYS_NEVER_SYNC : NEVER_SYNC;
34
41
  for (const segment of path.split('/')) {
35
- if (NEVER_SYNC.has(segment)) return true;
42
+ if (blockSet.has(segment)) return true;
36
43
  }
37
44
  return false;
38
45
  }
package/src/config.ts CHANGED
@@ -106,19 +106,28 @@ export function allSharedLinks(map: PathMap): string[] {
106
106
  }
107
107
 
108
108
  /**
109
- * Whitelist of names allowed in `path-map.json`'s top-level `extras` field.
110
- * Each entry is either a directory name (e.g. `.planning`) OR a single
111
- * root-level file name (e.g. `CLAUDE.md`); both are validated the same way
112
- * and copied verbatim under `shared/extras/<logical>/<name>`. Gates the
113
- * named-extras opt-in mechanism: only entries appearing in this list are
114
- * eligible for sync. Widening to include `.notes`, `.scratch`, `AGENTS.md`,
115
- * etc. is a one-line edit here with no schema migration required (the field
116
- * is additive on the consumer side). Mirrors `SHARED_LINKS` in shape and
117
- * intent: a short, append-only `as const` tuple that downstream callers
118
- * narrow against.
109
+ * Whitelist of names allowed in the `extras` field of `path-map.json`. Each
110
+ * entry is a directory (e.g. `.planning`) or root-level file (`CLAUDE.md`)
111
+ * copied under `shared/extras/<logical>/<name>`. Only listed names are
112
+ * eligible for sync; widening is a one-line edit with no migration required.
119
113
  */
120
114
  export const SUPPORTED_EXTRAS = ['.planning', 'CLAUDE.md'] as const;
121
115
 
116
+ /**
117
+ * Credential and host-config file names blocked even under `shared/extras/`,
118
+ * where the broader `NEVER_SYNC` segment scan is narrowed to avoid
119
+ * false-blocking ephemeral dir names (`todos`, `plans`, etc.) inside synced
120
+ * `.planning/` trees (Pitfall 6). Strict subset of `NEVER_SYNC`; doctor
121
+ * display and sharedDirs guard use the full set.
122
+ */
123
+ export const ALWAYS_NEVER_SYNC = new Set([
124
+ '.claude.json',
125
+ '.credentials.json',
126
+ 'settings.local.json',
127
+ 'history.jsonl',
128
+ 'stats-cache.json',
129
+ ]);
130
+
122
131
  /**
123
132
  * Path segments that must never cross the sync boundary in either direction.
124
133
  * Defense-in-depth pair with `PUSH_ALLOWED_STATIC`: even if the allow-list
@@ -142,8 +151,7 @@ export const NEVER_SYNC = new Set([
142
151
  'statsig',
143
152
  'telemetry',
144
153
  'ide',
145
- // Host-local caches and runtime state: never useful to share, and named here
146
- // so the sharedDirs guard rejects an accidental opt-in.
154
+ // Host-local caches and runtime state (sharedDirs guard also rejects these).
147
155
  'cache',
148
156
  'backups',
149
157
  'paste-cache',