argsbarg 7.1.0 → 7.1.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/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [7.1.1] - 2026-09-25
11
+
12
+ ### Added
13
+
14
+ - **`argsbarg create --template plugin`** — support `"plugin"` choice for scaffolding agent MCP plugins with in-repo Cursor and Claude manifests.
15
+
16
+ ### Changed
17
+
18
+ - **AGENTS.md** — updated Memory section to use `thread-memory` MCP plugin (`recall`/`feedback`) backed by centralized SQLite store.
19
+
20
+ ### Fixed
21
+
22
+ - Headless tool failures (MCP `tools/call` and HTTP JSON `{ "error" }`) now return the full error message (ANSI stripped, newlines preserved) instead of collapsing to the first line.
23
+ - `packClaudePlugin`, `packCursorPlugin`, and `packMcpBundle` staged the compiled binary via `cpSync(src, dest, { mode: 0o755 })` — but `cpSync`'s `mode` option is a bitmask of copy-behavior flags (`COPYFILE_EXCL`/`FICLONE`/`FICLONE_FORCE`, valid range 0–7), not a POSIX file permission mode. `0o755` (493) was always out of range; a stricter Bun `fs.cpSync` validation now throws on it instead of silently ignoring it. Fixed by dropping the bogus `mode` option and calling `chmodSync(dest, 0o755)` after the copy, which is what the code actually intended.
24
+
10
25
  ## [7.1.0] - 2026-09-18
11
26
 
12
27
  ### Changed
@@ -1033,7 +1048,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
1033
1048
  - Migrate schemas: rename every `children` property to **`commands`**; move positional definitions to **`CliPositional`** objects on `positionals` and strip `positional` / `argMin` / `argMax` from flag definitions under `options` (flags only carry `name`, `description`, `kind`, and optional `shortName`).
1034
1049
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
1035
1050
 
1036
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v7.1.0...HEAD
1051
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v7.1.1...HEAD
1052
+ [7.1.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v7.1.1
1037
1053
  [7.1.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v7.1.0
1038
1054
  [7.0.11]: https://github.com/bdombro/bun-argsbarg/releases/tag/v7.0.11
1039
1055
  [7.0.10]: https://github.com/bdombro/bun-argsbarg/releases/tag/v7.0.10
package/docs/mcp.md CHANGED
@@ -196,7 +196,7 @@ On success (`isError: false`):
196
196
  - **stderr** — when non-empty, a second `content` text block with trimmed stderr (no prefix). The block’s position signals stderr; hosts may label it themselves.
197
197
  - **structuredContent** — when trimmed stdout is valid JSON, the parsed value is also returned per the [MCP tools spec](https://modelcontextprotocol.io/specification/draft/server/tools). Objects and arrays from flags like `--json` are the common case. JSON **primitives** (`true`, `42`, `"hello"`) are parsed too — a handler that prints the literal string `true` as human text would get `structuredContent: true`. Prefer objects for machine-readable output.
198
198
 
199
- On failure (parse error, validation error, non-zero exit, thrown error), the message is returned as text content with `isError: true`. Handler stderr is included when present.
199
+ On failure (parse error, validation error, non-zero exit, thrown error), the **full** error message is returned as text content with `isError: true` (ANSI stripped, newlines preserved). HTTP JSON `{ "error": "…" }` uses the same full text. Do not collapse headless errors to the first line.
200
200
 
201
201
  Help and `docs cli-schema` are not available through tool calls; use the schema resource or run the CLI directly for those.
202
202
 
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "mcp-plugin",
3
+ "owner": {
4
+ "name": "Brian Dombrowski"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "mcp-plugin",
9
+ "source": ".",
10
+ "description": "Argsbarg MCP plugin template for Cursor and Claude Code marketplaces"
11
+ }
12
+ ]
13
+ }
@@ -6,6 +6,5 @@
6
6
  "name": "Brian Dombrowski"
7
7
  },
8
8
  "homepage": "https://github.com/bdombro/bun-argsbarg",
9
- "repository": "https://github.com/bdombro/bun-argsbarg",
10
- "skills": ["skills/mcp-plugin"]
9
+ "repository": "https://github.com/bdombro/bun-argsbarg"
11
10
  }
@@ -7,6 +7,5 @@
7
7
  "name": "Brian Dombrowski"
8
8
  },
