autonomous-flow-daemon 1.1.0 → 1.9.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +85 -46
  2. package/LICENSE +21 -21
  3. package/README-ko.md +282 -0
  4. package/README.md +282 -337
  5. package/mcp-config.json +10 -10
  6. package/package.json +14 -6
  7. package/src/adapters/index.ts +370 -159
  8. package/src/cli.ts +162 -57
  9. package/src/commands/benchmark.ts +187 -0
  10. package/src/commands/correlate.ts +180 -0
  11. package/src/commands/dashboard.ts +404 -0
  12. package/src/commands/diagnose.ts +56 -14
  13. package/src/commands/doctor.ts +243 -0
  14. package/src/commands/evolution.ts +190 -0
  15. package/src/commands/fix.ts +158 -138
  16. package/src/commands/hooks.ts +136 -0
  17. package/src/commands/lang.ts +41 -41
  18. package/src/commands/mcp.ts +129 -0
  19. package/src/commands/plugin.ts +110 -0
  20. package/src/commands/restart.ts +14 -0
  21. package/src/commands/score.ts +276 -208
  22. package/src/commands/start.ts +155 -96
  23. package/src/commands/stats.ts +103 -0
  24. package/src/commands/status.ts +157 -0
  25. package/src/commands/stop.ts +68 -49
  26. package/src/commands/suggest.ts +211 -0
  27. package/src/commands/sync.ts +567 -21
  28. package/src/commands/vaccine.ts +177 -0
  29. package/src/constants.ts +32 -8
  30. package/src/core/boast.ts +280 -265
  31. package/src/core/config.ts +49 -49
  32. package/src/core/correlation-engine.ts +265 -0
  33. package/src/core/db.ts +145 -46
  34. package/src/core/discovery.ts +65 -65
  35. package/src/core/evolution.ts +215 -0
  36. package/src/core/federation.ts +129 -0
  37. package/src/core/hologram/engine.ts +71 -0
  38. package/src/core/hologram/fallback.ts +11 -0
  39. package/src/core/hologram/go-extractor.ts +203 -0
  40. package/src/core/hologram/incremental.ts +227 -0
  41. package/src/core/hologram/py-extractor.ts +132 -0
  42. package/src/core/hologram/rust-extractor.ts +244 -0
  43. package/src/core/hologram/ts-extractor.ts +406 -0
  44. package/src/core/hologram/types.ts +27 -0
  45. package/src/core/hologram.ts +73 -243
  46. package/src/core/hook-manager.ts +259 -0
  47. package/src/core/i18n/messages.ts +309 -266
  48. package/src/core/immune.ts +8 -123
  49. package/src/core/locale.ts +88 -88
  50. package/src/core/log-rotate.ts +33 -0
  51. package/src/core/log-utils.ts +38 -0
  52. package/src/core/lru-map.ts +61 -0
  53. package/src/core/notify.ts +74 -66
  54. package/src/core/plugin-manager.ts +225 -0
  55. package/src/core/rule-engine.ts +287 -0
  56. package/src/core/rule-suggestion.ts +127 -0
  57. package/src/core/semantic-diff.ts +432 -0
  58. package/src/core/telemetry.ts +94 -0
  59. package/src/core/vaccine-registry.ts +212 -0
  60. package/src/core/validator-generator.ts +224 -0
  61. package/src/core/workspace.ts +28 -0
  62. package/src/core/yaml-minimal.ts +176 -0
  63. package/src/daemon/client.ts +78 -37
  64. package/src/daemon/event-batcher.ts +108 -0
  65. package/src/daemon/guards.ts +13 -0
  66. package/src/daemon/http-routes.ts +376 -0
  67. package/src/daemon/mcp-handler.ts +575 -0
  68. package/src/daemon/mcp-subscriptions.ts +81 -0
  69. package/src/daemon/mesh.ts +51 -0
  70. package/src/daemon/server.ts +655 -504
  71. package/src/daemon/types.ts +121 -0
  72. package/src/daemon/workspace-map.ts +104 -0
  73. package/src/platform.ts +60 -39
  74. package/src/version.ts +15 -0
  75. package/README.ko.md +0 -306
