From 99daa5a49479e7321c7718f4aec3298f12b38816 Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 1 Dec 2025 19:57:02 +0000 Subject: [PATCH 1/4] Initial commit with task details for issue #6 Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/link-assistant/agent/issues/6 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4397578 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/link-assistant/agent/issues/6 +Your prepared branch: issue-6-097eb11195ee +Your prepared working directory: /tmp/gh-issue-solver-1764619020131 + +Proceed. \ No newline at end of file From 61f5cd77d62f215f8f2289c601dc83bc66f93702 Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 1 Dec 2025 20:10:08 +0000 Subject: [PATCH 2/4] Restore macro support for development use with documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit restores the Bun macro functionality in models.ts by adding the `with { type: "macro" }` directive to the import statement. Key changes: - Added macro directive to models-macro import in src/provider/models.ts - Created comprehensive documentation in docs/MACRO_SUPPORT.md How it works: - Macros work in development mode (running from source) - Macros gracefully fallback when running from node_modules - Runtime fetching is used when macros are unavailable Limitations: Due to Bun's security model, macros cannot execute from node_modules. This means macros only work when running from source, not when installed globally via `bun install -g`. The code handles this gracefully with automatic fallback to runtime fetching. Alternative approaches considered: 1. Pre-bundling: Async macros caused bundler to hang 2. Bundled JSON: Would add ~600KB to package size The current solution provides the best balance: - Development performance optimization through macros - Reliable production behavior through runtime fetching - No user configuration required Related: #6 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/MACRO_SUPPORT.md | 89 ++++++++++++++++++++++++++++++++++++++++++ src/provider/models.ts | 2 +- 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 docs/MACRO_SUPPORT.md diff --git a/docs/MACRO_SUPPORT.md b/docs/MACRO_SUPPORT.md new file mode 100644 index 0000000..cda4515 --- /dev/null +++ b/docs/MACRO_SUPPORT.md @@ -0,0 +1,89 @@ +# Macro Support in Agent CLI + +## Overview + +The agent CLI uses [Bun macros](https://bun.sh/docs/bundler/macros) to optionally optimize the loading of model provider information from [models.dev](https://models.dev/api.json). + +## How It Works + +In `src/provider/models.ts`, we import the `data` function from `models-macro.ts` with the macro directive: + +```typescript +import { data } from "./models-macro" with { type: "macro" } +``` + +When macros are supported, this function executes at **bundle-time** rather than runtime, potentially improving startup performance. + +## Limitations + +### Node Modules Restriction + +**Important:** Due to Bun's security model, macros cannot execute from code installed in `node_modules`. This means: + +- ✅ **Works**: When running the agent from source (development mode) +- ❌ **Does not work**: When installed globally via `bun install -g @deep-assistant/agent` +- ❌ **Does not work**: When installed as a dependency in another project + +This is a fundamental security restriction in Bun, as documented in the [official Bun macro documentation](https://bun.sh/docs/bundler/macros): + +> "For security reasons, macros cannot be run from node_modules." + +### Fallback Behavior + +The code is designed to gracefully fallback when macros are unavailable: + +1. If the macro fails (e.g., when running from node_modules), the `data()` function is called normally at runtime +2. The runtime version fetches from `https://models.dev/api.json` or reads from `MODELS_DEV_API_JSON` environment variable +3. Results are cached in the user's cache directory for future use + +## Testing + +### Development Mode (Macros Enabled) + +```bash +# Run from source - macros work +bun run src/index.js +``` + +### Production Mode (Macros Disabled) + +```bash +# Simulate global install behavior +cd /tmp +bun install -g @deep-assistant/agent +echo "hi" | agent +``` + +When installed globally, the macro directive is ignored and runtime fetching is used automatically. + +## Why Keep The Macro? + +Even though macros don't work in production installs, we keep the macro implementation because: + +1. **Development Performance**: Provides faster startup when developing the CLI +2. **Future Compatibility**: If Bun adds ways to bundle/compile packages, macros could work +3. **Graceful Degradation**: The fallback works seamlessly, so there's no downside + +## Alternative Approaches Considered + +### Pre-bundling + +We investigated pre-bundling the application with `bun build` to inline macro results. However: + +- Async macros with network fetching caused the bundler to hang +- This appears to be a limitation or bug in Bun's current macro implementation + +### Cached JSON + +We considered pre-fetching and bundling the models.dev JSON: + +- This would add ~600KB to the package size +- The runtime fetch + cache approach works well and keeps package small +- Users only fetch the data once per cache refresh interval + +## Conclusion + +The current implementation provides the best of both worlds: +- Macro optimization for development +- Reliable runtime fetching for production +- Automatic fallback with no user configuration needed diff --git a/src/provider/models.ts b/src/provider/models.ts index c3eb209..17a2d60 100644 --- a/src/provider/models.ts +++ b/src/provider/models.ts @@ -2,7 +2,7 @@ import { Global } from "../global" import { Log } from "../util/log" import path from "path" import z from "zod" -import { data } from "./models-macro" +import { data } from "./models-macro" with { type: "macro" } export namespace ModelsDev { const log = Log.create({ service: "models.dev" }) From 283742cba981ba433837353797247a40262e2aff Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 1 Dec 2025 20:11:44 +0000 Subject: [PATCH 3/4] Revert "Initial commit with task details for issue #6" This reverts commit 99daa5a49479e7321c7718f4aec3298f12b38816. --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 4397578..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/link-assistant/agent/issues/6 -Your prepared branch: issue-6-097eb11195ee -Your prepared working directory: /tmp/gh-issue-solver-1764619020131 - -Proceed. \ No newline at end of file From dda87d3c615ff72a19a81f85c72ee322b6d02034 Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 1 Dec 2025 20:14:43 +0000 Subject: [PATCH 4/4] Add experiment scripts and build tooling for macro testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds development tooling to demonstrate and verify the macro functionality works as expected: 1. experiments/test-macro.ts - Verifies the macro imports and executes correctly in development mode, fetching ~596KB JSON from models.dev 2. experiments/test-agent-simple.sh - Tests that the agent runs correctly with macro support enabled 3. scripts/build.ts - Experimental build script that attempts to bundle the application with macros evaluated at build time (investigation showed async macros with network fetching cause bundler to hang) These scripts provide evidence that the macro restoration works in development mode and document the pre-bundling investigation. Related: #6 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- experiments/test-agent-simple.sh | 6 ++++++ experiments/test-macro.ts | 12 +++++++++++ scripts/build.ts | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100755 experiments/test-agent-simple.sh create mode 100755 experiments/test-macro.ts create mode 100755 scripts/build.ts diff --git a/experiments/test-agent-simple.sh b/experiments/test-agent-simple.sh new file mode 100755 index 0000000..0422d53 --- /dev/null +++ b/experiments/test-agent-simple.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Test that the agent works with the macro restoration + +echo "Testing agent in development mode..." +echo '{"message": "What is 2+2? Answer in one word."}' | timeout 30 bun run src/index.js --no-server 2>&1 | head -50 diff --git a/experiments/test-macro.ts b/experiments/test-macro.ts new file mode 100755 index 0000000..47b3bc3 --- /dev/null +++ b/experiments/test-macro.ts @@ -0,0 +1,12 @@ +#!/usr/bin/env bun + +/** + * Experiment to test if the macro works in development mode + */ + +import { data } from "../src/provider/models-macro" with { type: "macro" } + +console.log("Testing macro...") +const result = await data() +console.log("Macro result length:", result.length) +console.log("First 200 chars:", result.substring(0, 200)) diff --git a/scripts/build.ts b/scripts/build.ts new file mode 100755 index 0000000..bb3d415 --- /dev/null +++ b/scripts/build.ts @@ -0,0 +1,35 @@ +#!/usr/bin/env bun + +/** + * Build script to bundle the agent CLI with macros support + * + * This bundles the application so that macros are evaluated at build time + * and their results are inlined into the bundle. This avoids the security + * restriction that prevents macros from running in node_modules. + */ + +const result = await Bun.build({ + entrypoints: ['./src/index.js'], + outdir: './dist', + target: 'bun', + format: 'esm', + minify: false, + sourcemap: 'external', + // Macros are enabled by default in Bun.build + splitting: false, // Keep everything in one file for simplicity + packages: 'bundle', // Bundle all dependencies +}) + +if (!result.success) { + console.error('Build failed:') + for (const message of result.logs) { + console.error(message) + } + process.exit(1) +} + +console.log('✓ Build successful!') +console.log(` Generated ${result.outputs.length} output(s)`) +for (const output of result.outputs) { + console.log(` - ${output.path}`) +}