Files
msd-core/tests/pi-extension-reachability.test.cjs
Tom Boucher 79d7657eff feat(#2102): make pi a first-class installable runtime + fix its dispatch (ADR-1239)
Net-new EoS/pi installable runtime — purely additive (no prior runtime==='pi'
branches). pi is a bun-runtime programmatic-CLI whose /gsd command is registered
by a native ExtensionAPI extension and dispatches through the embedded engine.

Stage 1 (install plumbing):
- capabilities/pi/capability.json: full hostIntegration descriptor (imperative /
  slash-programmatic / active-model / native-extension / bun) + hostBehaviors
  {nativePlugin, pluginOnlyInstall}.
- --pi flag + interactive-menu renumber (All 17->18); pi added to RUNTIME_FLAG_IDS,
  RUNTIME_LABELS, RUNTIME_META, allRuntimes/runtimeMap, model-catalog defaults.
- Install mirrors OpenCode: pi installs the gsd.cjs extension + the shared engine
  payload (gsd-core + scripts + config markers) + the shared hooks bundle (spawned
  by the extension at lifecycle events, like OpenCode's plugin). pluginOnlyInstall
  EXCLUDES declarative command/agent/skill markdown, which pi has no host-read
  surface for (its /gsd is programmatic). _installNativePluginIfDeclared (extracted
  from the opencode-family path) copies pi/gsd.cjs -> ~/.pi/agent/extensions/gsd.cjs
  (global) / .pi/extensions/ (local). pi added to package.json files.
- Golden: new pi.json (320 files: extension + engine + 27-file hooks bundle, no
  markdown); the 16 other fixtures + claude-local change only by the shared
  model-catalog hash line.

Stage 2 (real dispatch + upgrades):
- Shared dispatchGsdCommand() (shell-command-projection): bounded, no-throw
  subprocess-shim to gsd-tools.cjs (the only full-surface dispatch path; no
  in-process full-hub factory exists). Fixes pi/gsd.cjs's createHub()-no-args bug
  (every dispatch was UnknownCommand) AND the identical bug in mcp-server.cts's
  gsd_invoke_command, which a vacuous unknown-family-only test had masked (now has
  a real dispatch regression test).
- pi/gsd.cjs: /gsd handler now (args, ctx) - tokenizes (quote-aware, via the
  shipped hooks/lib/git-cmd.js) + dispatches real family/subcommand (not hardcoded
  query/help); gsd_invoke gets a TypeBox (JSON-schema-fallback) parameters schema +
  consumes params; getArgumentCompletions; before_provider_request active-model
  steering (fail-open on null resolution); functional session_start /
  before_agent_start / session_before_compact hook bridges (spawn the shipped GSD
  hook scripts).
- EXTENSION_EVENT_SURFACES.pi expanded from ['tool_call'] to the full 30-event
  vocabulary.

Docs (host-integration matrix + how-to) + changeset (Added).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:07:38 -04:00

118 lines
4.9 KiB
JavaScript

'use strict';
/**
* pi extension reachability test — ADR-1239 Phase D / #1944, upgraded #2102.
*
* Proves the pi extension is keystone-WIRED: the registered /gsd command's
* `handler(args, ctx)` (pi's REAL ExtensionAPI shape — NOT the `execute(ctx)`
* shape the original #1944 cut used) dispatches through gsd-tools.cjs
* (subprocess-reuse — dispatchGsdCommand) and returns real output, not just a
* registration on a mock. This is the "user can invoke X" proof.
*
* Dispatch is exercised with a real read-only family/subcommand
* (progress/json) against a real temp project, matching the sibling
* tests/vscode-extension-reachability.test.cjs pattern — no fake dispatcher
* injected, because the whole point of "reachability" is that the real
* engine is reached.
*/
const { test } = require('node:test');
const assert = require('node:assert/strict');
const gsdPiExtension = require('../pi/gsd.cjs');
const { _internals } = require('../pi/gsd.cjs');
const { createTempDir, cleanup } = require('./helpers.cjs');
function mockPi() {
const recorded = { commands: {}, tools: {}, events: {} };
return {
registerCommand(name, def) { recorded.commands[name] = def; },
registerTool(def) { if (def && def.name) recorded.tools[def.name] = def; },
on(event, handler) { (recorded.events[event] = recorded.events[event] || []).push(handler); },
_recorded: recorded,
};
}
test('the pi extension registers /gsd (with getArgumentCompletions + handler) + gsd_invoke + the event surface via ExtensionAPI', () => {
const pi = mockPi();
gsdPiExtension(pi);
const gsdCommand = pi._recorded.commands['gsd'];
assert.ok(gsdCommand, 'registers /gsd command');
assert.equal(typeof gsdCommand.handler, 'function', '/gsd registers a handler(args, ctx) — pi\'s REAL ExtensionAPI shape, not execute(ctx)');
assert.equal(typeof gsdCommand.getArgumentCompletions, 'function', '/gsd registers getArgumentCompletions');
assert.ok(pi._recorded.tools['gsd_invoke'], 'registers gsd_invoke tool');
assert.equal(typeof pi._recorded.tools['gsd_invoke'].execute, 'function');
assert.ok(pi._recorded.events['tool_call'], 'subscribes to tool_call');
assert.ok(pi._recorded.events['before_provider_request'], 'subscribes to before_provider_request');
});
test('REACHABILITY: parseGsdCommandArgs tokenizes a raw args string into {family, subcommand, args}', () => {
const parsed = _internals.parseGsdCommandArgs('phase add --name test');
assert.deepEqual(parsed, { family: 'phase', subcommand: 'add', args: ['--name', 'test'] });
});
test('REACHABILITY: empty args dispatch a working default (gsd-tools.cjs --help), not the broken "query help"', () => {
const parsed = _internals.parseGsdCommandArgs('');
assert.equal(parsed.family, '--help');
assert.equal(parsed.subcommand, undefined);
});
test('REACHABILITY: the /gsd handler dispatches a real family through gsd-tools.cjs and returns real output (keystone wired)', async () => {
const pi = mockPi();
gsdPiExtension(pi);
const dir = createTempDir();
try {
const result = await pi._recorded.commands['gsd'].handler('progress json', { cwd: dir });
assert.equal(typeof result, 'string', '/gsd handler returns a string result');
const parsed = JSON.parse(result);
assert.equal(typeof parsed.percent, 'number', '/gsd dispatch reached gsd-tools.cjs for real (the engine was reached)');
} finally {
cleanup(dir);
}
});
test('REACHABILITY: an unknown family surfaces a clear GSD error string, not a throw', async () => {
const pi = mockPi();
gsdPiExtension(pi);
const dir = createTempDir();
try {
const result = await pi._recorded.commands['gsd'].handler('no-such-family-8675309', { cwd: dir });
assert.equal(typeof result, 'string');
assert.match(result, /GSD error:/);
assert.match(result, /no-such-family-8675309|Unknown command/);
} finally {
cleanup(dir);
}
});
test('REACHABILITY: the gsd_invoke tool dispatches through the engine and returns real content', async () => {
const pi = mockPi();
gsdPiExtension(pi);
const dir = createTempDir();
try {
const result = await pi._recorded.tools['gsd_invoke'].execute(
'call-1',
{ family: 'progress', subcommand: 'json' },
null,
null,
{ cwd: dir },
);
assert.ok(result && Array.isArray(result.content), 'gsd_invoke returns {content:[...]}');
const parsed = JSON.parse(result.content[0].text);
assert.equal(typeof parsed.percent, 'number', 'gsd_invoke dispatch reached gsd-tools.cjs for real');
} finally {
cleanup(dir);
}
});
test('gsd_invoke rejects a missing "family" without dispatching', async () => {
const pi = mockPi();
gsdPiExtension(pi);
const result = await pi._recorded.tools['gsd_invoke'].execute('call-2', {}, null, null, {});
assert.match(result.content[0].text, /requires a non-empty string "family"/);
});
test('gsdPiExtension throws without pi ExtensionAPI (fail-closed)', () => {
assert.throws(() => gsdPiExtension(null), /ExtensionAPI is required/);
});