mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 15:39:41 +02:00
fix(pi): align the doctor with the pi resolver, correct the strip rationale, update the skill
Second review pass on #282, the three items left open after f4dcfbe.
1. `codeman doctor` and the run mode disagreed about pi. The registry entry
accepted a bare `which pi` hit while pi-cli-resolver demanded semver-shaped
`--version` output, so the Dependencies panel could report an installed Pi CLI
on a box where Run Pi stays hidden, which reads as a broken mode rather than a
missing install. Both sides now share one exported PI_VERSION_REGEX, and
PathResolver gains an opt-in `requireVersionMatch` so a binary that fails the
shape check is reported MISSING instead of installed-with-unknown-version.
Only pi sets it; every other tool keeps its current behaviour.
2. The isAltScreenStripMode comment justified excluding pi with "the alt screen
is load-bearing for its fullscreen TUI". That is not what exclusion does: pi
is tmux-backed, so it falls through to isMuxAltScreenOnlyStripMode, which
strips the alt-screen toggles anyway. What exclusion actually preserves is
`\x1b[3J` and the mouse DECSETs, which is the real reason (pi renders into the
main screen and is mouse-aware). Comment and changeset now say that, and state
the consequence: fullscreen pi paints into the main buffer, like vim in a tmux
shell session.
3. skills/codeman still enumerated the five pre-pi modes in nine places, telling
agents a backend does not exist and understating class-wide caveats by one
mode. All updated, plus stale session.ts line references refreshed.
Tests: a new static guard derives the mode set from the Zod schema (not a copy)
and fails when a skill enumeration lists a partial set of external CLIs, verified
by mutation. It also documents the one legitimate exception it found: the "writes
no transcript" lists drop codex, which does write a rollout Codeman reads back.
Plus doctor cases for an unrelated `pi` on PATH and registry/resolver regex parity.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
} from '../src/utils/dependency-checker.js';
|
||||
import type { ProbeHost } from '../src/utils/dependency-checker.js';
|
||||
import type { ProbeEnvironment, ToolDependency } from '../src/config/dependency-registry.js';
|
||||
import { PI_VERSION_REGEX } from '../src/utils/pi-cli-resolver.js';
|
||||
|
||||
describe('DEPENDENCY_REGISTRY', () => {
|
||||
it('has unique ids', () => {
|
||||
@@ -29,6 +30,20 @@ describe('DEPENDENCY_REGISTRY', () => {
|
||||
expect(office.every((t) => t.required === false)).toBe(true);
|
||||
});
|
||||
|
||||
it('resolves pi through the SAME version rule the run mode uses', () => {
|
||||
// `pi` is a short generic name, so pi-cli-resolver.ts refuses a binary that does not
|
||||
// print semver. If the doctor did not apply the identical rule it would report
|
||||
// "Pi CLI ✓" on a box where Run Pi stays hidden, which reads as a broken mode
|
||||
// rather than a missing install. One regex, shared, is what keeps them agreeing.
|
||||
const pi = DEPENDENCY_REGISTRY.find((t) => t.id === 'pi');
|
||||
expect(pi).toBeDefined();
|
||||
const spec = pi!.resolvers.find((r) => r.resolver.kind === 'path');
|
||||
expect(spec).toBeDefined();
|
||||
const resolver = spec!.resolver as { versionRegex?: RegExp; requireVersionMatch?: boolean };
|
||||
expect(resolver.requireVersionMatch).toBe(true);
|
||||
expect(resolver.versionRegex).toBe(PI_VERSION_REGEX);
|
||||
});
|
||||
|
||||
it('gives msoffice a windows-side resolver scoped to wsl + win32 only', () => {
|
||||
const ms = DEPENDENCY_REGISTRY.find((t) => t.id === 'msoffice');
|
||||
expect(ms).toBeDefined();
|
||||
@@ -164,6 +179,54 @@ describe('checkTool', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkTool with requireVersionMatch (generic binary names)', () => {
|
||||
const piTool: ToolDependency = {
|
||||
id: 'pi',
|
||||
label: 'Pi CLI',
|
||||
category: 'core',
|
||||
required: false,
|
||||
resolvers: [
|
||||
{
|
||||
match: ['linux'],
|
||||
resolver: {
|
||||
kind: 'path',
|
||||
bins: ['pi'],
|
||||
versionArg: '--version',
|
||||
versionRegex: PI_VERSION_REGEX,
|
||||
requireVersionMatch: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('accepts a binary that prints a semver version', () => {
|
||||
const host = fakeHost('linux', { which: () => '/home/u/.npm-global/bin/pi', runVersion: () => '0.84.1\n' });
|
||||
expect(checkTool(piTool, host)).toMatchObject({
|
||||
id: 'pi',
|
||||
status: 'ok',
|
||||
version: '0.84.1',
|
||||
path: '/home/u/.npm-global/bin/pi',
|
||||
});
|
||||
});
|
||||
|
||||
it('reports MISSING for an unrelated `pi` on PATH instead of an installed tool', () => {
|
||||
// The whole point: a Raspberry Pi helper answers `--version` with prose, and calling
|
||||
// that "installed" contradicts resolvePiDir(), which rejects it.
|
||||
const host = fakeHost('linux', { which: () => '/usr/bin/pi', runVersion: () => 'Raspberry Pi utility\n' });
|
||||
expect(checkTool(piTool, host)).toMatchObject({ id: 'pi', status: 'missing' });
|
||||
});
|
||||
|
||||
it('reports MISSING when the binary answers nothing at all', () => {
|
||||
const host = fakeHost('linux', { which: () => '/usr/bin/pi', runVersion: () => null });
|
||||
expect(checkTool(piTool, host)).toMatchObject({ id: 'pi', status: 'missing' });
|
||||
});
|
||||
|
||||
it('leaves tools without the flag reporting ok on an unparsable version (unchanged)', () => {
|
||||
const host = fakeHost('linux', { which: () => '/usr/bin/tmux', runVersion: () => 'no version here' });
|
||||
expect(checkTool(tmuxTool, host)).toMatchObject({ id: 'tmux', status: 'ok', version: undefined });
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkAll', () => {
|
||||
it('maps every tool to a result', () => {
|
||||
const results = checkAll([tmuxTool, msTool], fakeHost('linux'));
|
||||
|
||||
Reference in New Issue
Block a user