archondev 1.5.0 → 1.6.1

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
@@ -39,6 +39,7 @@ Copy governance files into any project. Works with your existing AI tools (Curso
39
39
  - **Code Review Mode** — Structured code review without changing your code
40
40
  - **Local Database** — Track atoms and learnings in SQLite (no CLI required)
41
41
  - **Memory Management** — Context handoff protocol for long sessions
42
+ - **Task Extraction Protocol** — AI confirms all items before starting, nothing gets forgotten
42
43
  - Works with any AI coding assistant
43
44
 
44
45
  ---
@@ -50,6 +51,7 @@ Copy governance files into any project. Works with your existing AI tools (Curso
50
51
  | `archon` | Interactive mode — just run and follow prompts |
51
52
  | `archon init` | Initialize in your project |
52
53
  | `archon plan <description>` | Create a work item with AI planning |
54
+ | `archon plan` | Create a work item with AI planning (extracts and confirms multi-item requests) |
53
55
  | `archon execute <atom-id>` | Execute with quality gates |
54
56
  | `archon list` | List all work items |
55
57
  | `archon show <atom-id>` | Show details |
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  listLocalAtoms
3
- } from "./chunk-EDP55FCI.js";
3
+ } from "./chunk-3AAQEUY6.js";
4
4
 
5
5
  // src/cli/list.ts
6
6
  import chalk from "chalk";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  loadAtom
3
- } from "./chunk-EDP55FCI.js";
3
+ } from "./chunk-3AAQEUY6.js";
4
4
  import {
5
5
  ArchitectureParser
6
6
  } from "./chunk-2CFO5GVH.js";
