@subshell-ai/plugin-oh-my-pi 0.2.0 → 0.2.2
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 +4 -5
- package/dist/index.d.ts +42 -1
- package/dist/index.js +245 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# Oh My Pi plugin for Subshell
|
|
2
2
|
|
|
3
3
|
`@subshell-ai/plugin-oh-my-pi` launches the `omp` CLI from Subshell. It provides
|
|
4
|
-
model, provider, and thinking settings,
|
|
5
|
-
configuration. Install and authenticate Oh My
|
|
6
|
-
where you want to run it.
|
|
4
|
+
model, provider, and thinking settings, automatic per-launch MCP
|
|
5
|
+
configuration, and sidebar subagent monitoring. Install and authenticate Oh My
|
|
6
|
+
Pi separately on each node where you want to run it.
|
|
7
7
|
|
|
8
8
|
See the [Oh My Pi guide](https://docs.subshell.sh/agents/oh-my-pi) for setup
|
|
9
9
|
and supported behavior. The integration is verified with Oh My Pi 18.12.6.
|
|
10
|
-
Automatic conversation resumption
|
|
11
|
-
monitoring aren't supported.
|
|
10
|
+
Automatic conversation resumption and attention hooks aren't supported.
|
|
12
11
|
|
|
13
12
|
Licensed under Apache-2.0.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../plugin-api/dist/types-
|
|
1
|
+
//#region ../../plugin-api/dist/types-DPriUYLa.d.ts
|
|
2
2
|
/** How the plugin learns about children: harness lifecycle hooks, or a loaded extension. */
|
|
3
3
|
type HarnessSubagentDiscovery = "hooks" | "extension";
|
|
4
4
|
/** Whether the sidebar can move the parent's own view to a child, or the harness keeps that in-app. */
|
|
@@ -397,6 +397,19 @@ interface SubshellPlugin {
|
|
|
397
397
|
* used as-is, which is right for every harness that prints just a version.
|
|
398
398
|
*/
|
|
399
399
|
parseVersion?(raw: string): string | null;
|
|
400
|
+
/**
|
|
401
|
+
* Turns the harness's own terminal title into the subshell name the rail
|
|
402
|
+
* shows.
|
|
403
|
+
*
|
|
404
|
+
* The host removes escape sequences and control bytes and trims the result
|
|
405
|
+
* before calling this, so `raw` is safe text. Interpret what the title adds
|
|
406
|
+
* around the session name — a brand, a run-state separator, a cycling
|
|
407
|
+
* status glyph — and return the name behind it. Return "" when the title
|
|
408
|
+
* names nothing, and the caller keeps the name the pane already had. Omit
|
|
409
|
+
* the member when the harness titles its pane with the name the host
|
|
410
|
+
* supplied, which needs no interpretation.
|
|
411
|
+
*/
|
|
412
|
+
parsePaneTitle?(raw: string): string;
|
|
400
413
|
/** Overrides manifest-driven detection. Almost no plugin needs this. */
|
|
401
414
|
detect?(): Promise<DetectionResult>;
|
|
402
415
|
/** Restart-resume support. Declare the `resume` capability with it. */
|
|
@@ -487,6 +500,24 @@ interface InstallSpec {
|
|
|
487
500
|
*/
|
|
488
501
|
docsUrl: string;
|
|
489
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* How this plugin's harness CLI updates itself in place.
|
|
505
|
+
*
|
|
506
|
+
* The installer doubles as the updater for every built-in today (`curl … | sh`
|
|
507
|
+
* and `npm i -g` lines are idempotent), so this is OPTIONAL and falls back to
|
|
508
|
+
* {@link InstallSpec}. Declare it where the vendor ships a dedicated verb or a
|
|
509
|
+
* different channel — `claude update`, say — so pressing Update runs the
|
|
510
|
+
* vendor's own update path rather than reinstalling.
|
|
511
|
+
*
|
|
512
|
+
* Like `install.command`, this is a SHELL LINE the host runs through `sh -c`,
|
|
513
|
+
* on the node that holds the harness, and it may not need privileges.
|
|
514
|
+
*/
|
|
515
|
+
interface UpdateSpec {
|
|
516
|
+
/** Shell line that updates the CLI in place. */
|
|
517
|
+
command: string;
|
|
518
|
+
/** Where the vendor documents it; http(s) only, when present. */
|
|
519
|
+
docsUrl?: string;
|
|
520
|
+
}
|
|
490
521
|
/** One step an operator must run themselves, because the host may not. */
|
|
491
522
|
interface PrivilegedStep {
|
|
492
523
|
/** What it does, in a few words: "Install the daemon" */
|
|
@@ -630,6 +661,11 @@ interface SubshellManifest {
|
|
|
630
661
|
detect?: DetectSpec;
|
|
631
662
|
/** Install guidance shown when the binary is missing */
|
|
632
663
|
install?: InstallSpec;
|
|
664
|
+
/**
|
|
665
|
+
* How the CLI updates itself, when the installer is not the updater.
|
|
666
|
+
* Absent means `install.command` is the update path too.
|
|
667
|
+
*/
|
|
668
|
+
update?: UpdateSpec;
|
|
633
669
|
/**
|
|
634
670
|
* Environment variables this plugin computes against (spec 2026-09-10 §5).
|
|
635
671
|
*
|
|
@@ -664,6 +700,11 @@ declare const manifest: SubshellManifest;
|
|
|
664
700
|
* Oh My Pi uses its own executable and native MCP discovery, independently of
|
|
665
701
|
* pi. An explicit extension directory contributes mcp.json without redirecting
|
|
666
702
|
* the user's config or credentials. Verified against omp 18.12.6.
|
|
703
|
+
*
|
|
704
|
+
* The same directory carries the subagent bridge. omp keeps its child roster
|
|
705
|
+
* on the session event bus; the extension reports it so the sidebar can hang
|
|
706
|
+
* those children under the parent subshell. omp offers no extension API for
|
|
707
|
+
* moving the parent's terminal onto a child, so navigation stays unavailable.
|
|
667
708
|
*/
|
|
668
709
|
declare const createPlugin: HarnessPluginFactory;
|
|
669
710
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../plugin-api/dist/manifest-
|
|
1
|
+
//#region ../../plugin-api/dist/manifest-Cdy9tSLl.js
|
|
2
2
|
/** Every {@link PluginType}, for anything that has to iterate or validate them. */
|
|
3
3
|
const PLUGIN_TYPES = [
|
|
4
4
|
"agent-harness",
|
|
@@ -158,6 +158,19 @@ function parseManifest(pkgJson) {
|
|
|
158
158
|
docsUrl: i.docsUrl
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
|
+
let update;
|
|
162
|
+
if (block.update !== void 0) {
|
|
163
|
+
const u = block.update;
|
|
164
|
+
if (!isRecord(u) || typeof u.command !== "string" || u.command.trim() === "") return { error: "`subshell.update` needs a non-empty command" };
|
|
165
|
+
if (needsPrivileges(u.command)) return { error: "`subshell.update.command` must not need sudo, doas or pkexec — the host runs it through a shell and has no terminal for a password prompt" };
|
|
166
|
+
if (u.docsUrl !== void 0) {
|
|
167
|
+
if (typeof u.docsUrl !== "string" || !isDocsUrl(u.docsUrl)) return { error: "`subshell.update.docsUrl` must be an http(s) URL — it is rendered as a link on an admin page" };
|
|
168
|
+
update = {
|
|
169
|
+
command: u.command,
|
|
170
|
+
docsUrl: u.docsUrl
|
|
171
|
+
};
|
|
172
|
+
} else update = { command: u.command };
|
|
173
|
+
}
|
|
161
174
|
let hostEnv;
|
|
162
175
|
if (block.hostEnv !== void 0) {
|
|
163
176
|
if (!Array.isArray(block.hostEnv) || !block.hostEnv.every((k) => typeof k === "string" && k.trim() !== "")) return { error: "`subshell.hostEnv` must be an array of environment variable names" };
|
|
@@ -176,6 +189,7 @@ function parseManifest(pkgJson) {
|
|
|
176
189
|
entry: block.entry,
|
|
177
190
|
...detect ? { detect } : {},
|
|
178
191
|
...install ? { install } : {},
|
|
192
|
+
...update ? { update } : {},
|
|
179
193
|
...hostEnv ? { hostEnv } : {},
|
|
180
194
|
...parsedNetwork ? { network: parsedNetwork } : {},
|
|
181
195
|
...block.pickable === false ? { pickable: false } : {}
|
|
@@ -273,6 +287,193 @@ function validateGenericPreset(preset) {
|
|
|
273
287
|
};
|
|
274
288
|
}
|
|
275
289
|
//#endregion
|
|
290
|
+
//#region src/subagents.ts
|
|
291
|
+
/**
|
|
292
|
+
* omp's session event bus already carries the roster the task tool is running:
|
|
293
|
+
* `task:subagent:lifecycle` announces a child's start and settlement, and
|
|
294
|
+
* `task:subagent:progress` streams its status, generated one-line label, and
|
|
295
|
+
* latest intent. The extension below turns those frames into the snapshot
|
|
296
|
+
* reports the control plane reads, so the sidebar shows the children under the
|
|
297
|
+
* subshell that asked for them.
|
|
298
|
+
*
|
|
299
|
+
* Every frame is already bounded by omp; this module bounds again because the
|
|
300
|
+
* report is rendered in the sidebar of the owner and every viewer, and it is
|
|
301
|
+
* the pane's own porcelain that names them (160 characters, like the server).
|
|
302
|
+
* Progress frames arrive at the coalescing rate omp emits (about 150 ms), so
|
|
303
|
+
* they queue one snapshot per second; lifecycle changes report at once.
|
|
304
|
+
*
|
|
305
|
+
* A child session rebinds every extension factory, so this factory runs once
|
|
306
|
+
* per session. Each binding keeps its own roster, and `session_start` tells a
|
|
307
|
+
* child binding apart from the pane's main agent: only the main agent's roster
|
|
308
|
+
* is the set of children the sidebar should hang under the subshell.
|
|
309
|
+
*
|
|
310
|
+
* The roster repeats every two seconds while it is non-empty: the control
|
|
311
|
+
* plane keeps it in memory, so a restart has nothing until the harness reports
|
|
312
|
+
* again, and an idle child produces no other frames.
|
|
313
|
+
*/
|
|
314
|
+
const SUBAGENT_BRIDGE = `
|
|
315
|
+
export default function (pi) {
|
|
316
|
+
const LIFECYCLE = 'task:subagent:lifecycle';
|
|
317
|
+
const PROGRESS = 'task:subagent:progress';
|
|
318
|
+
const MAX_AGENTS = 256;
|
|
319
|
+
const MAX_TEXT = 160;
|
|
320
|
+
const REPORT_DELAY_MS = 1000;
|
|
321
|
+
// A live child keeps reporting, so a control-plane restart (which forgets
|
|
322
|
+
// every roster) gets the set back without waiting for the next tool event.
|
|
323
|
+
const HEARTBEAT_MS = 2000;
|
|
324
|
+
|
|
325
|
+
const agents = new Map();
|
|
326
|
+
let role = 'main';
|
|
327
|
+
let timer = null;
|
|
328
|
+
let reporting = false;
|
|
329
|
+
let queued = false;
|
|
330
|
+
let heartbeat = null;
|
|
331
|
+
let warned = false;
|
|
332
|
+
const label = value => {
|
|
333
|
+
if (typeof value !== 'string') return undefined;
|
|
334
|
+
const trimmed = value.trim().slice(0, MAX_TEXT);
|
|
335
|
+
return trimmed || undefined;
|
|
336
|
+
};
|
|
337
|
+
const validId = id => (typeof id === 'string' && id.length > 0 && id.length <= 256 ? id : null);
|
|
338
|
+
const statusFor = status =>
|
|
339
|
+
status === 'pending' ? 'waiting' : status === 'running' ? 'running' : null;
|
|
340
|
+
|
|
341
|
+
const put = (id, status, activity) => {
|
|
342
|
+
const key = validId(id);
|
|
343
|
+
if (!key || role !== 'main') return false;
|
|
344
|
+
if (!agents.has(key) && agents.size >= MAX_AGENTS) return false;
|
|
345
|
+
const previous = agents.get(key);
|
|
346
|
+
// The report schema caps the name field at 160 while the id may run to
|
|
347
|
+
// 256; an over-long name would fail validation for the whole snapshot.
|
|
348
|
+
const next = { id: key, name: key.slice(0, MAX_TEXT), status };
|
|
349
|
+
const words = activity || previous?.activity;
|
|
350
|
+
if (words) next.activity = words;
|
|
351
|
+
agents.set(key, next);
|
|
352
|
+
if (heartbeat === null) heartbeat = setInterval(beat, HEARTBEAT_MS);
|
|
353
|
+
return true;
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const drop = id => {
|
|
357
|
+
if (role !== 'main') return false;
|
|
358
|
+
const key = validId(id);
|
|
359
|
+
return key ? agents.delete(key) : false;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const report = async () => {
|
|
363
|
+
if (reporting) {
|
|
364
|
+
queued = true;
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const base = process.env.SUBSHELL_BASE_URL;
|
|
368
|
+
const token = process.env.SUBSHELL_API_KEY;
|
|
369
|
+
const id = process.env.SUBSHELL_ID;
|
|
370
|
+
if (!base || !token || !id) {
|
|
371
|
+
if (!warned) {
|
|
372
|
+
warned = true;
|
|
373
|
+
pi.logger?.debug?.('subshell subagent report skipped: the pane identity environment is missing');
|
|
374
|
+
}
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
reporting = true;
|
|
378
|
+
try {
|
|
379
|
+
const response = await fetch(base + '/api/subshells/' + encodeURIComponent(id) + '/subagents/report', {
|
|
380
|
+
method: 'POST',
|
|
381
|
+
headers: { authorization: 'Bearer ' + token, 'content-type': 'application/json' },
|
|
382
|
+
body: JSON.stringify({
|
|
383
|
+
event: 'snapshot',
|
|
384
|
+
agents: [...agents.values()].slice(0, MAX_AGENTS),
|
|
385
|
+
selectedId: null,
|
|
386
|
+
canSelect: false,
|
|
387
|
+
}),
|
|
388
|
+
signal: AbortSignal.timeout(2000),
|
|
389
|
+
});
|
|
390
|
+
if (!response.ok && !warned) {
|
|
391
|
+
warned = true;
|
|
392
|
+
pi.logger?.debug?.('subshell subagent report failed', { status: response.status });
|
|
393
|
+
}
|
|
394
|
+
} catch {
|
|
395
|
+
// A failed report is not fatal: the next frame reports again, and a pane
|
|
396
|
+
// without the identity variables above has no endpoint to reach.
|
|
397
|
+
} finally {
|
|
398
|
+
reporting = false;
|
|
399
|
+
if (queued) {
|
|
400
|
+
queued = false;
|
|
401
|
+
void report();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const schedule = () => {
|
|
407
|
+
if (timer !== null) return;
|
|
408
|
+
timer = setTimeout(() => {
|
|
409
|
+
timer = null;
|
|
410
|
+
void report();
|
|
411
|
+
}, REPORT_DELAY_MS);
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const beat = () => {
|
|
415
|
+
if (role !== 'main' || agents.size === 0) {
|
|
416
|
+
if (heartbeat !== null) {
|
|
417
|
+
clearInterval(heartbeat);
|
|
418
|
+
heartbeat = null;
|
|
419
|
+
}
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
void report();
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
pi.events.on(LIFECYCLE, frame => {
|
|
426
|
+
if (!frame || typeof frame !== 'object' || typeof frame.status !== 'string') return;
|
|
427
|
+
if (frame.status === 'started') {
|
|
428
|
+
if (put(frame.id, 'running', label(frame.description) || label(frame.agent))) schedule();
|
|
429
|
+
} else if (drop(frame.id)) schedule();
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
pi.events.on(PROGRESS, frame => {
|
|
433
|
+
if (!frame || typeof frame !== 'object') return;
|
|
434
|
+
const progress = frame.progress;
|
|
435
|
+
if (!progress || typeof progress !== 'object') return;
|
|
436
|
+
const status = statusFor(progress.status);
|
|
437
|
+
if (status) {
|
|
438
|
+
if (put(progress.id ?? frame.id, status, label(progress.description) || label(progress.lastIntent))) schedule();
|
|
439
|
+
} else if (progress.status === 'completed' || progress.status === 'failed' || progress.status === 'aborted') {
|
|
440
|
+
if (drop(progress.id ?? frame.id)) schedule();
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
const reset = () => {
|
|
445
|
+
agents.clear();
|
|
446
|
+
if (timer !== null) {
|
|
447
|
+
clearTimeout(timer);
|
|
448
|
+
timer = null;
|
|
449
|
+
}
|
|
450
|
+
if (heartbeat !== null) {
|
|
451
|
+
clearInterval(heartbeat);
|
|
452
|
+
heartbeat = null;
|
|
453
|
+
}
|
|
454
|
+
return report();
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
pi.on('session_start', (_event, ctx) => {
|
|
458
|
+
role = ctx && ctx.agent && ctx.agent.kind === 'sub' ? 'sub' : 'main';
|
|
459
|
+
if (role !== 'main') return;
|
|
460
|
+
return reset();
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// A switch (resume, fork, new) replaces the conversation the roster belonged
|
|
464
|
+
// to; its children are no longer this session's children.
|
|
465
|
+
pi.on('session_switch', () => {
|
|
466
|
+
if (role !== 'main') return;
|
|
467
|
+
return reset();
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
pi.on('session_shutdown', () => {
|
|
471
|
+
if (role !== 'main') return;
|
|
472
|
+
return reset();
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
`;
|
|
476
|
+
//#endregion
|
|
276
477
|
//#region src/manifest.ts
|
|
277
478
|
/**
|
|
278
479
|
* This package's own `subshell` block, read from its package.json so the
|
|
@@ -285,7 +486,7 @@ function validateGenericPreset(preset) {
|
|
|
285
486
|
const parsed = parseManifest({
|
|
286
487
|
name: "@subshell-ai/plugin-oh-my-pi",
|
|
287
488
|
description: "Oh My Pi harness plugin for Subshell",
|
|
288
|
-
version: "0.2.
|
|
489
|
+
version: "0.2.2",
|
|
289
490
|
license: "Apache-2.0",
|
|
290
491
|
type: "module",
|
|
291
492
|
main: "dist/index.js",
|
|
@@ -315,7 +516,7 @@ const parsed = parseManifest({
|
|
|
315
516
|
"@internal/tsconfig": "workspace:*",
|
|
316
517
|
"@subshell-ai/plugin-api": "workspace:*",
|
|
317
518
|
"@types/bun": "1.3.14",
|
|
318
|
-
"hash-runner": "
|
|
519
|
+
"hash-runner": "5.0.0",
|
|
319
520
|
"tsdown": "0.22.14",
|
|
320
521
|
"typescript": "7.0.2"
|
|
321
522
|
},
|
|
@@ -375,12 +576,49 @@ const SETTINGS = [
|
|
|
375
576
|
}
|
|
376
577
|
];
|
|
377
578
|
/**
|
|
579
|
+
* omp composes its terminal title from a brand, one column of run state, and
|
|
580
|
+
* the session label (title-generator's `buildTerminalTitleWithState`):
|
|
581
|
+
* `π > label` while idle, `π <spinner> label` while working, `π ! label` when
|
|
582
|
+
* blocked, and `π : label` when the working separator is pinned to the static
|
|
583
|
+
* colon (WSL, or a spinner pinned off); with titles disabled the form is
|
|
584
|
+
* `π: label`. The brand and the state separator describe the terminal, not the
|
|
585
|
+
* session, and mirroring the whole string read `π ⠋ 🚌 Fix the thing` in the
|
|
586
|
+
* rail — a stuck-looking spinner in front of every name (operator screenshot,
|
|
587
|
+
* 2026-11-09). The label keeps its emoji: the generator starts the session
|
|
588
|
+
* name with one.
|
|
589
|
+
*
|
|
590
|
+
* The whitespace is required where the generator writes one (brand, space,
|
|
591
|
+
* separator), and the disabled form's colon is followed by a space, so a label
|
|
592
|
+
* that merely starts with the brand — `π-calculus`, `π/2`, `π:notes` — keeps
|
|
593
|
+
* it. A bare brand appears for a moment before the session name lands; it
|
|
594
|
+
* parses to nothing, and the caller keeps the name it already had. The spinner
|
|
595
|
+
* set covers every style omp ships: braille frames (U+2801–U+28FF, which holds
|
|
596
|
+
* the activity and dots sets), the pulse moon, and the plain-ASCII line
|
|
597
|
+
* frames.
|
|
598
|
+
*/
|
|
599
|
+
const TITLE_PREFIX = /^π(?:$|\s+(?:[>!:]|[⠁-⣿]|[◔◑◕○●]|[-\\/|])|:(?=\s|$))\s*/u;
|
|
600
|
+
/**
|
|
378
601
|
* Oh My Pi uses its own executable and native MCP discovery, independently of
|
|
379
602
|
* pi. An explicit extension directory contributes mcp.json without redirecting
|
|
380
603
|
* the user's config or credentials. Verified against omp 18.12.6.
|
|
604
|
+
*
|
|
605
|
+
* The same directory carries the subagent bridge. omp keeps its child roster
|
|
606
|
+
* on the session event bus; the extension reports it so the sidebar can hang
|
|
607
|
+
* those children under the parent subshell. omp offers no extension API for
|
|
608
|
+
* moving the parent's terminal onto a child, so navigation stays unavailable.
|
|
381
609
|
*/
|
|
382
610
|
const createPlugin = () => ({
|
|
383
|
-
|
|
611
|
+
subagents: {
|
|
612
|
+
discovery: "extension",
|
|
613
|
+
navigation: "unavailable"
|
|
614
|
+
},
|
|
615
|
+
capabilities: () => [
|
|
616
|
+
"subagents",
|
|
617
|
+
"mcp",
|
|
618
|
+
"settings"
|
|
619
|
+
],
|
|
620
|
+
/** `π <state> <label>`: the label behind the brand and run state is the name. */
|
|
621
|
+
parsePaneTitle: (raw) => raw.replace(TITLE_PREFIX, "").trim(),
|
|
384
622
|
buildCommand({ binary, preset, extraFlags, mcp }) {
|
|
385
623
|
const args = [binary, ...mcp?.args ?? []];
|
|
386
624
|
const settings = preset.settings ?? {};
|
|
@@ -408,9 +646,10 @@ const createPlugin = () => ({
|
|
|
408
646
|
name: "subshell-mcp",
|
|
409
647
|
private: true,
|
|
410
648
|
type: "module",
|
|
411
|
-
omp: { extensions: ["bridge.mjs"] }
|
|
649
|
+
omp: { extensions: ["bridge.mjs", "subagents.mjs"] }
|
|
412
650
|
}),
|
|
413
651
|
"bridge.mjs": "export default function () {}\n",
|
|
652
|
+
"subagents.mjs": SUBAGENT_BRIDGE,
|
|
414
653
|
"mcp.json": fileContent
|
|
415
654
|
},
|
|
416
655
|
args: ["--extension", `${configPath}.d`]
|
|
@@ -419,7 +658,7 @@ const createPlugin = () => ({
|
|
|
419
658
|
mcpSetup() {
|
|
420
659
|
return {
|
|
421
660
|
mode: "auto",
|
|
422
|
-
summary: "Subshell registers its MCP server through a per-launch Oh My Pi extension. Your existing configuration stays in place."
|
|
661
|
+
summary: "Subshell registers its MCP server and reports the pane's subagents through a per-launch Oh My Pi extension. Your existing configuration stays in place."
|
|
423
662
|
};
|
|
424
663
|
},
|
|
425
664
|
validatePreset: validateGenericPreset,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@subshell-ai/plugin-oh-my-pi",
|
|
3
3
|
"description": "Oh My Pi harness plugin for Subshell",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@internal/tsconfig": "workspace:*",
|
|
36
36
|
"@subshell-ai/plugin-api": "workspace:*",
|
|
37
37
|
"@types/bun": "1.3.14",
|
|
38
|
-
"hash-runner": "
|
|
38
|
+
"hash-runner": "5.0.0",
|
|
39
39
|
"tsdown": "0.22.14",
|
|
40
40
|
"typescript": "7.0.2"
|
|
41
41
|
},
|