@timekast/factory 1.4.0 → 1.5.0
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 +38 -7
- package/dist/lib/constants.js +9 -0
- package/dist/lib/gitattributes.js +14 -13
- package/package.json +1 -1
package/dist/commands/update.js
CHANGED
|
@@ -41,7 +41,7 @@ import prompts from 'prompts';
|
|
|
41
41
|
import { applyPlan, clearUpdateState, defaultBackupDir, hasUpdateState, readUpdateState, validateStagedManifest, writeUpdateState, } from '../lib/atomic-swap.js';
|
|
42
42
|
import { collectClaudePaths } from '../lib/claude-paths.js';
|
|
43
43
|
import { CLIError } from '../lib/cli-error.js';
|
|
44
|
-
import { CLAUDE_MD_FILE, GITATTRIBUTES_FILE, LOCKFILE_FILE, PROFILES, TIMEKAST_DIR, } from '../lib/constants.js';
|
|
44
|
+
import { CLAUDE_MD_FILE, GITATTRIBUTES_FILE, LOCKFILE_FILE, PRETTIERIGNORE_FILE, PROFILES, TIMEKAST_DIR, } from '../lib/constants.js';
|
|
45
45
|
import { decideCommit } from '../lib/commit-decision.js';
|
|
46
46
|
import { extractManagedBlock, syncManagedBlock } from '../lib/gitattributes.js';
|
|
47
47
|
import { diffLockfiles, hasLockfile, normalizeThenHash, planAutoRegister, readLockfile, writeLockfile, } from '../lib/lockfile.js';
|
|
@@ -186,6 +186,7 @@ async function maybeCommitBrain(rootDir, version, appliedPaths, flags) {
|
|
|
186
186
|
`${TIMEKAST_DIR}/${LOCKFILE_FILE}`,
|
|
187
187
|
...(existsSync(path.join(rootDir, 'package.json')) ? ['package.json'] : []),
|
|
188
188
|
...(existsSync(path.join(rootDir, GITATTRIBUTES_FILE)) ? [GITATTRIBUTES_FILE] : []),
|
|
189
|
+
...(existsSync(path.join(rootDir, PRETTIERIGNORE_FILE)) ? [PRETTIERIGNORE_FILE] : []),
|
|
189
190
|
];
|
|
190
191
|
try {
|
|
191
192
|
await execa('git', ['add', '--', ...candidates], { cwd: rootDir, reject: false });
|
|
@@ -251,16 +252,18 @@ function warnOnScriptConflict(action, _pkgPath) {
|
|
|
251
252
|
}
|
|
252
253
|
}
|
|
253
254
|
/**
|
|
254
|
-
* After a sync, maintain the derived project's co-owned dotfiles — `package.json
|
|
255
|
-
* AND `.
|
|
256
|
-
* all THREE update paths (main, legacy auto-register, resume) are
|
|
257
|
-
* construction: a per-command call already regressed once (legacy +
|
|
258
|
-
* missed). `stagedDir` is the freshly-unpacked tarball, the source of
|
|
259
|
-
*
|
|
255
|
+
* After a sync, maintain the derived project's co-owned dotfiles — `package.json`,
|
|
256
|
+
* `.gitattributes` AND `.prettierignore` — in one place. Centralized (not scattered
|
|
257
|
+
* per command) so all THREE update paths (main, legacy auto-register, resume) are
|
|
258
|
+
* covered by construction: a per-command call already regressed once (legacy +
|
|
259
|
+
* resume were missed). `stagedDir` is the freshly-unpacked tarball, the source of
|
|
260
|
+
* the managed blocks. Runs BEFORE any brain commit, so the blocks land before a
|
|
261
|
+
* pre-commit hook could reformat the freshly-installed kit files.
|
|
260
262
|
*/
|
|
261
263
|
function maintainDerivedDotfiles(rootDir, stagedDir, agentKitVersion) {
|
|
262
264
|
maintainDerivedPkg(rootDir, agentKitVersion);
|
|
263
265
|
syncDerivedGitattributes(rootDir, stagedDir);
|
|
266
|
+
syncDerivedPrettierignore(rootDir, stagedDir);
|
|
264
267
|
}
|
|
265
268
|
/**
|
|
266
269
|
* Maintain the derived project's package.json: ensure the `factory:*` scripts
|
|
@@ -317,6 +320,34 @@ function syncDerivedGitattributes(rootDir, stagedDir) {
|
|
|
317
320
|
console.warn('Aviso: no se pudo sincronizar `.gitattributes`; el cerebro se instaló igual.');
|
|
318
321
|
}
|
|
319
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Sync the Factory's managed `.prettierignore` block (keeps the derived repo's
|
|
325
|
+
* formatter away from `.claude/**` — a pre-commit `prettier --write` over the kit
|
|
326
|
+
* files drifts disk from the lockfile hashes and turns every later update into
|
|
327
|
+
* false conflicts) into the derived repo, preserving the dev's own ignore rules.
|
|
328
|
+
* Same contract as `syncDerivedGitattributes`: canonical block READ from the
|
|
329
|
+
* staged tarball (single SSOT), best-effort, tarball without the file (pre-block
|
|
330
|
+
* releases) → silent no-op, a write failure never fails an applied update.
|
|
331
|
+
*/
|
|
332
|
+
function syncDerivedPrettierignore(rootDir, stagedDir) {
|
|
333
|
+
try {
|
|
334
|
+
const srcPath = path.join(stagedDir, PRETTIERIGNORE_FILE);
|
|
335
|
+
if (!existsSync(srcPath))
|
|
336
|
+
return;
|
|
337
|
+
const block = extractManagedBlock(readFileSync(srcPath, 'utf8'));
|
|
338
|
+
if (!block)
|
|
339
|
+
return;
|
|
340
|
+
const { action } = syncManagedBlock(rootDir, block, PRETTIERIGNORE_FILE);
|
|
341
|
+
if (action === 'unchanged')
|
|
342
|
+
return;
|
|
343
|
+
console.log(action === 'created'
|
|
344
|
+
? '✔ `.prettierignore` creado con el ignore del cerebro (`.claude/`) — evita falsos conflictos por formato en futuros updates.'
|
|
345
|
+
: '✔ `.prettierignore`: bloque del kit sincronizado (`.claude/` fuera del formatter).');
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
console.warn('Aviso: no se pudo sincronizar `.prettierignore`; el cerebro se instaló igual.');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
320
351
|
/** Print the deletes + kept-retired (design §7.6 — visible) + a one-line summary. */
|
|
321
352
|
function reportSummary(diff, manifest) {
|
|
322
353
|
if (diff.deleteSilent.length > 0) {
|
package/dist/lib/constants.js
CHANGED
|
@@ -39,6 +39,15 @@ export const CLAUDE_MD_FILE = 'CLAUDE.md';
|
|
|
39
39
|
* preserved byte-for-byte. See `syncManagedBlock` in `lib/gitattributes.ts`.
|
|
40
40
|
*/
|
|
41
41
|
export const GITATTRIBUTES_FILE = '.gitattributes';
|
|
42
|
+
/**
|
|
43
|
+
* Repo-root `.prettierignore`. Same dual-owned managed-block treatment as
|
|
44
|
+
* `.gitattributes` (NOT tracked in the manifest — the dev's own ignore rules
|
|
45
|
+
* would read as local edits): the kit's block keeps the derived project's
|
|
46
|
+
* formatter away from `.claude/**`, whose prettier rewrite on the brain commit
|
|
47
|
+
* (table alignment, emphasis style) drifts disk from the lockfile hashes and
|
|
48
|
+
* turns every later `factory:update` into false conflicts (A2).
|
|
49
|
+
*/
|
|
50
|
+
export const PRETTIERIGNORE_FILE = '.prettierignore';
|
|
42
51
|
/**
|
|
43
52
|
* Managed-block sentinels. These are PREFIXES (the live source lines carry extra
|
|
44
53
|
* descriptive text + trailing `>>>`/`<<<`), matched with `startsWith` so detection
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Surgical sync of
|
|
3
|
-
*
|
|
4
|
-
* the delimited block is replaced
|
|
2
|
+
* Surgical sync of a Factory-managed block into a derived project's dual-owned
|
|
3
|
+
* dotfile (`.gitattributes`, `.prettierignore`), mirroring the `package.json`
|
|
4
|
+
* script-insertion pattern (`package-json.ts`): the delimited block is replaced
|
|
5
|
+
* verbatim, everything outside it is preserved.
|
|
5
6
|
*
|
|
6
|
-
* Why not the lockfile/`track` engine:
|
|
7
|
+
* Why not the lockfile/`track` engine: these files are dual-owned at the
|
|
7
8
|
* intra-file level (the Factory block + the dev's own rules share one file). The
|
|
8
|
-
* lockfile hashes whole files, so tracking
|
|
9
|
+
* lockfile hashes whole files, so tracking them would read the dev's rules as a
|
|
9
10
|
* local edit and conflict on every update. Instead the block is synced as an
|
|
10
11
|
* idempotent post-apply step, outside `diffLockfiles`/`applyPlan`.
|
|
11
12
|
*
|
|
12
|
-
* The canonical block is the single SSOT: it is READ from the
|
|
13
|
+
* The canonical block is the single SSOT: it is READ from the same-named file
|
|
13
14
|
* that ships in the staged tarball (see `extractManagedBlock`), never duplicated
|
|
14
15
|
* as a CLI constant. A derived repo's existing block is located by the
|
|
15
16
|
* `MANAGED_BLOCK_START` / `_END` sentinels (prefix match), so the descriptive
|
|
@@ -49,11 +50,11 @@ export function extractManagedBlock(content) {
|
|
|
49
50
|
return slice.join('\n');
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
52
|
-
* Sync `sourceBlock` (markers inclusive) into `<rootDir
|
|
53
|
-
* preserving every rule outside the managed block. The whole
|
|
54
|
-
* to LF (it lives at repo root, outside `.claude/** text eol=lf`,
|
|
55
|
-
* packed with CRLF on an autocrlf machine is normalized here).
|
|
56
|
-
* writes only when the bytes actually change.
|
|
53
|
+
* Sync `sourceBlock` (markers inclusive) into `<rootDir>/<fileName>` (default
|
|
54
|
+
* `.gitattributes`), preserving every rule outside the managed block. The whole
|
|
55
|
+
* file is normalized to LF (it lives at repo root, outside `.claude/** text eol=lf`,
|
|
56
|
+
* so a source packed with CRLF on an autocrlf machine is normalized here).
|
|
57
|
+
* Idempotent: writes only when the bytes actually change.
|
|
57
58
|
*
|
|
58
59
|
* - no file / empty file → `created` (block only)
|
|
59
60
|
* - file without a start marker → `inserted` (block appended after a blank line)
|
|
@@ -61,8 +62,8 @@ export function extractManagedBlock(content) {
|
|
|
61
62
|
* - start without end (dev clobbered half a line) → `updated`, regenerated
|
|
62
63
|
* cleanly from start to EOF (NOT appended — avoids a dangling start marker)
|
|
63
64
|
*/
|
|
64
|
-
export function syncManagedBlock(rootDir, sourceBlock) {
|
|
65
|
-
const filePath = path.join(rootDir,
|
|
65
|
+
export function syncManagedBlock(rootDir, sourceBlock, fileName = GITATTRIBUTES_FILE) {
|
|
66
|
+
const filePath = path.join(rootDir, fileName);
|
|
66
67
|
const raw = existsSync(filePath) ? readFileSync(filePath, 'utf8') : '';
|
|
67
68
|
const block = sourceBlock.replace(/\r\n/g, '\n').replace(/\n+$/, '');
|
|
68
69
|
let result;
|