@axintai/compiler 0.3.0 → 0.3.1
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 +41 -13
- package/dist/cli/index.js +311 -5
- package/dist/cli/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -63,10 +63,10 @@ Axint is the fastest path from an AI coding tool to a shipped App Intent. **One
|
|
|
63
63
|
- **Native type fidelity.** `int → Int`, `double → Double`, `float → Float`, `date → Date`, `url → URL`, `duration → Measurement<UnitDuration>`, `optional<T> → T?`. Default values and optionality are preserved end-to-end.
|
|
64
64
|
- **Return-type-aware `perform()` signatures.** Every generated intent is a drop-in tool for Agent Siri and Shortcuts.
|
|
65
65
|
- **Info.plist and .entitlements emit.** Axint writes the `NSAppIntentsDomains` plist fragment and the App Intents entitlement XML alongside your `.swift` file. Drop all three into Xcode and ship.
|
|
66
|
-
- **MCP-native.** A bundled `axint-mcp` server exposes `axint_scaffold`, `axint_compile`, and `
|
|
66
|
+
- **MCP-native.** A bundled `axint-mcp` server exposes five tools — `axint_scaffold`, `axint_compile`, `axint_validate`, `axint_list_templates`, and `axint_template` — to any MCP client. Your AI coding agent can read your project, draft a TypeScript intent, compile it, and open a PR — without a human touching Xcode.
|
|
67
67
|
- **Rust-grade diagnostics.** 16 diagnostic codes (`AX001`–`AX202`) with fix suggestions and color-coded output.
|
|
68
68
|
- **Sub-millisecond compile.** A typical intent compiles in under a millisecond. The [axint.ai playground](https://axint.ai/#playground) runs the full compiler in your browser with zero server round-trip.
|
|
69
|
-
- **
|
|
69
|
+
- **155 tests.** Parser, validator, generator, emit paths, watch mode, and sandbox — all covered.
|
|
70
70
|
- **Apache 2.0, no CLA.** Fork it, extend it, ship it.
|
|
71
71
|
|
|
72
72
|
---
|
|
@@ -120,6 +120,31 @@ ios/Intents/
|
|
|
120
120
|
|
|
121
121
|
---
|
|
122
122
|
|
|
123
|
+
## Watch mode
|
|
124
|
+
|
|
125
|
+
For iterative development, `axint watch` recompiles on every save with sub-millisecond rebuilds:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Watch a single file
|
|
129
|
+
axint watch my-intent.ts --out ios/Intents/
|
|
130
|
+
|
|
131
|
+
# Watch a directory of intents
|
|
132
|
+
axint watch ./intents/ --out ios/Intents/ --emit-info-plist --emit-entitlements
|
|
133
|
+
|
|
134
|
+
# With swift-format
|
|
135
|
+
axint watch my-intent.ts --out ios/Intents/ --format
|
|
136
|
+
|
|
137
|
+
# Auto-run swift build after each compile
|
|
138
|
+
axint watch ./intents/ --out ios/Intents/ --swift-build
|
|
139
|
+
|
|
140
|
+
# Specify the Swift project root (defaults to --out parent)
|
|
141
|
+
axint watch ./intents/ --out ios/Sources/Intents/ --swift-build --swift-project ios/
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The watcher runs an initial compile pass, then re-triggers on file changes with a 150ms debounce. Errors are reported inline without killing the process — fix the file and it recompiles automatically. With `--swift-build`, each successful compile triggers `swift build` in the project directory so you get immediate feedback on whether the generated Swift compiles cleanly.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
123
148
|
## Compiled Swift output
|
|
124
149
|
|
|
125
150
|
```swift
|
|
@@ -183,13 +208,15 @@ Axint ships with `axint-mcp`, a Model Context Protocol server that exposes the c
|
|
|
183
208
|
}
|
|
184
209
|
```
|
|
185
210
|
|
|
186
|
-
|
|
211
|
+
Five tools are exposed:
|
|
187
212
|
|
|
188
|
-
| Tool
|
|
189
|
-
|
|
|
190
|
-
| `axint_scaffold`
|
|
191
|
-
| `axint_compile`
|
|
192
|
-
| `axint_validate`
|
|
213
|
+
| Tool | What it does |
|
|
214
|
+
| --------------------- | -------------------------------------------------------------------------- |
|
|
215
|
+
| `axint_scaffold` | Generates a TypeScript intent from a natural-language description |
|
|
216
|
+
| `axint_compile` | Runs the full pipeline and returns `.swift` + `.plist` + `.entitlements` |
|
|
217
|
+
| `axint_validate` | Dry-run validation with line/column diagnostics |
|
|
218
|
+
| `axint_list_templates`| Lists all bundled intent templates |
|
|
219
|
+
| `axint_template` | Returns the source of a specific bundled template |
|
|
193
220
|
|
|
194
221
|
Once connected, your AI coding agent can read a Swift project, draft an intent, compile it, and open a PR — without a human touching Xcode.
|
|
195
222
|
|
|
@@ -272,7 +299,7 @@ axint/
|
|
|
272
299
|
│ ├── mcp/ # MCP server (scaffold, compile, validate)
|
|
273
300
|
│ ├── cli/ # axint CLI (Commander.js)
|
|
274
301
|
│ └── templates/ # Intent template registry
|
|
275
|
-
├── tests/ #
|
|
302
|
+
├── tests/ # 155 vitest tests
|
|
276
303
|
├── examples/ # Example intent definitions
|
|
277
304
|
└── docs/ # Error reference, contributing, assets
|
|
278
305
|
```
|
|
@@ -300,10 +327,11 @@ See [`ROADMAP.md`](ROADMAP.md) for the full plan. Highlights:
|
|
|
300
327
|
- [x] Info.plist and `.entitlements` emit (v0.2.0)
|
|
301
328
|
- [x] Return-type-aware `perform()` (v0.2.0)
|
|
302
329
|
- [x] MCP scaffold tool (v0.2.0)
|
|
303
|
-
- [x]
|
|
304
|
-
- [
|
|
305
|
-
- [
|
|
306
|
-
- [
|
|
330
|
+
- [x] 155-test suite with snapshot coverage (v0.2.0+)
|
|
331
|
+
- [x] Intent template library with 12+ templates (v0.3.0)
|
|
332
|
+
- [x] `--watch` mode with `--swift-build` for live recompilation (v0.3.0)
|
|
333
|
+
- [x] SPM build plugin — auto-compile during `swift build` (v0.3.0)
|
|
334
|
+
- [x] Registry at registry.axint.ai (v0.3.0)
|
|
307
335
|
- [ ] GitHub template repo (`axint-starter`)
|
|
308
336
|
- [ ] Axint Cloud (hosted compilation)
|
|
309
337
|
|
package/dist/cli/index.js
CHANGED
|
@@ -2077,8 +2077,16 @@ var init_server = __esm({
|
|
|
2077
2077
|
// src/cli/index.ts
|
|
2078
2078
|
init_compiler();
|
|
2079
2079
|
import { Command } from "commander";
|
|
2080
|
-
import {
|
|
2080
|
+
import {
|
|
2081
|
+
readFileSync as readFileSync3,
|
|
2082
|
+
writeFileSync,
|
|
2083
|
+
mkdirSync,
|
|
2084
|
+
existsSync as existsSync3,
|
|
2085
|
+
watch as fsWatch,
|
|
2086
|
+
statSync
|
|
2087
|
+
} from "fs";
|
|
2081
2088
|
import { resolve as resolve2, dirname as dirname2, basename } from "path";
|
|
2089
|
+
import { spawn as spawn4 } from "child_process";
|
|
2082
2090
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2083
2091
|
|
|
2084
2092
|
// src/core/eject.ts
|
|
@@ -2343,7 +2351,7 @@ Any agent that supports MCP can now call \`axint_compile\`, \`axint_validate\`,
|
|
|
2343
2351
|
- Edit \`intents/${template}.ts\` \u2014 this is your App Intent source of truth.
|
|
2344
2352
|
- Add more intents in the \`intents/\` folder.
|
|
2345
2353
|
- Run \`axint templates\` to see every bundled starter.
|
|
2346
|
-
- Read the docs at https://
|
|
2354
|
+
- Read the docs at https://github.com/agenticempire/axint#readme
|
|
2347
2355
|
|
|
2348
2356
|
---
|
|
2349
2357
|
|
|
@@ -2406,7 +2414,9 @@ program.command("init").description("Scaffold a new Axint project (zero-config,
|
|
|
2406
2414
|
);
|
|
2407
2415
|
}
|
|
2408
2416
|
console.log();
|
|
2409
|
-
console.log(
|
|
2417
|
+
console.log(
|
|
2418
|
+
` \x1B[2mDocs: https://github.com/agenticempire/axint#readme\x1B[0m`
|
|
2419
|
+
);
|
|
2410
2420
|
console.log(
|
|
2411
2421
|
` \x1B[2mMCP: npx axint-mcp (add to Claude Code, Cursor, Windsurf)\x1B[0m`
|
|
2412
2422
|
);
|
|
@@ -2731,7 +2741,7 @@ program.command("templates").description("List bundled intent templates").argume
|
|
|
2731
2741
|
program.command("login").description("Authenticate with the Axint Registry via GitHub").action(async () => {
|
|
2732
2742
|
const { homedir } = await import("os");
|
|
2733
2743
|
const { join: join4 } = await import("path");
|
|
2734
|
-
const { spawn:
|
|
2744
|
+
const { spawn: spawn5 } = await import("child_process");
|
|
2735
2745
|
const configDir = join4(homedir(), ".axint");
|
|
2736
2746
|
const credPath = join4(configDir, "credentials.json");
|
|
2737
2747
|
const registryUrl = process.env.AXINT_REGISTRY_URL ?? "https://registry.axint.ai";
|
|
@@ -2760,7 +2770,7 @@ program.command("login").description("Authenticate with the Axint Registry via G
|
|
|
2760
2770
|
console.log(` \x1B[2mWaiting for authorization\u2026\x1B[0m`);
|
|
2761
2771
|
try {
|
|
2762
2772
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2763
|
-
|
|
2773
|
+
spawn5(openCmd, [verification_uri], { stdio: "ignore", detached: true }).unref();
|
|
2764
2774
|
} catch {
|
|
2765
2775
|
}
|
|
2766
2776
|
const pollInterval = (interval ?? 5) * 1e3;
|
|
@@ -3006,6 +3016,302 @@ program.command("add").description("Install a template from the Axint Registry")
|
|
|
3006
3016
|
process.exit(1);
|
|
3007
3017
|
}
|
|
3008
3018
|
});
|
|
3019
|
+
program.command("search").description("Search the Axint Registry for intent templates").argument("[query]", "Search term (lists popular packages if omitted)").option("--limit <n>", "Max results", "20").option("--json", "Output as JSON").action(
|
|
3020
|
+
async (query, options) => {
|
|
3021
|
+
const registryUrl = process.env.AXINT_REGISTRY_URL ?? "https://registry.axint.ai";
|
|
3022
|
+
const limit = Math.max(1, Math.min(100, parseInt(options.limit, 10) || 20));
|
|
3023
|
+
console.log();
|
|
3024
|
+
if (!options.json) {
|
|
3025
|
+
console.log(
|
|
3026
|
+
` \x1B[38;5;208m\u25C6\x1B[0m \x1B[1mAxint\x1B[0m \xB7 search ${query ? `"${query}"` : ""}`
|
|
3027
|
+
);
|
|
3028
|
+
console.log();
|
|
3029
|
+
}
|
|
3030
|
+
try {
|
|
3031
|
+
const params = new URLSearchParams({ limit: String(limit) });
|
|
3032
|
+
if (query) params.set("q", query);
|
|
3033
|
+
const res = await fetch(`${registryUrl}/api/v1/search?${params}`, {
|
|
3034
|
+
headers: { "X-Axint-Version": VERSION }
|
|
3035
|
+
});
|
|
3036
|
+
if (!res.ok) {
|
|
3037
|
+
console.error(`\x1B[31merror:\x1B[0m Search failed (HTTP ${res.status})`);
|
|
3038
|
+
process.exit(1);
|
|
3039
|
+
}
|
|
3040
|
+
const data = await res.json();
|
|
3041
|
+
if (options.json) {
|
|
3042
|
+
console.log(JSON.stringify(data, null, 2));
|
|
3043
|
+
return;
|
|
3044
|
+
}
|
|
3045
|
+
if (data.results.length === 0) {
|
|
3046
|
+
console.log(` No packages found`);
|
|
3047
|
+
console.log();
|
|
3048
|
+
return;
|
|
3049
|
+
}
|
|
3050
|
+
for (const pkg3 of data.results) {
|
|
3051
|
+
const downloads = pkg3.downloads > 0 ? `\u25BC ${pkg3.downloads}` : "";
|
|
3052
|
+
const dl = downloads ? ` \x1B[2m${downloads}\x1B[0m` : "";
|
|
3053
|
+
console.log(
|
|
3054
|
+
` \x1B[38;5;208m\u25C6\x1B[0m ${pkg3.package_name.padEnd(30)} ${pkg3.description.substring(0, 35).padEnd(35)}${dl}`
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
3057
|
+
console.log();
|
|
3058
|
+
console.log(
|
|
3059
|
+
` ${data.results.length} package${data.results.length === 1 ? "" : "s"} found`
|
|
3060
|
+
);
|
|
3061
|
+
console.log();
|
|
3062
|
+
console.log(
|
|
3063
|
+
` \x1B[2mInstall:\x1B[0m axint add ${data.results[0]?.package_name ?? "@namespace/slug"}`
|
|
3064
|
+
);
|
|
3065
|
+
console.log();
|
|
3066
|
+
} catch (err) {
|
|
3067
|
+
console.error(`\x1B[31merror:\x1B[0m ${err.message ?? err}`);
|
|
3068
|
+
process.exit(1);
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
);
|
|
3072
|
+
async function runSwiftBuild2(projectPath) {
|
|
3073
|
+
return new Promise((resolve3) => {
|
|
3074
|
+
const t0 = performance.now();
|
|
3075
|
+
const proc = spawn4("swift", ["build"], {
|
|
3076
|
+
cwd: projectPath,
|
|
3077
|
+
stdio: ["ignore", "inherit", "inherit"]
|
|
3078
|
+
});
|
|
3079
|
+
proc.on("close", (code) => {
|
|
3080
|
+
if (code === 0) {
|
|
3081
|
+
const dt = (performance.now() - t0).toFixed(0);
|
|
3082
|
+
console.log();
|
|
3083
|
+
console.log(`\x1B[38;5;208m\u2500 swift build\x1B[0m`);
|
|
3084
|
+
console.log(`\x1B[32m\u2713\x1B[0m Build succeeded \x1B[90m(${dt}ms)\x1B[0m`);
|
|
3085
|
+
console.log();
|
|
3086
|
+
resolve3(true);
|
|
3087
|
+
} else {
|
|
3088
|
+
console.log();
|
|
3089
|
+
console.log(`\x1B[38;5;208m\u2500 swift build\x1B[0m`);
|
|
3090
|
+
console.log(`\x1B[31m\u2717\x1B[0m Build failed (exit code: ${code})`);
|
|
3091
|
+
console.log("\x1B[90mContinuing to watch for changes\u2026\x1B[0m");
|
|
3092
|
+
console.log();
|
|
3093
|
+
resolve3(false);
|
|
3094
|
+
}
|
|
3095
|
+
});
|
|
3096
|
+
proc.on("error", (err) => {
|
|
3097
|
+
console.log();
|
|
3098
|
+
console.log(`\x1B[38;5;208m\u2500 swift build\x1B[0m`);
|
|
3099
|
+
console.error(`\x1B[31m\u2717\x1B[0m Error: ${err.message}`);
|
|
3100
|
+
console.log("\x1B[90mContinuing to watch for changes\u2026\x1B[0m");
|
|
3101
|
+
console.log();
|
|
3102
|
+
resolve3(false);
|
|
3103
|
+
});
|
|
3104
|
+
});
|
|
3105
|
+
}
|
|
3106
|
+
program.command("watch").description("Watch intent files and recompile on change").argument("<file>", "Path to a TypeScript intent file or directory of intents").option("-o, --out <dir>", "Output directory for generated Swift", ".").option("--no-validate", "Skip validation of generated Swift").option("--emit-info-plist", "Emit Info.plist fragments alongside Swift files").option("--emit-entitlements", "Emit entitlements fragments alongside Swift files").option("--format", "Pipe generated Swift through swift-format").option(
|
|
3107
|
+
"--strict-format",
|
|
3108
|
+
"Fail if swift-format is missing or errors (implies --format)"
|
|
3109
|
+
).option(
|
|
3110
|
+
"--swift-build",
|
|
3111
|
+
"Run `swift build` in the project after successful compilation"
|
|
3112
|
+
).option(
|
|
3113
|
+
"--swift-project <path>",
|
|
3114
|
+
"Path to the Swift project root (defaults to --out parent directory)"
|
|
3115
|
+
).action(
|
|
3116
|
+
async (file, options) => {
|
|
3117
|
+
const target = resolve2(file);
|
|
3118
|
+
const isDir = existsSync3(target) && statSync(target).isDirectory();
|
|
3119
|
+
const filesToWatch = [];
|
|
3120
|
+
if (isDir) {
|
|
3121
|
+
const { readdirSync } = await import("fs");
|
|
3122
|
+
for (const entry of readdirSync(target)) {
|
|
3123
|
+
if (entry.endsWith(".ts") && !entry.endsWith(".d.ts")) {
|
|
3124
|
+
filesToWatch.push(resolve2(target, entry));
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
if (filesToWatch.length === 0) {
|
|
3128
|
+
console.error(`\x1B[31merror:\x1B[0m No .ts files found in ${target}`);
|
|
3129
|
+
process.exit(1);
|
|
3130
|
+
}
|
|
3131
|
+
} else {
|
|
3132
|
+
if (!existsSync3(target)) {
|
|
3133
|
+
console.error(`\x1B[31merror:\x1B[0m File not found: ${target}`);
|
|
3134
|
+
process.exit(1);
|
|
3135
|
+
}
|
|
3136
|
+
filesToWatch.push(target);
|
|
3137
|
+
}
|
|
3138
|
+
const swiftProjectPath = options.swiftProject ?? dirname2(resolve2(options.out));
|
|
3139
|
+
function compileOne(filePath) {
|
|
3140
|
+
const t0 = performance.now();
|
|
3141
|
+
const result = compileFile(filePath, {
|
|
3142
|
+
outDir: options.out,
|
|
3143
|
+
validate: options.validate,
|
|
3144
|
+
emitInfoPlist: options.emitInfoPlist,
|
|
3145
|
+
emitEntitlements: options.emitEntitlements
|
|
3146
|
+
});
|
|
3147
|
+
for (const d of result.diagnostics) {
|
|
3148
|
+
const prefix = d.severity === "error" ? "\x1B[31merror\x1B[0m" : d.severity === "warning" ? "\x1B[33mwarning\x1B[0m" : "\x1B[36minfo\x1B[0m";
|
|
3149
|
+
console.error(` ${prefix}[${d.code}]: ${d.message}`);
|
|
3150
|
+
if (d.file) console.error(` --> ${d.file}${d.line ? `:${d.line}` : ""}`);
|
|
3151
|
+
if (d.suggestion) console.error(` = help: ${d.suggestion}`);
|
|
3152
|
+
}
|
|
3153
|
+
if (!result.success || !result.output) {
|
|
3154
|
+
const errors = result.diagnostics.filter((d) => d.severity === "error").length;
|
|
3155
|
+
console.error(`\x1B[31m\u2717\x1B[0m ${basename(filePath)} \u2014 ${errors} error(s)`);
|
|
3156
|
+
return false;
|
|
3157
|
+
}
|
|
3158
|
+
const swiftCode = result.output.swiftCode;
|
|
3159
|
+
if (options.format || options.strictFormat) {
|
|
3160
|
+
}
|
|
3161
|
+
const outPath = resolve2(result.output.outputPath);
|
|
3162
|
+
mkdirSync(dirname2(outPath), { recursive: true });
|
|
3163
|
+
writeFileSync(outPath, swiftCode, "utf-8");
|
|
3164
|
+
if (options.emitInfoPlist && result.output.infoPlistFragment) {
|
|
3165
|
+
const plistPath = outPath.replace(/\.swift$/, ".plist.fragment.xml");
|
|
3166
|
+
writeFileSync(plistPath, result.output.infoPlistFragment, "utf-8");
|
|
3167
|
+
}
|
|
3168
|
+
if (options.emitEntitlements && result.output.entitlementsFragment) {
|
|
3169
|
+
const entPath = outPath.replace(/\.swift$/, ".entitlements.fragment.xml");
|
|
3170
|
+
writeFileSync(entPath, result.output.entitlementsFragment, "utf-8");
|
|
3171
|
+
}
|
|
3172
|
+
const dt = (performance.now() - t0).toFixed(1);
|
|
3173
|
+
console.log(
|
|
3174
|
+
`\x1B[32m\u2713\x1B[0m ${result.output.ir.name} \u2192 ${outPath} \x1B[90m(${dt}ms)\x1B[0m`
|
|
3175
|
+
);
|
|
3176
|
+
return true;
|
|
3177
|
+
}
|
|
3178
|
+
async function compileWithFormat(filePath) {
|
|
3179
|
+
if (!options.format && !options.strictFormat) {
|
|
3180
|
+
return compileOne(filePath);
|
|
3181
|
+
}
|
|
3182
|
+
const t0 = performance.now();
|
|
3183
|
+
const result = compileFile(filePath, {
|
|
3184
|
+
outDir: options.out,
|
|
3185
|
+
validate: options.validate,
|
|
3186
|
+
emitInfoPlist: options.emitInfoPlist,
|
|
3187
|
+
emitEntitlements: options.emitEntitlements
|
|
3188
|
+
});
|
|
3189
|
+
for (const d of result.diagnostics) {
|
|
3190
|
+
const prefix = d.severity === "error" ? "\x1B[31merror\x1B[0m" : d.severity === "warning" ? "\x1B[33mwarning\x1B[0m" : "\x1B[36minfo\x1B[0m";
|
|
3191
|
+
console.error(` ${prefix}[${d.code}]: ${d.message}`);
|
|
3192
|
+
if (d.file) console.error(` --> ${d.file}${d.line ? `:${d.line}` : ""}`);
|
|
3193
|
+
if (d.suggestion) console.error(` = help: ${d.suggestion}`);
|
|
3194
|
+
}
|
|
3195
|
+
if (!result.success || !result.output) {
|
|
3196
|
+
const errors = result.diagnostics.filter((d) => d.severity === "error").length;
|
|
3197
|
+
console.error(`\x1B[31m\u2717\x1B[0m ${basename(filePath)} \u2014 ${errors} error(s)`);
|
|
3198
|
+
return false;
|
|
3199
|
+
}
|
|
3200
|
+
let swiftCode = result.output.swiftCode;
|
|
3201
|
+
try {
|
|
3202
|
+
const { formatSwift: formatSwift2 } = await Promise.resolve().then(() => (init_format(), format_exports));
|
|
3203
|
+
const fmt = await formatSwift2(swiftCode, { strict: options.strictFormat });
|
|
3204
|
+
if (fmt.ran) swiftCode = fmt.formatted;
|
|
3205
|
+
} catch (fmtErr) {
|
|
3206
|
+
if (options.strictFormat) {
|
|
3207
|
+
console.error(`\x1B[31merror:\x1B[0m ${fmtErr.message}`);
|
|
3208
|
+
return false;
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
const outPath = resolve2(result.output.outputPath);
|
|
3212
|
+
mkdirSync(dirname2(outPath), { recursive: true });
|
|
3213
|
+
writeFileSync(outPath, swiftCode, "utf-8");
|
|
3214
|
+
if (options.emitInfoPlist && result.output.infoPlistFragment) {
|
|
3215
|
+
writeFileSync(
|
|
3216
|
+
outPath.replace(/\.swift$/, ".plist.fragment.xml"),
|
|
3217
|
+
result.output.infoPlistFragment,
|
|
3218
|
+
"utf-8"
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
if (options.emitEntitlements && result.output.entitlementsFragment) {
|
|
3222
|
+
writeFileSync(
|
|
3223
|
+
outPath.replace(/\.swift$/, ".entitlements.fragment.xml"),
|
|
3224
|
+
result.output.entitlementsFragment,
|
|
3225
|
+
"utf-8"
|
|
3226
|
+
);
|
|
3227
|
+
}
|
|
3228
|
+
const dt = (performance.now() - t0).toFixed(1);
|
|
3229
|
+
console.log(
|
|
3230
|
+
`\x1B[32m\u2713\x1B[0m ${result.output.ir.name} \u2192 ${outPath} \x1B[90m(${dt}ms)\x1B[0m`
|
|
3231
|
+
);
|
|
3232
|
+
return true;
|
|
3233
|
+
}
|
|
3234
|
+
console.log(`\x1B[1maxint watch\x1B[0m \u2014 ${filesToWatch.length} file(s)
|
|
3235
|
+
`);
|
|
3236
|
+
let ok = 0;
|
|
3237
|
+
let fail = 0;
|
|
3238
|
+
for (const f of filesToWatch) {
|
|
3239
|
+
if (await compileWithFormat(f)) {
|
|
3240
|
+
ok++;
|
|
3241
|
+
} else {
|
|
3242
|
+
fail++;
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
console.log();
|
|
3246
|
+
if (fail > 0) {
|
|
3247
|
+
console.log(
|
|
3248
|
+
`\x1B[33m\u26A0\x1B[0m ${ok} compiled, ${fail} failed \u2014 watching for changes\u2026
|
|
3249
|
+
`
|
|
3250
|
+
);
|
|
3251
|
+
} else {
|
|
3252
|
+
console.log(`\x1B[32m\u2713\x1B[0m ${ok} compiled \u2014 watching for changes\u2026
|
|
3253
|
+
`);
|
|
3254
|
+
if (options.swiftBuild) {
|
|
3255
|
+
await runSwiftBuild2(swiftProjectPath);
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
const pending = /* @__PURE__ */ new Map();
|
|
3259
|
+
const DEBOUNCE_MS = 150;
|
|
3260
|
+
let batchInProgress = false;
|
|
3261
|
+
function onFileChange(filePath) {
|
|
3262
|
+
const existing = pending.get(filePath);
|
|
3263
|
+
if (existing) clearTimeout(existing);
|
|
3264
|
+
pending.set(
|
|
3265
|
+
filePath,
|
|
3266
|
+
setTimeout(async () => {
|
|
3267
|
+
pending.delete(filePath);
|
|
3268
|
+
if (batchInProgress) return;
|
|
3269
|
+
batchInProgress = true;
|
|
3270
|
+
try {
|
|
3271
|
+
const now = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
3272
|
+
console.log(`\x1B[90m[${now}]\x1B[0m ${basename(filePath)} changed`);
|
|
3273
|
+
const compiled = await compileWithFormat(filePath);
|
|
3274
|
+
console.log();
|
|
3275
|
+
if (compiled && options.swiftBuild) {
|
|
3276
|
+
await runSwiftBuild2(swiftProjectPath);
|
|
3277
|
+
}
|
|
3278
|
+
} finally {
|
|
3279
|
+
batchInProgress = false;
|
|
3280
|
+
}
|
|
3281
|
+
}, DEBOUNCE_MS)
|
|
3282
|
+
);
|
|
3283
|
+
}
|
|
3284
|
+
if (isDir) {
|
|
3285
|
+
const dirWatcher = fsWatch(target, { persistent: true }, (_event, filename) => {
|
|
3286
|
+
if (filename && typeof filename === "string" && filename.endsWith(".ts") && !filename.endsWith(".d.ts")) {
|
|
3287
|
+
onFileChange(resolve2(target, filename));
|
|
3288
|
+
}
|
|
3289
|
+
});
|
|
3290
|
+
dirWatcher.on("error", (err) => {
|
|
3291
|
+
console.error(`\x1B[31m\u2717\x1B[0m watcher error: ${err.message}`);
|
|
3292
|
+
});
|
|
3293
|
+
} else {
|
|
3294
|
+
const parentDir = dirname2(target);
|
|
3295
|
+
const targetBase = basename(target);
|
|
3296
|
+
const fileWatcher = fsWatch(
|
|
3297
|
+
parentDir,
|
|
3298
|
+
{ persistent: true },
|
|
3299
|
+
(_event, filename) => {
|
|
3300
|
+
if (filename === targetBase) {
|
|
3301
|
+
onFileChange(target);
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
);
|
|
3305
|
+
fileWatcher.on("error", (err) => {
|
|
3306
|
+
console.error(`\x1B[31m\u2717\x1B[0m watcher error: ${err.message}`);
|
|
3307
|
+
});
|
|
3308
|
+
}
|
|
3309
|
+
process.on("SIGINT", () => {
|
|
3310
|
+
console.log("\n\x1B[90mStopped watching.\x1B[0m");
|
|
3311
|
+
process.exit(0);
|
|
3312
|
+
});
|
|
3313
|
+
}
|
|
3314
|
+
);
|
|
3009
3315
|
program.command("mcp").description(
|
|
3010
3316
|
"Start the Axint MCP server (stdio) for Claude Code, Cursor, Windsurf, Zed, or any MCP client"
|
|
3011
3317
|
).action(async () => {
|