gm-skill 2.0.1902 → 2.0.1904

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.
@@ -1 +1 @@
1
- 0.1.868
1
+ 0.1.869
@@ -1 +1 @@
1
- 7986602074ddfb748ae4a392d072781ac6d15bcd97cfe553439059d1fba707c7 plugkit.wasm
1
+ 1b6631e4a6fffbf921b81f1eda73d1c9ed36739f1b5bbddcade3bd7320373f94 plugkit.wasm
@@ -70,11 +70,40 @@ function clearBootstrapError() {
70
70
  } catch (_) {}
71
71
  }
72
72
 
73
+ function sha256Hex(buf) {
74
+ return crypto.createHash('sha256').update(buf).digest('hex');
75
+ }
76
+
77
+ // User/agent edits to .gm/instructions/*.md are the whole point of vendoring
78
+ // them per-project -- a bare content-diff overwrite treats "user diverged
79
+ // from the shipped default" identically to "file is just stale", silently
80
+ // clobbering local edits on every routine bootstrap/auto-update. The
81
+ // manifest records the sha256 of what THIS install last shipped for each
82
+ // key; a local file matching that hash is safe to refresh (it's untouched),
83
+ // but a local file that differs from BOTH the manifest AND the new default
84
+ // is a real user edit -- write the new default beside it as .md.new instead
85
+ // of overwriting, so the edit survives and the update is still visible.
86
+ function instructionsManifestPath(cwd) {
87
+ return path.join(cwd, '.gm', '.instructions-shipped-manifest.json');
88
+ }
89
+
90
+ function readInstructionsManifest(cwd) {
91
+ try { return JSON.parse(fs.readFileSync(instructionsManifestPath(cwd), 'utf-8')); }
92
+ catch (_) { return {}; }
93
+ }
94
+
95
+ function writeInstructionsManifest(cwd, manifest) {
96
+ try { fs.writeFileSync(instructionsManifestPath(cwd), JSON.stringify(manifest, null, 2)); }
97
+ catch (e) { obsEvent('bootstrap', 'instructions-bundle.manifest-write-failed', { error: e.message }); }
98
+ }
99
+
73
100
  function ensureInstructionsBundle(cwd) {
74
101
  const srcDir = path.join(__dirname, 'instructions');
75
102
  if (!fs.existsSync(srcDir)) return;
76
103
  const dstDir = path.join(cwd, '.gm', 'instructions');
104
+ const manifest = readInstructionsManifest(cwd);
77
105
  let copied = 0;
106
+ let preserved = 0;
78
107
  const walk = (rel) => {
79
108
  const from = path.join(srcDir, rel);
80
109
  for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
@@ -84,17 +113,48 @@ function ensureInstructionsBundle(cwd) {
84
113
  try {
85
114
  fs.mkdirSync(path.dirname(dst), { recursive: true });
86
115
  const next = fs.readFileSync(path.join(srcDir, childRel));
116
+ const nextHash = sha256Hex(next);
87
117
  let prev = null;
88
118
  try { prev = fs.readFileSync(dst); } catch (_) {}
89
- if (!prev || !prev.equals(next)) { fs.writeFileSync(dst, next); copied++; }
119
+ if (!prev) {
120
+ fs.writeFileSync(dst, next);
121
+ manifest[childRel] = nextHash;
122
+ copied++;
123
+ continue;
124
+ }
125
+ if (prev.equals(next)) {
126
+ manifest[childRel] = nextHash;
127
+ continue; // already current, nothing to do
128
+ }
129
+ const lastShippedHash = manifest[childRel];
130
+ const localMatchesLastShipped = lastShippedHash && sha256Hex(prev) === lastShippedHash;
131
+ if (localMatchesLastShipped || !lastShippedHash) {
132
+ // Untouched since we last wrote it (or first time we've ever
133
+ // recorded a hash for this key -- pre-manifest install, treat as
134
+ // ours) -- safe to refresh with the new default.
135
+ fs.writeFileSync(dst, next);
136
+ manifest[childRel] = nextHash;
137
+ copied++;
138
+ } else {
139
+ // Local content diverges from what we shipped: a real user/agent
140
+ // edit. Never overwrite it -- stage the new default beside it so
141
+ // the update is visible without destroying the edit.
142
+ try { fs.writeFileSync(dst + '.new', next); } catch (_) {}
143
+ preserved++;
144
+ obsEvent('bootstrap', 'instructions-bundle.user-edit-preserved', { target: dst });
145
+ }
90
146
  } catch (e) { obsEvent('bootstrap', 'instructions-bundle.target-failed', { target: dst, error: e.message }); }
91
147
  }
92
148
  };
93
149
  try { walk(''); } catch (e) { obsEvent('bootstrap', 'instructions-bundle.walk-failed', { error: e.message }); }
150
+ if (copied > 0 || preserved > 0) writeInstructionsManifest(cwd, manifest);
94
151
  if (copied > 0) {
95
152
  log(`instructions bundle provisioned: ${copied} file(s)`);
96
153
  obsEvent('bootstrap', 'instructions-bundle.provisioned', { copied });
97
154
  }
155
+ if (preserved > 0) {
156
+ log(`instructions bundle: ${preserved} user-edited file(s) preserved (new default staged as .md.new)`);
157
+ }
98
158
  }
99
159
 
100
160
  function ensureNextStepWiring(cwd) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1902",
3
+ "version": "2.0.1904",
4
4
  "description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Includes plugkit-wasm-wrapper for WASM-based spool watching.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1 +1 @@
1
- 0.1.868
1
+ 0.1.869
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1902",
3
+ "version": "2.0.1904",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -17,5 +17,5 @@
17
17
  "publishConfig": {
18
18
  "access": "public"
19
19
  },
20
- "plugkitVersion": "0.1.868"
20
+ "plugkitVersion": "0.1.869"
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1902",
3
+ "version": "2.0.1904",
4
4
  "description": "Canonical universal harness — AI-native software engineering via skill-driven orchestration; bootstraps plugkit for task execution and session isolation. Install in any AI coding agent host.",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",