tachibot-mcp 2.26.0 → 2.26.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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### Multi-Model AI Orchestration Platform
6
6
 
7
- [![Version](https://img.shields.io/badge/version-2.26.0-blue.svg)](https://www.npmjs.com/package/tachibot-mcp)
7
+ [![Version](https://img.shields.io/badge/version-2.26.1-blue.svg)](https://www.npmjs.com/package/tachibot-mcp)
8
8
  [![Tools](https://img.shields.io/badge/tools-64_active-brightgreen.svg)](#-tool-ecosystem-64-tools)
9
9
  [![License](https://img.shields.io/badge/license-AGPL--3.0-green.svg)](LICENSE)
10
10
  [![Node](https://img.shields.io/badge/node-%3E%3D22.0.0-brightgreen.svg)](https://nodejs.org)
@@ -28,25 +28,32 @@ from Claude Code, Claude Desktop, Cursor, or any MCP client.
28
28
 
29
29
  ---
30
30
 
31
- ## What's New in v2.26.0
31
+ ## What's New
32
32
 
33
- ### Prompt stack, modernized
33
+ ### v2.26.1
34
+ - **`/test` and `/audit` skills** (19 skills total) — `/test` generates runnable tests via `testgen`; `/audit` runs an OWASP/CWE security review via `security_review`.
35
+ - **Skill install in the wizard** — `tachibot init` now offers to install Claude Code skills with a per-skill skip choice (`[Enter]`=all · `[s]`=choose which to skip · `[n]`=none). Skills are opt-in — `postinstall` no longer writes to `~/.claude` silently (`npm run install-skills` still installs all non-interactively).
36
+ - **Fixes** — the one-click `.mcpb` extension now points at a valid entry point (was broken) and tracks the package version; `tachibot init` exits cleanly on non-interactive/CI stdin instead of hanging.
37
+
38
+ ### v2.26.0
39
+
40
+ #### Prompt stack, modernized
34
41
  - **`refine_prompt`** (new tool) — opt-in prompt improver on a cheap/fast model: raw query → goal-first brief + **what changed** + **open questions**. Never auto-fires, never executes anything — you review, then use the brief. In Claude Code, `/prompt refine` presents the open questions as clickable choices and merges your answers into a final brief.
35
42
  - **Curated technique list** — `list_prompt_techniques` now defaults to the ~9 core techniques that still help 2026 reasoning models (output contracts like `scot`, `pre_mortem`, `bdd_spec`); `all=true` for the full 31.
36
43
  - **`technique="auto"`** — `preview_prompt_technique` recommends the right technique for your task, with reasons. Ask `tachi` "improve my prompt" for the symptom-based menu.
37
44
 
38
- ### Setup, de-mystified
45
+ #### Setup, de-mystified
39
46
  - **`tachibot init`** (new CLI wizard) — detects your API keys and clients, prints the exact config for Claude Code and Claude Desktop. Never writes or echoes keys.
40
47
  - **One-click Claude Desktop install** — download the `.mcpb` from the latest release and double-click. No JSON editing.
41
48
  - **`doctor`** — shows which keys are set, which tools are visible vs hidden and why, and what to try first.
42
49
 
43
- ### New tools & skills (64 tools · 17 skills)
50
+ #### New tools & skills (64 tools · 19 skills)
44
51
  - `debug_triage` — ranked root-cause hypotheses with the cheapest discriminating check for each (Grok 4.3)
45
52
  - `spec_writer` — loose request → reviewable spec: user stories, Given/When/Then, out-of-scope, open questions (GPT-5.5)
46
53
  - `diff_review` / `plan_critique` / `testgen` / `security_review` — multi-model diff review, adversarial plan red-team, test generation, OWASP/CWE audit
47
54
  - Skills: `/review`, `/redteam`, `/spec`, `/triage`, `/setup`
48
55
 
49
- ### Fixes
56
+ #### Fixes
50
57
  - `focus` orchestration screen: 37 lines of repeated scaffolding → 10 focused lines
51
58
  - `npm test` exits 0 again (uncancelled race timers leaked past Jest teardown)
52
59
  - GPT-5.5 high-effort reasoning no longer cut off at 3 minutes (timeout 180s → 600s)
@@ -55,7 +62,7 @@ from Claude Code, Claude Desktop, Cursor, or any MCP client.
55
62
 
56
63
  ## Skills (Claude Code)
57
64
 
58
- TachiBot ships with 17 slash commands for Claude Code. These orchestrate the tools into powerful workflows:
65
+ TachiBot ships with 19 slash commands for Claude Code. These orchestrate the tools into powerful workflows:
59
66
 
60
67
  | Skill | What it does | Example |
61
68
  |-------|-------------|---------|
@@ -75,6 +82,8 @@ TachiBot ships with 17 slash commands for Claude Code. These orchestrate the too
75
82
  | `/review` | Multi-model diff review — panel + Gemini judge verdict | `/review` (or paste a diff) |
76
83
  | `/redteam` | Adversarial plan red-team — pre-mortem, risks, plan edits | `/redteam <paste plan>` |
77
84
  | `/triage` | Ranked root-cause bug triage | `/triage <paste stack trace>` |
85
+ | `/test` | Generate runnable tests (edge cases first) | `/test src/auth.ts` |
86
+ | `/audit` | Security review — OWASP/CWE findings + fixes | `/audit the login handler` |
78
87
  | `/tachi` | Help - see available skills, tools, key status | `/tachi` |
79
88
 
80
89
  Skills automatically adapt to your configured API keys. Even with just 1-2 providers, all skills work.
@@ -8,6 +8,7 @@ import * as fs from "node:fs";
8
8
  import * as os from "node:os";
9
9
  import * as path from "node:path";
10
10
  import * as readline from "node:readline/promises";
11
+ import { fileURLToPath } from "node:url";
11
12
  const KEYS = [
12
13
  { name: "OpenRouter", envVar: "OPENROUTER_API_KEY", unlocks: "DeepSeek/GLM/Kimi/Qwen/MiniMax/StepFun/ERNIE + planner (~30 tools)" },
13
14
  { name: "Perplexity", envVar: "PERPLEXITY_API_KEY", unlocks: "web research tools" },
@@ -86,6 +87,62 @@ export function buildDesktopSnippet(setup, profile) {
86
87
  return JSON.stringify({ mcpServers: { tachibot: { command: "tachibot", env } } }, null, 2);
87
88
  }
88
89
  const mask = (v) => (v ? `${v.slice(0, 6)}…` : "");
90
+ /** The package's bundled skills dir. dist/src/cli/init.js → up 3 → pkg root. */
91
+ export function resolveSkillsDir() {
92
+ const here = path.dirname(fileURLToPath(import.meta.url));
93
+ return path.resolve(here, "..", "..", "..", "skills");
94
+ }
95
+ /** Read `skills/<name>/SKILL.md`, pulling the frontmatter `description`. */
96
+ export function listAvailableSkills(skillsDir) {
97
+ let entries;
98
+ try {
99
+ entries = fs.readdirSync(skillsDir).sort();
100
+ }
101
+ catch {
102
+ return [];
103
+ }
104
+ const skills = [];
105
+ for (const name of entries) {
106
+ let text;
107
+ try {
108
+ text = fs.readFileSync(path.join(skillsDir, name, "SKILL.md"), "utf8");
109
+ }
110
+ catch {
111
+ continue;
112
+ } // not a skill dir
113
+ const m = text.match(/^description:\s*(.+)$/m);
114
+ skills.push({ name, description: m ? m[1].trim() : "" });
115
+ }
116
+ return skills;
117
+ }
118
+ /**
119
+ * Parse a "skip which" answer ("1,3, 5") into a set of 0-based indices over a
120
+ * list of `count`. Blanks, non-numbers, and out-of-range tokens are ignored so
121
+ * a fat-fingered entry never throws or skips the wrong skill.
122
+ */
123
+ export function parseSkipSelection(input, count) {
124
+ const skip = new Set();
125
+ for (const tok of input.split(/[\s,]+/).filter(Boolean)) {
126
+ const n = Number(tok);
127
+ if (Number.isInteger(n) && n >= 1 && n <= count)
128
+ skip.add(n - 1);
129
+ }
130
+ return skip;
131
+ }
132
+ /** Copy the chosen skills' SKILL.md into `targetDir/<name>/`. Returns installed names. */
133
+ export function installSkills(names, skillsDir, targetDir) {
134
+ const installed = [];
135
+ for (const name of names) {
136
+ const src = path.join(skillsDir, name, "SKILL.md");
137
+ if (!fs.existsSync(src))
138
+ continue;
139
+ const dest = path.join(targetDir, name);
140
+ fs.mkdirSync(dest, { recursive: true });
141
+ fs.copyFileSync(src, path.join(dest, "SKILL.md"));
142
+ installed.push(name);
143
+ }
144
+ return installed;
145
+ }
89
146
  export async function runInitWizard() {
90
147
  const setup = detectSetup();
91
148
  const out = (s) => process.stdout.write(s + "\n");
@@ -100,12 +157,27 @@ export async function runInitWizard() {
100
157
  out("\nClients detected:");
101
158
  out(` ${setup.clients.claudeCode ? "✓" : "✗"} Claude Code (claude on PATH)`);
102
159
  out(` ${setup.clients.claudeDesktop ? "✓" : "✗"} Claude Desktop${setup.clients.desktopConfigPath ? ` (${setup.clients.desktopConfigPath})` : ""}`);
160
+ // Non-interactive (piped stdin / CI): prompting would hang on EOF with an
161
+ // "unsettled top-level await". Print the config for both clients with sane
162
+ // defaults and how to install skills, then exit cleanly.
163
+ if (!process.stdin.isTTY) {
164
+ out("\n(non-interactive: printing full config — run in a terminal to choose options)\n");
165
+ out("— Claude Code —\n");
166
+ out(buildClaudeCodeCommand(setup));
167
+ out("\n— Claude Desktop — double-click the .mcpb from GitHub releases, or merge into");
168
+ out((setup.clients.desktopConfigPath ?? desktopConfigPath()) + ":\n");
169
+ out(buildDesktopSnippet(setup, "full"));
170
+ const n = listAvailableSkills(resolveSkillsDir()).length;
171
+ out(`\nClaude Code skills (${n}): install with npm run install-skills (or re-run this wizard in a terminal to choose which).`);
172
+ out("\nOnce connected, run the `doctor` tool to see which tools your keys unlock.");
173
+ return;
174
+ }
103
175
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
104
176
  try {
105
177
  const choice = (await rl.question("\nSet up for: [1] Claude Code [2] Claude Desktop [3] both [q] quit > ")).trim();
106
178
  if (choice === "q")
107
179
  return;
108
- const profile = (await rl.question("Profile [full=all 63 tools | balanced | code_focus] (default: full) > ")).trim() || "full";
180
+ const profile = (await rl.question("Profile [full=all 64 tools | balanced | code_focus] (default: full) > ")).trim() || "full";
109
181
  if (choice === "1" || choice === "3") {
110
182
  out("\n— Claude Code — run this (fill in your real keys):\n");
111
183
  out(buildClaudeCodeCommand(setup));
@@ -117,6 +189,27 @@ export async function runInitWizard() {
117
189
  out(buildDesktopSnippet(setup, profile));
118
190
  out("\nThen restart Claude Desktop.");
119
191
  }
192
+ // Skills — Claude Code slash commands, opt-in with per-skill skip.
193
+ const skillsDir = resolveSkillsDir();
194
+ const available = listAvailableSkills(skillsDir);
195
+ if (available.length > 0 && choice !== "2") {
196
+ const target = path.join(os.homedir(), ".claude", "skills");
197
+ const ans = (await rl.question(`\nInstall ${available.length} Claude Code skills to ${target}? [Enter]=all · [s]=choose which to skip · [n]=none > `)).trim().toLowerCase();
198
+ if (ans !== "n") {
199
+ let chosen = available.map((s) => s.name);
200
+ if (ans === "s") {
201
+ out("");
202
+ available.forEach((s, i) => out(` ${String(i + 1).padStart(2)}. /${s.name} — ${s.description}`));
203
+ const skip = parseSkipSelection((await rl.question("\nSkip which? (comma-separated numbers, blank = skip none) > ")).trim(), available.length);
204
+ chosen = available.filter((_, i) => !skip.has(i)).map((s) => s.name);
205
+ }
206
+ const installed = installSkills(chosen, skillsDir, target);
207
+ out(`\n✓ Installed ${installed.length} skill${installed.length === 1 ? "" : "s"}: ${installed.map((n) => "/" + n).join(", ")}`);
208
+ const skipped = available.filter((s) => !installed.includes(s.name));
209
+ if (skipped.length)
210
+ out(` Skipped: ${skipped.map((s) => "/" + s.name).join(", ")}`);
211
+ }
212
+ }
120
213
  out("\nFirst thing to run once connected: the `doctor` tool — it shows which tools your keys unlock.");
121
214
  }
122
215
  finally {
@@ -143,6 +143,8 @@ export const SKILLS = [
143
143
  { name: "redteam", desc: "Adversarial plan red-team (pre-mortem, risks, edits)" },
144
144
  { name: "spec", desc: "Loose request to reviewable spec before planning" },
145
145
  { name: "triage", desc: "Ranked root-cause bug triage from an error/trace" },
146
+ { name: "test", desc: "Generate runnable tests (edge cases first) via testgen" },
147
+ { name: "audit", desc: "Security review: OWASP/CWE findings + fixes via security_review" },
146
148
  { name: "setup", desc: "Guided configuration — runs doctor, walks keys/profiles" },
147
149
  { name: "tachi", desc: "Help & discovery" },
148
150
  ];
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "tachibot-mcp",
3
3
  "mcpName": "io.github.byPawel/tachibot-mcp",
4
4
  "displayName": "TachiBot MCP - Universal AI Orchestrator",
5
- "version": "2.26.0",
5
+ "version": "2.26.1",
6
6
  "type": "module",
7
7
  "main": "dist/src/server.js",
8
8
  "bin": {
@@ -47,7 +47,8 @@
47
47
  "package:extension": "npm run build && ./scripts/package-extension.sh",
48
48
  "build:mcpb": "npm run package:extension",
49
49
  "verify": "node scripts/verify-installation.js",
50
- "postinstall": "bash scripts/install-skills.sh 2>/dev/null || true",
50
+ "postinstall": "node scripts/postinstall-hint.js || true",
51
+ "install-skills": "bash scripts/install-skills.sh",
51
52
  "add-tool": "plop tool",
52
53
  "test:golden": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/golden",
53
54
  "test:smoke": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/smoke"
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: audit
3
+ description: Use when the user wants a security review of code or a diff — OWASP/CWE findings with severity, taint/data-flow analysis, and concrete fixes via security_review
4
+ user-invocable: true
5
+ ---
6
+
7
+ # /audit — Security Review
8
+
9
+ Run a dedicated security audit with the `security_review` tool — it carries an attacker mental model (taint/data-flow, OWASP/CWE), not a generic code review.
10
+
11
+ ## Steps
12
+
13
+ 1. Scope the review: a diff (paste it, or `git diff`) to check changed code only, or `code` / `files` for a whole component.
14
+ 2. Add what sharpens it: `language`/framework, and `context` for the trust boundaries (e.g. "public internet-facing API", "internal service behind VPN", "handles untrusted uploads").
15
+ 3. Choose `standard`: `owasp`, `cwe`, or `both` (default).
16
+ 4. Call `security_review({ code | diff | files, language?, context?, standard? })`.
17
+ 5. Relay findings led by the highest severity — each has a CWE/OWASP ref, a short exploitability sketch, and a concrete fix. Offer to apply the top fix.
18
+
19
+ For code you are authorized to review. Requires OPENROUTER_API_KEY. If the tool returns its missing-key error, relay it and suggest /setup.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: test
3
+ description: Use when the user wants tests generated for code or a diff — enumerates edge cases and failure modes first, then emits runnable test code via testgen
4
+ user-invocable: true
5
+ ---
6
+
7
+ # /test — Generate Tests
8
+
9
+ Generate runnable tests for code with the `testgen` tool (a coding-specialized model that lists edge cases before writing tests).
10
+
11
+ ## Steps
12
+
13
+ 1. Collect the code under test. If the user pointed at files, pass them via `files` (use `path:start-end` line ranges for large files). Otherwise take the pasted `code`.
14
+ 2. Infer the test framework from the project (jest / vitest / pytest / go test / …) or ask, and pass it as `framework`. If there are existing tests nearby, paste one via `existingTests` so the generated tests match your conventions.
15
+ 3. Pick the coverage focus from intent: `edge` (boundaries + failure modes), `happy` (main paths), `regression` (lock current behavior), or `all` (default).
16
+ 4. Call `testgen({ code | files, framework?, coverage?, existingTests? })`.
17
+ 5. Relay the generated test file, then offer to write it to the right path and run it.
18
+
19
+ Requires OPENROUTER_API_KEY. If the tool returns its missing-key error, relay it and suggest /setup.