@soleri/cli 1.1.0 → 1.3.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/README.md +30 -1
- package/dist/commands/create.js +42 -4
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/hooks.js +40 -11
- package/dist/commands/hooks.js.map +1 -1
- package/dist/hook-packs/a11y/hookify.focus-ring-required.local.md +1 -0
- package/dist/hook-packs/a11y/hookify.semantic-html.local.md +1 -0
- package/dist/hook-packs/a11y/hookify.ux-touch-targets.local.md +1 -0
- package/dist/hook-packs/a11y/manifest.json +1 -0
- package/dist/hook-packs/clean-commits/hookify.no-ai-attribution.local.md +1 -0
- package/dist/hook-packs/clean-commits/manifest.json +1 -0
- package/dist/hook-packs/css-discipline/hookify.no-important.local.md +1 -0
- package/dist/hook-packs/css-discipline/hookify.no-inline-styles.local.md +1 -0
- package/dist/hook-packs/css-discipline/manifest.json +1 -0
- package/dist/hook-packs/full/manifest.json +1 -0
- package/dist/hook-packs/installer.d.ts +18 -5
- package/dist/hook-packs/installer.js +29 -10
- package/dist/hook-packs/installer.js.map +1 -1
- package/dist/hook-packs/installer.ts +42 -10
- package/dist/hook-packs/registry.d.ts +6 -2
- package/dist/hook-packs/registry.js +46 -13
- package/dist/hook-packs/registry.js.map +1 -1
- package/dist/hook-packs/registry.ts +49 -13
- package/dist/hook-packs/typescript-safety/hookify.no-any-types.local.md +1 -0
- package/dist/hook-packs/typescript-safety/hookify.no-console-log.local.md +1 -0
- package/dist/hook-packs/typescript-safety/manifest.json +1 -0
- package/dist/utils/checks.d.ts +1 -0
- package/dist/utils/checks.js +17 -0
- package/dist/utils/checks.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/create.ts +45 -3
- package/src/commands/hooks.ts +54 -11
- package/src/hook-packs/a11y/hookify.focus-ring-required.local.md +1 -0
- package/src/hook-packs/a11y/hookify.semantic-html.local.md +1 -0
- package/src/hook-packs/a11y/hookify.ux-touch-targets.local.md +1 -0
- package/src/hook-packs/a11y/manifest.json +1 -0
- package/src/hook-packs/clean-commits/hookify.no-ai-attribution.local.md +1 -0
- package/src/hook-packs/clean-commits/manifest.json +1 -0
- package/src/hook-packs/css-discipline/hookify.no-important.local.md +1 -0
- package/src/hook-packs/css-discipline/hookify.no-inline-styles.local.md +1 -0
- package/src/hook-packs/css-discipline/manifest.json +1 -0
- package/src/hook-packs/full/manifest.json +1 -0
- package/src/hook-packs/installer.ts +42 -10
- package/src/hook-packs/registry.ts +49 -13
- package/src/hook-packs/typescript-safety/hookify.no-any-types.local.md +1 -0
- package/src/hook-packs/typescript-safety/hookify.no-console-log.local.md +1 -0
- package/src/hook-packs/typescript-safety/manifest.json +1 -0
- package/src/utils/checks.ts +18 -0
|
@@ -8,14 +8,17 @@ import { homedir } from 'node:os';
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = dirname(__filename);
|
|
10
10
|
/** Root directory containing all built-in hook packs. */
|
|
11
|
-
function
|
|
11
|
+
function getBuiltinRoot() {
|
|
12
12
|
return __dirname;
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
/** Local custom packs directory. */
|
|
15
|
+
function getLocalRoot() {
|
|
16
|
+
return join(process.cwd(), '.soleri', 'hook-packs');
|
|
17
|
+
}
|
|
18
|
+
/** Scan a directory for pack manifests. */
|
|
19
|
+
function scanPacksDir(root, source) {
|
|
20
|
+
if (!existsSync(root))
|
|
21
|
+
return [];
|
|
19
22
|
const entries = readdirSync(root, { withFileTypes: true });
|
|
20
23
|
const packs = [];
|
|
21
24
|
for (const entry of entries) {
|
|
@@ -26,6 +29,7 @@ export function listPacks() {
|
|
|
26
29
|
continue;
|
|
27
30
|
try {
|
|
28
31
|
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
32
|
+
manifest.source = source;
|
|
29
33
|
packs.push(manifest);
|
|
30
34
|
}
|
|
31
35
|
catch {
|
|
@@ -35,17 +39,46 @@ export function listPacks() {
|
|
|
35
39
|
return packs;
|
|
36
40
|
}
|
|
37
41
|
/**
|
|
38
|
-
*
|
|
42
|
+
* List all available hook packs (built-in + local custom).
|
|
43
|
+
* Local packs in .soleri/hook-packs/ override built-in packs with the same name.
|
|
44
|
+
*/
|
|
45
|
+
export function listPacks() {
|
|
46
|
+
const builtIn = scanPacksDir(getBuiltinRoot(), 'built-in');
|
|
47
|
+
const local = scanPacksDir(getLocalRoot(), 'local');
|
|
48
|
+
// Local packs override built-in packs with same name
|
|
49
|
+
const byName = new Map();
|
|
50
|
+
for (const pack of builtIn)
|
|
51
|
+
byName.set(pack.name, pack);
|
|
52
|
+
for (const pack of local)
|
|
53
|
+
byName.set(pack.name, pack);
|
|
54
|
+
return Array.from(byName.values());
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get a specific pack by name. Local packs take precedence.
|
|
39
58
|
*/
|
|
40
59
|
export function getPack(name) {
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
60
|
+
// Check local first
|
|
61
|
+
const localDir = join(getLocalRoot(), name);
|
|
62
|
+
const localManifest = join(localDir, 'manifest.json');
|
|
63
|
+
if (existsSync(localManifest)) {
|
|
64
|
+
try {
|
|
65
|
+
const manifest = JSON.parse(readFileSync(localManifest, 'utf-8'));
|
|
66
|
+
manifest.source = 'local';
|
|
67
|
+
return { manifest, dir: localDir };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Fall through to built-in
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Then built-in
|
|
74
|
+
const builtinDir = join(getBuiltinRoot(), name);
|
|
75
|
+
const builtinManifest = join(builtinDir, 'manifest.json');
|
|
76
|
+
if (!existsSync(builtinManifest))
|
|
45
77
|
return null;
|
|
46
78
|
try {
|
|
47
|
-
const manifest = JSON.parse(readFileSync(
|
|
48
|
-
|
|
79
|
+
const manifest = JSON.parse(readFileSync(builtinManifest, 'utf-8'));
|
|
80
|
+
manifest.source = 'built-in';
|
|
81
|
+
return { manifest, dir: builtinDir };
|
|
49
82
|
}
|
|
50
83
|
catch {
|
|
51
84
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/hook-packs/registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/hook-packs/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,21 +11,27 @@ export interface HookPackManifest {
|
|
|
11
11
|
description: string;
|
|
12
12
|
hooks: string[];
|
|
13
13
|
composedFrom?: string[];
|
|
14
|
+
version?: string;
|
|
15
|
+
/** Whether this pack is built-in or user-defined */
|
|
16
|
+
source?: 'built-in' | 'local';
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
20
|
const __dirname = dirname(__filename);
|
|
18
21
|
|
|
19
22
|
/** Root directory containing all built-in hook packs. */
|
|
20
|
-
function
|
|
23
|
+
function getBuiltinRoot(): string {
|
|
21
24
|
return __dirname;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
/** Local custom packs directory. */
|
|
28
|
+
function getLocalRoot(): string {
|
|
29
|
+
return join(process.cwd(), '.soleri', 'hook-packs');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Scan a directory for pack manifests. */
|
|
33
|
+
function scanPacksDir(root: string, source: 'built-in' | 'local'): HookPackManifest[] {
|
|
34
|
+
if (!existsSync(root)) return [];
|
|
29
35
|
const entries = readdirSync(root, { withFileTypes: true });
|
|
30
36
|
const packs: HookPackManifest[] = [];
|
|
31
37
|
|
|
@@ -36,6 +42,7 @@ export function listPacks(): HookPackManifest[] {
|
|
|
36
42
|
|
|
37
43
|
try {
|
|
38
44
|
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')) as HookPackManifest;
|
|
45
|
+
manifest.source = source;
|
|
39
46
|
packs.push(manifest);
|
|
40
47
|
} catch {
|
|
41
48
|
// Skip malformed manifests
|
|
@@ -46,18 +53,47 @@ export function listPacks(): HookPackManifest[] {
|
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
/**
|
|
49
|
-
*
|
|
56
|
+
* List all available hook packs (built-in + local custom).
|
|
57
|
+
* Local packs in .soleri/hook-packs/ override built-in packs with the same name.
|
|
58
|
+
*/
|
|
59
|
+
export function listPacks(): HookPackManifest[] {
|
|
60
|
+
const builtIn = scanPacksDir(getBuiltinRoot(), 'built-in');
|
|
61
|
+
const local = scanPacksDir(getLocalRoot(), 'local');
|
|
62
|
+
|
|
63
|
+
// Local packs override built-in packs with same name
|
|
64
|
+
const byName = new Map<string, HookPackManifest>();
|
|
65
|
+
for (const pack of builtIn) byName.set(pack.name, pack);
|
|
66
|
+
for (const pack of local) byName.set(pack.name, pack);
|
|
67
|
+
|
|
68
|
+
return Array.from(byName.values());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get a specific pack by name. Local packs take precedence.
|
|
50
73
|
*/
|
|
51
74
|
export function getPack(name: string): { manifest: HookPackManifest; dir: string } | null {
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
const
|
|
75
|
+
// Check local first
|
|
76
|
+
const localDir = join(getLocalRoot(), name);
|
|
77
|
+
const localManifest = join(localDir, 'manifest.json');
|
|
78
|
+
if (existsSync(localManifest)) {
|
|
79
|
+
try {
|
|
80
|
+
const manifest = JSON.parse(readFileSync(localManifest, 'utf-8')) as HookPackManifest;
|
|
81
|
+
manifest.source = 'local';
|
|
82
|
+
return { manifest, dir: localDir };
|
|
83
|
+
} catch {
|
|
84
|
+
// Fall through to built-in
|
|
85
|
+
}
|
|
86
|
+
}
|
|
55
87
|
|
|
56
|
-
|
|
88
|
+
// Then built-in
|
|
89
|
+
const builtinDir = join(getBuiltinRoot(), name);
|
|
90
|
+
const builtinManifest = join(builtinDir, 'manifest.json');
|
|
91
|
+
if (!existsSync(builtinManifest)) return null;
|
|
57
92
|
|
|
58
93
|
try {
|
|
59
|
-
const manifest = JSON.parse(readFileSync(
|
|
60
|
-
|
|
94
|
+
const manifest = JSON.parse(readFileSync(builtinManifest, 'utf-8')) as HookPackManifest;
|
|
95
|
+
manifest.source = 'built-in';
|
|
96
|
+
return { manifest, dir: builtinDir };
|
|
61
97
|
} catch {
|
|
62
98
|
return null;
|
|
63
99
|
}
|
package/dist/utils/checks.d.ts
CHANGED
|
@@ -8,5 +8,6 @@ export declare function checkNpm(): CheckResult;
|
|
|
8
8
|
export declare function checkAgentProject(dir?: string): CheckResult;
|
|
9
9
|
export declare function checkAgentBuild(dir?: string): CheckResult;
|
|
10
10
|
export declare function checkNodeModules(dir?: string): CheckResult;
|
|
11
|
+
export declare function checkHookPacks(): CheckResult;
|
|
11
12
|
export declare function runAllChecks(dir?: string): CheckResult[];
|
|
12
13
|
export {};
|
package/dist/utils/checks.js
CHANGED
|
@@ -6,6 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
import { execFileSync } from 'node:child_process';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
8
|
import { detectAgent } from './agent-context.js';
|
|
9
|
+
import { getInstalledPacks } from '../hook-packs/registry.js';
|
|
9
10
|
export function checkNodeVersion() {
|
|
10
11
|
const [major] = process.versions.node.split('.').map(Number);
|
|
11
12
|
if (major >= 18) {
|
|
@@ -123,6 +124,21 @@ function checkCognee() {
|
|
|
123
124
|
};
|
|
124
125
|
}
|
|
125
126
|
}
|
|
127
|
+
export function checkHookPacks() {
|
|
128
|
+
const installed = getInstalledPacks();
|
|
129
|
+
if (installed.length === 0) {
|
|
130
|
+
return {
|
|
131
|
+
status: 'warn',
|
|
132
|
+
label: 'Hook packs',
|
|
133
|
+
detail: 'none installed — run soleri hooks list-packs',
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
status: 'pass',
|
|
138
|
+
label: 'Hook packs',
|
|
139
|
+
detail: installed.join(', '),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
126
142
|
export function runAllChecks(dir) {
|
|
127
143
|
return [
|
|
128
144
|
checkNodeVersion(),
|
|
@@ -132,6 +148,7 @@ export function runAllChecks(dir) {
|
|
|
132
148
|
checkNodeModules(dir),
|
|
133
149
|
checkAgentBuild(dir),
|
|
134
150
|
checkMcpRegistration(dir),
|
|
151
|
+
checkHookPacks(),
|
|
135
152
|
checkCognee(),
|
|
136
153
|
];
|
|
137
154
|
}
|
package/dist/utils/checks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/utils/checks.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/utils/checks.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/commands/create.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { Command } from 'commander';
|
|
|
4
4
|
import * as p from '@clack/prompts';
|
|
5
5
|
import { previewScaffold, scaffold, AgentConfigSchema } from '@soleri/forge/lib';
|
|
6
6
|
import { runCreateWizard } from '../prompts/create-wizard.js';
|
|
7
|
+
import { listPacks } from '../hook-packs/registry.js';
|
|
8
|
+
import { installPack } from '../hook-packs/installer.js';
|
|
7
9
|
|
|
8
10
|
export function registerCreate(program: Command): void {
|
|
9
11
|
program
|
|
@@ -38,12 +40,38 @@ export function registerCreate(program: Command): void {
|
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
// Hook packs — from config file or interactive prompt
|
|
44
|
+
let selectedPacks: string[] = [];
|
|
45
|
+
if (config.hookPacks && config.hookPacks.length > 0) {
|
|
46
|
+
selectedPacks = config.hookPacks;
|
|
47
|
+
} else if (!opts?.config) {
|
|
48
|
+
const packs = listPacks();
|
|
49
|
+
const packChoices = packs.map((pk) => ({
|
|
50
|
+
value: pk.name,
|
|
51
|
+
label: pk.name,
|
|
52
|
+
hint: `${pk.description} (${pk.hooks.length} hooks)`,
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
const chosen = await p.multiselect({
|
|
56
|
+
message: 'Install hook packs? (quality gates for ~/.claude/)',
|
|
57
|
+
options: packChoices,
|
|
58
|
+
required: false,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!p.isCancel(chosen)) {
|
|
62
|
+
selectedPacks = chosen as string[];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
41
66
|
// Preview
|
|
42
67
|
const preview = previewScaffold(config);
|
|
43
68
|
|
|
44
69
|
p.log.info(`Will create ${preview.files.length} files in ${preview.agentDir}`);
|
|
45
70
|
p.log.info(`Facades: ${preview.facades.map((f) => f.name).join(', ')}`);
|
|
46
71
|
p.log.info(`Domains: ${preview.domains.join(', ')}`);
|
|
72
|
+
if (selectedPacks.length > 0) {
|
|
73
|
+
p.log.info(`Hook packs: ${selectedPacks.join(', ')}`);
|
|
74
|
+
}
|
|
47
75
|
|
|
48
76
|
const confirmed = await p.confirm({ message: 'Create agent?' });
|
|
49
77
|
if (p.isCancel(confirmed) || !confirmed) {
|
|
@@ -57,13 +85,27 @@ export function registerCreate(program: Command): void {
|
|
|
57
85
|
const result = scaffold(config);
|
|
58
86
|
s.stop(result.success ? 'Agent created!' : 'Scaffolding failed');
|
|
59
87
|
|
|
60
|
-
if (result.success) {
|
|
61
|
-
p.note(result.summary, 'Next steps');
|
|
62
|
-
} else {
|
|
88
|
+
if (!result.success) {
|
|
63
89
|
p.log.error(result.summary);
|
|
64
90
|
process.exit(1);
|
|
65
91
|
}
|
|
66
92
|
|
|
93
|
+
// Install selected hook packs
|
|
94
|
+
if (selectedPacks.length > 0) {
|
|
95
|
+
for (const packName of selectedPacks) {
|
|
96
|
+
const { installed } = installPack(packName);
|
|
97
|
+
if (installed.length > 0) {
|
|
98
|
+
p.log.success(`Hook pack "${packName}" installed (${installed.length} hooks)`);
|
|
99
|
+
} else {
|
|
100
|
+
p.log.info(`Hook pack "${packName}" — all hooks already present`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (result.success) {
|
|
106
|
+
p.note(result.summary, 'Next steps');
|
|
107
|
+
}
|
|
108
|
+
|
|
67
109
|
p.outro('Done!');
|
|
68
110
|
} catch (err) {
|
|
69
111
|
p.log.error(err instanceof Error ? err.message : String(err));
|
package/src/commands/hooks.ts
CHANGED
|
@@ -87,8 +87,9 @@ export function registerHooks(program: Command): void {
|
|
|
87
87
|
hooks
|
|
88
88
|
.command('add-pack')
|
|
89
89
|
.argument('<pack>', 'Hook pack name')
|
|
90
|
-
.
|
|
91
|
-
.
|
|
90
|
+
.option('--project', 'Install to project .claude/ instead of global ~/.claude/')
|
|
91
|
+
.description('Install a hook pack globally (~/.claude/) or per-project (--project)')
|
|
92
|
+
.action((packName: string, opts: { project?: boolean }) => {
|
|
92
93
|
const pack = getPack(packName);
|
|
93
94
|
if (!pack) {
|
|
94
95
|
const available = listPacks().map((p) => p.name);
|
|
@@ -96,15 +97,17 @@ export function registerHooks(program: Command): void {
|
|
|
96
97
|
process.exit(1);
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
const
|
|
100
|
+
const projectDir = opts.project ? process.cwd() : undefined;
|
|
101
|
+
const target = opts.project ? '.claude/' : '~/.claude/';
|
|
102
|
+
const { installed, skipped } = installPack(packName, { projectDir });
|
|
100
103
|
for (const hook of installed) {
|
|
101
|
-
log.pass(`Installed hookify.${hook}.local.md`);
|
|
104
|
+
log.pass(`Installed hookify.${hook}.local.md → ${target}`);
|
|
102
105
|
}
|
|
103
106
|
for (const hook of skipped) {
|
|
104
107
|
log.dim(` hookify.${hook}.local.md — already exists, skipped`);
|
|
105
108
|
}
|
|
106
109
|
if (installed.length > 0) {
|
|
107
|
-
log.info(`Pack "${packName}" installed (${installed.length} hooks)`);
|
|
110
|
+
log.info(`Pack "${packName}" installed (${installed.length} hooks) → ${target}`);
|
|
108
111
|
} else {
|
|
109
112
|
log.info(`Pack "${packName}" — all hooks already installed`);
|
|
110
113
|
}
|
|
@@ -113,8 +116,9 @@ export function registerHooks(program: Command): void {
|
|
|
113
116
|
hooks
|
|
114
117
|
.command('remove-pack')
|
|
115
118
|
.argument('<pack>', 'Hook pack name')
|
|
116
|
-
.
|
|
117
|
-
.
|
|
119
|
+
.option('--project', 'Remove from project .claude/ instead of global ~/.claude/')
|
|
120
|
+
.description('Remove a hook pack')
|
|
121
|
+
.action((packName: string, opts: { project?: boolean }) => {
|
|
118
122
|
const pack = getPack(packName);
|
|
119
123
|
if (!pack) {
|
|
120
124
|
const available = listPacks().map((p) => p.name);
|
|
@@ -122,7 +126,8 @@ export function registerHooks(program: Command): void {
|
|
|
122
126
|
process.exit(1);
|
|
123
127
|
}
|
|
124
128
|
|
|
125
|
-
const
|
|
129
|
+
const projectDir = opts.project ? process.cwd() : undefined;
|
|
130
|
+
const { removed } = removePack(packName, { projectDir });
|
|
126
131
|
if (removed.length === 0) {
|
|
127
132
|
log.info(`No hooks from pack "${packName}" found to remove.`);
|
|
128
133
|
} else {
|
|
@@ -144,15 +149,53 @@ export function registerHooks(program: Command): void {
|
|
|
144
149
|
for (const pack of packs) {
|
|
145
150
|
const status = isPackInstalled(pack.name);
|
|
146
151
|
|
|
152
|
+
const versionLabel = pack.version ? ` v${pack.version}` : '';
|
|
153
|
+
const sourceLabel = pack.source === 'local' ? ' [local]' : '';
|
|
154
|
+
|
|
147
155
|
if (status === true) {
|
|
148
|
-
log.pass(
|
|
156
|
+
log.pass(
|
|
157
|
+
`${pack.name}${versionLabel}${sourceLabel}`,
|
|
158
|
+
`${pack.description} (${pack.hooks.length} hooks)`,
|
|
159
|
+
);
|
|
149
160
|
} else if (status === 'partial') {
|
|
150
|
-
log.warn(
|
|
161
|
+
log.warn(
|
|
162
|
+
`${pack.name}${versionLabel}${sourceLabel}`,
|
|
163
|
+
`${pack.description} (${pack.hooks.length} hooks) — partial`,
|
|
164
|
+
);
|
|
151
165
|
} else {
|
|
152
|
-
log.dim(
|
|
166
|
+
log.dim(
|
|
167
|
+
` ${pack.name}${versionLabel}${sourceLabel} — ${pack.description} (${pack.hooks.length} hooks)`,
|
|
168
|
+
);
|
|
153
169
|
}
|
|
154
170
|
}
|
|
155
171
|
});
|
|
172
|
+
|
|
173
|
+
hooks
|
|
174
|
+
.command('upgrade-pack')
|
|
175
|
+
.argument('<pack>', 'Hook pack name')
|
|
176
|
+
.option('--project', 'Upgrade in project .claude/ instead of global ~/.claude/')
|
|
177
|
+
.description('Upgrade a hook pack to the latest version (overwrites existing files)')
|
|
178
|
+
.action((packName: string, opts: { project?: boolean }) => {
|
|
179
|
+
const pack = getPack(packName);
|
|
180
|
+
if (!pack) {
|
|
181
|
+
const available = listPacks().map((p) => p.name);
|
|
182
|
+
log.fail(`Unknown pack "${packName}". Available: ${available.join(', ')}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const projectDir = opts.project ? process.cwd() : undefined;
|
|
187
|
+
const packVersion = pack.manifest.version ?? 'unknown';
|
|
188
|
+
|
|
189
|
+
// Remove then reinstall to force overwrite
|
|
190
|
+
removePack(packName, { projectDir });
|
|
191
|
+
const { installed } = installPack(packName, { projectDir });
|
|
192
|
+
|
|
193
|
+
for (const hook of installed) {
|
|
194
|
+
log.pass(`hookify.${hook}.local.md → v${packVersion}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
log.info(`Pack "${packName}" upgraded to v${packVersion} (${installed.length} hooks)`);
|
|
198
|
+
});
|
|
156
199
|
}
|
|
157
200
|
|
|
158
201
|
function isValidEditor(editor: string): editor is EditorId {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "full",
|
|
3
|
+
"version": "1.0.0",
|
|
3
4
|
"description": "Complete quality suite — all 8 hooks",
|
|
4
5
|
"hooks": ["no-any-types", "no-console-log", "no-important", "no-inline-styles", "semantic-html", "focus-ring-required", "ux-touch-targets", "no-ai-attribution"],
|
|
5
6
|
"composedFrom": ["typescript-safety", "a11y", "css-discipline", "clean-commits"]
|