@kntic/kntic 0.4.3 → 0.4.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/package.json +1 -1
- package/src/commands/update.js +38 -6
- package/src/commands/update.test.js +123 -3
- package/src/commands/usage.js +2 -2
package/package.json
CHANGED
package/src/commands/update.js
CHANGED
|
@@ -183,8 +183,13 @@ function extractLibOnly(tarball, destDir) {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
/**
|
|
186
|
-
* Extract .kntic/lib
|
|
187
|
-
*
|
|
186
|
+
* Extract .kntic/lib/**, .kntic/adrs/**, and .kntic/hooks/gdr/** from a tarball
|
|
187
|
+
* into the destination directory.
|
|
188
|
+
*
|
|
189
|
+
* - .kntic/lib and .kntic/adrs are **replaced** (cleared first, then extracted).
|
|
190
|
+
* - .kntic/hooks/gdr is **updated** (existing files are overwritten or new files
|
|
191
|
+
* added, but files not present in the archive are preserved).
|
|
192
|
+
* - .kntic/gia/weights.json is **replaced** if present in the archive.
|
|
188
193
|
*
|
|
189
194
|
* @param {string} tarball – path to the .tar.gz file
|
|
190
195
|
* @param {string} destDir – target directory (usually ".")
|
|
@@ -192,20 +197,47 @@ function extractLibOnly(tarball, destDir) {
|
|
|
192
197
|
function extractUpdate(tarball, destDir) {
|
|
193
198
|
const libDir = path.join(destDir, ".kntic", "lib");
|
|
194
199
|
const adrsDir = path.join(destDir, ".kntic", "adrs");
|
|
200
|
+
const gdrDir = path.join(destDir, ".kntic", "hooks", "gdr");
|
|
201
|
+
const giaDir = path.join(destDir, ".kntic", "gia");
|
|
195
202
|
|
|
196
203
|
// Ensure target directories exist
|
|
197
204
|
fs.mkdirSync(libDir, { recursive: true });
|
|
198
205
|
fs.mkdirSync(adrsDir, { recursive: true });
|
|
206
|
+
fs.mkdirSync(gdrDir, { recursive: true });
|
|
207
|
+
fs.mkdirSync(giaDir, { recursive: true });
|
|
199
208
|
|
|
200
|
-
// Clear
|
|
209
|
+
// Clear lib and adrs (full replacement)
|
|
201
210
|
clearDirectory(libDir);
|
|
202
211
|
clearDirectory(adrsDir);
|
|
203
212
|
|
|
204
|
-
//
|
|
213
|
+
// Note: gdr is NOT cleared — update semantics (overwrite/add, preserve others)
|
|
214
|
+
// Note: gia is NOT cleared — only weights.json is replaced
|
|
215
|
+
|
|
216
|
+
// Extract .kntic/lib/ and .kntic/adrs/ (guaranteed to be in the archive)
|
|
205
217
|
execSync(
|
|
206
218
|
`tar xzf "${tarball}" -C "${destDir}" "./.kntic/lib/" "./.kntic/adrs/"`,
|
|
207
219
|
{ stdio: "pipe" }
|
|
208
220
|
);
|
|
221
|
+
|
|
222
|
+
// Extract .kntic/hooks/gdr/ separately — archive may not contain it
|
|
223
|
+
try {
|
|
224
|
+
execSync(
|
|
225
|
+
`tar xzf "${tarball}" -C "${destDir}" "./.kntic/hooks/gdr/"`,
|
|
226
|
+
{ stdio: "pipe" }
|
|
227
|
+
);
|
|
228
|
+
} catch (_) {
|
|
229
|
+
// No .kntic/hooks/gdr/ in the archive — that's fine
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Extract .kntic/gia/weights.json separately — archive may not contain it
|
|
233
|
+
try {
|
|
234
|
+
execSync(
|
|
235
|
+
`tar xzf "${tarball}" -C "${destDir}" "./.kntic/gia/weights.json"`,
|
|
236
|
+
{ stdio: "pipe" }
|
|
237
|
+
);
|
|
238
|
+
} catch (_) {
|
|
239
|
+
// No .kntic/gia/weights.json in the archive — that's fine
|
|
240
|
+
}
|
|
209
241
|
}
|
|
210
242
|
|
|
211
243
|
async function update(options = {}) {
|
|
@@ -224,7 +256,7 @@ async function update(options = {}) {
|
|
|
224
256
|
console.log("Updating .kntic/lib …");
|
|
225
257
|
extractLibOnly(tmpFile, ".");
|
|
226
258
|
} else {
|
|
227
|
-
console.log("Updating .kntic/lib and .kntic/
|
|
259
|
+
console.log("Updating .kntic/lib, .kntic/adrs, .kntic/hooks/gdr, and .kntic/gia/weights.json …");
|
|
228
260
|
extractUpdate(tmpFile, ".");
|
|
229
261
|
}
|
|
230
262
|
|
|
@@ -238,7 +270,7 @@ async function update(options = {}) {
|
|
|
238
270
|
if (libOnly) {
|
|
239
271
|
console.log("Done. .kntic/lib updated successfully.");
|
|
240
272
|
} else {
|
|
241
|
-
console.log("Done. .kntic/lib and .kntic/
|
|
273
|
+
console.log("Done. .kntic/lib, .kntic/adrs, .kntic/hooks/gdr, and .kntic/gia/weights.json updated successfully.");
|
|
242
274
|
}
|
|
243
275
|
}
|
|
244
276
|
|
|
@@ -216,12 +216,13 @@ describe("extractUpdate", () => {
|
|
|
216
216
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
217
217
|
});
|
|
218
218
|
|
|
219
|
-
it("extracts
|
|
219
|
+
it("extracts .kntic/lib, .kntic/adrs, and .kntic/hooks/gdr from the archive", () => {
|
|
220
220
|
const tarball = createTarball(tmpDir, {
|
|
221
221
|
".kntic/lib/orchestrator.py": "# orchestrator\n",
|
|
222
222
|
".kntic/lib/skills/validator.py": "# validator\n",
|
|
223
223
|
".kntic/adrs/ADR-001.md": "# ADR 001\n",
|
|
224
224
|
".kntic/adrs/ADR-002.md": "# ADR 002\n",
|
|
225
|
+
".kntic/hooks/gdr/check.sh": "#!/bin/sh\necho check\n",
|
|
225
226
|
".kntic/MEMORY.MD": "# Memory\n",
|
|
226
227
|
"README.md": "# Hello\n",
|
|
227
228
|
});
|
|
@@ -248,7 +249,13 @@ describe("extractUpdate", () => {
|
|
|
248
249
|
"# ADR 002\n"
|
|
249
250
|
);
|
|
250
251
|
|
|
251
|
-
//
|
|
252
|
+
// hooks/gdr files must be extracted
|
|
253
|
+
assert.equal(
|
|
254
|
+
fs.readFileSync(path.join(destDir, ".kntic", "hooks", "gdr", "check.sh"), "utf8"),
|
|
255
|
+
"#!/bin/sh\necho check\n"
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
// Files outside lib/adrs/hooks/gdr must NOT be extracted
|
|
252
259
|
assert.ok(
|
|
253
260
|
!fs.existsSync(path.join(destDir, ".kntic", "MEMORY.MD")),
|
|
254
261
|
"MEMORY.MD must not be extracted"
|
|
@@ -289,7 +296,119 @@ describe("extractUpdate", () => {
|
|
|
289
296
|
);
|
|
290
297
|
});
|
|
291
298
|
|
|
292
|
-
it("
|
|
299
|
+
it("updates .kntic/hooks/gdr without clearing existing files (update semantics)", () => {
|
|
300
|
+
const gdrDir = path.join(destDir, ".kntic", "hooks", "gdr");
|
|
301
|
+
fs.mkdirSync(gdrDir, { recursive: true });
|
|
302
|
+
fs.writeFileSync(path.join(gdrDir, "existing-hook.sh"), "#!/bin/sh\necho existing\n");
|
|
303
|
+
fs.writeFileSync(path.join(gdrDir, "shared-hook.sh"), "#!/bin/sh\necho old\n");
|
|
304
|
+
|
|
305
|
+
const tarball = createTarball(tmpDir, {
|
|
306
|
+
".kntic/lib/orchestrator.py": "# orchestrator\n",
|
|
307
|
+
".kntic/adrs/ADR-001.md": "# ADR 001\n",
|
|
308
|
+
".kntic/hooks/gdr/shared-hook.sh": "#!/bin/sh\necho new\n",
|
|
309
|
+
".kntic/hooks/gdr/new-hook.sh": "#!/bin/sh\necho added\n",
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
extractUpdate(tarball, destDir);
|
|
313
|
+
|
|
314
|
+
// Existing file NOT in archive must be preserved (update semantics, not replace)
|
|
315
|
+
assert.equal(
|
|
316
|
+
fs.readFileSync(path.join(gdrDir, "existing-hook.sh"), "utf8"),
|
|
317
|
+
"#!/bin/sh\necho existing\n",
|
|
318
|
+
"existing hook not in archive must be preserved"
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// File in both archive and disk must be overwritten
|
|
322
|
+
assert.equal(
|
|
323
|
+
fs.readFileSync(path.join(gdrDir, "shared-hook.sh"), "utf8"),
|
|
324
|
+
"#!/bin/sh\necho new\n",
|
|
325
|
+
"shared hook must be overwritten with archive version"
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
// New file from archive must be added
|
|
329
|
+
assert.equal(
|
|
330
|
+
fs.readFileSync(path.join(gdrDir, "new-hook.sh"), "utf8"),
|
|
331
|
+
"#!/bin/sh\necho added\n",
|
|
332
|
+
"new hook from archive must be added"
|
|
333
|
+
);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("works when archive has no .kntic/hooks/gdr directory", () => {
|
|
337
|
+
const gdrDir = path.join(destDir, ".kntic", "hooks", "gdr");
|
|
338
|
+
fs.mkdirSync(gdrDir, { recursive: true });
|
|
339
|
+
fs.writeFileSync(path.join(gdrDir, "my-hook.sh"), "#!/bin/sh\necho mine\n");
|
|
340
|
+
|
|
341
|
+
const tarball = createTarball(tmpDir, {
|
|
342
|
+
".kntic/lib/orchestrator.py": "# orchestrator\n",
|
|
343
|
+
".kntic/adrs/ADR-001.md": "# ADR 001\n",
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Should not throw
|
|
347
|
+
extractUpdate(tarball, destDir);
|
|
348
|
+
|
|
349
|
+
// Existing gdr hook must be preserved
|
|
350
|
+
assert.equal(
|
|
351
|
+
fs.readFileSync(path.join(gdrDir, "my-hook.sh"), "utf8"),
|
|
352
|
+
"#!/bin/sh\necho mine\n",
|
|
353
|
+
"existing gdr hook must be preserved when archive has no gdr"
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
// lib and adrs must still be extracted
|
|
357
|
+
assert.ok(fs.existsSync(path.join(destDir, ".kntic", "lib", "orchestrator.py")));
|
|
358
|
+
assert.ok(fs.existsSync(path.join(destDir, ".kntic", "adrs", "ADR-001.md")));
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("replaces .kntic/gia/weights.json from the archive", () => {
|
|
362
|
+
const giaDir = path.join(destDir, ".kntic", "gia");
|
|
363
|
+
fs.mkdirSync(giaDir, { recursive: true });
|
|
364
|
+
fs.writeFileSync(path.join(giaDir, "weights.json"), '{"old": true}\n');
|
|
365
|
+
fs.writeFileSync(path.join(giaDir, "state.json"), '{"keep": true}\n');
|
|
366
|
+
|
|
367
|
+
const tarball = createTarball(tmpDir, {
|
|
368
|
+
".kntic/lib/orchestrator.py": "# orchestrator\n",
|
|
369
|
+
".kntic/adrs/ADR-001.md": "# ADR 001\n",
|
|
370
|
+
".kntic/gia/weights.json": '{"new": true}\n',
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
extractUpdate(tarball, destDir);
|
|
374
|
+
|
|
375
|
+
// weights.json must be replaced
|
|
376
|
+
assert.equal(
|
|
377
|
+
fs.readFileSync(path.join(giaDir, "weights.json"), "utf8"),
|
|
378
|
+
'{"new": true}\n',
|
|
379
|
+
"weights.json must be replaced with archive version"
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
// Other files in gia must be preserved
|
|
383
|
+
assert.equal(
|
|
384
|
+
fs.readFileSync(path.join(giaDir, "state.json"), "utf8"),
|
|
385
|
+
'{"keep": true}\n',
|
|
386
|
+
"state.json must not be touched"
|
|
387
|
+
);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("works when archive has no .kntic/gia/weights.json", () => {
|
|
391
|
+
const giaDir = path.join(destDir, ".kntic", "gia");
|
|
392
|
+
fs.mkdirSync(giaDir, { recursive: true });
|
|
393
|
+
fs.writeFileSync(path.join(giaDir, "weights.json"), '{"existing": true}\n');
|
|
394
|
+
|
|
395
|
+
const tarball = createTarball(tmpDir, {
|
|
396
|
+
".kntic/lib/orchestrator.py": "# orchestrator\n",
|
|
397
|
+
".kntic/adrs/ADR-001.md": "# ADR 001\n",
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// Should not throw
|
|
401
|
+
extractUpdate(tarball, destDir);
|
|
402
|
+
|
|
403
|
+
// Existing weights.json must be preserved when not in archive
|
|
404
|
+
assert.equal(
|
|
405
|
+
fs.readFileSync(path.join(giaDir, "weights.json"), "utf8"),
|
|
406
|
+
'{"existing": true}\n',
|
|
407
|
+
"existing weights.json must be preserved when archive has none"
|
|
408
|
+
);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it("preserves files outside .kntic/lib, .kntic/adrs, and .kntic/hooks/gdr", () => {
|
|
293
412
|
const knticDir = path.join(destDir, ".kntic");
|
|
294
413
|
fs.mkdirSync(knticDir, { recursive: true });
|
|
295
414
|
fs.writeFileSync(path.join(knticDir, "MEMORY.MD"), "# My memory\n");
|
|
@@ -298,6 +417,7 @@ describe("extractUpdate", () => {
|
|
|
298
417
|
const tarball = createTarball(tmpDir, {
|
|
299
418
|
".kntic/lib/orchestrator.py": "# orchestrator\n",
|
|
300
419
|
".kntic/adrs/ADR-001.md": "# ADR 001\n",
|
|
420
|
+
".kntic/hooks/gdr/check.sh": "#!/bin/sh\n",
|
|
301
421
|
".kntic/MEMORY.MD": "# Archive memory\n",
|
|
302
422
|
"README.md": "# Archive readme\n",
|
|
303
423
|
});
|
package/src/commands/usage.js
CHANGED
|
@@ -9,8 +9,8 @@ function usage() {
|
|
|
9
9
|
console.log(" start Build and start KNTIC services via docker compose (uses kntic.yml + .kntic.env)");
|
|
10
10
|
console.log(" --screen Run inside a GNU screen session");
|
|
11
11
|
console.log(" stop Stop KNTIC services via docker compose");
|
|
12
|
-
console.log(" update Download the latest KNTIC bootstrap and update .kntic/lib and .kntic/
|
|
13
|
-
console.log(" --lib-only Update only .kntic/lib (skip .kntic/adrs)");
|
|
12
|
+
console.log(" update Download the latest KNTIC bootstrap and update .kntic/lib, .kntic/adrs, .kntic/hooks/gdr, and .kntic/gia/weights.json");
|
|
13
|
+
console.log(" --lib-only Update only .kntic/lib (skip .kntic/adrs, .kntic/hooks/gdr, and .kntic/gia/weights.json)");
|
|
14
14
|
console.log("");
|
|
15
15
|
}
|
|
16
16
|
|