substrate-ai 0.1.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/README.md +204 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.js +14639 -0
- package/dist/cli/templates/parallel.yaml +72 -0
- package/dist/cli/templates/research-then-implement.yaml +103 -0
- package/dist/cli/templates/review-cycle.yaml +91 -0
- package/dist/cli/templates/sequential.yaml +68 -0
- package/dist/config-schema-C9tTMcm1.js +102 -0
- package/dist/config-watcher-P5CR4cZ0.js +5599 -0
- package/dist/index.d.ts +1389 -0
- package/dist/index.js +465 -0
- package/dist/upgrade-0Q2TKgQ4.js +5 -0
- package/dist/upgrade-BBPbOHol.js +126 -0
- package/dist/version-manager-impl-Bij9-k0W.js +4 -0
- package/dist/version-manager-impl-DbHmed-I.js +485 -0
- package/package.json +85 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
import { SUPPORTED_CONFIG_FORMAT_VERSIONS, SUPPORTED_TASK_GRAPH_VERSIONS } from "./config-schema-C9tTMcm1.js";
|
|
2
|
+
import { createRequire } from "module";
|
|
3
|
+
import path, { dirname } from "path";
|
|
4
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import os from "os";
|
|
6
|
+
import https from "https";
|
|
7
|
+
import * as semver from "semver";
|
|
8
|
+
|
|
9
|
+
//#region src/modules/config/config-migrator.ts
|
|
10
|
+
/**
|
|
11
|
+
* ConfigMigrator manages a registry of migration functions and applies them
|
|
12
|
+
* sequentially to upgrade config documents from one format version to another.
|
|
13
|
+
*/
|
|
14
|
+
var ConfigMigrator = class {
|
|
15
|
+
migrations = new Map();
|
|
16
|
+
/**
|
|
17
|
+
* Register a migration function for the given version key.
|
|
18
|
+
*
|
|
19
|
+
* @param key - Migration key in format "N->M" (e.g. "1->2")
|
|
20
|
+
* @param fn - Migration function that receives the raw config and returns the migrated config
|
|
21
|
+
*/
|
|
22
|
+
register(key, fn) {
|
|
23
|
+
this.migrations.set(key, fn);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check whether a sequential migration path exists from fromVersion to toVersion.
|
|
27
|
+
*
|
|
28
|
+
* @param fromVersion - Starting version string
|
|
29
|
+
* @param toVersion - Target version string
|
|
30
|
+
* @returns true if every step in the path is registered
|
|
31
|
+
*/
|
|
32
|
+
canMigrate(fromVersion, toVersion) {
|
|
33
|
+
if (fromVersion === toVersion) return true;
|
|
34
|
+
const from = parseInt(fromVersion, 10);
|
|
35
|
+
const to = parseInt(toVersion, 10);
|
|
36
|
+
if (isNaN(from) || isNaN(to) || from >= to) return false;
|
|
37
|
+
for (let v = from; v < to; v++) {
|
|
38
|
+
const key = `${String(v)}->${String(v + 1)}`;
|
|
39
|
+
if (!this.migrations.has(key)) return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Apply sequential migrations from fromVersion to toVersion.
|
|
45
|
+
*
|
|
46
|
+
* If fromVersion === toVersion, returns a no-op success result.
|
|
47
|
+
* If any intermediate migration is missing, returns success:false.
|
|
48
|
+
*
|
|
49
|
+
* When filePath is provided and migration is needed, a backup is written to
|
|
50
|
+
* `${filePath}.bak.v${fromVersion}` before any transformations are applied.
|
|
51
|
+
*
|
|
52
|
+
* @param config - Raw config object to migrate
|
|
53
|
+
* @param fromVersion - Starting format version string
|
|
54
|
+
* @param toVersion - Target format version string
|
|
55
|
+
* @param filePath - Optional path to the source config file for backup creation
|
|
56
|
+
* @returns Object containing the (possibly migrated) config and a MigrationResult
|
|
57
|
+
*/
|
|
58
|
+
migrate(config, fromVersion, toVersion, filePath) {
|
|
59
|
+
if (fromVersion === toVersion) return {
|
|
60
|
+
config,
|
|
61
|
+
result: {
|
|
62
|
+
success: true,
|
|
63
|
+
fromVersion,
|
|
64
|
+
toVersion,
|
|
65
|
+
migratedKeys: [],
|
|
66
|
+
manualStepsRequired: [],
|
|
67
|
+
backupPath: null
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const from = parseInt(fromVersion, 10);
|
|
71
|
+
const to = parseInt(toVersion, 10);
|
|
72
|
+
if (isNaN(from) || isNaN(to) || from >= to) return {
|
|
73
|
+
config,
|
|
74
|
+
result: {
|
|
75
|
+
success: false,
|
|
76
|
+
fromVersion,
|
|
77
|
+
toVersion,
|
|
78
|
+
migratedKeys: [],
|
|
79
|
+
manualStepsRequired: [`Cannot migrate from version "${fromVersion}" to "${toVersion}": invalid version range.`],
|
|
80
|
+
backupPath: null
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
for (let v = from; v < to; v++) {
|
|
84
|
+
const key = `${String(v)}->${String(v + 1)}`;
|
|
85
|
+
if (!this.migrations.has(key)) return {
|
|
86
|
+
config,
|
|
87
|
+
result: {
|
|
88
|
+
success: false,
|
|
89
|
+
fromVersion,
|
|
90
|
+
toVersion,
|
|
91
|
+
migratedKeys: [],
|
|
92
|
+
manualStepsRequired: [`Missing migration step: "${key}". Cannot automatically migrate from version "${fromVersion}" to "${toVersion}". Please upgrade the toolkit: npm install -g substrate@latest`],
|
|
93
|
+
backupPath: null
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
let backupPath = null;
|
|
98
|
+
if (filePath !== void 0) {
|
|
99
|
+
backupPath = `${filePath}.bak.v${fromVersion}`;
|
|
100
|
+
const originalContent = readFileSync(filePath, "utf-8");
|
|
101
|
+
writeFileSync(backupPath, originalContent, "utf-8");
|
|
102
|
+
}
|
|
103
|
+
let current = config;
|
|
104
|
+
const migratedKeys = [];
|
|
105
|
+
for (let v = from; v < to; v++) {
|
|
106
|
+
const key = `${String(v)}->${String(v + 1)}`;
|
|
107
|
+
const fn = this.migrations.get(key);
|
|
108
|
+
const before = JSON.stringify(current);
|
|
109
|
+
current = fn(current);
|
|
110
|
+
const after = JSON.stringify(current);
|
|
111
|
+
if (current !== null && typeof current === "object" && !Array.isArray(current)) {
|
|
112
|
+
const beforeObj = JSON.parse(before);
|
|
113
|
+
const afterObj = JSON.parse(after);
|
|
114
|
+
for (const k of Object.keys(afterObj)) if (JSON.stringify(afterObj[k]) !== JSON.stringify(beforeObj[k])) {
|
|
115
|
+
if (!migratedKeys.includes(k)) migratedKeys.push(k);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
config: current,
|
|
121
|
+
result: {
|
|
122
|
+
success: true,
|
|
123
|
+
fromVersion,
|
|
124
|
+
toVersion,
|
|
125
|
+
migratedKeys,
|
|
126
|
+
manualStepsRequired: [],
|
|
127
|
+
backupPath
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
/** Singleton instance for use throughout the toolkit */
|
|
133
|
+
const defaultConfigMigrator = new ConfigMigrator();
|
|
134
|
+
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/modules/version-manager/update-checker.ts
|
|
137
|
+
/**
|
|
138
|
+
* Thrown when an update check fails due to network error, timeout, or bad response.
|
|
139
|
+
*/
|
|
140
|
+
var UpdateCheckError = class extends Error {
|
|
141
|
+
name = "UpdateCheckError";
|
|
142
|
+
constructor(message) {
|
|
143
|
+
super(message);
|
|
144
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Queries the npm registry to determine the latest published version of a package.
|
|
149
|
+
*/
|
|
150
|
+
var UpdateChecker = class {
|
|
151
|
+
timeoutMs;
|
|
152
|
+
constructor(timeoutMs = 5e3) {
|
|
153
|
+
this.timeoutMs = timeoutMs;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Fetch the latest published version of a package from the npm registry.
|
|
157
|
+
*
|
|
158
|
+
* @param packageName - npm package name (e.g. 'substrate')
|
|
159
|
+
* @returns The latest version string (semver)
|
|
160
|
+
* @throws {UpdateCheckError} on timeout, network error, non-200 HTTP status, or parse failure
|
|
161
|
+
*/
|
|
162
|
+
fetchLatestVersion(packageName) {
|
|
163
|
+
return new Promise((resolve$1, reject) => {
|
|
164
|
+
const url = `https://registry.npmjs.org/${packageName}/latest`;
|
|
165
|
+
let settled = false;
|
|
166
|
+
const safeReject = (err) => {
|
|
167
|
+
if (!settled) {
|
|
168
|
+
settled = true;
|
|
169
|
+
clearTimeout(timer);
|
|
170
|
+
reject(err);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const safeResolve = (value) => {
|
|
174
|
+
if (!settled) {
|
|
175
|
+
settled = true;
|
|
176
|
+
clearTimeout(timer);
|
|
177
|
+
resolve$1(value);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
let req;
|
|
181
|
+
const timer = setTimeout(() => {
|
|
182
|
+
req.destroy();
|
|
183
|
+
safeReject(new UpdateCheckError(`Update check timed out after ${this.timeoutMs}ms`));
|
|
184
|
+
}, this.timeoutMs);
|
|
185
|
+
const followRedirect = (location, hopsLeft) => {
|
|
186
|
+
const redirectReq = https.get(location, (redirectRes) => {
|
|
187
|
+
if (redirectRes.statusCode !== void 0 && redirectRes.statusCode >= 300 && redirectRes.statusCode < 400 && redirectRes.headers.location && hopsLeft > 0) {
|
|
188
|
+
redirectRes.resume();
|
|
189
|
+
followRedirect(redirectRes.headers.location, hopsLeft - 1);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (redirectRes.statusCode !== 200) {
|
|
193
|
+
redirectRes.resume();
|
|
194
|
+
safeReject(new UpdateCheckError(`npm registry returned HTTP ${String(redirectRes.statusCode)} for ${packageName}`));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
collectBody(redirectRes, safeResolve, safeReject);
|
|
198
|
+
});
|
|
199
|
+
redirectReq.on("error", (err) => {
|
|
200
|
+
safeReject(new UpdateCheckError(`Update check network error: ${err.message}`));
|
|
201
|
+
});
|
|
202
|
+
redirectReq.setTimeout(this.timeoutMs, () => {
|
|
203
|
+
redirectReq.destroy();
|
|
204
|
+
safeReject(new UpdateCheckError(`Update check timed out after ${this.timeoutMs}ms`));
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
req = https.get(url, (res) => {
|
|
208
|
+
if (res.statusCode !== void 0 && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
209
|
+
res.resume();
|
|
210
|
+
followRedirect(res.headers.location, 1);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (res.statusCode !== 200) {
|
|
214
|
+
res.resume();
|
|
215
|
+
safeReject(new UpdateCheckError(`npm registry returned HTTP ${String(res.statusCode)} for ${packageName}`));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
collectBody(res, safeResolve, safeReject);
|
|
219
|
+
});
|
|
220
|
+
req.on("error", (err) => {
|
|
221
|
+
safeReject(new UpdateCheckError(`Update check network error: ${err.message}`));
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Determine whether upgrading from currentVersion to latestVersion is a breaking (major) change.
|
|
227
|
+
*
|
|
228
|
+
* @param currentVersion - The currently installed version (semver string)
|
|
229
|
+
* @param latestVersion - The available latest version (semver string)
|
|
230
|
+
* @returns true if the major version increases; false if either version is invalid
|
|
231
|
+
*/
|
|
232
|
+
isBreaking(currentVersion, latestVersion) {
|
|
233
|
+
if (!semver.valid(currentVersion) || !semver.valid(latestVersion)) return false;
|
|
234
|
+
const currentMajor = semver.major(currentVersion);
|
|
235
|
+
const latestMajor = semver.major(latestVersion);
|
|
236
|
+
return latestMajor > currentMajor;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Return a changelog URL for the given version.
|
|
240
|
+
*
|
|
241
|
+
* @param latestVersion - The version to link to
|
|
242
|
+
* @returns A URL string pointing to the GitHub release page
|
|
243
|
+
*/
|
|
244
|
+
getChangelog(latestVersion) {
|
|
245
|
+
return `See https://github.com/jplanow/ai-dev-toolkit-new/releases/tag/v${latestVersion}`;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
function collectBody(res, resolve$1, reject) {
|
|
249
|
+
const chunks = [];
|
|
250
|
+
res.on("data", (chunk) => {
|
|
251
|
+
chunks.push(chunk);
|
|
252
|
+
});
|
|
253
|
+
res.on("end", () => {
|
|
254
|
+
try {
|
|
255
|
+
const body = Buffer.concat(chunks).toString("utf-8");
|
|
256
|
+
const data = JSON.parse(body);
|
|
257
|
+
if (typeof data.version !== "string" || data.version.length === 0) {
|
|
258
|
+
reject(new UpdateCheckError("npm registry response missing version field"));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
resolve$1(data.version);
|
|
262
|
+
} catch {
|
|
263
|
+
reject(new UpdateCheckError("Failed to parse npm registry response"));
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
res.on("error", (err) => {
|
|
267
|
+
reject(new UpdateCheckError(`Response stream error: ${err.message}`));
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/modules/version-manager/version-cache.ts
|
|
273
|
+
/**
|
|
274
|
+
* Manages reading and writing the version check result cache.
|
|
275
|
+
*
|
|
276
|
+
* Write errors are silently swallowed so that a read-only home directory
|
|
277
|
+
* never breaks CLI execution.
|
|
278
|
+
*/
|
|
279
|
+
var VersionCache = class {
|
|
280
|
+
cachePath;
|
|
281
|
+
ttlMs;
|
|
282
|
+
constructor(cachePath = path.join(os.homedir(), ".substrate", "update-cache.json"), ttlMs = 24 * 60 * 60 * 1e3) {
|
|
283
|
+
this.cachePath = cachePath;
|
|
284
|
+
this.ttlMs = ttlMs;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Read the cache file. Returns null if the file is missing, unreadable, or expired.
|
|
288
|
+
*/
|
|
289
|
+
read() {
|
|
290
|
+
try {
|
|
291
|
+
const raw = readFileSync(this.cachePath, "utf-8");
|
|
292
|
+
const entry = JSON.parse(raw);
|
|
293
|
+
if (typeof entry.lastChecked !== "string" || typeof entry.latestVersion !== "string" || typeof entry.currentVersion !== "string") return null;
|
|
294
|
+
if (this.isExpired(entry)) return null;
|
|
295
|
+
return entry;
|
|
296
|
+
} catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Write a cache entry to disk. Creates the parent directory if needed.
|
|
302
|
+
* Silently swallows any write errors.
|
|
303
|
+
*/
|
|
304
|
+
write(entry) {
|
|
305
|
+
try {
|
|
306
|
+
mkdirSync(dirname(this.cachePath), { recursive: true });
|
|
307
|
+
writeFileSync(this.cachePath, JSON.stringify(entry, null, 2), "utf-8");
|
|
308
|
+
} catch {}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Check whether a cache entry has expired according to the configured TTL.
|
|
312
|
+
*/
|
|
313
|
+
isExpired(entry) {
|
|
314
|
+
const age = Date.now() - new Date(entry.lastChecked).getTime();
|
|
315
|
+
return age > this.ttlMs;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region src/modules/version-manager/version-manager-impl.ts
|
|
321
|
+
/**
|
|
322
|
+
* Concrete implementation of VersionManager.
|
|
323
|
+
*
|
|
324
|
+
* @param deps - Optional overrides for cache and updateChecker (for testing)
|
|
325
|
+
*/
|
|
326
|
+
var VersionManagerImpl = class {
|
|
327
|
+
cache;
|
|
328
|
+
updateChecker;
|
|
329
|
+
updateCheckEnabled;
|
|
330
|
+
constructor(deps = {}) {
|
|
331
|
+
this.cache = deps.cache ?? new VersionCache();
|
|
332
|
+
this.updateChecker = deps.updateChecker ?? new UpdateChecker();
|
|
333
|
+
this.updateCheckEnabled = deps.updateCheckEnabled !== false;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Read the current package version from the bundled package.json.
|
|
337
|
+
* Falls back to '0.0.0' if the file is unreadable.
|
|
338
|
+
*/
|
|
339
|
+
getCurrentVersion() {
|
|
340
|
+
try {
|
|
341
|
+
const _require = createRequire(import.meta.url);
|
|
342
|
+
const pkg = _require("../../../package.json");
|
|
343
|
+
return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "0.0.0";
|
|
344
|
+
} catch {
|
|
345
|
+
return "0.0.0";
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Check whether a newer version is available on npm.
|
|
350
|
+
*
|
|
351
|
+
* Respects:
|
|
352
|
+
* 1. update_check config setting (AC5)
|
|
353
|
+
* 2. SUBSTRATE_NO_UPDATE_CHECK env var
|
|
354
|
+
* 3. Cache TTL (24h by default), unless forceRefresh is true
|
|
355
|
+
*
|
|
356
|
+
* Returns updateAvailable: false without a network call when checks are disabled
|
|
357
|
+
* or the cache is fresh (and forceRefresh is not set).
|
|
358
|
+
*
|
|
359
|
+
* @param forceRefresh - When true, skips the cache and always makes a fresh network request.
|
|
360
|
+
*/
|
|
361
|
+
async checkForUpdates(forceRefresh = false) {
|
|
362
|
+
const currentVersion = this.getCurrentVersion();
|
|
363
|
+
if (!this.updateCheckEnabled) return {
|
|
364
|
+
currentVersion,
|
|
365
|
+
latestVersion: currentVersion,
|
|
366
|
+
updateAvailable: false,
|
|
367
|
+
isBreaking: false,
|
|
368
|
+
changelog: ""
|
|
369
|
+
};
|
|
370
|
+
if (process.env["SUBSTRATE_NO_UPDATE_CHECK"] === "1") return {
|
|
371
|
+
currentVersion,
|
|
372
|
+
latestVersion: currentVersion,
|
|
373
|
+
updateAvailable: false,
|
|
374
|
+
isBreaking: false,
|
|
375
|
+
changelog: ""
|
|
376
|
+
};
|
|
377
|
+
if (!forceRefresh) {
|
|
378
|
+
const cached = this.cache.read();
|
|
379
|
+
if (cached !== null) {
|
|
380
|
+
const updateAvailable = cached.latestVersion !== cached.currentVersion;
|
|
381
|
+
return {
|
|
382
|
+
currentVersion: cached.currentVersion,
|
|
383
|
+
latestVersion: cached.latestVersion,
|
|
384
|
+
updateAvailable,
|
|
385
|
+
isBreaking: this.updateChecker.isBreaking(cached.currentVersion, cached.latestVersion),
|
|
386
|
+
changelog: this.updateChecker.getChangelog(cached.latestVersion)
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
const latestVersion = await this.updateChecker.fetchLatestVersion("substrate");
|
|
392
|
+
const updateAvailable = latestVersion !== currentVersion;
|
|
393
|
+
this.cache.write({
|
|
394
|
+
lastChecked: new Date().toISOString(),
|
|
395
|
+
latestVersion,
|
|
396
|
+
currentVersion
|
|
397
|
+
});
|
|
398
|
+
return {
|
|
399
|
+
currentVersion,
|
|
400
|
+
latestVersion,
|
|
401
|
+
updateAvailable,
|
|
402
|
+
isBreaking: this.updateChecker.isBreaking(currentVersion, latestVersion),
|
|
403
|
+
changelog: this.updateChecker.getChangelog(latestVersion)
|
|
404
|
+
};
|
|
405
|
+
} catch {
|
|
406
|
+
return {
|
|
407
|
+
currentVersion,
|
|
408
|
+
latestVersion: currentVersion,
|
|
409
|
+
updateAvailable: false,
|
|
410
|
+
isBreaking: false,
|
|
411
|
+
changelog: ""
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Return an upgrade preview for the given target version.
|
|
417
|
+
*/
|
|
418
|
+
getUpgradePreview(targetVersion) {
|
|
419
|
+
const fromVersion = this.getCurrentVersion();
|
|
420
|
+
const breaking = this.updateChecker.isBreaking(fromVersion, targetVersion);
|
|
421
|
+
return {
|
|
422
|
+
fromVersion,
|
|
423
|
+
toVersion: targetVersion,
|
|
424
|
+
breakingChanges: breaking ? [`Major version bump from v${fromVersion} to v${targetVersion}`] : [],
|
|
425
|
+
migrationSteps: [this.updateChecker.getChangelog(targetVersion)],
|
|
426
|
+
automaticMigrations: breaking ? ["Run defaultConfigMigrator.migrate() if config changed"] : [],
|
|
427
|
+
manualStepsRequired: breaking ? ["Review breaking changes at the changelog URL above"] : []
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Migrate the project configuration from one format version to another.
|
|
432
|
+
* Delegates to the shared defaultConfigMigrator singleton.
|
|
433
|
+
* Loads the actual config from the project config file so migration functions receive real data.
|
|
434
|
+
*/
|
|
435
|
+
migrateConfiguration(fromVersion, toVersion) {
|
|
436
|
+
let configObj = {};
|
|
437
|
+
try {
|
|
438
|
+
const _require = createRequire(import.meta.url);
|
|
439
|
+
const fs = _require("fs");
|
|
440
|
+
const path$1 = _require("path");
|
|
441
|
+
const yaml = _require("js-yaml");
|
|
442
|
+
const configPath = path$1.join(process.cwd(), ".substrate", "config.yaml");
|
|
443
|
+
try {
|
|
444
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
445
|
+
configObj = yaml.load(raw) ?? {};
|
|
446
|
+
} catch {
|
|
447
|
+
configObj = {};
|
|
448
|
+
}
|
|
449
|
+
} catch {
|
|
450
|
+
configObj = {};
|
|
451
|
+
}
|
|
452
|
+
const { result } = defaultConfigMigrator.migrate(configObj, fromVersion, toVersion);
|
|
453
|
+
return result;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Migrate a task graph file from one format version to another.
|
|
457
|
+
* Delegates to the shared defaultConfigMigrator singleton.
|
|
458
|
+
*/
|
|
459
|
+
migrateTaskGraphFormat(fromVersion, toVersion, filePath) {
|
|
460
|
+
const { result } = defaultConfigMigrator.migrate({}, fromVersion, toVersion, filePath);
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Check whether the given config format version is supported by this toolkit.
|
|
465
|
+
*/
|
|
466
|
+
isConfigCompatible(configVersion) {
|
|
467
|
+
return SUPPORTED_CONFIG_FORMAT_VERSIONS.includes(configVersion);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Check whether the given task graph format version is supported by this toolkit.
|
|
471
|
+
*/
|
|
472
|
+
isTaskGraphCompatible(graphVersion) {
|
|
473
|
+
return SUPPORTED_TASK_GRAPH_VERSIONS.includes(graphVersion);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
/**
|
|
477
|
+
* Create a new VersionManager instance with optional dependency overrides.
|
|
478
|
+
*/
|
|
479
|
+
function createVersionManager(deps = {}) {
|
|
480
|
+
return new VersionManagerImpl(deps);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
//#endregion
|
|
484
|
+
export { VersionManagerImpl, createVersionManager, defaultConfigMigrator };
|
|
485
|
+
//# sourceMappingURL=version-manager-impl-DbHmed-I.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "substrate-ai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Substrate — multi-agent orchestration daemon for AI coding agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "AI Dev Toolkit Contributors",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/johnplanow/substrate.git"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=22.0.0"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"agents",
|
|
18
|
+
"orchestration",
|
|
19
|
+
"cli",
|
|
20
|
+
"task-graph",
|
|
21
|
+
"routing"
|
|
22
|
+
],
|
|
23
|
+
"bin": {
|
|
24
|
+
"substrate": "./dist/cli/index.js"
|
|
25
|
+
},
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"import": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts"
|
|
31
|
+
},
|
|
32
|
+
"./cli": {
|
|
33
|
+
"import": "./dist/cli/index.js",
|
|
34
|
+
"types": "./dist/cli/index.d.ts"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist/**/*.js",
|
|
39
|
+
"dist/**/*.d.ts",
|
|
40
|
+
"dist/**/*.json",
|
|
41
|
+
"dist/cli/templates",
|
|
42
|
+
"README.md"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsdown",
|
|
46
|
+
"postbuild": "cp -r src/cli/templates dist/cli/templates",
|
|
47
|
+
"dev": "tsx watch src/cli/index.ts",
|
|
48
|
+
"test": "vitest run --coverage",
|
|
49
|
+
"test:watch": "vitest",
|
|
50
|
+
"test:ui": "vitest --ui",
|
|
51
|
+
"lint": "eslint src test",
|
|
52
|
+
"lint:fix": "eslint src test --fix",
|
|
53
|
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
54
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"clean": "rm -rf dist"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
60
|
+
"better-sqlite3": "^12.6.2",
|
|
61
|
+
"commander": "^12.1.0",
|
|
62
|
+
"js-yaml": "^4.1.1",
|
|
63
|
+
"pino": "^9.6.0",
|
|
64
|
+
"semver": "^7.6.3",
|
|
65
|
+
"zod": "^4.3.6"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@eslint/js": "^9.17.0",
|
|
69
|
+
"@types/js-yaml": "^4.0.9",
|
|
70
|
+
"@types/node": "^22.10.0",
|
|
71
|
+
"@types/semver": "^7.5.8",
|
|
72
|
+
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
|
73
|
+
"@typescript-eslint/parser": "^8.18.0",
|
|
74
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
75
|
+
"@vitest/ui": "^2.1.8",
|
|
76
|
+
"eslint": "^9.17.0",
|
|
77
|
+
"pino-pretty": "^13.0.0",
|
|
78
|
+
"prettier": "^3.4.2",
|
|
79
|
+
"tsdown": "^0.9.0",
|
|
80
|
+
"tsx": "^4.19.2",
|
|
81
|
+
"typescript": "^5.9.0",
|
|
82
|
+
"typescript-eslint": "^8.18.0",
|
|
83
|
+
"vitest": "^2.1.8"
|
|
84
|
+
}
|
|
85
|
+
}
|