autonomous-flow-daemon 1.6.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 (61) hide show
  1. package/CHANGELOG.md +85 -85
  2. package/LICENSE +21 -21
  3. package/README-ko.md +282 -0
  4. package/README.md +282 -266
  5. package/mcp-config.json +10 -10
  6. package/package.json +4 -2
  7. package/src/adapters/index.ts +370 -370
  8. package/src/cli.ts +162 -127
  9. package/src/commands/benchmark.ts +187 -187
  10. package/src/commands/correlate.ts +180 -0
  11. package/src/commands/dashboard.ts +404 -0
  12. package/src/commands/evolution.ts +84 -1
  13. package/src/commands/fix.ts +158 -158
  14. package/src/commands/lang.ts +41 -41
  15. package/src/commands/plugin.ts +110 -0
  16. package/src/commands/restart.ts +14 -14
  17. package/src/commands/score.ts +276 -276
  18. package/src/commands/start.ts +155 -155
  19. package/src/commands/status.ts +157 -157
  20. package/src/commands/stop.ts +68 -68
  21. package/src/commands/suggest.ts +211 -0
  22. package/src/commands/sync.ts +329 -16
  23. package/src/constants.ts +32 -32
  24. package/src/core/boast.ts +280 -280
  25. package/src/core/config.ts +49 -49
  26. package/src/core/correlation-engine.ts +265 -0
  27. package/src/core/db.ts +145 -117
  28. package/src/core/discovery.ts +65 -65
  29. package/src/core/federation.ts +129 -0
  30. package/src/core/hologram/engine.ts +71 -71
  31. package/src/core/hologram/fallback.ts +11 -11
  32. package/src/core/hologram/go-extractor.ts +203 -0
  33. package/src/core/hologram/incremental.ts +227 -227
  34. package/src/core/hologram/py-extractor.ts +132 -132
  35. package/src/core/hologram/rust-extractor.ts +244 -0
  36. package/src/core/hologram/ts-extractor.ts +406 -320
  37. package/src/core/hologram/types.ts +27 -25
  38. package/src/core/hologram.ts +73 -71
  39. package/src/core/i18n/messages.ts +309 -309
  40. package/src/core/locale.ts +88 -88
  41. package/src/core/log-rotate.ts +33 -33
  42. package/src/core/log-utils.ts +38 -38
  43. package/src/core/lru-map.ts +61 -61
  44. package/src/core/notify.ts +74 -74
  45. package/src/core/plugin-manager.ts +225 -0
  46. package/src/core/rule-suggestion.ts +127 -0
  47. package/src/core/validator-generator.ts +224 -0
  48. package/src/core/workspace.ts +28 -28
  49. package/src/daemon/client.ts +78 -65
  50. package/src/daemon/event-batcher.ts +108 -108
  51. package/src/daemon/guards.ts +13 -13
  52. package/src/daemon/http-routes.ts +376 -293
  53. package/src/daemon/mcp-handler.ts +575 -270
  54. package/src/daemon/mcp-subscriptions.ts +81 -0
  55. package/src/daemon/mesh.ts +51 -0
  56. package/src/daemon/server.ts +655 -590
  57. package/src/daemon/types.ts +121 -100
  58. package/src/daemon/workspace-map.ts +104 -92
  59. package/src/platform.ts +60 -60
  60. package/src/version.ts +15 -15
  61. package/README.ko.md +0 -266
