archondev 1.2.0 → 1.5.0
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 +27 -5
- package/dist/{chunk-I4ZVNLNO.js → chunk-74EEFKVP.js} +211 -26
- package/dist/{chunk-UG2ZZ7CM.js → chunk-K2VBICW3.js} +61 -40
- package/dist/{execute-LYID2ODD.js → execute-N6CCEJ5I.js} +1 -1
- package/dist/index.js +250 -9
- package/dist/{preferences-PL2ON5VY.js → preferences-QEFXVCZN.js} +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -9,10 +9,10 @@ The complete AI development system. It manages your entire development process s
|
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
# macOS/Linux:
|
|
12
|
-
npm install -g
|
|
12
|
+
npm install -g archon && archon
|
|
13
13
|
|
|
14
14
|
# Windows PowerShell:
|
|
15
|
-
npm install -g
|
|
15
|
+
npm install -g archon; archon
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
**What you get:**
|
|
@@ -20,6 +20,7 @@ npm install -g archondev; archon
|
|
|
20
20
|
- Adversarial planning (Architect proposes, Sentinel critiques)
|
|
21
21
|
- Automatic quality gates before changes are applied
|
|
22
22
|
- Learning persistence — mistakes are remembered and avoided
|
|
23
|
+
- **Dependency tracking** — prevent regressions with "what-breaks-what" map
|
|
23
24
|
- Bug reporting with root cause analysis
|
|
24
25
|
- AI-powered code review for any codebase
|
|
25
26
|
- Multi-provider key support with adversarial features
|
|
@@ -33,6 +34,11 @@ Copy governance files into any project. Works with your existing AI tools (Curso
|
|
|
33
34
|
- ARCHITECTURE.md template with best practices
|
|
34
35
|
- IDE-specific rule files (.cursorrules, CLAUDE.md, etc.)
|
|
35
36
|
- Progress tracking templates
|
|
37
|
+
- **DEPENDENCIES.md** — Track file-level dependencies to prevent regressions
|
|
38
|
+
- **First-Run Walkthrough** — Guided onboarding when AI detects your governance files
|
|
39
|
+
- **Code Review Mode** — Structured code review without changing your code
|
|
40
|
+
- **Local Database** — Track atoms and learnings in SQLite (no CLI required)
|
|
41
|
+
- **Memory Management** — Context handoff protocol for long sessions
|
|
36
42
|
- Works with any AI coding assistant
|
|
37
43
|
|
|
38
44
|
---
|
|
@@ -57,19 +63,34 @@ Copy governance files into any project. Works with your existing AI tools (Curso
|
|
|
57
63
|
| `archon credits budget` | Set monthly budget and alerts |
|
|
58
64
|
| `archon keys add <provider>` | Add your own API key — BYOK (Bring Your Own Key) |
|
|
59
65
|
| `archon keys list` | Show configured API keys by provider |
|
|
60
|
-
| `archon preferences` |
|
|
66
|
+
| `archon preferences` | Interactive settings menu (billing, models, keys, usage) |
|
|
61
67
|
| `archon models` | List available AI models |
|
|
68
|
+
| `archon deps list` | View file dependency rules |
|
|
69
|
+
| `archon deps add` | Add a new dependency rule |
|
|
70
|
+
| `archon deps check --files <list>` | Check for downstream impacts |
|
|
71
|
+
| `archon deps graph` | Generate Mermaid dependency diagram |
|
|
62
72
|
|
|
63
73
|
## Pricing
|
|
64
74
|
|
|
65
75
|
| Tier | Cost | What You Get |
|
|
66
76
|
|------|------|--------------|
|
|
67
|
-
| **Free** | $0 |
|
|
77
|
+
| **Free** | $0 | Ultra-cheap models (GPT-5-nano, GPT-4.1-nano, Gemini 2.5 Flash-Lite) — $0 cost to us |
|
|
68
78
|
| **Credits** | Pay as you go | All models, 10% service fee, deposit any amount |
|
|
69
79
|
| **BYOK** (Bring Your Own Key) | $0 | Use your own API keys, unlimited usage |
|
|
80
|
+
| **Claude Subscription** *(coming soon)* | Your existing sub | Use Claude Pro/Max subscription instead of API keys |
|
|
70
81
|
|
|
71
82
|
No subscriptions. No commitments. Start free.
|
|
72
83
|
|
|
84
|
+
### Claude Pro/Max Subscription Support (Coming Soon)
|
|
85
|
+
|
|
86
|
+
If you have an existing Claude subscription, you'll soon be able to use it with ArchonDev instead of API keys:
|
|
87
|
+
|
|
88
|
+
- **Claude Pro** ($20/mo) — Use your Pro subscription
|
|
89
|
+
- **Claude Max 5x** ($100/mo) — Higher limits for power users
|
|
90
|
+
- **Claude Max 20x** ($200/mo) — Maximum throughput
|
|
91
|
+
|
|
92
|
+
This works similarly to how Claude Code allows subscription-based access. We're currently waiting on Anthropic to provide a public OAuth API for third-party applications. **For now, use BYOK (bring your own API key)** — we'll notify you when subscription support is available.
|
|
93
|
+
|
|
73
94
|
## How It Works
|
|
74
95
|
|
|
75
96
|
1. **Define Your Rules** — Create ARCHITECTURE.md with boundaries and invariants
|
|
@@ -101,7 +122,8 @@ The CLI detects existing projects and suggests this workflow automatically.
|
|
|
101
122
|
|
|
102
123
|
## Documentation
|
|
103
124
|
|
|
104
|
-
[archondev.io](https://archondev.io)
|
|
125
|
+
- [archondev.io](https://archondev.io) — Main website with animated AI problems story
|
|
126
|
+
- [AI Coding Problems Research](docs/ai-coding-problems-research.md) — Market research on AI coding assistant issues
|
|
105
127
|
|
|
106
128
|
---
|
|
107
129
|
|
|
@@ -2842,7 +2842,7 @@ var require_js_yaml2 = __commonJS({
|
|
|
2842
2842
|
|
|
2843
2843
|
// src/cli/execute.ts
|
|
2844
2844
|
import chalk2 from "chalk";
|
|
2845
|
-
import { existsSync as
|
|
2845
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2846
2846
|
import { readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
|
|
2847
2847
|
import { join as join3 } from "path";
|
|
2848
2848
|
import { execSync as execSync3 } from "child_process";
|
|
@@ -2850,7 +2850,157 @@ import { createInterface } from "readline";
|
|
|
2850
2850
|
|
|
2851
2851
|
// src/core/conflicts/checker.ts
|
|
2852
2852
|
import { stat } from "fs/promises";
|
|
2853
|
+
import { existsSync as existsSync2 } from "fs";
|
|
2854
|
+
|
|
2855
|
+
// src/core/dependencies/parser.ts
|
|
2856
|
+
import { readFile } from "fs/promises";
|
|
2853
2857
|
import { existsSync } from "fs";
|
|
2858
|
+
import matter from "gray-matter";
|
|
2859
|
+
var DEFAULT_DEPENDENCIES_FILENAME = "DEPENDENCIES.md";
|
|
2860
|
+
var DependencyParser = class {
|
|
2861
|
+
cwd;
|
|
2862
|
+
filePath;
|
|
2863
|
+
constructor(cwd = process.cwd(), filename = DEFAULT_DEPENDENCIES_FILENAME) {
|
|
2864
|
+
this.cwd = cwd;
|
|
2865
|
+
this.filePath = `${cwd}/${filename}`;
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* Check if DEPENDENCIES.md exists
|
|
2869
|
+
*/
|
|
2870
|
+
exists() {
|
|
2871
|
+
return existsSync(this.filePath);
|
|
2872
|
+
}
|
|
2873
|
+
/**
|
|
2874
|
+
* Parse DEPENDENCIES.md and return the document
|
|
2875
|
+
*/
|
|
2876
|
+
async parse() {
|
|
2877
|
+
if (!this.exists()) {
|
|
2878
|
+
return {
|
|
2879
|
+
success: true,
|
|
2880
|
+
document: {
|
|
2881
|
+
version: "1.0",
|
|
2882
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "",
|
|
2883
|
+
rules: []
|
|
2884
|
+
}
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
try {
|
|
2888
|
+
const content = await readFile(this.filePath, "utf-8");
|
|
2889
|
+
const { data } = matter(content);
|
|
2890
|
+
const document = this.validateAndNormalize(data);
|
|
2891
|
+
return { success: true, document };
|
|
2892
|
+
} catch (error) {
|
|
2893
|
+
return {
|
|
2894
|
+
success: false,
|
|
2895
|
+
error: error instanceof Error ? error.message : "Unknown parse error"
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Validate and normalize parsed YAML data
|
|
2901
|
+
*/
|
|
2902
|
+
validateAndNormalize(data) {
|
|
2903
|
+
const raw = data;
|
|
2904
|
+
const version = typeof raw["version"] === "string" ? raw["version"] : "1.0";
|
|
2905
|
+
const updatedAt = typeof raw["updatedAt"] === "string" ? raw["updatedAt"] : (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "";
|
|
2906
|
+
const rawRules = Array.isArray(raw["rules"]) ? raw["rules"] : [];
|
|
2907
|
+
const rules = [];
|
|
2908
|
+
for (const rawRule of rawRules) {
|
|
2909
|
+
if (typeof rawRule !== "object" || rawRule === null) continue;
|
|
2910
|
+
const r = rawRule;
|
|
2911
|
+
const id = typeof r["id"] === "string" ? r["id"] : `DEP-${rules.length + 1}`;
|
|
2912
|
+
const source = typeof r["source"] === "string" ? r["source"] : "";
|
|
2913
|
+
const dependents = Array.isArray(r["dependents"]) ? r["dependents"].filter((d) => typeof d === "string") : [];
|
|
2914
|
+
const severity = this.parseSeverity(r["severity"]);
|
|
2915
|
+
const reason = typeof r["reason"] === "string" ? r["reason"] : "";
|
|
2916
|
+
const symbols = Array.isArray(r["symbols"]) ? r["symbols"].filter((s) => typeof s === "string") : void 0;
|
|
2917
|
+
const mustTest = Array.isArray(r["mustTest"]) ? r["mustTest"].filter((t) => typeof t === "string") : void 0;
|
|
2918
|
+
if (source && dependents.length > 0) {
|
|
2919
|
+
rules.push({ id, source, dependents, severity, reason, symbols, mustTest });
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
return { version, updatedAt, rules };
|
|
2923
|
+
}
|
|
2924
|
+
parseSeverity(value) {
|
|
2925
|
+
if (typeof value === "string") {
|
|
2926
|
+
const upper = value.toUpperCase();
|
|
2927
|
+
if (upper === "BLOCKER" || upper === "WARNING" || upper === "INFO") {
|
|
2928
|
+
return upper;
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
return "WARNING";
|
|
2932
|
+
}
|
|
2933
|
+
/**
|
|
2934
|
+
* Check which dependency rules are triggered by a set of files
|
|
2935
|
+
*/
|
|
2936
|
+
async checkFiles(files) {
|
|
2937
|
+
const parseResult = await this.parse();
|
|
2938
|
+
if (!parseResult.success || !parseResult.document) {
|
|
2939
|
+
return {
|
|
2940
|
+
filesChecked: files,
|
|
2941
|
+
impacts: [],
|
|
2942
|
+
totalImpacts: 0,
|
|
2943
|
+
hasBlockers: false
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2946
|
+
const impacts = [];
|
|
2947
|
+
for (const file of files) {
|
|
2948
|
+
for (const rule of parseResult.document.rules) {
|
|
2949
|
+
if (this.matchesPattern(file, rule.source)) {
|
|
2950
|
+
impacts.push({
|
|
2951
|
+
rule,
|
|
2952
|
+
matchedSource: file,
|
|
2953
|
+
affectedDependents: rule.dependents
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
return {
|
|
2959
|
+
filesChecked: files,
|
|
2960
|
+
impacts,
|
|
2961
|
+
totalImpacts: impacts.reduce((sum, i) => sum + i.affectedDependents.length, 0),
|
|
2962
|
+
hasBlockers: impacts.some((i) => i.rule.severity === "BLOCKER")
|
|
2963
|
+
};
|
|
2964
|
+
}
|
|
2965
|
+
/**
|
|
2966
|
+
* Match a file path against a glob pattern
|
|
2967
|
+
* Reuses the same logic as ConflictChecker for consistency
|
|
2968
|
+
*/
|
|
2969
|
+
matchesPattern(path, pattern) {
|
|
2970
|
+
const regexPattern = pattern.replace(/\*\*/g, "<<<GLOBSTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<GLOBSTAR>>>/g, ".*").replace(/\//g, "\\/");
|
|
2971
|
+
return new RegExp(`^${regexPattern}$`).test(path);
|
|
2972
|
+
}
|
|
2973
|
+
/**
|
|
2974
|
+
* Generate Mermaid diagram of dependencies
|
|
2975
|
+
*/
|
|
2976
|
+
async generateGraph() {
|
|
2977
|
+
const parseResult = await this.parse();
|
|
2978
|
+
if (!parseResult.success || !parseResult.document || parseResult.document.rules.length === 0) {
|
|
2979
|
+
return 'graph LR\n empty["No dependencies defined"]';
|
|
2980
|
+
}
|
|
2981
|
+
const lines = ["graph LR"];
|
|
2982
|
+
const nodeIds = /* @__PURE__ */ new Map();
|
|
2983
|
+
let nodeCounter = 0;
|
|
2984
|
+
const getNodeId = (path) => {
|
|
2985
|
+
if (!nodeIds.has(path)) {
|
|
2986
|
+
nodeIds.set(path, `n${nodeCounter++}`);
|
|
2987
|
+
}
|
|
2988
|
+
return nodeIds.get(path);
|
|
2989
|
+
};
|
|
2990
|
+
for (const rule of parseResult.document.rules) {
|
|
2991
|
+
const sourceId = getNodeId(rule.source);
|
|
2992
|
+
const sourceLabel = rule.source.replace(/"/g, "'");
|
|
2993
|
+
for (const dependent of rule.dependents) {
|
|
2994
|
+
const depId = getNodeId(dependent);
|
|
2995
|
+
const depLabel = dependent.replace(/"/g, "'");
|
|
2996
|
+
lines.push(` ${sourceId}["${sourceLabel}"] --> ${depId}["${depLabel}"]`);
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
return lines.join("\n");
|
|
3000
|
+
}
|
|
3001
|
+
};
|
|
3002
|
+
|
|
3003
|
+
// src/core/conflicts/checker.ts
|
|
2854
3004
|
var ConflictChecker = class {
|
|
2855
3005
|
architecture;
|
|
2856
3006
|
constructor(architecture) {
|
|
@@ -2877,6 +3027,10 @@ var ConflictChecker = class {
|
|
|
2877
3027
|
}
|
|
2878
3028
|
const semanticConflicts = this.checkSemanticConflicts(plan);
|
|
2879
3029
|
conflicts.push(...semanticConflicts);
|
|
3030
|
+
if (options.checkFileDependencies !== false) {
|
|
3031
|
+
const fileDepsConflicts = await this.checkFileLevelDependencies(plan.files_to_modify, cwd);
|
|
3032
|
+
conflicts.push(...fileDepsConflicts);
|
|
3033
|
+
}
|
|
2880
3034
|
if (conflicts.some((c) => c.type === "FILE_MODIFIED")) {
|
|
2881
3035
|
recommendations.push("Review modified files and re-plan if necessary");
|
|
2882
3036
|
}
|
|
@@ -2902,7 +3056,7 @@ var ConflictChecker = class {
|
|
|
2902
3056
|
const conflicts = [];
|
|
2903
3057
|
for (const filePath of files) {
|
|
2904
3058
|
const fullPath = `${cwd}/${filePath}`;
|
|
2905
|
-
if (!
|
|
3059
|
+
if (!existsSync2(fullPath)) {
|
|
2906
3060
|
continue;
|
|
2907
3061
|
}
|
|
2908
3062
|
try {
|
|
@@ -3035,12 +3189,42 @@ var ConflictChecker = class {
|
|
|
3035
3189
|
if (!scope || scope === "*") return true;
|
|
3036
3190
|
return this.matchesPattern(path, scope) || path.startsWith(scope);
|
|
3037
3191
|
}
|
|
3192
|
+
/**
|
|
3193
|
+
* Check file-level dependencies from DEPENDENCIES.md
|
|
3194
|
+
*/
|
|
3195
|
+
async checkFileLevelDependencies(files, cwd) {
|
|
3196
|
+
const conflicts = [];
|
|
3197
|
+
try {
|
|
3198
|
+
const parser = new DependencyParser(cwd);
|
|
3199
|
+
if (!parser.exists()) {
|
|
3200
|
+
return [];
|
|
3201
|
+
}
|
|
3202
|
+
const result = await parser.checkFiles(files);
|
|
3203
|
+
for (const impact of result.impacts) {
|
|
3204
|
+
const severity = impact.rule.severity === "BLOCKER" ? "BLOCKER" : impact.rule.severity === "WARNING" ? "WARNING" : "INFO";
|
|
3205
|
+
conflicts.push({
|
|
3206
|
+
type: "DEPENDENCY",
|
|
3207
|
+
severity,
|
|
3208
|
+
path: impact.matchedSource,
|
|
3209
|
+
message: `Changing ${impact.matchedSource} may impact: ${impact.affectedDependents.join(", ")}`,
|
|
3210
|
+
recommendation: impact.rule.reason || "Review dependent files before proceeding"
|
|
3211
|
+
});
|
|
3212
|
+
}
|
|
3213
|
+
} catch {
|
|
3214
|
+
conflicts.push({
|
|
3215
|
+
type: "DEPENDENCY",
|
|
3216
|
+
severity: "INFO",
|
|
3217
|
+
message: "DEPENDENCIES.md could not be parsed; file-level dependency checks skipped"
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
return conflicts;
|
|
3221
|
+
}
|
|
3038
3222
|
};
|
|
3039
3223
|
|
|
3040
3224
|
// src/core/gates/runner.ts
|
|
3041
3225
|
import { execSync } from "child_process";
|
|
3042
3226
|
import { readFile as readFile2 } from "fs/promises";
|
|
3043
|
-
import { existsSync as
|
|
3227
|
+
import { existsSync as existsSync3 } from "fs";
|
|
3044
3228
|
import chalk from "chalk";
|
|
3045
3229
|
var QualityGateRunner = class {
|
|
3046
3230
|
cwd;
|
|
@@ -3125,7 +3309,7 @@ var QualityGateRunner = class {
|
|
|
3125
3309
|
*/
|
|
3126
3310
|
async runArchitectureGate() {
|
|
3127
3311
|
const archPath = `${this.cwd}/ARCHITECTURE.md`;
|
|
3128
|
-
if (!
|
|
3312
|
+
if (!existsSync3(archPath)) {
|
|
3129
3313
|
return "No ARCHITECTURE.md found - skipping";
|
|
3130
3314
|
}
|
|
3131
3315
|
return "Architecture constraints checked";
|
|
@@ -3135,13 +3319,13 @@ var QualityGateRunner = class {
|
|
|
3135
3319
|
*/
|
|
3136
3320
|
async runSyntaxGate() {
|
|
3137
3321
|
const pkgPath = `${this.cwd}/package.json`;
|
|
3138
|
-
if (!
|
|
3322
|
+
if (!existsSync3(pkgPath)) {
|
|
3139
3323
|
return "No package.json found - skipping syntax check";
|
|
3140
3324
|
}
|
|
3141
3325
|
const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
3142
3326
|
if (pkg.scripts?.["typecheck"]) {
|
|
3143
3327
|
return this.runCommand("npm run typecheck");
|
|
3144
|
-
} else if (
|
|
3328
|
+
} else if (existsSync3(`${this.cwd}/tsconfig.json`)) {
|
|
3145
3329
|
return this.runCommand("npx tsc --noEmit");
|
|
3146
3330
|
}
|
|
3147
3331
|
return "No TypeScript configuration found - skipping";
|
|
@@ -3151,7 +3335,7 @@ var QualityGateRunner = class {
|
|
|
3151
3335
|
*/
|
|
3152
3336
|
async runLintGate() {
|
|
3153
3337
|
const pkgPath = `${this.cwd}/package.json`;
|
|
3154
|
-
if (!
|
|
3338
|
+
if (!existsSync3(pkgPath)) {
|
|
3155
3339
|
return "No package.json found - skipping lint";
|
|
3156
3340
|
}
|
|
3157
3341
|
const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
@@ -3165,7 +3349,7 @@ var QualityGateRunner = class {
|
|
|
3165
3349
|
*/
|
|
3166
3350
|
async runUnitGate() {
|
|
3167
3351
|
const pkgPath = `${this.cwd}/package.json`;
|
|
3168
|
-
if (!
|
|
3352
|
+
if (!existsSync3(pkgPath)) {
|
|
3169
3353
|
return "No package.json found - skipping tests";
|
|
3170
3354
|
}
|
|
3171
3355
|
const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
@@ -3179,7 +3363,7 @@ var QualityGateRunner = class {
|
|
|
3179
3363
|
*/
|
|
3180
3364
|
async runIntegrationGate() {
|
|
3181
3365
|
const pkgPath = `${this.cwd}/package.json`;
|
|
3182
|
-
if (!
|
|
3366
|
+
if (!existsSync3(pkgPath)) {
|
|
3183
3367
|
return "No package.json found - skipping integration tests";
|
|
3184
3368
|
}
|
|
3185
3369
|
const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
@@ -3193,7 +3377,7 @@ var QualityGateRunner = class {
|
|
|
3193
3377
|
*/
|
|
3194
3378
|
async runE2EGate() {
|
|
3195
3379
|
const pkgPath = `${this.cwd}/package.json`;
|
|
3196
|
-
if (!
|
|
3380
|
+
if (!existsSync3(pkgPath)) {
|
|
3197
3381
|
return "No package.json found - skipping E2E tests";
|
|
3198
3382
|
}
|
|
3199
3383
|
const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
@@ -3243,7 +3427,7 @@ var QualityGateRunner = class {
|
|
|
3243
3427
|
|
|
3244
3428
|
// src/core/learning/capture.ts
|
|
3245
3429
|
import { readFile as readFile3, writeFile, appendFile } from "fs/promises";
|
|
3246
|
-
import { existsSync as
|
|
3430
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3247
3431
|
var PROGRESS_HEADER = `# ArchonDev Progress Log
|
|
3248
3432
|
|
|
3249
3433
|
Started: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
@@ -3294,7 +3478,7 @@ var LearningCapture = class {
|
|
|
3294
3478
|
* Ensure progress.txt exists with initial header
|
|
3295
3479
|
*/
|
|
3296
3480
|
async ensureProgressFile() {
|
|
3297
|
-
if (!
|
|
3481
|
+
if (!existsSync4(this.progressPath)) {
|
|
3298
3482
|
await writeFile(this.progressPath, PROGRESS_HEADER);
|
|
3299
3483
|
}
|
|
3300
3484
|
}
|
|
@@ -3536,7 +3720,7 @@ var LearningCapture = class {
|
|
|
3536
3720
|
|
|
3537
3721
|
// src/core/learning/agents-md.ts
|
|
3538
3722
|
import { readFile as readFile4, writeFile as writeFile2, appendFile as appendFile2, mkdir } from "fs/promises";
|
|
3539
|
-
import { existsSync as
|
|
3723
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3540
3724
|
import { dirname, join } from "path";
|
|
3541
3725
|
var AGENTS_MD_HEADER = `# AGENTS.md
|
|
3542
3726
|
|
|
@@ -3771,7 +3955,7 @@ var AgentsMdUpdater = class {
|
|
|
3771
3955
|
const agentsMdPath = join(this.cwd, dir, "AGENTS.md");
|
|
3772
3956
|
const parentAgentsMd = await this.findParentAgentsMd(dir);
|
|
3773
3957
|
let learningsAdded = 0;
|
|
3774
|
-
if (
|
|
3958
|
+
if (existsSync5(agentsMdPath)) {
|
|
3775
3959
|
learningsAdded = await this.appendToAgentsMd(agentsMdPath, knowledge);
|
|
3776
3960
|
return { path: agentsMdPath, updated: true, created: false, learningsAdded };
|
|
3777
3961
|
}
|
|
@@ -3794,7 +3978,7 @@ var AgentsMdUpdater = class {
|
|
|
3794
3978
|
let currentDir = dirname(dir);
|
|
3795
3979
|
while (currentDir && currentDir !== "." && currentDir !== "/") {
|
|
3796
3980
|
const agentsMdPath = join(this.cwd, currentDir, "AGENTS.md");
|
|
3797
|
-
if (
|
|
3981
|
+
if (existsSync5(agentsMdPath)) {
|
|
3798
3982
|
return agentsMdPath;
|
|
3799
3983
|
}
|
|
3800
3984
|
currentDir = dirname(currentDir);
|
|
@@ -3896,7 +4080,7 @@ var AgentsMdUpdater = class {
|
|
|
3896
4080
|
parts.push("");
|
|
3897
4081
|
}
|
|
3898
4082
|
const dir = dirname(path);
|
|
3899
|
-
if (!
|
|
4083
|
+
if (!existsSync5(dir)) {
|
|
3900
4084
|
await mkdir(dir, { recursive: true });
|
|
3901
4085
|
}
|
|
3902
4086
|
await writeFile2(path, parts.join("\n"));
|
|
@@ -3905,7 +4089,7 @@ var AgentsMdUpdater = class {
|
|
|
3905
4089
|
|
|
3906
4090
|
// src/agents/executor.ts
|
|
3907
4091
|
import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
|
|
3908
|
-
import { existsSync as
|
|
4092
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3909
4093
|
import { dirname as dirname2 } from "path";
|
|
3910
4094
|
import { execSync as execSync2 } from "child_process";
|
|
3911
4095
|
var SYSTEM_PROMPT = `You are the Executor, an expert software engineer responsible for implementing approved plans.
|
|
@@ -3972,7 +4156,7 @@ var ExecutorAgent = class {
|
|
|
3972
4156
|
try {
|
|
3973
4157
|
for (const diff of diffs) {
|
|
3974
4158
|
const fullPath = `${cwd}/${diff.path}`;
|
|
3975
|
-
if (
|
|
4159
|
+
if (existsSync6(fullPath)) {
|
|
3976
4160
|
originalContents.set(diff.path, await readFile5(fullPath, "utf-8"));
|
|
3977
4161
|
} else {
|
|
3978
4162
|
originalContents.set(diff.path, null);
|
|
@@ -3984,7 +4168,7 @@ var ExecutorAgent = class {
|
|
|
3984
4168
|
continue;
|
|
3985
4169
|
}
|
|
3986
4170
|
const dir = dirname2(fullPath);
|
|
3987
|
-
if (!
|
|
4171
|
+
if (!existsSync6(dir)) {
|
|
3988
4172
|
await mkdir2(dir, { recursive: true });
|
|
3989
4173
|
}
|
|
3990
4174
|
await writeFile3(fullPath, diff.newContent);
|
|
@@ -4042,7 +4226,7 @@ var ExecutorAgent = class {
|
|
|
4042
4226
|
parts.push("# Current File Contents");
|
|
4043
4227
|
for (const filePath of plan.files_to_modify) {
|
|
4044
4228
|
const fullPath = `${cwd}/${filePath}`;
|
|
4045
|
-
if (
|
|
4229
|
+
if (existsSync6(fullPath)) {
|
|
4046
4230
|
try {
|
|
4047
4231
|
const content = await readFile5(fullPath, "utf-8");
|
|
4048
4232
|
parts.push(`
|
|
@@ -4167,7 +4351,7 @@ var ExecutorAgent = class {
|
|
|
4167
4351
|
async runQualityChecks(cwd) {
|
|
4168
4352
|
try {
|
|
4169
4353
|
const pkgPath = `${cwd}/package.json`;
|
|
4170
|
-
if (
|
|
4354
|
+
if (existsSync6(pkgPath)) {
|
|
4171
4355
|
const pkg = JSON.parse(await readFile5(pkgPath, "utf-8"));
|
|
4172
4356
|
if (pkg.scripts?.["typecheck"]) {
|
|
4173
4357
|
execSync2("npm run typecheck", { cwd, stdio: "pipe" });
|
|
@@ -4219,7 +4403,7 @@ var DEFAULT_ENVIRONMENTS = {
|
|
|
4219
4403
|
|
|
4220
4404
|
// src/core/environments/config.ts
|
|
4221
4405
|
var import_js_yaml = __toESM(require_js_yaml2(), 1);
|
|
4222
|
-
import { existsSync as
|
|
4406
|
+
import { existsSync as existsSync7 } from "fs";
|
|
4223
4407
|
import { readFile as readFile6 } from "fs/promises";
|
|
4224
4408
|
import { join as join2 } from "path";
|
|
4225
4409
|
var VALID_GATES = [
|
|
@@ -4239,11 +4423,11 @@ var EnvironmentConfigLoader = class {
|
|
|
4239
4423
|
}
|
|
4240
4424
|
async loadConfig() {
|
|
4241
4425
|
const configPath = join2(this.cwd, "archon.config.yaml");
|
|
4242
|
-
if (
|
|
4426
|
+
if (existsSync7(configPath)) {
|
|
4243
4427
|
return this.loadFromConfigFile(configPath);
|
|
4244
4428
|
}
|
|
4245
4429
|
const archPath = join2(this.cwd, "ARCHITECTURE.md");
|
|
4246
|
-
if (
|
|
4430
|
+
if (existsSync7(archPath)) {
|
|
4247
4431
|
return this.loadFromArchitecture(archPath);
|
|
4248
4432
|
}
|
|
4249
4433
|
return DEFAULT_ENVIRONMENTS;
|
|
@@ -4462,7 +4646,7 @@ async function execute(atomId, options) {
|
|
|
4462
4646
|
process.exit(1);
|
|
4463
4647
|
}
|
|
4464
4648
|
const archPath = join3(cwd, "ARCHITECTURE.md");
|
|
4465
|
-
if (!
|
|
4649
|
+
if (!existsSync8(archPath)) {
|
|
4466
4650
|
console.error(chalk2.red("ARCHITECTURE.md not found."));
|
|
4467
4651
|
process.exit(1);
|
|
4468
4652
|
}
|
|
@@ -4630,7 +4814,7 @@ async function captureLearnings(atom, execution) {
|
|
|
4630
4814
|
}
|
|
4631
4815
|
async function loadAtomEnvironmentState(atomId, cwd) {
|
|
4632
4816
|
const stateFile = join3(cwd, ATOMS_DIR, `${atomId}.env.json`);
|
|
4633
|
-
if (!
|
|
4817
|
+
if (!existsSync8(stateFile)) {
|
|
4634
4818
|
return null;
|
|
4635
4819
|
}
|
|
4636
4820
|
const content = await readFile7(stateFile, "utf-8");
|
|
@@ -4642,6 +4826,7 @@ async function saveAtomEnvironmentState(atomId, cwd, state) {
|
|
|
4642
4826
|
}
|
|
4643
4827
|
|
|
4644
4828
|
export {
|
|
4829
|
+
DependencyParser,
|
|
4645
4830
|
EnvironmentConfigLoader,
|
|
4646
4831
|
EnvironmentValidator,
|
|
4647
4832
|
execute
|
|
@@ -15,40 +15,54 @@ import readline from "readline";
|
|
|
15
15
|
// src/core/models/registry.json
|
|
16
16
|
var registry_default = {
|
|
17
17
|
lastUpdated: "2026-01-21T00:00:00Z",
|
|
18
|
-
version: "1.
|
|
18
|
+
version: "1.2.0",
|
|
19
19
|
providers: {
|
|
20
20
|
anthropic: {
|
|
21
21
|
displayName: "Anthropic",
|
|
22
22
|
fast: [
|
|
23
23
|
{
|
|
24
|
-
id: "claude-
|
|
25
|
-
name: "Claude
|
|
24
|
+
id: "claude-haiku-4-5",
|
|
25
|
+
name: "Claude Haiku 4.5",
|
|
26
26
|
active: true,
|
|
27
|
-
costPer1kInput:
|
|
28
|
-
costPer1kOutput:
|
|
27
|
+
costPer1kInput: 1e-3,
|
|
28
|
+
costPer1kOutput: 5e-3
|
|
29
29
|
}
|
|
30
30
|
],
|
|
31
31
|
thinking: [
|
|
32
32
|
{
|
|
33
|
-
id: "claude-
|
|
34
|
-
name: "Claude
|
|
33
|
+
id: "claude-sonnet-4-5-20250929",
|
|
34
|
+
name: "Claude Sonnet 4.5",
|
|
35
35
|
active: true,
|
|
36
36
|
default: true,
|
|
37
37
|
costPer1kInput: 3e-3,
|
|
38
38
|
costPer1kOutput: 0.015
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
id: "claude-
|
|
42
|
-
name: "Claude
|
|
41
|
+
id: "claude-opus-4-5-20251101",
|
|
42
|
+
name: "Claude Opus 4.5",
|
|
43
43
|
active: true,
|
|
44
|
-
costPer1kInput:
|
|
45
|
-
costPer1kOutput: 0.
|
|
44
|
+
costPer1kInput: 5e-3,
|
|
45
|
+
costPer1kOutput: 0.025
|
|
46
46
|
}
|
|
47
47
|
]
|
|
48
48
|
},
|
|
49
49
|
openai: {
|
|
50
50
|
displayName: "OpenAI",
|
|
51
51
|
fast: [
|
|
52
|
+
{
|
|
53
|
+
id: "gpt-5-nano",
|
|
54
|
+
name: "GPT-5 Nano",
|
|
55
|
+
active: true,
|
|
56
|
+
costPer1kInput: 25e-6,
|
|
57
|
+
costPer1kOutput: 2e-4
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "gpt-4.1-nano",
|
|
61
|
+
name: "GPT-4.1 Nano",
|
|
62
|
+
active: true,
|
|
63
|
+
costPer1kInput: 5e-5,
|
|
64
|
+
costPer1kOutput: 2e-4
|
|
65
|
+
},
|
|
52
66
|
{
|
|
53
67
|
id: "gpt-4o-mini",
|
|
54
68
|
name: "GPT-4o Mini",
|
|
@@ -59,26 +73,26 @@ var registry_default = {
|
|
|
59
73
|
],
|
|
60
74
|
thinking: [
|
|
61
75
|
{
|
|
62
|
-
id: "gpt-
|
|
63
|
-
name: "GPT-
|
|
76
|
+
id: "gpt-5.2",
|
|
77
|
+
name: "GPT-5.2",
|
|
64
78
|
active: true,
|
|
65
79
|
default: true,
|
|
66
|
-
costPer1kInput:
|
|
67
|
-
costPer1kOutput: 0.
|
|
80
|
+
costPer1kInput: 3e-3,
|
|
81
|
+
costPer1kOutput: 0.012
|
|
68
82
|
},
|
|
69
83
|
{
|
|
70
|
-
id: "
|
|
71
|
-
name: "
|
|
84
|
+
id: "o3",
|
|
85
|
+
name: "o3",
|
|
72
86
|
active: true,
|
|
73
|
-
costPer1kInput:
|
|
74
|
-
costPer1kOutput:
|
|
87
|
+
costPer1kInput: 2e-3,
|
|
88
|
+
costPer1kOutput: 8e-3
|
|
75
89
|
},
|
|
76
90
|
{
|
|
77
|
-
id: "
|
|
78
|
-
name: "
|
|
91
|
+
id: "o4-mini",
|
|
92
|
+
name: "o4-mini",
|
|
79
93
|
active: true,
|
|
80
|
-
costPer1kInput:
|
|
81
|
-
costPer1kOutput:
|
|
94
|
+
costPer1kInput: 11e-4,
|
|
95
|
+
costPer1kOutput: 44e-4
|
|
82
96
|
}
|
|
83
97
|
]
|
|
84
98
|
},
|
|
@@ -86,42 +100,49 @@ var registry_default = {
|
|
|
86
100
|
displayName: "Google",
|
|
87
101
|
fast: [
|
|
88
102
|
{
|
|
89
|
-
id: "gemini-
|
|
90
|
-
name: "Gemini
|
|
103
|
+
id: "gemini-2.5-flash-lite",
|
|
104
|
+
name: "Gemini 2.5 Flash-Lite",
|
|
91
105
|
active: true,
|
|
92
106
|
costPer1kInput: 75e-6,
|
|
93
107
|
costPer1kOutput: 3e-4
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "gemini-3-flash-preview",
|
|
111
|
+
name: "Gemini 3 Flash",
|
|
112
|
+
active: true,
|
|
113
|
+
costPer1kInput: 5e-4,
|
|
114
|
+
costPer1kOutput: 3e-3
|
|
94
115
|
}
|
|
95
116
|
],
|
|
96
117
|
thinking: [
|
|
97
118
|
{
|
|
98
|
-
id: "gemini-
|
|
99
|
-
name: "Gemini
|
|
119
|
+
id: "gemini-2.5-pro",
|
|
120
|
+
name: "Gemini 2.5 Pro",
|
|
100
121
|
active: true,
|
|
101
122
|
default: true,
|
|
102
123
|
costPer1kInput: 125e-5,
|
|
103
|
-
costPer1kOutput:
|
|
124
|
+
costPer1kOutput: 0.01
|
|
104
125
|
},
|
|
105
126
|
{
|
|
106
|
-
id: "gemini-
|
|
107
|
-
name: "Gemini
|
|
127
|
+
id: "gemini-3-pro-preview",
|
|
128
|
+
name: "Gemini 3 Pro",
|
|
108
129
|
active: true,
|
|
109
|
-
costPer1kInput:
|
|
110
|
-
costPer1kOutput: 0
|
|
130
|
+
costPer1kInput: 2e-3,
|
|
131
|
+
costPer1kOutput: 0.012
|
|
111
132
|
}
|
|
112
133
|
]
|
|
113
134
|
}
|
|
114
135
|
},
|
|
115
136
|
defaults: {
|
|
116
|
-
fast: "claude-
|
|
117
|
-
thinking: "claude-
|
|
118
|
-
primaryAdversarial: "claude-
|
|
119
|
-
secondaryAdversarial: "gpt-
|
|
137
|
+
fast: "claude-haiku-4-5",
|
|
138
|
+
thinking: "claude-sonnet-4-5-20250929",
|
|
139
|
+
primaryAdversarial: "claude-sonnet-4-5-20250929",
|
|
140
|
+
secondaryAdversarial: "gpt-5.2"
|
|
120
141
|
},
|
|
121
142
|
freeModels: [
|
|
122
|
-
"
|
|
123
|
-
"gpt-
|
|
124
|
-
"gemini-
|
|
143
|
+
"gpt-5-nano",
|
|
144
|
+
"gpt-4.1-nano",
|
|
145
|
+
"gemini-2.5-flash-lite"
|
|
125
146
|
]
|
|
126
147
|
};
|
|
127
148
|
|
|
@@ -605,7 +626,7 @@ async function setDefaultModelsMenu(profile, providers) {
|
|
|
605
626
|
console.log(chalk.bold("\nAvailable models:\n"));
|
|
606
627
|
availableModels.forEach((m, i) => {
|
|
607
628
|
const categoryLabel = m.category === "fast" ? chalk.cyan("[fast]") : chalk.magenta("[thinking]");
|
|
608
|
-
console.log(` ${chalk.cyan(String(i + 1))}) ${m.
|
|
629
|
+
console.log(` ${chalk.cyan(String(i + 1))}) ${m.modelName} ${categoryLabel}`);
|
|
609
630
|
});
|
|
610
631
|
console.log();
|
|
611
632
|
const modelChoice = await prompt("Select model number");
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
resetPreferences,
|
|
5
5
|
setPreference,
|
|
6
6
|
showPreferences
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-K2VBICW3.js";
|
|
8
8
|
import {
|
|
9
9
|
reviewAnalyze,
|
|
10
10
|
reviewExport,
|
|
@@ -27,10 +27,11 @@ import {
|
|
|
27
27
|
status
|
|
28
28
|
} from "./chunk-CAYCSBNX.js";
|
|
29
29
|
import {
|
|
30
|
+
DependencyParser,
|
|
30
31
|
EnvironmentConfigLoader,
|
|
31
32
|
EnvironmentValidator,
|
|
32
33
|
execute
|
|
33
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-74EEFKVP.js";
|
|
34
35
|
import {
|
|
35
36
|
list
|
|
36
37
|
} from "./chunk-PK3OQVBG.js";
|
|
@@ -58,8 +59,8 @@ import {
|
|
|
58
59
|
import "./chunk-QGM4M3NI.js";
|
|
59
60
|
|
|
60
61
|
// src/cli/index.ts
|
|
61
|
-
import { Command } from "commander";
|
|
62
|
-
import
|
|
62
|
+
import { Command as Command2 } from "commander";
|
|
63
|
+
import chalk7 from "chalk";
|
|
63
64
|
import "dotenv/config";
|
|
64
65
|
|
|
65
66
|
// src/cli/init.ts
|
|
@@ -725,7 +726,7 @@ async function listAtoms() {
|
|
|
725
726
|
async function executeNext() {
|
|
726
727
|
const atomId = await prompt("Enter atom ID to execute (or press Enter for next planned)");
|
|
727
728
|
if (atomId.trim()) {
|
|
728
|
-
const { execute: execute2 } = await import("./execute-
|
|
729
|
+
const { execute: execute2 } = await import("./execute-N6CCEJ5I.js");
|
|
729
730
|
await execute2(atomId.trim(), {});
|
|
730
731
|
} else {
|
|
731
732
|
console.log(chalk4.yellow('No atom ID provided. Use "archon list" to see available atoms.'));
|
|
@@ -743,7 +744,7 @@ async function viewStatus() {
|
|
|
743
744
|
await status2();
|
|
744
745
|
}
|
|
745
746
|
async function settingsMenu() {
|
|
746
|
-
const { interactiveSettings } = await import("./preferences-
|
|
747
|
+
const { interactiveSettings } = await import("./preferences-QEFXVCZN.js");
|
|
747
748
|
await interactiveSettings();
|
|
748
749
|
await start();
|
|
749
750
|
}
|
|
@@ -1399,12 +1400,251 @@ async function watch() {
|
|
|
1399
1400
|
await waitUntilExit();
|
|
1400
1401
|
}
|
|
1401
1402
|
|
|
1403
|
+
// src/cli/deps.ts
|
|
1404
|
+
import { Command } from "commander";
|
|
1405
|
+
import chalk6 from "chalk";
|
|
1406
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
1407
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1408
|
+
var DEPENDENCIES_FILENAME = "DEPENDENCIES.md";
|
|
1409
|
+
function createDepsCommand() {
|
|
1410
|
+
const deps = new Command("deps").description("Manage file-level dependencies (DEPENDENCIES.md)").addHelpText(
|
|
1411
|
+
"after",
|
|
1412
|
+
`
|
|
1413
|
+
Examples:
|
|
1414
|
+
archon deps list List all dependency rules
|
|
1415
|
+
archon deps check --files a.ts,b.ts Check if files have downstream impacts
|
|
1416
|
+
archon deps add --source src/api.ts --dependent src/cli/**
|
|
1417
|
+
archon deps graph Generate Mermaid dependency diagram
|
|
1418
|
+
`
|
|
1419
|
+
);
|
|
1420
|
+
deps.command("list").description("List all dependency rules").option("-v, --verbose", "Show detailed information").action(async (options) => {
|
|
1421
|
+
const parser = new DependencyParser();
|
|
1422
|
+
if (!parser.exists()) {
|
|
1423
|
+
console.log(chalk6.yellow("No DEPENDENCIES.md found."));
|
|
1424
|
+
console.log(chalk6.dim("Create one with: archon deps add --source <path> --dependent <path>"));
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
const result = await parser.parse();
|
|
1428
|
+
if (!result.success) {
|
|
1429
|
+
console.log(chalk6.red(`Parse error: ${result.error}`));
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
const rules = result.document?.rules ?? [];
|
|
1433
|
+
if (rules.length === 0) {
|
|
1434
|
+
console.log(chalk6.dim("No dependency rules defined."));
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
console.log(chalk6.bold(`
|
|
1438
|
+
\u{1F4E6} Dependency Rules (${rules.length})
|
|
1439
|
+
`));
|
|
1440
|
+
for (const rule of rules) {
|
|
1441
|
+
const severityColor = rule.severity === "BLOCKER" ? chalk6.red : rule.severity === "WARNING" ? chalk6.yellow : chalk6.blue;
|
|
1442
|
+
console.log(`${severityColor(`[${rule.severity}]`)} ${chalk6.bold(rule.id)}`);
|
|
1443
|
+
console.log(` Source: ${chalk6.cyan(rule.source)}`);
|
|
1444
|
+
console.log(` Dependents: ${rule.dependents.map((d) => chalk6.dim(d)).join(", ")}`);
|
|
1445
|
+
if (rule.reason && options.verbose) {
|
|
1446
|
+
console.log(` Reason: ${chalk6.dim(rule.reason)}`);
|
|
1447
|
+
}
|
|
1448
|
+
if (rule.mustTest && options.verbose) {
|
|
1449
|
+
console.log(` Must test: ${rule.mustTest.join(", ")}`);
|
|
1450
|
+
}
|
|
1451
|
+
console.log("");
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
deps.command("check").description("Check if files have downstream dependency impacts").requiredOption("-f, --files <files>", "Comma-separated list of files to check").action(async (options) => {
|
|
1455
|
+
const files = options.files.split(",").map((f) => f.trim());
|
|
1456
|
+
const parser = new DependencyParser();
|
|
1457
|
+
if (!parser.exists()) {
|
|
1458
|
+
console.log(chalk6.dim("No DEPENDENCIES.md found. No dependency checks performed."));
|
|
1459
|
+
process.exit(0);
|
|
1460
|
+
}
|
|
1461
|
+
const result = await parser.checkFiles(files);
|
|
1462
|
+
if (result.impacts.length === 0) {
|
|
1463
|
+
console.log(chalk6.green("\u2705 No downstream dependency impacts found."));
|
|
1464
|
+
process.exit(0);
|
|
1465
|
+
}
|
|
1466
|
+
console.log(chalk6.yellow(`
|
|
1467
|
+
\u26A0\uFE0F Found ${result.impacts.length} dependency impact(s):
|
|
1468
|
+
`));
|
|
1469
|
+
for (const impact of result.impacts) {
|
|
1470
|
+
const severityColor = impact.rule.severity === "BLOCKER" ? chalk6.red : impact.rule.severity === "WARNING" ? chalk6.yellow : chalk6.blue;
|
|
1471
|
+
console.log(severityColor(`[${impact.rule.severity}] ${impact.rule.id}`));
|
|
1472
|
+
console.log(` Changing: ${chalk6.cyan(impact.matchedSource)}`);
|
|
1473
|
+
console.log(` May impact: ${impact.affectedDependents.join(", ")}`);
|
|
1474
|
+
if (impact.rule.reason) {
|
|
1475
|
+
console.log(` Reason: ${chalk6.dim(impact.rule.reason)}`);
|
|
1476
|
+
}
|
|
1477
|
+
console.log("");
|
|
1478
|
+
}
|
|
1479
|
+
if (result.hasBlockers) {
|
|
1480
|
+
console.log(chalk6.red("\u274C BLOCKER-level impacts found. Review before proceeding."));
|
|
1481
|
+
process.exit(1);
|
|
1482
|
+
}
|
|
1483
|
+
process.exit(0);
|
|
1484
|
+
});
|
|
1485
|
+
deps.command("add").description("Add a new dependency rule").requiredOption("-s, --source <path>", "Source file or glob pattern").requiredOption("-d, --dependent <path>", "Dependent file or glob pattern").option("--severity <level>", "Severity: INFO, WARNING, BLOCKER", "WARNING").option("--reason <text>", "Reason for this dependency").option("--id <id>", "Custom rule ID (e.g., DEP-001)").action(async (options) => {
|
|
1486
|
+
const source = options.source;
|
|
1487
|
+
const dependent = options.dependent;
|
|
1488
|
+
const severity = options.severity.toUpperCase();
|
|
1489
|
+
const reason = options.reason || "";
|
|
1490
|
+
const parser = new DependencyParser();
|
|
1491
|
+
let existingRules = [];
|
|
1492
|
+
let markdownBody = "";
|
|
1493
|
+
if (parser.exists()) {
|
|
1494
|
+
const content = await readFile3(DEPENDENCIES_FILENAME, "utf-8");
|
|
1495
|
+
const result = await parser.parse();
|
|
1496
|
+
if (result.success && result.document) {
|
|
1497
|
+
existingRules = result.document.rules;
|
|
1498
|
+
}
|
|
1499
|
+
const parts = content.split("---");
|
|
1500
|
+
if (parts.length >= 3) {
|
|
1501
|
+
markdownBody = parts.slice(2).join("---");
|
|
1502
|
+
}
|
|
1503
|
+
} else {
|
|
1504
|
+
markdownBody = `
|
|
1505
|
+
|
|
1506
|
+
# Dependencies (File & Symbol Impact Map)
|
|
1507
|
+
|
|
1508
|
+
## How to use
|
|
1509
|
+
- When you change a \`source\`, you must review each \`dependent\`.
|
|
1510
|
+
- Keep rules coarse and high-signal. Prefer fewer, accurate rules over exhaustive lists.
|
|
1511
|
+
|
|
1512
|
+
## Conventions
|
|
1513
|
+
- \`source\` and \`dependents\` accept glob patterns.
|
|
1514
|
+
- Use \`symbols\` to document function/class coupling when helpful.
|
|
1515
|
+
`;
|
|
1516
|
+
}
|
|
1517
|
+
const nextId = options.id || `DEP-${String(existingRules.length + 1).padStart(3, "0")}`;
|
|
1518
|
+
const existingRule = existingRules.find(
|
|
1519
|
+
(r) => r.source === source && r.dependents.includes(dependent)
|
|
1520
|
+
);
|
|
1521
|
+
if (existingRule) {
|
|
1522
|
+
console.log(chalk6.yellow(`Rule already exists: ${existingRule.id}`));
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
const newRule = {
|
|
1526
|
+
id: nextId,
|
|
1527
|
+
source,
|
|
1528
|
+
dependents: [dependent],
|
|
1529
|
+
severity,
|
|
1530
|
+
reason
|
|
1531
|
+
};
|
|
1532
|
+
existingRules.push(newRule);
|
|
1533
|
+
const yaml = generateYamlFrontmatter(existingRules);
|
|
1534
|
+
await writeFile3(DEPENDENCIES_FILENAME, `---
|
|
1535
|
+
${yaml}---${markdownBody}`, "utf-8");
|
|
1536
|
+
console.log(chalk6.green(`\u2705 Added dependency rule: ${nextId}`));
|
|
1537
|
+
console.log(` Source: ${chalk6.cyan(source)}`);
|
|
1538
|
+
console.log(` Dependent: ${chalk6.dim(dependent)}`);
|
|
1539
|
+
});
|
|
1540
|
+
deps.command("graph").description("Generate Mermaid diagram of dependencies").option("--output <file>", "Write to file instead of stdout").action(async (options) => {
|
|
1541
|
+
const parser = new DependencyParser();
|
|
1542
|
+
if (!parser.exists()) {
|
|
1543
|
+
console.log(chalk6.yellow("No DEPENDENCIES.md found."));
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
const mermaid = await parser.generateGraph();
|
|
1547
|
+
if (options.output) {
|
|
1548
|
+
await writeFile3(options.output, mermaid, "utf-8");
|
|
1549
|
+
console.log(chalk6.green(`\u2705 Graph written to ${options.output}`));
|
|
1550
|
+
} else {
|
|
1551
|
+
console.log("\n```mermaid");
|
|
1552
|
+
console.log(mermaid);
|
|
1553
|
+
console.log("```\n");
|
|
1554
|
+
}
|
|
1555
|
+
});
|
|
1556
|
+
deps.command("init").description("Create a starter DEPENDENCIES.md file").action(async () => {
|
|
1557
|
+
if (existsSync5(DEPENDENCIES_FILENAME)) {
|
|
1558
|
+
console.log(chalk6.yellow("DEPENDENCIES.md already exists."));
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
const template = `---
|
|
1562
|
+
version: "1.0"
|
|
1563
|
+
updatedAt: "${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}"
|
|
1564
|
+
rules: []
|
|
1565
|
+
---
|
|
1566
|
+
|
|
1567
|
+
# Dependencies (File & Symbol Impact Map)
|
|
1568
|
+
|
|
1569
|
+
## How to use
|
|
1570
|
+
|
|
1571
|
+
This file tracks file-level dependencies to prevent regressions. When you change a \`source\` file, you must review and potentially update each \`dependent\`.
|
|
1572
|
+
|
|
1573
|
+
**Example rule:**
|
|
1574
|
+
\`\`\`yaml
|
|
1575
|
+
rules:
|
|
1576
|
+
- id: "DEP-001"
|
|
1577
|
+
source: "src/api/auth.ts"
|
|
1578
|
+
dependents:
|
|
1579
|
+
- "src/cli/**"
|
|
1580
|
+
- "src/server/routes.ts"
|
|
1581
|
+
severity: "WARNING"
|
|
1582
|
+
reason: "CLI and server depend on auth types; changes may require updates"
|
|
1583
|
+
mustTest:
|
|
1584
|
+
- "pnpm test auth"
|
|
1585
|
+
\`\`\`
|
|
1586
|
+
|
|
1587
|
+
## Severity Levels
|
|
1588
|
+
|
|
1589
|
+
- **INFO** \u2014 FYI, might want to review
|
|
1590
|
+
- **WARNING** \u2014 Should review dependents before merging
|
|
1591
|
+
- **BLOCKER** \u2014 Must update dependents before this change can proceed
|
|
1592
|
+
|
|
1593
|
+
## Conventions
|
|
1594
|
+
|
|
1595
|
+
- Use glob patterns for broad dependencies (\`src/core/**\`)
|
|
1596
|
+
- Keep rules high-signal; don't track every import
|
|
1597
|
+
- Add \`mustTest\` for critical paths
|
|
1598
|
+
- Update \`updatedAt\` when modifying rules
|
|
1599
|
+
|
|
1600
|
+
---
|
|
1601
|
+
|
|
1602
|
+
*Powered by [ArchonDev](https://archondev.io)*
|
|
1603
|
+
`;
|
|
1604
|
+
await writeFile3(DEPENDENCIES_FILENAME, template, "utf-8");
|
|
1605
|
+
console.log(chalk6.green("\u2705 Created DEPENDENCIES.md"));
|
|
1606
|
+
console.log(chalk6.dim("Add your first rule with: archon deps add --source <path> --dependent <path>"));
|
|
1607
|
+
});
|
|
1608
|
+
return deps;
|
|
1609
|
+
}
|
|
1610
|
+
function generateYamlFrontmatter(rules) {
|
|
1611
|
+
const lines = [];
|
|
1612
|
+
lines.push(`version: "1.0"`);
|
|
1613
|
+
lines.push(`updatedAt: "${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}"`);
|
|
1614
|
+
lines.push(`rules:`);
|
|
1615
|
+
for (const rule of rules) {
|
|
1616
|
+
lines.push(` - id: "${rule.id}"`);
|
|
1617
|
+
lines.push(` source: "${rule.source}"`);
|
|
1618
|
+
lines.push(` dependents:`);
|
|
1619
|
+
for (const dep of rule.dependents) {
|
|
1620
|
+
lines.push(` - "${dep}"`);
|
|
1621
|
+
}
|
|
1622
|
+
lines.push(` severity: "${rule.severity}"`);
|
|
1623
|
+
if (rule.reason) {
|
|
1624
|
+
lines.push(` reason: "${rule.reason.replace(/"/g, '\\"')}"`);
|
|
1625
|
+
}
|
|
1626
|
+
if (rule.symbols && rule.symbols.length > 0) {
|
|
1627
|
+
lines.push(` symbols:`);
|
|
1628
|
+
for (const sym of rule.symbols) {
|
|
1629
|
+
lines.push(` - "${sym}"`);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
if (rule.mustTest && rule.mustTest.length > 0) {
|
|
1633
|
+
lines.push(` mustTest:`);
|
|
1634
|
+
for (const test of rule.mustTest) {
|
|
1635
|
+
lines.push(` - "${test}"`);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
return lines.join("\n") + "\n";
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1402
1642
|
// src/cli/index.ts
|
|
1403
|
-
var program = new
|
|
1643
|
+
var program = new Command2();
|
|
1404
1644
|
program.name("archon").description("Local-first AI-powered development governance").version("1.1.0").action(async () => {
|
|
1405
1645
|
const cwd = process.cwd();
|
|
1406
1646
|
if (!isInitialized(cwd)) {
|
|
1407
|
-
console.log(
|
|
1647
|
+
console.log(chalk7.blue("\nArchonDev is not initialized in this folder.\n"));
|
|
1408
1648
|
await init({ analyze: true, git: true });
|
|
1409
1649
|
}
|
|
1410
1650
|
await start();
|
|
@@ -1412,7 +1652,7 @@ program.name("archon").description("Local-first AI-powered development governanc
|
|
|
1412
1652
|
program.command("login").description("Authenticate with ArchonDev").option("-p, --provider <provider>", "OAuth provider (github or google)", "github").action(async (options) => {
|
|
1413
1653
|
const provider = options.provider;
|
|
1414
1654
|
if (provider !== "github" && provider !== "google") {
|
|
1415
|
-
console.error(
|
|
1655
|
+
console.error(chalk7.red('Invalid provider. Use "github" or "google"'));
|
|
1416
1656
|
process.exit(1);
|
|
1417
1657
|
}
|
|
1418
1658
|
await login(provider);
|
|
@@ -1531,4 +1771,5 @@ reviewCommand.command("run").description("Run AI-powered review on pending tasks
|
|
|
1531
1771
|
reviewCommand.action(async () => {
|
|
1532
1772
|
await reviewStatus();
|
|
1533
1773
|
});
|
|
1774
|
+
program.addCommand(createDepsCommand());
|
|
1534
1775
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archondev",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Local-first AI-powered development governance system",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"archon": "dist/index.js"
|
|
7
|
+
"archon": "dist/index.js",
|
|
8
|
+
"archondev": "dist/index.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"dist"
|