feat(install): --env + --skill flags (re-PR after squash collapsed the stack) - #47
Merged
Conversation
The companion to PR #44 (env-aware memory backend). Now the install CLI can write the env block into client MCP configs, so `agentmark-mcp install --env=THINKFLEET_API_KEY=sk-…` makes the memory plugin in PR #44 actually pick up SaaS creds at startup. Surface: agentmark-mcp install \\ --client=claude-code \\ --env=THINKFLEET_BASE_URL=https://app.thinkfleet.ai \\ --env=THINKFLEET_PROJECT_ID=proj_xxx \\ --env=THINKFLEET_API_KEY=sk-xxx The values land in the MCP server entry's `env` field — Claude Code, Cursor, Windsurf, Codex CLI all already honor that. Refactor: - parseFlags / parseEnvFlag / buildEntryFromFlags moved from cli.ts to src/mcp/install/flags.ts. cli.ts imports them. The extraction is needed because tests can't import cli.ts directly (it has top-level main() side effects that spawn the MCP server). Validation (all of these MUST throw): - Missing or empty key: --env=NOEQUALS, --env==value - Shell-relevant chars in key: --env=FOO;rm=anything - Whitespace in key: --env=FOO BAR=x - Key starting with a digit: --env=1FOO=bar - Null byte in value: --env=FOO=before\0after - Value over 4KB cap: prevents config-bomb injection Behavior: - Duplicate --env=KEY=… prints a stderr warning and uses last. Warning contains only the key name; values never echo. - `--env=FLAG=` (empty value) is legal — some env-driven flags expect that. - Mixed-case keys allowed (NodeEnv-style); strictly alphanumeric + underscore otherwise. Security note (codified in HELP text): - --env values are written to the client's MCP config file on disk (e.g. ~/.cursor/mcp.json). Prefer rotating from an OS keychain rather than passing long-lived keys on a shared machine. - ThinkFleet Desktop (PR γ, upcoming) wires this from Electron safeStorage so secrets live in the OS keychain at rest and only surface as --env at install time. Tests: - test/mcp/install-flags.test.ts — 22 cases covering each validation branch + the duplicate-key warning semantics. - test/mcp/install.test.ts — 2 new e2e cases proving the env block lands in the written JSON and that re-running with new values updates idempotently. 564 tests pass. Typecheck clean. No new dependencies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The third tier of the memory bridge slice. PR #44 made the memory plugin auto-select the SaaS backend. PR #45 lets the install CLI write the env block into client configs. This PR adds the skill file that teaches the agent WHEN and HOW to use the memory tools — without it, an agent has the tools but rarely calls them at the right moments. What's a "skill" here: Markdown content (with optional YAML frontmatter) that an AI client loads automatically at session start. Each major coding assistant has a slot for it: - Claude Code: ~/.claude/skills/<name>/skill.md - Claude Desktop: ~/Library/Application Support/Claude/skills/... For tools without a native skills concept (Cursor, Windsurf, Codex CLI), the same content gets wrapped in marker comments and surgically injected into their rules file. That codepath is scaffolded here (upsertManagedBlock + non-exclusive targets) but no rules-file clients are in the default target list yet — adding them needs per-tool research on the rules-file location semantics. Canonical skill — thinkfleet-memory: - Tells the agent to call agentmark_memory_search at session start to load project context. - Tells it to save user preferences, project facts, decisions without being asked. - Tells it to search memory before guessing about the user's environment. - Includes a scope picker, failure modes, and an explicit "what NOT to do" section (don't dump every memory at the user, don't save secrets, don't overwrite user-scope with session-scope writes). - Inlined as a TypeScript constant (~5KB) so the skill ships with the npm package without separate asset bundling. CLI surface: agentmark-mcp install \\ --client=claude-code \\ --env=THINKFLEET_BASE_URL=... \\ --env=THINKFLEET_PROJECT_ID=... \\ --env=THINKFLEET_API_KEY=... \\ --skill=thinkfleet-memory Validation: - Skill name must match [a-z0-9][a-z0-9-]* — rejects path- traversal attempts (../escape) and shell-relevant chars. - Unknown skill names log to stderr and the install still returns a non-zero exit so CI catches typos. - Atomic write via temp-file + rename, 0644 perms. - Idempotent: same content → "already_present"; different content → "updated"; non-existent → "added". Marker-block logic (pure function, fully tested): upsertManagedBlock(text, block) — replaces an existing <!-- thinkfleet:skill:start --> ... <!-- thinkfleet:skill:end --> block in place, or appends one if absent. Preserves user content outside the markers verbatim. Tests: - test/mcp/install-skills.test.ts — 16 cases covering: - upsertManagedBlock: append-empty, append-with-trailing-NL, replace-existing, idempotent, preserve-user-content. - installSkill end-to-end with fake targets: native (whole file), rules-file (marker block), already_present idempotency, dryRun, unsupported-platform skip. - Built-in skill catalog: thinkfleet-memory is registered, unknown names return null, every memory tool name appears in the skill content. - test/mcp/install-flags.test.ts — 6 new cases for --skill name validation. 596 tests pass. Typecheck clean. No new dependencies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Re-PR of #45 + #46 against main. The originals were stacked PRs whose squash-merge of #44 collapsed the base branches and dropped the child content from main even though GitHub reported them as merged. Verifiable in the commit log:
git log origin/mainshows only #44.Bundled as one PR this time so the same merge accident can't happen again.
What's in
From #45 —
--env=KEY=VALUEon installagentmark-mcp install --env=KEY=VALwrites that env block into each AI client's MCP config[A-Za-z_][A-Za-z0-9_]*(no shell metacharacters), 4 KiB value cap, null bytes rejected, duplicate keys warn-and-overwrite (warning contains the key name but never the value)parseFlags / parseEnvFlag / buildEntryFromFlagsmoved fromcli.tstosrc/mcp/install/flags.tsso tests can import without spawning the MCP serverFrom #46 —
--skill=<name>on installagentmark-mcp install --skill=thinkfleet-memorywrites the skill content into the right per-tool path (Claude Code / Claude Desktop today; rules-file targets for Cursor/Windsurf/Codex are scaffolded but not in the default target list yet)thinkfleet-memoryskill (~5KB markdown with YAML frontmatter) — teaches the agent to callagentmark_memory_*at session start, save preferences without being asked, search before guessing about the user's environment[a-z0-9][a-z0-9-]*— rejects path-traversal (../escape) and shell-relevant charsalready_present; different →updatedupsertManagedBlockfor future rules-file targets — fully testedTests
test/mcp/install-flags.test.ts— 28 cases (env validation + skill flag validation)test/mcp/install-skills.test.ts— 16 cases (skill installer, marker-block logic, built-in catalog)Typecheck clean.
Why this is one PR not two
The original PRs were stacked, which made the GitHub UX clean during review but broke catastrophically at merge time — GitHub silently dropped #45 and #46 from main when #44 squash-merged. Consolidating into one PR removes the failure mode.
Once this lands
Cut
@thinkfleet/agentmark@0.13.0— version bump + CHANGELOG +npm publish. I'll prep that release PR the moment this merges.🤖 Generated with Claude Code