@@ -1,68 +1,68 @@
1
- import { getDaemonInfo, isDaemonAlive, daemonRequest } from "../daemon/client";
2
- import { unlinkSync } from "fs";
3
- import { resolveWorkspacePaths } from "../constants";
4
- import { formatShiftSummary } from "../core/boast";
5
- import type { ShiftSummary } from "../core/boast";
6
- import { getSystemLanguage } from "../core/locale";
7
- import { getMessages, t } from "../core/i18n/messages";
8
- import { detectEcosystem } from "../adapters/index";
9
-
10
- function cleanupFiles() {
11
- const paths = resolveWorkspacePaths();
12
- try { unlinkSync(paths.pidFile); } catch {}
13
- try { unlinkSync(paths.portFile); } catch {}
14
- }
15
-
16
- export async function stopCommand(options?: { clean?: boolean }) {
17
- const lang = getSystemLanguage();
18
- const msg = getMessages(lang);
19
- const info = getDaemonInfo();
20
-
21
- if (!info) {
22
- console.log(msg.DAEMON_NOT_RUNNING);
23
- return;
24
- }
25
-
26
- if (await isDaemonAlive(info)) {
27
- // Fetch shift summary before stopping
28
- try {
29
- const summary = await daemonRequest<ShiftSummary>("/shift-summary");
30
- console.log(formatShiftSummary(summary, lang));
31
- } catch {
32
- // Non-fatal: summary is a nicety, not a requirement
33
- }
34
-
35
- try {
36
- await daemonRequest("/stop");
37
- console.log(t(msg.DAEMON_STOPPED, { pid: info.pid }));
38
- } catch {
39
- try {
40
- process.kill(info.pid, "SIGTERM");
41
- console.log(t(msg.DAEMON_KILLED, { pid: info.pid }));
42
- } catch {
43
- console.log("[afd] Daemon process already gone.");
44
- }
45
- }
46
- } else {
47
- console.log(msg.DAEMON_NOT_RESPONDING);
48
- }
49
-
50
- cleanupFiles();
51
-
52
- // --clean: remove injected hooks and MCP registration
53
- if (options?.clean) {
54
- const cwd = process.cwd();
55
- const ecosystems = detectEcosystem(cwd);
56
- for (const { adapter } of ecosystems) {
57
- if (adapter.removeHooks) {
58
- const r = adapter.removeHooks(cwd);
59
- if (r.removed) console.log(`[afd] ${r.message}`);
60
- }
61
- if (adapter.unregisterMcp) {
62
- const r = adapter.unregisterMcp(cwd);
63
- if (r.removed) console.log(`[afd] ${r.message}`);
64
- }
65
- }
66
- console.log("[afd] Clean stop complete. All afd integrations removed.");
67
- }
68
- }
1
+ import { getDaemonInfo, isDaemonAlive, daemonRequest } from "../daemon/client";
2
+ import { unlinkSync } from "fs";
3
+ import { resolveWorkspacePaths } from "../constants";
4
+ import { formatShiftSummary } from "../core/boast";
5
+ import type { ShiftSummary } from "../core/boast";
6
+ import { getSystemLanguage } from "../core/locale";
7
+ import { getMessages, t } from "../core/i18n/messages";
8
+ import { detectEcosystem } from "../adapters/index";
9
+
10
+ function cleanupFiles() {
11
+ const paths = resolveWorkspacePaths();
12
+ try { unlinkSync(paths.pidFile); } catch {}
13
+ try { unlinkSync(paths.portFile); } catch {}
14
+ }
15
+
16
+ export async function stopCommand(options?: { clean?: boolean }) {
17
+ const lang = getSystemLanguage();
18
+ const msg = getMessages(lang);
19
+ const info = getDaemonInfo();
20
+
21
+ if (!info) {
22
+ console.log(msg.DAEMON_NOT_RUNNING);
23
+ return;
24
+ }
25
+
26
+ if (await isDaemonAlive(info)) {
27
+ // Fetch shift summary before stopping
28
+ try {
29
+ const summary = await daemonRequest<ShiftSummary>("/shift-summary");
30
+ console.log(formatShiftSummary(summary, lang));
31
+ } catch {
32
+ // Non-fatal: summary is a nicety, not a requirement
33
+ }
34
+
35
+ try {
36
+ await daemonRequest("/stop");
37
+ console.log(t(msg.DAEMON_STOPPED, { pid: info.pid }));
38
+ } catch {
39
+ try {
40
+ process.kill(info.pid, "SIGTERM");
41
+ console.log(t(msg.DAEMON_KILLED, { pid: info.pid }));
42
+ } catch {
43
+ console.log("[afd] Daemon process already gone.");
44
+ }
45
+ }
46
+ } else {
47
+ console.log(msg.DAEMON_NOT_RESPONDING);
48
+ }
49
+
50
+ cleanupFiles();
51
+
52
+ // --clean: remove injected hooks and MCP registration
53
+ if (options?.clean) {
54
+ const cwd = process.cwd();
55
+ const ecosystems = detectEcosystem(cwd);
56
+ for (const { adapter } of ecosystems) {
57
+ if (adapter.removeHooks) {
58
+ const r = adapter.removeHooks(cwd);
59
+ if (r.removed) console.log(`[afd] ${r.message}`);
60
+ }
61
+ if (adapter.unregisterMcp) {
62
+ const r = adapter.unregisterMcp(cwd);
63
+ if (r.removed) console.log(`[afd] ${r.message}`);
64
+ }
65
+ }
66
+ console.log("[afd] Clean stop complete. All afd integrations removed.");
67
+ }
68
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * afd suggest — Rule Suggestion Engine CLI
3
+ *
4
+ * Analyzes mistake_history to recommend auto-validator generation
5
+ * for frequently recurring failure patterns.
6
+ */
7
+
8
+ import { suggestRules } from "../core/rule-suggestion";
9
+ import { correlatePatterns, findMatchingHotspot } from "../core/correlation-engine";
10
+ import { generateValidator } from "../core/validator-generator";
11
+ import type { ValidatorGenInput } from "../core/validator-generator";
12
+ import { initDb } from "../core/db";
13
+ import { getSystemLanguage } from "../core/locale";
14
+ import { existsSync, readFileSync } from "fs";
15
+
16
+ interface SuggestOptions {
17
+ days?: string;
18
+ min?: string;
19
+ apply?: boolean;
20
+ /** Annotate suggestions matching cross-project hotspots as "Community Verified" */
21
+ cross?: boolean;
22
+ }
23
+
24
+ const msgs = {
25
+ en: {
26
+ title: "afd suggest — Rule Suggestion Engine",
27
+ noData: "No recurring patterns found. Keep working — the engine learns from mistakes over time.",
28
+ header: "Recommended validators based on failure history:",
29
+ frequency: "occurrences",
30
+ lastSeen: "last seen",
31
+ covered: "already covered",
32
+ uncovered: "no validator",
33
+ applyTitle: "Auto-generating validators for uncovered patterns...",
34
+ applyDone: (n: number) => `${n} validator(s) generated. Daemon will hot-reload automatically.`,
35
+ applySkipped: (n: number) => `${n} pattern(s) already covered — skipped.`,
36
+ hint: "Run `afd suggest --apply` to auto-generate validators for uncovered patterns.",
37
+ daysLabel: "Analysis window",
38
+ minLabel: "Minimum frequency",
39
+ communityVerified: "Community Verified",
40
+ crossHint: "🌐 = Community Verified (pattern seen in multiple projects). Run `afd correlate` for details.",
41
+ },
42
+ ko: {
43
+ title: "afd suggest — 규칙 추천 엔진",
44
+ noData: "반복 패턴을 찾지 못했습니다. 작업을 계속하세요 — 엔진이 시간이 지나면 실수에서 학습합니다.",
45
+ header: "실패 이력 기반 추천 검증기:",
46
+ frequency: "회 발생",
47
+ lastSeen: "최근",
48
+ covered: "이미 보호됨",
49
+ uncovered: "검증기 없음",
50
+ applyTitle: "미보호 패턴에 대해 검증기 자동 생성 중...",
51
+ applyDone: (n: number) => `${n}개 검증기 생성 완료. 데몬이 자동으로 핫 리로드합니다.`,
52
+ applySkipped: (n: number) => `${n}개 패턴은 이미 보호됨 — 건너뜀.`,
53
+ hint: "`afd suggest --apply`를 실행하여 미보호 패턴의 검증기를 자동 생성하세요.",
54
+ daysLabel: "분석 기간",
55
+ minLabel: "최소 빈도",
56
+ communityVerified: "커뮤니티 검증됨",
57
+ crossHint: "🌐 = 커뮤니티 검증됨 (여러 프로젝트에서 발견된 패턴). 자세히: `afd correlate`",
58
+ },
59
+ };
60
+
61
+ const BOX = { tl: "┌", tr: "┐", bl: "└", br: "┘", h: "─", v: "│", ml: "├", mr: "┤" };
62
+ const W = 62;
63
+
64
+ function hline(l: string, r: string) { return `${l}${BOX.h.repeat(W)}${r}`; }
65
+ function row(s: string) {
66
+ // Simple padding — doesn't account for wide chars but good enough for ASCII
67
+ const pad = Math.max(0, W - 2 - s.length);
68
+ return `${BOX.v} ${s}${" ".repeat(pad)} ${BOX.v}`;
69
+ }
70
+
71
+ function formatDate(ts: number): string {
72
+ const d = new Date(ts);
73
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
74
+ }
75
+
76
+ export async function suggestCommand(opts: SuggestOptions = {}) {
77
+ const lang = getSystemLanguage();
78
+ const m = msgs[lang];
79
+
80
+ const days = parseInt(opts.days ?? "30", 10) || 30;
81
+ const minFreq = parseInt(opts.min ?? "3", 10) || 3;
82
+
83
+ const db = initDb();
84
+ try {
85
+ const suggestions = suggestRules(db, { days, minFrequency: minFreq, limit: 10 });
86
+
87
+ // Cross-project correlation: load hotspots once if --cross is set
88
+ const hotspots = opts.cross ? correlatePatterns(db, { minScopes: 2, limit: 50 }).hotspots : [];
89
+
90
+ if (suggestions.length === 0) {
91
+ console.log(`[afd suggest] ${m.noData}`);
92
+ return;
93
+ }
94
+
95
+ // ── Apply mode: generate validators for uncovered patterns ──
96
+ if (opts.apply) {
97
+ const uncovered = suggestions.filter(s => !s.alreadyCovered);
98
+ const covered = suggestions.filter(s => s.alreadyCovered);
99
+
100
+ if (uncovered.length === 0) {
101
+ console.log(`[afd suggest] ${m.applySkipped(covered.length)}`);
102
+ return;
103
+ }
104
+
105
+ console.log(`[afd suggest] ${m.applyTitle}`);
106
+
107
+ let generated = 0;
108
+ for (const s of uncovered) {
109
+ // Build a ValidatorGenInput from the suggestion
110
+ const input = buildGenInput(s);
111
+ if (!input) continue;
112
+ const result = generateValidator(input);
113
+ if (result.written) {
114
+ console.log(` ✅ ${result.filename}`);
115
+ generated++;
116
+ }
117
+ }
118
+
119
+ console.log(`[afd suggest] ${m.applyDone(generated)}`);
120
+ if (covered.length > 0) {
121
+ console.log(`[afd suggest] ${m.applySkipped(covered.length)}`);
122
+ }
123
+ return;
124
+ }
125
+
126
+ // ── Display mode: show ranked suggestions ──
127
+ console.log("");
128
+ console.log(hline(BOX.tl, BOX.tr));
129
+ console.log(row(`🔍 ${m.title}`));
130
+ console.log(hline(BOX.ml, BOX.mr));
131
+ console.log(row(`${m.daysLabel}: ${days}d | ${m.minLabel}: ${minFreq}`));
132
+ console.log(hline(BOX.ml, BOX.mr));
133
+
134
+ let hasCrossAnnotation = false;
135
+ for (let i = 0; i < suggestions.length; i++) {
136
+ const s = suggestions[i];
137
+ const statusIcon = s.alreadyCovered ? "🛡️" : "⚠️";
138
+ const statusText = s.alreadyCovered ? m.covered : m.uncovered;
139
+ const rank = `#${i + 1}`;
140
+
141
+ // Cross-project annotation
142
+ const matchedHotspot = hotspots.length > 0 ? findMatchingHotspot(s.mistakeType, hotspots) : null;
143
+ const crossBadge = matchedHotspot ? ` 🌐` : "";
144
+ if (matchedHotspot) hasCrossAnnotation = true;
145
+
146
+ console.log(row(`${rank} ${statusIcon} ${s.filePath}${crossBadge}`));
147
+ console.log(row(` ${s.mistakeType} — ${s.frequency} ${m.frequency}`));
148
+ if (matchedHotspot) {
149
+ console.log(row(` 🌐 ${m.communityVerified} (${matchedHotspot.scopeCount} projects)`));
150
+ }
151
+ console.log(row(` ${m.lastSeen}: ${formatDate(s.lastSeen)} | ${statusText}`));
152
+
153
+ // Truncate description
154
+ const maxDesc = W - 8;
155
+ const desc = s.description.length > maxDesc ? s.description.slice(0, maxDesc - 3) + "..." : s.description;
156
+ console.log(row(` ${desc}`));
157
+
158
+ if (i < suggestions.length - 1) {
159
+ console.log(row(""));
160
+ }
161
+ }
162
+
163
+ const uncoveredCount = suggestions.filter(s => !s.alreadyCovered).length;
164
+ if (uncoveredCount > 0 || hasCrossAnnotation) {
165
+ console.log(hline(BOX.ml, BOX.mr));
166
+ if (uncoveredCount > 0) console.log(row(`💡 ${m.hint}`));
167
+ if (hasCrossAnnotation) console.log(row(`💡 ${m.crossHint}`));
168
+ }
169
+ console.log(hline(BOX.bl, BOX.br));
170
+ } finally {
171
+ db.close();
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Build a ValidatorGenInput from a suggestion.
177
+ * Uses the mistake_type to infer the failure type and creates a synthetic input
178
+ * so the validator-generator can produce the correct template.
179
+ */
180
+ function buildGenInput(s: { filePath: string; mistakeType: string; description: string }): ValidatorGenInput | null {
181
+ const filePath = s.filePath;
182
+
183
+ // Map mistake_type to failureType + synthetic content
184
+ if (s.mistakeType.includes("deletion") || s.mistakeType.includes("delete")) {
185
+ return { failureType: "deletion", originalPath: filePath, corruptedContent: "DELETED", restoredContent: readFileSafe(filePath) };
186
+ }
187
+
188
+ if (s.mistakeType.includes("empty") || s.mistakeType.includes("blank")) {
189
+ return { failureType: "corruption", originalPath: filePath, corruptedContent: "", restoredContent: readFileSafe(filePath) };
190
+ }
191
+
192
+ if (s.mistakeType.includes("truncat")) {
193
+ // Simulate severe truncation
194
+ const restored = readFileSafe(filePath);
195
+ return { failureType: "corruption", originalPath: filePath, corruptedContent: "x", restoredContent: restored };
196
+ }
197
+
198
+ if (s.mistakeType.includes("json") || s.mistakeType.includes("syntax")) {
199
+ return { failureType: "corruption", originalPath: filePath, corruptedContent: "{invalid", restoredContent: readFileSafe(filePath) };
200
+ }
201
+
202
+ // Generic corruption
203
+ return { failureType: "corruption", originalPath: filePath, corruptedContent: "corrupted", restoredContent: readFileSafe(filePath) };
204
+ }
205
+
206
+ function readFileSafe(path: string): string | null {
207
+ try {
208
+ if (existsSync(path)) return readFileSync(path, "utf-8");
209
+ } catch { /* ignore */ }
210
+ return null;
211
+ }