@cortexkit/aft-opencode 0.2.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 (69) hide show
  1. package/dist/bridge.d.ts +43 -0
  2. package/dist/bridge.d.ts.map +1 -0
  3. package/dist/bridge.js +194 -0
  4. package/dist/bridge.js.map +1 -0
  5. package/dist/config.d.ts +42 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/config.js +180 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/downloader.d.ts +32 -0
  10. package/dist/downloader.d.ts.map +1 -0
  11. package/dist/downloader.js +140 -0
  12. package/dist/downloader.js.map +1 -0
  13. package/dist/index.d.ts +21 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +62 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/lsp.d.ts +32 -0
  18. package/dist/lsp.d.ts.map +1 -0
  19. package/dist/lsp.js +71 -0
  20. package/dist/lsp.js.map +1 -0
  21. package/dist/pool.d.ts +33 -0
  22. package/dist/pool.d.ts.map +1 -0
  23. package/dist/pool.js +86 -0
  24. package/dist/pool.js.map +1 -0
  25. package/dist/resolver.d.ts +36 -0
  26. package/dist/resolver.d.ts.map +1 -0
  27. package/dist/resolver.js +122 -0
  28. package/dist/resolver.js.map +1 -0
  29. package/dist/tools/ast.d.ts +9 -0
  30. package/dist/tools/ast.d.ts.map +1 -0
  31. package/dist/tools/ast.js +102 -0
  32. package/dist/tools/ast.js.map +1 -0
  33. package/dist/tools/editing.d.ts +7 -0
  34. package/dist/tools/editing.d.ts.map +1 -0
  35. package/dist/tools/editing.js +150 -0
  36. package/dist/tools/editing.js.map +1 -0
  37. package/dist/tools/imports.d.ts +7 -0
  38. package/dist/tools/imports.d.ts.map +1 -0
  39. package/dist/tools/imports.js +67 -0
  40. package/dist/tools/imports.js.map +1 -0
  41. package/dist/tools/lsp.d.ts +8 -0
  42. package/dist/tools/lsp.d.ts.map +1 -0
  43. package/dist/tools/lsp.js +120 -0
  44. package/dist/tools/lsp.js.map +1 -0
  45. package/dist/tools/navigation.d.ts +7 -0
  46. package/dist/tools/navigation.d.ts.map +1 -0
  47. package/dist/tools/navigation.js +46 -0
  48. package/dist/tools/navigation.js.map +1 -0
  49. package/dist/tools/reading.d.ts +7 -0
  50. package/dist/tools/reading.d.ts.map +1 -0
  51. package/dist/tools/reading.js +103 -0
  52. package/dist/tools/reading.js.map +1 -0
  53. package/dist/tools/refactoring.d.ts +7 -0
  54. package/dist/tools/refactoring.d.ts.map +1 -0
  55. package/dist/tools/refactoring.js +80 -0
  56. package/dist/tools/refactoring.js.map +1 -0
  57. package/dist/tools/safety.d.ts +8 -0
  58. package/dist/tools/safety.d.ts.map +1 -0
  59. package/dist/tools/safety.js +53 -0
  60. package/dist/tools/safety.js.map +1 -0
  61. package/dist/tools/structure.d.ts +8 -0
  62. package/dist/tools/structure.d.ts.map +1 -0
  63. package/dist/tools/structure.js +107 -0
  64. package/dist/tools/structure.js.map +1 -0
  65. package/dist/types.d.ts +13 -0
  66. package/dist/types.d.ts.map +1 -0
  67. package/dist/types.js +2 -0
  68. package/dist/types.js.map +1 -0
  69. package/package.json +40 -0
