Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "genesis-tools",
"version": "1.0.102",
"version": "1.0.107",
"description": "Plugins for GenesisTools CLI development and management",
"owner": {
"name": "genesiscz",
Expand All @@ -11,7 +11,7 @@
{
"name": "genesis-tools",
"description": "Skills and utilities for working with GenesisTools CLI toolkit. Provides guidance for discovering, executing, and troubleshooting genesis tools with integrated workflow support.",
"version": "1.0.102",
"version": "1.0.107",
"author": {
"name": "GenesisTools"
},
Expand Down
2 changes: 1 addition & 1 deletion plugins/genesis-tools/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genesis-tools",
"version": "1.0.102",
"version": "1.0.107",
"description": "Skills and utilities for working with GenesisTools CLI toolkit. Provides guidance for discovering, executing, and troubleshooting genesis tools with integrated workflow support.",
"author": {
"name": "GenesisTools"
Expand Down
2 changes: 1 addition & 1 deletion plugins/genesis-tools/hooks/native-inbox-hint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const RUNNING_HINT =
'INBOX: the user\'s GenesisTools widget is running on this Mac, and its inbox reaches the user even when they are not watching this chat. Post there only when you need the user: a decision you cannot make yourself, as a question_post item with type "decision" (CLI: `tools question ask --json -`); or a finished long task, a blocker, or a result they must see (attach the screenshot), as inbox_send with the screenshot paths in `images` (CLI: `tools question message "<text>" --image /abs/shot.png`). Never post routine progress or anything you can decide yourself. Also write every question in your chat reply.';

export const INSTALLED_HINT =
'INBOX: the GenesisTools app is installed on this Mac, but its widget is not running, so nobody may see the inbox. You may still post a decision, as a question_post item with type "decision" (CLI: `tools question ask --json -`), or a message or screenshot, as inbox_send with the screenshot paths in `images` (CLI: `tools question message "<text>" --image /abs/shot.png`). But ALWAYS also ask the user directly in this chat, with your native question tool or in your reply.';
'INBOX: the GenesisTools app is installed on this Mac, but its widget is not running. A post is still stored and reaches the user when they turn the widget on, so keep posting: a decision as a question_post item with type "decision" (CLI: `tools question ask --json -`), a message or screenshot as inbox_send with the screenshot paths in `images` (CLI: `tools question message "<text>" --image /abs/shot.png`). Until the widget is on, the user sees only this chat, so ALWAYS also write each question in your reply, as it is.';

export interface InboxStateRecord {
installed?: unknown;
Expand Down
21 changes: 10 additions & 11 deletions plugins/genesis-tools/skills/research/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,18 @@ Always record what was missing and what substitution was used in `Confidence & G

### Offer install (at most once per run)

If a missing MCP would materially improve the run AND the user didn't say "quick"/"temporary"/"just tell me", ask ONCE before dispatch (`AskUserQuestion`, `request_user_input_async` or `ask_user_question`, whichever your harness has):
If a missing MCP would materially improve the run AND the user didn't say "quick"/"temporary"/"just tell me", offer it ONCE, and dispatch with the degraded toolset without waiting: an install cannot help THIS run anyway (step 3 below). Write the offer in your reply, not in a native question tool (`AskUserQuestion` stops the run until the user answers, and this is not a blocker):
Comment thread
genesiscz marked this conversation as resolved.

- **Question:** "I'd dispatch this with `<MCP-name>` for `<one-line value-add>`, but it's not installed. Want install instructions, or proceed without?"
- **Options:** `Install now`, `Skip and proceed`, `Don't ask again this run`.
- **Offer:** "I dispatched this without `<MCP-name>`, which would add `<one-line value-add>`. Say "install" for the install steps."

If the user picks **Install now**, read `references/mcps.md` from this skill directory and output:
If the user answers **install**, read `references/mcps.md` from this skill directory and output:
1. The `bun add --global <package>` command (if local) or "no install — hosted HTTP MCP" (if remote).
2. The exact `mcpServers` JSON snippet for the user to paste into their Claude config (or the equivalent `claude mcp add` one-liner).
3. Note that the user must restart their agent for the new server to become callable (Claude Code and Codex reload MCP config on restart; Grok re-reads `~/.grok/config.toml` on start too), and the skill will proceed without it for THIS run.

The skill does **not** execute install commands itself. The user runs the install. The skill then proceeds with whatever is currently available.

If the user picks **Skip** or **Don't ask again**, dispatch immediately with the degraded toolset.
Do not repeat the offer for another missing MCP in the same run.

## Categories (6)

Expand Down Expand Up @@ -314,10 +313,10 @@ Resolve the vault path in this order; **stop at the first hit:**

### Persist the resolved path (offer once)

If the path came from steps 2–5 (i.e. it was **not** already in `$OBSIDIAN_VAULT_PATH`) — and this isn't a "temporary"/"quick" run — offer once to persist it so future runs skip detection. **Exception:** if the path came from step 2 (a user-named path), only offer to persist it when it's actually a vault root (contains a `.obsidian/` subdir); a one-off subfolder like `~/Notes/scratch` should be used for this run but never saved as the global `OBSIDIAN_VAULT_PATH`. `AskUserQuestion`:
If the path came from steps 2–5 (i.e. it was **not** already in `$OBSIDIAN_VAULT_PATH`) — and this isn't a "temporary"/"quick" run — offer once to persist it so future runs skip detection. **Exception:** if the path came from step 2 (a user-named path), only offer to persist it when it's actually a vault root (contains a `.obsidian/` subdir); a one-off subfolder like `~/Notes/scratch` should be used for this run but never saved as the global `OBSIDIAN_VAULT_PATH`. Write the offer in your reply and keep working with the resolved path; persist only when the user answers. It is not a blocker, so not a native question tool:

- **Question:** "Save `OBSIDIAN_VAULT_PATH=<path>` so I can skip detection next time?"
- **Options:**
- **Offer:** "Save `OBSIDIAN_VAULT_PATH=<path>` so I can skip detection next time?"
- **Choices:**
- **`~/.claude/settings.json`** — Claude-only; the harness injects the `env` block into Claude Code's environment at session start. Add/replace `OBSIDIAN_VAULT_PATH` inside the top-level `env` object (create `env` if absent). **Use the Read + Edit tools to splice the one key in place** — do NOT parse-and-rewrite the whole file (settings.json is JSONC; a strict `JSON.parse`/stringify round-trip would strip comments and reformat unrelated keys).
- **Shell rc** — all tools, all shells. Detect from `$SHELL`: zsh → `~/.zshrc`, bash → `~/.bashrc`, fish → `~/.config/fish/config.fish`. Append `export OBSIDIAN_VAULT_PATH="<path>"` (fish: `set -Ux OBSIDIAN_VAULT_PATH "<path>"`). If a line already sets it, replace that line instead of duplicating.
- **`Just this run`** — don't persist.
Expand Down Expand Up @@ -482,9 +481,9 @@ SAVE RAW REPORT TO: <exact absolute path, provided by main Claude>
- **Source minimums are hard** (see table above). No invention, no padding.
- **Always run `scripts/resolve.ts` when saving, and surface its `warnings`.** Never hardcode `.claude/work/research/`, never read the config file yourself, and never fall back to `defaultPath` when a project declares a resolver that failed.
- **Ask the default path at most once until remembered.** If `config.json` is missing or unusable and this run will save, ask then write the file. Do not re-ask on later runs while the config is valid. "Change research path" is the explicit re-ask trigger.
- **`AskUserQuestion` budget.** Up to **three** for the core run: once for the optional install offer, once for ambiguity/depth, once for the default research path when config is unset. **Plus up to two more** — *only* when an Obsidian path is required and `$OBSIDIAN_VAULT_PATH` isn't already set: one for vault resolution (pick-from-list **or** free-text, never both) and one for the vault persist offer. Never prompt about the vault when no Obsidian path is needed.
- **`AskUserQuestion` budget.** Up to **two** for the core run: once for ambiguity/depth, once for the default research path when config is unset. **Plus one more**, *only* when an Obsidian path is required and `$OBSIDIAN_VAULT_PATH` isn't already set: vault resolution (pick-from-list **or** free-text, never both). The optional install offer and the vault persist offer are written in the reply and never wait for an answer. Never prompt about the vault when no Obsidian path is needed.
- **Tool-availability fallback.** Missing MCP → drop from allowlist, substitute per the table, note in "Confidence & Gaps".
- **Install offer is opt-in**, never automatic. Default to Skip when in doubt. Never run install commands on behalf of the user — show the command and config snippet only.
- **Install offer is opt-in**, never automatic, and the run never waits for it. Leave it out when in doubt. Never run install commands on behalf of the user — show the command and config snippet only.
- **Vault detection is gated on an Obsidian path being needed**, and persists only with explicit consent. Never auto-edit `settings.json` or a shell rc without the user picking that option in the persist offer.
- **"Temporary" override.** Query contains "temporary", "just tell me", "don't save", "quick", "no need to save" → skip file write, skip install offer, skip default-path ask, skip the vault persist offer, inline only. Source minimums still enforced.
- **Concurrency cap = 4 parallel agents** per research run. If a preset would spawn more, drop the lowest-priority angle.
Expand All @@ -499,7 +498,7 @@ User: "what's the current stable version of Bun?"

**Example 2 — comparison, first save (no research config yet), reddit MCP missing**
User: "research state management options for React in 2026"
→ category=`comparison`, `needsSave=true`, `scripts/resolve.ts` answers `found: false` → ask once where research for this project belongs. User picks Project `.claude/work/research/`. Write the `research` key of the shared config (`pathKind: project-relative`, `defaultPath: .claude/work/research`). Detect `reddit-mcp-server` missing, ask once: install / skip / don't ask. User picks Install. Skill outputs `bun add --global reddit-mcp-server` and the config snippet from `references/mcps.md`. User restarts later. Skill proceeds with Sonnet docs + Haiku GitHub (Reddit angle dropped this run, gap recorded). 8 sources combined, save to `<repo>/.claude/work/research/2026-04-27-ReactStateManagement2026.md`.
→ category=`comparison`, `needsSave=true`, `scripts/resolve.ts` answers `found: false` → ask once where research for this project belongs. User picks Project `.claude/work/research/`. Write the `research` key of the shared config (`pathKind: project-relative`, `defaultPath: .claude/work/research`). Detect `reddit-mcp-server` missing and dispatch at once with Sonnet docs + Haiku GitHub (Reddit angle dropped this run, gap recorded). The reply offers the install once, in text. The user answers "install" later, and the skill outputs `bun add --global reddit-mcp-server` and the config snippet from `references/mcps.md`; it takes effect after a restart. 8 sources combined, save to `<repo>/.claude/work/research/2026-04-27-ReactStateManagement2026.md`.

**Example 3 — sentiment, user declined install**
User: "what do people complain about with tRPC, quick"
Expand Down
7 changes: 2 additions & 5 deletions plugins/genesis-tools/skills/research/references/mcps.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@ Check whether an MCP is available in the current session by looking at the syste
When the skill decides an MCP would materially improve a research run and it's missing, it MUST:

1. Tell the user **what the MCP adds** to this run, in one sentence ("brave-search would let me cross-check facts against a second search index").
2. Offer install via `AskUserQuestion` with three choices:
- **Install now** (recommended) — show the install command and config snippet, then continue once they confirm install.
- **Skip** — proceed without it, note the gap in `Confidence & Gaps`.
- **Don't ask again this run** — proceed and don't re-prompt for any other missing MCP this run.
3. If the user picks Install now, output the **install command** AND the **config snippet** the user pastes into their Claude Code MCP config (the user runs the install themselves; the skill does not auto-run install commands).
2. Offer the install in the reply, once per run, and dispatch without it: an install takes effect only in a new session, so the run never waits for the answer. Not a native question tool such as `AskUserQuestion`: it stops the run, and an optional install is not a blocker. Note the gap in `Confidence & Gaps`.
3. If the user answers "install", output the **install command** AND the **config snippet** the user pastes into their Claude Code MCP config (the user runs the install themselves; the skill does not auto-run install commands).

Default to **Skip** if the run is small, low-stakes, or the user typed "quick"/"just tell me"/"temporary".

Expand Down
3 changes: 2 additions & 1 deletion src/genesis-tools-mcp/lib/tools/question-answer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ describe("inbox_send", () => {
);

expect(receipt.context).toMatchObject({ agent: "codex", sessionId: "codex-thread" });
expect(receipt.summary).toContain("also tell the user in this chat");
expect(receipt.summary).toContain("Tell the user in this chat now as well");
expect(receipt.summary).toContain("keep sending both ways");
});

it("refuses a message with neither text nor image, and an unknown session gets the warning", async () => {
Expand Down
5 changes: 3 additions & 2 deletions src/genesis-tools-mcp/lib/tools/question-post.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -504,15 +504,16 @@ describe("the opt-in nudge", () => {
const [instructions, description] = texts(askViaQuestionTool, "installed");

expect(instructions).toContain(inboxInstructions("installed"));
expect(instructions).toContain("ALWAYS also ask the user directly in this chat");
expect(instructions).toContain("ALWAYS also write each question in your reply");
expect(description).toContain("widget is not running");
expect(description).not.toContain("Post every ❓ DECISION");

const note = await handleQuestionPost(
{ sessionHint: "sess-1", items: [decision] },
withLog(askViaQuestionTool, "installed")
);
expect(note).toContain("Ask the user directly in this chat now");
expect(note).toContain("Write the question in your chat reply now");
expect(note).toContain("only for a real blocker");
}
});

Expand Down
Loading
Loading