magic-spec 1.4.77 → 1.4.162
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 +21 -2
- package/README.md +22 -6
- package/installers/config.json +9 -1
- package/installers/node/index.js +95 -27
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,26 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [1.4.
|
|
8
|
+
## [1.4.162] - 2026-03-12
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Session Isolation Rule (C17)**: Formalized the requirement for "New Chat" sessions between major workflow transitions (Spec → Task → Run) to prevent context bleed-over and hallucinations.
|
|
13
|
+
- **Multi-Workspace Support (C22)**: Enhanced `check-prerequisites.js` and `init.js` to support nested workspaces with inherited root rules.
|
|
14
|
+
- **Model-Aware History**: Updated engine history schema to include AI Model information, improving auditability of generated code.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **Task Checklist Logic**: Consolidated implementation checklists into `TASKS.md` for better execution tracking and status reporting.
|
|
19
|
+
- **Gitignore Resilience**: Improved installers to automatically manage `.gitignore` entries for `.magic/` and `.agent/` directories with idempotent updates.
|
|
20
|
+
- **Onboarding Safety**: Added production data collision guards to `onboard.md` to prevent accidental overwrites of existing project plans.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **Error Reporting**: Enhanced `check-prerequisites.js` with structured, actionable JSON error suggestions.
|
|
25
|
+
- **Path Handling**: Fixed Windows-specific path issues in installer scripts.
|
|
26
|
+
|
|
27
|
+
## [1.4.162] - 2026-03-03
|
|
9
28
|
|
|
10
29
|
### Added
|
|
11
30
|
|
|
@@ -20,7 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
20
39
|
|
|
21
40
|
- **Resilient Logic**: Improved `check-prerequisites.js` to distinguish between critical engine integrity (HALT) and project data drift (WARNING), preserving self-healing capabilities.
|
|
22
41
|
|
|
23
|
-
## [1.4.
|
|
42
|
+
## [1.4.162] - 2026-03-02
|
|
24
43
|
|
|
25
44
|
### Changed
|
|
26
45
|
|
package/README.md
CHANGED
|
@@ -55,16 +55,23 @@ root-project/
|
|
|
55
55
|
|
|
56
56
|
Magic Spec is built around a single conviction: **AI agents write better code when they are forced to think before they act.** Left unconstrained, they jump straight to implementation — producing code that is fragile, misaligned, and expensive to refactor. Magic Spec installs a structured pipeline that makes this impossible.
|
|
57
57
|
|
|
58
|
-
### Human-Minimal Engineering
|
|
58
|
+
### Human-Minimal Engineering (Autonomous Partner)
|
|
59
59
|
|
|
60
|
-
The core design goal is to **keep humans out of the loop as much as possible** — without sacrificing control over what actually matters.
|
|
60
|
+
The core design goal is to **keep humans out of the loop as much as possible** — without sacrificing control over what actually matters. Magic Spec moves from manual "Status Gates" to an **Autonomous Partner** model.
|
|
61
|
+
|
|
62
|
+
#### Trust Mode: Encapsulated Logic
|
|
61
63
|
|
|
62
64
|
Once you describe what you want, the engine takes over:
|
|
63
65
|
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
- **Type A — "AI Trust"**: You provide intent, the agent handles the rest (`Draft -> RFC -> Stable -> Plan -> Run`). The internal SDD ceremony is **encapsulated** — you only see the result and a final "Go" gate.
|
|
67
|
+
- **Type B — "Expert Audit"**: You maintain full control. Inspect `.design/` at any time to review specifications and plans. The rigor is there for when you need it.
|
|
68
|
+
|
|
69
|
+
#### Silent Orchestration
|
|
70
|
+
|
|
71
|
+
- **Auto-Stabilization**: Specifications are drafted, reviewed, and promoted to `Stable` automatically if the logic is clear.
|
|
72
|
+
- **Zero-Prompt Planning**: Tasks are decomposed, prioritized, and scheduled without interrupting your flow.
|
|
73
|
+
- **Silent Operations**: Phases execute end-to-end: retrospectives, changelogs, and context regeneration happen silently.
|
|
74
|
+
- **Single Execution Gate**: The only mandatory prompt is the final sign-off before implementation begins.
|
|
68
75
|
|
|
69
76
|
Everything else is automated. The agent does the engineering. You approve the direction.
|
|
70
77
|
|
|
@@ -87,6 +94,7 @@ This separation prevents a common failure mode in AI-assisted development: mixin
|
|
|
87
94
|
The engine actively protects specification integrity throughout the project lifecycle:
|
|
88
95
|
|
|
89
96
|
- **Quarantine Cascade**: If a Layer 1 spec is destabilized (demoted from `Stable`), all dependent Layer 2 specs are automatically flagged and their tasks are blocked. The plan cannot proceed on a broken foundation.
|
|
97
|
+
- **Session Isolation (Phase Gates)**: To prevent AI "hallucinations" and context bleed-over, major workflow transitions enforce a **Hard Stop** (e.g., from Specification to Planning). You are required to physically open a "New Chat" in your IDE to proceed. Simply telling the AI to "forget" does not clear its context window reliably.
|
|
90
98
|
- **Registry Parity**: Every spec that exists on disk must be registered in `INDEX.md`. Every registered spec must appear in the implementation plan or the backlog. Orphaned specs are treated as critical blockers.
|
|
91
99
|
- **Rules Parity**: If project conventions change (`RULES.md`), any existing task plan is flagged as stale. The agent will not execute tasks generated under outdated rules without an explicit sync.
|
|
92
100
|
- **Engine Integrity**: Core engine files are checksummed. Any untracked modification halts all workflows until the engine state is reconciled.
|
|
@@ -232,11 +240,19 @@ npx magic-spec@latest --update
|
|
|
232
240
|
> [!TIP]
|
|
233
241
|
> The update process preserves your `.design/` workspace and automatically creates backups of `.magic/` and `.agent/` folders. If you have modified core engine files, the installer will detect conflicts and ask for your preference (overwrite, skip, or abort).
|
|
234
242
|
|
|
243
|
+
### Post-Install: `.gitignore`
|
|
244
|
+
|
|
245
|
+
The installer automatically adds `.magic/` and the adapter directory (e.g., `.agent/`, `.cursor/rules/`) to your project's `.gitignore`. These directories are **installed dependencies** — similar to `node_modules/` — and should be reinstalled via `npx magic-spec@latest` rather than committed to version control.
|
|
246
|
+
|
|
247
|
+
> [!TIP]
|
|
248
|
+
> **Vendoring**: If you prefer to commit the engine into your repository (so teammates get it without running the installer), simply remove the `.magic/` and `.agent/` entries from your `.gitignore`.
|
|
249
|
+
|
|
235
250
|
## 💬 Usage
|
|
236
251
|
|
|
237
252
|
Just talk to your AI agent naturally in your prompt interface. No complex commands to learn:
|
|
238
253
|
|
|
239
254
|
- *"Dispatch this thought into specs..."* → Triggers **Specification** workflow.
|
|
255
|
+
- *"Run a project audit"*, *"magic.analyze"* → Triggers **Analyze** (Ventilation) workflow.
|
|
240
256
|
- *"Create an implementation plan"* → Triggers **Task & Plan** workflow.
|
|
241
257
|
- *"Execute the next task"* → Triggers **Run** workflow.
|
|
242
258
|
- *"Add a rule: always use Inter font"* → Triggers **Rule** workflow.
|
package/installers/config.json
CHANGED
|
@@ -5,8 +5,13 @@
|
|
|
5
5
|
"engineDir": ".magic",
|
|
6
6
|
"agentDir": ".agent",
|
|
7
7
|
"workflowsDir": "workflows",
|
|
8
|
+
"designDir": ".design",
|
|
9
|
+
"versionFile": ".version",
|
|
10
|
+
"checksumsFile": ".checksums",
|
|
11
|
+
"historyDir": "history",
|
|
8
12
|
"defaultExt": ".md",
|
|
9
13
|
"workflows": [
|
|
14
|
+
"magic.analyze",
|
|
10
15
|
"magic.onboard",
|
|
11
16
|
"magic.rule",
|
|
12
17
|
"magic.run",
|
|
@@ -32,7 +37,7 @@
|
|
|
32
37
|
"scripts/init.js",
|
|
33
38
|
"templates/plan.md",
|
|
34
39
|
"templates/retrospective.md",
|
|
35
|
-
"templates/
|
|
40
|
+
"templates/spec.md",
|
|
36
41
|
"templates/tasks.md"
|
|
37
42
|
],
|
|
38
43
|
"download": {
|
|
@@ -64,6 +69,9 @@
|
|
|
64
69
|
],
|
|
65
70
|
"docsDir": "docs"
|
|
66
71
|
},
|
|
72
|
+
"git": {
|
|
73
|
+
"defaultBranch": "master"
|
|
74
|
+
},
|
|
67
75
|
"tests": {
|
|
68
76
|
"sandboxDir": "installers/tests/sandbox",
|
|
69
77
|
"adaptersJson": "installers/adapters.json",
|
package/installers/node/index.js
CHANGED
|
@@ -77,6 +77,11 @@ function loadInstallerConfig() {
|
|
|
77
77
|
failConfig("field 'magicFiles' must be an array of strings");
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
const designDir = requireNonEmptyString(parsed.designDir, 'designDir');
|
|
81
|
+
const versionFile = requireNonEmptyString(parsed.versionFile, 'versionFile');
|
|
82
|
+
const checksumsFile = requireNonEmptyString(parsed.checksumsFile, 'checksumsFile');
|
|
83
|
+
const historyDir = requireNonEmptyString(parsed.historyDir, 'historyDir');
|
|
84
|
+
|
|
80
85
|
return {
|
|
81
86
|
githubRepo,
|
|
82
87
|
packageName,
|
|
@@ -84,6 +89,10 @@ function loadInstallerConfig() {
|
|
|
84
89
|
engineDir,
|
|
85
90
|
agentDir,
|
|
86
91
|
workflowsDir,
|
|
92
|
+
designDir,
|
|
93
|
+
versionFile,
|
|
94
|
+
checksumsFile,
|
|
95
|
+
historyDir,
|
|
87
96
|
defaultExt,
|
|
88
97
|
workflows,
|
|
89
98
|
magicFiles,
|
|
@@ -102,6 +111,10 @@ const WORKFLOWS_DIR = INSTALLER_CONFIG.workflowsDir;
|
|
|
102
111
|
const DEFAULT_EXT = INSTALLER_CONFIG.defaultExt;
|
|
103
112
|
const WORKFLOWS = INSTALLER_CONFIG.workflows;
|
|
104
113
|
const MAGIC_FILES = INSTALLER_CONFIG.magicFiles;
|
|
114
|
+
const DESIGN_DIR = INSTALLER_CONFIG.designDir;
|
|
115
|
+
const VERSION_FILE = INSTALLER_CONFIG.versionFile;
|
|
116
|
+
const CHECKSUMS_FILE = INSTALLER_CONFIG.checksumsFile;
|
|
117
|
+
const HISTORY_DIR = INSTALLER_CONFIG.historyDir;
|
|
105
118
|
const DEFAULT_REMOVE_PREFIX = INSTALLER_CONFIG.removePrefix;
|
|
106
119
|
const DOWNLOAD_TIMEOUT_MS = INSTALLER_CONFIG.download.timeoutMs;
|
|
107
120
|
const NODE_USER_AGENT = INSTALLER_CONFIG.userAgent.node;
|
|
@@ -178,6 +191,45 @@ function copyDir(src, dest) {
|
|
|
178
191
|
fs.cpSync(src, dest, { recursive: true, force: true });
|
|
179
192
|
}
|
|
180
193
|
|
|
194
|
+
function appendToGitignore(entries) {
|
|
195
|
+
const gitignoreFile = path.join(cwd, '.gitignore');
|
|
196
|
+
let content = '';
|
|
197
|
+
if (fs.existsSync(gitignoreFile)) {
|
|
198
|
+
content = fs.readFileSync(gitignoreFile, 'utf8');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const added = [];
|
|
202
|
+
for (const entry of entries) {
|
|
203
|
+
// Normalize: ensure trailing slash for directories
|
|
204
|
+
const normalized = entry.endsWith('/') ? entry : entry + '/';
|
|
205
|
+
// Check if already present (with or without trailing slash)
|
|
206
|
+
const base = normalized.replace(/\/$/, '');
|
|
207
|
+
const alreadyPresent = content.split(/\r?\n/).some(line => {
|
|
208
|
+
const trimmed = line.trim();
|
|
209
|
+
return trimmed === base || trimmed === normalized;
|
|
210
|
+
});
|
|
211
|
+
if (!alreadyPresent) {
|
|
212
|
+
added.push(normalized);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (added.length === 0) return;
|
|
217
|
+
|
|
218
|
+
// Append under a Magic Spec section header
|
|
219
|
+
const section = '\n# Magic Spec (engine & workflows — installed via npx/uvx)';
|
|
220
|
+
const hasSection = content.includes('# Magic Spec');
|
|
221
|
+
let appendBlock = '';
|
|
222
|
+
if (!hasSection) {
|
|
223
|
+
appendBlock = section + '\n';
|
|
224
|
+
}
|
|
225
|
+
appendBlock += added.join('\n');
|
|
226
|
+
|
|
227
|
+
content = content.trimEnd() + '\n' + appendBlock + '\n';
|
|
228
|
+
fs.writeFileSync(gitignoreFile, content, 'utf8');
|
|
229
|
+
console.log(`📝 Updated .gitignore: added ${added.join(', ')}`);
|
|
230
|
+
console.log(` 💡 To vendor engine files in your repo, remove these entries.`);
|
|
231
|
+
}
|
|
232
|
+
|
|
181
233
|
function convertToToml(content, description) {
|
|
182
234
|
// Escape quotes and backslashes for TOML triple-quoted strings
|
|
183
235
|
const escapedContent = content
|
|
@@ -327,7 +379,7 @@ function runDoctor() {
|
|
|
327
379
|
console.log(`✅ ${item.path || name} is present`);
|
|
328
380
|
} else {
|
|
329
381
|
const hint = requiredHint ? ` (Hint: ${requiredHint})` : '';
|
|
330
|
-
console.log(`❌
|
|
382
|
+
console.log(`❌ ${DESIGN_DIR}/${name} is missing${hint}`);
|
|
331
383
|
}
|
|
332
384
|
};
|
|
333
385
|
|
|
@@ -363,12 +415,12 @@ function runInfo() {
|
|
|
363
415
|
console.log('magic-spec installation status');
|
|
364
416
|
console.log('────────────────────────────────');
|
|
365
417
|
|
|
366
|
-
const
|
|
418
|
+
const versionFilePath = path.join(cwd, ENGINE_DIR, VERSION_FILE);
|
|
367
419
|
let installedVersion = 'none';
|
|
368
|
-
if (fs.existsSync(
|
|
369
|
-
installedVersion = fs.readFileSync(
|
|
420
|
+
if (fs.existsSync(versionFilePath)) {
|
|
421
|
+
installedVersion = fs.readFileSync(versionFilePath, 'utf8').trim();
|
|
370
422
|
}
|
|
371
|
-
console.log(`Installed version : ${installedVersion} (
|
|
423
|
+
console.log(`Installed version : ${installedVersion} (${ENGINE_DIR}/${VERSION_FILE})`);
|
|
372
424
|
|
|
373
425
|
const ADAPTERS_PATH = path.join(__dirname, '..', 'adapters.json');
|
|
374
426
|
let activeEnvs = [];
|
|
@@ -387,8 +439,8 @@ function runInfo() {
|
|
|
387
439
|
const enginePresent = fs.existsSync(path.join(cwd, ENGINE_DIR));
|
|
388
440
|
console.log(`Engine : ${ENGINE_DIR}/ ${enginePresent ? '✅ present' : '❌ missing'}`);
|
|
389
441
|
|
|
390
|
-
const designPresent = fs.existsSync(path.join(cwd,
|
|
391
|
-
console.log(`Workspace :
|
|
442
|
+
const designPresent = fs.existsSync(path.join(cwd, DESIGN_DIR));
|
|
443
|
+
console.log(`Workspace : ${DESIGN_DIR}/ ${designPresent ? '✅ present' : '❌ missing'}`);
|
|
392
444
|
|
|
393
445
|
console.log('────────────────────────────────');
|
|
394
446
|
console.log(`Run \`npx ${PACKAGE_NAME}@latest --update\` to refresh engine files.`);
|
|
@@ -396,9 +448,9 @@ function runInfo() {
|
|
|
396
448
|
}
|
|
397
449
|
|
|
398
450
|
function runCheck() {
|
|
399
|
-
const versionFile = path.join(cwd, ENGINE_DIR,
|
|
451
|
+
const versionFile = path.join(cwd, ENGINE_DIR, VERSION_FILE);
|
|
400
452
|
if (!fs.existsSync(versionFile)) {
|
|
401
|
-
console.log(`⚠️ Not installed via magic-spec (no ${ENGINE_DIR}
|
|
453
|
+
console.log(`⚠️ Not installed via magic-spec (no ${ENGINE_DIR}/${VERSION_FILE} file)`);
|
|
402
454
|
process.exit(0);
|
|
403
455
|
}
|
|
404
456
|
|
|
@@ -451,7 +503,7 @@ async function runEject() {
|
|
|
451
503
|
console.log(` - ${ENGINE_DIR}/`);
|
|
452
504
|
console.log(` - ${AGENT_DIR}/ (or active env adapter dir)`);
|
|
453
505
|
console.log(` - ${ENGINE_DIR}.bak/ (if exists)`);
|
|
454
|
-
console.log(
|
|
506
|
+
console.log(`\n Your ${DESIGN_DIR}/ workspace will NOT be affected.`);
|
|
455
507
|
|
|
456
508
|
let shouldRun = autoAccept;
|
|
457
509
|
if (!shouldRun) {
|
|
@@ -500,10 +552,10 @@ function getDirectoryChecksums(dir, baseDir = dir) {
|
|
|
500
552
|
for (const item of items) {
|
|
501
553
|
const fullPath = path.join(dir, item.name);
|
|
502
554
|
if (item.isDirectory()) {
|
|
503
|
-
if (item.name ===
|
|
555
|
+
if (item.name === CHECKSUMS_FILE || item.name === HISTORY_DIR) continue;
|
|
504
556
|
Object.assign(results, getDirectoryChecksums(fullPath, baseDir));
|
|
505
557
|
} else {
|
|
506
|
-
if (item.name ===
|
|
558
|
+
if (item.name === CHECKSUMS_FILE) continue;
|
|
507
559
|
const relPath = path.relative(baseDir, fullPath).replace(/\\/g, '/');
|
|
508
560
|
results[relPath] = getFileChecksum(fullPath);
|
|
509
561
|
}
|
|
@@ -512,22 +564,22 @@ function getDirectoryChecksums(dir, baseDir = dir) {
|
|
|
512
564
|
}
|
|
513
565
|
|
|
514
566
|
async function handleConflicts(cwd) {
|
|
515
|
-
const
|
|
516
|
-
if (!fs.existsSync(
|
|
567
|
+
const checksumsFilePath = path.join(cwd, ENGINE_DIR, CHECKSUMS_FILE);
|
|
568
|
+
if (!fs.existsSync(checksumsFilePath)) return;
|
|
517
569
|
|
|
518
570
|
let storedChecksums = {};
|
|
519
571
|
try {
|
|
520
|
-
storedChecksums = JSON.parse(fs.readFileSync(
|
|
572
|
+
storedChecksums = JSON.parse(fs.readFileSync(checksumsFilePath, 'utf8'));
|
|
521
573
|
} catch (e) {
|
|
522
574
|
return;
|
|
523
575
|
}
|
|
524
576
|
|
|
525
577
|
const conflicts = [];
|
|
526
578
|
for (const [relPath, storedHash] of Object.entries(storedChecksums)) {
|
|
527
|
-
if (relPath ===
|
|
579
|
+
if (relPath === CHECKSUMS_FILE || relPath === VERSION_FILE) continue;
|
|
528
580
|
|
|
529
|
-
// Backward compatibility: try
|
|
530
|
-
let localPath = path.join(cwd,
|
|
581
|
+
// Backward compatibility: try engine dir first, then project root
|
|
582
|
+
let localPath = path.join(cwd, ENGINE_DIR, relPath);
|
|
531
583
|
if (!fs.existsSync(localPath)) {
|
|
532
584
|
localPath = path.join(cwd, relPath);
|
|
533
585
|
}
|
|
@@ -733,6 +785,7 @@ async function main() {
|
|
|
733
785
|
}
|
|
734
786
|
|
|
735
787
|
try {
|
|
788
|
+
let selectedEnvResolved = null;
|
|
736
789
|
if (!isLocal) {
|
|
737
790
|
sourceRoot = await downloadPayload(versionToFetch);
|
|
738
791
|
}
|
|
@@ -863,8 +916,8 @@ async function main() {
|
|
|
863
916
|
if (!isUpdate) {
|
|
864
917
|
const isWindows = process.platform === 'win32';
|
|
865
918
|
const initScript = isWindows
|
|
866
|
-
? path.join(cwd,
|
|
867
|
-
: path.join(cwd,
|
|
919
|
+
? path.join(cwd, ENGINE_DIR, 'scripts', 'init.ps1')
|
|
920
|
+
: path.join(cwd, ENGINE_DIR, 'scripts', 'init.sh');
|
|
868
921
|
|
|
869
922
|
if (fs.existsSync(initScript)) {
|
|
870
923
|
let shouldRun = true;
|
|
@@ -901,21 +954,36 @@ async function main() {
|
|
|
901
954
|
console.log(`✅ ${PACKAGE_NAME} updated successfully!`);
|
|
902
955
|
}
|
|
903
956
|
|
|
904
|
-
// 4. Write version file
|
|
957
|
+
// 4. Write version file - [T-2B01]
|
|
905
958
|
try {
|
|
906
|
-
const
|
|
907
|
-
fs.writeFileSync(
|
|
959
|
+
const versionFileDest = path.join(cwd, ENGINE_DIR, VERSION_FILE);
|
|
960
|
+
fs.writeFileSync(versionFileDest, version, 'utf8');
|
|
908
961
|
} catch (vErr) {
|
|
909
|
-
console.warn(`⚠️ Failed to write
|
|
962
|
+
console.warn(`⚠️ Failed to write ${ENGINE_DIR}/${VERSION_FILE}: ${vErr.message}`);
|
|
910
963
|
}
|
|
911
964
|
|
|
965
|
+
// 5. Auto-update .gitignore
|
|
966
|
+
const gitignoreEntries = [ENGINE_DIR];
|
|
967
|
+
if (envValues.length > 0) {
|
|
968
|
+
for (const env of envValues) {
|
|
969
|
+
const adapter = ADAPTERS[env];
|
|
970
|
+
if (adapter && adapter.dest) {
|
|
971
|
+
gitignoreEntries.push(adapter.dest);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
} else if (selectedEnvResolved && ADAPTERS[selectedEnvResolved]) {
|
|
975
|
+
gitignoreEntries.push(ADAPTERS[selectedEnvResolved].dest);
|
|
976
|
+
} else {
|
|
977
|
+
gitignoreEntries.push(AGENT_DIR);
|
|
978
|
+
}
|
|
979
|
+
appendToGitignore(gitignoreEntries);
|
|
912
980
|
|
|
913
981
|
// 6. Save checksums - [T-2C03]
|
|
914
982
|
try {
|
|
915
|
-
const currentChecksums = getDirectoryChecksums(path.join(cwd,
|
|
983
|
+
const currentChecksums = getDirectoryChecksums(path.join(cwd, ENGINE_DIR));
|
|
916
984
|
|
|
917
985
|
if (isUpdate) {
|
|
918
|
-
const checksumsFile = path.join(cwd,
|
|
986
|
+
const checksumsFile = path.join(cwd, ENGINE_DIR, CHECKSUMS_FILE);
|
|
919
987
|
if (fs.existsSync(checksumsFile)) {
|
|
920
988
|
try {
|
|
921
989
|
const oldChecksums = JSON.parse(fs.readFileSync(checksumsFile, 'utf8'));
|
|
@@ -930,7 +998,7 @@ async function main() {
|
|
|
930
998
|
|
|
931
999
|
Object.assign(currentChecksums, adapterChecksums);
|
|
932
1000
|
|
|
933
|
-
fs.writeFileSync(path.join(cwd,
|
|
1001
|
+
fs.writeFileSync(path.join(cwd, ENGINE_DIR, CHECKSUMS_FILE), JSON.stringify(currentChecksums, null, 2), 'utf8');
|
|
934
1002
|
} catch (cErr) {
|
|
935
1003
|
console.warn(`⚠️ Failed to save checksums: ${cErr.message}`);
|
|
936
1004
|
}
|