architectonic 0.0.4 → 0.0.6
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 +58 -3
- package/bin/architectonic.js +739 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,8 +12,11 @@ skills -- reusable procedures and capabilities
|
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The initial placeholder releases were intentionally minimal. Starting in
|
|
15
|
-
`0.0.
|
|
16
|
-
|
|
15
|
+
`0.0.5`, `architectonic` can initialize a workspace, add layers, inspect the
|
|
16
|
+
local manifest, repair light manifest drift, update safely, and remove layers
|
|
17
|
+
without trampling local forks.
|
|
18
|
+
|
|
19
|
+
Starting in `0.0.6`, it can also inspect drift with `status` and `diff`.
|
|
17
20
|
|
|
18
21
|
## Command shape
|
|
19
22
|
|
|
@@ -25,9 +28,13 @@ architectonic add identity
|
|
|
25
28
|
architectonic add project
|
|
26
29
|
architectonic add skills
|
|
27
30
|
architectonic add teleology identity skills
|
|
31
|
+
architectonic init
|
|
28
32
|
architectonic doctor
|
|
33
|
+
architectonic status
|
|
34
|
+
architectonic diff
|
|
29
35
|
architectonic list
|
|
30
36
|
architectonic update
|
|
37
|
+
architectonic remove
|
|
31
38
|
```
|
|
32
39
|
|
|
33
40
|
`add` is explicit and leaves room for future verbs such as `doctor`, `list`,
|
|
@@ -45,8 +52,16 @@ npx architectonic add skills
|
|
|
45
52
|
npx architectonic add teleology identity skills
|
|
46
53
|
npx architectonic add skills --dir ./vendor
|
|
47
54
|
npx architectonic add teleology --source npm
|
|
55
|
+
npx architectonic init MyWorkspace
|
|
56
|
+
npx architectonic init --preset company
|
|
48
57
|
npx architectonic list
|
|
49
58
|
npx architectonic doctor
|
|
59
|
+
npx architectonic doctor --fix
|
|
60
|
+
npx architectonic status
|
|
61
|
+
npx architectonic diff teleology
|
|
62
|
+
npx architectonic update
|
|
63
|
+
npx architectonic update --dry-run
|
|
64
|
+
npx architectonic remove skills
|
|
50
65
|
```
|
|
51
66
|
|
|
52
67
|
`add` installs from the Architectonic GitHub organization into the current
|
|
@@ -87,7 +102,47 @@ ARCHITECTONIC_ADD_SOURCE # change the default source mode
|
|
|
87
102
|
`list` reads `architectonic.json` and shows installed layers.
|
|
88
103
|
|
|
89
104
|
`doctor` verifies that each recorded layer still exists and that the installed
|
|
90
|
-
package metadata matches the expected layer.
|
|
105
|
+
package metadata matches the expected layer. `doctor --fix` repairs light
|
|
106
|
+
manifest drift such as stale package names or recoverable default paths.
|
|
107
|
+
|
|
108
|
+
`status` gives a read-only summary of each layer:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
git layers: branch, dirty/clean, ahead/behind upstream
|
|
112
|
+
npm layers: installed version vs published version
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`diff <layer>` drills into one layer:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
git layers: local status lines plus ahead/behind numbers
|
|
119
|
+
npm layers: installed version vs published version
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`init` creates a workspace root, installs a preset, and seeds a top-level
|
|
123
|
+
`README.md` and `AGENTS.md`.
|
|
124
|
+
|
|
125
|
+
Supported presets:
|
|
126
|
+
|
|
127
|
+
```text
|
|
128
|
+
solo # teleology + identity + project + skills
|
|
129
|
+
company # teleology + project + skills
|
|
130
|
+
project # project + skills
|
|
131
|
+
agent # identity + skills
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`update` is conservative by design:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
git layers: only fast-forward clean git worktrees
|
|
138
|
+
npm layers: report newer packages but do not overwrite local forks
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If a user has modified an installed instance, `update` should skip it rather
|
|
142
|
+
than flatten their divergence.
|
|
143
|
+
|
|
144
|
+
`remove` deletes a recorded layer and updates the manifest. If the layer is a
|
|
145
|
+
dirty git worktree, it refuses unless `--force` is explicit.
|
|
91
146
|
|
|
92
147
|
## Run vs install
|
|
93
148
|
|
package/bin/architectonic.js
CHANGED
|
@@ -4,7 +4,7 @@ import fs from "node:fs";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
|
|
7
|
-
const VERSION = "0.0.
|
|
7
|
+
const VERSION = "0.0.6";
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
9
|
const [command, ...rest] = args;
|
|
10
10
|
const supported = ["teleology", "identity", "project", "skills"];
|
|
@@ -30,8 +30,14 @@ Usage:
|
|
|
30
30
|
npx architectonic add teleology identity skills
|
|
31
31
|
npx architectonic add skills --dir ./vendor
|
|
32
32
|
npx architectonic add teleology --source npm
|
|
33
|
+
npx architectonic init [name]
|
|
33
34
|
npx architectonic list
|
|
34
35
|
npx architectonic doctor
|
|
36
|
+
npx architectonic doctor --fix
|
|
37
|
+
npx architectonic status
|
|
38
|
+
npx architectonic diff <layer>
|
|
39
|
+
npx architectonic update
|
|
40
|
+
npx architectonic remove <layer>
|
|
35
41
|
|
|
36
42
|
Layers:
|
|
37
43
|
teleology purpose, principles, doctrine, governance
|
|
@@ -46,7 +52,14 @@ Run vs install:
|
|
|
46
52
|
|
|
47
53
|
What add does:
|
|
48
54
|
Installs the selected layer repositories into the target directory
|
|
49
|
-
from git or npm sources and records them in architectonic.json
|
|
55
|
+
from git or npm sources and records them in architectonic.json.
|
|
56
|
+
|
|
57
|
+
What status and diff do:
|
|
58
|
+
Inspect local drift from recorded sources without mutating anything.`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function printUsageError(message) {
|
|
62
|
+
throw new Error(`${message}\nRun \`architectonic help\` for usage.`);
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
function parseAddArgs(tokens) {
|
|
@@ -95,6 +108,203 @@ function parseAddArgs(tokens) {
|
|
|
95
108
|
return { targets, installDir, source };
|
|
96
109
|
}
|
|
97
110
|
|
|
111
|
+
function parseInitArgs(tokens) {
|
|
112
|
+
let installDir = process.cwd();
|
|
113
|
+
let source = process.env.ARCHITECTONIC_ADD_SOURCE || "git";
|
|
114
|
+
let preset = "solo";
|
|
115
|
+
let workspaceName = "";
|
|
116
|
+
|
|
117
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
118
|
+
const token = tokens[index];
|
|
119
|
+
if (token === "--dir" || token === "--out") {
|
|
120
|
+
const next = tokens[index + 1];
|
|
121
|
+
if (!next) {
|
|
122
|
+
throw new Error(`Missing value for ${token}`);
|
|
123
|
+
}
|
|
124
|
+
installDir = path.resolve(next);
|
|
125
|
+
index += 1;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (token.startsWith("--dir=")) {
|
|
129
|
+
installDir = path.resolve(token.slice("--dir=".length));
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (token.startsWith("--out=")) {
|
|
133
|
+
installDir = path.resolve(token.slice("--out=".length));
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (token === "--source") {
|
|
137
|
+
const next = tokens[index + 1];
|
|
138
|
+
if (!next) {
|
|
139
|
+
throw new Error("Missing value for --source");
|
|
140
|
+
}
|
|
141
|
+
source = next;
|
|
142
|
+
index += 1;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (token.startsWith("--source=")) {
|
|
146
|
+
source = token.slice("--source=".length);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (token === "--preset") {
|
|
150
|
+
const next = tokens[index + 1];
|
|
151
|
+
if (!next) {
|
|
152
|
+
throw new Error("Missing value for --preset");
|
|
153
|
+
}
|
|
154
|
+
preset = next;
|
|
155
|
+
index += 1;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (token.startsWith("--preset=")) {
|
|
159
|
+
preset = token.slice("--preset=".length);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (token.startsWith("-")) {
|
|
163
|
+
throw new Error(`Unknown option: ${token}`);
|
|
164
|
+
}
|
|
165
|
+
if (!workspaceName) {
|
|
166
|
+
workspaceName = token;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
throw new Error(`Unexpected argument: ${token}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (workspaceName) {
|
|
173
|
+
installDir = path.resolve(installDir, workspaceName);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return { installDir, source, preset, workspaceName };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function parseDoctorArgs(tokens) {
|
|
180
|
+
let installDir = process.cwd();
|
|
181
|
+
let fix = false;
|
|
182
|
+
|
|
183
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
184
|
+
const token = tokens[index];
|
|
185
|
+
if (token === "--fix") {
|
|
186
|
+
fix = true;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (token.startsWith("-")) {
|
|
190
|
+
throw new Error(`Unknown option: ${token}`);
|
|
191
|
+
}
|
|
192
|
+
installDir = path.resolve(token);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return { installDir, fix };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function parseUpdateArgs(tokens) {
|
|
199
|
+
let installDir = process.cwd();
|
|
200
|
+
let dryRun = false;
|
|
201
|
+
|
|
202
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
203
|
+
const token = tokens[index];
|
|
204
|
+
if (token === "--dry-run") {
|
|
205
|
+
dryRun = true;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (token.startsWith("-")) {
|
|
209
|
+
throw new Error(`Unknown option: ${token}`);
|
|
210
|
+
}
|
|
211
|
+
installDir = path.resolve(token);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return { installDir, dryRun };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function parseStatusArgs(tokens) {
|
|
218
|
+
let installDir = process.cwd();
|
|
219
|
+
|
|
220
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
221
|
+
const token = tokens[index];
|
|
222
|
+
if (token.startsWith("-")) {
|
|
223
|
+
throw new Error(`Unknown option: ${token}`);
|
|
224
|
+
}
|
|
225
|
+
installDir = path.resolve(token);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { installDir };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function parseDiffArgs(tokens) {
|
|
232
|
+
let installDir = process.cwd();
|
|
233
|
+
let target = "";
|
|
234
|
+
|
|
235
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
236
|
+
const token = tokens[index];
|
|
237
|
+
if (token === "--dir" || token === "--out") {
|
|
238
|
+
const next = tokens[index + 1];
|
|
239
|
+
if (!next) {
|
|
240
|
+
throw new Error(`Missing value for ${token}`);
|
|
241
|
+
}
|
|
242
|
+
installDir = path.resolve(next);
|
|
243
|
+
index += 1;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (token.startsWith("--dir=")) {
|
|
247
|
+
installDir = path.resolve(token.slice("--dir=".length));
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (token.startsWith("--out=")) {
|
|
251
|
+
installDir = path.resolve(token.slice("--out=".length));
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (token.startsWith("-")) {
|
|
255
|
+
throw new Error(`Unknown option: ${token}`);
|
|
256
|
+
}
|
|
257
|
+
if (!target) {
|
|
258
|
+
target = token;
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
throw new Error(`Unexpected argument: ${token}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return { installDir, target };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function parseRemoveArgs(tokens) {
|
|
268
|
+
let installDir = process.cwd();
|
|
269
|
+
let force = false;
|
|
270
|
+
let target = "";
|
|
271
|
+
|
|
272
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
273
|
+
const token = tokens[index];
|
|
274
|
+
if (token === "--force") {
|
|
275
|
+
force = true;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (token === "--dir" || token === "--out") {
|
|
279
|
+
const next = tokens[index + 1];
|
|
280
|
+
if (!next) {
|
|
281
|
+
throw new Error(`Missing value for ${token}`);
|
|
282
|
+
}
|
|
283
|
+
installDir = path.resolve(next);
|
|
284
|
+
index += 1;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (token.startsWith("--dir=")) {
|
|
288
|
+
installDir = path.resolve(token.slice("--dir=".length));
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (token.startsWith("--out=")) {
|
|
292
|
+
installDir = path.resolve(token.slice("--out=".length));
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (token.startsWith("-")) {
|
|
296
|
+
throw new Error(`Unknown option: ${token}`);
|
|
297
|
+
}
|
|
298
|
+
if (!target) {
|
|
299
|
+
target = token;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
throw new Error(`Unexpected argument: ${token}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return { installDir, force, target };
|
|
306
|
+
}
|
|
307
|
+
|
|
98
308
|
function ensureGitAvailable() {
|
|
99
309
|
const result = spawnSync("git", ["--version"], { encoding: "utf8" });
|
|
100
310
|
if (result.status !== 0) {
|
|
@@ -165,6 +375,14 @@ function writeManifest(manifestPath, manifest) {
|
|
|
165
375
|
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
166
376
|
}
|
|
167
377
|
|
|
378
|
+
function readJson(filePath) {
|
|
379
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function pathExists(filePath) {
|
|
383
|
+
return fs.existsSync(filePath);
|
|
384
|
+
}
|
|
385
|
+
|
|
168
386
|
function cloneLayer(target, installDir) {
|
|
169
387
|
const repoUrl = repoUrlFor(target);
|
|
170
388
|
const targetPath = targetPathFor(installDir, target);
|
|
@@ -261,51 +479,170 @@ function readInstalledPackageName(layerPath) {
|
|
|
261
479
|
return null;
|
|
262
480
|
}
|
|
263
481
|
try {
|
|
264
|
-
return
|
|
482
|
+
return readJson(packageJsonPath).name || null;
|
|
265
483
|
} catch {
|
|
266
484
|
return null;
|
|
267
485
|
}
|
|
268
486
|
}
|
|
269
487
|
|
|
270
|
-
function
|
|
488
|
+
function readInstalledVersion(layerPath) {
|
|
489
|
+
const packageJsonPath = path.join(layerPath, "package.json");
|
|
490
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
return readJson(packageJsonPath).version || null;
|
|
495
|
+
} catch {
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function hasGitRepo(layerPath) {
|
|
501
|
+
return pathExists(path.join(layerPath, ".git"));
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function gitResult(args, cwd) {
|
|
505
|
+
return spawnSync("git", args, {
|
|
506
|
+
cwd,
|
|
507
|
+
encoding: "utf8",
|
|
508
|
+
stdio: "pipe",
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function isGitWorktreeDirty(layerPath) {
|
|
513
|
+
const result = gitResult(["status", "--porcelain"], layerPath);
|
|
514
|
+
if (result.status !== 0) {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
return Boolean((result.stdout || "").trim());
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function packageMetaFor(target, layerPath) {
|
|
521
|
+
return {
|
|
522
|
+
expectedPackageName: packageMap[target] || null,
|
|
523
|
+
packageName: readInstalledPackageName(layerPath),
|
|
524
|
+
version: readInstalledVersion(layerPath),
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function relativeManifestPath(installDir, absolutePath) {
|
|
529
|
+
return `./${path.relative(installDir, absolutePath).replace(/\\/g, "/")}`;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function ensureInstallPrereqs(source) {
|
|
271
533
|
if (source === "git") {
|
|
272
|
-
|
|
534
|
+
ensureGitAvailable();
|
|
535
|
+
return;
|
|
273
536
|
}
|
|
274
537
|
if (source === "npm") {
|
|
275
|
-
|
|
538
|
+
ensureNpmAvailable();
|
|
539
|
+
ensureTarAvailable();
|
|
540
|
+
return;
|
|
276
541
|
}
|
|
277
542
|
throw new Error(`Unsupported source: ${source}. Use git or npm.`);
|
|
278
543
|
}
|
|
279
544
|
|
|
280
|
-
function
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
545
|
+
function updateManifestLayerRecord(manifest, installDir, layerName, absoluteLayerPath, source, refOverride = null) {
|
|
546
|
+
manifest.layers[layerName] = {
|
|
547
|
+
source,
|
|
548
|
+
ref: refOverride ?? manifest.layers[layerName]?.ref ?? (source === "git" ? repoUrlFor(layerName) : packageSpecFor(layerName)),
|
|
549
|
+
path: relativeManifestPath(installDir, absoluteLayerPath),
|
|
550
|
+
installed_at: new Date().toISOString(),
|
|
551
|
+
package_name: readInstalledPackageName(absoluteLayerPath),
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function getGitHead(layerPath) {
|
|
556
|
+
const result = gitResult(["rev-parse", "HEAD"], layerPath);
|
|
557
|
+
if (result.status !== 0) {
|
|
558
|
+
return null;
|
|
284
559
|
}
|
|
285
|
-
return
|
|
560
|
+
return (result.stdout || "").trim() || null;
|
|
286
561
|
}
|
|
287
562
|
|
|
288
|
-
function
|
|
289
|
-
const
|
|
563
|
+
function getGitBranch(layerPath) {
|
|
564
|
+
const result = gitResult(["rev-parse", "--abbrev-ref", "HEAD"], layerPath);
|
|
565
|
+
if (result.status !== 0) {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
return (result.stdout || "").trim() || null;
|
|
569
|
+
}
|
|
290
570
|
|
|
291
|
-
|
|
292
|
-
|
|
571
|
+
function getGitUpstream(layerPath) {
|
|
572
|
+
const result = gitResult(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], layerPath);
|
|
573
|
+
if (result.status !== 0) {
|
|
574
|
+
return null;
|
|
293
575
|
}
|
|
576
|
+
return (result.stdout || "").trim() || null;
|
|
577
|
+
}
|
|
294
578
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
579
|
+
function getGitAheadBehind(layerPath) {
|
|
580
|
+
const upstream = getGitUpstream(layerPath);
|
|
581
|
+
if (!upstream) {
|
|
582
|
+
return null;
|
|
298
583
|
}
|
|
584
|
+
const result = gitResult(["rev-list", "--left-right", "--count", `${upstream}...HEAD`], layerPath);
|
|
585
|
+
if (result.status !== 0) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
const [behindRaw, aheadRaw] = (result.stdout || "").trim().split(/\s+/);
|
|
589
|
+
return {
|
|
590
|
+
upstream,
|
|
591
|
+
behind: Number.parseInt(behindRaw || "0", 10),
|
|
592
|
+
ahead: Number.parseInt(aheadRaw || "0", 10),
|
|
593
|
+
};
|
|
594
|
+
}
|
|
299
595
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
ensureTarAvailable();
|
|
305
|
-
} else {
|
|
306
|
-
throw new Error(`Unsupported source: ${source}. Use git or npm.`);
|
|
596
|
+
function getGitDiffSummary(layerPath) {
|
|
597
|
+
const result = gitResult(["status", "--short"], layerPath);
|
|
598
|
+
if (result.status !== 0) {
|
|
599
|
+
return null;
|
|
307
600
|
}
|
|
601
|
+
const lines = (result.stdout || "").split(/\r?\n/).filter(Boolean);
|
|
602
|
+
return {
|
|
603
|
+
dirty: lines.length > 0,
|
|
604
|
+
lines,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
308
607
|
|
|
608
|
+
function getNpmPublishedVersion(packageSpec) {
|
|
609
|
+
const result = spawnSync("npm", ["view", packageSpec, "version"], {
|
|
610
|
+
encoding: "utf8",
|
|
611
|
+
stdio: "pipe",
|
|
612
|
+
shell: true,
|
|
613
|
+
});
|
|
614
|
+
if (result.status !== 0) {
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
return (result.stdout || "").trim() || null;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function describeGitLayerState(layerPath) {
|
|
621
|
+
const branch = getGitBranch(layerPath);
|
|
622
|
+
const head = getGitHead(layerPath);
|
|
623
|
+
const diffSummary = getGitDiffSummary(layerPath);
|
|
624
|
+
const aheadBehind = getGitAheadBehind(layerPath);
|
|
625
|
+
return {
|
|
626
|
+
branch,
|
|
627
|
+
head,
|
|
628
|
+
dirty: diffSummary?.dirty ?? false,
|
|
629
|
+
diffLines: diffSummary?.lines ?? [],
|
|
630
|
+
aheadBehind,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function describeNpmLayerState(layerName, layerPath) {
|
|
635
|
+
const installedVersion = readInstalledVersion(layerPath);
|
|
636
|
+
const publishedVersion = getNpmPublishedVersion(packageSpecFor(layerName));
|
|
637
|
+
return {
|
|
638
|
+
installedVersion,
|
|
639
|
+
publishedVersion,
|
|
640
|
+
outdated: Boolean(installedVersion && publishedVersion && installedVersion !== publishedVersion),
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function installTargets(targets, installDir, source) {
|
|
645
|
+
ensureInstallPrereqs(source);
|
|
309
646
|
fs.mkdirSync(installDir, { recursive: true });
|
|
310
647
|
|
|
311
648
|
const installed = [];
|
|
@@ -322,13 +659,7 @@ function addCommand(tokens) {
|
|
|
322
659
|
manifest.npm_source_base = npmBase || "registry";
|
|
323
660
|
|
|
324
661
|
for (const item of installed) {
|
|
325
|
-
manifest
|
|
326
|
-
source: item.source,
|
|
327
|
-
ref: item.repo,
|
|
328
|
-
path: `./${path.relative(installDir, item.path).replace(/\\/g, "/")}`,
|
|
329
|
-
installed_at: manifest.installed_at,
|
|
330
|
-
package_name: readInstalledPackageName(item.path),
|
|
331
|
-
};
|
|
662
|
+
updateManifestLayerRecord(manifest, installDir, item.name, item.path, item.source, item.repo);
|
|
332
663
|
}
|
|
333
664
|
|
|
334
665
|
writeManifest(manifestPath, manifest);
|
|
@@ -338,6 +669,86 @@ function addCommand(tokens) {
|
|
|
338
669
|
console.log(`Wrote ${manifestPath}`);
|
|
339
670
|
}
|
|
340
671
|
|
|
672
|
+
function installLayer(target, installDir, source) {
|
|
673
|
+
if (source === "git") {
|
|
674
|
+
return cloneLayer(target, installDir);
|
|
675
|
+
}
|
|
676
|
+
if (source === "npm") {
|
|
677
|
+
return packNpmLayer(target, installDir);
|
|
678
|
+
}
|
|
679
|
+
throw new Error(`Unsupported source: ${source}. Use git or npm.`);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function loadManifestFromDir(installDir) {
|
|
683
|
+
const manifestPath = manifestPathFor(installDir);
|
|
684
|
+
if (!fs.existsSync(manifestPath)) {
|
|
685
|
+
throw new Error(`No architectonic.json found in ${installDir}`);
|
|
686
|
+
}
|
|
687
|
+
return { manifestPath, manifest: readManifest(manifestPath) };
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function addCommand(tokens) {
|
|
691
|
+
const { targets, installDir, source } = parseAddArgs(tokens);
|
|
692
|
+
|
|
693
|
+
if (!targets.length) {
|
|
694
|
+
throw new Error("Specify one or more layers: teleology, identity, project, skills");
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const invalid = targets.filter((target) => !supportedSet.has(target));
|
|
698
|
+
if (invalid.length) {
|
|
699
|
+
throw new Error(`Unknown layer(s): ${invalid.join(", ")}\nSupported layers: ${supported.join(", ")}`);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
installTargets(targets, installDir, source);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function resolvePreset(preset) {
|
|
706
|
+
const presets = {
|
|
707
|
+
solo: ["teleology", "identity", "project", "skills"],
|
|
708
|
+
company: ["teleology", "project", "skills"],
|
|
709
|
+
project: ["project", "skills"],
|
|
710
|
+
agent: ["identity", "skills"],
|
|
711
|
+
};
|
|
712
|
+
const layers = presets[preset];
|
|
713
|
+
if (!layers) {
|
|
714
|
+
throw new Error(`Unknown preset: ${preset}. Supported presets: ${Object.keys(presets).join(", ")}`);
|
|
715
|
+
}
|
|
716
|
+
return layers;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function writeInitFiles(installDir, workspaceName, layers) {
|
|
720
|
+
const displayName = workspaceName || path.basename(installDir);
|
|
721
|
+
const readmePath = path.join(installDir, "README.md");
|
|
722
|
+
const agentsPath = path.join(installDir, "AGENTS.md");
|
|
723
|
+
|
|
724
|
+
if (!pathExists(readmePath)) {
|
|
725
|
+
fs.writeFileSync(
|
|
726
|
+
readmePath,
|
|
727
|
+
`# ${displayName}\n\nThis workspace was initialized by \`architectonic\`.\n\nInstalled layers:\n\n${layers.map((layer) => `- ${layer}`).join("\n")}\n`,
|
|
728
|
+
"utf8",
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if (!pathExists(agentsPath)) {
|
|
733
|
+
fs.writeFileSync(
|
|
734
|
+
agentsPath,
|
|
735
|
+
`# Agent Instructions\n\nRead installed layers before making structural changes.\n\nPriority order:\n1. teleology\n2. identity\n3. project\n4. skills\n`,
|
|
736
|
+
"utf8",
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function initCommand(tokens) {
|
|
742
|
+
const { installDir, source, preset, workspaceName } = parseInitArgs(tokens);
|
|
743
|
+
if (pathExists(installDir) && fs.readdirSync(installDir).length > 0) {
|
|
744
|
+
throw new Error(`Refusing to initialize into a non-empty directory: ${installDir}`);
|
|
745
|
+
}
|
|
746
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
747
|
+
const layers = resolvePreset(preset);
|
|
748
|
+
writeInitFiles(installDir, workspaceName, layers);
|
|
749
|
+
installTargets(layers, installDir, source);
|
|
750
|
+
}
|
|
751
|
+
|
|
341
752
|
function listCommand(tokens) {
|
|
342
753
|
const installDir = tokens[0] ? path.resolve(tokens[0]) : process.cwd();
|
|
343
754
|
const { manifest } = loadManifestFromDir(installDir);
|
|
@@ -359,11 +770,65 @@ function listCommand(tokens) {
|
|
|
359
770
|
}
|
|
360
771
|
}
|
|
361
772
|
|
|
773
|
+
function statusCommand(tokens) {
|
|
774
|
+
const { installDir } = parseStatusArgs(tokens);
|
|
775
|
+
const { manifest } = loadManifestFromDir(installDir);
|
|
776
|
+
const entries = Object.entries(manifest.layers || {});
|
|
777
|
+
|
|
778
|
+
if (!entries.length) {
|
|
779
|
+
console.log("No layers installed.");
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
ensureGitAvailable();
|
|
784
|
+
ensureNpmAvailable();
|
|
785
|
+
|
|
786
|
+
console.log(`architectonic status`);
|
|
787
|
+
console.log(` root: ${installDir}`);
|
|
788
|
+
|
|
789
|
+
for (const [name, layer] of entries) {
|
|
790
|
+
const layerPath = path.resolve(installDir, String(layer.path || ""));
|
|
791
|
+
if (!pathExists(layerPath)) {
|
|
792
|
+
console.log(` [missing] ${name}: ${layer.path || "unknown path"}`);
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
if (layer.source === "git") {
|
|
797
|
+
const state = describeGitLayerState(layerPath);
|
|
798
|
+
const dirtyLabel = state.dirty ? "dirty" : "clean";
|
|
799
|
+
const branchLabel = state.branch || "detached";
|
|
800
|
+
let relation = "no-upstream";
|
|
801
|
+
if (state.aheadBehind) {
|
|
802
|
+
relation = `ahead ${state.aheadBehind.ahead}, behind ${state.aheadBehind.behind}`;
|
|
803
|
+
}
|
|
804
|
+
console.log(` [git] ${name}: ${branchLabel}, ${dirtyLabel}, ${relation}`);
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (layer.source === "npm") {
|
|
809
|
+
const state = describeNpmLayerState(name, layerPath);
|
|
810
|
+
const installed = state.installedVersion || "unknown";
|
|
811
|
+
const published = state.publishedVersion || "unknown";
|
|
812
|
+
const relation = state.outdated ? "outdated" : "current-or-unknown";
|
|
813
|
+
console.log(` [npm] ${name}: installed ${installed}, published ${published}, ${relation}`);
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
console.log(` [unknown] ${name}: unsupported source ${layer.source || "unknown"}`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function inferLayerPath(installDir, layerName) {
|
|
822
|
+
const candidate = targetPathFor(installDir, layerName);
|
|
823
|
+
return pathExists(candidate) ? candidate : null;
|
|
824
|
+
}
|
|
825
|
+
|
|
362
826
|
function doctorCommand(tokens) {
|
|
363
|
-
const installDir
|
|
827
|
+
const { installDir, fix } = parseDoctorArgs(tokens);
|
|
364
828
|
const { manifestPath, manifest } = loadManifestFromDir(installDir);
|
|
365
829
|
const entries = Object.entries(manifest.layers || {});
|
|
366
830
|
let failures = 0;
|
|
831
|
+
let changed = false;
|
|
367
832
|
|
|
368
833
|
console.log(`architectonic doctor`);
|
|
369
834
|
console.log(` root: ${installDir}`);
|
|
@@ -384,6 +849,13 @@ function doctorCommand(tokens) {
|
|
|
384
849
|
const expectedPackageName = packageMap[name];
|
|
385
850
|
|
|
386
851
|
if (!exists) {
|
|
852
|
+
const inferredPath = inferLayerPath(installDir, name);
|
|
853
|
+
if (fix && inferredPath) {
|
|
854
|
+
updateManifestLayerRecord(manifest, installDir, name, inferredPath, layer.source || "unknown", layer.ref || null);
|
|
855
|
+
changed = true;
|
|
856
|
+
console.log(` [fix] ${name}: repointed path to ${relativeManifestPath(installDir, inferredPath)}`);
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
387
859
|
console.log(` [fail] ${name}: missing directory ${layerPath}`);
|
|
388
860
|
failures += 1;
|
|
389
861
|
continue;
|
|
@@ -396,25 +868,238 @@ function doctorCommand(tokens) {
|
|
|
396
868
|
}
|
|
397
869
|
|
|
398
870
|
if (packageName && expectedPackageName && packageName !== expectedPackageName) {
|
|
871
|
+
if (fix) {
|
|
872
|
+
manifest.layers[name].package_name = packageName;
|
|
873
|
+
manifest.layers[name].path = relativeManifestPath(installDir, layerPath);
|
|
874
|
+
changed = true;
|
|
875
|
+
console.log(` [fix] ${name}: synced manifest package name to ${packageName}`);
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
399
878
|
console.log(` [fail] ${name}: expected package ${expectedPackageName}, found ${packageName}`);
|
|
400
879
|
failures += 1;
|
|
401
880
|
continue;
|
|
402
881
|
}
|
|
403
882
|
|
|
883
|
+
if (fix) {
|
|
884
|
+
const normalizedPath = relativeManifestPath(installDir, layerPath);
|
|
885
|
+
if (manifest.layers[name].path !== normalizedPath || manifest.layers[name].package_name !== packageName) {
|
|
886
|
+
manifest.layers[name].path = normalizedPath;
|
|
887
|
+
manifest.layers[name].package_name = packageName;
|
|
888
|
+
changed = true;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
404
892
|
console.log(` [ok] ${name}: ${relativePath} (${layer.source || "unknown"})`);
|
|
405
893
|
}
|
|
406
894
|
|
|
895
|
+
if (fix && changed) {
|
|
896
|
+
writeManifest(manifestPath, manifest);
|
|
897
|
+
console.log(` [write] updated manifest repairs`);
|
|
898
|
+
}
|
|
899
|
+
|
|
407
900
|
if (failures > 0) {
|
|
408
901
|
process.exit(1);
|
|
409
902
|
}
|
|
410
903
|
}
|
|
411
904
|
|
|
905
|
+
function updateGitLayer(layerName, layerPath, dryRun) {
|
|
906
|
+
if (!hasGitRepo(layerPath)) {
|
|
907
|
+
return { status: "skip", detail: "not a git repository" };
|
|
908
|
+
}
|
|
909
|
+
const dirty = isGitWorktreeDirty(layerPath);
|
|
910
|
+
if (dirty === null) {
|
|
911
|
+
return { status: "skip", detail: "unable to inspect git status" };
|
|
912
|
+
}
|
|
913
|
+
if (dirty) {
|
|
914
|
+
return { status: "skip", detail: "local changes detected; preserving fork" };
|
|
915
|
+
}
|
|
916
|
+
if (dryRun) {
|
|
917
|
+
return { status: "plan", detail: "would run git pull --ff-only" };
|
|
918
|
+
}
|
|
919
|
+
const result = gitResult(["pull", "--ff-only"], layerPath);
|
|
920
|
+
if (result.status !== 0) {
|
|
921
|
+
return { status: "fail", detail: (result.stderr || result.stdout || "").trim() || "git pull failed" };
|
|
922
|
+
}
|
|
923
|
+
return { status: "ok", detail: (result.stdout || "").trim() || "up to date" };
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function getLatestNpmVersion(packageSpec) {
|
|
927
|
+
const result = spawnSync("npm", ["view", packageSpec, "version"], {
|
|
928
|
+
encoding: "utf8",
|
|
929
|
+
stdio: "pipe",
|
|
930
|
+
shell: true,
|
|
931
|
+
});
|
|
932
|
+
if (result.status !== 0) {
|
|
933
|
+
return null;
|
|
934
|
+
}
|
|
935
|
+
return (result.stdout || "").trim() || null;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function updateNpmLayer(layerName, layerPath) {
|
|
939
|
+
const installedVersion = readInstalledVersion(layerPath);
|
|
940
|
+
const packageSpec = packageSpecFor(layerName);
|
|
941
|
+
const latestVersion = getLatestNpmVersion(packageSpec);
|
|
942
|
+
if (!latestVersion) {
|
|
943
|
+
return { status: "skip", detail: "unable to resolve latest npm version" };
|
|
944
|
+
}
|
|
945
|
+
if (!installedVersion) {
|
|
946
|
+
return { status: "skip", detail: `latest ${latestVersion} available; local version unknown` };
|
|
947
|
+
}
|
|
948
|
+
if (installedVersion === latestVersion) {
|
|
949
|
+
return { status: "ok", detail: `already at ${installedVersion}` };
|
|
950
|
+
}
|
|
951
|
+
return {
|
|
952
|
+
status: "skip",
|
|
953
|
+
detail: `newer package ${latestVersion} available; skipped to preserve local fork (${installedVersion} installed)`,
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
function updateCommand(tokens) {
|
|
958
|
+
const { installDir, dryRun } = parseUpdateArgs(tokens);
|
|
959
|
+
const { manifestPath, manifest } = loadManifestFromDir(installDir);
|
|
960
|
+
const entries = Object.entries(manifest.layers || {});
|
|
961
|
+
|
|
962
|
+
if (!entries.length) {
|
|
963
|
+
throw new Error("No installed layers recorded.");
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
ensureGitAvailable();
|
|
967
|
+
ensureNpmAvailable();
|
|
968
|
+
|
|
969
|
+
let changed = false;
|
|
970
|
+
console.log(`architectonic update${dryRun ? " --dry-run" : ""}`);
|
|
971
|
+
console.log(` root: ${installDir}`);
|
|
972
|
+
|
|
973
|
+
for (const [name, layer] of entries) {
|
|
974
|
+
const layerPath = path.resolve(installDir, String(layer.path || ""));
|
|
975
|
+
if (!pathExists(layerPath)) {
|
|
976
|
+
console.log(` [skip] ${name}: missing directory`);
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
let outcome;
|
|
981
|
+
if (layer.source === "git") {
|
|
982
|
+
outcome = updateGitLayer(name, layerPath, dryRun);
|
|
983
|
+
} else if (layer.source === "npm") {
|
|
984
|
+
outcome = updateNpmLayer(name, layerPath);
|
|
985
|
+
} else {
|
|
986
|
+
outcome = { status: "skip", detail: `unsupported source ${layer.source || "unknown"}` };
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
console.log(` [${outcome.status}] ${name}: ${outcome.detail}`);
|
|
990
|
+
if (outcome.status === "ok" && !dryRun) {
|
|
991
|
+
manifest.layers[name].installed_at = new Date().toISOString();
|
|
992
|
+
manifest.layers[name].package_name = readInstalledPackageName(layerPath);
|
|
993
|
+
changed = true;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (changed) {
|
|
998
|
+
writeManifest(manifestPath, manifest);
|
|
999
|
+
console.log(` [write] refreshed manifest timestamps`);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function removeCommand(tokens) {
|
|
1004
|
+
const { installDir, force, target } = parseRemoveArgs(tokens);
|
|
1005
|
+
if (!target) {
|
|
1006
|
+
throw new Error("Specify a layer to remove.");
|
|
1007
|
+
}
|
|
1008
|
+
if (!supportedSet.has(target)) {
|
|
1009
|
+
throw new Error(`Unknown layer: ${target}`);
|
|
1010
|
+
}
|
|
1011
|
+
const { manifestPath, manifest } = loadManifestFromDir(installDir);
|
|
1012
|
+
const layer = manifest.layers[target];
|
|
1013
|
+
if (!layer) {
|
|
1014
|
+
throw new Error(`Layer not recorded in manifest: ${target}`);
|
|
1015
|
+
}
|
|
1016
|
+
const layerPath = path.resolve(installDir, String(layer.path || ""));
|
|
1017
|
+
if (pathExists(layerPath) && hasGitRepo(layerPath) && !force) {
|
|
1018
|
+
const dirty = isGitWorktreeDirty(layerPath);
|
|
1019
|
+
if (dirty) {
|
|
1020
|
+
throw new Error(`Refusing to remove ${target}: local git changes detected. Re-run with --force if you really want to delete it.`);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (pathExists(layerPath)) {
|
|
1024
|
+
fs.rmSync(layerPath, { recursive: true, force: true });
|
|
1025
|
+
}
|
|
1026
|
+
delete manifest.layers[target];
|
|
1027
|
+
writeManifest(manifestPath, manifest);
|
|
1028
|
+
console.log(`Removed ${target}`);
|
|
1029
|
+
console.log(`Updated ${manifestPath}`);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function diffCommand(tokens) {
|
|
1033
|
+
const { installDir, target } = parseDiffArgs(tokens);
|
|
1034
|
+
if (!target) {
|
|
1035
|
+
throw new Error("Specify a layer to diff.");
|
|
1036
|
+
}
|
|
1037
|
+
if (!supportedSet.has(target)) {
|
|
1038
|
+
throw new Error(`Unknown layer: ${target}`);
|
|
1039
|
+
}
|
|
1040
|
+
const { manifest } = loadManifestFromDir(installDir);
|
|
1041
|
+
const layer = manifest.layers[target];
|
|
1042
|
+
if (!layer) {
|
|
1043
|
+
throw new Error(`Layer not recorded in manifest: ${target}`);
|
|
1044
|
+
}
|
|
1045
|
+
const layerPath = path.resolve(installDir, String(layer.path || ""));
|
|
1046
|
+
if (!pathExists(layerPath)) {
|
|
1047
|
+
throw new Error(`Layer path does not exist: ${layerPath}`);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
if (layer.source === "git") {
|
|
1051
|
+
ensureGitAvailable();
|
|
1052
|
+
const summary = getGitDiffSummary(layerPath);
|
|
1053
|
+
const aheadBehind = getGitAheadBehind(layerPath);
|
|
1054
|
+
console.log(`architectonic diff ${target}`);
|
|
1055
|
+
console.log(` source: git`);
|
|
1056
|
+
console.log(` path: ${layerPath}`);
|
|
1057
|
+
if (aheadBehind) {
|
|
1058
|
+
console.log(` upstream: ${aheadBehind.upstream}`);
|
|
1059
|
+
console.log(` ahead: ${aheadBehind.ahead}`);
|
|
1060
|
+
console.log(` behind: ${aheadBehind.behind}`);
|
|
1061
|
+
}
|
|
1062
|
+
if (!summary || !summary.lines.length) {
|
|
1063
|
+
console.log(` local changes: none`);
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
console.log(` local changes:`);
|
|
1067
|
+
for (const line of summary.lines) {
|
|
1068
|
+
console.log(` ${line}`);
|
|
1069
|
+
}
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
if (layer.source === "npm") {
|
|
1074
|
+
ensureNpmAvailable();
|
|
1075
|
+
const state = describeNpmLayerState(target, layerPath);
|
|
1076
|
+
console.log(`architectonic diff ${target}`);
|
|
1077
|
+
console.log(` source: npm`);
|
|
1078
|
+
console.log(` path: ${layerPath}`);
|
|
1079
|
+
console.log(` installed version: ${state.installedVersion || "unknown"}`);
|
|
1080
|
+
console.log(` published version: ${state.publishedVersion || "unknown"}`);
|
|
1081
|
+
if (state.outdated) {
|
|
1082
|
+
console.log(` drift: newer package available`);
|
|
1083
|
+
} else {
|
|
1084
|
+
console.log(` drift: none detected from package version`);
|
|
1085
|
+
}
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
throw new Error(`Unsupported source for diff: ${layer.source || "unknown"}`);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
412
1092
|
try {
|
|
413
1093
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
414
1094
|
printHelp();
|
|
415
1095
|
process.exit(0);
|
|
416
1096
|
}
|
|
417
1097
|
|
|
1098
|
+
if (command === "init") {
|
|
1099
|
+
initCommand(rest);
|
|
1100
|
+
process.exit(0);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
418
1103
|
if (command === "add") {
|
|
419
1104
|
addCommand(rest);
|
|
420
1105
|
process.exit(0);
|
|
@@ -430,7 +1115,27 @@ try {
|
|
|
430
1115
|
process.exit(0);
|
|
431
1116
|
}
|
|
432
1117
|
|
|
433
|
-
|
|
1118
|
+
if (command === "status") {
|
|
1119
|
+
statusCommand(rest);
|
|
1120
|
+
process.exit(0);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
if (command === "diff") {
|
|
1124
|
+
diffCommand(rest);
|
|
1125
|
+
process.exit(0);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if (command === "update") {
|
|
1129
|
+
updateCommand(rest);
|
|
1130
|
+
process.exit(0);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
if (command === "remove") {
|
|
1134
|
+
removeCommand(rest);
|
|
1135
|
+
process.exit(0);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
printUsageError(`Unknown command: ${command}`);
|
|
434
1139
|
} catch (error) {
|
|
435
1140
|
console.error(error.message);
|
|
436
1141
|
process.exit(1);
|