clikit-plugin 0.2.0 → 0.2.2
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/dist/cli.js +39 -8
- package/dist/hooks/comment-checker.d.ts +2 -2
- package/dist/hooks/comment-checker.d.ts.map +1 -1
- package/dist/hooks/compaction.d.ts.map +1 -1
- package/dist/hooks/security-check.d.ts.map +1 -1
- package/dist/hooks/truncator.d.ts.map +1 -1
- package/dist/index.js +27 -1
- package/memory/handoffs/2026-02-15-plugin-install-fix.md +140 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -44,13 +44,28 @@ function ensureConfigDir() {
|
|
|
44
44
|
function parseConfig(configPath) {
|
|
45
45
|
try {
|
|
46
46
|
if (!fs.existsSync(configPath)) {
|
|
47
|
-
return {};
|
|
47
|
+
return { config: {}, raw: "", parseError: null };
|
|
48
48
|
}
|
|
49
49
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
50
50
|
const cleaned = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
51
|
-
|
|
51
|
+
const cleanedTrailing = cleaned.replace(/,\s*([}\]])/g, "$1");
|
|
52
|
+
const parsed = JSON.parse(cleanedTrailing);
|
|
53
|
+
return { config: parsed, raw: content, parseError: null };
|
|
54
|
+
} catch (err) {
|
|
55
|
+
const raw = fs.existsSync(configPath) ? fs.readFileSync(configPath, "utf-8") : "";
|
|
56
|
+
return { config: {}, raw, parseError: String(err) };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function backupConfig(configPath) {
|
|
60
|
+
try {
|
|
61
|
+
if (!fs.existsSync(configPath)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const backupPath = `${configPath}.backup-${Date.now()}`;
|
|
65
|
+
fs.copyFileSync(configPath, backupPath);
|
|
66
|
+
return backupPath;
|
|
52
67
|
} catch {
|
|
53
|
-
return
|
|
68
|
+
return null;
|
|
54
69
|
}
|
|
55
70
|
}
|
|
56
71
|
function writeConfig(configPath, config) {
|
|
@@ -75,12 +90,28 @@ async function install() {
|
|
|
75
90
|
}
|
|
76
91
|
const configPath = getConfigPath();
|
|
77
92
|
try {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
const backupPath = backupConfig(configPath);
|
|
94
|
+
if (backupPath) {
|
|
95
|
+
console.log(` Backup created: ${backupPath}`);
|
|
96
|
+
}
|
|
97
|
+
const result = parseConfig(configPath);
|
|
98
|
+
if (result.parseError && result.raw.trim()) {
|
|
99
|
+
console.error(`\u2717 Config file has syntax errors and cannot be safely modified.`);
|
|
100
|
+
console.error(` Error: ${result.parseError}`);
|
|
101
|
+
console.error(` Please fix the config file manually or restore from backup.`);
|
|
102
|
+
console.error(` Backup: ${backupPath}`);
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
const config = result.config;
|
|
106
|
+
const existingPlugins = Array.isArray(config.plugin) ? config.plugin : [];
|
|
107
|
+
const preservedKeys = Object.keys(config).filter((k) => k !== "plugin");
|
|
108
|
+
if (preservedKeys.length > 0) {
|
|
109
|
+
console.log(` Preserving existing config: ${preservedKeys.join(", ")}`);
|
|
110
|
+
}
|
|
111
|
+
const filteredPlugins = existingPlugins.filter((p) => p !== PLUGIN_NAME && !p.startsWith(`${PLUGIN_NAME}@`));
|
|
81
112
|
filteredPlugins.push(PLUGIN_NAME);
|
|
82
|
-
config
|
|
83
|
-
writeConfig(configPath,
|
|
113
|
+
const newConfig = { ...config, plugin: filteredPlugins };
|
|
114
|
+
writeConfig(configPath, newConfig);
|
|
84
115
|
console.log(`\u2713 Plugin added to ${configPath}`);
|
|
85
116
|
} catch (err) {
|
|
86
117
|
console.error(`\u2717 Failed to update OpenCode config: ${err}`);
|
|
@@ -11,7 +11,7 @@ export interface CommentCheckResult {
|
|
|
11
11
|
totalLines: number;
|
|
12
12
|
ratio: number;
|
|
13
13
|
}
|
|
14
|
-
export declare function checkCommentDensity(content:
|
|
15
|
-
export declare function hasExcessiveAIComments(content:
|
|
14
|
+
export declare function checkCommentDensity(content: unknown, threshold?: number): CommentCheckResult;
|
|
15
|
+
export declare function hasExcessiveAIComments(content: unknown): boolean;
|
|
16
16
|
export declare function formatCommentWarning(result: CommentCheckResult): string;
|
|
17
17
|
//# sourceMappingURL=comment-checker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comment-checker.d.ts","sourceRoot":"","sources":["../../src/hooks/comment-checker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"comment-checker.d.ts","sourceRoot":"","sources":["../../src/hooks/comment-checker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,GAAE,MAAY,GAAG,kBAAkB,CAqDjG;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAYhE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAGvE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/hooks/compaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAuFzE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/hooks/compaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAuFzE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,SAAS,EAAE,CA2DlF;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,EAAE,QAAQ,GAAE,MAAa,GAAG,MAAM,CA4ChG;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,gBAAgB,GACxB,iBAAiB,CAYnB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAMtE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-check.d.ts","sourceRoot":"","sources":["../../src/hooks/security-check.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyBH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,mBAAmB,
|
|
1
|
+
{"version":3,"file":"security-check.d.ts","sourceRoot":"","sources":["../../src/hooks/security-check.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyBH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAmB7F;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAUzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"truncator.d.ts","sourceRoot":"","sources":["../../src/hooks/truncator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAOD,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,
|
|
1
|
+
{"version":3,"file":"truncator.d.ts","sourceRoot":"","sources":["../../src/hooks/truncator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAOD,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAYT;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,eAAe,GACvB,cAAc,CAsFhB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAIlE"}
|
package/dist/index.js
CHANGED
|
@@ -3917,6 +3917,8 @@ var SENSITIVE_FILES = [
|
|
|
3917
3917
|
/id_ed25519$/
|
|
3918
3918
|
];
|
|
3919
3919
|
function scanContentForSecrets(content, filename) {
|
|
3920
|
+
if (typeof content !== "string")
|
|
3921
|
+
return { safe: true, findings: [] };
|
|
3920
3922
|
const findings = [];
|
|
3921
3923
|
const lines = content.split(`
|
|
3922
3924
|
`);
|
|
@@ -3984,6 +3986,9 @@ var EXCESSIVE_COMMENT_PATTERNS = [
|
|
|
3984
3986
|
/#\s*(?:This|The|A|An) (?:function|method|class|script)/i
|
|
3985
3987
|
];
|
|
3986
3988
|
function checkCommentDensity(content, threshold = 0.3) {
|
|
3989
|
+
if (typeof content !== "string") {
|
|
3990
|
+
return { excessive: false, count: 0, totalLines: 0, ratio: 0 };
|
|
3991
|
+
}
|
|
3987
3992
|
const lines = content.split(`
|
|
3988
3993
|
`);
|
|
3989
3994
|
const totalLines = lines.filter((l) => l.trim().length > 0).length;
|
|
@@ -4026,6 +4031,9 @@ function checkCommentDensity(content, threshold = 0.3) {
|
|
|
4026
4031
|
};
|
|
4027
4032
|
}
|
|
4028
4033
|
function hasExcessiveAIComments(content) {
|
|
4034
|
+
if (typeof content !== "string") {
|
|
4035
|
+
return false;
|
|
4036
|
+
}
|
|
4029
4037
|
let matches = 0;
|
|
4030
4038
|
for (const pattern of EXCESSIVE_COMMENT_PATTERNS) {
|
|
4031
4039
|
const found = content.match(new RegExp(pattern, "gm"));
|
|
@@ -4450,6 +4458,8 @@ var DEFAULT_MAX_LINES = 500;
|
|
|
4450
4458
|
var DEFAULT_HEAD_LINES = 50;
|
|
4451
4459
|
var DEFAULT_TAIL_LINES = 50;
|
|
4452
4460
|
function shouldTruncate(content, config) {
|
|
4461
|
+
if (typeof content !== "string")
|
|
4462
|
+
return false;
|
|
4453
4463
|
const maxChars = config?.max_output_chars ?? DEFAULT_MAX_CHARS;
|
|
4454
4464
|
const maxLines = config?.max_output_lines ?? DEFAULT_MAX_LINES;
|
|
4455
4465
|
if (content.length > maxChars)
|
|
@@ -4461,6 +4471,16 @@ function shouldTruncate(content, config) {
|
|
|
4461
4471
|
return false;
|
|
4462
4472
|
}
|
|
4463
4473
|
function truncateOutput(content, config) {
|
|
4474
|
+
if (typeof content !== "string") {
|
|
4475
|
+
return {
|
|
4476
|
+
truncated: false,
|
|
4477
|
+
originalLength: 0,
|
|
4478
|
+
truncatedLength: 0,
|
|
4479
|
+
originalLines: 0,
|
|
4480
|
+
truncatedLines: 0,
|
|
4481
|
+
content: ""
|
|
4482
|
+
};
|
|
4483
|
+
}
|
|
4464
4484
|
const maxChars = config?.max_output_chars ?? DEFAULT_MAX_CHARS;
|
|
4465
4485
|
const maxLines = config?.max_output_lines ?? DEFAULT_MAX_LINES;
|
|
4466
4486
|
const headLines = config?.preserve_head_lines ?? DEFAULT_HEAD_LINES;
|
|
@@ -4632,6 +4652,8 @@ function readMemoryRefs(projectDir, limit = 10) {
|
|
|
4632
4652
|
const fullPath = path7.join(subdirPath, file);
|
|
4633
4653
|
const stat = fs7.statSync(fullPath);
|
|
4634
4654
|
const raw = fs7.readFileSync(fullPath, "utf-8");
|
|
4655
|
+
if (typeof raw !== "string" || !raw.trim())
|
|
4656
|
+
continue;
|
|
4635
4657
|
let summary;
|
|
4636
4658
|
const ext = path7.extname(file);
|
|
4637
4659
|
if (ext === ".md") {
|
|
@@ -4639,7 +4661,8 @@ function readMemoryRefs(projectDir, limit = 10) {
|
|
|
4639
4661
|
summary = headingMatch ? headingMatch[1] : raw.substring(0, 100).trim();
|
|
4640
4662
|
} else if (ext === ".json") {
|
|
4641
4663
|
const parsed = JSON.parse(raw);
|
|
4642
|
-
|
|
4664
|
+
const content = parsed.content;
|
|
4665
|
+
summary = parsed.summary || parsed.title || (typeof content === "string" ? content.substring(0, 100) : "") || "";
|
|
4643
4666
|
} else {
|
|
4644
4667
|
summary = raw.substring(0, 100).trim();
|
|
4645
4668
|
}
|
|
@@ -4700,6 +4723,9 @@ Recent Memory References:`);
|
|
|
4700
4723
|
lines.push("</compaction-context>");
|
|
4701
4724
|
const block = lines.join(`
|
|
4702
4725
|
`);
|
|
4726
|
+
if (typeof block !== "string")
|
|
4727
|
+
return `<compaction-context>
|
|
4728
|
+
</compaction-context>`;
|
|
4703
4729
|
if (block.length > maxChars) {
|
|
4704
4730
|
return block.substring(0, maxChars) + `
|
|
4705
4731
|
... [compaction context truncated]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
date: 2026-02-15
|
|
3
|
+
phase: implementing
|
|
4
|
+
branch: main
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Handoff: CliKit Plugin Installation & Runtime Fixes
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Status Summary
|
|
12
|
+
|
|
13
|
+
CliKit plugin published to npm (v0.1.9 → v0.2.0). Fixed snap/bun path detection and multiple runtime errors. **CRITICAL ISSUE IDENTIFIED**: Installer overwrites `opencode.json` instead of merging with existing config, deleting user's existing settings. Multiple `.split()`, `.substring()` errors still occurring in hooks due to non-string arguments.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Artifacts
|
|
18
|
+
|
|
19
|
+
| Type | Path | Status |
|
|
20
|
+
|------|------|--------|
|
|
21
|
+
| Handoff | `.opencode/memory/handoffs/2026-02-15-installing.md` | 📚 Previous |
|
|
22
|
+
| Config | `~/.config/opencode/opencode.json` | ⚠️ Gets overwritten |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Task Status
|
|
27
|
+
|
|
28
|
+
### ✅ Completed
|
|
29
|
+
- [x] T-001-T-007: Plugin implementation (10 agents, 19 commands, 48 skills, 14 hooks, 6 tools)
|
|
30
|
+
- [x] Fix snap/bun path detection using `SNAP_REAL_HOME` and regex fallback
|
|
31
|
+
- [x] Fix `buildErrorNotification` to handle Error objects
|
|
32
|
+
- [x] Fix `buildIdleNotification` to handle non-string sessionId
|
|
33
|
+
- [x] Fix `checkCommentDensity` and `hasExcessiveAIComments` to handle non-string content
|
|
34
|
+
- [x] Published v0.1.7, v0.1.8, v0.1.9, v0.2.0 to npm
|
|
35
|
+
|
|
36
|
+
### 🔄 In Progress
|
|
37
|
+
- [ ] **CRITICAL**: Fix installer to MERGE with existing opencode.json instead of overwriting
|
|
38
|
+
- **Current state:** `parseConfig()` reads existing config, but may not preserve all keys
|
|
39
|
+
- **User report:** "nó ghi đè file opencode.json làm xóa config có sẵn của tôi"
|
|
40
|
+
- **Root cause:** Need to deep merge, not shallow overwrite
|
|
41
|
+
|
|
42
|
+
- [ ] Fix remaining runtime errors in hooks
|
|
43
|
+
- **Files needing defensive type checks:**
|
|
44
|
+
- `src/hooks/truncator.ts` - `shouldTruncate()`, `truncateOutput()`
|
|
45
|
+
- `src/hooks/security-check.ts` - `scanContentForSecrets()`
|
|
46
|
+
- `src/hooks/compaction.ts` - multiple `.substring()` calls
|
|
47
|
+
- **Pattern:** Add `if (typeof content !== "string") return defaultResult;` at function start
|
|
48
|
+
|
|
49
|
+
### 📋 Not Started
|
|
50
|
+
- [ ] T-009: Verify plugin loads when OpenCode starts
|
|
51
|
+
- [ ] T-010: Test installation in fresh environment
|
|
52
|
+
- [ ] Add automated tests for installer
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Files Modified
|
|
57
|
+
|
|
58
|
+
| File | Status | Notes |
|
|
59
|
+
|------|--------|-------|
|
|
60
|
+
| `src/cli.ts` | Modified | Added `getRealHome()` for snap path detection |
|
|
61
|
+
| `src/hooks/session-notification.ts` | Modified | Fixed `buildIdleNotification`, `buildErrorNotification` |
|
|
62
|
+
| `src/hooks/comment-checker.ts` | Modified | Added type guards for content param |
|
|
63
|
+
| `src/index.ts` | Modified | Pass raw error to `buildErrorNotification` |
|
|
64
|
+
| `package.json` | Modified | Version bumps to 0.2.0 |
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Git State
|
|
69
|
+
|
|
70
|
+
- **Branch:** `main`
|
|
71
|
+
- **Last commit:** `31b200c` - Update README with simplified installation
|
|
72
|
+
- **Uncommitted changes:** Yes (5 modified files)
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Known Issues
|
|
77
|
+
|
|
78
|
+
1. **CRITICAL: Installer overwrites opencode.json** - deletes user's existing config (mcp, provider settings)
|
|
79
|
+
- Need to deep merge instead of shallow merge
|
|
80
|
+
- Should backup existing config before modifying
|
|
81
|
+
|
|
82
|
+
2. **Runtime TypeError: `.split()`, `.substring()` on undefined**
|
|
83
|
+
- Multiple hooks receive non-string arguments from OpenCode events
|
|
84
|
+
- Need defensive type checks in ALL functions that call string methods
|
|
85
|
+
|
|
86
|
+
3. **Snap/bun path mismatch** - partially fixed but user still sees wrong path
|
|
87
|
+
- `bun x` may cache old versions
|
|
88
|
+
- Need to verify `SNAP_REAL_HOME` detection works in user's environment
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Next Steps
|
|
93
|
+
|
|
94
|
+
1. [ ] **FIX INSTALLER** - Merge with existing config, don't overwrite:
|
|
95
|
+
```typescript
|
|
96
|
+
// In cli.ts, preserve existing config keys
|
|
97
|
+
const existingConfig = parseConfig(configPath);
|
|
98
|
+
const newConfig = { ...existingConfig, plugin: filteredPlugins };
|
|
99
|
+
writeConfig(configPath, newConfig);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
2. [ ] Add defensive type checks to all hooks that use string methods:
|
|
103
|
+
- `truncator.ts`: `shouldTruncate()`, `truncateOutput()`
|
|
104
|
+
- `security-check.ts`: `scanContentForSecrets()`
|
|
105
|
+
- `compaction.ts`: All substring calls
|
|
106
|
+
|
|
107
|
+
3. [ ] Publish v0.2.1 with fixes
|
|
108
|
+
|
|
109
|
+
4. [ ] Test: `bun x clikit-plugin@latest install` should preserve existing config
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Context for Resumption
|
|
114
|
+
|
|
115
|
+
### The Core Problem
|
|
116
|
+
The installer's `writeConfig()` function writes the entire config object to disk. The current code does spread, but needs to ensure `parseConfig()` returns complete existing config.
|
|
117
|
+
|
|
118
|
+
### Solution Approach
|
|
119
|
+
```typescript
|
|
120
|
+
// cli.ts - install() function:
|
|
121
|
+
const existingConfig = parseConfig(configPath) || {};
|
|
122
|
+
const plugins = Array.isArray(existingConfig.plugin) ? [...existingConfig.plugin] : [];
|
|
123
|
+
// ... modify plugins array ...
|
|
124
|
+
config.plugin = filteredPlugins;
|
|
125
|
+
// existingConfig already has all keys, we just update plugin
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Files to Review
|
|
129
|
+
- `src/cli.ts:55-108` — Installer logic
|
|
130
|
+
- `src/hooks/truncator.ts` — Needs type guards
|
|
131
|
+
- `src/hooks/security-check.ts` — Needs type guards
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Resume Command
|
|
136
|
+
|
|
137
|
+
To resume, use `/resume` and it will:
|
|
138
|
+
1. Load this handoff
|
|
139
|
+
2. Check for drift
|
|
140
|
+
3. Propose fixing installer merge logic first
|