architectonic 0.0.5 → 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 +20 -0
- package/bin/architectonic.js +264 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ The initial placeholder releases were intentionally minimal. Starting in
|
|
|
16
16
|
local manifest, repair light manifest drift, update safely, and remove layers
|
|
17
17
|
without trampling local forks.
|
|
18
18
|
|
|
19
|
+
Starting in `0.0.6`, it can also inspect drift with `status` and `diff`.
|
|
20
|
+
|
|
19
21
|
## Command shape
|
|
20
22
|
|
|
21
23
|
The primary interaction model is:
|
|
@@ -28,6 +30,8 @@ architectonic add skills
|
|
|
28
30
|
architectonic add teleology identity skills
|
|
29
31
|
architectonic init
|
|
30
32
|
architectonic doctor
|
|
33
|
+
architectonic status
|
|
34
|
+
architectonic diff
|
|
31
35
|
architectonic list
|
|
32
36
|
architectonic update
|
|
33
37
|
architectonic remove
|
|
@@ -53,6 +57,8 @@ npx architectonic init --preset company
|
|
|
53
57
|
npx architectonic list
|
|
54
58
|
npx architectonic doctor
|
|
55
59
|
npx architectonic doctor --fix
|
|
60
|
+
npx architectonic status
|
|
61
|
+
npx architectonic diff teleology
|
|
56
62
|
npx architectonic update
|
|
57
63
|
npx architectonic update --dry-run
|
|
58
64
|
npx architectonic remove skills
|
|
@@ -99,6 +105,20 @@ ARCHITECTONIC_ADD_SOURCE # change the default source mode
|
|
|
99
105
|
package metadata matches the expected layer. `doctor --fix` repairs light
|
|
100
106
|
manifest drift such as stale package names or recoverable default paths.
|
|
101
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
|
+
|
|
102
122
|
`init` creates a workspace root, installs a preset, and seeds a top-level
|
|
103
123
|
`README.md` and `AGENTS.md`.
|
|
104
124
|
|
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"];
|
|
@@ -34,6 +34,8 @@ Usage:
|
|
|
34
34
|
npx architectonic list
|
|
35
35
|
npx architectonic doctor
|
|
36
36
|
npx architectonic doctor --fix
|
|
37
|
+
npx architectonic status
|
|
38
|
+
npx architectonic diff <layer>
|
|
37
39
|
npx architectonic update
|
|
38
40
|
npx architectonic remove <layer>
|
|
39
41
|
|
|
@@ -50,7 +52,10 @@ Run vs install:
|
|
|
50
52
|
|
|
51
53
|
What add does:
|
|
52
54
|
Installs the selected layer repositories into the target directory
|
|
53
|
-
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.`);
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
function printUsageError(message) {
|
|
@@ -209,6 +214,56 @@ function parseUpdateArgs(tokens) {
|
|
|
209
214
|
return { installDir, dryRun };
|
|
210
215
|
}
|
|
211
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
|
+
|
|
212
267
|
function parseRemoveArgs(tokens) {
|
|
213
268
|
let installDir = process.cwd();
|
|
214
269
|
let force = false;
|
|
@@ -497,6 +552,95 @@ function updateManifestLayerRecord(manifest, installDir, layerName, absoluteLaye
|
|
|
497
552
|
};
|
|
498
553
|
}
|
|
499
554
|
|
|
555
|
+
function getGitHead(layerPath) {
|
|
556
|
+
const result = gitResult(["rev-parse", "HEAD"], layerPath);
|
|
557
|
+
if (result.status !== 0) {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
return (result.stdout || "").trim() || null;
|
|
561
|
+
}
|
|
562
|
+
|
|
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
|
+
}
|
|
570
|
+
|
|
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;
|
|
575
|
+
}
|
|
576
|
+
return (result.stdout || "").trim() || null;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function getGitAheadBehind(layerPath) {
|
|
580
|
+
const upstream = getGitUpstream(layerPath);
|
|
581
|
+
if (!upstream) {
|
|
582
|
+
return null;
|
|
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
|
+
}
|
|
595
|
+
|
|
596
|
+
function getGitDiffSummary(layerPath) {
|
|
597
|
+
const result = gitResult(["status", "--short"], layerPath);
|
|
598
|
+
if (result.status !== 0) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
const lines = (result.stdout || "").split(/\r?\n/).filter(Boolean);
|
|
602
|
+
return {
|
|
603
|
+
dirty: lines.length > 0,
|
|
604
|
+
lines,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
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
|
+
|
|
500
644
|
function installTargets(targets, installDir, source) {
|
|
501
645
|
ensureInstallPrereqs(source);
|
|
502
646
|
fs.mkdirSync(installDir, { recursive: true });
|
|
@@ -626,6 +770,54 @@ function listCommand(tokens) {
|
|
|
626
770
|
}
|
|
627
771
|
}
|
|
628
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
|
+
|
|
629
821
|
function inferLayerPath(installDir, layerName) {
|
|
630
822
|
const candidate = targetPathFor(installDir, layerName);
|
|
631
823
|
return pathExists(candidate) ? candidate : null;
|
|
@@ -837,6 +1029,66 @@ function removeCommand(tokens) {
|
|
|
837
1029
|
console.log(`Updated ${manifestPath}`);
|
|
838
1030
|
}
|
|
839
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
|
+
|
|
840
1092
|
try {
|
|
841
1093
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
842
1094
|
printHelp();
|
|
@@ -863,6 +1115,16 @@ try {
|
|
|
863
1115
|
process.exit(0);
|
|
864
1116
|
}
|
|
865
1117
|
|
|
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
|
+
|
|
866
1128
|
if (command === "update") {
|
|
867
1129
|
updateCommand(rest);
|
|
868
1130
|
process.exit(0);
|