claudekit-cli 4.3.1-dev.11 → 4.3.1-dev.13
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/bin/postinstall-self-heal.cjs +143 -0
- package/cli-manifest.json +2 -2
- package/dist/index.js +1573 -1097
- package/package.json +3 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const os = require("node:os");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
|
|
8
|
+
const MAX_SETTINGS_BYTES = 5 * 1024 * 1024;
|
|
9
|
+
|
|
10
|
+
function isLegacyDescriptiveNamePrompt(entry) {
|
|
11
|
+
if (!entry || entry.type !== "prompt" || typeof entry.prompt !== "string") return false;
|
|
12
|
+
|
|
13
|
+
const prompt = entry.prompt;
|
|
14
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
15
|
+
const isOriginalLegacyPrompt =
|
|
16
|
+
prompt.includes("Use kebab-case file naming") &&
|
|
17
|
+
prompt.includes("self-documenting") &&
|
|
18
|
+
prompt.includes("Grep, Glob, Search");
|
|
19
|
+
const isDescriptiveNameKebabPrompt =
|
|
20
|
+
lowerPrompt.includes("descriptive-name") &&
|
|
21
|
+
lowerPrompt.includes("kebab-case") &&
|
|
22
|
+
(lowerPrompt.includes("file") || lowerPrompt.includes("filename"));
|
|
23
|
+
|
|
24
|
+
return isOriginalLegacyPrompt || isDescriptiveNameKebabPrompt;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function pruneHooks(settings) {
|
|
28
|
+
if (!settings || typeof settings !== "object" || !settings.hooks) return 0;
|
|
29
|
+
|
|
30
|
+
let pruned = 0;
|
|
31
|
+
for (const [eventName, entries] of Object.entries(settings.hooks)) {
|
|
32
|
+
if (!Array.isArray(entries)) continue;
|
|
33
|
+
|
|
34
|
+
const keptEntries = [];
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
if (isLegacyDescriptiveNamePrompt(entry)) {
|
|
37
|
+
pruned++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (Array.isArray(entry?.hooks)) {
|
|
42
|
+
const keptHooks = entry.hooks.filter((hook) => {
|
|
43
|
+
if (isLegacyDescriptiveNamePrompt(hook)) {
|
|
44
|
+
pruned++;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
if (keptHooks.length > 0) keptEntries.push({ ...entry, hooks: keptHooks });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
keptEntries.push(entry);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (keptEntries.length === 0) {
|
|
57
|
+
delete settings.hooks[eventName];
|
|
58
|
+
} else {
|
|
59
|
+
settings.hooks[eventName] = keptEntries;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (Object.keys(settings.hooks).length === 0) settings.hooks = undefined;
|
|
64
|
+
return pruned;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function pruneSettingsFile(filePath) {
|
|
68
|
+
try {
|
|
69
|
+
if (!filePath || !fs.existsSync(filePath)) return 0;
|
|
70
|
+
const stat = fs.statSync(filePath);
|
|
71
|
+
if (!stat.isFile() || stat.size > MAX_SETTINGS_BYTES) return 0;
|
|
72
|
+
|
|
73
|
+
const settings = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
74
|
+
const pruned = pruneHooks(settings);
|
|
75
|
+
if (pruned > 0) {
|
|
76
|
+
fs.writeFileSync(filePath, `${JSON.stringify(settings, null, 2)}\n`);
|
|
77
|
+
}
|
|
78
|
+
return pruned;
|
|
79
|
+
} catch {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getHomeDir() {
|
|
85
|
+
return process.env.CK_TEST_HOME || os.homedir();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function addIfSettingsFile(files, filePath) {
|
|
89
|
+
if (!filePath) return;
|
|
90
|
+
const base = path.basename(filePath);
|
|
91
|
+
if (
|
|
92
|
+
base === "settings.json" ||
|
|
93
|
+
base === "settings.local.json" ||
|
|
94
|
+
base.endsWith(".settings.json")
|
|
95
|
+
) {
|
|
96
|
+
files.add(path.resolve(filePath));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function collectCandidateSettingsFiles() {
|
|
101
|
+
const files = new Set();
|
|
102
|
+
const initCwd = process.env.INIT_CWD;
|
|
103
|
+
if (initCwd && path.isAbsolute(initCwd)) {
|
|
104
|
+
addIfSettingsFile(files, path.join(initCwd, ".claude", "settings.json"));
|
|
105
|
+
addIfSettingsFile(files, path.join(initCwd, ".claude", "settings.local.json"));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const homeDir = getHomeDir();
|
|
109
|
+
const globalClaudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(homeDir, ".claude");
|
|
110
|
+
addIfSettingsFile(files, path.join(globalClaudeDir, "settings.json"));
|
|
111
|
+
addIfSettingsFile(files, path.join(globalClaudeDir, "settings.local.json"));
|
|
112
|
+
|
|
113
|
+
const ccsDir = process.env.CK_TEST_CCS_DIR || path.join(homeDir, ".ccs");
|
|
114
|
+
try {
|
|
115
|
+
for (const dirent of fs.readdirSync(ccsDir, { withFileTypes: true })) {
|
|
116
|
+
if (dirent.isFile()) addIfSettingsFile(files, path.join(ccsDir, dirent.name));
|
|
117
|
+
}
|
|
118
|
+
} catch {
|
|
119
|
+
// Optional compatibility directory; skip when absent or unreadable.
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return [...files];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function main() {
|
|
126
|
+
let pruned = 0;
|
|
127
|
+
for (const filePath of collectCandidateSettingsFiles()) {
|
|
128
|
+
pruned += pruneSettingsFile(filePath);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (pruned > 0 && process.env.CK_POSTINSTALL_DEBUG === "1") {
|
|
132
|
+
console.warn(`[claudekit-cli] Pruned ${pruned} legacy hook prompt(s)`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
main();
|
|
137
|
+
|
|
138
|
+
module.exports = {
|
|
139
|
+
collectCandidateSettingsFiles,
|
|
140
|
+
isLegacyDescriptiveNamePrompt,
|
|
141
|
+
pruneHooks,
|
|
142
|
+
pruneSettingsFile,
|
|
143
|
+
};
|
package/cli-manifest.json
CHANGED