@skillsmith/cli 0.5.12 → 0.6.1
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 +16 -0
- package/README.md +92 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/commands/audit-collisions.d.ts +71 -0
- package/dist/src/commands/audit-collisions.d.ts.map +1 -0
- package/dist/src/commands/audit-collisions.js +300 -0
- package/dist/src/commands/audit-collisions.js.map +1 -0
- package/dist/src/commands/audit.d.ts +27 -2
- package/dist/src/commands/audit.d.ts.map +1 -1
- package/dist/src/commands/audit.js +73 -13
- package/dist/src/commands/audit.js.map +1 -1
- package/dist/src/commands/audit.test.js +2 -2
- package/dist/src/commands/audit.test.js.map +1 -1
- package/dist/src/commands/config.d.ts +55 -0
- package/dist/src/commands/config.d.ts.map +1 -0
- package/dist/src/commands/config.js +211 -0
- package/dist/src/commands/config.js.map +1 -0
- package/dist/src/commands/create.d.ts +3 -3
- package/dist/src/commands/create.d.ts.map +1 -1
- package/dist/src/commands/create.js +11 -9
- package/dist/src/commands/create.js.map +1 -1
- package/dist/src/commands/diff.js +2 -2
- package/dist/src/commands/diff.js.map +1 -1
- package/dist/src/commands/import-local.d.ts +21 -0
- package/dist/src/commands/import-local.d.ts.map +1 -0
- package/dist/src/commands/import-local.helpers.d.ts +28 -0
- package/dist/src/commands/import-local.helpers.d.ts.map +1 -0
- package/dist/src/commands/import-local.helpers.js +166 -0
- package/dist/src/commands/import-local.helpers.js.map +1 -0
- package/dist/src/commands/import-local.js +264 -0
- package/dist/src/commands/import-local.js.map +1 -0
- package/dist/src/commands/import-local.types.d.ts +59 -0
- package/dist/src/commands/import-local.types.d.ts.map +1 -0
- package/dist/src/commands/import-local.types.js +2 -0
- package/dist/src/commands/import-local.types.js.map +1 -0
- package/dist/src/{import.d.ts → commands/import.d.ts} +11 -0
- package/dist/src/commands/import.d.ts.map +1 -0
- package/dist/src/{import.js → commands/import.js} +39 -4
- package/dist/src/commands/import.js.map +1 -0
- package/dist/src/commands/index.d.ts +3 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +6 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/info.d.ts.map +1 -1
- package/dist/src/commands/info.js +3 -3
- package/dist/src/commands/info.js.map +1 -1
- package/dist/src/commands/install-skill.d.ts.map +1 -1
- package/dist/src/commands/install-skill.js +6 -3
- package/dist/src/commands/install-skill.js.map +1 -1
- package/dist/src/commands/install.d.ts.map +1 -1
- package/dist/src/commands/install.js +81 -7
- package/dist/src/commands/install.js.map +1 -1
- package/dist/src/commands/login.d.ts.map +1 -1
- package/dist/src/commands/login.js +37 -0
- package/dist/src/commands/login.js.map +1 -1
- package/dist/src/commands/login.post-login-sync.test.d.ts +2 -0
- package/dist/src/commands/login.post-login-sync.test.d.ts.map +1 -0
- package/dist/src/commands/login.post-login-sync.test.js +120 -0
- package/dist/src/commands/login.post-login-sync.test.js.map +1 -0
- package/dist/src/commands/login.test-helpers.d.ts +7 -0
- package/dist/src/commands/login.test-helpers.d.ts.map +1 -0
- package/dist/src/commands/login.test-helpers.js +44 -0
- package/dist/src/commands/login.test-helpers.js.map +1 -0
- package/dist/src/commands/login.test.js +30 -31
- package/dist/src/commands/login.test.js.map +1 -1
- package/dist/src/commands/manage.d.ts.map +1 -1
- package/dist/src/commands/manage.js +17 -4
- package/dist/src/commands/manage.js.map +1 -1
- package/dist/src/commands/recommend.helpers.d.ts +5 -1
- package/dist/src/commands/recommend.helpers.d.ts.map +1 -1
- package/dist/src/commands/recommend.helpers.js +9 -3
- package/dist/src/commands/recommend.helpers.js.map +1 -1
- package/dist/src/commands/run-registry-sync.d.ts +31 -0
- package/dist/src/commands/run-registry-sync.d.ts.map +1 -0
- package/dist/src/commands/run-registry-sync.js +45 -0
- package/dist/src/commands/run-registry-sync.js.map +1 -0
- package/dist/src/commands/search.d.ts.map +1 -1
- package/dist/src/commands/search.helpers.d.ts +23 -0
- package/dist/src/commands/search.helpers.d.ts.map +1 -0
- package/dist/src/commands/search.helpers.js +57 -0
- package/dist/src/commands/search.helpers.js.map +1 -0
- package/dist/src/commands/search.js +12 -5
- package/dist/src/commands/search.js.map +1 -1
- package/dist/src/commands/sync.d.ts.map +1 -1
- package/dist/src/commands/sync.js +10 -21
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/config.d.ts +6 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +8 -2
- package/dist/src/config.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +10 -28
- package/dist/src/index.js.map +1 -1
- package/dist/src/templates/mcp-server.template.d.ts +1 -1
- package/dist/src/templates/mcp-server.template.d.ts.map +1 -1
- package/dist/src/templates/mcp-server.template.js +13 -13
- package/dist/src/templates/mcp-server.template.js.map +1 -1
- package/dist/src/templates/mcp-server.template.snippets.d.ts +51 -0
- package/dist/src/templates/mcp-server.template.snippets.d.ts.map +1 -0
- package/dist/src/templates/mcp-server.template.snippets.js +162 -0
- package/dist/src/templates/mcp-server.template.snippets.js.map +1 -0
- package/dist/src/templates/readme.md.template.d.ts +1 -1
- package/dist/src/templates/readme.md.template.d.ts.map +1 -1
- package/dist/src/templates/readme.md.template.js +2 -2
- package/dist/src/utils/open-database.d.ts +27 -0
- package/dist/src/utils/open-database.d.ts.map +1 -0
- package/dist/src/utils/open-database.js +31 -0
- package/dist/src/utils/open-database.js.map +1 -0
- package/dist/src/utils/skills-directory.d.ts +26 -7
- package/dist/src/utils/skills-directory.d.ts.map +1 -1
- package/dist/src/utils/skills-directory.js +78 -33
- package/dist/src/utils/skills-directory.js.map +1 -1
- package/dist/tests/audit-collisions.test.d.ts +42 -0
- package/dist/tests/audit-collisions.test.d.ts.map +1 -0
- package/dist/tests/audit-collisions.test.js +370 -0
- package/dist/tests/audit-collisions.test.js.map +1 -0
- package/dist/tests/audit-restructure.test.d.ts +15 -0
- package/dist/tests/audit-restructure.test.d.ts.map +1 -0
- package/dist/tests/audit-restructure.test.js +151 -0
- package/dist/tests/audit-restructure.test.js.map +1 -0
- package/dist/tests/config.test.d.ts +33 -0
- package/dist/tests/config.test.d.ts.map +1 -0
- package/dist/tests/config.test.js +240 -0
- package/dist/tests/config.test.js.map +1 -0
- package/dist/tests/install-multi-client.test.d.ts +2 -0
- package/dist/tests/install-multi-client.test.d.ts.map +1 -0
- package/dist/tests/install-multi-client.test.js +202 -0
- package/dist/tests/install-multi-client.test.js.map +1 -0
- package/dist/tests/integration/import-local.test.d.ts +2 -0
- package/dist/tests/integration/import-local.test.d.ts.map +1 -0
- package/dist/tests/integration/import-local.test.js +158 -0
- package/dist/tests/integration/import-local.test.js.map +1 -0
- package/dist/tests/mcp-server-template.test.js +21 -0
- package/dist/tests/mcp-server-template.test.js.map +1 -1
- package/dist/tests/open-database.test.d.ts +2 -0
- package/dist/tests/open-database.test.d.ts.map +1 -0
- package/dist/tests/open-database.test.js +52 -0
- package/dist/tests/open-database.test.js.map +1 -0
- package/dist/tests/search-helpers.test.d.ts +2 -0
- package/dist/tests/search-helpers.test.d.ts.map +1 -0
- package/dist/tests/search-helpers.test.js +110 -0
- package/dist/tests/search-helpers.test.js.map +1 -0
- package/dist/tests/skills-directory.test.d.ts +2 -0
- package/dist/tests/skills-directory.test.d.ts.map +1 -0
- package/dist/tests/skills-directory.test.js +97 -0
- package/dist/tests/skills-directory.test.js.map +1 -0
- package/dist/tests/unit/commands/install.test.js +75 -1
- package/dist/tests/unit/commands/install.test.js.map +1 -1
- package/dist/tests/unit/import-local.helpers.test.d.ts +2 -0
- package/dist/tests/unit/import-local.helpers.test.d.ts.map +1 -0
- package/dist/tests/unit/import-local.helpers.test.js +96 -0
- package/dist/tests/unit/import-local.helpers.test.js.map +1 -0
- package/package.json +10 -4
- package/dist/src/import.d.ts.map +0 -1
- package/dist/src/import.js.map +0 -1
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview SMI-4590 Wave 4 PR 5/6 — `sklx audit collisions` tests
|
|
3
|
+
* @module @skillsmith/cli/tests/audit-collisions
|
|
4
|
+
*
|
|
5
|
+
* Coverage:
|
|
6
|
+
*
|
|
7
|
+
* Typed-confirmation gate (`requireConfirmationPhrase`) — load-bearing per
|
|
8
|
+
* plan §235-239. Strict literal equality, NO normalization.
|
|
9
|
+
* 1. Exact phrase ('APPLY ALL') — RESOLVES.
|
|
10
|
+
* 2. Single-letter ('Y') — REJECTS.
|
|
11
|
+
* 3. Lowercase word ('yes') — REJECTS.
|
|
12
|
+
* 4. Lowercase phrase ('apply all') — REJECTS.
|
|
13
|
+
* 5. No-space variant ('APPLYALL') — REJECTS.
|
|
14
|
+
* 6. Trailing whitespace ('APPLY ALL ') — REJECTS (no trim).
|
|
15
|
+
* 7. Empty string — REJECTS.
|
|
16
|
+
*
|
|
17
|
+
* Both interactive (TTY) and piped-stdin paths route through the same
|
|
18
|
+
* `@inquirer/prompts` `input()` call, so the rejection invariant holds for
|
|
19
|
+
* both. We mock `input` to assert the contract — the prompt library
|
|
20
|
+
* receives the answer verbatim and our code applies strict equality.
|
|
21
|
+
*
|
|
22
|
+
* Interactive prompt loop (`runInteractiveLoop`):
|
|
23
|
+
* 8. 'apply' choice → applyOneSuggestion called, status=applied.
|
|
24
|
+
* 9. 'skip' choice → outcome recorded as skipped, no apply call.
|
|
25
|
+
* 10. 'edit' choice → input prompts for custom name, applyRename
|
|
26
|
+
* receives `customName`.
|
|
27
|
+
* 11. 'quit' choice → loop terminates early; remaining suggestions
|
|
28
|
+
* are NOT processed.
|
|
29
|
+
* 12. Empty suggestion list → prints "No collisions found." and returns.
|
|
30
|
+
*
|
|
31
|
+
* Reset-ledger flow (`runResetLedger`):
|
|
32
|
+
* 13. Backs up an existing ledger to `~/.skillsmith/backups/ledger-*.json`
|
|
33
|
+
* BEFORE clearing.
|
|
34
|
+
* 14. No backup file when no prior ledger exists.
|
|
35
|
+
* 15. Confirmation phrase mismatch → throws, NO writeLedger call.
|
|
36
|
+
*
|
|
37
|
+
* `--json` output (`runAuditCollisions`):
|
|
38
|
+
* 16. JSON mode prints `RunInventoryAuditResult` to stdout, no prompts,
|
|
39
|
+
* no apply.
|
|
40
|
+
*/
|
|
41
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
42
|
+
import * as fs from 'node:fs';
|
|
43
|
+
import * as os from 'node:os';
|
|
44
|
+
import { join } from 'node:path';
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Mock setup — must precede source imports.
|
|
47
|
+
// ============================================================================
|
|
48
|
+
const mocks = vi.hoisted(() => ({
|
|
49
|
+
input: vi.fn(),
|
|
50
|
+
select: vi.fn(),
|
|
51
|
+
applyRename: vi.fn(),
|
|
52
|
+
readLedger: vi.fn(),
|
|
53
|
+
writeLedger: vi.fn(),
|
|
54
|
+
runInventoryAudit: vi.fn(),
|
|
55
|
+
getLicenseStatus: vi.fn(),
|
|
56
|
+
}));
|
|
57
|
+
vi.mock('@inquirer/prompts', () => ({
|
|
58
|
+
input: (opts) => mocks.input(opts),
|
|
59
|
+
select: (opts) => mocks.select(opts),
|
|
60
|
+
}));
|
|
61
|
+
vi.mock('@skillsmith/mcp-server/audit', () => ({
|
|
62
|
+
applyRename: (opts) => mocks.applyRename(opts),
|
|
63
|
+
readLedger: () => mocks.readLedger(),
|
|
64
|
+
writeLedger: (data) => mocks.writeLedger(data),
|
|
65
|
+
runInventoryAudit: (opts) => mocks.runInventoryAudit(opts),
|
|
66
|
+
NAMESPACE_OVERRIDES_CURRENT_VERSION: 1,
|
|
67
|
+
}));
|
|
68
|
+
vi.mock('../src/utils/license.js', () => ({
|
|
69
|
+
getLicenseStatus: () => mocks.getLicenseStatus(),
|
|
70
|
+
}));
|
|
71
|
+
import { requireConfirmationPhrase, runResetLedger, runInteractiveLoop, runAuditCollisions, APPLY_ALL_PHRASE, RESET_LEDGER_PHRASE, CONFIRMATION_REJECTED_MESSAGE, } from '../src/commands/audit-collisions.js';
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Helpers
|
|
74
|
+
// ============================================================================
|
|
75
|
+
function makeSuggestion(id, currentName, suggested) {
|
|
76
|
+
return {
|
|
77
|
+
collisionId: id,
|
|
78
|
+
currentName,
|
|
79
|
+
suggested,
|
|
80
|
+
reason: `mock-reason-${id}`,
|
|
81
|
+
targetMtime: 0,
|
|
82
|
+
siblingMtime: 0,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function makeAudit(overrides = {}) {
|
|
86
|
+
return {
|
|
87
|
+
auditId: '01HMOCKAUDITID000000000000',
|
|
88
|
+
reportPath: '/tmp/audit-report.md',
|
|
89
|
+
renameSuggestions: [],
|
|
90
|
+
...overrides,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Tests
|
|
95
|
+
// ============================================================================
|
|
96
|
+
describe('SMI-4590 Wave 4 PR 5/6 — sklx audit collisions', () => {
|
|
97
|
+
let stdoutSpy;
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
vi.clearAllMocks();
|
|
100
|
+
mocks.applyRename.mockResolvedValue({ success: true, summary: 'renamed' });
|
|
101
|
+
mocks.readLedger.mockResolvedValue({ version: 1, overrides: [] });
|
|
102
|
+
mocks.writeLedger.mockResolvedValue(undefined);
|
|
103
|
+
mocks.getLicenseStatus.mockResolvedValue({ tier: 'team' });
|
|
104
|
+
stdoutSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
105
|
+
});
|
|
106
|
+
afterEach(() => {
|
|
107
|
+
stdoutSpy.mockRestore();
|
|
108
|
+
});
|
|
109
|
+
describe('typed-confirmation gate (plan §235-239)', () => {
|
|
110
|
+
it('resolves on exact APPLY ALL match', async () => {
|
|
111
|
+
mocks.input.mockResolvedValueOnce(APPLY_ALL_PHRASE);
|
|
112
|
+
await expect(requireConfirmationPhrase(APPLY_ALL_PHRASE, 'msg')).resolves.toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
it('rejects single-letter Y', async () => {
|
|
115
|
+
mocks.input.mockResolvedValueOnce('Y');
|
|
116
|
+
await expect(requireConfirmationPhrase(APPLY_ALL_PHRASE, 'msg')).rejects.toThrow(CONFIRMATION_REJECTED_MESSAGE);
|
|
117
|
+
});
|
|
118
|
+
it('rejects lowercase yes', async () => {
|
|
119
|
+
mocks.input.mockResolvedValueOnce('yes');
|
|
120
|
+
await expect(requireConfirmationPhrase(APPLY_ALL_PHRASE, 'msg')).rejects.toThrow(CONFIRMATION_REJECTED_MESSAGE);
|
|
121
|
+
});
|
|
122
|
+
it('rejects lowercase apply all (case-sensitive)', async () => {
|
|
123
|
+
mocks.input.mockResolvedValueOnce('apply all');
|
|
124
|
+
await expect(requireConfirmationPhrase(APPLY_ALL_PHRASE, 'msg')).rejects.toThrow(CONFIRMATION_REJECTED_MESSAGE);
|
|
125
|
+
});
|
|
126
|
+
it('rejects no-space APPLYALL', async () => {
|
|
127
|
+
mocks.input.mockResolvedValueOnce('APPLYALL');
|
|
128
|
+
await expect(requireConfirmationPhrase(APPLY_ALL_PHRASE, 'msg')).rejects.toThrow(CONFIRMATION_REJECTED_MESSAGE);
|
|
129
|
+
});
|
|
130
|
+
it('rejects trailing whitespace (no trim)', async () => {
|
|
131
|
+
mocks.input.mockResolvedValueOnce('APPLY ALL ');
|
|
132
|
+
await expect(requireConfirmationPhrase(APPLY_ALL_PHRASE, 'msg')).rejects.toThrow(CONFIRMATION_REJECTED_MESSAGE);
|
|
133
|
+
});
|
|
134
|
+
it('rejects empty string', async () => {
|
|
135
|
+
mocks.input.mockResolvedValueOnce('');
|
|
136
|
+
await expect(requireConfirmationPhrase(APPLY_ALL_PHRASE, 'msg')).rejects.toThrow(CONFIRMATION_REJECTED_MESSAGE);
|
|
137
|
+
});
|
|
138
|
+
it('resolves on exact RESET LEDGER match', async () => {
|
|
139
|
+
mocks.input.mockResolvedValueOnce(RESET_LEDGER_PHRASE);
|
|
140
|
+
await expect(requireConfirmationPhrase(RESET_LEDGER_PHRASE, 'msg')).resolves.toBeUndefined();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('interactive prompt loop choices', () => {
|
|
144
|
+
it("'apply' choice triggers applyRename", async () => {
|
|
145
|
+
const audit = makeAudit({
|
|
146
|
+
renameSuggestions: [makeSuggestion('c1', 'foo/bar', 'foo/bar-2')],
|
|
147
|
+
});
|
|
148
|
+
mocks.select.mockResolvedValueOnce('apply');
|
|
149
|
+
await runInteractiveLoop(audit);
|
|
150
|
+
expect(mocks.applyRename).toHaveBeenCalledTimes(1);
|
|
151
|
+
expect(mocks.applyRename.mock.calls[0]?.[0]).toMatchObject({
|
|
152
|
+
request: { action: 'apply', auditId: audit.auditId },
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
it("'skip' choice records skipped outcome and does NOT call applyRename", async () => {
|
|
156
|
+
const audit = makeAudit({
|
|
157
|
+
renameSuggestions: [makeSuggestion('c1', 'foo/bar', 'foo/bar-2')],
|
|
158
|
+
});
|
|
159
|
+
mocks.select.mockResolvedValueOnce('skip');
|
|
160
|
+
await runInteractiveLoop(audit);
|
|
161
|
+
expect(mocks.applyRename).not.toHaveBeenCalled();
|
|
162
|
+
});
|
|
163
|
+
it("'edit' choice forwards customName to applyRename", async () => {
|
|
164
|
+
const audit = makeAudit({
|
|
165
|
+
renameSuggestions: [makeSuggestion('c1', 'foo/bar', 'foo/bar-2')],
|
|
166
|
+
});
|
|
167
|
+
mocks.select.mockResolvedValueOnce('edit');
|
|
168
|
+
mocks.input.mockResolvedValueOnce(' custom-renamed ');
|
|
169
|
+
await runInteractiveLoop(audit);
|
|
170
|
+
expect(mocks.applyRename).toHaveBeenCalledWith({
|
|
171
|
+
suggestion: expect.objectContaining({ collisionId: 'c1' }),
|
|
172
|
+
request: {
|
|
173
|
+
action: 'apply',
|
|
174
|
+
auditId: audit.auditId,
|
|
175
|
+
customName: 'custom-renamed',
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
it("'quit' choice terminates the loop early", async () => {
|
|
180
|
+
const audit = makeAudit({
|
|
181
|
+
renameSuggestions: [
|
|
182
|
+
makeSuggestion('c1', 'foo/bar', 'foo/bar-2'),
|
|
183
|
+
makeSuggestion('c2', 'foo/baz', 'foo/baz-2'),
|
|
184
|
+
],
|
|
185
|
+
});
|
|
186
|
+
mocks.select.mockResolvedValueOnce('quit');
|
|
187
|
+
await runInteractiveLoop(audit);
|
|
188
|
+
// Loop aborted before processing any suggestion → applyRename never called.
|
|
189
|
+
expect(mocks.applyRename).not.toHaveBeenCalled();
|
|
190
|
+
// And select was only consulted once (for the first suggestion before quit).
|
|
191
|
+
expect(mocks.select).toHaveBeenCalledTimes(1);
|
|
192
|
+
});
|
|
193
|
+
it('empty suggestions prints No collisions found and returns', async () => {
|
|
194
|
+
const audit = makeAudit({ renameSuggestions: [] });
|
|
195
|
+
await runInteractiveLoop(audit);
|
|
196
|
+
expect(mocks.select).not.toHaveBeenCalled();
|
|
197
|
+
expect(mocks.applyRename).not.toHaveBeenCalled();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
describe('--reset-ledger flow', () => {
|
|
201
|
+
let tmpHome;
|
|
202
|
+
let originalHome;
|
|
203
|
+
beforeEach(() => {
|
|
204
|
+
tmpHome = fs.mkdtempSync(join(os.tmpdir(), 'sklx-test-home-'));
|
|
205
|
+
originalHome = process.env['HOME'];
|
|
206
|
+
process.env['HOME'] = tmpHome;
|
|
207
|
+
});
|
|
208
|
+
afterEach(() => {
|
|
209
|
+
if (originalHome !== undefined) {
|
|
210
|
+
process.env['HOME'] = originalHome;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
delete process.env['HOME'];
|
|
214
|
+
}
|
|
215
|
+
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
216
|
+
});
|
|
217
|
+
it('backs up an existing ledger before clearing', async () => {
|
|
218
|
+
const ledgerDir = join(tmpHome, '.skillsmith');
|
|
219
|
+
fs.mkdirSync(ledgerDir, { recursive: true });
|
|
220
|
+
const ledgerFile = join(ledgerDir, 'namespace-overrides.json');
|
|
221
|
+
fs.writeFileSync(ledgerFile, '{"version":1,"overrides":[{"id":"x"}]}', 'utf-8');
|
|
222
|
+
mocks.input.mockResolvedValueOnce(RESET_LEDGER_PHRASE);
|
|
223
|
+
await runResetLedger();
|
|
224
|
+
// Backup file written.
|
|
225
|
+
const backupsDir = join(ledgerDir, 'backups');
|
|
226
|
+
expect(fs.existsSync(backupsDir)).toBe(true);
|
|
227
|
+
const entries = fs.readdirSync(backupsDir).filter((f) => f.startsWith('ledger-'));
|
|
228
|
+
expect(entries.length).toBe(1);
|
|
229
|
+
// Backup contains the original payload.
|
|
230
|
+
const backup = fs.readFileSync(join(backupsDir, entries[0]), 'utf-8');
|
|
231
|
+
expect(backup).toContain('"id":"x"');
|
|
232
|
+
// writeLedger called with empty overrides.
|
|
233
|
+
expect(mocks.writeLedger).toHaveBeenCalledWith({
|
|
234
|
+
version: 1,
|
|
235
|
+
overrides: [],
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
it('no backup file when no prior ledger exists', async () => {
|
|
239
|
+
mocks.input.mockResolvedValueOnce(RESET_LEDGER_PHRASE);
|
|
240
|
+
await runResetLedger();
|
|
241
|
+
const backupsDir = join(tmpHome, '.skillsmith', 'backups');
|
|
242
|
+
// backupsDir may not exist at all when no source ledger present.
|
|
243
|
+
const entries = fs.existsSync(backupsDir) ? fs.readdirSync(backupsDir) : [];
|
|
244
|
+
expect(entries.length).toBe(0);
|
|
245
|
+
expect(mocks.writeLedger).toHaveBeenCalledWith({
|
|
246
|
+
version: 1,
|
|
247
|
+
overrides: [],
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
it('confirmation mismatch throws and does NOT call writeLedger', async () => {
|
|
251
|
+
mocks.input.mockResolvedValueOnce('reset ledger'); // wrong case
|
|
252
|
+
await expect(runResetLedger()).rejects.toThrow(CONFIRMATION_REJECTED_MESSAGE);
|
|
253
|
+
expect(mocks.writeLedger).not.toHaveBeenCalled();
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
describe('--reset-ledger preflight (plan §297)', () => {
|
|
257
|
+
let tmpHome;
|
|
258
|
+
let originalHome;
|
|
259
|
+
beforeEach(() => {
|
|
260
|
+
tmpHome = fs.mkdtempSync(join(os.tmpdir(), 'sklx-test-home-'));
|
|
261
|
+
originalHome = process.env['HOME'];
|
|
262
|
+
process.env['HOME'] = tmpHome;
|
|
263
|
+
});
|
|
264
|
+
afterEach(() => {
|
|
265
|
+
if (originalHome !== undefined) {
|
|
266
|
+
process.env['HOME'] = originalHome;
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
delete process.env['HOME'];
|
|
270
|
+
}
|
|
271
|
+
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
272
|
+
});
|
|
273
|
+
it('reads the ledger BEFORE prompting (so version_unsupported errors surface first)', async () => {
|
|
274
|
+
// Simulate a higher-version ledger by having readLedger throw.
|
|
275
|
+
const versionError = new Error('namespace.ledger.version_unsupported');
|
|
276
|
+
mocks.readLedger.mockRejectedValueOnce(versionError);
|
|
277
|
+
await expect(runAuditCollisions({
|
|
278
|
+
deep: false,
|
|
279
|
+
json: false,
|
|
280
|
+
applyAll: false,
|
|
281
|
+
reportOnly: false,
|
|
282
|
+
resetLedger: true,
|
|
283
|
+
})).rejects.toThrow('namespace.ledger.version_unsupported');
|
|
284
|
+
// The user was never prompted because readLedger threw first.
|
|
285
|
+
expect(mocks.input).not.toHaveBeenCalled();
|
|
286
|
+
expect(mocks.writeLedger).not.toHaveBeenCalled();
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
describe('--apply-all end-to-end (typed gate + sequential apply)', () => {
|
|
290
|
+
it('applies every suggestion sequentially after APPLY ALL phrase', async () => {
|
|
291
|
+
const audit = makeAudit({
|
|
292
|
+
renameSuggestions: [
|
|
293
|
+
makeSuggestion('c1', 'a/x', 'a/x-2'),
|
|
294
|
+
makeSuggestion('c2', 'a/y', 'a/y-2'),
|
|
295
|
+
makeSuggestion('c3', 'a/z', 'a/z-2'),
|
|
296
|
+
],
|
|
297
|
+
});
|
|
298
|
+
mocks.runInventoryAudit.mockResolvedValueOnce(audit);
|
|
299
|
+
mocks.input.mockResolvedValueOnce(APPLY_ALL_PHRASE);
|
|
300
|
+
await runAuditCollisions({
|
|
301
|
+
deep: false,
|
|
302
|
+
json: false,
|
|
303
|
+
applyAll: true,
|
|
304
|
+
reportOnly: false,
|
|
305
|
+
resetLedger: false,
|
|
306
|
+
});
|
|
307
|
+
// applyRename invoked once per suggestion.
|
|
308
|
+
expect(mocks.applyRename).toHaveBeenCalledTimes(3);
|
|
309
|
+
expect(mocks.select).not.toHaveBeenCalled();
|
|
310
|
+
});
|
|
311
|
+
it('rejects --apply-all when phrase is wrong; no applyRename calls', async () => {
|
|
312
|
+
const audit = makeAudit({
|
|
313
|
+
renameSuggestions: [makeSuggestion('c1', 'a/x', 'a/x-2')],
|
|
314
|
+
});
|
|
315
|
+
mocks.runInventoryAudit.mockResolvedValueOnce(audit);
|
|
316
|
+
mocks.input.mockResolvedValueOnce('apply all'); // wrong case
|
|
317
|
+
await expect(runAuditCollisions({
|
|
318
|
+
deep: false,
|
|
319
|
+
json: false,
|
|
320
|
+
applyAll: true,
|
|
321
|
+
reportOnly: false,
|
|
322
|
+
resetLedger: false,
|
|
323
|
+
})).rejects.toThrow(CONFIRMATION_REJECTED_MESSAGE);
|
|
324
|
+
expect(mocks.applyRename).not.toHaveBeenCalled();
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
describe('interactive default is `skip` (regression guard for inattentive Enter-press)', () => {
|
|
328
|
+
it('defaults the select prompt to `skip` not `apply`', async () => {
|
|
329
|
+
const audit = makeAudit({
|
|
330
|
+
renameSuggestions: [makeSuggestion('c1', 'a/x', 'a/x-2')],
|
|
331
|
+
});
|
|
332
|
+
// Capture the options passed to select.
|
|
333
|
+
let capturedOpts;
|
|
334
|
+
mocks.select.mockImplementationOnce((opts) => {
|
|
335
|
+
capturedOpts = opts;
|
|
336
|
+
return Promise.resolve('skip');
|
|
337
|
+
});
|
|
338
|
+
await runInteractiveLoop(audit);
|
|
339
|
+
expect(capturedOpts).toBeDefined();
|
|
340
|
+
expect(capturedOpts?.default).toBe('skip');
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
describe('--json output', () => {
|
|
344
|
+
it('prints RunInventoryAuditResult JSON and skips prompts', async () => {
|
|
345
|
+
const audit = makeAudit({
|
|
346
|
+
renameSuggestions: [makeSuggestion('c1', 'foo/bar', 'foo/bar-2')],
|
|
347
|
+
});
|
|
348
|
+
mocks.runInventoryAudit.mockResolvedValueOnce(audit);
|
|
349
|
+
await runAuditCollisions({
|
|
350
|
+
deep: false,
|
|
351
|
+
json: true,
|
|
352
|
+
applyAll: false,
|
|
353
|
+
reportOnly: false,
|
|
354
|
+
resetLedger: false,
|
|
355
|
+
});
|
|
356
|
+
// No prompts consulted.
|
|
357
|
+
expect(mocks.input).not.toHaveBeenCalled();
|
|
358
|
+
expect(mocks.select).not.toHaveBeenCalled();
|
|
359
|
+
expect(mocks.applyRename).not.toHaveBeenCalled();
|
|
360
|
+
// Last console.log call should contain serialized JSON of audit.
|
|
361
|
+
const calls = stdoutSpy.mock.calls.map((c) => String(c[0] ?? ''));
|
|
362
|
+
const jsonOutput = calls.find((c) => c.includes(audit.auditId));
|
|
363
|
+
expect(jsonOutput).toBeDefined();
|
|
364
|
+
// Parse and check shape.
|
|
365
|
+
const parsed = JSON.parse(jsonOutput);
|
|
366
|
+
expect(parsed.auditId).toBe(audit.auditId);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
//# sourceMappingURL=audit-collisions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-collisions.test.js","sourceRoot":"","sources":["../../tests/audit-collisions.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview SMI-4590 Step 0a — `sklx audit` restructure tests
|
|
3
|
+
* @module @skillsmith/cli/tests/audit-restructure
|
|
4
|
+
*
|
|
5
|
+
* Asserts the parent `audit` command surface:
|
|
6
|
+
* - `sklx audit advisories` is registered as a subcommand.
|
|
7
|
+
* - `sklx audit <skill-id>` (positional fallback) emits the deprecation
|
|
8
|
+
* warning and forwards to the same advisories handler.
|
|
9
|
+
* - `sklx audit` with no args prints parent help.
|
|
10
|
+
*
|
|
11
|
+
* Behavioral coverage of `runAdvisoriesAudit` itself stays under
|
|
12
|
+
* `audit.ts`'s existing test surface — these tests verify the wiring only.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=audit-restructure.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-restructure.test.d.ts","sourceRoot":"","sources":["../../tests/audit-restructure.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview SMI-4590 Step 0a — `sklx audit` restructure tests
|
|
3
|
+
* @module @skillsmith/cli/tests/audit-restructure
|
|
4
|
+
*
|
|
5
|
+
* Asserts the parent `audit` command surface:
|
|
6
|
+
* - `sklx audit advisories` is registered as a subcommand.
|
|
7
|
+
* - `sklx audit <skill-id>` (positional fallback) emits the deprecation
|
|
8
|
+
* warning and forwards to the same advisories handler.
|
|
9
|
+
* - `sklx audit` with no args prints parent help.
|
|
10
|
+
*
|
|
11
|
+
* Behavioral coverage of `runAdvisoriesAudit` itself stays under
|
|
12
|
+
* `audit.ts`'s existing test surface — these tests verify the wiring only.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Mock setup — must precede source imports so commander.action() bodies see
|
|
18
|
+
// the mocked deps when they execute.
|
|
19
|
+
// ============================================================================
|
|
20
|
+
const mocks = vi.hoisted(() => ({
|
|
21
|
+
requireTier: vi.fn(async (_tier) => undefined),
|
|
22
|
+
createDatabaseAsync: vi.fn(async (_path) => ({
|
|
23
|
+
close: () => undefined,
|
|
24
|
+
})),
|
|
25
|
+
initializeSchema: vi.fn((_db) => undefined),
|
|
26
|
+
AdvisoryRepositoryCtor: vi.fn((_db) => undefined),
|
|
27
|
+
getActiveAdvisories: vi.fn(() => []),
|
|
28
|
+
}));
|
|
29
|
+
vi.mock('@skillsmith/core', () => ({
|
|
30
|
+
createDatabaseAsync: (path) => mocks.createDatabaseAsync(path),
|
|
31
|
+
initializeSchema: (db) => mocks.initializeSchema(db),
|
|
32
|
+
AdvisoryRepository: function AdvisoryRepository(db) {
|
|
33
|
+
mocks.AdvisoryRepositoryCtor(db);
|
|
34
|
+
this.getActiveAdvisories =
|
|
35
|
+
mocks.getActiveAdvisories;
|
|
36
|
+
},
|
|
37
|
+
}));
|
|
38
|
+
vi.mock('../src/utils/require-tier.js', () => ({
|
|
39
|
+
requireTier: (tier) => mocks.requireTier(tier),
|
|
40
|
+
}));
|
|
41
|
+
import { createAuditCommand, createAuditAdvisoriesSubcommand, AUDIT_FLAT_DEPRECATION_NOTICE, } from '../src/commands/audit.js';
|
|
42
|
+
function setupDbStub() {
|
|
43
|
+
const db = { close: vi.fn() };
|
|
44
|
+
mocks.createDatabaseAsync.mockResolvedValue(db);
|
|
45
|
+
return db;
|
|
46
|
+
}
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Tests
|
|
49
|
+
// ============================================================================
|
|
50
|
+
describe('SMI-4590 Step 0a — sklx audit restructure', () => {
|
|
51
|
+
let stderrSpy;
|
|
52
|
+
let stdoutSpy;
|
|
53
|
+
let exitSpy;
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
vi.clearAllMocks();
|
|
56
|
+
mocks.requireTier.mockResolvedValue(undefined);
|
|
57
|
+
mocks.getActiveAdvisories.mockReturnValue([]);
|
|
58
|
+
stderrSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
59
|
+
stdoutSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
60
|
+
// process.exit must throw so test bodies never actually exit the runner.
|
|
61
|
+
exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {
|
|
62
|
+
throw new Error('process.exit called');
|
|
63
|
+
}));
|
|
64
|
+
});
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
stderrSpy.mockRestore();
|
|
67
|
+
stdoutSpy.mockRestore();
|
|
68
|
+
exitSpy.mockRestore();
|
|
69
|
+
});
|
|
70
|
+
describe('parent command shape', () => {
|
|
71
|
+
it('registers `audit` as a parent command with description', () => {
|
|
72
|
+
const audit = createAuditCommand();
|
|
73
|
+
expect(audit.name()).toBe('audit');
|
|
74
|
+
expect(audit.description()).toMatch(/audit/i);
|
|
75
|
+
});
|
|
76
|
+
it('registers `advisories` as a subcommand', () => {
|
|
77
|
+
const audit = createAuditCommand();
|
|
78
|
+
const subcommandNames = audit.commands.map((c) => c.name());
|
|
79
|
+
expect(subcommandNames).toContain('advisories');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('`sklx audit advisories` (canonical)', () => {
|
|
83
|
+
it('runs the advisories handler', async () => {
|
|
84
|
+
setupDbStub();
|
|
85
|
+
const audit = createAuditCommand();
|
|
86
|
+
const root = new Command().exitOverride();
|
|
87
|
+
root.addCommand(audit);
|
|
88
|
+
await root.parseAsync(['node', 'sklx', 'audit', 'advisories']);
|
|
89
|
+
expect(mocks.requireTier).toHaveBeenCalledWith('team');
|
|
90
|
+
expect(mocks.createDatabaseAsync).toHaveBeenCalled();
|
|
91
|
+
expect(mocks.AdvisoryRepositoryCtor).toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
it('does NOT emit the deprecation warning', async () => {
|
|
94
|
+
setupDbStub();
|
|
95
|
+
const audit = createAuditCommand();
|
|
96
|
+
const root = new Command().exitOverride();
|
|
97
|
+
root.addCommand(audit);
|
|
98
|
+
await root.parseAsync(['node', 'sklx', 'audit', 'advisories']);
|
|
99
|
+
const warnings = stderrSpy.mock.calls.flat().filter((arg) => typeof arg === 'string' && arg.includes('DEPRECATED'));
|
|
100
|
+
expect(warnings).toHaveLength(0);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('`sklx audit <skill-id>` deprecation alias', () => {
|
|
104
|
+
it('emits the deprecation warning to stderr', async () => {
|
|
105
|
+
setupDbStub();
|
|
106
|
+
const audit = createAuditCommand();
|
|
107
|
+
const root = new Command().exitOverride();
|
|
108
|
+
root.addCommand(audit);
|
|
109
|
+
await root.parseAsync(['node', 'sklx', 'audit', 'some/skill-id']);
|
|
110
|
+
const warnings = stderrSpy.mock.calls.flat().filter((arg) => typeof arg === 'string' && arg.includes(AUDIT_FLAT_DEPRECATION_NOTICE));
|
|
111
|
+
expect(warnings.length).toBeGreaterThan(0);
|
|
112
|
+
});
|
|
113
|
+
it('forwards to the same advisories handler', async () => {
|
|
114
|
+
setupDbStub();
|
|
115
|
+
const audit = createAuditCommand();
|
|
116
|
+
const root = new Command().exitOverride();
|
|
117
|
+
root.addCommand(audit);
|
|
118
|
+
await root.parseAsync(['node', 'sklx', 'audit', 'some/skill-id']);
|
|
119
|
+
expect(mocks.requireTier).toHaveBeenCalledWith('team');
|
|
120
|
+
expect(mocks.createDatabaseAsync).toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
it('mentions the canonical `audit advisories` form in the warning', () => {
|
|
123
|
+
expect(AUDIT_FLAT_DEPRECATION_NOTICE).toMatch(/audit advisories/);
|
|
124
|
+
});
|
|
125
|
+
it('flags the alias as removed in the next minor', () => {
|
|
126
|
+
expect(AUDIT_FLAT_DEPRECATION_NOTICE).toMatch(/next minor/i);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe('`sklx audit` with no args', () => {
|
|
130
|
+
it('prints help (no advisories handler invocation)', async () => {
|
|
131
|
+
const audit = createAuditCommand();
|
|
132
|
+
const root = new Command().exitOverride();
|
|
133
|
+
root.addCommand(audit);
|
|
134
|
+
// commander's `.help()` calls process.exit; our spy throws.
|
|
135
|
+
await expect(root.parseAsync(['node', 'sklx', 'audit'])).rejects.toThrow();
|
|
136
|
+
expect(mocks.requireTier).not.toHaveBeenCalled();
|
|
137
|
+
expect(mocks.createDatabaseAsync).not.toHaveBeenCalled();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
describe('subcommand factory exports (regression guard)', () => {
|
|
141
|
+
it('createAuditAdvisoriesSubcommand returns a Command named `advisories`', () => {
|
|
142
|
+
const cmd = createAuditAdvisoriesSubcommand();
|
|
143
|
+
expect(cmd.name()).toBe('advisories');
|
|
144
|
+
});
|
|
145
|
+
it('createAuditCommand returns a Command named `audit`', () => {
|
|
146
|
+
const cmd = createAuditCommand();
|
|
147
|
+
expect(cmd.name()).toBe('audit');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=audit-restructure.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-restructure.test.js","sourceRoot":"","sources":["../../tests/audit-restructure.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview SMI-4590 Wave 4 PR 5/6 — `sklx config get/set audit_mode` tests
|
|
3
|
+
* @module @skillsmith/cli/tests/config
|
|
4
|
+
*
|
|
5
|
+
* Coverage (plan §810-813 — load-bearing tier-revalidation security tests):
|
|
6
|
+
*
|
|
7
|
+
* Tier revalidation matrix — the `set` path MUST refuse a write when
|
|
8
|
+
* `tierAllowsAuditMode(tier, mode)` returns false. No file IO occurs on
|
|
9
|
+
* rejection; the existing config (if any) stays intact.
|
|
10
|
+
*
|
|
11
|
+
* 1. community + power_user → audit.mode.tier_ineligible, NO write
|
|
12
|
+
* 2. community + governance → audit.mode.tier_ineligible, NO write
|
|
13
|
+
* 3. individual + power_user → audit.mode.tier_ineligible, NO write
|
|
14
|
+
* 4. individual + governance → audit.mode.tier_ineligible, NO write
|
|
15
|
+
* 5. team + power_user → SUCCESS, file written
|
|
16
|
+
* 6. team + governance → audit.mode.tier_ineligible (Enterprise-only)
|
|
17
|
+
* 7. enterprise + governance → SUCCESS, file written
|
|
18
|
+
*
|
|
19
|
+
* Value validation:
|
|
20
|
+
* 8. any tier + invalid_value → audit.mode.invalid_value, NO write
|
|
21
|
+
* 9. any tier + preventative → SUCCESS for every tier
|
|
22
|
+
* 10. any tier + off → SUCCESS for every tier
|
|
23
|
+
*
|
|
24
|
+
* Unsupported keys:
|
|
25
|
+
* 11. set with unknown key → config.unsupported_key, NO write
|
|
26
|
+
* 12. get with unknown key → config.unsupported_key
|
|
27
|
+
*
|
|
28
|
+
* Get path:
|
|
29
|
+
* 13. get audit_mode reads existing file value when present
|
|
30
|
+
* 14. get audit_mode falls back to tier default when unset
|
|
31
|
+
*/
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=config.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../tests/config.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG"}
|