architectonic 0.0.2 → 0.0.3

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 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 are intentionally minimal. Their job is to reserve the
15
- package name and establish the top-level command surface for future install,
16
- cascade, and upkeep workflows.
14
+ The initial placeholder releases were intentionally minimal. Starting in
15
+ `0.0.3`, `architectonic add` becomes real: it clones the selected layer
16
+ repositories into a target directory and records them in `architectonic.json`.
17
17
 
18
18
  ## Command shape
19
19
 
@@ -35,16 +35,32 @@ architectonic update
35
35
 
36
36
  ## Current behavior
37
37
 
38
- For now, the CLI only exposes:
38
+ The primary implemented command is:
39
39
 
40
40
  ```text
41
- npx architectonic
42
- npx architectonic help
43
- npx architectonic add <layer>
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
44
47
  ```
45
48
 
46
- The `add` surface is reserved, but the installer logic is not implemented in
47
- `0.0.2` yet.
49
+ `add` clones from the Architectonic GitHub organization into the current
50
+ directory by default:
51
+
52
+ ```text
53
+ ./teleology
54
+ ./identity
55
+ ./project
56
+ ./skills
57
+ ./architectonic.json
58
+ ```
59
+
60
+ `architectonic.json` records what was installed and where it landed.
61
+
62
+ If a target directory already exists, the command stops instead of silently
63
+ overwriting it.
48
64
 
49
65
  ## Run vs install
50
66
 
@@ -1,12 +1,18 @@
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.3";
3
8
  const args = process.argv.slice(2);
4
- const [command, ...targets] = args;
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";
7
13
 
8
14
  function printHelp() {
9
- console.log(`architectonic 0.0.2
15
+ console.log(`architectonic ${VERSION}
10
16
 
11
17
  CLI for composing the core layers of an agentic system.
12
18
 
@@ -15,6 +21,7 @@ Usage:
15
21
  npx architectonic help
16
22
  npx architectonic add <teleology|identity|project|skills>
17
23
  npx architectonic add teleology identity skills
24
+ npx architectonic add skills --dir ./vendor
18
25
 
19
26
  Layers:
20
27
  teleology purpose, principles, doctrine, governance
@@ -27,36 +34,158 @@ Run vs install:
27
34
  npm install architectonic install in a project
28
35
  npm install -g architectonic install globally
29
36
 
30
- Status:
31
- 0.0.2 reserves the public CLI name and establishes the command surface.
32
- The add subcommands are placeholders in this initial release.`);
37
+ What add does:
38
+ Clones the selected layer repositories into the target directory
39
+ and records them in architectonic.json.`);
40
+ }
41
+
42
+ function parseAddArgs(tokens) {
43
+ const targets = [];
44
+ let installDir = process.cwd();
45
+
46
+ for (let index = 0; index < tokens.length; index += 1) {
47
+ const token = tokens[index];
48
+ if (token === "--dir" || token === "--out") {
49
+ const next = tokens[index + 1];
50
+ if (!next) {
51
+ throw new Error(`Missing value for ${token}`);
52
+ }
53
+ installDir = path.resolve(next);
54
+ index += 1;
55
+ continue;
56
+ }
57
+ if (token.startsWith("--dir=")) {
58
+ installDir = path.resolve(token.slice("--dir=".length));
59
+ continue;
60
+ }
61
+ if (token.startsWith("--out=")) {
62
+ installDir = path.resolve(token.slice("--out=".length));
63
+ continue;
64
+ }
65
+ if (token.startsWith("-")) {
66
+ throw new Error(`Unknown option: ${token}`);
67
+ }
68
+ targets.push(token);
69
+ }
70
+
71
+ return { targets, installDir };
72
+ }
73
+
74
+ function ensureGitAvailable() {
75
+ const result = spawnSync("git", ["--version"], { encoding: "utf8" });
76
+ if (result.status !== 0) {
77
+ throw new Error("git is required on PATH for `architectonic add`.");
78
+ }
79
+ }
80
+
81
+ function repoUrlFor(target) {
82
+ const normalizedBase = repoBase.replace(/\\/g, "/");
83
+ if (/^(?:[A-Za-z]:\/|\/|\.{1,2}\/)/.test(normalizedBase)) {
84
+ return path.resolve(normalizedBase, target);
85
+ }
86
+ return `${normalizedBase}/${target}.git`;
87
+ }
88
+
89
+ function targetPathFor(installDir, target) {
90
+ return path.join(installDir, target);
91
+ }
92
+
93
+ function readManifest(manifestPath) {
94
+ if (!fs.existsSync(manifestPath)) {
95
+ return {
96
+ schema_version: 1,
97
+ installed_at: new Date().toISOString(),
98
+ layers: {},
99
+ };
100
+ }
101
+
102
+ return JSON.parse(fs.readFileSync(manifestPath, "utf8"));
33
103
  }
