clikit-plugin 0.2.5 → 0.2.7
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/hooks/auto-format.d.ts +2 -2
- package/dist/hooks/auto-format.d.ts.map +1 -1
- package/dist/hooks/git-guard.d.ts +1 -1
- package/dist/hooks/git-guard.d.ts.map +1 -1
- package/dist/hooks/subagent-question-blocker.d.ts +2 -2
- package/dist/hooks/subagent-question-blocker.d.ts.map +1 -1
- package/dist/hooks/swarm-enforcer.d.ts +3 -3
- package/dist/hooks/swarm-enforcer.d.ts.map +1 -1
- package/dist/hooks/typecheck-gate.d.ts +4 -4
- package/dist/hooks/typecheck-gate.d.ts.map +1 -1
- package/dist/index.js +28 -13
- package/memory/handoffs/2026-02-15-complete-audit.md +136 -0
- package/package.json +1 -1
|
@@ -23,8 +23,8 @@ type FormatterEntry = {
|
|
|
23
23
|
command: (file: string) => string;
|
|
24
24
|
};
|
|
25
25
|
export declare function detectFormatter(projectDir: string): FormatterEntry | undefined;
|
|
26
|
-
export declare function shouldFormat(filePath:
|
|
27
|
-
export declare function runFormatter(filePath:
|
|
26
|
+
export declare function shouldFormat(filePath: unknown, extensions?: string[]): boolean;
|
|
27
|
+
export declare function runFormatter(filePath: unknown, projectDir: unknown, formatterOverride?: string): FormatResult;
|
|
28
28
|
export declare function formatAutoFormatLog(result: FormatResult): string;
|
|
29
29
|
export {};
|
|
30
30
|
//# sourceMappingURL=auto-format.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-format.d.ts","sourceRoot":"","sources":["../../src/hooks/auto-format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC,CAAC;AAsBF,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CA2B9E;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"auto-format.d.ts","sourceRoot":"","sources":["../../src/hooks/auto-format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC,CAAC;AAsBF,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CA2B9E;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAK9E;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,OAAO,EACnB,iBAAiB,CAAC,EAAE,MAAM,GACzB,YAAY,CAqCd;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAKhE"}
|
|
@@ -9,6 +9,6 @@ export interface GitGuardResult {
|
|
|
9
9
|
command: string;
|
|
10
10
|
reason?: string;
|
|
11
11
|
}
|
|
12
|
-
export declare function checkDangerousCommand(command:
|
|
12
|
+
export declare function checkDangerousCommand(command: unknown, allowForceWithLease?: boolean): GitGuardResult;
|
|
13
13
|
export declare function formatBlockedWarning(result: GitGuardResult): string;
|
|
14
14
|
//# sourceMappingURL=git-guard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-guard.d.ts","sourceRoot":"","sources":["../../src/hooks/git-guard.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"git-guard.d.ts","sourceRoot":"","sources":["../../src/hooks/git-guard.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,mBAAmB,UAAO,GAAG,cAAc,CAalG;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEnE"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Subagents should execute their task autonomously without clarification.
|
|
6
6
|
* Runs on tool.execute.before for Task tool.
|
|
7
7
|
*/
|
|
8
|
-
export declare function containsQuestion(text:
|
|
9
|
-
export declare function isSubagentTool(toolName:
|
|
8
|
+
export declare function containsQuestion(text: unknown): boolean;
|
|
9
|
+
export declare function isSubagentTool(toolName: unknown): boolean;
|
|
10
10
|
export declare function formatBlockerWarning(): string;
|
|
11
11
|
//# sourceMappingURL=subagent-question-blocker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent-question-blocker.d.ts","sourceRoot":"","sources":["../../src/hooks/subagent-question-blocker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAiBH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"subagent-question-blocker.d.ts","sourceRoot":"","sources":["../../src/hooks/subagent-question-blocker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAiBH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAMvD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAEzD;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}
|
|
@@ -24,8 +24,8 @@ export interface EnforcementResult {
|
|
|
24
24
|
reason?: string;
|
|
25
25
|
suggestion?: string;
|
|
26
26
|
}
|
|
27
|
-
export declare function isFileInScope(filePath:
|
|
28
|
-
export declare function checkEditPermission(filePath:
|
|
29
|
-
export declare function extractFileFromToolInput(toolName:
|
|
27
|
+
export declare function isFileInScope(filePath: unknown, scope: TaskScope): boolean;
|
|
28
|
+
export declare function checkEditPermission(filePath: unknown, scope: TaskScope | undefined, config?: SwarmEnforcerConfig): EnforcementResult;
|
|
29
|
+
export declare function extractFileFromToolInput(toolName: unknown, input: Record<string, unknown>): string | undefined;
|
|
30
30
|
export declare function formatEnforcementWarning(result: EnforcementResult): string;
|
|
31
31
|
//# sourceMappingURL=swarm-enforcer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swarm-enforcer.d.ts","sourceRoot":"","sources":["../../src/hooks/swarm-enforcer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"swarm-enforcer.d.ts","sourceRoot":"","sources":["../../src/hooks/swarm-enforcer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,SAAS,GACf,OAAO,CAsBT;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,MAAM,CAAC,EAAE,mBAAmB,GAC3B,iBAAiB,CAyBnB;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,GAAG,SAAS,CA6BpB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAM1E"}
|
|
@@ -23,9 +23,9 @@ export interface TypeCheckResult {
|
|
|
23
23
|
errors: TypeDiagnostic[];
|
|
24
24
|
checkedFile: string;
|
|
25
25
|
}
|
|
26
|
-
export declare function isTypeScriptFile(filePath:
|
|
27
|
-
export declare function findTsConfig(projectDir:
|
|
28
|
-
export declare function hasTscInstalled(projectDir:
|
|
29
|
-
export declare function runTypeCheck(filePath:
|
|
26
|
+
export declare function isTypeScriptFile(filePath: unknown): boolean;
|
|
27
|
+
export declare function findTsConfig(projectDir: unknown, override?: string): string | undefined;
|
|
28
|
+
export declare function hasTscInstalled(projectDir: unknown): boolean;
|
|
29
|
+
export declare function runTypeCheck(filePath: unknown, projectDir: unknown, config?: TypeCheckConfig): TypeCheckResult;
|
|
30
30
|
export declare function formatTypeCheckWarning(result: TypeCheckResult): string;
|
|
31
31
|
//# sourceMappingURL=typecheck-gate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typecheck-gate.d.ts","sourceRoot":"","sources":["../../src/hooks/typecheck-gate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"typecheck-gate.d.ts","sourceRoot":"","sources":["../../src/hooks/typecheck-gate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAG3D;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgBvF;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,CAY5D;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,OAAO,EACnB,MAAM,CAAC,EAAE,eAAe,GACvB,eAAe,CAiCjB;AAsCD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAetE"}
|
package/dist/index.js
CHANGED
|
@@ -3884,6 +3884,9 @@ var DANGEROUS_PATTERNS = [
|
|
|
3884
3884
|
{ pattern: /git\s+branch\s+-D/, reason: "Force-deleting branch may lose unmerged work" }
|
|
3885
3885
|
];
|
|
3886
3886
|
function checkDangerousCommand(command, allowForceWithLease = true) {
|
|
3887
|
+
if (typeof command !== "string") {
|
|
3888
|
+
return { blocked: false, command: "" };
|
|
3889
|
+
}
|
|
3887
3890
|
for (const { pattern, reason } of DANGEROUS_PATTERNS) {
|
|
3888
3891
|
if (pattern.test(command)) {
|
|
3889
3892
|
if (allowForceWithLease && /--force-with-lease/.test(command)) {
|
|
@@ -3971,11 +3974,14 @@ var QUESTION_INDICATORS = [
|
|
|
3971
3974
|
"could you provide"
|
|
3972
3975
|
];
|
|
3973
3976
|
function containsQuestion(text) {
|
|
3977
|
+
if (typeof text !== "string" || !text) {
|
|
3978
|
+
return false;
|
|
3979
|
+
}
|
|
3974
3980
|
const lower = text.toLowerCase();
|
|
3975
3981
|
return QUESTION_INDICATORS.some((indicator) => lower.includes(indicator));
|
|
3976
3982
|
}
|
|
3977
3983
|
function isSubagentTool(toolName) {
|
|
3978
|
-
return toolName === "task" || toolName === "Task";
|
|
3984
|
+
return typeof toolName === "string" && (toolName === "task" || toolName === "Task");
|
|
3979
3985
|
}
|
|
3980
3986
|
function formatBlockerWarning() {
|
|
3981
3987
|
return `[CliKit:subagent-blocker] Subagent attempted to ask clarifying questions. Subagents should execute autonomously.`;
|
|
@@ -4263,23 +4269,26 @@ function detectFormatter(projectDir) {
|
|
|
4263
4269
|
return;
|
|
4264
4270
|
}
|
|
4265
4271
|
function shouldFormat(filePath, extensions) {
|
|
4272
|
+
if (typeof filePath !== "string")
|
|
4273
|
+
return false;
|
|
4266
4274
|
const ext = path5.extname(filePath).toLowerCase();
|
|
4267
4275
|
const allowedExts = extensions || DEFAULT_EXTENSIONS;
|
|
4268
4276
|
return allowedExts.includes(ext);
|
|
4269
4277
|
}
|
|
4270
4278
|
function runFormatter(filePath, projectDir, formatterOverride) {
|
|
4279
|
+
const safePath = typeof filePath === "string" && filePath ? filePath : "";
|
|
4271
4280
|
const safeDir = typeof projectDir === "string" && projectDir ? projectDir : process.cwd();
|
|
4272
4281
|
const formatter = formatterOverride ? FORMATTERS.find((f) => f.name === formatterOverride) : detectFormatter(safeDir);
|
|
4273
|
-
if (!formatter) {
|
|
4282
|
+
if (!formatter || !safePath) {
|
|
4274
4283
|
return {
|
|
4275
4284
|
formatted: false,
|
|
4276
|
-
file:
|
|
4285
|
+
file: safePath,
|
|
4277
4286
|
formatter: "none",
|
|
4278
|
-
error: "No formatter detected"
|
|
4287
|
+
error: formatter ? "No file path provided" : "No formatter detected"
|
|
4279
4288
|
};
|
|
4280
4289
|
}
|
|
4281
4290
|
try {
|
|
4282
|
-
const cmd = formatter.command(
|
|
4291
|
+
const cmd = formatter.command(safePath);
|
|
4283
4292
|
execSync2(cmd, {
|
|
4284
4293
|
cwd: safeDir,
|
|
4285
4294
|
timeout: 1e4,
|
|
@@ -4287,13 +4296,13 @@ function runFormatter(filePath, projectDir, formatterOverride) {
|
|
|
4287
4296
|
});
|
|
4288
4297
|
return {
|
|
4289
4298
|
formatted: true,
|
|
4290
|
-
file:
|
|
4299
|
+
file: safePath,
|
|
4291
4300
|
formatter: formatter.name
|
|
4292
4301
|
};
|
|
4293
4302
|
} catch (err) {
|
|
4294
4303
|
return {
|
|
4295
4304
|
formatted: false,
|
|
4296
|
-
file:
|
|
4305
|
+
file: safePath,
|
|
4297
4306
|
formatter: formatter.name,
|
|
4298
4307
|
error: err instanceof Error ? err.message : String(err)
|
|
4299
4308
|
};
|
|
@@ -4311,16 +4320,19 @@ import * as path6 from "path";
|
|
|
4311
4320
|
import { execSync as execSync3 } from "child_process";
|
|
4312
4321
|
var TS_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts"];
|
|
4313
4322
|
function isTypeScriptFile(filePath) {
|
|
4323
|
+
if (typeof filePath !== "string")
|
|
4324
|
+
return false;
|
|
4314
4325
|
return TS_EXTENSIONS.includes(path6.extname(filePath).toLowerCase());
|
|
4315
4326
|
}
|
|
4316
4327
|
function findTsConfig(projectDir, override) {
|
|
4328
|
+
const safeDir = typeof projectDir === "string" && projectDir ? projectDir : process.cwd();
|
|
4317
4329
|
if (override) {
|
|
4318
|
-
const overridePath = path6.resolve(
|
|
4330
|
+
const overridePath = path6.resolve(safeDir, override);
|
|
4319
4331
|
return fs6.existsSync(overridePath) ? overridePath : undefined;
|
|
4320
4332
|
}
|
|
4321
4333
|
const candidates = ["tsconfig.json", "tsconfig.build.json"];
|
|
4322
4334
|
for (const candidate of candidates) {
|
|
4323
|
-
const fullPath = path6.join(
|
|
4335
|
+
const fullPath = path6.join(safeDir, candidate);
|
|
4324
4336
|
if (fs6.existsSync(fullPath)) {
|
|
4325
4337
|
return fullPath;
|
|
4326
4338
|
}
|
|
@@ -4328,10 +4340,11 @@ function findTsConfig(projectDir, override) {
|
|
|
4328
4340
|
return;
|
|
4329
4341
|
}
|
|
4330
4342
|
function runTypeCheck(filePath, projectDir, config) {
|
|
4343
|
+
const safePath = typeof filePath === "string" && filePath ? filePath : "";
|
|
4331
4344
|
const safeDir = typeof projectDir === "string" && projectDir ? projectDir : process.cwd();
|
|
4332
4345
|
const tsConfig = findTsConfig(safeDir, config?.tsconfig);
|
|
4333
4346
|
if (!tsConfig) {
|
|
4334
|
-
return { clean: true, errors: [], checkedFile:
|
|
4347
|
+
return { clean: true, errors: [], checkedFile: safePath };
|
|
4335
4348
|
}
|
|
4336
4349
|
try {
|
|
4337
4350
|
const tscCmd = `npx tsc --noEmit --pretty false -p "${tsConfig}"`;
|
|
@@ -4341,18 +4354,20 @@ function runTypeCheck(filePath, projectDir, config) {
|
|
|
4341
4354
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4342
4355
|
encoding: "utf-8"
|
|
4343
4356
|
});
|
|
4344
|
-
return { clean: true, errors: [], checkedFile:
|
|
4357
|
+
return { clean: true, errors: [], checkedFile: safePath };
|
|
4345
4358
|
} catch (err) {
|
|
4346
4359
|
const output = err instanceof Error && "stdout" in err ? String(err.stdout) : "";
|
|
4347
|
-
const errors = parseTscOutput(output,
|
|
4360
|
+
const errors = parseTscOutput(output, safePath);
|
|
4348
4361
|
return {
|
|
4349
4362
|
clean: errors.length === 0,
|
|
4350
4363
|
errors,
|
|
4351
|
-
checkedFile:
|
|
4364
|
+
checkedFile: safePath
|
|
4352
4365
|
};
|
|
4353
4366
|
}
|
|
4354
4367
|
}
|
|
4355
4368
|
function parseTscOutput(output, filterFile) {
|
|
4369
|
+
if (typeof output !== "string")
|
|
4370
|
+
return [];
|
|
4356
4371
|
const diagnostics = [];
|
|
4357
4372
|
const lines = output.split(`
|
|
4358
4373
|
`);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
date: 2026-02-15
|
|
3
|
+
phase: implementing
|
|
4
|
+
branch: main
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Handoff: CliKit Complete Type Guard Audit
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Status Summary
|
|
12
|
+
|
|
13
|
+
CliKit plugin v0.2.6 published with many type guards. Typecheck passes. However, there are still potential runtime issues where OpenCode passes non-string values that could cause `.toLowerCase()`, `.test()`, `.split()` etc. to fail.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Version History
|
|
18
|
+
|
|
19
|
+
| Version | Fix |
|
|
20
|
+
|---------|-----|
|
|
21
|
+
| 0.2.1 | Type guards in hooks (truncator, security-check, compaction) |
|
|
22
|
+
| 0.2.2 | Abort on JSON parse error |
|
|
23
|
+
| 0.2.3 | Fix JSONC comment stripping breaking URLs |
|
|
24
|
+
| 0.2.4 | Remove backup logic, fix `todos.filter` error |
|
|
25
|
+
| 0.2.5 | Fix `path.join` error - guards for projectDir/cwd params |
|
|
26
|
+
| 0.2.6 | Fix `text.toLowerCase` error in subagent-question-blocker |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Remaining Issues (Need Type Guards)
|
|
31
|
+
|
|
32
|
+
### 1. git-guard.ts: `checkDangerousCommand`
|
|
33
|
+
**Line 25:** `command: string` - no type guard
|
|
34
|
+
**Used:** `index.ts:221` - `toolInput.command as string | undefined`
|
|
35
|
+
**Risk:** `pattern.test(command)` fails if command is object
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Fix needed:
|
|
39
|
+
export function checkDangerousCommand(command: unknown, allowForceWithLease = true): GitGuardResult {
|
|
40
|
+
if (typeof command !== "string") {
|
|
41
|
+
return { blocked: false, command: "" };
|
|
42
|
+
}
|
|
43
|
+
// ... rest
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. swarm-enforcer.ts: `isFileInScope`
|
|
48
|
+
**Line 33-36:** `filePath: string` - no type guard
|
|
49
|
+
**Risk:** `path.resolve(filePath)` fails if filePath is not string
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Fix needed:
|
|
53
|
+
export function isFileInScope(filePath: unknown, scope: TaskScope): boolean {
|
|
54
|
+
if (typeof filePath !== "string") return false;
|
|
55
|
+
// ...
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. swarm-enforcer.ts: `checkEditPermission`
|
|
60
|
+
**Line 59-62:** `filePath: string` - no type guard
|
|
61
|
+
|
|
62
|
+
### 4. index.ts: Tool input casting
|
|
63
|
+
Multiple places where `toolInput.X as string | undefined` is used but the value could be an object:
|
|
64
|
+
- Line 218: `toolInput.command`
|
|
65
|
+
- Line 269: `toolInput.prompt`
|
|
66
|
+
- Line 335: `toolInput.filePath`
|
|
67
|
+
- Line 351: `toolInput.filePath`
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Files Modified (Uncommitted)
|
|
72
|
+
|
|
73
|
+
| File | Status |
|
|
74
|
+
|------|--------|
|
|
75
|
+
| `.opencode/package.json` | v0.2.6 |
|
|
76
|
+
| `.opencode/src/cli.ts` | Removed backup, fixed JSONC |
|
|
77
|
+
| `.opencode/src/config.ts` | Type guard for loadCliKitConfig |
|
|
78
|
+
| `.opencode/src/index.ts` | Array.isArray for todos |
|
|
79
|
+
| `.opencode/src/hooks/auto-format.ts` | Type guards |
|
|
80
|
+
| `.opencode/src/hooks/comment-checker.ts` | Type guards |
|
|
81
|
+
| `.opencode/src/hooks/compaction.ts` | Type guards |
|
|
82
|
+
| `.opencode/src/hooks/env-context.ts` | Type guards |
|
|
83
|
+
| `.opencode/src/hooks/security-check.ts` | Type guards |
|
|
84
|
+
| `.opencode/src/hooks/session-notification.ts` | Type guards |
|
|
85
|
+
| `.opencode/src/hooks/subagent-question-blocker.ts` | Type guards |
|
|
86
|
+
| `.opencode/src/hooks/swarm-enforcer.ts` | Partial (extractFileFromToolInput only) |
|
|
87
|
+
| `.opencode/src/hooks/todo-enforcer.ts` | Type guards |
|
|
88
|
+
| `.opencode/src/hooks/truncator.ts` | Type guards |
|
|
89
|
+
| `.opencode/src/hooks/typecheck-gate.ts` | Type guards |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Git State
|
|
94
|
+
|
|
95
|
+
- **Branch:** `main`
|
|
96
|
+
- **Last commit:** `31b200c` - Update README with simplified installation
|
|
97
|
+
- **Uncommitted changes:** 15 files
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Next Steps
|
|
102
|
+
|
|
103
|
+
1. [ ] Add type guard to `git-guard.ts:checkDangerousCommand(command: unknown)`
|
|
104
|
+
2. [ ] Add type guard to `swarm-enforcer.ts:isFileInScope(filePath: unknown)`
|
|
105
|
+
3. [ ] Add type guard to `swarm-enforcer.ts:checkEditPermission(filePath: unknown)`
|
|
106
|
+
4. [ ] Run typecheck
|
|
107
|
+
5. [ ] Publish v0.2.7
|
|
108
|
+
6. [ ] Test: `opencode` starts without errors
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Pattern for Type Guards
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// Before (breaks on non-string):
|
|
116
|
+
export function myFunc(content: string): Result {
|
|
117
|
+
return content.split("\n"); // CRASH if content is object
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// After (safe):
|
|
121
|
+
export function myFunc(content: unknown): Result {
|
|
122
|
+
if (typeof content !== "string") {
|
|
123
|
+
return defaultResult; // Safe fallback
|
|
124
|
+
}
|
|
125
|
+
return content.split("\n");
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Resume Command
|
|
132
|
+
|
|
133
|
+
To resume, use `/resume` and it will:
|
|
134
|
+
1. Load this handoff
|
|
135
|
+
2. Propose fixing remaining type guards in git-guard.ts and swarm-enforcer.ts
|
|
136
|
+
3. Publish v0.2.7
|