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