hillclimb 0.1.6 → 0.1.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/cli.js +70 -45
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -501,6 +501,7 @@ import os2 from "os";
|
|
|
501
501
|
import path5 from "path";
|
|
502
502
|
var HOOK_CMD = (sub) => `npx hillclimb@latest ${sub}`;
|
|
503
503
|
var GIT_TRACES_CMD = (tool) => `npx hillclimb@latest git-traces --tool=${tool}`;
|
|
504
|
+
var CLAUDE_SESSIONEND_UPLOAD_TIMEOUT_SECONDS = 30;
|
|
504
505
|
var TOOLS = [
|
|
505
506
|
{
|
|
506
507
|
tool: "claude",
|
|
@@ -508,7 +509,11 @@ var TOOLS = [
|
|
|
508
509
|
settingsFile: ".claude/settings.local.json",
|
|
509
510
|
format: "claude",
|
|
510
511
|
events: [
|
|
511
|
-
{
|
|
512
|
+
{
|
|
513
|
+
eventName: "SessionEnd",
|
|
514
|
+
command: HOOK_CMD("upload"),
|
|
515
|
+
timeout: CLAUDE_SESSIONEND_UPLOAD_TIMEOUT_SECONDS
|
|
516
|
+
},
|
|
512
517
|
{ eventName: "SessionStart", command: GIT_TRACES_CMD("claude") },
|
|
513
518
|
{ eventName: "Stop", command: GIT_TRACES_CMD("claude") },
|
|
514
519
|
{ eventName: "SessionEnd", command: GIT_TRACES_CMD("claude") }
|
|
@@ -593,11 +598,7 @@ function isDir(p7) {
|
|
|
593
598
|
}
|
|
594
599
|
function copilotChatDetect() {
|
|
595
600
|
const home = os2.homedir();
|
|
596
|
-
const suffix = path5.join(
|
|
597
|
-
"User",
|
|
598
|
-
"globalStorage",
|
|
599
|
-
"github.copilot-chat"
|
|
600
|
-
);
|
|
601
|
+
const suffix = path5.join("User", "globalStorage", "github.copilot-chat");
|
|
601
602
|
const candidates = [
|
|
602
603
|
path5.join(home, "Library", "Application Support", "Code", suffix),
|
|
603
604
|
path5.join(home, ".config", "Code", suffix),
|
|
@@ -605,6 +606,10 @@ function copilotChatDetect() {
|
|
|
605
606
|
].filter((p7) => p7 !== null);
|
|
606
607
|
return candidates.some(isDir);
|
|
607
608
|
}
|
|
609
|
+
function ensureHooks(settings) {
|
|
610
|
+
if (!settings.hooks) settings.hooks = {};
|
|
611
|
+
return settings.hooks;
|
|
612
|
+
}
|
|
608
613
|
function settingsPath(repoRoot, def) {
|
|
609
614
|
return path5.join(repoRoot, def.settingsFile);
|
|
610
615
|
}
|
|
@@ -624,31 +629,45 @@ async function writeJson(file, obj) {
|
|
|
624
629
|
await fs4.promises.writeFile(file, `${JSON.stringify(obj, null, 2)}
|
|
625
630
|
`);
|
|
626
631
|
}
|
|
627
|
-
function claudeHookPresent(matchers, command) {
|
|
632
|
+
function claudeHookPresent(matchers, command, options = {}) {
|
|
628
633
|
return matchers.some(
|
|
629
|
-
(m) => (m.hooks ?? []).some(
|
|
630
|
-
(h
|
|
631
|
-
|
|
634
|
+
(m) => (m.hooks ?? []).some((h) => {
|
|
635
|
+
if (h.type !== "command" || h.command !== command) return false;
|
|
636
|
+
if (options.timeout !== void 0 && h.timeout !== options.timeout) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
return true;
|
|
640
|
+
})
|
|
632
641
|
);
|
|
633
642
|
}
|
|
634
|
-
function claudeInstall(settings, eventName, command) {
|
|
635
|
-
|
|
636
|
-
if (!
|
|
637
|
-
const matchers =
|
|
638
|
-
|
|
643
|
+
function claudeInstall(settings, eventName, command, options = {}) {
|
|
644
|
+
const hooks = ensureHooks(settings);
|
|
645
|
+
if (!hooks[eventName]) hooks[eventName] = [];
|
|
646
|
+
const matchers = hooks[eventName];
|
|
647
|
+
for (const matcher of matchers) {
|
|
648
|
+
for (const hook of matcher.hooks ?? []) {
|
|
649
|
+
if (hook.type !== "command" || hook.command !== command) continue;
|
|
650
|
+
if (options.timeout === void 0 || hook.timeout === options.timeout) {
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
hook.timeout = options.timeout;
|
|
654
|
+
return true;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
639
657
|
matchers.push({
|
|
640
658
|
matcher: "",
|
|
641
|
-
hooks: [{ type: "command", command }]
|
|
659
|
+
hooks: [{ type: "command", command, ...options }]
|
|
642
660
|
});
|
|
643
661
|
return true;
|
|
644
662
|
}
|
|
645
|
-
function claudeCheck(settings, eventName, command) {
|
|
663
|
+
function claudeCheck(settings, eventName, command, options = {}) {
|
|
646
664
|
const matchers = settings.hooks?.[eventName] ?? [];
|
|
647
|
-
return claudeHookPresent(matchers, command);
|
|
665
|
+
return claudeHookPresent(matchers, command, options);
|
|
648
666
|
}
|
|
649
667
|
function claudeUninstall(settings, eventName, command) {
|
|
650
|
-
const
|
|
651
|
-
|
|
668
|
+
const hooks = settings.hooks;
|
|
669
|
+
const matchers = hooks?.[eventName];
|
|
670
|
+
if (!hooks || !matchers || matchers.length === 0) return false;
|
|
652
671
|
let removedAny = false;
|
|
653
672
|
const next = [];
|
|
654
673
|
for (const m of matchers) {
|
|
@@ -660,10 +679,10 @@ function claudeUninstall(settings, eventName, command) {
|
|
|
660
679
|
}
|
|
661
680
|
if (!removedAny) return false;
|
|
662
681
|
if (next.length > 0) {
|
|
663
|
-
|
|
682
|
+
hooks[eventName] = next;
|
|
664
683
|
} else {
|
|
665
|
-
delete
|
|
666
|
-
if (Object.keys(
|
|
684
|
+
delete hooks[eventName];
|
|
685
|
+
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
667
686
|
}
|
|
668
687
|
return true;
|
|
669
688
|
}
|
|
@@ -672,9 +691,9 @@ function cursorHookPresent(entries, command) {
|
|
|
672
691
|
}
|
|
673
692
|
function cursorInstall(settings, eventName, command) {
|
|
674
693
|
if (!settings.version) settings.version = 1;
|
|
675
|
-
|
|
676
|
-
if (!
|
|
677
|
-
const entries =
|
|
694
|
+
const hooks = ensureHooks(settings);
|
|
695
|
+
if (!hooks[eventName]) hooks[eventName] = [];
|
|
696
|
+
const entries = hooks[eventName];
|
|
678
697
|
if (cursorHookPresent(entries, command)) return false;
|
|
679
698
|
entries.push({ command });
|
|
680
699
|
return true;
|
|
@@ -684,15 +703,16 @@ function cursorCheck(settings, eventName, command) {
|
|
|
684
703
|
return cursorHookPresent(entries, command);
|
|
685
704
|
}
|
|
686
705
|
function cursorUninstall(settings, eventName, command) {
|
|
687
|
-
const
|
|
688
|
-
|
|
706
|
+
const hooks = settings.hooks;
|
|
707
|
+
const entries = hooks?.[eventName];
|
|
708
|
+
if (!hooks || !entries || entries.length === 0) return false;
|
|
689
709
|
const next = entries.filter((e) => e.command !== command);
|
|
690
710
|
if (next.length === entries.length) return false;
|
|
691
711
|
if (next.length > 0) {
|
|
692
|
-
|
|
712
|
+
hooks[eventName] = next;
|
|
693
713
|
} else {
|
|
694
|
-
delete
|
|
695
|
-
if (Object.keys(
|
|
714
|
+
delete hooks[eventName];
|
|
715
|
+
if (Object.keys(hooks).length === 0) {
|
|
696
716
|
delete settings.hooks;
|
|
697
717
|
delete settings.version;
|
|
698
718
|
}
|
|
@@ -703,9 +723,9 @@ function copilotHookPresent(entries, command) {
|
|
|
703
723
|
return entries.some((e) => e.type === "command" && e.command === command);
|
|
704
724
|
}
|
|
705
725
|
function copilotInstall(settings, eventName, command) {
|
|
706
|
-
|
|
707
|
-
if (!
|
|
708
|
-
const entries =
|
|
726
|
+
const hooks = ensureHooks(settings);
|
|
727
|
+
if (!hooks[eventName]) hooks[eventName] = [];
|
|
728
|
+
const entries = hooks[eventName];
|
|
709
729
|
if (copilotHookPresent(entries, command)) return false;
|
|
710
730
|
entries.push({ type: "command", command });
|
|
711
731
|
return true;
|
|
@@ -715,17 +735,18 @@ function copilotCheck(settings, eventName, command) {
|
|
|
715
735
|
return copilotHookPresent(entries, command);
|
|
716
736
|
}
|
|
717
737
|
function copilotUninstall(settings, eventName, command) {
|
|
718
|
-
const
|
|
719
|
-
|
|
738
|
+
const hooks = settings.hooks;
|
|
739
|
+
const entries = hooks?.[eventName];
|
|
740
|
+
if (!hooks || !entries || entries.length === 0) return false;
|
|
720
741
|
const next = entries.filter(
|
|
721
742
|
(e) => !(e.type === "command" && e.command === command)
|
|
722
743
|
);
|
|
723
744
|
if (next.length === entries.length) return false;
|
|
724
745
|
if (next.length > 0) {
|
|
725
|
-
|
|
746
|
+
hooks[eventName] = next;
|
|
726
747
|
} else {
|
|
727
|
-
delete
|
|
728
|
-
if (Object.keys(
|
|
748
|
+
delete hooks[eventName];
|
|
749
|
+
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
729
750
|
}
|
|
730
751
|
return true;
|
|
731
752
|
}
|
|
@@ -897,24 +918,24 @@ async function opencodeCheck(file) {
|
|
|
897
918
|
return false;
|
|
898
919
|
}
|
|
899
920
|
}
|
|
900
|
-
function install(settings, format, eventName, command) {
|
|
921
|
+
function install(settings, format, eventName, command, options = {}) {
|
|
901
922
|
switch (format) {
|
|
902
923
|
case "cursor":
|
|
903
924
|
return cursorInstall(settings, eventName, command);
|
|
904
925
|
case "copilot":
|
|
905
926
|
return copilotInstall(settings, eventName, command);
|
|
906
927
|
default:
|
|
907
|
-
return claudeInstall(settings, eventName, command);
|
|
928
|
+
return claudeInstall(settings, eventName, command, options);
|
|
908
929
|
}
|
|
909
930
|
}
|
|
910
|
-
function check(settings, format, eventName, command) {
|
|
931
|
+
function check(settings, format, eventName, command, options = {}) {
|
|
911
932
|
switch (format) {
|
|
912
933
|
case "cursor":
|
|
913
934
|
return cursorCheck(settings, eventName, command);
|
|
914
935
|
case "copilot":
|
|
915
936
|
return copilotCheck(settings, eventName, command);
|
|
916
937
|
default:
|
|
917
|
-
return claudeCheck(settings, eventName, command);
|
|
938
|
+
return claudeCheck(settings, eventName, command, options);
|
|
918
939
|
}
|
|
919
940
|
}
|
|
920
941
|
function uninstall(settings, format, eventName, command) {
|
|
@@ -961,7 +982,9 @@ async function installHooksForTool(repoRoot, def) {
|
|
|
961
982
|
mutated = true;
|
|
962
983
|
}
|
|
963
984
|
}
|
|
964
|
-
const added = install(settings, def.format, evt.eventName, evt.command
|
|
985
|
+
const added = install(settings, def.format, evt.eventName, evt.command, {
|
|
986
|
+
timeout: evt.timeout
|
|
987
|
+
});
|
|
965
988
|
if (added) {
|
|
966
989
|
installed++;
|
|
967
990
|
mutated = true;
|
|
@@ -985,7 +1008,9 @@ async function checkHooksForTool(repoRoot, def) {
|
|
|
985
1008
|
const settings = await readJson(file);
|
|
986
1009
|
const missing = [];
|
|
987
1010
|
for (const evt of def.events) {
|
|
988
|
-
if (!check(settings, def.format, evt.eventName, evt.command
|
|
1011
|
+
if (!check(settings, def.format, evt.eventName, evt.command, {
|
|
1012
|
+
timeout: evt.timeout
|
|
1013
|
+
})) {
|
|
989
1014
|
missing.push(`${evt.eventName}:${evt.command}`);
|
|
990
1015
|
}
|
|
991
1016
|
}
|