@@ -0,0 +1,342 @@
1
+ // src/cli/init.ts
2
+ import { readdir, readFile, writeFile, mkdir } from "fs/promises";
3
+ import { existsSync } from "fs";
4
+ import { join, extname } from "path";
5
+ import { execSync } from "child_process";
6
+ import chalk from "chalk";
7
+ import ora from "ora";
8
+ import readline from "readline";
9
+ function isInitialized(cwd) {
10
+ const archMdPath = join(cwd, "ARCHITECTURE.md");
11
+ const archonDir = join(cwd, ".archon");
12
+ return existsSync(archMdPath) || existsSync(archonDir);
13
+ }
14
+ var LANGUAGE_EXTENSIONS = {
15
+ ".ts": "TypeScript",
16
+ ".tsx": "TypeScript",
17
+ ".js": "JavaScript",
18
+ ".jsx": "JavaScript",
19
+ ".py": "Python",
20
+ ".go": "Go",
21
+ ".rs": "Rust",
22
+ ".java": "Java",
23
+ ".rb": "Ruby",
24
+ ".php": "PHP",
25
+ ".cs": "C#",
26
+ ".cpp": "C++",
27
+ ".c": "C",
28
+ ".swift": "Swift",
29
+ ".kt": "Kotlin",
30
+ ".vue": "Vue",
31
+ ".svelte": "Svelte"
32
+ };
33
+ var IGNORE_DIRS = /* @__PURE__ */ new Set([
34
+ "node_modules",
35
+ ".git",
36
+ "dist",
37
+ "build",
38
+ ".next",
39
+ "coverage",
40
+ "__pycache__",
41
+ ".venv",
42
+ "venv",
43
+ "target",
44
+ "vendor",
45
+ ".archon"
46
+ ]);
47
+ async function init(options = {}) {
48
+ const cwd = process.cwd();
49
+ const archMdPath = join(cwd, "ARCHITECTURE.md");
50
+ const archonDir = join(cwd, ".archon");
51
+ console.log(chalk.blue("\n\u{1F3DB}\uFE0F ArchonDev Initialization\n"));
52
+ if (existsSync(archMdPath)) {
53
+ const overwrite = await promptYesNo("ARCHITECTURE.md already exists. Overwrite?", false);
54
+ if (!overwrite) {
55
+ console.log(chalk.yellow("Initialization cancelled."));
56
+ return;
57
+ }
58
+ }
59
+ const spinner = ora("Detecting project capabilities...").start();
60
+ const capabilities = await detectCapabilities(cwd);
61
+ spinner.succeed("Project capabilities detected");
62
+ spinner.start("Scanning codebase...");
63
+ const stats = await scanRepository(cwd);
64
+ spinner.succeed(`Scanned ${stats.totalFiles} files (${formatLines(stats.totalLines)} lines)`);
65
+ if (options.git !== false && !capabilities.hasGit) {
66
+ spinner.start("Initializing git repository...");
67
+ try {
68
+ execSync("git init", { cwd, stdio: "pipe" });
69
+ spinner.succeed("Git repository initialized");
70
+ } catch {
71
+ spinner.warn("Could not initialize git");
72
+ }
73
+ }
74
+ if (!existsSync(archonDir)) {
75
+ await mkdir(archonDir, { recursive: true });
76
+ }
77
+ spinner.start("Generating ARCHITECTURE.md...");
78
+ const archContent = generateArchitectureMd(capabilities, stats, options.analyze);
79
+ await writeFile(archMdPath, archContent);
80
+ spinner.succeed("ARCHITECTURE.md created");
81
+ printSummary(capabilities, stats, options.analyze);
82
+ }
83
+ function promptYesNo(question, defaultValue) {
84
+ return new Promise((resolve) => {
85
+ const rl = readline.createInterface({
86
+ input: process.stdin,
87
+ output: process.stdout
88
+ });
89
+ const hint = defaultValue ? "(Y/n)" : "(y/N)";
90
+ rl.question(`${question} ${hint} `, (answer) => {
91
+ rl.close();
92
+ if (answer.trim() === "") {
93
+ resolve(defaultValue);
94
+ } else {
95
+ resolve(answer.toLowerCase().startsWith("y"));
96
+ }
97
+ });
98
+ });
99
+ }
100
+ async function detectCapabilities(cwd) {
101
+ const hasFile = (name) => existsSync(join(cwd, name));
102
+ const hasJsonDep = async (name) => {
103
+ try {
104
+ const pkg = JSON.parse(await readFile(join(cwd, "package.json"), "utf-8"));
105
+ return !!(pkg.dependencies?.[name] || pkg.devDependencies?.[name]);
106
+ } catch {
107
+ return false;
108
+ }
109
+ };
110
+ let packageManager = null;
111
+ if (hasFile("pnpm-lock.yaml")) packageManager = "pnpm";
112
+ else if (hasFile("yarn.lock")) packageManager = "yarn";
113
+ else if (hasFile("bun.lockb")) packageManager = "bun";
114
+ else if (hasFile("package-lock.json")) packageManager = "npm";
115
+ const hasTypeScript = hasFile("tsconfig.json") || await hasJsonDep("typescript");
116
+ const hasESLint = hasFile(".eslintrc") || hasFile(".eslintrc.js") || hasFile("eslint.config.js") || await hasJsonDep("eslint");
117
+ const hasJest = hasFile("jest.config.js") || hasFile("jest.config.ts") || await hasJsonDep("jest") || await hasJsonDep("vitest");
118
+ const hasPrettier = hasFile(".prettierrc") || hasFile(".prettierrc.js") || await hasJsonDep("prettier");
119
+ const hasGit = hasFile(".git");
120
+ return {
121
+ hasTypeScript,
122
+ hasESLint,
123
+ hasJest,
124
+ hasPrettier,
125
+ hasGit,
126
+ packageManager,
127
+ primaryLanguage: hasTypeScript ? "TypeScript" : "JavaScript"
128
+ };
129
+ }
130
+ async function scanRepository(cwd) {
131
+ const stats = {
132
+ totalFiles: 0,
133
+ totalLines: 0,
134
+ languageBreakdown: {},
135
+ directories: []
136
+ };
137
+ async function scanDir(dir, depth = 0) {
138
+ if (depth > 10) return;
139
+ try {
140
+ const entries = await readdir(dir, { withFileTypes: true });
141
+ for (const entry of entries) {
142
+ if (IGNORE_DIRS.has(entry.name)) continue;
143
+ if (entry.name.startsWith(".") && entry.name !== ".github") continue;
144
+ const fullPath = join(dir, entry.name);
145
+ if (entry.isDirectory()) {
146
+ if (depth < 2) {
147
+ const relativePath = fullPath.replace(cwd, "").replace(/^\//, "");
148
+ if (relativePath) {
149
+ stats.directories.push(relativePath);
150
+ }
151
+ }
152
+ await scanDir(fullPath, depth + 1);
153
+ } else if (entry.isFile()) {
154
+ const ext = extname(entry.name).toLowerCase();
155
+ const language = LANGUAGE_EXTENSIONS[ext];
156
+ if (language) {
157
+ stats.totalFiles++;
158
+ try {
159
+ const content = await readFile(fullPath, "utf-8");
160
+ const lines = content.split("\n").length;
161
+ stats.totalLines += lines;
162
+ if (!stats.languageBreakdown[language]) {
163
+ stats.languageBreakdown[language] = { files: 0, lines: 0 };
164
+ }
165
+ stats.languageBreakdown[language].files++;
166
+ stats.languageBreakdown[language].lines += lines;
167
+ } catch {
168
+ }
169
+ }
170
+ }
171
+ }
172
+ } catch {
173
+ }
174
+ }
175
+ await scanDir(cwd);
176
+ return stats;
177
+ }
178
+ function generateArchitectureMd(capabilities, stats, analyze) {
179
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
180
+ const suggestedComponents = stats.directories.filter((d) => !d.includes("/")).slice(0, 10).map((dir) => ({
181
+ id: dir.replace(/[^a-zA-Z0-9]/g, "-"),
182
+ name: dir.charAt(0).toUpperCase() + dir.slice(1),
183
+ path: `${dir}/**`
184
+ }));
185
+ const qualityGates = [];
186
+ if (capabilities.hasTypeScript) qualityGates.push("SYNTAX");
187
+ if (capabilities.hasESLint) qualityGates.push("LINT");
188
+ if (capabilities.hasJest) qualityGates.push("UNIT");
189
+ return `---
190
+ version: "1.0"
191
+ updatedAt: "${now}"
192
+ profile: "balanced"
193
+ strictMode: true
194
+
195
+ # System Goals
196
+ systemGoals:
197
+ - id: "GOAL-001"
198
+ title: "Maintain code quality"
199
+ description: "All changes must pass quality gates before merging"
200
+ priority: "HIGH"
201
+
202
+ - id: "GOAL-002"
203
+ title: "Preserve architectural boundaries"
204
+ description: "Components should not violate defined boundaries"
205
+ priority: "HIGH"
206
+
207
+ # Components & Boundaries
208
+ # Detected ${suggestedComponents.length} top-level directories
209
+ components:
210
+ ${suggestedComponents.map((c) => ` - id: "${c.id}"
211
+ name: "${c.name}"
212
+ paths:
213
+ - "${c.path}"
214
+ boundary: "INTERNAL"
215
+ stability: "EVOLVING"`).join("\n\n")}
216
+
217
+ # Invariants (Rules to enforce)
218
+ invariants:
219
+ - id: "INV-001"
220
+ severity: "WARN"
221
+ rule: "Avoid console.log in production code"
222
+ match: "console\\\\.log"
223
+ scope: "src"
224
+ reason: "Use proper logging library instead"
225
+
226
+ # Protected Paths
227
+ protectedPaths:
228
+ - pattern: "package.json"
229
+ level: "SOFT"
230
+ reason: "Dependency changes should be reviewed"
231
+
232
+ ${capabilities.hasTypeScript ? ` - pattern: "tsconfig.json"
233
+ level: "SOFT"
234
+ reason: "TypeScript config affects entire project"` : ""}
235
+
236
+ # Environment Configuration
237
+ environments:
238
+ development:
239
+ autoApprove: true
240
+ qualityGates:
241
+ - ARCHITECTURE
242
+ ${qualityGates.map((g) => ` - ${g}`).join("\n")}
243
+
244
+ production:
245
+ autoApprove: false
246
+ requiresManualPromotion: true
247
+ qualityGates:
248
+ - ARCHITECTURE
249
+ ${qualityGates.map((g) => ` - ${g}`).join("\n")}
250
+ - ACCEPTANCE
251
+ ---
252
+
253
+ # Project Architecture
254
+
255
+ ## Overview
256
+
257
+ This architecture document was auto-generated by ArchonDev on ${now}.
258
+
259
+ ${analyze ? `## Codebase Analysis
260
+
261
+ - **Total Files:** ${stats.totalFiles}
262
+ - **Total Lines:** ${formatLines(stats.totalLines)}
263
+ - **Primary Language:** ${capabilities.primaryLanguage}
264
+
265
+ ### Language Breakdown
266
+
267
+ ${Object.entries(stats.languageBreakdown).sort((a, b) => b[1].lines - a[1].lines).map(([lang, data]) => `- ${lang}: ${data.files} files, ${formatLines(data.lines)} lines`).join("\n")}
268
+
269
+ ### Detected Capabilities
270
+
271
+ - TypeScript: ${capabilities.hasTypeScript ? "\u2705" : "\u274C"}
272
+ - ESLint: ${capabilities.hasESLint ? "\u2705" : "\u274C"}
273
+ - Testing: ${capabilities.hasJest ? "\u2705" : "\u274C"}
274
+ - Package Manager: ${capabilities.packageManager ?? "Not detected"}
275
+ ` : ""}
276
+ ## Components
277
+
278
+ ${suggestedComponents.map((c) => `### ${c.name}
279
+
280
+ Path: \`${c.path}\`
281
+
282
+ TODO: Add description for this component.
283
+ `).join("\n")}
284
+
285
+ ## Getting Started
286
+
287
+ 1. Review and customize the components above
288
+ 2. Add invariants for your specific rules
289
+ 3. Define protected paths for sensitive files
290
+ 4. Run \`archon plan <description>\` to create your first atom
291
+
292
+ ## Architecture Decision Records
293
+
294
+ Document important architectural decisions here.
295
+ `;
296
+ }
297
+ function formatLines(n) {
298
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
299
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
300
+ return n.toString();
301
+ }
302
+ function printSummary(capabilities, stats, analyze) {
303
+ console.log();
304
+ console.log(chalk.green("\u2713 ArchonDev initialized successfully!"));
305
+ console.log();
306
+ console.log(chalk.bold("Detected Capabilities:"));
307
+ console.log(` ${capabilities.hasTypeScript ? chalk.green("\u2713") : chalk.gray("\u25CB")} TypeScript`);
308
+ console.log(` ${capabilities.hasESLint ? chalk.green("\u2713") : chalk.gray("\u25CB")} ESLint`);
309
+ console.log(` ${capabilities.hasJest ? chalk.green("\u2713") : chalk.gray("\u25CB")} Testing (Jest/Vitest)`);
310
+ console.log(` ${capabilities.packageManager ? chalk.green("\u2713") : chalk.gray("\u25CB")} Package Manager: ${capabilities.packageManager ?? "none"}`);
311
+ console.log();
312
+ console.log(chalk.bold("Codebase Summary:"));
313
+ console.log(` Files: ${stats.totalFiles}`);
314
+ console.log(` Lines: ${formatLines(stats.totalLines)}`);
315
+ console.log(` Directories: ${stats.directories.length}`);
316
+ console.log();
317
+ if (Object.keys(stats.languageBreakdown).length > 0) {
318
+ console.log(chalk.bold("Languages:"));
319
+ Object.entries(stats.languageBreakdown).sort((a, b) => b[1].lines - a[1].lines).slice(0, 5).forEach(([lang, data]) => {
320
+ const pct = Math.round(data.lines / stats.totalLines * 100);
321
+ console.log(` ${lang}: ${pct}%`);
322
+ });
323
+ console.log();
324
+ }
325
+ if (analyze && stats.directories.length > 0) {
326
+ console.log(chalk.bold("Suggested Boundaries:"));
327
+ stats.directories.filter((d) => !d.includes("/")).slice(0, 5).forEach((dir) => {
328
+ console.log(` \u2022 ${dir}/`);
329
+ });
330
+ console.log();
331
+ }
332
+ console.log(chalk.bold("Next Steps:"));
333
+ console.log(` 1. ${chalk.cyan("Review")} ARCHITECTURE.md and customize components`);
334
+ console.log(` 2. ${chalk.cyan("Run")} ${chalk.dim('archon plan "your first task"')} to create an atom`);
335
+ console.log(` 3. ${chalk.cyan("Execute")} ${chalk.dim("archon execute ATOM-001")} to implement it`);
336
+ console.log();
337
+ }
338
+
339
+ export {
340
+ isInitialized,
341
+ init
342
+ };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  execute
3
- } from "./chunk-74EEFKVP.js";
4
- import "./chunk-EDP55FCI.js";
3
+ } from "./chunk-S7UXMYWS.js";
4
+ import "./chunk-3AAQEUY6.js";
5
+ import "./chunk-SMR7JQK6.js";
5
6
  import "./chunk-2CFO5GVH.js";
6
7
  import "./chunk-MOZHC2GX.js";
7
8
  import "./chunk-A7QU6JC6.js";
8
- import "./chunk-SMR7JQK6.js";
9
9
  import "./chunk-WCCBJSNI.js";
10
10
  import "./chunk-QGM4M3NI.js";
11
11
  export {