architectonic 0.0.2 → 0.0.4
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 +52 -9
- package/bin/architectonic.js +396 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,9 +11,9 @@ project -- operating context for a concrete initiative
|
|
|
11
11
|
skills -- reusable procedures and capabilities
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
The initial placeholder releases
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
The initial placeholder releases were intentionally minimal. Starting in
|
|
15
|
+
`0.0.4`, `architectonic add` installs the selected layer repositories from
|
|
16
|
+
either git or npm sources and records them in `architectonic.json`.
|
|
17
17
|
|
|
18
18
|
## Command shape
|
|
19
19
|
|
|
@@ -35,16 +35,59 @@ architectonic update
|
|
|
35
35
|
|
|
36
36
|
## Current behavior
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
The primary implemented command is:
|
|
39
39
|
|
|
40
40
|
```text
|
|
41
|
-
npx architectonic
|
|
42
|
-
npx architectonic
|
|
43
|
-
npx architectonic add
|
|
41
|
+
npx architectonic add teleology
|
|
42
|
+
npx architectonic add identity
|
|
43
|
+
npx architectonic add project
|
|
44
|
+
npx architectonic add skills
|
|
45
|
+
npx architectonic add teleology identity skills
|
|
46
|
+
npx architectonic add skills --dir ./vendor
|
|
47
|
+
npx architectonic add teleology --source npm
|
|
48
|
+
npx architectonic list
|
|
49
|
+
npx architectonic doctor
|
|
44
50
|
```
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
`add` installs from the Architectonic GitHub organization into the current
|
|
53
|
+
directory by default:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
./teleology
|
|
57
|
+
./identity
|
|
58
|
+
./project
|
|
59
|
+
./skills
|
|
60
|
+
./architectonic.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`architectonic.json` records what was installed and where it landed.
|
|
64
|
+
|
|
65
|
+
If a target directory already exists, the command stops instead of silently
|
|
66
|
+
overwriting it.
|
|
67
|
+
|
|
68
|
+
## Sources
|
|
69
|
+
|
|
70
|
+
`add` supports two source modes:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
--source git # clone from GitHub or another git base
|
|
74
|
+
--source npm # pack and extract from npm packages
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The default is `git`.
|
|
78
|
+
|
|
79
|
+
Environment overrides:
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
ARCHITECTONIC_SOURCE_BASE # override the git source base
|
|
83
|
+
ARCHITECTONIC_NPM_BASE # override the npm package base or local package root
|
|
84
|
+
ARCHITECTONIC_ADD_SOURCE # change the default source mode
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`list` reads `architectonic.json` and shows installed layers.
|
|
88
|
+
|
|
89
|
+
`doctor` verifies that each recorded layer still exists and that the installed
|
|
90
|
+
package metadata matches the expected layer.
|
|
48
91
|
|
|
49
92
|
## Run vs install
|
|
50
93
|
|
package/bin/architectonic.js
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
const VERSION = "0.0.4";
|
|
3
8
|
const args = process.argv.slice(2);
|
|
4
|
-
const [command, ...
|
|
9
|
+
const [command, ...rest] = args;
|
|
5
10
|
const supported = ["teleology", "identity", "project", "skills"];
|
|
6
11
|
const supportedSet = new Set(supported);
|
|
12
|
+
const repoBase = process.env.ARCHITECTONIC_SOURCE_BASE || "https://github.com/architectonic";
|
|
13
|
+
const npmBase = process.env.ARCHITECTONIC_NPM_BASE || "";
|
|
14
|
+
const packageMap = {
|
|
15
|
+
teleology: "teleology",
|
|
16
|
+
identity: "architectonic-identity",
|
|
17
|
+
project: "architectonic-project",
|
|
18
|
+
skills: "architectonic-skills",
|
|
19
|
+
};
|
|
7
20
|
|
|
8
21
|
function printHelp() {
|
|
9
|
-
console.log(`architectonic
|
|
22
|
+
console.log(`architectonic ${VERSION}
|
|
10
23
|
|
|
11
24
|
CLI for composing the core layers of an agentic system.
|
|
12
25
|
|
|
@@ -15,6 +28,10 @@ Usage:
|
|
|
15
28
|
npx architectonic help
|
|
16
29
|
npx architectonic add <teleology|identity|project|skills>
|
|
17
30
|
npx architectonic add teleology identity skills
|
|
31
|
+
npx architectonic add skills --dir ./vendor
|
|
32
|
+
npx architectonic add teleology --source npm
|
|
33
|
+
npx architectonic list
|
|
34
|
+
npx architectonic doctor
|
|
18
35
|
|
|
19
36
|
Layers:
|
|
20
37
|
teleology purpose, principles, doctrine, governance
|
|
@@ -27,36 +44,394 @@ Run vs install:
|
|
|
27
44
|
npm install architectonic install in a project
|
|
28
45
|
npm install -g architectonic install globally
|
|
29
46
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
What add does:
|
|
48
|
+
Installs the selected layer repositories into the target directory
|
|
49
|
+
from git or npm sources and records them in architectonic.json.`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseAddArgs(tokens) {
|
|
53
|
+
const targets = [];
|
|
54
|
+
let installDir = process.cwd();
|
|
55
|
+
let source = process.env.ARCHITECTONIC_ADD_SOURCE || "git";
|
|
56
|
+
|
|
57
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
58
|
+
const token = tokens[index];
|
|
59
|
+
if (token === "--dir" || token === "--out") {
|
|
60
|
+
const next = tokens[index + 1];
|
|
61
|
+
if (!next) {
|
|
62
|
+
throw new Error(`Missing value for ${token}`);
|
|
63
|
+
}
|
|
64
|
+
installDir = path.resolve(next);
|
|
65
|
+
index += 1;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (token.startsWith("--dir=")) {
|
|
69
|
+
installDir = path.resolve(token.slice("--dir=".length));
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (token.startsWith("--out=")) {
|
|
73
|
+
installDir = path.resolve(token.slice("--out=".length));
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (token === "--source") {
|
|
77
|
+
const next = tokens[index + 1];
|
|
78
|
+
if (!next) {
|
|
79
|
+
throw new Error("Missing value for --source");
|
|
80
|
+
}
|
|
81
|
+
source = next;
|
|
82
|
+
index += 1;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (token.startsWith("--source=")) {
|
|
86
|
+
source = token.slice("--source=".length);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (token.startsWith("-")) {
|
|
90
|
+
throw new Error(`Unknown option: ${token}`);
|
|
91
|
+
}
|
|
92
|
+
targets.push(token);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { targets, installDir, source };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function ensureGitAvailable() {
|
|
99
|
+
const result = spawnSync("git", ["--version"], { encoding: "utf8" });
|
|
100
|
+
if (result.status !== 0) {
|
|
101
|
+
throw new Error("git is required on PATH for `architectonic add`.");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function ensureNpmAvailable() {
|
|
106
|
+
const result = spawnSync("npm", ["--version"], { encoding: "utf8", shell: true });
|
|
107
|
+
if (result.status !== 0) {
|
|
108
|
+
throw new Error("npm is required on PATH for `architectonic add --source npm`.");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function ensureTarAvailable() {
|
|
113
|
+
const result = spawnSync("tar", ["--version"], { encoding: "utf8" });
|
|
114
|
+
if (result.status !== 0) {
|
|
115
|
+
throw new Error("tar is required on PATH for npm package extraction.");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function repoUrlFor(target) {
|
|
120
|
+
const normalizedBase = repoBase.replace(/\\/g, "/");
|
|
121
|
+
if (/^(?:[A-Za-z]:\/|\/|\.{1,2}\/)/.test(normalizedBase)) {
|
|
122
|
+
return path.resolve(normalizedBase, target);
|
|
123
|
+
}
|
|
124
|
+
return `${normalizedBase}/${target}.git`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function packageSpecFor(target) {
|
|
128
|
+
const packageName = packageMap[target];
|
|
129
|
+
if (!packageName) {
|
|
130
|
+
throw new Error(`No npm package mapping defined for ${target}`);
|
|
131
|
+
}
|
|
132
|
+
if (!npmBase) {
|
|
133
|
+
return packageName;
|
|
134
|
+
}
|
|
135
|
+
const normalizedBase = npmBase.replace(/\\/g, "/");
|
|
136
|
+
if (/^(?:[A-Za-z]:\/|\/|\.{1,2}\/)/.test(normalizedBase)) {
|
|
137
|
+
return path.resolve(normalizedBase, target);
|
|
138
|
+
}
|
|
139
|
+
return normalizedBase.endsWith("/")
|
|
140
|
+
? `${normalizedBase}${packageName}`
|
|
141
|
+
: `${normalizedBase}/${packageName}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function targetPathFor(installDir, target) {
|
|
145
|
+
return path.join(installDir, target);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function manifestPathFor(installDir) {
|
|
149
|
+
return path.join(installDir, "architectonic.json");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function readManifest(manifestPath) {
|
|
153
|
+
if (!fs.existsSync(manifestPath)) {
|
|
154
|
+
return {
|
|
155
|
+
schema_version: 1,
|
|
156
|
+
installed_at: new Date().toISOString(),
|
|
157
|
+
layers: {},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function writeManifest(manifestPath, manifest) {
|
|
165
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function cloneLayer(target, installDir) {
|
|
169
|
+
const repoUrl = repoUrlFor(target);
|
|
170
|
+
const targetPath = targetPathFor(installDir, target);
|
|
171
|
+
|
|
172
|
+
if (fs.existsSync(targetPath)) {
|
|
173
|
+
throw new Error(`Target already exists: ${targetPath}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const clone = spawnSync("git", ["clone", repoUrl, targetPath], {
|
|
177
|
+
cwd: installDir,
|
|
178
|
+
encoding: "utf8",
|
|
179
|
+
stdio: "pipe",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
if (clone.status !== 0) {
|
|
183
|
+
const detail = (clone.stderr || clone.stdout || "").trim();
|
|
184
|
+
throw new Error(`Failed to clone ${repoUrl}${detail ? `\n${detail}` : ""}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
name: target,
|
|
189
|
+
repo: repoUrl,
|
|
190
|
+
path: targetPath,
|
|
191
|
+
source: "git",
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function packNpmLayer(target, installDir) {
|
|
196
|
+
const packageSpec = packageSpecFor(target);
|
|
197
|
+
const targetPath = targetPathFor(installDir, target);
|
|
198
|
+
const tempRoot = fs.mkdtempSync(path.join(installDir, "architectonic-npm-"));
|
|
199
|
+
const pack = spawnSync("npm", ["pack", packageSpec, "--pack-destination", tempRoot], {
|
|
200
|
+
cwd: installDir,
|
|
201
|
+
encoding: "utf8",
|
|
202
|
+
stdio: "pipe",
|
|
203
|
+
shell: true,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (pack.status !== 0) {
|
|
207
|
+
const detail = (pack.stderr || pack.stdout || "").trim();
|
|
208
|
+
throw new Error(`Failed to pack ${packageSpec}${detail ? `\n${detail}` : ""}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const tgzName = (pack.stdout || "").trim().split(/\r?\n/).filter(Boolean).pop();
|
|
212
|
+
if (!tgzName) {
|
|
213
|
+
throw new Error(`npm pack did not return a tarball name for ${packageSpec}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const tgzPath = path.join(tempRoot, tgzName);
|
|
217
|
+
if (!fs.existsSync(tgzPath)) {
|
|
218
|
+
throw new Error(`Packed tarball not found: ${tgzPath}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (fs.existsSync(targetPath)) {
|
|
222
|
+
throw new Error(`Target already exists: ${targetPath}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
226
|
+
const extract = spawnSync("tar", ["-xzf", tgzPath, "-C", targetPath], {
|
|
227
|
+
cwd: installDir,
|
|
228
|
+
encoding: "utf8",
|
|
229
|
+
stdio: "pipe",
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (extract.status !== 0) {
|
|
233
|
+
const detail = (extract.stderr || extract.stdout || "").trim();
|
|
234
|
+
throw new Error(`Failed to extract ${tgzPath}${detail ? `\n${detail}` : ""}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const extractedPackageDir = path.join(targetPath, "package");
|
|
238
|
+
if (!fs.existsSync(extractedPackageDir)) {
|
|
239
|
+
throw new Error(`Expected extracted package directory at ${extractedPackageDir}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
for (const entry of fs.readdirSync(extractedPackageDir, { withFileTypes: true })) {
|
|
243
|
+
const from = path.join(extractedPackageDir, entry.name);
|
|
244
|
+
const to = path.join(targetPath, entry.name);
|
|
245
|
+
fs.renameSync(from, to);
|
|
246
|
+
}
|
|
247
|
+
fs.rmdirSync(extractedPackageDir);
|
|
248
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
name: target,
|
|
252
|
+
repo: packageSpec,
|
|
253
|
+
path: targetPath,
|
|
254
|
+
source: "npm",
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function readInstalledPackageName(layerPath) {
|
|
259
|
+
const packageJsonPath = path.join(layerPath, "package.json");
|
|
260
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, "utf8")).name || null;
|
|
265
|
+
} catch {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
33
268
|
}
|
|
34
269
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
270
|
+
function installLayer(target, installDir, source) {
|
|
271
|
+
if (source === "git") {
|
|
272
|
+
return cloneLayer(target, installDir);
|
|
273
|
+
}
|
|
274
|
+
if (source === "npm") {
|
|
275
|
+
return packNpmLayer(target, installDir);
|
|
276
|
+
}
|
|
277
|
+
throw new Error(`Unsupported source: ${source}. Use git or npm.`);
|
|
38
278
|
}
|
|
39
279
|
|
|
40
|
-
|
|
280
|
+
function loadManifestFromDir(installDir) {
|
|
281
|
+
const manifestPath = manifestPathFor(installDir);
|
|
282
|
+
if (!fs.existsSync(manifestPath)) {
|
|
283
|
+
throw new Error(`No architectonic.json found in ${installDir}`);
|
|
284
|
+
}
|
|
285
|
+
return { manifestPath, manifest: readManifest(manifestPath) };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function addCommand(tokens) {
|
|
289
|
+
const { targets, installDir, source } = parseAddArgs(tokens);
|
|
290
|
+
|
|
41
291
|
if (!targets.length) {
|
|
42
|
-
|
|
43
|
-
process.exit(1);
|
|
292
|
+
throw new Error("Specify one or more layers: teleology, identity, project, skills");
|
|
44
293
|
}
|
|
45
294
|
|
|
46
295
|
const invalid = targets.filter((target) => !supportedSet.has(target));
|
|
47
296
|
if (invalid.length) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
297
|
+
throw new Error(`Unknown layer(s): ${invalid.join(", ")}\nSupported layers: ${supported.join(", ")}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (source === "git") {
|
|
301
|
+
ensureGitAvailable();
|
|
302
|
+
} else if (source === "npm") {
|
|
303
|
+
ensureNpmAvailable();
|
|
304
|
+
ensureTarAvailable();
|
|
305
|
+
} else {
|
|
306
|
+
throw new Error(`Unsupported source: ${source}. Use git or npm.`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
310
|
+
|
|
311
|
+
const installed = [];
|
|
312
|
+
for (const target of targets) {
|
|
313
|
+
console.log(`Adding ${target} from ${source}...`);
|
|
314
|
+
installed.push(installLayer(target, installDir, source));
|
|
51
315
|
}
|
|
52
316
|
|
|
53
|
-
|
|
317
|
+
const manifestPath = manifestPathFor(installDir);
|
|
318
|
+
const manifest = readManifest(manifestPath);
|
|
319
|
+
manifest.installed_at = new Date().toISOString();
|
|
320
|
+
manifest.last_source = source;
|
|
321
|
+
manifest.git_source_base = repoBase;
|
|
322
|
+
manifest.npm_source_base = npmBase || "registry";
|
|
323
|
+
|
|
324
|
+
for (const item of installed) {
|
|
325
|
+
manifest.layers[item.name] = {
|
|
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
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
writeManifest(manifestPath, manifest);
|
|
335
|
+
|
|
54
336
|
console.log("");
|
|
55
|
-
console.log(
|
|
56
|
-
console.log(
|
|
57
|
-
process.exit(0);
|
|
337
|
+
console.log(`Installed ${installed.map((item) => item.name).join(", ")} into ${installDir}`);
|
|
338
|
+
console.log(`Wrote ${manifestPath}`);
|
|
58
339
|
}
|
|
59
340
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
341
|
+
function listCommand(tokens) {
|
|
342
|
+
const installDir = tokens[0] ? path.resolve(tokens[0]) : process.cwd();
|
|
343
|
+
const { manifest } = loadManifestFromDir(installDir);
|
|
344
|
+
const entries = Object.entries(manifest.layers || {});
|
|
345
|
+
|
|
346
|
+
if (!entries.length) {
|
|
347
|
+
console.log("No layers installed.");
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
for (const [name, layer] of entries) {
|
|
352
|
+
console.log(`${name}`);
|
|
353
|
+
console.log(` source: ${layer.source || "unknown"}`);
|
|
354
|
+
console.log(` path: ${layer.path || "unknown"}`);
|
|
355
|
+
console.log(` ref: ${layer.ref || "unknown"}`);
|
|
356
|
+
if (layer.package_name) {
|
|
357
|
+
console.log(` pkg: ${layer.package_name}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function doctorCommand(tokens) {
|
|
363
|
+
const installDir = tokens[0] ? path.resolve(tokens[0]) : process.cwd();
|
|
364
|
+
const { manifestPath, manifest } = loadManifestFromDir(installDir);
|
|
365
|
+
const entries = Object.entries(manifest.layers || {});
|
|
366
|
+
let failures = 0;
|
|
367
|
+
|
|
368
|
+
console.log(`architectonic doctor`);
|
|
369
|
+
console.log(` root: ${installDir}`);
|
|
370
|
+
console.log(` manifest: ${manifestPath}`);
|
|
371
|
+
|
|
372
|
+
if (!entries.length) {
|
|
373
|
+
console.log(` [fail] no installed layers recorded`);
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
for (const [name, layer] of entries) {
|
|
378
|
+
const relativePath = String(layer.path || "");
|
|
379
|
+
const layerPath = path.resolve(installDir, relativePath);
|
|
380
|
+
const packageJsonPath = path.join(layerPath, "package.json");
|
|
381
|
+
const exists = fs.existsSync(layerPath);
|
|
382
|
+
const hasPackageJson = fs.existsSync(packageJsonPath);
|
|
383
|
+
const packageName = hasPackageJson ? readInstalledPackageName(layerPath) : null;
|
|
384
|
+
const expectedPackageName = packageMap[name];
|
|
385
|
+
|
|
386
|
+
if (!exists) {
|
|
387
|
+
console.log(` [fail] ${name}: missing directory ${layerPath}`);
|
|
388
|
+
failures += 1;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (!hasPackageJson) {
|
|
393
|
+
console.log(` [fail] ${name}: missing package.json`);
|
|
394
|
+
failures += 1;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (packageName && expectedPackageName && packageName !== expectedPackageName) {
|
|
399
|
+
console.log(` [fail] ${name}: expected package ${expectedPackageName}, found ${packageName}`);
|
|
400
|
+
failures += 1;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
console.log(` [ok] ${name}: ${relativePath} (${layer.source || "unknown"})`);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (failures > 0) {
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
try {
|
|
413
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
414
|
+
printHelp();
|
|
415
|
+
process.exit(0);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (command === "add") {
|
|
419
|
+
addCommand(rest);
|
|
420
|
+
process.exit(0);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (command === "list") {
|
|
424
|
+
listCommand(rest);
|
|
425
|
+
process.exit(0);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (command === "doctor") {
|
|
429
|
+
doctorCommand(rest);
|
|
430
|
+
process.exit(0);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
throw new Error(`Unknown command: ${command}\nRun \`architectonic help\` for usage.`);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
console.error(error.message);
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|