34
104
 
35
- if (!command || command === "help" || command === "--help" || command === "-h") {
36
- printHelp();
37
- process.exit(0);
105
+ function writeManifest(manifestPath, manifest) {
106
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
38
107
  }
39
108
 
40
- if (command === "add") {
109
+ function cloneLayer(target, installDir) {
110
+ const repoUrl = repoUrlFor(target);
111
+ const targetPath = targetPathFor(installDir, target);
112
+
113
+ if (fs.existsSync(targetPath)) {
114
+ throw new Error(`Target already exists: ${targetPath}`);
115
+ }
116
+
117
+ const clone = spawnSync("git", ["clone", repoUrl, targetPath], {
118
+ cwd: installDir,
119
+ encoding: "utf8",
120
+ stdio: "pipe",
121
+ });
122
+
123
+ if (clone.status !== 0) {
124
+ const detail = (clone.stderr || clone.stdout || "").trim();
125
+ throw new Error(`Failed to clone ${repoUrl}${detail ? `\n${detail}` : ""}`);
126
+ }
127
+
128
+ return {
129
+ name: target,
130
+ repo: repoUrl,
131
+ path: targetPath,
132
+ };
133
+ }
134
+
135
+ function addCommand(tokens) {
136
+ const { targets, installDir } = parseAddArgs(tokens);
137
+
41
138
  if (!targets.length) {
42
- console.error("Specify one or more layers: teleology, identity, project, skills");
43
- process.exit(1);
139
+ throw new Error("Specify one or more layers: teleology, identity, project, skills");
44
140
  }
45
141
 
46
142
  const invalid = targets.filter((target) => !supportedSet.has(target));
47
143
  if (invalid.length) {
48
- console.error(`Unknown layer(s): ${invalid.join(", ")}`);
49
- console.error(`Supported layers: ${supported.join(", ")}`);
50
- process.exit(1);
144
+ throw new Error(`Unknown layer(s): ${invalid.join(", ")}\nSupported layers: ${supported.join(", ")}`);
145
+ }
146
+
147
+ ensureGitAvailable();
148
+ fs.mkdirSync(installDir, { recursive: true });
149
+
150
+ const installed = [];
151
+ for (const target of targets) {
152
+ console.log(`Adding ${target}...`);
153
+ installed.push(cloneLayer(target, installDir));
51
154
  }
52
155
 
53
- console.log(`architectonic add ${targets.join(" ")}`);
156
+ const manifestPath = path.join(installDir, "architectonic.json");
157
+ const manifest = readManifest(manifestPath);
158
+ manifest.installed_at = new Date().toISOString();
159
+ manifest.source_base = repoBase;
160
+
161
+ for (const item of installed) {
162
+ manifest.layers[item.name] = {
163
+ repo: item.repo,
164
+ path: `./${path.relative(installDir, item.path).replace(/\\/g, "/")}`,
165
+ installed_at: manifest.installed_at,
166
+ };
167
+ }
168
+
169
+ writeManifest(manifestPath, manifest);
170
+
54
171
  console.log("");
55
- console.log("This placeholder release does not install packages yet.");
56
- console.log("It reserves the command contract for future cascade/install behavior.");
57
- process.exit(0);
172
+ console.log(`Installed ${installed.map((item) => item.name).join(", ")} into ${installDir}`);
173
+ console.log(`Wrote ${manifestPath}`);
58
174
  }
59
175
 
60
- console.error(`Unknown command: ${command}`);
61
- console.error("Run `architectonic help` for usage.");
62
- process.exit(1);
176
+ try {
177
+ if (!command || command === "help" || command === "--help" || command === "-h") {
178
+ printHelp();
179
+ process.exit(0);
180
+ }
181
+
182
+ if (command === "add") {
183
+ addCommand(rest);
184
+ process.exit(0);
185
+ }
186
+
187
+ throw new Error(`Unknown command: ${command}\nRun \`architectonic help\` for usage.`);
188
+ } catch (error) {
189
+ console.error(error.message);
190
+ process.exit(1);
191
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "architectonic",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "CLI for composing agentic system layers such as teleology, identity, project, and skills.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",