@timekast/factory 0.1.1 → 0.1.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/commands/update.js +16 -4
- package/dist/lib/package-json.js +50 -0
- package/package.json +2 -1
package/dist/commands/update.js
CHANGED
|
@@ -37,7 +37,7 @@ import prompts from 'prompts';
|
|
|
37
37
|
import { applyPlan, clearUpdateState, defaultBackupDir, hasUpdateState, readUpdateState, validateStagedManifest, writeUpdateState, } from '../lib/atomic-swap.js';
|
|
38
38
|
import { CLIError } from '../lib/cli-error.js';
|
|
39
39
|
import { diffLockfiles, hasLockfile, normalizeThenHash, planAutoRegister, readLockfile, writeLockfile, } from '../lib/lockfile.js';
|
|
40
|
-
import { insertFactoryUpdateScript } from '../lib/package-json.js';
|
|
40
|
+
import { insertFactoryUpdateScript, pruneFactoryMeta } from '../lib/package-json.js';
|
|
41
41
|
import { runPreflight } from '../lib/preflight.js';
|
|
42
42
|
import { downloadProfileTarball, stageProfileTarball } from '../lib/unpack.js';
|
|
43
43
|
/** Parse `update`'s argv slice into flags. Unknown flags are ignored here. */
|
|
@@ -186,7 +186,7 @@ function runResume(rootDir) {
|
|
|
186
186
|
clearUpdateState(rootDir);
|
|
187
187
|
const pkgPath = path.join(rootDir, 'package.json');
|
|
188
188
|
if (existsSync(pkgPath))
|
|
189
|
-
|
|
189
|
+
maintainDerivedPkg(pkgPath);
|
|
190
190
|
rmSync(state.stagedDir, { recursive: true, force: true });
|
|
191
191
|
console.log('✔ `update` retomado y completado.');
|
|
192
192
|
}
|
|
@@ -206,6 +206,18 @@ function warnOnScriptConflict(action, _pkgPath) {
|
|
|
206
206
|
console.warn('Aviso: `factory:update` ya existe en package.json con otro valor; no se sobrescribió.');
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* After a sync, maintain the derived project's package.json: ensure the
|
|
211
|
+
* `factory:update` script (warn on a divergent value) and prune the stale
|
|
212
|
+
* `agentKitVersion` leaked by the template. The lockfile — not package.json — is
|
|
213
|
+
* the live-version SSOT, so that copy only drifts; pruning it here also cleans
|
|
214
|
+
* derivatives that predate this fix on their next update. `factoryVersion` (the
|
|
215
|
+
* frozen birth stamp) is kept.
|
|
216
|
+
*/
|
|
217
|
+
function maintainDerivedPkg(pkgPath) {
|
|
218
|
+
warnOnScriptConflict(insertFactoryUpdateScript(pkgPath).action, pkgPath);
|
|
219
|
+
pruneFactoryMeta(pkgPath);
|
|
220
|
+
}
|
|
209
221
|
/** Print the deletes + kept-retired (design §7.6 — visible) + a one-line summary. */
|
|
210
222
|
function reportSummary(diff) {
|
|
211
223
|
if (diff.deleteSilent.length > 0) {
|
|
@@ -300,7 +312,7 @@ export async function runUpdate(flags, deps = {}) {
|
|
|
300
312
|
clearUpdateState(rootDir);
|
|
301
313
|
const pkgPath = path.join(rootDir, 'package.json');
|
|
302
314
|
if (existsSync(pkgPath))
|
|
303
|
-
|
|
315
|
+
maintainDerivedPkg(pkgPath);
|
|
304
316
|
reportSummary(diff);
|
|
305
317
|
}
|
|
306
318
|
finally {
|
|
@@ -340,7 +352,7 @@ async function applyLegacy(rootDir, stagedDir, manifest) {
|
|
|
340
352
|
writeLockfile(rootDir, { ...manifest, factoryVersion: manifest.version });
|
|
341
353
|
const pkgPath = path.join(rootDir, 'package.json');
|
|
342
354
|
if (existsSync(pkgPath))
|
|
343
|
-
|
|
355
|
+
maintainDerivedPkg(pkgPath);
|
|
344
356
|
if (plan.ambiguous.length > 0) {
|
|
345
357
|
console.log('\nArchivos en `.claude/` que no están en el manifest (revisar manualmente):');
|
|
346
358
|
for (const p of plan.ambiguous)
|
package/dist/lib/package-json.js
CHANGED
|
@@ -31,6 +31,31 @@ export function parsePackageJson(raw) {
|
|
|
31
31
|
throw new CLIError('El `package.json` desempacado no es JSON válido; no se puede continuar de forma segura.');
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Stale Factory version metadata to prune from a derivative's `package.json`.
|
|
36
|
+
*
|
|
37
|
+
* The `full` template's `package.json` carries `factoryVersion` + `agentKitVersion`
|
|
38
|
+
* for the Factory's dual-version release model, and both leak into a derivative on
|
|
39
|
+
* unpack. Only `agentKitVersion` is pruned: it is the LIVE brain version, whose
|
|
40
|
+
* SSOT in a derivative is `.timekast/lockfile.json` (`version`), so a `package.json`
|
|
41
|
+
* copy just goes stale on every `update` and misleads (what `status`/`doctor` report
|
|
42
|
+
* comes from the lockfile, never from here). `factoryVersion` is deliberately KEPT —
|
|
43
|
+
* it is the FROZEN birth stamp (never changes after install) and the canonical
|
|
44
|
+
* boilerplate marker (DISTRIBUTION_DESIGN §10). `version` (the derivative's own app
|
|
45
|
+
* semver) is untouched.
|
|
46
|
+
*/
|
|
47
|
+
const FACTORY_META_KEYS = ['agentKitVersion'];
|
|
48
|
+
/** Delete stale Factory version metadata in place. Returns true if a key was removed. */
|
|
49
|
+
function stripFactoryMeta(pkg) {
|
|
50
|
+
let changed = false;
|
|
51
|
+
for (const key of FACTORY_META_KEYS) {
|
|
52
|
+
if (key in pkg) {
|
|
53
|
+
delete pkg[key];
|
|
54
|
+
changed = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return changed;
|
|
58
|
+
}
|
|
34
59
|
/**
|
|
35
60
|
* Pure core of the script insertion: mutate `pkg.scripts` in place to ensure
|
|
36
61
|
* `factory:update` exists, never overwriting a divergent value. Shared by
|
|
@@ -63,6 +88,9 @@ export function applyPackageJsonEdits(raw, name) {
|
|
|
63
88
|
const pkg = parsePackageJson(raw);
|
|
64
89
|
pkg.name = name;
|
|
65
90
|
const result = ensureUpdateScript(pkg);
|
|
91
|
+
// Prune the stale `agentKitVersion` leaked by the unpacked template; the
|
|
92
|
+
// derivative's live brain version lives in the lockfile, not here.
|
|
93
|
+
stripFactoryMeta(pkg);
|
|
66
94
|
const content = `${JSON.stringify(pkg, null, indent)}\n`;
|
|
67
95
|
return { content, scriptAlreadyPresent: result.action === 'conflict' };
|
|
68
96
|
}
|
|
@@ -88,3 +116,25 @@ export function insertFactoryUpdateScript(pkgPath) {
|
|
|
88
116
|
}
|
|
89
117
|
return result;
|
|
90
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Surgically prune the stale `agentKitVersion` leaked into a derived project's
|
|
121
|
+
* `package.json` when the `full` profile is unpacked. It is NOT the version SSOT
|
|
122
|
+
* — the lockfile is — so it goes stale on every update and misleads. Reads,
|
|
123
|
+
* deletes only that key, and re-serializes with the original indentation, writing
|
|
124
|
+
* only when something changed. NEVER touches `name`, `version`, `factoryVersion`
|
|
125
|
+
* (the frozen birth stamp), deps, or scripts.
|
|
126
|
+
*
|
|
127
|
+
* Idempotent: a derivative already free of the key is left byte-identical.
|
|
128
|
+
*
|
|
129
|
+
* @param pkgPath Absolute path to the derived project's `package.json`.
|
|
130
|
+
* @returns true when a field was removed (and the file rewritten).
|
|
131
|
+
*/
|
|
132
|
+
export function pruneFactoryMeta(pkgPath) {
|
|
133
|
+
const raw = readFileSync(pkgPath, 'utf8');
|
|
134
|
+
const indent = detectIndent(raw);
|
|
135
|
+
const pkg = parsePackageJson(raw);
|
|
136
|
+
if (!stripFactoryMeta(pkg))
|
|
137
|
+
return false;
|
|
138
|
+
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, indent)}\n`, 'utf8');
|
|
139
|
+
return true;
|
|
140
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timekast/factory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Public, thin CLI to bootstrap and maintain TimeKast Factory derived projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
35
|
"prepublishOnly": "pnpm build",
|
|
36
36
|
"test": "vitest run",
|
|
37
|
+
"test:unit": "vitest run tests/unit",
|
|
37
38
|
"test:watch": "vitest"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|