mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-07 07:55:13 +02:00
docs: move output polish specs out of ktx
This commit is contained in:
parent
9171ef72f4
commit
ed67083779
3 changed files with 0 additions and 1123 deletions
|
|
@ -1,175 +0,0 @@
|
|||
# KTX Setup Agents Output Polish Follow-Up Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Fix the remaining v1-blocking verification gap by updating the parent
|
||||
setup integration test to expect the new inline agent install summary.
|
||||
|
||||
**Architecture:** No production code changes are needed. The already-implemented
|
||||
`runKtxSetupAgentsStep` output now prints a `Claude Code · Project scope`
|
||||
inline summary before the parent `Finish KTX agent setup` note, so the excluded
|
||||
`setup.test.ts` assertion must stop expecting the removed
|
||||
`Agent integration complete` note title.
|
||||
|
||||
**Tech Stack:** TypeScript, Vitest, pnpm workspace commands, `uv` pre-commit.
|
||||
|
||||
---
|
||||
|
||||
## Current audit
|
||||
|
||||
Original spec:
|
||||
`docs/superpowers/specs/2026-05-19-ktx-setup-agents-output-polish-design.md`
|
||||
|
||||
Executed plan:
|
||||
`docs/superpowers/plans/2026-05-19-ktx-setup-agents-output-polish.md`
|
||||
|
||||
Committed implementation:
|
||||
`0cbf8121d9f373e047c43883e8f7623f22cddef7`
|
||||
|
||||
Verification already run:
|
||||
|
||||
- `pnpm --filter @ktx/cli run type-check` passed.
|
||||
- `pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts` passed.
|
||||
- `pnpm --filter @ktx/cli run test` passed, but this script excludes
|
||||
`src/setup.test.ts`.
|
||||
- `pnpm run dead-code` passed.
|
||||
- `source .venv/bin/activate && uv run pre-commit run --files packages/cli/src/setup-agents.ts packages/cli/src/setup-agents.test.ts`
|
||||
passed.
|
||||
|
||||
V1-blocking gap:
|
||||
|
||||
- `pnpm --filter @ktx/cli exec vitest run src/setup.test.ts` fails one test:
|
||||
`prints agent next actions inside the final ready summary during full setup`.
|
||||
The test still expects `Agent integration complete`, which the spec required
|
||||
removing from setup-agents install-summary output.
|
||||
|
||||
Non-blocking gaps:
|
||||
|
||||
- The commit also carries Claude Desktop split-ZIP behavior in
|
||||
`packages/cli/src/setup-agents.ts`. That is outside the output-polish spec,
|
||||
but the executed plan treated it as the baseline and the user identified the
|
||||
remaining README/docs-site split-ZIP edits as unrelated.
|
||||
- Tests do not directly assert that the short multiselect hint is skipped when
|
||||
`--target` is supplied. The current implementation does skip it through the
|
||||
`args.inputMode === 'auto' && args.target === undefined` guard, so this is a
|
||||
coverage improvement, not a v1 blocker.
|
||||
|
||||
## File structure
|
||||
|
||||
Modify:
|
||||
|
||||
- `packages/cli/src/setup.test.ts`
|
||||
- Update the stale full-setup assertion to expect the new inline summary and
|
||||
reject the removed note title.
|
||||
|
||||
Do not modify:
|
||||
|
||||
- `packages/cli/src/setup-agents.ts`
|
||||
- `packages/cli/src/setup-agents.test.ts`
|
||||
- `README.md`
|
||||
- `docs-site/content/docs/integrations/agent-clients.mdx`
|
||||
|
||||
## Task 1: Update the parent setup integration test
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `packages/cli/src/setup.test.ts`
|
||||
|
||||
- [ ] **Step 1: Reproduce the failing direct setup test**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup.test.ts
|
||||
```
|
||||
|
||||
Expected: FAIL with this assertion:
|
||||
|
||||
```text
|
||||
expected ... to contain 'Agent integration complete'
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Replace the stale output assertion**
|
||||
|
||||
In `packages/cli/src/setup.test.ts`, inside the test named
|
||||
`prints agent next actions inside the final ready summary during full setup`,
|
||||
replace:
|
||||
|
||||
```typescript
|
||||
expect(output).toContain('Agent integration complete');
|
||||
expect(output).toContain('Finish KTX agent setup');
|
||||
expect(output).not.toContain('KTX project ready');
|
||||
expect(output).toContain('REQUIRED BEFORE USING AGENTS');
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
expect(output).toContain('Claude Code · Project scope');
|
||||
expect(output).toContain(join(tempDir, '.mcp.json'));
|
||||
expect(output).toContain('Requires MCP to be started.');
|
||||
expect(output).toContain('Analytics skill installed.');
|
||||
expect(output).not.toContain('Agent integration complete');
|
||||
expect(output).toContain('Finish KTX agent setup');
|
||||
expect(output).not.toContain('KTX project ready');
|
||||
expect(output).toContain('REQUIRED BEFORE USING AGENTS');
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run the direct setup test and verify it passes**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup.test.ts
|
||||
```
|
||||
|
||||
Expected: PASS with `46 passed`.
|
||||
|
||||
- [ ] **Step 4: Re-run setup-agents tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts
|
||||
```
|
||||
|
||||
Expected: PASS with `28 passed`.
|
||||
|
||||
- [ ] **Step 5: Run package verification**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli run type-check
|
||||
pnpm --filter @ktx/cli run test
|
||||
pnpm run dead-code
|
||||
source .venv/bin/activate && uv run pre-commit run --files packages/cli/src/setup.test.ts
|
||||
```
|
||||
|
||||
Expected: all commands PASS. The `pnpm --filter @ktx/cli run test` command
|
||||
still excludes `src/setup.test.ts`, so Step 3 remains the required direct
|
||||
coverage for this gap.
|
||||
|
||||
- [ ] **Step 6: Commit the follow-up test fix**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
git add packages/cli/src/setup.test.ts
|
||||
git commit -m "test: update setup agents output polish assertion"
|
||||
```
|
||||
|
||||
Expected: the commit includes only `packages/cli/src/setup.test.ts`. Leave the
|
||||
unrelated dirty `README.md` and
|
||||
`docs-site/content/docs/integrations/agent-clients.mdx` files uncommitted.
|
||||
|
||||
## Self-review checklist
|
||||
|
||||
- `packages/cli/src/setup.test.ts` no longer expects
|
||||
`Agent integration complete`.
|
||||
- The test asserts the new inline summary heading and key body lines.
|
||||
- The test still verifies the parent `Finish KTX agent setup` ready summary and
|
||||
required next-actions content.
|
||||
- No production code changed.
|
||||
- Unrelated README and docs-site split-ZIP edits remain untouched.
|
||||
|
|
@ -1,728 +0,0 @@
|
|||
# KTX Setup Agents Output Polish Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Make `ktx setup --agents` print the polished interactive hint,
|
||||
per-install inline summaries, the required next-actions note, and the final
|
||||
`All set.` outro described by the May 19 output-polish spec.
|
||||
|
||||
**Architecture:** Keep the change centered in
|
||||
`packages/cli/src/setup-agents.ts` and preserve the existing
|
||||
`withMultiselectNavigation` helper for other setup flows. Replace the old
|
||||
boxed install-summary string with structured summary entries, then render those
|
||||
entries with Clack `log.step` for real TTY output and plain `io.stdout` text in
|
||||
tests or scripted output. The current uncommitted Claude Desktop split-ZIP
|
||||
changes are treated as the baseline and must not be reverted.
|
||||
|
||||
**Tech Stack:** TypeScript, Vitest, Node stream types, `@clack/prompts`,
|
||||
pnpm workspace commands.
|
||||
|
||||
---
|
||||
|
||||
## Current audit
|
||||
|
||||
Original spec:
|
||||
`docs/superpowers/specs/2026-05-19-ktx-setup-agents-output-polish-design.md`
|
||||
|
||||
Current uncommitted files:
|
||||
|
||||
- `README.md`
|
||||
- `docs-site/content/docs/integrations/agent-clients.mdx`
|
||||
- `packages/cli/src/setup-agents.test.ts`
|
||||
- `packages/cli/src/setup-agents.ts`
|
||||
|
||||
Observed implementation status:
|
||||
|
||||
- The branch currently contains unrelated Claude Desktop split-ZIP work:
|
||||
`ktx-analytics.zip` and optional `ktx.zip` replace the old combined
|
||||
`ktx-skills.zip`. Keep that work intact.
|
||||
- `packages/cli/src/setup-agents.ts` still imports and uses
|
||||
`withMultiselectNavigation` for the agents multiselect prompt.
|
||||
- `runKtxSetupAgentsStep` does not emit
|
||||
`Space to select, Enter to confirm, Esc to go back.` before interactive
|
||||
prompts.
|
||||
- `formatInstallSummary` still returns one boxed string containing
|
||||
`KTX project`, `Installed agents`, and nested indentation.
|
||||
- `runKtxSetupAgentsStep` still prints the install summary with
|
||||
`setupUi.note(..., 'Agent integration complete', io)`.
|
||||
- `runKtxSetupAgentsStep` does not emit `outro('All set.')` after the
|
||||
"Required before using agents" note.
|
||||
- Tests still assert the old long multiselect message and old boxed install
|
||||
summary.
|
||||
|
||||
V1-blocking gaps:
|
||||
|
||||
- Move the multiselect navigation hint out of the question text and print it
|
||||
once before interactive prompts.
|
||||
- Render install summaries as inline `log.step` blocks, one block per install.
|
||||
- Keep the next-actions note boxed and emit `All set.` only when next-actions
|
||||
are shown.
|
||||
- Update setup-agents tests to cover the new output contract.
|
||||
|
||||
Non-blocking gaps:
|
||||
|
||||
- The docs updates for Claude Desktop split ZIPs are outside this output-polish
|
||||
spec and are already present in the current working tree.
|
||||
- `setup --sources` and `setup --databases` still use
|
||||
`withMultiselectNavigation`; the spec explicitly defers those flows.
|
||||
- There is no docs-site update for the output shape because no public docs page
|
||||
currently promises the old boxed install-summary layout.
|
||||
|
||||
## File structure
|
||||
|
||||
Modify:
|
||||
|
||||
- `packages/cli/src/setup-agents.ts`
|
||||
- Remove the agents-flow dependency on `withMultiselectNavigation`.
|
||||
- Add tiny output helpers that use Clack `log.info`, `log.step`, and `outro`
|
||||
when `io.stdout` is a writable TTY and fall back to plain writes otherwise.
|
||||
- Rename `formatInstallSummary` to `formatInstallSummaryLines` and return
|
||||
structured entries.
|
||||
- Render each summary entry with `log.step`.
|
||||
- Emit the final outro only when next-actions are printed.
|
||||
- `packages/cli/src/setup-agents.test.ts`
|
||||
- Import `formatInstallSummaryLines`.
|
||||
- Update the four output-polish tests named in the spec.
|
||||
- Add or update assertions for the single hint line and final outro.
|
||||
|
||||
Do not modify:
|
||||
|
||||
- `packages/cli/src/prompt-navigation.ts`
|
||||
- `packages/cli/src/prompt-navigation.test.ts`
|
||||
- `README.md`
|
||||
- `docs-site/content/docs/integrations/agent-clients.mdx`
|
||||
|
||||
## Task 1: Move the interactive multiselect hint out of the prompt
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `packages/cli/src/setup-agents.test.ts`
|
||||
- Modify: `packages/cli/src/setup-agents.ts`
|
||||
|
||||
- [ ] **Step 1: Write the failing prompt-output test**
|
||||
|
||||
In `packages/cli/src/setup-agents.test.ts`, replace the existing test named
|
||||
`explains how to select multiple agent targets in interactive mode` with:
|
||||
|
||||
```typescript
|
||||
it('prints one navigation hint before interactive agent target prompts', async () => {
|
||||
const io = makeIo();
|
||||
const prompts = {
|
||||
select: vi.fn(async () => 'mcp-cli'),
|
||||
multiselect: vi.fn(async () => ['back']),
|
||||
cancel: vi.fn(),
|
||||
};
|
||||
|
||||
await expect(
|
||||
runKtxSetupAgentsStep(
|
||||
{
|
||||
projectDir: tempDir,
|
||||
inputMode: 'auto',
|
||||
yes: false,
|
||||
agents: true,
|
||||
scope: 'project',
|
||||
mode: 'mcp-cli',
|
||||
skipAgents: false,
|
||||
},
|
||||
io.io,
|
||||
{ prompts },
|
||||
),
|
||||
).resolves.toEqual({ status: 'back', projectDir: tempDir });
|
||||
|
||||
expect(io.stdout()).toContain('Space to select, Enter to confirm, Esc to go back.');
|
||||
expect(io.stdout().match(/Space to select/g)).toHaveLength(1);
|
||||
expect(prompts.multiselect).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'Which agent targets should KTX install?',
|
||||
}),
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the focused test and verify it fails**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts -t "prints one navigation hint before interactive agent target prompts"
|
||||
```
|
||||
|
||||
Expected: FAIL because the current implementation embeds the long navigation
|
||||
hint in the multiselect message and does not write the short hint to
|
||||
`io.stdout`.
|
||||
|
||||
- [ ] **Step 3: Add Clack output helpers for setup-agents**
|
||||
|
||||
In `packages/cli/src/setup-agents.ts`, replace the current top imports:
|
||||
|
||||
```typescript
|
||||
import { existsSync } from 'node:fs';
|
||||
import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { dirname, join, relative, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
import { existsSync } from 'node:fs';
|
||||
import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { dirname, join, relative, resolve } from 'node:path';
|
||||
import type { Writable } from 'node:stream';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { log, outro } from '@clack/prompts';
|
||||
```
|
||||
|
||||
Remove this import:
|
||||
|
||||
```typescript
|
||||
import { withMultiselectNavigation } from './prompt-navigation.js';
|
||||
```
|
||||
|
||||
Add these helpers after the `KtxCliLauncher` interface:
|
||||
|
||||
```typescript
|
||||
function isWritableTtyOutput(output: KtxCliIo['stdout']): output is KtxCliIo['stdout'] & Writable {
|
||||
return (
|
||||
output.isTTY === true &&
|
||||
typeof (output as { on?: unknown }).on === 'function' &&
|
||||
typeof (output as { columns?: unknown }).columns !== 'undefined'
|
||||
);
|
||||
}
|
||||
|
||||
function writeSetupInfo(io: KtxCliIo, message: string): void {
|
||||
if (isWritableTtyOutput(io.stdout)) {
|
||||
log.info(message, { output: io.stdout });
|
||||
return;
|
||||
}
|
||||
io.stdout.write(`${message}\n`);
|
||||
}
|
||||
|
||||
function writeSetupStep(io: KtxCliIo, message: string): void {
|
||||
if (isWritableTtyOutput(io.stdout)) {
|
||||
log.step(message, { output: io.stdout });
|
||||
return;
|
||||
}
|
||||
io.stdout.write(`\n${message}\n`);
|
||||
}
|
||||
|
||||
function writeSetupOutro(io: KtxCliIo, message: string): void {
|
||||
if (isWritableTtyOutput(io.stdout)) {
|
||||
outro(message, { output: io.stdout });
|
||||
return;
|
||||
}
|
||||
io.stdout.write(`\n${message}\n`);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Print the short hint only before interactive prompts**
|
||||
|
||||
In `runKtxSetupAgentsStep`, immediately after:
|
||||
|
||||
```typescript
|
||||
const prompts = deps.prompts ?? createPromptAdapter();
|
||||
```
|
||||
|
||||
add:
|
||||
|
||||
```typescript
|
||||
if (args.inputMode === 'auto' && args.target === undefined) {
|
||||
writeSetupInfo(io, 'Space to select, Enter to confirm, Esc to go back.');
|
||||
}
|
||||
```
|
||||
|
||||
In the multiselect call, replace:
|
||||
|
||||
```typescript
|
||||
message: withMultiselectNavigation('Which agent targets should KTX install?'),
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
message: 'Which agent targets should KTX install?',
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run the focused test and verify it passes**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts -t "prints one navigation hint before interactive agent target prompts"
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
## Task 2: Return structured install-summary entries
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `packages/cli/src/setup-agents.test.ts`
|
||||
- Modify: `packages/cli/src/setup-agents.ts`
|
||||
|
||||
- [ ] **Step 1: Update the summary formatter import in tests**
|
||||
|
||||
In `packages/cli/src/setup-agents.test.ts`, replace:
|
||||
|
||||
```typescript
|
||||
formatInstallSummary,
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
formatInstallSummaryLines,
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Write failing structured formatter tests**
|
||||
|
||||
In `packages/cli/src/setup-agents.test.ts`, replace the test named
|
||||
`formats summary with explicit project-scoped config paths` with:
|
||||
|
||||
```typescript
|
||||
it('formats summary with explicit project-scoped config paths', () => {
|
||||
const summary = formatInstallSummaryLines(
|
||||
[{ target: 'cursor', scope: 'project', mode: 'mcp-cli' }],
|
||||
[
|
||||
{ kind: 'file', path: join(tempDir, '.cursor/rules/ktx-analytics.mdc'), role: 'analytics-skill' },
|
||||
{ kind: 'file', path: join(tempDir, '.cursor/rules/ktx.mdc') },
|
||||
{ kind: 'json-key', path: join(tempDir, '.cursor/mcp.json'), jsonPath: ['mcpServers', 'ktx'] },
|
||||
],
|
||||
tempDir,
|
||||
);
|
||||
|
||||
expect(summary).toEqual([
|
||||
{
|
||||
title: 'Cursor · Project scope',
|
||||
lines: [
|
||||
join(tempDir, '.cursor/mcp.json'),
|
||||
'Requires MCP to be started.',
|
||||
'Cursor rules installed.',
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
```
|
||||
|
||||
Replace the test named `formats summary with multiple agent targets` with:
|
||||
|
||||
```typescript
|
||||
it('formats summary with multiple agent targets', () => {
|
||||
const summary = formatInstallSummaryLines(
|
||||
[
|
||||
{ target: 'claude-code', scope: 'project', mode: 'mcp-cli' },
|
||||
{ target: 'codex', scope: 'project', mode: 'mcp-cli' },
|
||||
],
|
||||
[
|
||||
{ kind: 'file', path: join(tempDir, '.claude/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
||||
{ kind: 'file', path: join(tempDir, '.claude/skills/ktx/SKILL.md'), role: 'skill' },
|
||||
{ kind: 'file', path: join(tempDir, '.claude/rules/ktx.md'), role: 'rule' },
|
||||
{ kind: 'json-key', path: join(tempDir, '.mcp.json'), jsonPath: ['mcpServers', 'ktx'] },
|
||||
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx-analytics/SKILL.md'), role: 'analytics-skill' },
|
||||
{ kind: 'file', path: join(tempDir, '.agents/skills/ktx/SKILL.md'), role: 'skill' },
|
||||
{ kind: 'file', path: join(tempDir, '.codex/instructions/ktx.md'), role: 'rule' },
|
||||
],
|
||||
tempDir,
|
||||
);
|
||||
|
||||
expect(summary).toEqual([
|
||||
{
|
||||
title: 'Claude Code · Project scope',
|
||||
lines: [
|
||||
join(tempDir, '.mcp.json'),
|
||||
'Requires MCP to be started.',
|
||||
'Analytics skill installed.',
|
||||
'Admin CLI skill installed.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Codex · Project scope',
|
||||
lines: [
|
||||
'Add the snippet shown below to ~/.codex/config.toml.',
|
||||
'Requires MCP to be started.',
|
||||
'Codex guidance installed.',
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run the focused formatter tests and verify they fail**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts -t "formats summary"
|
||||
```
|
||||
|
||||
Expected: FAIL because `formatInstallSummaryLines` is not exported and the
|
||||
current formatter returns one string.
|
||||
|
||||
- [ ] **Step 4: Replace the string summary formatter with structured entries**
|
||||
|
||||
In `packages/cli/src/setup-agents.ts`, add this exported interface immediately
|
||||
before the current `formatInstallSummary` function:
|
||||
|
||||
```typescript
|
||||
export interface InstallSummaryEntry {
|
||||
title: string;
|
||||
lines: string[];
|
||||
}
|
||||
```
|
||||
|
||||
Replace the full `export function formatInstallSummary(...)` implementation
|
||||
with:
|
||||
|
||||
```typescript
|
||||
function formatInlinePath(path: string): string {
|
||||
const home = process.env.HOME;
|
||||
if (!home) return path;
|
||||
const resolvedHome = resolve(home);
|
||||
if (path === resolvedHome) return '~';
|
||||
if (path.startsWith(`${resolvedHome}/`)) {
|
||||
return `~/${relative(resolvedHome, path)}`;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
export function formatInstallSummaryLines(
|
||||
installs: Array<{ target: KtxAgentTarget; scope: KtxAgentScope; mode: KtxAgentInstallMode }>,
|
||||
entries: InstallEntry[],
|
||||
projectDir: string,
|
||||
): InstallSummaryEntry[] {
|
||||
const entriesByTarget = new Map<KtxAgentTarget, InstallEntry[]>();
|
||||
for (const install of installs) {
|
||||
const plannedFilePaths = new Set(
|
||||
plannedKtxAgentFiles({ projectDir, ...install })
|
||||
.filter((entry) => entry.kind === 'file')
|
||||
.map((entry) => entry.path),
|
||||
);
|
||||
entriesByTarget.set(
|
||||
install.target,
|
||||
entries.filter((entry) => entry.kind === 'file' && plannedFilePaths.has(entry.path)),
|
||||
);
|
||||
}
|
||||
|
||||
const mcpEntriesByTarget = new Map<KtxAgentTarget, InstallEntry[]>();
|
||||
for (const install of installs) {
|
||||
const plannedMcpKeys = new Set(plannedMcpJsonEntries({ projectDir, ...install }).map(entryKey));
|
||||
mcpEntriesByTarget.set(
|
||||
install.target,
|
||||
entries.filter((entry) => entry.kind === 'json-key' && plannedMcpKeys.has(entryKey(entry))),
|
||||
);
|
||||
}
|
||||
|
||||
return installs.map((install) => {
|
||||
const targetEntries = entriesByTarget.get(install.target) ?? [];
|
||||
const mcpEntry = mcpEntriesByTarget
|
||||
.get(install.target)
|
||||
?.find((entry): entry is Extract<InstallEntry, { kind: 'json-key' }> => entry.kind === 'json-key');
|
||||
const lines: string[] = [];
|
||||
|
||||
if (mcpEntry) {
|
||||
lines.push(formatInlinePath(mcpEntry.path));
|
||||
} else if (install.target !== 'claude-desktop') {
|
||||
lines.push(manualMcpConfigInstruction(install.target, install.scope));
|
||||
}
|
||||
|
||||
if (targetUsesHttpMcpDaemon(install.target)) {
|
||||
lines.push('Requires MCP to be started.');
|
||||
}
|
||||
|
||||
const hasAnalytics = hasEntryRole(targetEntries, 'analytics-skill');
|
||||
const hasAdmin = hasAdminCliEntries(targetEntries);
|
||||
const claudeDesktopSkillBundles = targetEntries.filter(
|
||||
(entry): entry is Extract<InstallEntry, { kind: 'file' }> =>
|
||||
entry.kind === 'file' && entry.role === 'claude-desktop-skill-bundle',
|
||||
);
|
||||
|
||||
if (install.target === 'claude-code') {
|
||||
if (hasAnalytics) {
|
||||
lines.push('Analytics skill installed.');
|
||||
}
|
||||
if (hasAdmin) {
|
||||
lines.push('Admin CLI skill installed.');
|
||||
}
|
||||
} else if (install.target === 'claude-desktop') {
|
||||
if (claudeDesktopSkillBundles.length > 0) {
|
||||
lines.push('Skill bundles:');
|
||||
for (const bundle of claudeDesktopSkillBundles) {
|
||||
lines.push(` ${bundle.path}`);
|
||||
}
|
||||
}
|
||||
} else if (hasAnalytics || hasAdmin) {
|
||||
lines.push(`${guidanceInstallLine(install.target)}.`);
|
||||
}
|
||||
|
||||
if (hasEntryRole(targetEntries, 'launcher')) {
|
||||
lines.push('Starts KTX over stdio from Claude Desktop.');
|
||||
}
|
||||
|
||||
return {
|
||||
title: `${targetDisplayName(install.target)} · ${scopeDisplayName(install.scope)}`,
|
||||
lines,
|
||||
};
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run the focused formatter tests and verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts -t "formats summary"
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
## Task 3: Render inline install summaries with `log.step`
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `packages/cli/src/setup-agents.test.ts`
|
||||
- Modify: `packages/cli/src/setup-agents.ts`
|
||||
|
||||
- [ ] **Step 1: Write failing install-summary output assertions**
|
||||
|
||||
In `packages/cli/src/setup-agents.test.ts`, replace the body of the test named
|
||||
`prints per-agent install summary after successful installation` with:
|
||||
|
||||
```typescript
|
||||
const io = makeIo();
|
||||
|
||||
await runKtxSetupAgentsStep(
|
||||
{
|
||||
projectDir: tempDir,
|
||||
inputMode: 'disabled',
|
||||
yes: true,
|
||||
agents: true,
|
||||
target: 'claude-code',
|
||||
scope: 'project',
|
||||
mode: 'mcp-cli',
|
||||
skipAgents: false,
|
||||
},
|
||||
io.io,
|
||||
);
|
||||
|
||||
const output = io.stdout();
|
||||
expect(output).toContain('Claude Code · Project scope');
|
||||
expect(output).toContain(join(tempDir, '.mcp.json'));
|
||||
expect(output).toContain('Requires MCP to be started.');
|
||||
expect(output).toContain('Analytics skill installed.');
|
||||
expect(output).toContain('Admin CLI skill installed.');
|
||||
expect(output).not.toContain('Agent integration complete');
|
||||
expect(output).not.toContain(`KTX project\n ${tempDir}`);
|
||||
expect(output).not.toContain('Installed agents');
|
||||
expect(output).not.toContain('.claude/skills/ktx-analytics/SKILL.md');
|
||||
expect(output).not.toContain('.claude/skills/ktx/SKILL.md');
|
||||
expect(output).not.toContain('.claude/rules/ktx.md');
|
||||
```
|
||||
|
||||
In the test named `can return agent next actions without printing them`, replace:
|
||||
|
||||
```typescript
|
||||
expect(io.stdout()).toContain('Agent integration complete');
|
||||
expect(io.stdout()).not.toContain('Required before using agents');
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
expect(io.stdout()).toContain('Claude Code · Project scope');
|
||||
expect(io.stdout()).not.toContain('Agent integration complete');
|
||||
expect(io.stdout()).not.toContain('Required before using agents');
|
||||
expect(io.stdout()).not.toContain('All set.');
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the focused output tests and verify they fail**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts -t "install summary|without printing"
|
||||
```
|
||||
|
||||
Expected: FAIL because `Agent integration complete` is still printed and the
|
||||
new inline summary heading is absent.
|
||||
|
||||
- [ ] **Step 3: Render each structured summary as an inline step**
|
||||
|
||||
In `runKtxSetupAgentsStep`, replace:
|
||||
|
||||
```typescript
|
||||
setupUi.note(
|
||||
formatInstallSummary(installs, entries, args.projectDir),
|
||||
'Agent integration complete',
|
||||
io,
|
||||
);
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
for (const summary of formatInstallSummaryLines(installs, entries, args.projectDir)) {
|
||||
writeSetupStep(
|
||||
io,
|
||||
summary.lines.length > 0 ? `${summary.title}\n${summary.lines.join('\n')}` : summary.title,
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run the focused output tests and verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts -t "install summary|without printing"
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
## Task 4: Emit the final outro after next actions
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `packages/cli/src/setup-agents.test.ts`
|
||||
- Modify: `packages/cli/src/setup-agents.ts`
|
||||
|
||||
- [ ] **Step 1: Add failing outro assertions**
|
||||
|
||||
In `packages/cli/src/setup-agents.test.ts`, in the test named
|
||||
`prints standalone agent next actions after successful installation`, add this
|
||||
assertion after the existing next-actions stdout assertions:
|
||||
|
||||
```typescript
|
||||
expect(io.stdout()).toContain('All set.');
|
||||
```
|
||||
|
||||
In the test named `prints one target-aware next actions block for mixed agent
|
||||
targets`, add this assertion near the other stdout assertions:
|
||||
|
||||
```typescript
|
||||
expect(output).toContain('All set.');
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the focused next-actions tests and verify they fail**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts -t "next actions|All set|without printing"
|
||||
```
|
||||
|
||||
Expected: FAIL because no outro is emitted.
|
||||
|
||||
- [ ] **Step 3: Emit `All set.` only when next-actions are shown**
|
||||
|
||||
In `runKtxSetupAgentsStep`, replace:
|
||||
|
||||
```typescript
|
||||
if (args.showNextActions !== false) {
|
||||
setupUi.note(nextActions, 'Required before using agents', io, { format: (line) => line });
|
||||
}
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
if (args.showNextActions !== false) {
|
||||
setupUi.note(nextActions, 'Required before using agents', io, { format: (line) => line });
|
||||
writeSetupOutro(io, 'All set.');
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run the focused next-actions tests and verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts -t "next actions|All set|without printing"
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
## Task 5: Verify the full CLI package surface
|
||||
|
||||
**Files:**
|
||||
|
||||
- Verify: `packages/cli/src/setup-agents.ts`
|
||||
- Verify: `packages/cli/src/setup-agents.test.ts`
|
||||
|
||||
- [ ] **Step 1: Run the setup-agents test file**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli exec vitest run src/setup-agents.test.ts
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 2: Run the CLI type-check**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli run type-check
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 3: Run the CLI test script**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --filter @ktx/cli run test
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 4: Run dead-code checks for TypeScript changes**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm run dead-code
|
||||
```
|
||||
|
||||
Expected: PASS. If Knip reports unrelated existing findings, inspect them and
|
||||
record the exact unrelated findings before finishing.
|
||||
|
||||
- [ ] **Step 5: Run pre-commit for modified TypeScript files**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pre-commit run --files packages/cli/src/setup-agents.ts packages/cli/src/setup-agents.test.ts
|
||||
```
|
||||
|
||||
Expected: PASS. If the repository has no usable pre-commit environment or the
|
||||
configured tool versions are unavailable, state the exact failure and keep the
|
||||
passing pnpm checks as the closest available verification.
|
||||
|
||||
- [ ] **Step 6: Inspect the final diff**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git diff -- packages/cli/src/setup-agents.ts packages/cli/src/setup-agents.test.ts
|
||||
```
|
||||
|
||||
Expected: The diff only contains output-polish changes on top of the existing
|
||||
Claude Desktop split-ZIP baseline. It must not revert the current
|
||||
`ktx-analytics.zip` and `ktx.zip` behavior.
|
||||
|
||||
## Self-review checklist
|
||||
|
||||
- The agents multiselect message is exactly
|
||||
`Which agent targets should KTX install?`.
|
||||
- The short navigation hint appears once for interactive no-target runs and not
|
||||
for scripted target runs.
|
||||
- The shared `withMultiselectNavigation` helper and its tests are unchanged.
|
||||
- `Agent integration complete`, `KTX project`, and `Installed agents` no longer
|
||||
appear in setup-agents install-summary output.
|
||||
- `Required before using agents` remains a `note()` box.
|
||||
- `All set.` appears only when the next-actions note is printed.
|
||||
- Current uncommitted README and docs-site split-ZIP edits are untouched.
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
# `ktx setup --agents` output polish
|
||||
|
||||
## Problem
|
||||
|
||||
The current `ktx setup --agents` flow renders four visual issues:
|
||||
|
||||
1. The multiselect prompt embeds a long navigation hint
|
||||
(`Use Up/Down to move, Space to select or unselect, Enter to confirm,
|
||||
Escape to go back, or Ctrl+C to exit.`) directly into the question text.
|
||||
Clack uses the same message verbatim for the confirmed-state echo, so the
|
||||
hint stays on screen after the user has already answered.
|
||||
2. After install, two heavy `note()` boxes appear back-to-back ("Agent
|
||||
integration complete" and "Required before using agents") with the same
|
||||
visual weight, making the post-install output feel boxed in.
|
||||
3. The install-complete box has 3–4 levels of nested indentation, which is
|
||||
hard to scan.
|
||||
4. The flow ends after the second box; there is no `outro` marker.
|
||||
|
||||
## Scope
|
||||
|
||||
Narrow: changes apply only to `runKtxSetupAgentsStep` in
|
||||
`packages/cli/src/setup-agents.ts`. The shared `withMultiselectNavigation`
|
||||
helper is left as-is so `setup --sources` and `setup --databases` keep their
|
||||
current prompt rendering until a follow-up.
|
||||
|
||||
## Goals
|
||||
|
||||
- Replace the in-question navigation hint for the agents multiselect with a
|
||||
one-time `log.info(...)` line emitted before the first interactive prompt.
|
||||
- Render the per-install summary inline using `log.step` (one block per
|
||||
install) instead of a `note()` box.
|
||||
- Keep the "Required before using agents" `note()` box (the file paths users
|
||||
copy/paste benefit from visual framing).
|
||||
- End the flow with `outro('All set.')` when next-actions are shown.
|
||||
|
||||
Non-goals:
|
||||
|
||||
- No change to the actual install logic, manifest, or generated config files.
|
||||
- No change to `setup --sources` or `setup --databases` (deferred).
|
||||
- No change to `formatAgentNextActions` content (only the surrounding `note`
|
||||
call site is touched; body stays).
|
||||
- No path shortening to project-relative in next-actions output — users may
|
||||
copy/paste from terminals outside the project, so absolute paths stay.
|
||||
|
||||
## Design
|
||||
|
||||
### Prompt polish
|
||||
|
||||
In `runKtxSetupAgentsStep`, emit the navigation hint **once, only when
|
||||
interactive prompts will actually run** (`args.inputMode === 'auto'` and
|
||||
`args.target === undefined`), immediately before the first prompt:
|
||||
|
||||
```ts
|
||||
log.info('Space to select, Enter to confirm, Esc to go back.');
|
||||
```
|
||||
|
||||
In scripted mode (`inputMode === 'disabled'` or `--target` supplied), the
|
||||
hint is skipped — no prompts run, so the help would be noise.
|
||||
|
||||
Replace `withMultiselectNavigation('Which agent targets should KTX install?')`
|
||||
with the plain string `'Which agent targets should KTX install?'`.
|
||||
|
||||
The shared `withMultiselectNavigation` helper, its export, and its
|
||||
`prompt-navigation.test.ts` test remain untouched — they still apply to the
|
||||
other two setup flows.
|
||||
|
||||
### Install summary
|
||||
|
||||
Today: `formatInstallSummary(...)` returns a multi-line string that
|
||||
`runKtxSetupAgentsStep` passes to `setupUi.note(..., 'Agent integration
|
||||
complete', io)`.
|
||||
|
||||
New: rename `formatInstallSummary` to `formatInstallSummaryLines` and have it
|
||||
return a structured array — one entry per install — with:
|
||||
|
||||
```ts
|
||||
type InstallSummaryEntry = {
|
||||
title: string; // e.g. "Claude Desktop · Global scope"
|
||||
lines: string[]; // body lines, no indentation prefix
|
||||
};
|
||||
```
|
||||
|
||||
`runKtxSetupAgentsStep` then iterates and emits one `log.step(title +
|
||||
'\n' + body)` per install. The "KTX project / <path>" header line is dropped
|
||||
(the user already sees the project path in the existing `intro` block, e.g.
|
||||
`Project: /tmp/ktx7`).
|
||||
|
||||
Body lines for a Claude Desktop install:
|
||||
|
||||
```
|
||||
~/Library/Application Support/Claude/claude_desktop_config.json
|
||||
Skill bundles:
|
||||
/tmp/ktx7/.ktx/agents/claude/ktx-analytics.zip
|
||||
/tmp/ktx7/.ktx/agents/claude/ktx.zip
|
||||
Starts KTX over stdio from Claude Desktop.
|
||||
```
|
||||
|
||||
Body lines for a Claude Code project install (HTTP MCP target):
|
||||
|
||||
```
|
||||
Project scope: /tmp/ktx7/.mcp.json
|
||||
Requires MCP to be started.
|
||||
Analytics skill installed.
|
||||
Admin CLI skill installed.
|
||||
```
|
||||
|
||||
Path rendering: replace a leading `$HOME` with `~` for absolute paths shown
|
||||
inline in the body, leaving non-home paths untouched. Skill-bundle paths
|
||||
inside `projectDir` stay absolute (users copy these into the Claude Desktop
|
||||
file picker).
|
||||
|
||||
### Next actions
|
||||
|
||||
`formatAgentNextActions(...)` is unchanged. The `setupUi.note(...,
|
||||
'Required before using agents', io, { format: (line) => line })` call site
|
||||
stays. This is the one remaining box and acts as the visual payoff with
|
||||
copy-pasteable instructions.
|
||||
|
||||
### Outro
|
||||
|
||||
After the next-actions note (when `args.showNextActions !== false`), emit:
|
||||
|
||||
```ts
|
||||
outro('All set.');
|
||||
```
|
||||
|
||||
When next actions are suppressed (`showNextActions === false`), no `outro` is
|
||||
emitted — the caller is composing a larger flow.
|
||||
|
||||
## Final layout (Claude Desktop, global)
|
||||
|
||||
```
|
||||
┌ KTX setup
|
||||
│ Project: /tmp/ktx7
|
||||
│
|
||||
● Space to select, Enter to confirm, Esc to go back.
|
||||
│
|
||||
◇ What should agents be allowed to do with this KTX project?
|
||||
│ Ask data questions + manage KTX with CLI commands
|
||||
│
|
||||
◇ Which agent targets should KTX install?
|
||||
│ Claude Desktop
|
||||
│
|
||||
◆ Claude Desktop · Global scope
|
||||
│ ~/Library/Application Support/Claude/claude_desktop_config.json
|
||||
│ Skill bundles:
|
||||
│ /tmp/ktx7/.ktx/agents/claude/ktx-analytics.zip
|
||||
│ /tmp/ktx7/.ktx/agents/claude/ktx.zip
|
||||
│ Starts KTX over stdio from Claude Desktop.
|
||||
│
|
||||
◇ Required before using agents ───────────────────────────────────╮
|
||||
│ │
|
||||
│ 1. Restart Claude Desktop │
|
||||
│ Claude Desktop loads KTX MCP after restart. │
|
||||
│ │
|
||||
│ 2. Upload Claude Desktop skills │
|
||||
│ Open Claude Desktop: Customize > Skills > + > Create skill. │
|
||||
│ Upload these files: │
|
||||
│ /tmp/ktx7/.ktx/agents/claude/ktx-analytics.zip │
|
||||
│ /tmp/ktx7/.ktx/agents/claude/ktx.zip │
|
||||
│ Toggle the uploaded KTX skills on. │
|
||||
│ │
|
||||
├──────────────────────────────────────────────────────────────────╯
|
||||
│
|
||||
└ All set.
|
||||
```
|
||||
|
||||
## Test impact
|
||||
|
||||
`packages/cli/src/setup-agents.test.ts` — four tests update:
|
||||
|
||||
- **`explains how to select multiple agent targets in interactive mode`**
|
||||
(L879) — currently asserts the multiselect `message` ends with the long
|
||||
navigation hint string. Update to assert the plain message string. The test
|
||||
name and intent shift: it now verifies the multiselect message no longer
|
||||
embeds the hint and that the navigation hint was emitted once via stdout
|
||||
before the prompts (`Space to select, Enter to confirm, Esc to go back.`).
|
||||
- **`prints per-agent install summary after successful installation`**
|
||||
(L911) — currently asserts `Agent integration complete` appears in stdout
|
||||
plus the multi-level indented summary structure (`KTX project\n
|
||||
${tempDir}`, `Installed agents`, `Project scope\n ${path}`).
|
||||
Update to assert the new inline `log.step` lines:
|
||||
- `Claude Code · Project scope` heading
|
||||
- path on its own line (e.g., `${join(tempDir, '.mcp.json')}`)
|
||||
- `Requires MCP to be started.`
|
||||
- `Analytics skill installed.`
|
||||
- `Admin CLI skill installed.`
|
||||
- Stops asserting `Agent integration complete` and `KTX project\n …`.
|
||||
- **`formats summary with explicit project-scoped config paths`** (L942) and
|
||||
**`formats summary with multiple agent targets`** (L961) — call
|
||||
`formatInstallSummary` directly. Update to call
|
||||
`formatInstallSummaryLines` and assert on the returned structured array
|
||||
shape and content per install.
|
||||
- **`can return agent next actions without printing them`** (L212) — asserts
|
||||
`'Agent integration complete'` appears in stdout when `showNextActions:
|
||||
false`. Update to assert the new heading `Claude Code · Project scope`
|
||||
appears instead.
|
||||
|
||||
The next-actions assertions (L181, L1028, L1089) keep passing — that code
|
||||
path is unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
- `pnpm --filter @ktx/cli run type-check`
|
||||
- `pnpm --filter @ktx/cli run test`
|
||||
- Manual run: `ktx setup --agents` against `/tmp/ktx7` (or any fresh project)
|
||||
with `--target claude-desktop --scope global --mode mcp-cli` to compare
|
||||
against the layout above.
|
||||
|
||||
## Risks
|
||||
|
||||
- The `formatInstallSummary` rename is a breaking change for any external
|
||||
importer. Grep confirms it is only imported from
|
||||
`packages/cli/src/setup-agents.test.ts`; no production consumer outside
|
||||
`runKtxSetupAgentsStep`. KTX has no public users (`feedback_ktx_no_backward_compat`),
|
||||
so a clean rename is acceptable.
|
||||
- `log.info` for the navigation hint runs in TTY mode only via Clack. In
|
||||
non-TTY mode (`setupUi.note` falls back to plain stdout) the hint is not
|
||||
emitted. Acceptable because non-TTY runs are scripted (`--target`/`--yes`)
|
||||
and don't need keyboard help.
|
||||
Loading…
Add table
Add a link
Reference in a new issue