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
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Federation types and scope resolution for Team Antibody Federation (v1.7).
3
+ *
4
+ * Namespace: "<scope>/<id>" (e.g. "myorg/missing-import-guard")
5
+ * Versioning: monotonic integer + ISO-8601 timestamp (LWW tiebreak)
6
+ * Conflict policy: Remote-Wins-If-Newer (RWIN) — higher version wins;
7
+ * on tie, newer updatedAt wins; on tie, keep local.
8
+ */
9
+
10
+ export interface FederatedAntibody {
11
+ /** Local name portion, e.g. "missing-import-guard" */
12
+ id: string;
13
+ /** Namespace slug, e.g. "myorg". Defaults to "local" for non-federated antibodies. */
14
+ scope: string;
15
+ /** Globally unique: "<scope>/<id>" */
16
+ fqid: string;
17
+ patternType: string;
18
+ fileTarget: string;
19
+ patches: { op: string; path: string; value?: string }[];
20
+ /** Monotonic integer starting at 1; incremented on each content change */
21
+ version: number;
22
+ /** ISO 8601 — last mutation time (used as LWW tiebreaker) */
23
+ updatedAt: string;
24
+ /** ISO 8601 — original learning time (immutable) */
25
+ learnedAt: string;
26
+ }
27
+
28
+ export interface FederatedPayload {
29
+ /** Payload format version, e.g. "1.7" */
30
+ version: string;
31
+ generatedAt: string;
32
+ ecosystem: string;
33
+ /** Scope of the publishing team/repo */
34
+ scope: string;
35
+ antibodyCount: number;
36
+ antibodies: FederatedAntibody[];
37
+ }
38
+
39
+ /**
40
+ * Resolve the local scope for this workspace:
41
+ * 1. AFD_SCOPE env var (explicit override)
42
+ * 2. Git remote origin → org slug
43
+ * 3. Fallback: "local"
44
+ */
45
+ export function resolveScope(): string {
46
+ const envScope = process.env.AFD_SCOPE?.trim();
47
+ if (envScope) return slugify(envScope);
48
+
49
+ try {
50
+ const proc = Bun.spawnSync(["git", "remote", "get-url", "origin"], { stderr: "ignore" });
51
+ if (proc.exitCode === 0) {
52
+ const remoteUrl = new TextDecoder().decode(proc.stdout).trim();
53
+ return parseGitRemoteScope(remoteUrl) || "local";
54
+ }
55
+ } catch {
56
+ // no git binary or no remote configured
57
+ }
58
+
59
+ return "local";
60
+ }
61
+
62
+ function parseGitRemoteScope(remoteUrl: string): string {
63
+ try {
64
+ if (remoteUrl.startsWith("git@")) {
65
+ // git@github.com:myorg/repo.git
66
+ const colonPart = remoteUrl.split(":")[1] ?? "";
67
+ return slugify(colonPart.split("/")[0]);
68
+ }
69
+ const u = new URL(remoteUrl);
70
+ const firstSegment = u.pathname.replace(/^\//, "").split("/")[0] ?? "";
71
+ return slugify(firstSegment);
72
+ } catch {
73
+ return "";
74
+ }
75
+ }
76
+
77
+ function slugify(s: string): string {
78
+ return s.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
79
+ }
80
+
81
+ /**
82
+ * RWIN decision: should the incoming (remote) antibody overwrite the local one?
83
+ * Returns true if remote should win.
84
+ */
85
+ export function remoteWins(
86
+ remoteVersion: number,
87
+ remoteUpdatedAt: string,
88
+ localVersion: number,
89
+ localUpdatedAt: string,
90
+ ): boolean {
91
+ if (remoteVersion > localVersion) return true;
92
+ if (remoteVersion < localVersion) return false;
93
+ // Same version — use updatedAt as tiebreaker (newer wins)
94
+ return remoteUpdatedAt > localUpdatedAt;
95
+ }
96
+
97
+ /**
98
+ * Conflict arbitration for split-brain scenarios.
99
+ *
100
+ * Called when version AND updatedAt are identical but patch content differs
101
+ * (two daemons learned the same antibody concurrently and produced different patches).
102
+ *
103
+ * Strategy: deterministic lexicographic comparison of serialised patch content.
104
+ * The "larger" content string wins — ensures all peers converge to the same value
105
+ * without coordination, as long as both sides run the same function.
106
+ *
107
+ * Returns true if the remote patch should overwrite the local one.
108
+ */
109
+ export function arbitrateConflict(remotePatch: string, localPatch: string): boolean {
110
+ return remotePatch > localPatch;
111
+ }
112
+
113
+ /**
114
+ * Full conflict resolution combining RWIN + arbitration.
115
+ * Use this as the single decision point in /antibodies/learn.
116
+ */
117
+ export function shouldAcceptRemote(
118
+ remote: { version: number; updatedAt: string; patch: string },
119
+ local: { version: number; updatedAt: string; patch: string },
120
+ ): { accept: boolean; reason: "newer_version" | "newer_timestamp" | "arbitrated" | "local_wins" } {
121
+ if (remote.version > local.version) return { accept: true, reason: "newer_version" };
122
+ if (remote.version < local.version) return { accept: false, reason: "local_wins" };
123
+ // Same version
124
+ if (remote.updatedAt > local.updatedAt) return { accept: true, reason: "newer_timestamp" };
125
+ if (remote.updatedAt < local.updatedAt) return { accept: false, reason: "local_wins" };
126
+ // True split-brain: identical version + timestamp, different content
127
+ if (remote.patch === local.patch) return { accept: false, reason: "local_wins" };
128
+ return { accept: arbitrateConflict(remote.patch, local.patch), reason: "arbitrated" };
129
+ }
@@ -1,71 +1,71 @@
1
- import { Parser, Language, type Tree } from "web-tree-sitter";
2
- import { resolve, dirname } from "path";
3
-
4
- /**
5
- * Singleton Tree-sitter engine with grammar caching.
6
- * Parser.init() runs once; grammar WASMs are lazy-loaded and cached in-memory.
7
- */
8
- export class TreeSitterEngine {
9
- private static instance: TreeSitterEngine | null = null;
10
- private parser: Parser | null = null;
11
- private grammarCache = new Map<string, Language>();
12
- private initPromise: Promise<void> | null = null;
13
-
14
- static async getInstance(): Promise<TreeSitterEngine> {
15
- if (!this.instance) {
16
- this.instance = new TreeSitterEngine();
17
- await this.instance.init();
18
- }
19
- return this.instance;
20
- }
21
-
22
- /** Reset singleton — for testing only */
23
- static resetForTest(): void {
24
- if (this.instance?.parser) {
25
- this.instance.parser.delete();
26
- }
27
- this.instance = null;
28
- }
29
-
30
- private async init(): Promise<void> {
31
- if (this.initPromise) return this.initPromise;
32
- this.initPromise = (async () => {
33
- await Parser.init();
34
- this.parser = new Parser();
35
- })();
36
- return this.initPromise;
37
- }
38
-
39
- async parse(source: string, grammarName: string): Promise<Tree> {
40
- const grammar = await this.loadGrammar(grammarName);
41
- this.parser!.setLanguage(grammar);
42
- const tree = this.parser!.parse(source);
43
- if (!tree) throw new Error(`Failed to parse with grammar: ${grammarName}`);
44
- return tree;
45
- }
46
-
47
- private async loadGrammar(grammarName: string): Promise<Language> {
48
- const cached = this.grammarCache.get(grammarName);
49
- if (cached) return cached;
50
-
51
- const wasmPath = resolveGrammarWasm(grammarName);
52
- const lang = await Language.load(wasmPath);
53
- this.grammarCache.set(grammarName, lang);
54
- return lang;
55
- }
56
- }
57
-
58
- /** Resolve WASM file path from installed npm grammar package */
59
- function resolveGrammarWasm(grammarName: string): string {
60
- // Grammar packages: tree-sitter-typescript, tree-sitter-python, etc.
61
- // WASM file is at package root: node_modules/tree-sitter-{name}/tree-sitter-{name}.wasm
62
- const packageName = `tree-sitter-${grammarName}`;
63
- try {
64
- // require.resolve returns bindings/node/index.js — walk up to package root
65
- const pkgJson = require.resolve(`${packageName}/package.json`);
66
- return resolve(dirname(pkgJson), `${packageName}.wasm`);
67
- } catch {
68
- // Fallback: try direct node_modules path
69
- return resolve(process.cwd(), "node_modules", packageName, `${packageName}.wasm`);
70
- }
71
- }
1
+ import { Parser, Language, type Tree } from "web-tree-sitter";
2
+ import { resolve, dirname } from "path";
3
+
4
+ /**
5
+ * Singleton Tree-sitter engine with grammar caching.
6
+ * Parser.init() runs once; grammar WASMs are lazy-loaded and cached in-memory.
7
+ */
8
+ export class TreeSitterEngine {
9
+ private static instance: TreeSitterEngine | null = null;
10
+ private parser: Parser | null = null;
11
+ private grammarCache = new Map<string, Language>();
12
+ private initPromise: Promise<void> | null = null;
13
+
14
+ static async getInstance(): Promise<TreeSitterEngine> {
15
+ if (!this.instance) {
16
+ this.instance = new TreeSitterEngine();
17
+ await this.instance.init();
18
+ }
19
+ return this.instance;
20
+ }
21
+
22
+ /** Reset singleton — for testing only */
23
+ static resetForTest(): void {
24
+ if (this.instance?.parser) {
25
+ this.instance.parser.delete();
26
+ }
27
+ this.instance = null;
28
+ }
29
+
30
+ private async init(): Promise<void> {
31
+ if (this.initPromise) return this.initPromise;
32
+ this.initPromise = (async () => {
33
+ await Parser.init();
34
+ this.parser = new Parser();
35
+ })();
36
+ return this.initPromise;
37
+ }
38
+
39
+ async parse(source: string, grammarName: string): Promise<Tree> {
40
+ const grammar = await this.loadGrammar(grammarName);
41
+ this.parser!.setLanguage(grammar);
42
+ const tree = this.parser!.parse(source);
43
+ if (!tree) throw new Error(`Failed to parse with grammar: ${grammarName}`);
44
+ return tree;
45
+ }
46
+
47
+ private async loadGrammar(grammarName: string): Promise<Language> {
48
+ const cached = this.grammarCache.get(grammarName);
49
+ if (cached) return cached;
50
+
51
+ const wasmPath = resolveGrammarWasm(grammarName);
52
+ const lang = await Language.load(wasmPath);
53
+ this.grammarCache.set(grammarName, lang);
54
+ return lang;
55
+ }
56
+ }
57
+
58
+ /** Resolve WASM file path from installed npm grammar package */
59
+ function resolveGrammarWasm(grammarName: string): string {
60
+ // Grammar packages: tree-sitter-typescript, tree-sitter-python, etc.
61
+ // WASM file is at package root: node_modules/tree-sitter-{name}/tree-sitter-{name}.wasm
62
+ const packageName = `tree-sitter-${grammarName}`;
63
+ try {
64
+ // require.resolve returns bindings/node/index.js — walk up to package root
65
+ const pkgJson = require.resolve(`${packageName}/package.json`);
66
+ return resolve(dirname(pkgJson), `${packageName}.wasm`);
67
+ } catch {
68
+ // Fallback: try direct node_modules path
69
+ return resolve(process.cwd(), "node_modules", packageName, `${packageName}.wasm`);
70
+ }
71
+ }
@@ -1,11 +1,11 @@
1
- import type { HologramResult } from "./types";
2
-
3
- /** L0 fallback: return full source when tree-sitter cannot parse */
4
- export function fallbackL0(filePath: string, source: string): HologramResult {
5
- return {
6
- hologram: source,
7
- originalLength: source.length,
8
- hologramLength: source.length,
9
- savings: 0,
10
- };
11
- }
1
+ import type { HologramResult } from "./types";
2
+
3
+ /** L0 fallback: return full source when tree-sitter cannot parse */
4
+ export function fallbackL0(filePath: string, source: string): HologramResult {
5
+ return {
6
+ hologram: source,
7
+ originalLength: source.length,
8
+ hologramLength: source.length,
9
+ savings: 0,
10
+ };
11
+ }
@@ -0,0 +1,203 @@
1
+ import type { Node, Tree } from "web-tree-sitter";
2
+ import type { LanguageExtractor, HologramOptions } from "./types";
3
+
4
+ /**
5
+ * Collapse multiple whitespace/newlines into a single space.
6
+ */
7
+ function collapse(s: string): string {
8
+ return s.replace(/\s+/g, " ").trim();
9
+ }
10
+
11
+ /**
12
+ * Extract the text of a field, collapsing whitespace.
13
+ */
14
+ function fieldText(node: Node, field: string): string {
15
+ return collapse(node.childForFieldName(field)?.text ?? "");
16
+ }
17
+
18
+ /**
19
+ * Format a Go function/method signature with stubbed body.
20
+ * Slices source up to (but not including) the body block.
21
+ */
22
+ function stubGoBody(node: Node, source: string): string {
23
+ const body = node.childForFieldName("body");
24
+ if (!body) return collapse(node.text);
25
+ return collapse(source.slice(node.startIndex, body.startIndex).trimEnd()) + " {…}";
26
+ }
27
+
28
+ /**
29
+ * Extract a `function_declaration`:
30
+ * func Name(params) ReturnType {…}
31
+ */
32
+ function extractFunction(node: Node, source: string): string {
33
+ return stubGoBody(node, source);
34
+ }
35
+
36
+ /**
37
+ * Extract a `method_declaration`:
38
+ * func (r ReceiverType) Name(params) ReturnType {…}
39
+ */
40
+ function extractMethod(node: Node, source: string): string {
41
+ return stubGoBody(node, source);
42
+ }
43
+
44
+ /**
45
+ * Extract a `type_spec` node (the individual name+type inside a type declaration).
46
+ * Handles struct, interface, and other type aliases/definitions.
47
+ */
48
+ function extractTypeSpec(node: Node): string {
49
+ const name = fieldText(node, "name");
50
+ const typeNode = node.childForFieldName("type");
51
+ if (!typeNode) return `type ${name}`;
52
+
53
+ switch (typeNode.type) {
54
+ case "struct_type":
55
+ return extractStructType(name, typeNode);
56
+ case "interface_type":
57
+ return extractInterfaceType(name, typeNode);
58
+ default:
59
+ // type alias or other: e.g. `type MyInt int`, `type Handler func(...)`
60
+ return `type ${name} ${collapse(typeNode.text)}`;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Extract a struct type with all field declarations.
66
+ *
67
+ * Example output:
68
+ * type DaemonState struct {
69
+ * Running bool
70
+ * Pid int
71
+ * }
72
+ */
73
+ function extractStructType(name: string, structNode: Node): string {
74
+ const fieldList = structNode.namedChildren.find(c => c.type === "field_declaration_list");
75
+ if (!fieldList || fieldList.namedChildCount === 0) {
76
+ return `type ${name} struct {}`;
77
+ }
78
+
79
+ const fields: string[] = [];
80
+ for (const field of fieldList.namedChildren) {
81
+ if (field.type === "field_declaration") {
82
+ // Collect field names (may be multiple: `X, Y int`)
83
+ const names: string[] = [];
84
+ let typeNode: Node | null = null;
85
+ for (const child of field.namedChildren) {
86
+ if (child.type === "field_identifier" || child.type === "identifier") {
87
+ names.push(child.text);
88
+ } else {
89
+ typeNode = child;
90
+ }
91
+ }
92
+ const typeStr = typeNode ? collapse(typeNode.text) : "?";
93
+ if (names.length > 0) {
94
+ fields.push(` ${names.join(", ")} ${typeStr}`);
95
+ } else {
96
+ // Embedded field: just the type
97
+ fields.push(` ${typeStr}`);
98
+ }
99
+ } else if (field.type === "comment") {
100
+ // Skip comments inside struct
101
+ }
102
+ }
103
+
104
+ return `type ${name} struct {\n${fields.join("\n")}\n}`;
105
+ }
106
+
107
+ /**
108
+ * Extract an interface type with method/embedded type signatures.
109
+ *
110
+ * Example output:
111
+ * type Reader interface {
112
+ * Read(p []byte) (n int, err error)
113
+ * }
114
+ */
115
+ function extractInterfaceType(name: string, ifaceNode: Node): string {
116
+ // In tree-sitter-go, method_elem nodes are direct named children of interface_type
117
+ // (there is no intermediate interface_body wrapper node)
118
+ const members: string[] = [];
119
+ for (const child of ifaceNode.namedChildren) {
120
+ switch (child.type) {
121
+ case "method_elem":
122
+ case "interface_type_name":
123
+ case "type_constraint":
124
+ case "type_elem":
125
+ members.push(" " + collapse(child.text));
126
+ break;
127
+ case "comment":
128
+ break;
129
+ default:
130
+ members.push(" " + collapse(child.text));
131
+ }
132
+ }
133
+
134
+ if (members.length === 0) return `type ${name} interface {}`;
135
+ return `type ${name} interface {\n${members.join("\n")}\n}`;
136
+ }
137
+
138
+ /**
139
+ * Extract a `type_declaration` node.
140
+ * A single `type_declaration` may contain one or more `type_spec` nodes
141
+ * (the grouped `type (...)` form).
142
+ */
143
+ function extractTypeDeclaration(node: Node): string[] {
144
+ const specs = node.namedChildren.filter(c => c.type === "type_spec");
145
+ return specs.map(extractTypeSpec);
146
+ }
147
+
148
+ /**
149
+ * Extract an `import_declaration`.
150
+ * Handles both single-spec (`import "fmt"`) and grouped (`import (...)`).
151
+ */
152
+ function extractImport(node: Node): string {
153
+ return collapse(node.text);
154
+ }
155
+
156
+ /**
157
+ * Extract a `package_clause`.
158
+ */
159
+ function extractPackage(node: Node): string {
160
+ return collapse(node.text);
161
+ }
162
+
163
+ /**
164
+ * Process a single top-level declaration node.
165
+ * Returns one or more lines (type declarations may emit multiple lines).
166
+ */
167
+ function extractTopLevel(node: Node, source: string): string[] {
168
+ switch (node.type) {
169
+ case "package_clause":
170
+ return [extractPackage(node)];
171
+ case "import_declaration":
172
+ return [extractImport(node)];
173
+ case "function_declaration":
174
+ return [extractFunction(node, source)];
175
+ case "method_declaration":
176
+ return [extractMethod(node, source)];
177
+ case "type_declaration":
178
+ return extractTypeDeclaration(node);
179
+ case "comment":
180
+ // Skip top-level comments (doc comments) for compression
181
+ return [];
182
+ default:
183
+ // Variables, constants, and other top-level declarations: skip bodies,
184
+ // emit a stub if meaningful.
185
+ return [];
186
+ }
187
+ }
188
+
189
+ export const goExtractor: LanguageExtractor = {
190
+ extensions: ["go"],
191
+ grammarName: "go",
192
+
193
+ extract(tree: Tree, source: string, _options?: HologramOptions): string[] {
194
+ const lines: string[] = [];
195
+
196
+ for (const node of tree.rootNode.namedChildren) {
197
+ const extracted = extractTopLevel(node, source);
198
+ lines.push(...extracted);
199
+ }
200
+
201
+ return lines;
202
+ },
203
+ };