9
9
  "homepage": "https://github.com/bdombro/bun-argsbarg",
10
- "repository": "https://github.com/bdombro/bun-argsbarg",
11
- "skills": ["skills/mcp-plugin"]
10
+ "repository": "https://github.com/bdombro/bun-argsbarg"
12
11
  }
@@ -11,22 +11,23 @@ Argsbarg MCP plugin template for Cursor and Claude Code marketplaces (@sg schema
11
11
  - **In-repo skills**: `skills/mcp-plugin/SKILL.md` discovered and loaded by agent platforms.
12
12
  - **Standalone runner**: `bun build --target=node src/index.ts --outfile scripts/mcp.mjs` creates an inlined, zero-npm-install script.
13
13
 
14
- ## Development
14
+ ## Installation
15
15
 
16
- ```bash
17
- # Install dependencies and generate schemas
18
- just setup
16
+ ### Cursor
19
17
 
20
- # Build standalone MCP bundle
21
- just build
18
+ Recommended: import directly from GitHub — Cursor Dashboard → **Settings → Plugins → Team Marketplaces → Import**, then enter `https://github.com/bdombro/bun-argsbarg` (subdirectory `examples/mcp-plugin` once copied to its own repo). Once published to the [official marketplace](https://cursor.com/marketplace/publish), install via `/add-plugin mcp-plugin` or the Customize sidebar.
22
19
 
23
- # Test MCP server directly with node
24
- node ./scripts/mcp.mjs mcp
20
+ ### Claude Code
25
21
 
26
- # Link into local Cursor plugins for live testing
27
- just install-plugin-cursor
22
+ Recommended: add the GitHub repo as a marketplace, then install:
23
+
24
+ ```bash
25
+ /plugin marketplace add <owner>/<repo>
26
+ /plugin install mcp-plugin@<repo>
28
27
  ```
29
28
 
29
+ Once merged into [anthropics/claude-plugins-official](https://github.com/anthropics/claude-plugins-official), install via `/plugin install mcp-plugin@claude-plugins-official`.
30
+
30
31
  ## Commands
31
32
 
32
33
  - `mcp-plugin echo` — Echo text back to stdout or inspect flags.
@@ -42,23 +43,24 @@ just install-plugin-cursor
42
43
  - `mcp-plugin mcp` — Start the Model Context Protocol (stdio) server for AI coding agents.
43
44
  - `mcp-plugin version` — Display version information.
44
45
 
45
- ## Distribution & Marketplaces
46
+ ## Contributing / Local Development
46
47
 
47
- ### Cursor
48
+ ```bash
49
+ git clone https://github.com/bdombro/bun-argsbarg.git
50
+ cd bun-argsbarg/examples/mcp-plugin
48
51
 
49
- - **Local Development / Testing**: Run `just install-plugin-cursor` to link the repo into `~/.cursor/plugins/local/mcp-plugin`. Reload the Cursor window to activate.
50
- - **Team Marketplace**: In Cursor Dashboard → **Settings → Plugins → Team Marketplaces → Import**, enter your GitHub repository URL.
51
- - **Official Cursor Marketplace**: Submit your repository URL at [cursor.com/marketplace/publish](https://cursor.com/marketplace/publish). Once approved, users can install directly via `/add-plugin mcp-plugin` or from the Customize sidebar.
52
+ # Install dependencies and generate schemas
53
+ just setup
52
54
 
53
- ### Claude Code
55
+ # Build standalone MCP bundle
56
+ just build
54
57
 
55
- - **Direct Git Install**: Users can add your repository without central registration:
56
- ```bash
57
- /plugin marketplace add <owner>/<repo>
58
- /plugin install mcp-plugin
59
- /reload-plugins
60
- ```
61
- - **Official Directory**: Submit a pull request to [anthropics/claude-plugins-official](https://github.com/anthropics/claude-plugins-official) under `external_plugins/`. Once merged, users can install directly via `/plugin install mcp-plugin@claude-plugins-official`.
58
+ # Test MCP server directly with node
59
+ node ./scripts/mcp.mjs mcp
60
+
61
+ # Link into local Cursor plugins for live testing
62
+ just install-plugin-cursor
63
+ ```
62
64
 
63
65
  ## Documentation
64
66
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "7.1.0",
3
+ "version": "7.1.1",
4
4
  "main": "./src/index.ts",
5
5
  "module": "./src/index.ts",
6
6
  "dependencies": {
@@ -18,7 +18,7 @@ export const program = {
18
18
  name: "template",
19
19
  description: "Template: cli (default) or json (schema-first).",
20
20
  kind: CliOptionKind.Enum,
21
- choices: ["cli", "json"],
21
+ choices: ["cli", "json", "plugin"],
22
22
  },
23
23
  { name: "key", description: "CLI binary name.", kind: CliOptionKind.String },
24
24
  {
@@ -0,0 +1,32 @@
1
+ /* Unit tests for shared headless error text (MCP and HTTP). */
2
+
3
+ import { describe, expect, test } from "bun:test";
4
+ import { type HeadlessToolCallFailure, headlessFailureMcpMessage, headlessFailureToHttpResponse } from "./tool-call.ts";
5
+
6
+ /** Builds a failed headless result with the given error message. */
7
+ function invokeFailure(
8
+ /** Full error text from the leaf. */
9
+ message: string,
10
+ ): HeadlessToolCallFailure {
11
+ return {
12
+ exitCode: 1,
13
+ kind: "invoke",
14
+ message,
15
+ ok: false,
16
+ stderr: "",
17
+ stdout: "",
18
+ };
19
+ }
20
+
21
+ describe("headless error text", () => {
22
+ const multiline =
23
+ 'cannot copy tab "Spec" losslessly (1 smart chip(s): "Ada"). Use force: true.\n' +
24
+ ' • node h.x: contains 1 smart chip(s) ("Ada")\n\n' +
25
+ "Google Docs REST API has no native tab duplication endpoint.";
26
+
27
+ test("MCP and HTTP both keep the full multi-line error", async () => {
28
+ expect(headlessFailureMcpMessage(invokeFailure(multiline))).toBe(multiline);
29
+ const body = (await headlessFailureToHttpResponse(invokeFailure(multiline)).json()) as { error: string };
30
+ expect(body.error).toBe(multiline);
31
+ });
32
+ });
@@ -6,7 +6,7 @@ import { bootstrapAppConfig } from "../config/bootstrap.ts";
6
6
  import { formatMcpMissingConfigMessage, missingRequiredConfig } from "../config/resolve.ts";
7
7
  import type { CliInvocation, CliProgram, InvokeFailureKind } from "../core/types.ts";
8
8
  import { failureKindHttpStatus } from "../hooks/run.ts";
9
- import { apiErrorResponse, apiSuccessResponse, firstErrorLine } from "../http/result.ts";
9
+ import { apiErrorResponse, apiSuccessResponse, stripAnsi } from "../http/result.ts";
10
10
  import { type HttpRouteDef, httpRequestToArgv } from "../http/routes.ts";
11
11
  import { obscureUnexpectedClientMessage } from "../log/emitter.ts";
12
12
  import { buildToolCallSuccessFromResponse } from "../mcp/result.ts";
@@ -188,11 +188,7 @@ export function headlessSuccessToHttpResponse(
188
188
  /** Maps a headless failure result to a JSON HTTP error Response. */
189
189
  export function headlessFailureToHttpResponse(result: HeadlessToolCallFailure, obscureUnexpected = false): Response {
190
190
  const status = resolveHttpErrorStatus(result);
191
- let message = firstErrorLine(result.message);
192
- if (obscureUnexpected && result.failureKind === "unexpected") {
193
- message = obscureUnexpectedClientMessage();
194
- }
195
- return apiErrorResponse(status, { error: message });
191
+ return apiErrorResponse(status, { error: formatHeadlessError(result, obscureUnexpected) });
196
192
  }
197
193
 
198
194
  function resolveHttpErrorStatus(result: HeadlessToolCallFailure): number {
@@ -212,11 +208,26 @@ function resolveHttpErrorStatus(result: HeadlessToolCallFailure): number {
212
208
  }
213
209
 
214
210
  /** Maps invoke failure kind to MCP tools/call error text (respects obscureUnexpected). */
215
- export function headlessFailureMcpMessage(result: HeadlessToolCallFailure, obscureUnexpected = false): string {
211
+ export function headlessFailureMcpMessage(
212
+ /** Failed headless tool invocation. */
213
+ result: HeadlessToolCallFailure,
214
+ /** When true, unexpected failures return a generic client message. */
215
+ obscureUnexpected = false,
216
+ ): string {
217
+ return formatHeadlessError(result, obscureUnexpected);
218
+ }
219
+
220
+ /** Formats a headless failure for MCP text content and HTTP JSON `error` (full message, ANSI stripped). */
221
+ function formatHeadlessError(
222
+ /** Failed headless tool invocation. */
223
+ result: HeadlessToolCallFailure,
224
+ /** When true, unexpected failures return a generic client message. */
225
+ obscureUnexpected: boolean,
226
+ ): string {
216
227
  if (obscureUnexpected && result.failureKind === "unexpected") {
217
228
  return obscureUnexpectedClientMessage();
218
229
  }
219
- return firstErrorLine(result.message);
230
+ return stripAnsi(result.message).trim();
220
231
  }
221
232
 
222
233
  /** Missing-config lookup failures as MCP/HTTP pre-invoke errors. */
package/src/mcp/bundle.ts CHANGED
@@ -3,7 +3,7 @@ Packs a CLI program into an MCP Bundle (`.mcpb`) for Claude Desktop.
3
3
  Expects `dist/<program.key>` as the compiled binary input.
4
4
  */
5
5
 
6
- import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
6
+ import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
7
7
  import { tmpdir } from "node:os";
8
8
  import { basename, join, resolve } from "node:path";
9
9
  import { buildProgramUserConfig } from "../config/manifest.ts";
@@ -119,7 +119,8 @@ export function packMcpBundle(program: CliProgram, opts: PackMcpBundleOpts = {})
119
119
  const staging = mkdtempSync(join(tmpdir(), "mcpb-"));
120
120
  try {
121
121
  const stagedBinary = join(staging, binaryName);
122
- cpSync(binaryPath, stagedBinary, { mode: 0o755 });
122
+ cpSync(binaryPath, stagedBinary);
123
+ chmodSync(stagedBinary, 0o755);
123
124
 
124
125
  const manifest = generateMcpManifest(program, binaryName);
125
126
  const files: { name: string; data: Buffer }[] = [
package/src/mcp/claude.ts CHANGED
@@ -3,7 +3,7 @@ Packs a Claude Code plugin zip from a compiled CLI binary.
3
3
  Internal module — not exported from index.ts.
4
4
  */
5
5
 
6
- import { cpSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
6
+ import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
7
7
  import { tmpdir } from "node:os";
8
8
  import { basename, join, resolve } from "node:path";
9
9
  import { buildPluginMcpEnvMapping, buildProgramUserConfig } from "../config/manifest.ts";
@@ -104,7 +104,9 @@ function writePluginTree(
104
104
  join(pluginRoot, ".mcp.json"),
105
105
  `${JSON.stringify(generatePluginMcpJson(program, binaryName), null, 2)}\n`,
106
106
  );
107
- cpSync(binaryPath, join(pluginRoot, "bin", binaryName), { mode: 0o755 });
107
+ const stagedBinary = join(pluginRoot, "bin", binaryName);
108
+ cpSync(binaryPath, stagedBinary);
109
+ chmodSync(stagedBinary, 0o755);
108
110
  stagePluginSkills(pluginRoot, program, cwd);
109
111
  }
110
112
 
package/src/mcp/cursor.ts CHANGED
@@ -3,7 +3,7 @@ Packs a Cursor plugin zip from a compiled CLI binary.
3
3
  Internal module — not exported from index.ts.
4
4
  */
5
5
 
6
- import { cpSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
6
+ import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
7
7
  import { tmpdir } from "node:os";
8
8
  import { basename, join, resolve } from "node:path";
9
9
  import { buildCursorPluginMcpEnvMapping, buildCursorPluginVariables } from "../config/manifest.ts";
@@ -117,7 +117,9 @@ function writePluginTree(
117
117
  join(pluginRoot, "mcp.json"),
118
118
  `${JSON.stringify(generateCursorPluginMcpJson(program, binaryName), null, 2)}\n`,
119
119
  );
120
- cpSync(binaryPath, join(pluginRoot, "bin", binaryName), { mode: 0o755 });
120
+ const stagedBinary = join(pluginRoot, "bin", binaryName);
121
+ cpSync(binaryPath, stagedBinary);
122
+ chmodSync(stagedBinary, 0o755);
121
123
  stagePluginSkills(pluginRoot, program, cwd);
122
124
  }
123
125