@@ -0,0 +1,243 @@
1
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
2
+ import { dirname } from "path";
3
+ import { loadAllRules, evaluateRules } from "../core/rule-engine";
4
+ import type { DiagnosticRule } from "../core/rule-engine";
5
+ import type { PatchOp, Symptom } from "../core/immune";
6
+ import { notifyAutoHeal } from "../core/notify";
7
+ import { getSystemLanguage } from "../core/locale";
8
+
9
+ interface DoctorOptions {
10
+ fix?: boolean;
11
+ }
12
+
13
+ // ── i18n ──
14
+ const msgs = {
15
+ en: {
16
+ title: "afd doctor — Deep Health Analysis",
17
+ ruleCount: "Rules loaded",
18
+ builtIn: "built-in",
19
+ custom: "custom",
20
+ scanning: "Scanning project health...",
21
+ healthy: "All checks passed. Project is healthy!",
22
+ grade: "Health Grade",
23
+ passed: "Passed",
24
+ failed: "Failed",
25
+ findings: "Findings",
26
+ recommendation: "Recommendation",
27
+ fixable: "auto-fixable",
28
+ fixApplied: "Fixed",
29
+ fixSkipped: "Skipped (no patches)",
30
+ fixSummary: "Auto-fix complete",
31
+ fixHint: "Run `afd doctor --fix` to auto-fix {count} issue(s).",
32
+ severity: { critical: "CRITICAL", warning: "WARNING", info: "INFO" },
33
+ },
34
+ ko: {
35
+ title: "afd doctor — 딥 헬스 분석",
36
+ ruleCount: "로드된 규칙",
37
+ builtIn: "내장",
38
+ custom: "사용자",
39
+ scanning: "프로젝트 건강 상태 스캔 중...",
40
+ healthy: "모든 검사 통과. 프로젝트가 건강합니다!",
41
+ grade: "건강 등급",
42
+ passed: "통과",
43
+ failed: "실패",
44
+ findings: "발견 사항",
45
+ recommendation: "권고사항",
46
+ fixable: "자동 수정 가능",
47
+ fixApplied: "수정 완료",
48
+ fixSkipped: "건너뜀 (패치 없음)",
49
+ fixSummary: "자동 수정 완료",
50
+ fixHint: "`afd doctor --fix`로 {count}건 자동 수정 가능합니다.",
51
+ severity: { critical: "심각", warning: "경고", info: "정보" },
52
+ },
53
+ };
54
+
55
+ // ── Box Drawing ──
56
+ const BOX = { tl: "┌", tr: "┐", bl: "└", br: "┘", h: "─", v: "│", ml: "├", mr: "┤" };
57
+ const W = 52;
58
+
59
+ function line(left: string, right: string) {
60
+ return `${left}${BOX.h.repeat(W)}${right}`;
61
+ }
62
+
63
+ function row(content: string) {
64
+ const visible = visualWidth(content);
65
+ const pad = Math.max(0, W - 2 - visible);
66
+ return `${BOX.v} ${content}${" ".repeat(pad)}${BOX.v}`;
67
+ }
68
+
69
+ function divider() {
70
+ return line(BOX.ml, BOX.mr);
71
+ }
72
+
73
+ function visualWidth(s: string): number {
74
+ let w = 0;
75
+ for (const ch of s) {
76
+ const cp = ch.codePointAt(0)!;
77
+ // CJK + emoji ranges → width 2
78
+ if (
79
+ (cp >= 0x1100 && cp <= 0x11ff) ||
80
+ (cp >= 0x2e80 && cp <= 0x9fff) ||
81
+ (cp >= 0xac00 && cp <= 0xd7af) ||
82
+ (cp >= 0xf900 && cp <= 0xfaff) ||
83
+ (cp >= 0xfe30 && cp <= 0xfe4f) ||
84
+ (cp >= 0x1f000 && cp <= 0x1faff) ||
85
+ (cp >= 0x20000 && cp <= 0x2fa1f)
86
+ ) {
87
+ w += 2;
88
+ } else {
89
+ w += 1;
90
+ }
91
+ }
92
+ return w;
93
+ }
94
+
95
+ function kv(label: string, value: string) {
96
+ const gap = 16 - visualWidth(label);
97
+ return row(`${label}${" ".repeat(Math.max(1, gap))}: ${value}`);
98
+ }
99
+
100
+ function severityIcon(sev: "critical" | "warning" | "info"): string {
101
+ return sev === "critical" ? "🔴" : sev === "warning" ? "🟡" : "🔵";
102
+ }
103
+
104
+ function healthGrade(passed: number, total: number): { grade: string; icon: string } {
105
+ if (total === 0) return { grade: "A+", icon: "💎" };
106
+ const pct = (passed / total) * 100;
107
+ if (pct === 100) return { grade: "A+", icon: "💎" };
108
+ if (pct >= 80) return { grade: "A", icon: "🟢" };
109
+ if (pct >= 60) return { grade: "B", icon: "🟡" };
110
+ if (pct >= 40) return { grade: "C", icon: "🟠" };
111
+ return { grade: "D", icon: "🔴" };
112
+ }
113
+
114
+ // ── Recommendations ──
115
+ const recommendations: Record<string, { en: string; ko: string }> = {
116
+ "file-missing": {
117
+ en: "Create the missing file to ensure proper AI agent behavior.",
118
+ ko: "파일을 생성하여 AI 에이전트가 정상 동작하도록 하세요.",
119
+ },
120
+ "file-empty": {
121
+ en: "Add meaningful content to the file.",
122
+ ko: "파일에 유의미한 내용을 추가하세요.",
123
+ },
124
+ "file-invalid-json": {
125
+ en: "Fix the JSON syntax error. Use a JSON validator.",
126
+ ko: "JSON 구문 오류를 수정하세요. JSON 검증기를 사용해보세요.",
127
+ },
128
+ "file-missing-line": {
129
+ en: "Add the required pattern to the file.",
130
+ ko: "필수 패턴을 파일에 추가하세요.",
131
+ },
132
+ "file-contains": {
133
+ en: "Remove the unwanted content from the file.",
134
+ ko: "파일에서 비허용 콘텐츠를 제거하세요.",
135
+ },
136
+ };
137
+
138
+ function getRecommendation(patternType: string, lang: "en" | "ko"): string {
139
+ return recommendations[patternType]?.[lang] ?? (lang === "ko" ? "수동 확인이 필요합니다." : "Manual review required.");
140
+ }
141
+
142
+ // ── Patch Applier ──
143
+ function applyPatch(patch: PatchOp): boolean {
144
+ const filePath = patch.path.replace(/^\//, "");
145
+ if (patch.op === "add") {
146
+ if (existsSync(filePath)) return false;
147
+ const dir = dirname(filePath);
148
+ if (dir !== ".") mkdirSync(dir, { recursive: true });
149
+ writeFileSync(filePath, patch.value ?? "", "utf-8");
150
+ return true;
151
+ }
152
+ if (patch.op === "replace") {
153
+ const dir = dirname(filePath);
154
+ if (dir !== ".") mkdirSync(dir, { recursive: true });
155
+ writeFileSync(filePath, patch.value ?? "", "utf-8");
156
+ return true;
157
+ }
158
+ return false;
159
+ }
160
+
161
+ // ── Main Command ──
162
+ export async function doctorCommand(opts: DoctorOptions) {
163
+ const lang = getSystemLanguage();
164
+ const m = msgs[lang];
165
+
166
+ const rules = loadAllRules();
167
+ const builtInCount = rules.filter(r => r.id.startsWith("IMM-")).length;
168
+ const customCount = rules.length - builtInCount;
169
+
170
+ // Evaluate all rules (raw mode — ignore antibody immunization for full picture)
171
+ const result = evaluateRules(rules, [], { raw: true });
172
+ const totalChecks = result.symptoms.length + result.healthy.length;
173
+ const passedCount = result.healthy.length;
174
+ const failedCount = result.symptoms.length;
175
+ const { grade, icon } = healthGrade(passedCount, totalChecks);
176
+
177
+ const output: string[] = [];
178
+ output.push(line(BOX.tl, BOX.tr));
179
+ output.push(row(`${m.title}`));
180
+ output.push(divider());
181
+ output.push(kv(m.ruleCount, `${rules.length} (${builtInCount} ${m.builtIn} + ${customCount} ${m.custom})`));
182
+ output.push(kv(m.grade, `${icon} ${grade} (${passedCount}/${totalChecks})`));
183
+ output.push(kv(m.passed, `✅ ${passedCount}`));
184
+ output.push(kv(m.failed, `❌ ${failedCount}`));
185
+
186
+ if (failedCount === 0) {
187
+ output.push(divider());
188
+ output.push(row(`✅ ${m.healthy}`));
189
+ output.push(line(BOX.bl, BOX.br));
190
+ console.log(output.join("\n"));
191
+ return;
192
+ }
193
+
194
+ // ── Findings ──
195
+ output.push(divider());
196
+ output.push(row(`${m.findings}`));
197
+ output.push(row("─".repeat(W - 4)));
198
+
199
+ let fixableCount = 0;
200
+
201
+ for (const symptom of result.symptoms) {
202
+ const sevLabel = m.severity[symptom.severity];
203
+ const hasPatches = symptom.patches.length > 0;
204
+ if (hasPatches) fixableCount++;
205
+
206
+ output.push(row(`${severityIcon(symptom.severity)} [${sevLabel}] ${symptom.id}: ${symptom.title}`));
207
+ output.push(row(` ${symptom.fileTarget}`));
208
+ output.push(row(` ${m.recommendation}: ${getRecommendation(symptom.patternType, lang)}`));
209
+ if (hasPatches) output.push(row(` 🔧 ${m.fixable}`));
210
+ output.push(row(""));
211
+ }
212
+
213
+ // ── Fix Mode ──
214
+ if (opts.fix) {
215
+ output.push(divider());
216
+ let fixedCount = 0;
217
+ for (const symptom of result.symptoms) {
218
+ if (symptom.patches.length === 0) {
219
+ output.push(row(`⏭️ ${symptom.id}: ${m.fixSkipped}`));
220
+ continue;
221
+ }
222
+ let applied = false;
223
+ for (const patch of symptom.patches) {
224
+ if (applyPatch(patch)) applied = true;
225
+ }
226
+ if (applied) {
227
+ output.push(row(`✅ ${symptom.id}: ${m.fixApplied}`));
228
+ notifyAutoHeal(symptom.id);
229
+ fixedCount++;
230
+ } else {
231
+ output.push(row(`⏭️ ${symptom.id}: ${m.fixSkipped}`));
232
+ }
233
+ }
234
+ output.push(divider());
235
+ output.push(row(`🩺 ${m.fixSummary}: ${fixedCount}/${failedCount}`));
236
+ } else if (fixableCount > 0) {
237
+ output.push(divider());
238
+ output.push(row(`💡 ${m.fixHint.replace("{count}", String(fixableCount))}`));
239
+ }
240
+
241
+ output.push(line(BOX.bl, BOX.br));
242
+ console.log(output.join("\n"));
243
+ }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * afd evolution — Self-Evolution command
3
+ *
4
+ * Analyzes quarantined files, generates failure lessons,
5
+ * and writes them to afd-lessons.md for AI agent learning.
6
+ */
7
+
8
+ import { evolve, analyzeQuarantine, listQuarantine } from "../core/evolution";
9
+ import { generateValidators } from "../core/validator-generator";
10
+ import type { ValidatorGenInput } from "../core/validator-generator";
11
+ import { getSystemLanguage } from "../core/locale";
12
+
13
+ const msgs = {
14
+ en: {
15
+ title: "afd Self-Evolution Report",
16
+ noQuarantine: "No quarantined files found. Nothing to learn from.",
17
+ noPending: "All quarantined files already learned. No new lessons.",
18
+ analyzing: "Analyzing quarantined failures...",
19
+ written: (n: number) => `${n} new lesson(s) written to afd-lessons.md`,
20
+ total: (n: number) => `Total lessons learned: ${n}`,
21
+ stats: "Quarantine Stats",
22
+ quarantined: "Quarantined",
23
+ learned: "Learned",
24
+ pending: "Pending",
25
+ lessonDetail: "New Lessons",
26
+ genTitle: "Auto-Validator Generation",
27
+ genWritten: (n: number) => `${n} validator(s) generated in .afd/validators/`,
28
+ genSkipped: (n: number, reason: string) => `${n} skipped (${reason})`,
29
+ genNone: "No pending patterns to generate validators from.",
30
+ },
31
+ ko: {
32
+ title: "afd 자가 진화 리포트",
33
+ noQuarantine: "격리된 파일이 없습니다. 학습할 대상이 없습니다.",
34
+ noPending: "모든 격리 파일이 이미 학습되었습니다. 새로운 교훈이 없습니다.",
35
+ analyzing: "격리된 실패 사례 분석 중...",
36
+ written: (n: number) => `${n}개의 새로운 교훈이 afd-lessons.md에 기록되었습니다`,
37
+ total: (n: number) => `총 학습된 교훈: ${n}개`,
38
+ stats: "격리 통계",
39
+ quarantined: "격리됨",
40
+ learned: "학습 완료",
41
+ pending: "대기 중",
42
+ lessonDetail: "새로운 교훈",
43
+ genTitle: "자동 검증기 생성",
44
+ genWritten: (n: number) => `${n}개의 검증기가 .afd/validators/에 생성되었습니다`,
45
+ genSkipped: (n: number, reason: string) => `${n}개 건너뜀 (${reason})`,
46
+ genNone: "검증기를 생성할 대기 중인 패턴이 없습니다.",
47
+ },
48
+ };
49
+
50
+ const BOX = { tl: "┌", tr: "┐", bl: "└", br: "┘", h: "─", v: "│", ml: "├", mr: "┤" };
51
+ const W = 58;
52
+
53
+ function hline(l: string, r: string) { return `${l}${BOX.h.repeat(W)}${r}`; }
54
+ function row(s: string) {
55
+ const pad = Math.max(0, W - 2 - visualWidth(s));
56
+ return `${BOX.v} ${s}${" ".repeat(pad)} ${BOX.v}`;
57
+ }
58
+ function visualWidth(s: string): number {
59
+ let w = 0;
60
+ for (const ch of s) {
61
+ const cp = ch.codePointAt(0)!;
62
+ if ((cp >= 0x1100 && cp <= 0x11ff) || (cp >= 0x2e80 && cp <= 0x9fff) ||
63
+ (cp >= 0xac00 && cp <= 0xd7af) || (cp >= 0xf900 && cp <= 0xfaff) ||
64
+ (cp >= 0x1f000 && cp <= 0x1faff) || (cp >= 0x20000 && cp <= 0x2fa1f)) w += 2;
65
+ else w += 1;
66
+ }
67
+ return w;
68
+ }
69
+
70
+ export async function evolutionCommand(opts: { generate?: boolean } = {}) {
71
+ const lang = getSystemLanguage();
72
+ const m = msgs[lang];
73
+
74
+ const entries = listQuarantine();
75
+ if (entries.length === 0) {
76
+ console.log(m.noQuarantine);
77
+ return;
78
+ }
79
+
80
+ const stats = analyzeQuarantine();
81
+
82
+ // --generate: produce validators from ALL quarantine patterns (not just pending)
83
+ if (opts.generate) {
84
+ const allStats = analyzeQuarantine();
85
+ // Also include already-learned entries for validator generation
86
+ const allEntries = listQuarantine();
87
+ const inputs: ValidatorGenInput[] = allEntries.map(entry => {
88
+ const lesson = allStats.lessons.find(l => l.entry.quarantinePath === entry.quarantinePath);
89
+ if (lesson) {
90
+ return {
91
+ failureType: lesson.failureType,
92
+ originalPath: lesson.entry.originalPath,
93
+ corruptedContent: lesson.corruptedContent,
94
+ restoredContent: lesson.restoredContent,
95
+ };
96
+ }
97
+ // For already-learned entries, read quarantine file directly
98
+ const { readFileSync, existsSync } = require("fs");
99
+ const corruptedContent = readFileSync(entry.quarantinePath, "utf-8") as string;
100
+ const failureType = corruptedContent.trim() === "DELETED" ? "deletion" as const : "corruption" as const;
101
+ const restoredContent = existsSync(entry.originalPath)
102
+ ? readFileSync(entry.originalPath, "utf-8") as string
103
+ : null;
104
+ return { failureType, originalPath: entry.originalPath, corruptedContent, restoredContent };
105
+ });
106
+
107
+ if (inputs.length === 0) {
108
+ console.log(m.genNone);
109
+ return;
110
+ }
111
+
112
+ const results = generateValidators(inputs);
113
+ const written = results.filter(r => r.written);
114
+ const skipped = results.filter(r => !r.written);
115
+
116
+ console.log("");
117
+ console.log(hline(BOX.tl, BOX.tr));
118
+ console.log(row(`🧬 ${m.genTitle}`));
119
+ console.log(hline(BOX.ml, BOX.mr));
120
+
121
+ for (const r of written) {
122
+ console.log(row(` ✅ ${r.filename}`));
123
+ }
124
+ for (const r of skipped) {
125
+ console.log(row(` ⏭️ ${r.filename} (${r.reason})`));
126
+ }
127
+
128
+ console.log(hline(BOX.ml, BOX.mr));
129
+ console.log(row(m.genWritten(written.length)));
130
+ if (skipped.length > 0) {
131
+ console.log(row(m.genSkipped(skipped.length, "user-modified")));
132
+ }
133
+ console.log(hline(BOX.bl, BOX.br));
134
+ return;
135
+ }
136
+
137
+ if (stats.pending === 0) {
138
+ console.log(m.noPending);
139
+ return;
140
+ }
141
+
142
+ console.log(m.analyzing);
143
+ const result = evolve();
144
+
145
+ // Auto-generate validators for newly learned patterns
146
+ const genInputs: ValidatorGenInput[] = stats.lessons.map(lesson => ({
147
+ failureType: lesson.failureType,
148
+ originalPath: lesson.entry.originalPath,
149
+ corruptedContent: lesson.corruptedContent,
150
+ restoredContent: lesson.restoredContent,
151
+ }));
152
+ const genResults = generateValidators(genInputs);
153
+ const genWritten = genResults.filter(r => r.written);
154
+
155
+ console.log("");
156
+ console.log(hline(BOX.tl, BOX.tr));
157
+ console.log(row(`🧬 ${m.title}`));
158
+ console.log(hline(BOX.ml, BOX.mr));
159
+ console.log(row(`${m.stats}`));
160
+ console.log(row(` ${m.quarantined} : ${stats.totalQuarantined}`));
161
+ console.log(row(` ${m.learned} : ${result.totalLessons}`));
162
+ console.log(row(` ${m.pending} : 0`));
163
+ console.log(hline(BOX.ml, BOX.mr));
164
+ console.log(row(`${m.lessonDetail}`));
165
+ console.log(row(BOX.h.repeat(W - 4)));
166
+
167
+ for (const lesson of stats.lessons) {
168
+ const icon = lesson.failureType === "deletion" ? "🗑️" : "💥";
169
+ const file = lesson.entry.originalPath;
170
+ console.log(row(`${icon} ${file}`));
171
+ // Truncate suggestion to fit box
172
+ const maxSug = W - 8;
173
+ const sug = lesson.suggestion.length > maxSug
174
+ ? lesson.suggestion.slice(0, maxSug - 3) + "..."
175
+ : lesson.suggestion;
176
+ console.log(row(` ${sug}`));
177
+ }
178
+
179
+ console.log(hline(BOX.ml, BOX.mr));
180
+ console.log(row(m.written(result.lessonsWritten)));
181
+ console.log(row(m.total(result.totalLessons)));
182
+ if (genWritten.length > 0) {
183
+ console.log(hline(BOX.ml, BOX.mr));
184
+ console.log(row(`🧬 ${m.genWritten(genWritten.length)}`));
185
+ for (const r of genWritten) {
186
+ console.log(row(` ✅ ${r.filename}`));
187
+ }
188
+ }
189
+ console.log(hline(BOX.bl, BOX.br));
190
+ }