@@ -0,0 +1,43 @@
1
+ export interface BridgeOptions {
2
+ /** Request timeout in milliseconds. Default: 30000 */
3
+ timeoutMs?: number;
4
+ /** Maximum restart attempts before giving up. Default: 3 */
5
+ maxRestarts?: number;
6
+ }
7
+ /**
8
+ * Manages a persistent `aft` child process, communicating via NDJSON over
9
+ * stdin/stdout. Lazy-spawns on first `send()` call. Handles crash detection
10
+ * with exponential backoff auto-restart.
11
+ */
12
+ export declare class BinaryBridge {
13
+ private binaryPath;
14
+ private cwd;
15
+ private process;
16
+ private pending;
17
+ private nextId;
18
+ private stdoutBuffer;
19
+ private _restartCount;
20
+ private _shuttingDown;
21
+ private timeoutMs;
22
+ private maxRestarts;
23
+ private configured;
24
+ private configOverrides;
25
+ constructor(binaryPath: string, cwd: string, options?: BridgeOptions, configOverrides?: Record<string, unknown>);
26
+ /** Number of times the binary has been restarted after a crash. */
27
+ get restartCount(): number;
28
+ /** Whether the child process is currently alive. */
29
+ isAlive(): boolean;
30
+ /**
31
+ * Send a command to the binary and return the parsed response.
32
+ * Lazy-spawns the binary on first call.
33
+ */
34
+ send(command: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
35
+ /** Kill the child process and reject all pending requests. */
36
+ shutdown(): Promise<void>;
37
+ private ensureSpawned;
38
+ private spawnProcess;
39
+ private onStdoutData;
40
+ private handleCrash;
41
+ private rejectAllPending;
42
+ }
43
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAA0B;gBAG/C,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAS3C,mEAAmE;IACnE,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,oDAAoD;IACpD,OAAO,IAAI,OAAO;IAIlB;;;OAGG;IACG,IAAI,CACR,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAoDnC,8DAA8D;IACxD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B/B,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,YAAY;IAkCpB,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,gBAAgB;CAOzB"}
package/dist/bridge.js ADDED
@@ -0,0 +1,194 @@
1
+ import { spawn } from "node:child_process";
2
+ /** Prefix for all bridge diagnostic messages on stderr. */
3
+ const TAG = "[aft-plugin]";
4
+ /**
5
+ * Manages a persistent `aft` child process, communicating via NDJSON over
6
+ * stdin/stdout. Lazy-spawns on first `send()` call. Handles crash detection
7
+ * with exponential backoff auto-restart.
8
+ */
9
+ export class BinaryBridge {
10
+ binaryPath;
11
+ cwd;
12
+ process = null;
13
+ pending = new Map();
14
+ nextId = 1;
15
+ stdoutBuffer = "";
16
+ _restartCount = 0;
17
+ _shuttingDown = false;
18
+ timeoutMs;
19
+ maxRestarts;
20
+ configured = false;
21
+ configOverrides;
22
+ constructor(binaryPath, cwd, options, configOverrides) {
23
+ this.binaryPath = binaryPath;
24
+ this.cwd = cwd;
25
+ this.timeoutMs = options?.timeoutMs ?? 30_000;
26
+ this.maxRestarts = options?.maxRestarts ?? 3;
27
+ this.configOverrides = configOverrides ?? {};
28
+ }
29
+ /** Number of times the binary has been restarted after a crash. */
30
+ get restartCount() {
31
+ return this._restartCount;
32
+ }
33
+ /** Whether the child process is currently alive. */
34
+ isAlive() {
35
+ return this.process !== null && this.process.exitCode === null && !this.process.killed;
36
+ }
37
+ /**
38
+ * Send a command to the binary and return the parsed response.
39
+ * Lazy-spawns the binary on first call.
40
+ */
41
+ async send(command, params = {}) {
42
+ if (this._shuttingDown) {
43
+ throw new Error(`${TAG} Bridge is shutting down, cannot send "${command}"`);
44
+ }
45
+ this.ensureSpawned();
46
+ // Auto-configure project root + plugin config on first command
47
+ if (!this.configured) {
48
+ this.configured = true;
49
+ if (command !== "configure") {
50
+ await this.send("configure", {
51
+ project_root: this.cwd,
52
+ ...this.configOverrides,
53
+ });
54
+ }
55
+ }
56
+ const id = String(this.nextId++);
57
+ const request = { id, command, ...params };
58
+ const line = `${JSON.stringify(request)}\n`;
59
+ return new Promise((resolve, reject) => {
60
+ const timer = setTimeout(() => {
61
+ this.pending.delete(id);
62
+ reject(new Error(`${TAG} Request "${command}" (id=${id}) timed out after ${this.timeoutMs}ms`));
63
+ }, this.timeoutMs);
64
+ this.pending.set(id, { resolve, reject, timer });
65
+ if (!this.process?.stdin?.writable) {
66
+ this.pending.delete(id);
67
+ clearTimeout(timer);
68
+ reject(new Error(`${TAG} stdin not writable for command "${command}"`));
69
+ return;
70
+ }
71
+ this.process.stdin.write(line, (err) => {
72
+ if (err) {
73
+ const entry = this.pending.get(id);
74
+ if (entry) {
75
+ this.pending.delete(id);
76
+ clearTimeout(entry.timer);
77
+ entry.reject(new Error(`${TAG} Failed to write to stdin: ${err.message}`));
78
+ }
79
+ }
80
+ });
81
+ });
82
+ }
83
+ /** Kill the child process and reject all pending requests. */
84
+ async shutdown() {
85
+ this._shuttingDown = true;
86
+ this.rejectAllPending(new Error(`${TAG} Bridge shutting down`));
87
+ if (this.process) {
88
+ const proc = this.process;
89
+ this.process = null;
90
+ return new Promise((resolve) => {
91
+ const forceKillTimer = setTimeout(() => {
92
+ proc.kill("SIGKILL");
93
+ resolve();
94
+ }, 5_000);
95
+ proc.once("exit", () => {
96
+ clearTimeout(forceKillTimer);
97
+ console.error(`${TAG} Process exited during shutdown`);
98
+ resolve();
99
+ });
100
+ proc.kill("SIGTERM");
101
+ });
102
+ }
103
+ }
104
+ // ---- Internal ----
105
+ ensureSpawned() {
106
+ if (this.isAlive())
107
+ return;
108
+ this.spawnProcess();
109
+ }
110
+ spawnProcess() {
111
+ console.error(`${TAG} Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
112
+ const child = spawn(this.binaryPath, [], {
113
+ cwd: this.cwd,
114
+ stdio: ["pipe", "pipe", "pipe"],
115
+ });
116
+ child.stdout?.on("data", (chunk) => {
117
+ this.onStdoutData(chunk.toString("utf-8"));
118
+ });
119
+ child.stderr?.on("data", (chunk) => {
120
+ const lines = chunk.toString("utf-8").trimEnd().split("\n");
121
+ for (const line of lines) {
122
+ console.error(`${TAG} stderr: ${line}`);
123
+ }
124
+ });
125
+ child.on("error", (err) => {
126
+ console.error(`${TAG} Process error: ${err.message}`);
127
+ this.handleCrash();
128
+ });
129
+ child.on("exit", (code, signal) => {
130
+ if (this._shuttingDown)
131
+ return;
132
+ console.error(`${TAG} Process exited: code=${code}, signal=${signal}`);
133
+ this.handleCrash();
134
+ });
135
+ this.process = child;
136
+ this.stdoutBuffer = "";
137
+ }
138
+ onStdoutData(data) {
139
+ this.stdoutBuffer += data;
140
+ // Process complete lines
141
+ let newlineIdx;
142
+ while ((newlineIdx = this.stdoutBuffer.indexOf("\n")) !== -1) {
143
+ const line = this.stdoutBuffer.slice(0, newlineIdx).trim();
144
+ this.stdoutBuffer = this.stdoutBuffer.slice(newlineIdx + 1);
145
+ if (!line)
146
+ continue;
147
+ try {
148
+ const response = JSON.parse(line);
149
+ const id = response.id;
150
+ if (id && this.pending.has(id)) {
151
+ const entry = this.pending.get(id);
152
+ this.pending.delete(id);
153
+ clearTimeout(entry.timer);
154
+ entry.resolve(response);
155
+ }
156
+ }
157
+ catch (_err) {
158
+ console.error(`${TAG} Failed to parse stdout line: ${line}`);
159
+ }
160
+ }
161
+ }
162
+ handleCrash() {
163
+ this.process = null;
164
+ // Reject all pending requests
165
+ this.rejectAllPending(new Error(`${TAG} Binary crashed (restarts: ${this._restartCount})`));
166
+ // Auto-restart with exponential backoff
167
+ if (this._restartCount < this.maxRestarts) {
168
+ const delay = 100 * 2 ** this._restartCount; // 100ms, 200ms, 400ms
169
+ this._restartCount++;
170
+ console.error(`${TAG} Auto-restart #${this._restartCount} in ${delay}ms`);
171
+ setTimeout(() => {
172
+ if (!this._shuttingDown) {
173
+ try {
174
+ this.spawnProcess();
175
+ }
176
+ catch (err) {
177
+ console.error(`${TAG} Failed to restart: ${err.message}`);
178
+ }
179
+ }
180
+ }, delay);
181
+ }
182
+ else {
183
+ console.error(`${TAG} Max restarts (${this.maxRestarts}) reached, giving up`);
184
+ }
185
+ }
186
+ rejectAllPending(error) {
187
+ for (const [_id, entry] of this.pending) {
188
+ clearTimeout(entry.timer);
189
+ entry.reject(error);
190
+ }
191
+ this.pending.clear();
192
+ }
193
+ }
194
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ export declare const AftConfigSchema: z.ZodObject<{
3
+ format_on_edit: z.ZodOptional<z.ZodBoolean>;
4
+ validate_on_edit: z.ZodOptional<z.ZodEnum<{
5
+ syntax: "syntax";
6
+ full: "full";
7
+ }>>;
8
+ formatter: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
9
+ biome: "biome";
10
+ prettier: "prettier";
11
+ deno: "deno";
12
+ ruff: "ruff";
13
+ black: "black";
14
+ rustfmt: "rustfmt";
15
+ goimports: "goimports";
16
+ gofmt: "gofmt";
17
+ none: "none";
18
+ }>>>;
19
+ checker: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
20
+ biome: "biome";
21
+ ruff: "ruff";
22
+ none: "none";
23
+ tsc: "tsc";
24
+ pyright: "pyright";
25
+ cargo: "cargo";
26
+ go: "go";
27
+ staticcheck: "staticcheck";
28
+ }>>>;
29
+ }, z.core.$strip>;
30
+ export type AftConfig = z.infer<typeof AftConfigSchema>;
31
+ /**
32
+ * Load AFT config using the same two-level pattern as oh-my-opencode:
33
+ *
34
+ * 1. User-level: ~/.config/opencode/aft.jsonc (or .json)
35
+ * 2. Project-level: <project>/.opencode/aft.jsonc (or .json)
36
+ *
37
+ * Project config merges on top of user config.
38
+ * Both support JSONC (comments allowed).
39
+ * Invalid sections are skipped, valid sections still load.
40
+ */
41
+ export declare function loadAftConfig(projectDirectory: string): AftConfig;
42
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA6BxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAS1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AA6IxD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAwBjE"}
package/dist/config.js ADDED
@@ -0,0 +1,180 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { parse, printParseErrorCode } from "jsonc-parser";
4
+ import { z } from "zod";
5
+ // ---------------------------------------------------------------------------
6
+ // Zod schema
7
+ // ---------------------------------------------------------------------------
8
+ const FormatterEnum = z.enum([
9
+ "biome",
10
+ "prettier",
11
+ "deno",
12
+ "ruff",
13
+ "black",
14
+ "rustfmt",
15
+ "goimports",
16
+ "gofmt",
17
+ "none",
18
+ ]);
19
+ const CheckerEnum = z.enum([
20
+ "tsc",
21
+ "biome",
22
+ "pyright",
23
+ "ruff",
24
+ "cargo",
25
+ "go",
26
+ "staticcheck",
27
+ "none",
28
+ ]);
29
+ export const AftConfigSchema = z.object({
30
+ /** Whether to auto-format files after edits. Default: true. */
31
+ format_on_edit: z.boolean().optional(),
32
+ /** Auto-validate after edits: "syntax" (tree-sitter) or "full" (runs type checker). */
33
+ validate_on_edit: z.enum(["syntax", "full"]).optional(),
34
+ /** Per-language formatter overrides. Keys: "typescript", "python", "rust", "go". */
35
+ formatter: z.record(z.string(), FormatterEnum).optional(),
36
+ /** Per-language type checker overrides. Keys: "typescript", "python", "rust", "go". */
37
+ checker: z.record(z.string(), CheckerEnum).optional(),
38
+ });
39
+ // ---------------------------------------------------------------------------
40
+ // JSONC parsing (same approach as oh-my-opencode)
41
+ // ---------------------------------------------------------------------------
42
+ function parseJsonc(content) {
43
+ const errors = [];
44
+ const result = parse(content, errors, {
45
+ allowTrailingComma: true,
46
+ disallowComments: false,
47
+ });
48
+ if (errors.length > 0) {
49
+ const errorMessages = errors
50
+ .map((e) => `${printParseErrorCode(e.error)} at offset ${e.offset}`)
51
+ .join(", ");
52
+ throw new SyntaxError(`JSONC parse error: ${errorMessages}`);
53
+ }
54
+ return result;
55
+ }
56
+ // ---------------------------------------------------------------------------
57
+ // Config file detection (.jsonc preferred over .json)
58
+ // ---------------------------------------------------------------------------
59
+ function detectConfigFile(basePath) {
60
+ const jsoncPath = `${basePath}.jsonc`;
61
+ const jsonPath = `${basePath}.json`;
62
+ if (existsSync(jsoncPath)) {
63
+ return { format: "jsonc", path: jsoncPath };
64
+ }
65
+ if (existsSync(jsonPath)) {
66
+ return { format: "json", path: jsonPath };
67
+ }
68
+ return { format: "none", path: jsonPath };
69
+ }
70
+ // ---------------------------------------------------------------------------
71
+ // Partial parse (valid sections survive, invalid sections are skipped)
72
+ // ---------------------------------------------------------------------------
73
+ function parseConfigPartially(rawConfig) {
74
+ const fullResult = AftConfigSchema.safeParse(rawConfig);
75
+ if (fullResult.success) {
76
+ return fullResult.data;
77
+ }
78
+ const partialConfig = {};
79
+ const invalidSections = [];
80
+ for (const key of Object.keys(rawConfig)) {
81
+ const sectionResult = AftConfigSchema.safeParse({ [key]: rawConfig[key] });
82
+ if (sectionResult.success) {
83
+ const parsed = sectionResult.data;
84
+ if (parsed[key] !== undefined) {
85
+ partialConfig[key] = parsed[key];
86
+ }
87
+ }
88
+ else {
89
+ const sectionErrors = sectionResult.error.issues
90
+ .filter((i) => i.path[0] === key)
91
+ .map((i) => `${i.path.join(".")}: ${i.message}`)
92
+ .join(", ");
93
+ if (sectionErrors) {
94
+ invalidSections.push(`${key}: ${sectionErrors}`);
95
+ }
96
+ }
97
+ }
98
+ if (invalidSections.length > 0) {
99
+ console.error(`[aft-plugin] Partial config loaded — invalid sections skipped: ${invalidSections.join("; ")}`);
100
+ }
101
+ return partialConfig;
102
+ }
103
+ // ---------------------------------------------------------------------------
104
+ // Load config from a single file path
105
+ // ---------------------------------------------------------------------------
106
+ function loadConfigFromPath(configPath) {
107
+ try {
108
+ if (!existsSync(configPath)) {
109
+ return null;
110
+ }
111
+ const content = readFileSync(configPath, "utf-8");
112
+ const rawConfig = parseJsonc(content);
113
+ const result = AftConfigSchema.safeParse(rawConfig);
114
+ if (result.success) {
115
+ console.error(`[aft-plugin] Config loaded from ${configPath}`);
116
+ return result.data;
117
+ }
118
+ const errorMsg = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
119
+ console.error(`[aft-plugin] Config validation error in ${configPath}: ${errorMsg}`);
120
+ return parseConfigPartially(rawConfig);
121
+ }
122
+ catch (err) {
123
+ const errorMsg = err instanceof Error ? err.message : String(err);
124
+ console.error(`[aft-plugin] Error loading config from ${configPath}: ${errorMsg}`);
125
+ return null;
126
+ }
127
+ }
128
+ // ---------------------------------------------------------------------------
129
+ // Merge configs (project overrides user, simple shallow merge for flat schema)
130
+ // ---------------------------------------------------------------------------
131
+ function mergeConfigs(base, override) {
132
+ return {
133
+ ...base,
134
+ ...override,
135
+ // Deep-merge language-scoped maps instead of replacing
136
+ formatter: { ...base.formatter, ...override.formatter },
137
+ checker: { ...base.checker, ...override.checker },
138
+ };
139
+ }
140
+ // ---------------------------------------------------------------------------
141
+ // OpenCode config directory detection (same logic as oh-my-opencode)
142
+ // ---------------------------------------------------------------------------
143
+ function getOpenCodeConfigDir() {
144
+ // XDG_CONFIG_HOME or ~/.config, then /opencode
145
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join(process.env.HOME || "~", ".config");
146
+ return join(xdgConfig, "opencode");
147
+ }
148
+ // ---------------------------------------------------------------------------
149
+ // Public API: loadAftConfig
150
+ // ---------------------------------------------------------------------------
151
+ /**
152
+ * Load AFT config using the same two-level pattern as oh-my-opencode:
153
+ *
154
+ * 1. User-level: ~/.config/opencode/aft.jsonc (or .json)
155
+ * 2. Project-level: <project>/.opencode/aft.jsonc (or .json)
156
+ *
157
+ * Project config merges on top of user config.
158
+ * Both support JSONC (comments allowed).
159
+ * Invalid sections are skipped, valid sections still load.
160
+ */
161
+ export function loadAftConfig(projectDirectory) {
162
+ // User-level config
163
+ const configDir = getOpenCodeConfigDir();
164
+ const userBasePath = join(configDir, "aft");
165
+ const userDetected = detectConfigFile(userBasePath);
166
+ const userConfigPath = userDetected.format !== "none" ? userDetected.path : `${userBasePath}.json`;
167
+ // Project-level config
168
+ const projectBasePath = join(projectDirectory, ".opencode", "aft");
169
+ const projectDetected = detectConfigFile(projectBasePath);
170
+ const projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : `${projectBasePath}.json`;
171
+ // Load user config first (base)
172
+ let config = loadConfigFromPath(userConfigPath) ?? {};
173
+ // Override with project config
174
+ const projectConfig = loadConfigFromPath(projectConfigPath);
175
+ if (projectConfig) {
176
+ config = mergeConfigs(config, projectConfig);
177
+ }
178
+ return config;
179
+ }
180
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Auto-download the AFT binary from GitHub releases.
3
+ *
4
+ * Resolution order (in resolver.ts):
5
+ * 1. Cached binary in ~/.cache/aft/bin/
6
+ * 2. npm platform package (@cortexkit/aft-darwin-arm64, etc.)
7
+ * 3. PATH lookup (which aft)
8
+ * 4. ~/.cargo/bin/aft
9
+ * 5. Auto-download from GitHub releases (this module)
10
+ *
11
+ * Cache dir respects XDG_CACHE_HOME on Linux/macOS and LOCALAPPDATA on Windows.
12
+ */
13
+ /** Get the cache directory, respecting XDG_CACHE_HOME / LOCALAPPDATA. */
14
+ export declare function getCacheDir(): string;
15
+ /** Binary name for the current platform. */
16
+ export declare function getBinaryName(): string;
17
+ /** Return the cached binary path if it exists, otherwise null. */
18
+ export declare function getCachedBinaryPath(): string | null;
19
+ /**
20
+ * Download the AFT binary for the current platform from GitHub releases.
21
+ *
22
+ * @param version - Git tag to download from (e.g. "v0.1.0"). If omitted,
23
+ * fetches the latest release tag via the GitHub API.
24
+ * @returns Absolute path to the downloaded binary, or null on failure.
25
+ */
26
+ export declare function downloadBinary(version?: string): Promise<string | null>;
27
+ /**
28
+ * Ensure the AFT binary is available: check cache, then download if needed.
29
+ * This is the main entry point called by the resolver.
30
+ */
31
+ export declare function ensureBinary(version?: string): Promise<string | null>;
32
+ //# sourceMappingURL=downloader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downloader.d.ts","sourceRoot":"","sources":["../src/downloader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkBH,yEAAyE;AACzE,wBAAgB,WAAW,IAAI,MAAM,CASpC;AAED,4CAA4C;AAC5C,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAGnD;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2E7E;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAI3E"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Auto-download the AFT binary from GitHub releases.
3
+ *
4
+ * Resolution order (in resolver.ts):
5
+ * 1. Cached binary in ~/.cache/aft/bin/
6
+ * 2. npm platform package (@cortexkit/aft-darwin-arm64, etc.)
7
+ * 3. PATH lookup (which aft)
8
+ * 4. ~/.cargo/bin/aft
9
+ * 5. Auto-download from GitHub releases (this module)
10
+ *
11
+ * Cache dir respects XDG_CACHE_HOME on Linux/macOS and LOCALAPPDATA on Windows.
12
+ */
13
+ import { chmodSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
14
+ import { homedir } from "node:os";
15
+ import { join } from "node:path";
16
+ const REPO = "nichochar/opencode-sfm";
17
+ const TAG = "[aft-downloader]";
18
+ /** Platform → GitHub release asset suffix */
19
+ const PLATFORM_MAP = {
20
+ "darwin-arm64": "aft-darwin-arm64",
21
+ "darwin-x64": "aft-darwin-x64",
22
+ "linux-arm64": "aft-linux-arm64",
23
+ "linux-x64": "aft-linux-x64",
24
+ "win32-x64": "aft-win32-x64.exe",
25
+ };
26
+ /** Get the cache directory, respecting XDG_CACHE_HOME / LOCALAPPDATA. */
27
+ export function getCacheDir() {
28
+ if (process.platform === "win32") {
29
+ const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
30
+ const base = localAppData || join(homedir(), "AppData", "Local");
31
+ return join(base, "aft", "bin");
32
+ }
33
+ const base = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
34
+ return join(base, "aft", "bin");
35
+ }
36
+ /** Binary name for the current platform. */
37
+ export function getBinaryName() {
38
+ return process.platform === "win32" ? "aft.exe" : "aft";
39
+ }
40
+ /** Return the cached binary path if it exists, otherwise null. */
41
+ export function getCachedBinaryPath() {
42
+ const binaryPath = join(getCacheDir(), getBinaryName());
43
+ return existsSync(binaryPath) ? binaryPath : null;
44
+ }
45
+ /**
46
+ * Download the AFT binary for the current platform from GitHub releases.
47
+ *
48
+ * @param version - Git tag to download from (e.g. "v0.1.0"). If omitted,
49
+ * fetches the latest release tag via the GitHub API.
50
+ * @returns Absolute path to the downloaded binary, or null on failure.
51
+ */
52
+ export async function downloadBinary(version) {
53
+ const platformKey = `${process.platform}-${process.arch}`;
54
+ const assetName = PLATFORM_MAP[platformKey];
55
+ if (!assetName) {
56
+ console.error(`${TAG} Unsupported platform: ${platformKey}`);
57
+ return null;
58
+ }
59
+ const cacheDir = getCacheDir();
60
+ const binaryName = getBinaryName();
61
+ const binaryPath = join(cacheDir, binaryName);
62
+ // Already cached
63
+ if (existsSync(binaryPath)) {
64
+ return binaryPath;
65
+ }
66
+ // Resolve version if not provided
67
+ const tag = version ?? (await fetchLatestTag());
68
+ if (!tag) {
69
+ console.error(`${TAG} Could not determine latest release version.`);
70
+ return null;
71
+ }
72
+ const downloadUrl = `https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
73
+ console.error(`${TAG} Downloading AFT binary (${tag}) for ${platformKey}...`);
74
+ try {
75
+ // Ensure cache directory exists
76
+ if (!existsSync(cacheDir)) {
77
+ mkdirSync(cacheDir, { recursive: true });
78
+ }
79
+ // Download
80
+ const response = await fetch(downloadUrl, { redirect: "follow" });
81
+ if (!response.ok) {
82
+ throw new Error(`HTTP ${response.status}: ${response.statusText} (${downloadUrl})`);
83
+ }
84
+ const arrayBuffer = await response.arrayBuffer();
85
+ // Write to a temp file first, then rename (atomic-ish)
86
+ const tmpPath = `${binaryPath}.tmp`;
87
+ const { writeFileSync } = await import("node:fs");
88
+ writeFileSync(tmpPath, Buffer.from(arrayBuffer));
89
+ // Make executable
90
+ if (process.platform !== "win32") {
91
+ chmodSync(tmpPath, 0o755);
92
+ }
93
+ // Atomic rename
94
+ const { renameSync } = await import("node:fs");
95
+ renameSync(tmpPath, binaryPath);
96
+ console.error(`${TAG} AFT binary ready at ${binaryPath}`);
97
+ return binaryPath;
98
+ }
99
+ catch (err) {
100
+ const msg = err instanceof Error ? err.message : String(err);
101
+ console.error(`${TAG} Failed to download AFT binary: ${msg}`);
102
+ // Clean up partial download
103
+ const tmpPath = `${binaryPath}.tmp`;
104
+ if (existsSync(tmpPath)) {
105
+ try {
106
+ unlinkSync(tmpPath);
107
+ }
108
+ catch {
109
+ // ignore cleanup failure
110
+ }
111
+ }
112
+ return null;
113
+ }
114
+ }
115
+ /**
116
+ * Ensure the AFT binary is available: check cache, then download if needed.
117
+ * This is the main entry point called by the resolver.
118
+ */
119
+ export async function ensureBinary(version) {
120
+ const cached = getCachedBinaryPath();
121
+ if (cached)
122
+ return cached;
123
+ return downloadBinary(version);
124
+ }
125
+ /** Fetch the latest release tag from GitHub API. */
126
+ async function fetchLatestTag() {
127
+ try {
128
+ const response = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`, {
129
+ headers: { Accept: "application/vnd.github.v3+json" },
130
+ });
131
+ if (!response.ok)
132
+ return null;
133
+ const data = (await response.json());
134
+ return data.tag_name ?? null;
135
+ }
136
+ catch {
137
+ return null;
138
+ }
139
+ }
140
+ //# sourceMappingURL=downloader.js.map