claudekit-cli 3.41.4-dev.51 → 3.41.4-dev.53
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/cli-manifest.json +2 -2
- package/dist/index.js +93 -52
- package/package.json +1 -1
package/cli-manifest.json
CHANGED
package/dist/index.js
CHANGED
|
@@ -53623,6 +53623,24 @@ function sanitizeOutput(obj, rules) {
|
|
|
53623
53623
|
return result;
|
|
53624
53624
|
}
|
|
53625
53625
|
|
|
53626
|
+
/**
|
|
53627
|
+
* True when the given event's scrub rules allow a permissionDecision of "deny".
|
|
53628
|
+
* Used to translate Claude Code's exit-code protocol (exit 2 = block) into
|
|
53629
|
+
* Codex's JSON protocol ({permissionDecision: "deny"}).
|
|
53630
|
+
*/
|
|
53631
|
+
function eventSupportsDeny(rules) {
|
|
53632
|
+
if (!rules || rules.allowedPermissionValues === null) return false;
|
|
53633
|
+
return rules.allowedPermissionValues.indexOf("deny") !== -1;
|
|
53634
|
+
}
|
|
53635
|
+
|
|
53636
|
+
function emitDeny(reason) {
|
|
53637
|
+
process.stdout.write(JSON.stringify({
|
|
53638
|
+
permissionDecision: "deny",
|
|
53639
|
+
reason: reason && reason.length > 0 ? reason : "Hook blocked this operation",
|
|
53640
|
+
}));
|
|
53641
|
+
process.exit(0);
|
|
53642
|
+
}
|
|
53643
|
+
|
|
53626
53644
|
function main() {
|
|
53627
53645
|
// Collect stdin
|
|
53628
53646
|
const stdinChunks = [];
|
|
@@ -53630,6 +53648,7 @@ function main() {
|
|
|
53630
53648
|
process.stdin.on("end", () => {
|
|
53631
53649
|
const stdinData = Buffer.concat(stdinChunks).toString("utf8");
|
|
53632
53650
|
const event = getEventFromStdin(stdinData);
|
|
53651
|
+
const rules = event && SCRUB_RULES[event];
|
|
53633
53652
|
|
|
53634
53653
|
// Spawn original hook with same stdin/env
|
|
53635
53654
|
const result = spawnSync(process.execPath, [ORIGINAL_HOOK], {
|
|
@@ -53645,33 +53664,57 @@ function main() {
|
|
|
53645
53664
|
process.exit(1);
|
|
53646
53665
|
}
|
|
53647
53666
|
|
|
53648
|
-
|
|
53649
|
-
|
|
53650
|
-
|
|
53651
|
-
|
|
53652
|
-
|
|
53667
|
+
const stderrText = (result.stderr || "").toString();
|
|
53668
|
+
const rawOutput = (result.stdout || "").toString();
|
|
53669
|
+
const exitCode = result.status ?? 1;
|
|
53670
|
+
// Claude Code protocol: exit 2 + stderr = block. Codex expects JSON instead.
|
|
53671
|
+
// Translate only for events where the Codex capability table allows "deny".
|
|
53672
|
+
const isBlockSignal = exitCode === 2 && eventSupportsDeny(rules);
|
|
53653
53673
|
|
|
53654
|
-
//
|
|
53674
|
+
// No stdout: either silent allow (exit 0) or Claude-style block (exit 2).
|
|
53655
53675
|
if (!rawOutput.trim()) {
|
|
53656
|
-
|
|
53676
|
+
if (isBlockSignal) {
|
|
53677
|
+
return emitDeny(stderrText.trim());
|
|
53678
|
+
}
|
|
53679
|
+
// Non-block failure or plain allow: forward stderr and pass exit code through.
|
|
53680
|
+
if (stderrText) process.stderr.write(stderrText);
|
|
53681
|
+
process.exit(exitCode);
|
|
53657
53682
|
}
|
|
53658
53683
|
|
|
53659
|
-
// Try to parse
|
|
53684
|
+
// Try to parse stdout as JSON.
|
|
53660
53685
|
let parsed;
|
|
53661
53686
|
try {
|
|
53662
53687
|
parsed = JSON.parse(rawOutput);
|
|
53663
53688
|
} catch {
|
|
53664
|
-
//
|
|
53689
|
+
// Non-JSON stdout. If this is a Claude block signal, treat the stdout
|
|
53690
|
+
// (or stderr) as the deny reason. Otherwise forward unchanged.
|
|
53691
|
+
if (isBlockSignal) {
|
|
53692
|
+
const reason = rawOutput.trim() || stderrText.trim();
|
|
53693
|
+
return emitDeny(reason);
|
|
53694
|
+
}
|
|
53695
|
+
if (stderrText) process.stderr.write(stderrText);
|
|
53665
53696
|
process.stdout.write(rawOutput);
|
|
53666
|
-
process.exit(
|
|
53697
|
+
process.exit(exitCode);
|
|
53667
53698
|
}
|
|
53668
53699
|
|
|
53700
|
+
// Forward stderr for JSON-emitting hooks (diagnostic output). We still
|
|
53701
|
+
// scrub and re-emit the JSON to Codex's stdout.
|
|
53702
|
+
if (stderrText) process.stderr.write(stderrText);
|
|
53703
|
+
|
|
53669
53704
|
// Apply scrub rules for the detected event
|
|
53670
|
-
const rules = event && SCRUB_RULES[event];
|
|
53671
53705
|
const sanitized = rules ? sanitizeOutput(parsed, rules) : parsed;
|
|
53672
53706
|
|
|
53707
|
+
// If the hook signalled block via exit 2 but didn't emit a deny decision
|
|
53708
|
+
// in the JSON, translate — otherwise Codex ignores the exit code.
|
|
53709
|
+
if (isBlockSignal && (!sanitized || sanitized.permissionDecision !== "deny")) {
|
|
53710
|
+
return emitDeny(stderrText.trim());
|
|
53711
|
+
}
|
|
53712
|
+
|
|
53673
53713
|
process.stdout.write(JSON.stringify(sanitized));
|
|
53674
|
-
|
|
53714
|
+
// When the hook already emitted a valid deny JSON but also exited 2,
|
|
53715
|
+
// exit 0 so Codex treats the deny as authoritative (consistent with
|
|
53716
|
+
// emitDeny's exit-0 contract).
|
|
53717
|
+
process.exit(isBlockSignal ? 0 : exitCode);
|
|
53675
53718
|
});
|
|
53676
53719
|
}
|
|
53677
53720
|
|
|
@@ -53811,7 +53854,7 @@ var init_gemini_hook_event_map = __esm(() => {
|
|
|
53811
53854
|
import { existsSync as existsSync25 } from "node:fs";
|
|
53812
53855
|
import { mkdir as mkdir11, readFile as readFile21, rename as rename7, rm as rm5, writeFile as writeFile12 } from "node:fs/promises";
|
|
53813
53856
|
import { homedir as homedir27 } from "node:os";
|
|
53814
|
-
import { basename as basename11, dirname as dirname12,
|
|
53857
|
+
import { basename as basename11, dirname as dirname12, join as join39, resolve as resolve15 } from "node:path";
|
|
53815
53858
|
function validateHooksSectionShape(value) {
|
|
53816
53859
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
53817
53860
|
return "hooks must be a non-null object";
|
|
@@ -53974,33 +54017,31 @@ async function mergeHooksIntoSettings(targetSettingsPath, newHooks) {
|
|
|
53974
54017
|
}
|
|
53975
54018
|
return { backupPath };
|
|
53976
54019
|
}
|
|
53977
|
-
function extractLeadingAbsolutePath(command) {
|
|
53978
|
-
const trimmed = command.trim();
|
|
53979
|
-
if (trimmed.length === 0)
|
|
53980
|
-
return null;
|
|
53981
|
-
const firstToken = trimmed.split(/\s+/)[0];
|
|
53982
|
-
if (!firstToken)
|
|
53983
|
-
return null;
|
|
53984
|
-
if (!isAbsolute5(firstToken))
|
|
53985
|
-
return null;
|
|
53986
|
-
return firstToken;
|
|
53987
|
-
}
|
|
53988
54020
|
function isCkManagedHookPath(absPath) {
|
|
53989
54021
|
const normalized = absPath.replace(/\\/g, "/");
|
|
53990
54022
|
return normalized.includes("/.claude/hooks/") || normalized.includes("/.codex/hooks/") || normalized.includes("/.gemini/hooks/");
|
|
53991
54023
|
}
|
|
54024
|
+
function extractAbsolutePaths(command) {
|
|
54025
|
+
const matches = [];
|
|
54026
|
+
const pathPattern = /(?:^|[\s"'(])(\/[^\s"'()]+)/g;
|
|
54027
|
+
let match = pathPattern.exec(command);
|
|
54028
|
+
while (match !== null) {
|
|
54029
|
+
matches.push(match[1]);
|
|
54030
|
+
match = pathPattern.exec(command);
|
|
54031
|
+
}
|
|
54032
|
+
return matches;
|
|
54033
|
+
}
|
|
53992
54034
|
function pruneStaleFileHooks(existing) {
|
|
53993
54035
|
const result = {};
|
|
53994
54036
|
for (const [event, groups] of Object.entries(existing)) {
|
|
53995
54037
|
const prunedGroups = [];
|
|
53996
54038
|
for (const group of groups) {
|
|
53997
54039
|
const survivingHooks = group.hooks.filter((h2) => {
|
|
53998
|
-
const
|
|
53999
|
-
|
|
54000
|
-
|
|
54001
|
-
if (!isCkManagedHookPath(path3))
|
|
54040
|
+
const paths = extractAbsolutePaths(h2.command);
|
|
54041
|
+
const ckPaths = paths.filter(isCkManagedHookPath);
|
|
54042
|
+
if (ckPaths.length === 0)
|
|
54002
54043
|
return true;
|
|
54003
|
-
return existsSync25(
|
|
54044
|
+
return ckPaths.some((p) => existsSync25(p));
|
|
54004
54045
|
});
|
|
54005
54046
|
if (survivingHooks.length > 0) {
|
|
54006
54047
|
prunedGroups.push({ ...group, hooks: survivingHooks });
|
|
@@ -57777,13 +57818,13 @@ var init_plan_scanner = () => {};
|
|
|
57777
57818
|
|
|
57778
57819
|
// src/domains/plan-parser/plan-scope.ts
|
|
57779
57820
|
import { homedir as homedir30 } from "node:os";
|
|
57780
|
-
import { isAbsolute as
|
|
57821
|
+
import { isAbsolute as isAbsolute5, join as join44, relative as relative9, resolve as resolve18 } from "node:path";
|
|
57781
57822
|
function resolveConfiguredDir(configuredPath, baseDir) {
|
|
57782
57823
|
const trimmed = configuredPath?.trim();
|
|
57783
57824
|
if (!trimmed) {
|
|
57784
57825
|
return join44(baseDir, DEFAULT_PLANS_DIRNAME);
|
|
57785
57826
|
}
|
|
57786
|
-
return
|
|
57827
|
+
return isAbsolute5(trimmed) ? resolve18(trimmed) : resolve18(baseDir, trimmed);
|
|
57787
57828
|
}
|
|
57788
57829
|
function resolveProjectPlansDir(projectRoot, config) {
|
|
57789
57830
|
return resolveConfiguredDir(config?.paths?.plans, projectRoot);
|
|
@@ -57798,7 +57839,7 @@ function isWithinDir(targetPath, baseDir) {
|
|
|
57798
57839
|
const resolvedTarget = resolve18(targetPath);
|
|
57799
57840
|
const resolvedBase = resolve18(baseDir);
|
|
57800
57841
|
const relativePath = relative9(resolvedBase, resolvedTarget);
|
|
57801
|
-
return relativePath === "" || !relativePath.startsWith("..") && relativePath !== ".." && !
|
|
57842
|
+
return relativePath === "" || !relativePath.startsWith("..") && relativePath !== ".." && !isAbsolute5(relativePath);
|
|
57802
57843
|
}
|
|
57803
57844
|
function inferPlanScopeForDir(planDir, config) {
|
|
57804
57845
|
return isWithinDir(planDir, resolveGlobalPlansDir(config)) ? "global" : "project";
|
|
@@ -57807,7 +57848,7 @@ function parsePlanReference(reference, defaultScope) {
|
|
|
57807
57848
|
const trimmed = reference.trim();
|
|
57808
57849
|
const normalizePlanId = (rawPlanId) => {
|
|
57809
57850
|
const planId = rawPlanId.trim();
|
|
57810
|
-
const valid = planId.length > 0 && !
|
|
57851
|
+
const valid = planId.length > 0 && !isAbsolute5(planId) && !planId.includes("/") && !planId.includes("\\") && PLAN_ID_PATTERN.test(planId);
|
|
57811
57852
|
return { planId, valid };
|
|
57812
57853
|
};
|
|
57813
57854
|
if (trimmed.startsWith(GLOBAL_PLAN_PREFIX)) {
|
|
@@ -58588,7 +58629,7 @@ import {
|
|
|
58588
58629
|
unlinkSync,
|
|
58589
58630
|
writeFileSync as writeFileSync4
|
|
58590
58631
|
} from "node:fs";
|
|
58591
|
-
import { dirname as dirname18, isAbsolute as
|
|
58632
|
+
import { dirname as dirname18, isAbsolute as isAbsolute6, join as join47, parse as parse2, relative as relative10, resolve as resolve19 } from "node:path";
|
|
58592
58633
|
function createEmptyRegistry() {
|
|
58593
58634
|
return {
|
|
58594
58635
|
version: 1,
|
|
@@ -58643,7 +58684,7 @@ function migrateFromProjectLocal(cwd2, globalPath) {
|
|
|
58643
58684
|
}
|
|
58644
58685
|
}
|
|
58645
58686
|
function normalizeRegistryDir(cwd2, dir) {
|
|
58646
|
-
const absoluteDir =
|
|
58687
|
+
const absoluteDir = isAbsolute6(dir) ? dir : resolve19(cwd2, dir);
|
|
58647
58688
|
const relativeDir = relative10(cwd2, absoluteDir) || dir;
|
|
58648
58689
|
return relativeDir.replace(/\\/g, "/");
|
|
58649
58690
|
}
|
|
@@ -62203,7 +62244,7 @@ var package_default;
|
|
|
62203
62244
|
var init_package = __esm(() => {
|
|
62204
62245
|
package_default = {
|
|
62205
62246
|
name: "claudekit-cli",
|
|
62206
|
-
version: "3.41.4-dev.
|
|
62247
|
+
version: "3.41.4-dev.53",
|
|
62207
62248
|
description: "CLI tool for bootstrapping and updating ClaudeKit projects",
|
|
62208
62249
|
type: "module",
|
|
62209
62250
|
repository: {
|
|
@@ -82431,11 +82472,11 @@ init_path_resolver();
|
|
|
82431
82472
|
init_zod();
|
|
82432
82473
|
var import_fs_extra9 = __toESM(require_lib3(), 1);
|
|
82433
82474
|
import { mkdir as mkdir19, readdir as readdir17, readlink, rename as rename10, symlink } from "node:fs/promises";
|
|
82434
|
-
import { basename as basename21, dirname as dirname28, isAbsolute as
|
|
82475
|
+
import { basename as basename21, dirname as dirname28, isAbsolute as isAbsolute7, join as join71, normalize as normalize4, resolve as resolve27, sep as sep8 } from "node:path";
|
|
82435
82476
|
var SNAPSHOT_DIR = "snapshot";
|
|
82436
82477
|
var MANIFEST_FILE = "manifest.json";
|
|
82437
82478
|
function normalizeRelativePath(rootDir, inputPath) {
|
|
82438
|
-
if (!inputPath ||
|
|
82479
|
+
if (!inputPath || isAbsolute7(inputPath)) {
|
|
82439
82480
|
throw new Error(`Unsafe backup path: ${inputPath}`);
|
|
82440
82481
|
}
|
|
82441
82482
|
const normalized = normalize4(inputPath).replaceAll("\\", "/");
|
|
@@ -82695,7 +82736,7 @@ async function loadDestructiveOperationBackup(backupDir) {
|
|
|
82695
82736
|
const resolvedBackupDir = await assertManagedBackupDir(backupDir);
|
|
82696
82737
|
const manifestPath = join71(resolvedBackupDir, MANIFEST_FILE);
|
|
82697
82738
|
const manifest = destructiveOperationBackupManifestSchema.parse(await import_fs_extra9.readJson(manifestPath));
|
|
82698
|
-
if (!
|
|
82739
|
+
if (!isAbsolute7(manifest.sourceRoot)) {
|
|
82699
82740
|
throw new Error(`Backup manifest source root must be absolute: ${manifest.sourceRoot}`);
|
|
82700
82741
|
}
|
|
82701
82742
|
const resolvedSourceRoot = resolve27(manifest.sourceRoot);
|
|
@@ -88551,7 +88592,7 @@ init_config_version_checker();
|
|
|
88551
88592
|
|
|
88552
88593
|
// src/domains/sync/sync-engine.ts
|
|
88553
88594
|
import { lstat as lstat4, readFile as readFile43, readlink as readlink2, realpath as realpath6, stat as stat15 } from "node:fs/promises";
|
|
88554
|
-
import { isAbsolute as
|
|
88595
|
+
import { isAbsolute as isAbsolute8, join as join91, normalize as normalize8, relative as relative15 } from "node:path";
|
|
88555
88596
|
|
|
88556
88597
|
// src/services/file-operations/ownership-checker.ts
|
|
88557
88598
|
init_metadata_migration();
|
|
@@ -89773,10 +89814,10 @@ async function validateSymlinkChain(path7, basePath, maxDepth = MAX_SYMLINK_DEPT
|
|
|
89773
89814
|
if (!stats.isSymbolicLink())
|
|
89774
89815
|
break;
|
|
89775
89816
|
const target = await readlink2(current);
|
|
89776
|
-
const resolvedTarget =
|
|
89817
|
+
const resolvedTarget = isAbsolute8(target) ? target : join91(current, "..", target);
|
|
89777
89818
|
const normalizedTarget = normalize8(resolvedTarget);
|
|
89778
89819
|
const rel = relative15(basePath, normalizedTarget);
|
|
89779
|
-
if (rel.startsWith("..") ||
|
|
89820
|
+
if (rel.startsWith("..") || isAbsolute8(rel)) {
|
|
89780
89821
|
throw new Error(`Symlink chain escapes base directory at depth ${depth}: ${path7}`);
|
|
89781
89822
|
}
|
|
89782
89823
|
current = normalizedTarget;
|
|
@@ -89803,7 +89844,7 @@ async function validateSyncPath(basePath, filePath) {
|
|
|
89803
89844
|
throw new Error(`Path too long: ${filePath.slice(0, 50)}...`);
|
|
89804
89845
|
}
|
|
89805
89846
|
const normalized = normalize8(filePath);
|
|
89806
|
-
if (
|
|
89847
|
+
if (isAbsolute8(normalized)) {
|
|
89807
89848
|
throw new Error(`Absolute paths not allowed: ${filePath}`);
|
|
89808
89849
|
}
|
|
89809
89850
|
if (normalized.startsWith("..") || normalized.includes("/../")) {
|
|
@@ -89811,7 +89852,7 @@ async function validateSyncPath(basePath, filePath) {
|
|
|
89811
89852
|
}
|
|
89812
89853
|
const fullPath = join91(basePath, normalized);
|
|
89813
89854
|
const rel = relative15(basePath, fullPath);
|
|
89814
|
-
if (rel.startsWith("..") ||
|
|
89855
|
+
if (rel.startsWith("..") || isAbsolute8(rel)) {
|
|
89815
89856
|
throw new Error(`Path escapes base directory: ${filePath}`);
|
|
89816
89857
|
}
|
|
89817
89858
|
await validateSymlinkChain(fullPath, basePath);
|
|
@@ -89819,7 +89860,7 @@ async function validateSyncPath(basePath, filePath) {
|
|
|
89819
89860
|
const resolvedBase = await realpath6(basePath);
|
|
89820
89861
|
const resolvedFull = await realpath6(fullPath);
|
|
89821
89862
|
const resolvedRel = relative15(resolvedBase, resolvedFull);
|
|
89822
|
-
if (resolvedRel.startsWith("..") ||
|
|
89863
|
+
if (resolvedRel.startsWith("..") || isAbsolute8(resolvedRel)) {
|
|
89823
89864
|
throw new Error(`Symlink escapes base directory: ${filePath}`);
|
|
89824
89865
|
}
|
|
89825
89866
|
} catch (error) {
|
|
@@ -89829,7 +89870,7 @@ async function validateSyncPath(basePath, filePath) {
|
|
|
89829
89870
|
const resolvedBase = await realpath6(basePath);
|
|
89830
89871
|
const resolvedParent = await realpath6(parentPath);
|
|
89831
89872
|
const resolvedRel = relative15(resolvedBase, resolvedParent);
|
|
89832
|
-
if (resolvedRel.startsWith("..") ||
|
|
89873
|
+
if (resolvedRel.startsWith("..") || isAbsolute8(resolvedRel)) {
|
|
89833
89874
|
throw new Error(`Parent symlink escapes base directory: ${filePath}`);
|
|
89834
89875
|
}
|
|
89835
89876
|
} catch (parentError) {
|
|
@@ -95277,11 +95318,11 @@ var modeFix = (mode, isDir, portable) => {
|
|
|
95277
95318
|
|
|
95278
95319
|
// node_modules/tar/dist/esm/strip-absolute-path.js
|
|
95279
95320
|
import { win32 as win322 } from "node:path";
|
|
95280
|
-
var { isAbsolute:
|
|
95321
|
+
var { isAbsolute: isAbsolute9, parse: parse5 } = win322;
|
|
95281
95322
|
var stripAbsolutePath = (path7) => {
|
|
95282
95323
|
let r2 = "";
|
|
95283
95324
|
let parsed = parse5(path7);
|
|
95284
|
-
while (
|
|
95325
|
+
while (isAbsolute9(path7) || parsed.root) {
|
|
95285
95326
|
const root = path7.charAt(0) === "/" && path7.slice(0, 4) !== "//?/" ? "/" : parsed.root;
|
|
95286
95327
|
path7 = path7.slice(root.length);
|
|
95287
95328
|
r2 += root;
|
|
@@ -108922,7 +108963,7 @@ Please use only one download method.`);
|
|
|
108922
108963
|
// src/commands/plan/plan-command.ts
|
|
108923
108964
|
init_output_manager();
|
|
108924
108965
|
import { existsSync as existsSync67, statSync as statSync12 } from "node:fs";
|
|
108925
|
-
import { dirname as dirname46, isAbsolute as
|
|
108966
|
+
import { dirname as dirname46, isAbsolute as isAbsolute11, join as join147, parse as parse7, resolve as resolve46 } from "node:path";
|
|
108926
108967
|
|
|
108927
108968
|
// src/commands/plan/plan-read-handlers.ts
|
|
108928
108969
|
init_config();
|
|
@@ -108990,14 +109031,14 @@ init_config();
|
|
|
108990
109031
|
init_plan_parser();
|
|
108991
109032
|
init_plan_scope();
|
|
108992
109033
|
init_plans_registry();
|
|
108993
|
-
import { isAbsolute as
|
|
109034
|
+
import { isAbsolute as isAbsolute10, resolve as resolve43 } from "node:path";
|
|
108994
109035
|
async function getGlobalPlansDirFromCwd() {
|
|
108995
109036
|
const projectRoot = findProjectRoot(process.cwd());
|
|
108996
109037
|
const { config } = await CkConfigManager.loadFull(projectRoot);
|
|
108997
109038
|
return resolveGlobalPlansDir(config);
|
|
108998
109039
|
}
|
|
108999
109040
|
function resolveTargetFromBase(target, baseDir) {
|
|
109000
|
-
const resolvedTarget =
|
|
109041
|
+
const resolvedTarget = isAbsolute10(target) ? resolve43(target) : resolve43(baseDir, target);
|
|
109001
109042
|
return isWithinDir(resolvedTarget, baseDir) ? resolvedTarget : null;
|
|
109002
109043
|
}
|
|
109003
109044
|
|
|
@@ -109501,7 +109542,7 @@ function resolveTargetPath(target, baseDir) {
|
|
|
109501
109542
|
if (!baseDir) {
|
|
109502
109543
|
return resolve46(target);
|
|
109503
109544
|
}
|
|
109504
|
-
if (
|
|
109545
|
+
if (isAbsolute11(target)) {
|
|
109505
109546
|
return resolve46(target);
|
|
109506
109547
|
}
|
|
109507
109548
|
const cwdCandidate = resolve46(target);
|