beth-copilot 1.1.0 → 2.1.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/CHANGELOG.md +51 -1
- package/README.md +121 -132
- package/assets/beth-questioning.png +0 -0
- package/assets/yellowstone-beth.png +0 -0
- package/bin/cli.js +359 -445
- package/dist/__tests__/inject-skills.test.d.ts +9 -0
- package/dist/__tests__/inject-skills.test.d.ts.map +1 -0
- package/dist/__tests__/inject-skills.test.js +143 -0
- package/dist/__tests__/inject-skills.test.js.map +1 -0
- package/dist/__tests__/skills/disambiguation.test.d.ts +10 -0
- package/dist/__tests__/skills/disambiguation.test.d.ts.map +1 -0
- package/dist/__tests__/skills/disambiguation.test.js +192 -0
- package/dist/__tests__/skills/disambiguation.test.js.map +1 -0
- package/dist/__tests__/skills/hook-injection.test.d.ts +11 -0
- package/dist/__tests__/skills/hook-injection.test.d.ts.map +1 -0
- package/dist/__tests__/skills/hook-injection.test.js +173 -0
- package/dist/__tests__/skills/hook-injection.test.js.map +1 -0
- package/dist/__tests__/skills/mapping-completeness.test.d.ts +17 -0
- package/dist/__tests__/skills/mapping-completeness.test.d.ts.map +1 -0
- package/dist/__tests__/skills/mapping-completeness.test.js +281 -0
- package/dist/__tests__/skills/mapping-completeness.test.js.map +1 -0
- package/dist/__tests__/skills/pipeline-integration.test.d.ts +18 -0
- package/dist/__tests__/skills/pipeline-integration.test.d.ts.map +1 -0
- package/dist/__tests__/skills/pipeline-integration.test.js +234 -0
- package/dist/__tests__/skills/pipeline-integration.test.js.map +1 -0
- package/dist/__tests__/skills/skill-routing.test.d.ts +15 -0
- package/dist/__tests__/skills/skill-routing.test.d.ts.map +1 -0
- package/dist/__tests__/skills/skill-routing.test.js +723 -0
- package/dist/__tests__/skills/skill-routing.test.js.map +1 -0
- package/dist/__tests__/skills/trigger-coverage.test.d.ts +24 -0
- package/dist/__tests__/skills/trigger-coverage.test.d.ts.map +1 -0
- package/dist/__tests__/skills/trigger-coverage.test.js +746 -0
- package/dist/__tests__/skills/trigger-coverage.test.js.map +1 -0
- package/dist/__tests__/smoke.test.js +13 -0
- package/dist/__tests__/smoke.test.js.map +1 -1
- package/dist/__tests__/verify-skills.test.d.ts +9 -0
- package/dist/__tests__/verify-skills.test.d.ts.map +1 -0
- package/dist/__tests__/verify-skills.test.js +78 -0
- package/dist/__tests__/verify-skills.test.js.map +1 -0
- package/dist/cli/commands/beads.e2e.test.d.ts +4 -2
- package/dist/cli/commands/beads.e2e.test.d.ts.map +1 -1
- package/dist/cli/commands/beads.e2e.test.js +97 -38
- package/dist/cli/commands/beads.e2e.test.js.map +1 -1
- package/dist/cli/commands/cli-edge-cases.e2e.test.js +1 -1
- package/dist/cli/commands/cli-edge-cases.e2e.test.js.map +1 -1
- package/dist/cli/commands/close.d.ts +11 -46
- package/dist/cli/commands/close.d.ts.map +1 -1
- package/dist/cli/commands/close.e2e.test.d.ts +4 -20
- package/dist/cli/commands/close.e2e.test.d.ts.map +1 -1
- package/dist/cli/commands/close.e2e.test.js +23 -204
- package/dist/cli/commands/close.e2e.test.js.map +1 -1
- package/dist/cli/commands/close.js +26 -240
- package/dist/cli/commands/close.js.map +1 -1
- package/dist/cli/commands/close.test.d.ts +7 -9
- package/dist/cli/commands/close.test.d.ts.map +1 -1
- package/dist/cli/commands/close.test.js +44 -424
- package/dist/cli/commands/close.test.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +16 -22
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.e2e.test.js +3 -59
- package/dist/cli/commands/doctor.e2e.test.js.map +1 -1
- package/dist/cli/commands/doctor.js +87 -103
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/doctor.test.js +120 -229
- package/dist/cli/commands/doctor.test.js.map +1 -1
- package/dist/cli/commands/framework-isolation.test.d.ts +1 -1
- package/dist/cli/commands/framework-isolation.test.js +2 -3
- package/dist/cli/commands/framework-isolation.test.js.map +1 -1
- package/dist/cli/commands/help.e2e.test.js +1 -5
- package/dist/cli/commands/help.e2e.test.js.map +1 -1
- package/dist/cli/commands/init-logic.e2e.test.js +114 -2
- package/dist/cli/commands/init-logic.e2e.test.js.map +1 -1
- package/dist/cli/commands/init.test.js +4 -21
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/commands/land.d.ts +3 -15
- package/dist/cli/commands/land.d.ts.map +1 -1
- package/dist/cli/commands/land.js +13 -68
- package/dist/cli/commands/land.js.map +1 -1
- package/dist/cli/commands/land.test.d.ts +0 -1
- package/dist/cli/commands/land.test.d.ts.map +1 -1
- package/dist/cli/commands/land.test.js +2 -57
- package/dist/cli/commands/land.test.js.map +1 -1
- package/dist/cli/commands/mcp.e2e.test.js +28 -3
- package/dist/cli/commands/mcp.e2e.test.js.map +1 -1
- package/dist/cli/commands/pipeline.e2e.test.js +23 -26
- package/dist/cli/commands/pipeline.e2e.test.js.map +1 -1
- package/dist/cli/commands/pre-push-guard.d.ts +2 -12
- package/dist/cli/commands/pre-push-guard.d.ts.map +1 -1
- package/dist/cli/commands/pre-push-guard.e2e.test.js +1 -1
- package/dist/cli/commands/pre-push-guard.e2e.test.js.map +1 -1
- package/dist/cli/commands/pre-push-guard.js +2 -47
- package/dist/cli/commands/pre-push-guard.js.map +1 -1
- package/dist/cli/commands/pre-push-guard.test.d.ts +0 -1
- package/dist/cli/commands/pre-push-guard.test.d.ts.map +1 -1
- package/dist/cli/commands/pre-push-guard.test.js +15 -98
- package/dist/cli/commands/pre-push-guard.test.js.map +1 -1
- package/dist/cli/commands/quickstart-expanded.e2e.test.d.ts +1 -1
- package/dist/cli/commands/quickstart-expanded.e2e.test.js +3 -30
- package/dist/cli/commands/quickstart-expanded.e2e.test.js.map +1 -1
- package/dist/cli/commands/quickstart.d.ts +0 -1
- package/dist/cli/commands/quickstart.d.ts.map +1 -1
- package/dist/cli/commands/quickstart.js +2 -60
- package/dist/cli/commands/quickstart.js.map +1 -1
- package/dist/cli/commands/quickstart.test.js +10 -104
- package/dist/cli/commands/quickstart.test.js.map +1 -1
- package/dist/cli/commands/uninstall.test.d.ts +5 -0
- package/dist/cli/commands/uninstall.test.d.ts.map +1 -0
- package/dist/cli/commands/uninstall.test.js +223 -0
- package/dist/cli/commands/uninstall.test.js.map +1 -0
- package/dist/cli/commands/update.d.ts +35 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.e2e.test.d.ts +24 -0
- package/dist/cli/commands/update.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/update.e2e.test.js +238 -0
- package/dist/cli/commands/update.e2e.test.js.map +1 -0
- package/dist/cli/commands/update.js +255 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/core/agents/frontmatter.test.js +1 -1
- package/dist/core/agents/frontmatter.test.js.map +1 -1
- package/dist/core/agents/handoffs.test.js +1 -1
- package/dist/core/agents/handoffs.test.js.map +1 -1
- package/dist/core/agents/loader.d.ts +4 -2
- package/dist/core/agents/loader.d.ts.map +1 -1
- package/dist/core/agents/loader.js +5 -3
- package/dist/core/agents/loader.js.map +1 -1
- package/dist/core/agents/loader.test.js +42 -4
- package/dist/core/agents/loader.test.js.map +1 -1
- package/dist/core/agents/suite.test.js +8 -7
- package/dist/core/agents/suite.test.js.map +1 -1
- package/dist/core/agents/tools.test.js +10 -8
- package/dist/core/agents/tools.test.js.map +1 -1
- package/dist/core/agents/types.test.js +1 -1
- package/dist/core/agents/types.test.js.map +1 -1
- package/dist/core/skills/loader.test.js +1 -1
- package/dist/core/skills/loader.test.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/pathValidation.d.ts +0 -5
- package/dist/lib/pathValidation.d.ts.map +1 -1
- package/dist/lib/pathValidation.js +0 -11
- package/dist/lib/pathValidation.js.map +1 -1
- package/dist/lib/pathValidation.test.js +2 -14
- package/dist/lib/pathValidation.test.js.map +1 -1
- package/package.json +3 -6
- package/sbom.json +259 -371
- package/templates/.github/agents/beth.agent.md +194 -122
- package/templates/.github/agents/developer.agent.md +30 -22
- package/templates/.github/agents/product-manager.agent.md +15 -6
- package/templates/.github/agents/researcher.agent.md +10 -7
- package/templates/.github/agents/security-reviewer.agent.md +16 -7
- package/templates/.github/agents/tester.agent.md +16 -8
- package/templates/.github/agents/ux-designer.agent.md +12 -9
- package/templates/.github/copilot-instructions.md +33 -4
- package/templates/.github/copilot-mcp-config.json +12 -0
- package/templates/.github/dependabot.yml +68 -0
- package/templates/.github/hooks/scripts/inject-skills.mjs +139 -0
- package/templates/.github/hooks/scripts/verify-skills.mjs +47 -0
- package/templates/.github/hooks/skill-enforcement.json +18 -0
- package/templates/.github/pull_request_template.md +48 -0
- package/templates/.github/skills/framer-components/SKILL.md +0 -0
- package/templates/.github/skills/prd/SKILL.md +0 -0
- package/templates/.github/skills/security-analysis/SKILL.md +798 -798
- package/templates/.github/skills/shadcn-ui/SKILL.md +561 -561
- package/templates/.github/skills/vercel-react-best-practices/AGENTS.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/SKILL.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-parallel.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-auth-actions.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-dedup-props.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-serialization.md +0 -0
- package/templates/.github/skills/web-design-guidelines/SKILL.md +0 -0
- package/templates/.vscode/settings.json +16 -16
- package/templates/AGENTS.md +59 -98
- package/templates/Backlog.md +80 -80
- package/templates/mcp.json.example +8 -0
- package/assets/beth-portrait-small.txt +0 -13
- package/assets/beth-portrait.txt +0 -60
- package/bin/beth-animation.sh +0 -155
- package/bin/lib/animation.js +0 -189
- package/bin/lib/pathValidation.js +0 -233
- package/bin/lib/pathValidation.test.js +0 -280
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for uninstall command.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import { existsSync, mkdirSync, writeFileSync, rmSync, readFileSync, readdirSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { tmpdir } from 'os';
|
|
10
|
+
const CLI_PATH = join(process.cwd(), 'bin', 'cli.js');
|
|
11
|
+
/**
|
|
12
|
+
* Run init in a directory to set up a Beth installation.
|
|
13
|
+
*/
|
|
14
|
+
function runInit(cwd, flags = []) {
|
|
15
|
+
const command = `node "${CLI_PATH}" init ${flags.join(' ')}`;
|
|
16
|
+
try {
|
|
17
|
+
return execSync(command, {
|
|
18
|
+
cwd,
|
|
19
|
+
encoding: 'utf-8',
|
|
20
|
+
env: { ...process.env, NO_COLOR: '1' },
|
|
21
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
const execError = error;
|
|
26
|
+
return execError.stdout || '';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run the uninstall command.
|
|
31
|
+
*/
|
|
32
|
+
function runUninstall(cwd, flags = []) {
|
|
33
|
+
const command = `node "${CLI_PATH}" uninstall ${flags.join(' ')}`;
|
|
34
|
+
try {
|
|
35
|
+
const stdout = execSync(command, {
|
|
36
|
+
cwd,
|
|
37
|
+
encoding: 'utf-8',
|
|
38
|
+
env: { ...process.env, NO_COLOR: '1' },
|
|
39
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
40
|
+
});
|
|
41
|
+
return { stdout, stderr: '', exitCode: 0 };
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const execError = error;
|
|
45
|
+
return {
|
|
46
|
+
stdout: execError.stdout || '',
|
|
47
|
+
stderr: execError.stderr || '',
|
|
48
|
+
exitCode: execError.status || 1,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
describe('uninstall command', () => {
|
|
53
|
+
let testDir;
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
testDir = join(tmpdir(), `beth-uninstall-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
56
|
+
mkdirSync(testDir, { recursive: true });
|
|
57
|
+
});
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
if (existsSync(testDir)) {
|
|
60
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
describe('removes Beth-installed files', () => {
|
|
64
|
+
it('should remove .github/agents directory', () => {
|
|
65
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
66
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'agents')), true, 'agents dir should exist after init');
|
|
67
|
+
runUninstall(testDir, ['--force']);
|
|
68
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'agents')), false, 'agents dir should be removed');
|
|
69
|
+
});
|
|
70
|
+
it('should remove .github/skills directory', () => {
|
|
71
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
72
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'skills')), true, 'skills dir should exist after init');
|
|
73
|
+
runUninstall(testDir, ['--force']);
|
|
74
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'skills')), false, 'skills dir should be removed');
|
|
75
|
+
});
|
|
76
|
+
it('should remove .github/hooks directory', () => {
|
|
77
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
78
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'hooks')), true, 'hooks dir should exist after init');
|
|
79
|
+
runUninstall(testDir, ['--force']);
|
|
80
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'hooks')), false, 'hooks dir should be removed');
|
|
81
|
+
});
|
|
82
|
+
it('should remove AGENTS.md', () => {
|
|
83
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
84
|
+
assert.strictEqual(existsSync(join(testDir, 'AGENTS.md')), true, 'AGENTS.md should exist after init');
|
|
85
|
+
runUninstall(testDir, ['--force']);
|
|
86
|
+
assert.strictEqual(existsSync(join(testDir, 'AGENTS.md')), false, 'AGENTS.md should be removed');
|
|
87
|
+
});
|
|
88
|
+
it('should remove Backlog.md', () => {
|
|
89
|
+
// Create a Backlog.md manually (since --skip-backlog skips backlog init)
|
|
90
|
+
writeFileSync(join(testDir, 'Backlog.md'), '# Backlog');
|
|
91
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
92
|
+
// Manually ensure Backlog.md exists for the test
|
|
93
|
+
writeFileSync(join(testDir, 'Backlog.md'), '# Backlog');
|
|
94
|
+
runUninstall(testDir, ['--force']);
|
|
95
|
+
assert.strictEqual(existsSync(join(testDir, 'Backlog.md')), false, 'Backlog.md should be removed');
|
|
96
|
+
});
|
|
97
|
+
it('should remove .github/copilot-instructions.md', () => {
|
|
98
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
99
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'copilot-instructions.md')), true, 'copilot-instructions.md should exist after init');
|
|
100
|
+
runUninstall(testDir, ['--force']);
|
|
101
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'copilot-instructions.md')), false, 'copilot-instructions.md should be removed');
|
|
102
|
+
});
|
|
103
|
+
it('should remove .vscode/settings.json', () => {
|
|
104
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
105
|
+
assert.strictEqual(existsSync(join(testDir, '.vscode', 'settings.json')), true, 'settings.json should exist after init');
|
|
106
|
+
runUninstall(testDir, ['--force']);
|
|
107
|
+
assert.strictEqual(existsSync(join(testDir, '.vscode', 'settings.json')), false, 'settings.json should be removed');
|
|
108
|
+
});
|
|
109
|
+
it('should remove mcp.json.example', () => {
|
|
110
|
+
runInit(testDir);
|
|
111
|
+
assert.strictEqual(existsSync(join(testDir, 'mcp.json.example')), true, 'mcp.json.example should exist after init');
|
|
112
|
+
runUninstall(testDir, ['--force']);
|
|
113
|
+
assert.strictEqual(existsSync(join(testDir, 'mcp.json.example')), false, 'mcp.json.example should be removed');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe('removes backlog directory', () => {
|
|
117
|
+
it('should remove backlog/ directory if it exists', () => {
|
|
118
|
+
// Simulate what `backlog init` creates
|
|
119
|
+
const backlogDir = join(testDir, 'backlog');
|
|
120
|
+
mkdirSync(join(backlogDir, 'tasks'), { recursive: true });
|
|
121
|
+
writeFileSync(join(backlogDir, 'config.yml'), 'prefix: TEST');
|
|
122
|
+
// Also need agents dir so uninstall detects an installation
|
|
123
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
124
|
+
runUninstall(testDir, ['--force']);
|
|
125
|
+
assert.strictEqual(existsSync(backlogDir), false, 'backlog/ should be removed');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('cleans up empty parent directories', () => {
|
|
129
|
+
it('should remove .github/ if empty after cleanup', () => {
|
|
130
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
131
|
+
assert.strictEqual(existsSync(join(testDir, '.github')), true, '.github should exist after init');
|
|
132
|
+
runUninstall(testDir, ['--force']);
|
|
133
|
+
assert.strictEqual(existsSync(join(testDir, '.github')), false, '.github should be removed when empty');
|
|
134
|
+
});
|
|
135
|
+
it('should remove .vscode/ if empty after cleanup', () => {
|
|
136
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
137
|
+
assert.strictEqual(existsSync(join(testDir, '.vscode')), true, '.vscode should exist after init');
|
|
138
|
+
runUninstall(testDir, ['--force']);
|
|
139
|
+
assert.strictEqual(existsSync(join(testDir, '.vscode')), false, '.vscode should be removed when empty');
|
|
140
|
+
});
|
|
141
|
+
it('should preserve .github/ if it has non-Beth files', () => {
|
|
142
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
143
|
+
// Add a non-Beth file to .github
|
|
144
|
+
mkdirSync(join(testDir, '.github', 'workflows'), { recursive: true });
|
|
145
|
+
writeFileSync(join(testDir, '.github', 'workflows', 'ci.yml'), 'name: CI');
|
|
146
|
+
runUninstall(testDir, ['--force']);
|
|
147
|
+
assert.strictEqual(existsSync(join(testDir, '.github')), true, '.github should be preserved');
|
|
148
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'workflows', 'ci.yml')), true, 'non-Beth files preserved');
|
|
149
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'agents')), false, 'Beth dirs still removed');
|
|
150
|
+
});
|
|
151
|
+
it('should preserve .vscode/ if it has non-Beth files', () => {
|
|
152
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
153
|
+
// Add a non-Beth file to .vscode
|
|
154
|
+
writeFileSync(join(testDir, '.vscode', 'extensions.json'), '{}');
|
|
155
|
+
runUninstall(testDir, ['--force']);
|
|
156
|
+
assert.strictEqual(existsSync(join(testDir, '.vscode')), true, '.vscode should be preserved');
|
|
157
|
+
assert.strictEqual(existsSync(join(testDir, '.vscode', 'extensions.json')), true, 'non-Beth files preserved');
|
|
158
|
+
assert.strictEqual(existsSync(join(testDir, '.vscode', 'settings.json')), false, 'Beth files still removed');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
describe('pre-push hook cleanup', () => {
|
|
162
|
+
it('should remove Beth guard block from pre-push hook', () => {
|
|
163
|
+
// Create a .git/hooks directory and a pre-push hook with Beth guard
|
|
164
|
+
const hooksDir = join(testDir, '.git', 'hooks');
|
|
165
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
166
|
+
const guardContent = `#!/bin/sh
|
|
167
|
+
# User's custom hook
|
|
168
|
+
echo "custom check"
|
|
169
|
+
|
|
170
|
+
# --- BEGIN BETH GUARD ---
|
|
171
|
+
# Branch discipline enforcement — installed by beth-copilot
|
|
172
|
+
echo "beth guard"
|
|
173
|
+
# --- END BETH GUARD ---
|
|
174
|
+
`;
|
|
175
|
+
writeFileSync(join(hooksDir, 'pre-push'), guardContent);
|
|
176
|
+
// Need agents dir so uninstall detects installation
|
|
177
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
178
|
+
runUninstall(testDir, ['--force']);
|
|
179
|
+
const hookPath = join(hooksDir, 'pre-push');
|
|
180
|
+
assert.strictEqual(existsSync(hookPath), true, 'hook should still exist (has user content)');
|
|
181
|
+
const remaining = readFileSync(hookPath, 'utf-8');
|
|
182
|
+
assert.ok(!remaining.includes('BEGIN BETH GUARD'), 'guard block should be removed');
|
|
183
|
+
assert.ok(remaining.includes('custom check'), 'user content should be preserved');
|
|
184
|
+
});
|
|
185
|
+
it('should remove entire pre-push file if only Beth guard remains', () => {
|
|
186
|
+
const hooksDir = join(testDir, '.git', 'hooks');
|
|
187
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
188
|
+
const guardOnlyContent = `#!/bin/sh
|
|
189
|
+
# --- BEGIN BETH GUARD ---
|
|
190
|
+
# Branch discipline enforcement — installed by beth-copilot
|
|
191
|
+
echo "beth guard"
|
|
192
|
+
# --- END BETH GUARD ---
|
|
193
|
+
`;
|
|
194
|
+
writeFileSync(join(hooksDir, 'pre-push'), guardOnlyContent);
|
|
195
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
196
|
+
runUninstall(testDir, ['--force']);
|
|
197
|
+
assert.strictEqual(existsSync(join(hooksDir, 'pre-push')), false, 'hook should be removed when Beth-only');
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
describe('no installation detected', () => {
|
|
201
|
+
it('should exit gracefully when no Beth installation exists', () => {
|
|
202
|
+
const result = runUninstall(testDir, ['--force']);
|
|
203
|
+
assert.ok(result.stdout.includes('No Beth installation') || result.exitCode === 0, 'Should handle missing installation gracefully');
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
describe('full round-trip', () => {
|
|
207
|
+
it('init then uninstall should leave directory clean', () => {
|
|
208
|
+
// Record initial state (empty dir)
|
|
209
|
+
const before = readdirSync(testDir);
|
|
210
|
+
// Install Beth
|
|
211
|
+
runInit(testDir, ['--skip-backlog', '--skip-mcp']);
|
|
212
|
+
// Verify something was installed
|
|
213
|
+
assert.strictEqual(existsSync(join(testDir, '.github', 'agents')), true, 'init should install agents');
|
|
214
|
+
assert.strictEqual(existsSync(join(testDir, 'AGENTS.md')), true, 'init should install AGENTS.md');
|
|
215
|
+
// Uninstall Beth
|
|
216
|
+
runUninstall(testDir, ['--force']);
|
|
217
|
+
// Directory should be back to empty (or have only files that existed before)
|
|
218
|
+
const after = readdirSync(testDir);
|
|
219
|
+
assert.deepStrictEqual(after.sort(), before.sort(), 'directory should be clean after uninstall');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
//# sourceMappingURL=uninstall.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.test.js","sourceRoot":"","sources":["../../../src/cli/commands/uninstall.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAC7F,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAE5B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAEtD;;GAEG;AACH,SAAS,OAAO,CAAC,GAAW,EAAE,QAAkB,EAAE;IAChD,MAAM,OAAO,GAAG,SAAS,QAAQ,UAAU,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7D,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,OAAO,EAAE;YACvB,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE;YACtC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,SAAS,GAAG,KAA4B,CAAC;QAC/C,OAAO,SAAS,CAAC,MAAM,IAAI,EAAE,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,GAAW,EAAE,QAAkB,EAAE;IACrD,MAAM,OAAO,GAAG,SAAS,QAAQ,eAAe,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,EAAE;YAC/B,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE;YACtC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,SAAS,GAAG,KAA8D,CAAC;QACjF,OAAO;YACL,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,EAAE;YAC9B,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,EAAE;YAC9B,QAAQ,EAAE,SAAS,CAAC,MAAM,IAAI,CAAC;SAChC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,OAAe,CAAC;IAEpB,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,uBAAuB,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACrG,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,oCAAoC,CAAC,CAAC;YAE/G,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,8BAA8B,CAAC,CAAC;QAC5G,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,oCAAoC,CAAC,CAAC;YAE/G,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,8BAA8B,CAAC,CAAC;QAC5G,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC/C,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,mCAAmC,CAAC,CAAC;YAE7G,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,6BAA6B,CAAC,CAAC;QAC1G,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;YACjC,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,mCAAmC,CAAC,CAAC;YAEtG,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,6BAA6B,CAAC,CAAC;QACnG,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;YAClC,yEAAyE;YACzE,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,WAAW,CAAC,CAAC;YACxD,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YAEnD,iDAAiD;YACjD,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,WAAW,CAAC,CAAC;YAExD,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,8BAA8B,CAAC,CAAC;QACrG,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACvD,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,WAAW,CAChB,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,yBAAyB,CAAC,CAAC,EAC/D,IAAI,EACJ,iDAAiD,CAClD,CAAC;YAEF,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAChB,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,yBAAyB,CAAC,CAAC,EAC/D,KAAK,EACL,2CAA2C,CAC5C,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;YAC7C,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,uCAAuC,CAAC,CAAC;YAEzH,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,iCAAiC,CAAC,CAAC;QACtH,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,OAAO,CAAC,OAAO,CAAC,CAAC;YACjB,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE,0CAA0C,CAAC,CAAC;YAEpH,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,oCAAoC,CAAC,CAAC;QACjH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACzC,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACvD,uCAAuC;YACvC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC5C,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,cAAc,CAAC,CAAC;YAE9D,4DAA4D;YAC5D,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YAEnD,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,4BAA4B,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAClD,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACvD,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,iCAAiC,CAAC,CAAC;YAElG,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,sCAAsC,CAAC,CAAC;QAC1G,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACvD,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,iCAAiC,CAAC,CAAC;YAElG,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,sCAAsC,CAAC,CAAC;QAC1G,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,iCAAiC;YACjC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACtE,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC;YAE3E,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,6BAA6B,CAAC,CAAC;YAC9F,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,0BAA0B,CAAC,CAAC;YAClH,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,yBAAyB,CAAC,CAAC;QACvG,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACnD,iCAAiC;YACjC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;YAEjE,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,6BAA6B,CAAC,CAAC;YAC9F,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,0BAA0B,CAAC,CAAC;YAC9G,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,0BAA0B,CAAC,CAAC;QAC/G,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,oEAAoE;YACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YAChD,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAEzC,MAAM,YAAY,GAAG;;;;;;;;CAQ1B,CAAC;YACI,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC;YAExD,oDAAoD;YACpD,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YAEnD,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YAEnC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YAC5C,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,4CAA4C,CAAC,CAAC;YAE7F,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,+BAA+B,CAAC,CAAC;YACpF,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,kCAAkC,CAAC,CAAC;QACpF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;YACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YAChD,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAEzC,MAAM,gBAAgB,GAAG;;;;;CAK9B,CAAC;YACI,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;YAE5D,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YAEnD,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YAEnC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,uCAAuC,CAAC,CAAC;QAC7G,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YAClD,MAAM,CAAC,EAAE,CACP,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAC,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EACvE,+CAA+C,CAChD,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,mCAAmC;YACnC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YAEpC,eAAe;YACf,OAAO,CAAC,OAAO,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YAEnD,iCAAiC;YACjC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,4BAA4B,CAAC,CAAC;YACvG,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,+BAA+B,CAAC,CAAC;YAElG,iBAAiB;YACjB,YAAY,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YAEnC,6EAA6E;YAC7E,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YACnC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,2CAA2C,CAAC,CAAC;QACnG,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update Command
|
|
3
|
+
*
|
|
4
|
+
* Updates project files to the latest Beth templates.
|
|
5
|
+
* Compares installed files against the package templates and
|
|
6
|
+
* copies new or updated files into the project.
|
|
7
|
+
*
|
|
8
|
+
* Behavior:
|
|
9
|
+
* - New files (exist in template but not in project) → always installed
|
|
10
|
+
* - Unchanged files (content matches template) → skipped
|
|
11
|
+
* - User-modified files (content differs from template):
|
|
12
|
+
* - Without --force → skipped with warning
|
|
13
|
+
* - With --force → overwritten
|
|
14
|
+
* - --check-only → reports status without modifying anything
|
|
15
|
+
* - --verbose → shows per-file detail
|
|
16
|
+
*
|
|
17
|
+
* Options:
|
|
18
|
+
* --check-only Report update status without modifying files
|
|
19
|
+
* --force Overwrite user-modified files with template versions
|
|
20
|
+
* --verbose Show per-file detail
|
|
21
|
+
*/
|
|
22
|
+
export interface UpdateOptions {
|
|
23
|
+
checkOnly?: boolean;
|
|
24
|
+
force?: boolean;
|
|
25
|
+
verbose?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse update command arguments from raw argv.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseUpdateArgs(rawArgs: string[]): UpdateOptions;
|
|
31
|
+
/**
|
|
32
|
+
* Main update command.
|
|
33
|
+
*/
|
|
34
|
+
export declare function update(rawArgs: string[]): Promise<void>;
|
|
35
|
+
//# sourceMappingURL=update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAgBH,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAYD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,aAAa,CAQhE;AA+DD;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqJ7D"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E tests for the update command.
|
|
3
|
+
* Run with: npx vitest run src/cli/commands/update.e2e.test.ts
|
|
4
|
+
*
|
|
5
|
+
* beth-r08.6: Validates `npx beth-copilot update` behavior end-to-end.
|
|
6
|
+
*
|
|
7
|
+
* Test scenarios:
|
|
8
|
+
* 1. Command is recognized (not rejected as unknown)
|
|
9
|
+
* 2. Listed in help output
|
|
10
|
+
* 3. --check-only reports version status without modifying files
|
|
11
|
+
* 4. Reports "already up to date" when templates match
|
|
12
|
+
* 5. Shows update summary with --verbose
|
|
13
|
+
* 6. --force overwrites existing files
|
|
14
|
+
* 7. Does not overwrite user-modified files without --force
|
|
15
|
+
* 8. Handles network errors gracefully (npm registry unreachable)
|
|
16
|
+
* 9. Exits with code 0 on success, non-zero on failure
|
|
17
|
+
* 10. Installs new agent/skill files that did not exist before
|
|
18
|
+
*
|
|
19
|
+
* These tests exercise the real CLI binary in temp directories.
|
|
20
|
+
* They do NOT hit the npm registry — network calls are tested
|
|
21
|
+
* separately or mocked via env vars.
|
|
22
|
+
*/
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=update.e2e.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.e2e.test.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/update.e2e.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E tests for the update command.
|
|
3
|
+
* Run with: npx vitest run src/cli/commands/update.e2e.test.ts
|
|
4
|
+
*
|
|
5
|
+
* beth-r08.6: Validates `npx beth-copilot update` behavior end-to-end.
|
|
6
|
+
*
|
|
7
|
+
* Test scenarios:
|
|
8
|
+
* 1. Command is recognized (not rejected as unknown)
|
|
9
|
+
* 2. Listed in help output
|
|
10
|
+
* 3. --check-only reports version status without modifying files
|
|
11
|
+
* 4. Reports "already up to date" when templates match
|
|
12
|
+
* 5. Shows update summary with --verbose
|
|
13
|
+
* 6. --force overwrites existing files
|
|
14
|
+
* 7. Does not overwrite user-modified files without --force
|
|
15
|
+
* 8. Handles network errors gracefully (npm registry unreachable)
|
|
16
|
+
* 9. Exits with code 0 on success, non-zero on failure
|
|
17
|
+
* 10. Installs new agent/skill files that did not exist before
|
|
18
|
+
*
|
|
19
|
+
* These tests exercise the real CLI binary in temp directories.
|
|
20
|
+
* They do NOT hit the npm registry — network calls are tested
|
|
21
|
+
* separately or mocked via env vars.
|
|
22
|
+
*/
|
|
23
|
+
import { describe, it, beforeEach, afterEach } from 'vitest';
|
|
24
|
+
import assert from 'node:assert';
|
|
25
|
+
import { execSync } from 'child_process';
|
|
26
|
+
import { existsSync, mkdirSync, writeFileSync, rmSync, readFileSync, readdirSync, } from 'fs';
|
|
27
|
+
import { join, resolve } from 'path';
|
|
28
|
+
import { tmpdir } from 'os';
|
|
29
|
+
const CLI_PATH = resolve(join(import.meta.dirname, '..', '..', '..', 'bin', 'cli.js'));
|
|
30
|
+
/**
|
|
31
|
+
* Run a CLI command and capture output.
|
|
32
|
+
*/
|
|
33
|
+
function runCli(args, options = {}) {
|
|
34
|
+
try {
|
|
35
|
+
const stdout = execSync(`node "${CLI_PATH}" ${args}`, {
|
|
36
|
+
cwd: options.cwd,
|
|
37
|
+
encoding: 'utf-8',
|
|
38
|
+
env: { ...process.env, NO_COLOR: '1', ...options.env },
|
|
39
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
40
|
+
timeout: 30000,
|
|
41
|
+
});
|
|
42
|
+
return { stdout, stderr: '', code: 0 };
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
const e = error;
|
|
46
|
+
return { stdout: e.stdout || '', stderr: e.stderr || '', code: e.status || 1 };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Initialize a temp directory with a minimal beth installation
|
|
51
|
+
* (simulates a project that previously ran `init`).
|
|
52
|
+
*/
|
|
53
|
+
function setupInstalledProject(dir) {
|
|
54
|
+
// Create .github/agents with a sample agent
|
|
55
|
+
const agentsDir = join(dir, '.github', 'agents');
|
|
56
|
+
mkdirSync(agentsDir, { recursive: true });
|
|
57
|
+
writeFileSync(join(agentsDir, 'beth.agent.md'), `---
|
|
58
|
+
name: Beth
|
|
59
|
+
description: Orchestrator
|
|
60
|
+
model: Claude Opus 4.6
|
|
61
|
+
tools:
|
|
62
|
+
- runSubagent
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
# Beth
|
|
66
|
+
`);
|
|
67
|
+
// Create .github/skills with a sample skill
|
|
68
|
+
const skillDir = join(dir, '.github', 'skills', 'prd');
|
|
69
|
+
mkdirSync(skillDir, { recursive: true });
|
|
70
|
+
writeFileSync(join(skillDir, 'SKILL.md'), '# PRD Skill\n');
|
|
71
|
+
// Create AGENTS.md
|
|
72
|
+
writeFileSync(join(dir, 'AGENTS.md'), '# Agent Instructions\n');
|
|
73
|
+
// Create package.json (needed for version detection)
|
|
74
|
+
writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: 'test-project', version: '1.0.0' }, null, 2));
|
|
75
|
+
}
|
|
76
|
+
// ─────────────────────────────────────────────────────────
|
|
77
|
+
// Test suites
|
|
78
|
+
// ─────────────────────────────────────────────────────────
|
|
79
|
+
describe('update command E2E', () => {
|
|
80
|
+
let testDir;
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
testDir = join(tmpdir(), `beth-update-e2e-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
83
|
+
mkdirSync(testDir, { recursive: true });
|
|
84
|
+
});
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
if (existsSync(testDir)) {
|
|
87
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// ── 1. Command recognition ──────────────────────────────
|
|
91
|
+
describe('command recognition', () => {
|
|
92
|
+
it('should accept "update" as a valid command (not rejected as unknown)', () => {
|
|
93
|
+
// When update is implemented, this should NOT return the
|
|
94
|
+
// "Unknown command" error that unrecognized commands get.
|
|
95
|
+
const result = runCli('update --check-only', { cwd: testDir });
|
|
96
|
+
// The command should either succeed or fail for a reason
|
|
97
|
+
// OTHER than "unknown command" or "unknown flag". Without this
|
|
98
|
+
// dual check, the test passes vacuously when the CLI rejects
|
|
99
|
+
// --check-only as an unknown flag before it ever dispatches.
|
|
100
|
+
const combined = result.stdout + result.stderr;
|
|
101
|
+
const isRejected = /unknown (command|flag)/i.test(combined);
|
|
102
|
+
assert.strictEqual(isRejected, false, '"update --check-only" should be recognized — not rejected as unknown command or unknown flag');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
// ── 2. Help integration ─────────────────────────────────
|
|
106
|
+
describe('help integration', () => {
|
|
107
|
+
it('should list the update command in help output', () => {
|
|
108
|
+
const result = runCli('help');
|
|
109
|
+
assert.ok(result.stdout.includes('update'), 'Help output should mention the update command');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
// ── 3. --check-only flag ────────────────────────────────
|
|
113
|
+
describe('--check-only flag', () => {
|
|
114
|
+
it('should report version status without modifying any files', () => {
|
|
115
|
+
setupInstalledProject(testDir);
|
|
116
|
+
// Snapshot file state before
|
|
117
|
+
const agentBefore = readFileSync(join(testDir, '.github', 'agents', 'beth.agent.md'), 'utf-8');
|
|
118
|
+
const result = runCli('update --check-only', { cwd: testDir });
|
|
119
|
+
// File should be unchanged
|
|
120
|
+
const agentAfter = readFileSync(join(testDir, '.github', 'agents', 'beth.agent.md'), 'utf-8');
|
|
121
|
+
assert.strictEqual(agentBefore, agentAfter, '--check-only should not modify any files');
|
|
122
|
+
// Should output version information
|
|
123
|
+
assert.ok(result.stdout.includes('version') ||
|
|
124
|
+
result.stdout.includes('up to date') ||
|
|
125
|
+
result.stdout.includes('available'), '--check-only should report version status');
|
|
126
|
+
});
|
|
127
|
+
it('should exit with code 0 when check succeeds', () => {
|
|
128
|
+
setupInstalledProject(testDir);
|
|
129
|
+
const result = runCli('update --check-only', { cwd: testDir });
|
|
130
|
+
assert.strictEqual(result.code, 0, '--check-only should exit 0 on success');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
// ── 4. Already up to date ───────────────────────────────
|
|
134
|
+
describe('already up to date', () => {
|
|
135
|
+
it('should report when no update is available', () => {
|
|
136
|
+
setupInstalledProject(testDir);
|
|
137
|
+
// Run update (templates should match since they came from the same version)
|
|
138
|
+
const result = runCli('update', { cwd: testDir });
|
|
139
|
+
// Should indicate that things are current or that update completed
|
|
140
|
+
const indicatesStatus = result.stdout.includes('up to date') ||
|
|
141
|
+
result.stdout.includes('already') ||
|
|
142
|
+
result.stdout.includes('Updated') ||
|
|
143
|
+
result.stdout.includes('installed');
|
|
144
|
+
assert.ok(indicatesStatus, 'Should report update status');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
// ── 5. --verbose flag ──────────────────────────────────
|
|
148
|
+
describe('--verbose flag', () => {
|
|
149
|
+
it('should show additional detail with --verbose', () => {
|
|
150
|
+
setupInstalledProject(testDir);
|
|
151
|
+
const normal = runCli('update --check-only', { cwd: testDir });
|
|
152
|
+
const verbose = runCli('update --check-only --verbose', { cwd: testDir });
|
|
153
|
+
// Verbose output should be equal or longer than normal output
|
|
154
|
+
assert.ok(verbose.stdout.length >= normal.stdout.length, '--verbose should produce equal or more output than default');
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
// ── 6. --force flag ────────────────────────────────────
|
|
158
|
+
describe('--force flag', () => {
|
|
159
|
+
it('should overwrite existing files when --force is used', () => {
|
|
160
|
+
setupInstalledProject(testDir);
|
|
161
|
+
// Modify a file to simulate user customization
|
|
162
|
+
const agentPath = join(testDir, '.github', 'agents', 'beth.agent.md');
|
|
163
|
+
writeFileSync(agentPath, 'USER CUSTOMIZED CONTENT');
|
|
164
|
+
runCli('update --force', { cwd: testDir });
|
|
165
|
+
// With --force, the file should be overwritten with the template version
|
|
166
|
+
const afterContent = readFileSync(agentPath, 'utf-8');
|
|
167
|
+
assert.notStrictEqual(afterContent, 'USER CUSTOMIZED CONTENT', '--force should overwrite user-modified files with template versions');
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
// ── 7. Preserve user modifications ──────────────────────
|
|
171
|
+
describe('preserve user modifications', () => {
|
|
172
|
+
it('should not overwrite user-modified files without --force', () => {
|
|
173
|
+
setupInstalledProject(testDir);
|
|
174
|
+
// Modify a file to simulate user customization
|
|
175
|
+
const agentPath = join(testDir, '.github', 'agents', 'beth.agent.md');
|
|
176
|
+
writeFileSync(agentPath, 'USER CUSTOMIZED CONTENT');
|
|
177
|
+
const result = runCli('update', { cwd: testDir });
|
|
178
|
+
// Without --force, the user's customization should be preserved
|
|
179
|
+
const afterContent = readFileSync(agentPath, 'utf-8');
|
|
180
|
+
assert.strictEqual(afterContent, 'USER CUSTOMIZED CONTENT', 'Should preserve user-modified files without --force');
|
|
181
|
+
// Should warn the user that files were skipped
|
|
182
|
+
const mentionsSkipped = result.stdout.includes('skip') ||
|
|
183
|
+
result.stdout.includes('Skip') ||
|
|
184
|
+
result.stdout.includes('modified') ||
|
|
185
|
+
result.stdout.includes('preserved');
|
|
186
|
+
assert.ok(mentionsSkipped, 'Should mention that user-modified files were skipped');
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
// ── 8. Network error handling ───────────────────────────
|
|
190
|
+
describe('network error handling', () => {
|
|
191
|
+
it('should handle npm registry being unreachable gracefully', () => {
|
|
192
|
+
setupInstalledProject(testDir);
|
|
193
|
+
// Use a bogus registry URL to simulate network failure
|
|
194
|
+
const result = runCli('update --check-only', {
|
|
195
|
+
cwd: testDir,
|
|
196
|
+
env: { npm_config_registry: 'http://localhost:1' },
|
|
197
|
+
});
|
|
198
|
+
// Should not crash — either succeeds with cached data or
|
|
199
|
+
// reports the error cleanly
|
|
200
|
+
const crashed = result.stderr.includes('Unhandled') ||
|
|
201
|
+
result.stderr.includes('FATAL');
|
|
202
|
+
assert.strictEqual(crashed, false, 'Should handle network errors without crashing');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
// ── 9. Exit codes ──────────────────────────────────────
|
|
206
|
+
describe('exit codes', () => {
|
|
207
|
+
it('should exit with code 0 on successful update', () => {
|
|
208
|
+
setupInstalledProject(testDir);
|
|
209
|
+
const result = runCli('update', { cwd: testDir });
|
|
210
|
+
assert.strictEqual(result.code, 0, 'Successful update should exit with code 0');
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
// ── 10. New files added by update ───────────────────────
|
|
214
|
+
describe('new template files', () => {
|
|
215
|
+
it('should install new agent files that did not exist before', () => {
|
|
216
|
+
setupInstalledProject(testDir);
|
|
217
|
+
// Setup creates only beth.agent.md — update should add more
|
|
218
|
+
const agentsDir = join(testDir, '.github', 'agents');
|
|
219
|
+
const beforeCount = readdirSync(agentsDir).filter(f => f.endsWith('.agent.md')).length;
|
|
220
|
+
runCli('update', { cwd: testDir });
|
|
221
|
+
const afterCount = readdirSync(agentsDir).filter(f => f.endsWith('.agent.md')).length;
|
|
222
|
+
assert.ok(afterCount > beforeCount, `Update should install new agent files. Before: ${beforeCount}, after: ${afterCount}`);
|
|
223
|
+
});
|
|
224
|
+
it('should install new skill directories that did not exist before', () => {
|
|
225
|
+
setupInstalledProject(testDir);
|
|
226
|
+
// Setup creates only prd/ — update should add more
|
|
227
|
+
const skillsDir = join(testDir, '.github', 'skills');
|
|
228
|
+
const beforeCount = readdirSync(skillsDir, { withFileTypes: true })
|
|
229
|
+
.filter(d => d.isDirectory()).length;
|
|
230
|
+
runCli('update', { cwd: testDir });
|
|
231
|
+
assert.ok(existsSync(skillsDir), 'Skills directory should exist after update');
|
|
232
|
+
const afterCount = readdirSync(skillsDir, { withFileTypes: true })
|
|
233
|
+
.filter(d => d.isDirectory()).length;
|
|
234
|
+
assert.ok(afterCount > beforeCount, `Update should install new skill dirs. Before: ${beforeCount}, after: ${afterCount}`);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
//# sourceMappingURL=update.e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.e2e.test.js","sourceRoot":"","sources":["../../../src/cli/commands/update.e2e.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,UAAU,EACV,SAAS,EACT,aAAa,EACb,MAAM,EACN,YAAY,EACZ,WAAW,GACZ,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAE5B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEvF;;GAEG;AACH,SAAS,MAAM,CACb,IAAY,EACZ,UAA0D,EAAE;IAE5D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,KAAK,IAAI,EAAE,EAAE;YACpD,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,QAAQ,EAAE,OAAO;YACjB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;YACtD,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACzC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,KAA8D,CAAC;QACzE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;IACjF,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAAC,GAAW;IACxC,4CAA4C;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACjD,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,aAAa,CACX,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EAChC;;;;;;;;;CASH,CACE,CAAC;IAEF,4CAA4C;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IACvD,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,eAAe,CAAC,CAAC;IAE3D,mBAAmB;IACnB,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,wBAAwB,CAAC,CAAC;IAEhE,qDAAqD;IACrD,aAAa,CACX,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EACzB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CACpE,CAAC;AACJ,CAAC;AAED,4DAA4D;AAC5D,cAAc;AACd,4DAA4D;AAE5D,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,IAAI,OAAe,CAAC;IAEpB,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,IAAI,CACZ,MAAM,EAAE,EACR,mBAAmB,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CACvE,CAAC;QACF,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,2DAA2D;IAE3D,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;YAC7E,yDAAyD;YACzD,0DAA0D;YAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE/D,yDAAyD;YACzD,+DAA+D;YAC/D,6DAA6D;YAC7D,6DAA6D;YAC7D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YAC/C,MAAM,UAAU,GACd,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,CAAC,WAAW,CAChB,UAAU,EACV,KAAK,EACL,8FAA8F,CAC/F,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,2DAA2D;IAE3D,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACvD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9B,MAAM,CAAC,EAAE,CACP,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAChC,+CAA+C,CAChD,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,2DAA2D;IAE3D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE/B,6BAA6B;YAC7B,MAAM,WAAW,GAAG,YAAY,CAC9B,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,EACnD,OAAO,CACR,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE/D,2BAA2B;YAC3B,MAAM,UAAU,GAAG,YAAY,CAC7B,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,EACnD,OAAO,CACR,CAAC;YACF,MAAM,CAAC,WAAW,CAChB,WAAW,EACX,UAAU,EACV,0CAA0C,CAC3C,CAAC;YAEF,oCAAoC;YACpC,MAAM,CAAC,EAAE,CACP,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAC/B,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;gBACpC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EACrC,2CAA2C,CAC5C,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAC/D,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,uCAAuC,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,2DAA2D;IAE3D,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE/B,4EAA4E;YAC5E,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAElD,mEAAmE;YACnE,MAAM,eAAe,GACnB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;gBACpC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;gBACjC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;gBACjC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YACtC,MAAM,CAAC,EAAE,CAAC,eAAe,EAAE,6BAA6B,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,0DAA0D;IAE1D,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE/B,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,+BAA+B,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE1E,8DAA8D;YAC9D,MAAM,CAAC,EAAE,CACP,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAC7C,4DAA4D,CAC7D,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,0DAA0D;IAE1D,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE/B,+CAA+C;YAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;YACtE,aAAa,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAC;YAEpD,MAAM,CAAC,gBAAgB,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3C,yEAAyE;YACzE,MAAM,YAAY,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACtD,MAAM,CAAC,cAAc,CACnB,YAAY,EACZ,yBAAyB,EACzB,qEAAqE,CACtE,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,2DAA2D;IAE3D,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;QAC3C,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE/B,+CAA+C;YAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;YACtE,aAAa,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAC;YAEpD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAElD,gEAAgE;YAChE,MAAM,YAAY,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACtD,MAAM,CAAC,WAAW,CAChB,YAAY,EACZ,yBAAyB,EACzB,qDAAqD,CACtD,CAAC;YAEF,+CAA+C;YAC/C,MAAM,eAAe,GACnB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC9B,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC9B,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YACtC,MAAM,CAAC,EAAE,CACP,eAAe,EACf,sDAAsD,CACvD,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,2DAA2D;IAE3D,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACtC,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE/B,uDAAuD;YACvD,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,EAAE;gBAC3C,GAAG,EAAE,OAAO;gBACZ,GAAG,EAAE,EAAE,mBAAmB,EAAE,oBAAoB,EAAE;aACnD,CAAC,CAAC;YAEH,yDAAyD;YACzD,4BAA4B;YAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;gBACjD,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAClC,MAAM,CAAC,WAAW,CAChB,OAAO,EACP,KAAK,EACL,+CAA+C,CAChD,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,0DAA0D;IAE1D,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAClD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,2CAA2C,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,2DAA2D;IAE3D,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE/B,4DAA4D;YAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;YACrD,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;YAEvF,MAAM,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAEnC,MAAM,UAAU,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;YAEtF,MAAM,CAAC,EAAE,CACP,UAAU,GAAG,WAAW,EACxB,kDAAkD,WAAW,YAAY,UAAU,EAAE,CACtF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;YACxE,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE/B,mDAAmD;YACnD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;YACrD,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;iBAChE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YAEvC,MAAM,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAEnC,MAAM,CAAC,EAAE,CACP,UAAU,CAAC,SAAS,CAAC,EACrB,4CAA4C,CAC7C,CAAC;YAEF,MAAM,UAAU,GAAG,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;iBAC/D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YAEvC,MAAM,CAAC,EAAE,CACP,UAAU,GAAG,WAAW,EACxB,iDAAiD,WAAW,YAAY,UAAU,EAAE,CACrF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|