mantisai-cli 2.0.0

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/lib/picker.js ADDED
@@ -0,0 +1,150 @@
1
+ import { createSpaceState, fetchThreads } from './fetch.js';
2
+ import { loadConfig, saveConfig } from './config.js';
3
+ import { syncMcpConfigs } from './mcp-config.js';
4
+ import { searchSpaces, resolveSpaceFromInput } from './spaces.js';
5
+ import { parseSpaceIdFromInput } from './space-id.js';
6
+ import { die, info, promptInput, promptSearch, success } from './ui.js';
7
+
8
+ const BROWSE_PAGE = 20;
9
+
10
+ function spaceLabel(s) {
11
+ const name = s.name || '(unnamed)';
12
+ const maps = s.map_count != null ? ` · ${s.map_count} map(s)` : '';
13
+ const role = s.role ? ` · ${s.role}` : '';
14
+ return name + maps + role;
15
+ }
16
+
17
+ function spaceChoices(spaces) {
18
+ return spaces.map((s) => ({
19
+ name: spaceLabel(s),
20
+ value: s,
21
+ description: s.id,
22
+ }));
23
+ }
24
+
25
+ export async function requireConfig() {
26
+ const cfg = loadConfig();
27
+ if (!cfg.apiKey || !cfg.apiBaseUrl) {
28
+ die('Run mantis setup or /mantis:connect first (API key + URL).');
29
+ }
30
+ return cfg;
31
+ }
32
+
33
+ function threadChoices(threads, query) {
34
+ const terms = (query || '').trim().toLowerCase();
35
+ const list = terms
36
+ ? threads.filter((t) => {
37
+ const hay = `${t.name} ${t.id}`.toLowerCase();
38
+ return hay.includes(terms);
39
+ })
40
+ : threads;
41
+ const out = [
42
+ { name: '➕ Create new thread', value: '__new__', description: 'New space state' },
43
+ ];
44
+ for (const t of list) {
45
+ const when = t.updated_at ? String(t.updated_at).slice(0, 10) : '';
46
+ out.push({
47
+ name: t.name || '(unnamed)',
48
+ value: t,
49
+ description: when || t.id,
50
+ });
51
+ }
52
+ return out;
53
+ }
54
+
55
+ async function pickSpaceByLink(cfg) {
56
+ const raw = await promptInput('Paste Mantis space link or UUID (Enter to browse)', {
57
+ default: '',
58
+ });
59
+ if (!raw?.trim()) return null;
60
+ const space = await resolveSpaceFromInput(cfg.apiBaseUrl, cfg.apiKey, raw);
61
+ if (!space) die('Space not found or you do not have access.');
62
+ return saveSpace(cfg, space);
63
+ }
64
+
65
+ export async function pickSpace() {
66
+ const cfg = await requireConfig();
67
+
68
+ if (!process.stdin.isTTY) {
69
+ const page = await searchSpaces(cfg.apiBaseUrl, cfg.apiKey, { limit: 1 });
70
+ if (!page.spaces?.length) die('No spaces found.');
71
+ return saveSpace(cfg, page.spaces[0]);
72
+ }
73
+
74
+ const fromLink = await pickSpaceByLink(cfg);
75
+ if (fromLink) return fromLink;
76
+
77
+ const picked = await promptSearch(
78
+ 'Select space (↑↓ type to search, paste link in previous step)',
79
+ async (input) => {
80
+ const id = parseSpaceIdFromInput(input);
81
+ if (id) {
82
+ const one = await resolveSpaceFromInput(cfg.apiBaseUrl, cfg.apiKey, input);
83
+ return one ? spaceChoices([one]) : [{ name: 'No match for that link/id', value: null, disabled: true }];
84
+ }
85
+ const page = await searchSpaces(cfg.apiBaseUrl, cfg.apiKey, {
86
+ q: input || '',
87
+ limit: BROWSE_PAGE,
88
+ offset: 0,
89
+ });
90
+ return spaceChoices(page.spaces || []);
91
+ },
92
+ );
93
+ if (!picked) die('No space selected.');
94
+ return saveSpace(cfg, picked);
95
+ }
96
+
97
+ function saveSpace(cfg, space) {
98
+ const changed = cfg.spaceId !== space.id;
99
+ const next = { ...cfg, spaceId: space.id, spaceName: space.name };
100
+ if (changed) {
101
+ delete next.spaceStateId;
102
+ delete next.spaceStateName;
103
+ }
104
+ saveConfig(next);
105
+ syncMcpConfigs(next);
106
+ success(`Space: ${space.name}`);
107
+ if (changed) {
108
+ info('Thread cleared — pick a thread, then /reload-plugins in Claude Code.');
109
+ }
110
+ return next;
111
+ }
112
+
113
+ export async function pickThread(initialFilter = '') {
114
+ const cfg = await requireConfig();
115
+ if (!cfg.spaceId) die('Pick a space first: /mantis:space');
116
+
117
+ const threads = await fetchThreads(cfg.apiBaseUrl, cfg.apiKey, cfg.spaceId);
118
+
119
+ if (!process.stdin.isTTY) {
120
+ if (!threads.length) die('No threads. Run interactively to create one.');
121
+ return saveThread(cfg, threads[0]);
122
+ }
123
+
124
+ const picked = await promptSearch(
125
+ `Thread in "${cfg.spaceName || 'space'}" (↑↓, type to filter)`,
126
+ async (input) => threadChoices(threads, input || initialFilter),
127
+ );
128
+
129
+ if (picked === '__new__') {
130
+ const name = await promptInput('Thread name', { default: 'Claude Code' });
131
+ const created = await createSpaceState(
132
+ cfg.apiBaseUrl, cfg.apiKey, cfg.spaceId, name || 'Claude Code',
133
+ );
134
+ return saveThread(cfg, created);
135
+ }
136
+ return saveThread(cfg, picked);
137
+ }
138
+
139
+ function saveThread(cfg, thread) {
140
+ const next = {
141
+ ...cfg,
142
+ spaceStateId: thread.id,
143
+ spaceStateName: thread.name,
144
+ };
145
+ saveConfig(next);
146
+ syncMcpConfigs(next);
147
+ success(`Thread: ${thread.name}`);
148
+ info('MCP headers update on reconnect — run /reload-plugins if tools lack context.');
149
+ return next;
150
+ }
@@ -0,0 +1,13 @@
1
+ const UUID_RE =
2
+ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
3
+
4
+ /** Extract space UUID from a Mantis URL (/space/{id}), raw UUID, or search text containing one. */
5
+ export function parseSpaceIdFromInput(text) {
6
+ const t = (text || '').trim();
7
+ if (!t) return null;
8
+ const pathMatch = t.match(/\/space\/([0-9a-f-]{36})/i);
9
+ if (pathMatch) return pathMatch[1].toLowerCase();
10
+ const uuidMatch = t.match(UUID_RE);
11
+ if (uuidMatch) return uuidMatch[0].toLowerCase();
12
+ return null;
13
+ }
package/lib/spaces.js ADDED
@@ -0,0 +1,48 @@
1
+ import { listSpaces } from './api.js';
2
+ import { parseSpaceIdFromInput } from './space-id.js';
3
+
4
+ export async function fetchSpaceById(baseUrl, apiKey, spaceId) {
5
+ const page = await listSpaces(baseUrl, apiKey, {
6
+ scope: 'accessible',
7
+ space_id: spaceId,
8
+ limit: 1,
9
+ offset: 0,
10
+ });
11
+ return page.spaces?.[0] ?? null;
12
+ }
13
+
14
+ export async function resolveSpaceFromInput(baseUrl, apiKey, text) {
15
+ const id = parseSpaceIdFromInput(text);
16
+ if (!id) return null;
17
+ return fetchSpaceById(baseUrl, apiKey, id);
18
+ }
19
+
20
+ /** One page of spaces; optional name/id search — does not load the full catalog. */
21
+ export async function searchSpaces(
22
+ baseUrl,
23
+ apiKey,
24
+ { q = '', limit = 20, offset = 0, scope = 'accessible' } = {},
25
+ ) {
26
+ const id = parseSpaceIdFromInput(q);
27
+ if (id) {
28
+ const one = await fetchSpaceById(baseUrl, apiKey, id);
29
+ return {
30
+ spaces: one ? [one] : [],
31
+ total: one ? 1 : 0,
32
+ limit: 1,
33
+ offset: 0,
34
+ };
35
+ }
36
+ const page = await listSpaces(baseUrl, apiKey, {
37
+ scope,
38
+ limit,
39
+ offset,
40
+ q: q.trim() || undefined,
41
+ });
42
+ return {
43
+ spaces: page.spaces || [],
44
+ total: page.total ?? (page.spaces || []).length,
45
+ limit: page.limit ?? limit,
46
+ offset: page.offset ?? offset,
47
+ };
48
+ }
package/lib/ui.js ADDED
@@ -0,0 +1,73 @@
1
+ import { confirm, input, password, search, select } from '@inquirer/prompts';
2
+
3
+ export function isCancel(err) {
4
+ return err?.name === 'ExitPromptError';
5
+ }
6
+
7
+ export function die(msg) {
8
+ console.error(`\n ✖ ${msg}\n`);
9
+ process.exit(1);
10
+ }
11
+
12
+ export function banner(title, subtitle) {
13
+ console.log('');
14
+ console.log(` \x1b[36m${title}\x1b[0m`);
15
+ if (subtitle) console.log(` \x1b[2m${subtitle}\x1b[0m`);
16
+ console.log('');
17
+ }
18
+
19
+ export function success(msg) {
20
+ console.log(` \x1b[32m✔\x1b[0m ${msg}`);
21
+ }
22
+
23
+ export function info(msg) {
24
+ console.log(` \x1b[2m→\x1b[0m ${msg}`);
25
+ }
26
+
27
+ export async function promptInput(message, { default: def } = {}) {
28
+ try {
29
+ return await input({ message, default: def });
30
+ } catch (e) {
31
+ if (isCancel(e)) die('Cancelled.');
32
+ throw e;
33
+ }
34
+ }
35
+
36
+ export async function promptSecret(message, { default: def } = {}) {
37
+ if (def) {
38
+ return promptInput(message, { default: def });
39
+ }
40
+ try {
41
+ return await password({ message, mask: '•' });
42
+ } catch (e) {
43
+ if (isCancel(e)) die('Cancelled.');
44
+ throw e;
45
+ }
46
+ }
47
+
48
+ export async function promptSearch(message, source, { pageSize = 12 } = {}) {
49
+ try {
50
+ return await search({ message, pageSize, source });
51
+ } catch (e) {
52
+ if (isCancel(e)) die('Cancelled.');
53
+ throw e;
54
+ }
55
+ }
56
+
57
+ export async function promptSelect(message, choices) {
58
+ try {
59
+ return await select({ message, choices });
60
+ } catch (e) {
61
+ if (isCancel(e)) die('Cancelled.');
62
+ throw e;
63
+ }
64
+ }
65
+
66
+ export async function promptConfirm(message, { default: def = false } = {}) {
67
+ try {
68
+ return await confirm({ message, default: def });
69
+ } catch (e) {
70
+ if (isCancel(e)) die('Cancelled.');
71
+ throw e;
72
+ }
73
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "mantisai-cli",
3
+ "version": "2.0.0",
4
+ "description": "Mantis developer CLI — create maps, manage spaces and threads, configure Claude Code, and connect agents to Mantis MCP.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "AnasMaar",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/KellisLab/mantis-cli.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/KellisLab/mantis-cli/issues"
14
+ },
15
+ "homepage": "https://github.com/KellisLab/mantis-cli#readme",
16
+ "publishConfig": {
17
+ "registry": "https://registry.npmjs.org"
18
+ },
19
+ "dependencies": {
20
+ "@inquirer/prompts": "^7.5.1",
21
+ "commander": "^13.1.0",
22
+ "csv-parse": "^6.2.1",
23
+ "csv-stringify": "^6.7.0",
24
+ "fast-glob": "^3.3.3"
25
+ },
26
+ "bin": {
27
+ "mantis": "bin/mantis.js"
28
+ },
29
+ "files": [
30
+ ".claude-plugin",
31
+ ".claude-plugin/marketplace.json",
32
+ ".mcp.json",
33
+ "skills",
34
+ "bin",
35
+ "lib",
36
+ "LICENSE",
37
+ "README.md"
38
+ ],
39
+ "engines": {
40
+ "node": ">=18"
41
+ },
42
+ "keywords": [
43
+ "mantis",
44
+ "cli",
45
+ "mcp",
46
+ "claude-code",
47
+ "plugin"
48
+ ]
49
+ }
@@ -0,0 +1,75 @@
1
+ ---
2
+ description: Index a local codebase into a CSV for Mantis, optionally creating and activating a Mantis codebase map. Use when the user wants Claude Code to map a repository or create a codebase CSV.
3
+ argument-hint: [repo-path]
4
+ allowed-tools: Bash, AskUserQuestion
5
+ disable-model-invocation: true
6
+ ---
7
+
8
+ # Create Codebase CSV
9
+
10
+ Use `mantis create codebase`. This command is local-first: it scans files on the user's machine and writes a CSV. It can optionally continue into `mantis create map`.
11
+
12
+ ## CSV-only Flow
13
+
14
+ 1. Get the repo root from `$ARGUMENTS` or ask the user.
15
+ 2. Run:
16
+
17
+ ```bash
18
+ mantis create codebase "./repo" --out "./repo-codebase.csv"
19
+ ```
20
+
21
+ 3. Explain the CSV columns:
22
+ - `path`: repo-relative file path
23
+ - `file_name`
24
+ - `extension`
25
+ - `language`
26
+ - `kind`: rough file role such as component, route, api, utility, test, source
27
+ - `loc`
28
+ - `bytes`
29
+ - `imports`
30
+ - `summary`
31
+ - `content`
32
+
33
+ ## Full Demo Flow
34
+
35
+ If the user wants the codebase to become a Mantis map immediately, run:
36
+
37
+ ```bash
38
+ mantis create codebase "./repo" \
39
+ --create-map \
40
+ --space-mode new \
41
+ --space-name "Codebase: Repo Name" \
42
+ --private \
43
+ --map-name "Source Code Index" \
44
+ --activate \
45
+ --thread-name "Architecture Review"
46
+ ```
47
+
48
+ The command uses good defaults for codebase maps:
49
+
50
+ - `title`: `path`
51
+ - `semantic`: `summary,content,imports`
52
+ - `categoric`: `language,kind,extension`
53
+ - `numeric`: `loc,bytes`
54
+
55
+ ## After Activation
56
+
57
+ If `--activate` was used, tell the user to run:
58
+
59
+ ```text
60
+ /reload-plugins
61
+ ```
62
+
63
+ Then suggest prompts like:
64
+
65
+ ```text
66
+ What are the main architectural zones in this codebase?
67
+ Which files are central or risky?
68
+ Where should I start if I want to refactor the map rendering system?
69
+ ```
70
+
71
+ ## Notes
72
+
73
+ - Use `mantis setup` first if API configuration is missing.
74
+ - Avoid scanning dependency/build folders manually; the CLI already ignores common junk like `.git`, `node_modules`, `.next`, `dist`, `build`, and caches.
75
+ - For agent-driven demos, pass flags explicitly instead of relying on interactive prompts.
@@ -0,0 +1,42 @@
1
+ ---
2
+ description: Run the Mantis MCP setup wizard (API key, space, thread). Use when the user wants to connect Claude Code to Mantis or fix MCP connection issues.
3
+ disable-model-invocation: true
4
+ ---
5
+
6
+ # Connect Mantis MCP
7
+
8
+ Run the interactive setup in the user's terminal:
9
+
10
+ ```bash
11
+ mantis setup
12
+ ```
13
+
14
+ If the plugin is not on PATH, use:
15
+
16
+ ```bash
17
+ node "${CLAUDE_PLUGIN_ROOT}/bin/mantis.js" setup
18
+ ```
19
+
20
+ The wizard will:
21
+
22
+ 1. Ask for Mantis API URL and Developer API key (`live_…` from the Mantis Developer portal).
23
+ 2. List accessible spaces — owned, shared, and public (most recent first).
24
+ 3. Pick or create a **space state** (thread).
25
+ 4. Save config for MCP headers (`X-Space-State-ID`).
26
+
27
+ After setup, switch context with **`/mantis:space`** and **`/mantis:thread`** (Claude Code **AskUserQuestion** menu with arrow keys). Terminal: `mantis select space` / `mantis select thread`.
28
+
29
+ If they have not run **`mantis setup`** yet, tell them:
30
+
31
+ ```bash
32
+ npm install -g mantisai-cli
33
+ mantis setup
34
+ ```
35
+
36
+ That installs the **mantis@mantis-plugins** Claude Code plugin and saves API/space/thread config.
37
+
38
+ Then: **`/reload-plugins`** in Claude Code (if already open). Confirm **API URL** when prompted (default `https://kellis-h200-1.csail.mit.edu`; use `http://localhost:8000` for local Docker). API keys: https://mantis.csail.mit.edu/developer/#keys
39
+
40
+ If `/plugin` shows **Disabled** or *not installed*: run `claude plugin install mantis@mantis-plugins`, enable it, then `/reload-plugins`. If update fails, uninstall and reinstall the plugin (stale `installed_plugins.json` / orphaned cache).
41
+
42
+ For connection errors: URL must end with `/mcp_integrated/`; avoid `/mcp/mcp` unless their deployment uses it.
@@ -0,0 +1,62 @@
1
+ ---
2
+ description: Create a Mantis map from a local CSV or XLSX file using the mantis CLI. Use when the user wants to upload a dataset, configure field types, or turn a CSV into a Mantis map.
3
+ argument-hint: [csv-or-xlsx-path]
4
+ allowed-tools: Bash, AskUserQuestion
5
+ disable-model-invocation: true
6
+ ---
7
+
8
+ # Create Mantis Map
9
+
10
+ Use the local `mantis create map` CLI. Prefer passing arguments explicitly so this works for agents and humans.
11
+
12
+ ## Steps
13
+
14
+ 1. Get the local CSV/XLSX path from `$ARGUMENTS` or ask the user for it.
15
+
16
+ 2. Inspect the header row if it is a CSV. Decide field types:
17
+ - `title`: one human-readable identifier column, usually `title`, `name`, `path`, or `file`.
18
+ - `semantic`: text columns to embed, usually `content`, `summary`, `description`, `text`, or `body`.
19
+ - `categoric`: labels like `language`, `kind`, `extension`, `category`.
20
+ - `numeric`: measurements like `loc`, `bytes`, `score`, `count`.
21
+ - `date`: date/time columns.
22
+ - `links`: URL columns.
23
+ - ignored columns go in `--delete-column`.
24
+
25
+ 3. Ask where to put the map if the user did not specify:
26
+ - new space
27
+ - existing space
28
+
29
+ 4. Run `mantis create map` with explicit flags. Example:
30
+
31
+ ```bash
32
+ mantis create map "./dataset.csv" \
33
+ --space-mode new \
34
+ --space-name "Codebase Index" \
35
+ --private \
36
+ --map-name "Source Files" \
37
+ --title-column "path" \
38
+ --semantic-column "summary,content" \
39
+ --categoric-column "language,kind,extension" \
40
+ --numeric-column "loc,bytes" \
41
+ --activate \
42
+ --thread-name "Dataset Exploration"
43
+ ```
44
+
45
+ For an existing space:
46
+
47
+ ```bash
48
+ mantis create map "./dataset.csv" \
49
+ --space-mode existing \
50
+ --space-id "<space-uuid>" \
51
+ --map-name "Source Files" \
52
+ --title-column "path" \
53
+ --semantic-column "summary,content"
54
+ ```
55
+
56
+ 5. Report the created space link and map id from the JSON output. If `--activate` was used, tell the user to run `/reload-plugins`.
57
+
58
+ ## Notes
59
+
60
+ - If the user wants a fully interactive human flow, run only `mantis create map "./dataset.csv"`.
61
+ - If Claude Code is driving the flow, avoid interactive prompts by passing flags.
62
+ - Use `mantis setup` first if the CLI says API key or URL is missing.
@@ -0,0 +1,52 @@
1
+ ---
2
+
3
+ ## name: mantis
4
+ description: Work with MIT Mantis spaces, maps, clusters, and notebooks via MCP. Use when the user mentions Mantis, spaces, maps, embeddings, clusters, bags, or wants to explore/visualize data in their workspace.
5
+
6
+ # Mantis (Claude Code)
7
+
8
+ Mantis is a spatial data workspace: **spaces** contain **maps** (embeddings of datasets), **clusters**, **bags**, and **notebooks**.
9
+
10
+ ## Connection
11
+
12
+ 1. User runs `mantis setup` (or `/mantis:connect`) with a Developer API key from the Mantis portal.
13
+ 2. MCP endpoint: `{api_base_url}/mcp_integrated/` (trailing slash required).
14
+ 3. Required header on every MCP request: `**X-Space-State-ID`** = thread UUID from setup (setup writes this into the plugin MCP config).
15
+ 4. After changing space/thread: `mantis select` then ask the user to run `**/reload-plugins**`.
16
+
17
+ ## REST (setup only)
18
+
19
+
20
+ | Endpoint | Purpose |
21
+ | --------------------------------------------- | ----------------------------------- |
22
+ | `GET /api/v1/me/spaces/?scope=accessible&limit=100` | List owned, shared, and public spaces |
23
+ | `GET /api/v1/me/space-states/?space_id=` | List threads |
24
+ | `POST /api/v1/me/space-states/` | Create thread `{ space_id, name? }` |
25
+
26
+
27
+ Auth: `Authorization: Bearer live_…` — key must be linked to a Mantis user.
28
+
29
+ ## MCP tools (read-focused)
30
+
31
+ Call `**get_space_context**` first when you need map IDs, field types, cluster names, or point counts.
32
+
33
+ Other useful tools: `get_tree_status`, `get_cluster_children`, `get_bags_from_map`, `get_bag_contents`, `get_point_details`, `get_selected_points`, `get_available_dimensions`.
34
+
35
+ Space management tools (`create_space`, `create_map_from_url`, etc.) need session or space-state context.
36
+
37
+ ## Conventions
38
+
39
+ - Refer to spaces and maps by **name** in user-facing text; use UUIDs only in tool arguments.
40
+ - Maps belong to a space; pick `primary_map_id` from space list API or `get_space_context`.
41
+ - Notebooks can bootstrap from `X-Space-State-ID` alone; file uploads still need a chat session when applicable.
42
+ - Default API URL: `https://kellis-h200-1.csail.mit.edu` (local: `http://localhost:8000`). API keys: https://mantis.csail.mit.edu/developer/#keys
43
+
44
+ ## Slash commands in this plugin
45
+
46
+ - `/mantis:connect` — first-time setup (API key)
47
+ - `/mantis:space` — pick space via **AskUserQuestion** (↑↓ in Claude UI; optional filter: `/mantis:space atlas`)
48
+ - `/mantis:thread` — pick thread the same way
49
+ - Terminal pickers: `mantis select space` / `mantis select thread` if the in-chat menu fails
50
+ - `/mantis:status` — show current space/thread
51
+
52
+ After **`/mantis:thread`** only, run `/reload-plugins` once (MCP headers are fixed at connect time). Space-only changes do not need a reload until a thread is set.
@@ -0,0 +1,17 @@
1
+ ---
2
+ description: Change the active Mantis space or thread (space state) for MCP. Use when the user wants to switch workspace or conversation context.
3
+ disable-model-invocation: true
4
+ ---
5
+
6
+ # Select Mantis space / thread
7
+
8
+ Prefer the interactive slash commands (arrow keys + filter as you type):
9
+
10
+ - **`/mantis:space`** — pick a space
11
+ - **`/mantis:thread`** — pick a thread (after space)
12
+
13
+ CLI fallback: `mantis select` or `node "${CLAUDE_PLUGIN_ROOT}/bin/mantis.js" select`.
14
+
15
+ After a change, run **`/reload-plugins`**.
16
+
17
+ Optional argument from user (not passed to CLI automatically): if they give a **thread UUID**, they can paste it during setup or edit `~/.mantis/claude-code/config.json` (or `${CLAUDE_PLUGIN_DATA}/config.json`) and set `spaceStateId`.
@@ -0,0 +1,62 @@
1
+ ---
2
+ description: Select your Mantis space (arrow-key menu in Claude Code). Use when switching workspace for MCP.
3
+ argument-hint: [filter]
4
+ allowed-tools: AskUserQuestion, Bash
5
+ disable-model-invocation: true
6
+ ---
7
+
8
+ # Select Mantis space
9
+
10
+ Slash-command `!` blocks cannot use an interactive terminal (no arrow keys). Use **AskUserQuestion** — that is the native Claude Code picker UI.
11
+
12
+ ## Steps
13
+
14
+ 1. If `$ARGUMENTS` looks like a Mantis space URL (`/space/{uuid}`) or a UUID, resolve it first:
15
+
16
+ ```bash
17
+ node "${CLAUDE_PLUGIN_ROOT}/bin/mantis-resolve-space.js" $ARGUMENTS
18
+ ```
19
+
20
+ If JSON has `space`, run `mantis-set-space.js` with that id and name, then stop (skip browse). On `error`, explain and offer browse.
21
+
22
+ 2. Otherwise run (search text or empty for recent page):
23
+
24
+ ```bash
25
+ node "${CLAUDE_PLUGIN_ROOT}/bin/mantis-list-spaces.js" --filter "$ARGUMENTS"
26
+ ```
27
+
28
+ Parse the JSON. On `error`, tell the user to run `mantis setup` in a terminal.
29
+
30
+ 3. **AskUserQuestion** (max 4 options per call):
31
+ - If the user pasted a link in chat, pass it as `$ARGUMENTS` or run step 1 with that URL.
32
+ - `header`: `Space`
33
+ - `question`: `Which Mantis space should Claude use?`
34
+ - For each item in `spaces`, one option:
35
+ - `label`: space `name` (short)
36
+ - `description`: full `id` + map count if present
37
+ - If `hasMore` is true, use the 3rd slot for the best match and the 4th option:
38
+ - `label`: `More spaces…`
39
+ - `description`: `Show next page (${total} total)`
40
+ - On "More spaces…", re-run with `--offset` increased by `limit` (same filter), then AskUserQuestion again.
41
+
42
+ 4. After the user picks a space (not "More"), run:
43
+
44
+ ```bash
45
+ node "${CLAUDE_PLUGIN_ROOT}/bin/mantis-set-space.js" <uuid-from-description> "<name>"
46
+ ```
47
+
48
+ Use the UUID from the selected option's **description**, not the label.
49
+
50
+ 5. If `needThread` or `threadCleared`, run **`/mantis:thread`** next in this session (user must pick a thread for the new space).
51
+
52
+ 6. After space or thread changes, tell the user to run **`/reload-plugins`** so MCP reconnects with the updated `X-Space-State-ID` header. Do not try to invoke `/reload-plugins` as a Skill tool.
53
+
54
+ ## Terminal fallback
55
+
56
+ If AskUserQuestion does not appear (known CC bug in some versions), tell the user to run in a separate terminal:
57
+
58
+ ```bash
59
+ mantis select space
60
+ ```
61
+
62
+ Then `/reload-plugins`.
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: Show the current Mantis space, thread, and MCP URL from plugin config. Use when checking whether Claude Code is connected to Mantis.
3
+ disable-model-invocation: true
4
+ ---
5
+
6
+ # Mantis status
7
+
8
+ Run in the terminal:
9
+
10
+ ```bash
11
+ mantis status
12
+ ```
13
+
14
+ Or:
15
+
16
+ ```bash
17
+ node "${CLAUDE_PLUGIN_ROOT}/bin/mantis.js" status
18
+ ```
19
+
20
+ Report the output to the user. If no thread is set, tell them to run `/mantis:connect` or `mantis setup`, then `/reload-plugins`.