@basestream/cli 0.2.4 → 0.2.6
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.mjs +133 -42
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import fs from "node:fs";
|
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import os from "node:os";
|
|
16
16
|
function ensureDirs() {
|
|
17
|
-
for (const dir of [BASESTREAM_DIR, BUFFER_DIR, SESSION_DIR]) {
|
|
17
|
+
for (const dir of [BASESTREAM_DIR, BUFFER_DIR, SESSION_DIR, LOG_DIR]) {
|
|
18
18
|
fs.mkdirSync(dir, { recursive: true });
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -34,7 +34,7 @@ function writeConfig(config) {
|
|
|
34
34
|
ensureDirs();
|
|
35
35
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
36
36
|
}
|
|
37
|
-
var BASESTREAM_DIR, BUFFER_DIR, CONFIG_FILE, SESSION_DIR;
|
|
37
|
+
var BASESTREAM_DIR, BUFFER_DIR, CONFIG_FILE, SESSION_DIR, LOG_DIR, LOG_RETENTION_DAYS;
|
|
38
38
|
var init_config = __esm({
|
|
39
39
|
"src/cli/config.ts"() {
|
|
40
40
|
"use strict";
|
|
@@ -42,6 +42,8 @@ var init_config = __esm({
|
|
|
42
42
|
BUFFER_DIR = path.join(BASESTREAM_DIR, "buffer");
|
|
43
43
|
CONFIG_FILE = path.join(BASESTREAM_DIR, "config.json");
|
|
44
44
|
SESSION_DIR = path.join(BASESTREAM_DIR, "sessions");
|
|
45
|
+
LOG_DIR = path.join(BASESTREAM_DIR, "logs");
|
|
46
|
+
LOG_RETENTION_DAYS = 7;
|
|
45
47
|
}
|
|
46
48
|
});
|
|
47
49
|
|
|
@@ -332,22 +334,47 @@ function injectClaudeCodeHook() {
|
|
|
332
334
|
);
|
|
333
335
|
if (alreadyInstalled) {
|
|
334
336
|
check("Claude Code hook already installed");
|
|
335
|
-
|
|
337
|
+
} else {
|
|
338
|
+
hooks.Stop.push({
|
|
339
|
+
matcher: "*",
|
|
340
|
+
hooks: [
|
|
341
|
+
{
|
|
342
|
+
type: "command",
|
|
343
|
+
command: HOOK_COMMAND,
|
|
344
|
+
timeout: 30
|
|
345
|
+
}
|
|
346
|
+
]
|
|
347
|
+
});
|
|
336
348
|
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
hooks.Stop = [];
|
|
340
|
-
}
|
|
341
|
-
hooks.Stop.push({
|
|
342
|
-
matcher: "*",
|
|
343
|
-
hooks: [
|
|
349
|
+
} else {
|
|
350
|
+
hooks.Stop = [
|
|
344
351
|
{
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
352
|
+
matcher: "*",
|
|
353
|
+
hooks: [
|
|
354
|
+
{
|
|
355
|
+
type: "command",
|
|
356
|
+
command: HOOK_COMMAND,
|
|
357
|
+
timeout: 30
|
|
358
|
+
}
|
|
359
|
+
]
|
|
348
360
|
}
|
|
349
|
-
]
|
|
350
|
-
}
|
|
361
|
+
];
|
|
362
|
+
}
|
|
363
|
+
if (!settings.permissions || typeof settings.permissions !== "object") {
|
|
364
|
+
settings.permissions = {};
|
|
365
|
+
}
|
|
366
|
+
const permissions = settings.permissions;
|
|
367
|
+
if (!Array.isArray(permissions.allow)) {
|
|
368
|
+
permissions.allow = [];
|
|
369
|
+
}
|
|
370
|
+
const allow = permissions.allow;
|
|
371
|
+
const SKILL_PERMISSIONS2 = [
|
|
372
|
+
`Write(${os2.homedir()}/.basestream/sessions/**)`,
|
|
373
|
+
`Read(${os2.homedir()}/.basestream/sessions/**)`
|
|
374
|
+
];
|
|
375
|
+
for (const rule of SKILL_PERMISSIONS2) {
|
|
376
|
+
if (!allow.includes(rule)) allow.push(rule);
|
|
377
|
+
}
|
|
351
378
|
fs2.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
352
379
|
check("Injected tracking hook into ~/.claude/settings.json");
|
|
353
380
|
}
|
|
@@ -700,6 +727,30 @@ function buildSummary(acc, category) {
|
|
|
700
727
|
}
|
|
701
728
|
return parts.length > 0 ? parts.join(", ") : `Claude Code session in ${acc.projectName || "unknown project"}`;
|
|
702
729
|
}
|
|
730
|
+
function makeLogger(sessionId) {
|
|
731
|
+
ensureDirs();
|
|
732
|
+
const logFile = path5.join(LOG_DIR, `${sessionId}.log`);
|
|
733
|
+
return (...args) => {
|
|
734
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${args.join(" ")}
|
|
735
|
+
`;
|
|
736
|
+
try {
|
|
737
|
+
fs5.appendFileSync(logFile, line);
|
|
738
|
+
} catch {
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
function pruneOldLogs() {
|
|
743
|
+
try {
|
|
744
|
+
const cutoff = Date.now() - LOG_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
745
|
+
for (const file of fs5.readdirSync(LOG_DIR)) {
|
|
746
|
+
if (!file.endsWith(".log")) continue;
|
|
747
|
+
const full = path5.join(LOG_DIR, file);
|
|
748
|
+
const stat = fs5.statSync(full);
|
|
749
|
+
if (stat.mtimeMs < cutoff) fs5.unlinkSync(full);
|
|
750
|
+
}
|
|
751
|
+
} catch {
|
|
752
|
+
}
|
|
753
|
+
}
|
|
703
754
|
async function hookStop() {
|
|
704
755
|
let payload;
|
|
705
756
|
try {
|
|
@@ -714,6 +765,9 @@ async function hookStop() {
|
|
|
714
765
|
const { session_id, transcript_path, cwd } = payload;
|
|
715
766
|
if (!session_id) process.exit(0);
|
|
716
767
|
ensureDirs();
|
|
768
|
+
pruneOldLogs();
|
|
769
|
+
const log = makeLogger(session_id);
|
|
770
|
+
log("hook fired", "cwd=" + cwd);
|
|
717
771
|
let acc = readSessionAccumulator(session_id);
|
|
718
772
|
if (!acc) {
|
|
719
773
|
const gitInfo = extractGitInfo(cwd);
|
|
@@ -730,11 +784,13 @@ async function hookStop() {
|
|
|
730
784
|
gitRepo: gitInfo.repo,
|
|
731
785
|
projectName: gitInfo.projectName
|
|
732
786
|
};
|
|
787
|
+
log("new session");
|
|
733
788
|
}
|
|
734
789
|
if (transcript_path) {
|
|
735
790
|
acc = analyzeTranscript(transcript_path, acc);
|
|
736
791
|
}
|
|
737
792
|
acc.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
793
|
+
log(`turns=${acc.turns} files=${acc.filesWritten.size} commits=${acc.commitShas.size}`);
|
|
738
794
|
writeSessionAccumulator(acc);
|
|
739
795
|
if (acc.filesWritten.size > 0 || acc.commitShas.size > 0 || acc.turns >= 3) {
|
|
740
796
|
flushToBuffer(acc);
|
|
@@ -743,9 +799,16 @@ async function hookStop() {
|
|
|
743
799
|
try {
|
|
744
800
|
const { syncEntries: syncEntries2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
|
|
745
801
|
await syncEntries2(config);
|
|
746
|
-
|
|
802
|
+
log("synced");
|
|
803
|
+
process.stdout.write(JSON.stringify({ systemMessage: "basestream synced" }) + "\n");
|
|
804
|
+
} catch (e) {
|
|
805
|
+
log("sync error:", String(e));
|
|
747
806
|
}
|
|
807
|
+
} else {
|
|
808
|
+
log("no apiKey, skipping sync");
|
|
748
809
|
}
|
|
810
|
+
} else {
|
|
811
|
+
log("nothing to flush");
|
|
749
812
|
}
|
|
750
813
|
}
|
|
751
814
|
|
|
@@ -754,51 +817,79 @@ init_util();
|
|
|
754
817
|
import fs6 from "node:fs";
|
|
755
818
|
import path6 from "node:path";
|
|
756
819
|
import os4 from "node:os";
|
|
757
|
-
var
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
);
|
|
820
|
+
var CLAUDE_DIR2 = path6.join(os4.homedir(), ".claude");
|
|
821
|
+
var CLAUDE_SETTINGS_PATH2 = path6.join(CLAUDE_DIR2, "settings.json");
|
|
822
|
+
var CLAUDE_SKILLS_PATH2 = path6.join(CLAUDE_DIR2, "skills", "bs-summarize.md");
|
|
823
|
+
var CLAUDE_MD_PATH2 = path6.join(CLAUDE_DIR2, "CLAUDE.md");
|
|
762
824
|
var HOOK_MARKER2 = "_hook-stop";
|
|
825
|
+
var CLAUDE_MD_MARKER2 = "<!-- basestream -->";
|
|
826
|
+
var SKILL_PERMISSIONS = [
|
|
827
|
+
`Write(${os4.homedir()}/.basestream/sessions/**)`,
|
|
828
|
+
`Read(${os4.homedir()}/.basestream/sessions/**)`
|
|
829
|
+
];
|
|
763
830
|
function removeClaudeCodeHook() {
|
|
764
831
|
if (!fs6.existsSync(CLAUDE_SETTINGS_PATH2)) {
|
|
765
832
|
warn("No Claude Code settings found \u2014 nothing to remove");
|
|
766
|
-
return
|
|
833
|
+
return;
|
|
767
834
|
}
|
|
768
835
|
let settings;
|
|
769
836
|
try {
|
|
770
837
|
settings = JSON.parse(fs6.readFileSync(CLAUDE_SETTINGS_PATH2, "utf-8"));
|
|
771
838
|
} catch {
|
|
772
839
|
warn("Could not parse ~/.claude/settings.json");
|
|
773
|
-
return
|
|
840
|
+
return;
|
|
774
841
|
}
|
|
842
|
+
let changed = false;
|
|
775
843
|
const hooks = settings.hooks;
|
|
776
|
-
if (
|
|
777
|
-
|
|
778
|
-
|
|
844
|
+
if (hooks && Array.isArray(hooks.Stop)) {
|
|
845
|
+
const before = hooks.Stop.length;
|
|
846
|
+
hooks.Stop = hooks.Stop.filter(
|
|
847
|
+
(entry) => !entry.hooks?.some((h) => h.command?.includes(HOOK_MARKER2))
|
|
848
|
+
);
|
|
849
|
+
if (hooks.Stop.length !== before) {
|
|
850
|
+
if (hooks.Stop.length === 0) delete hooks.Stop;
|
|
851
|
+
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
852
|
+
changed = true;
|
|
853
|
+
}
|
|
779
854
|
}
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
855
|
+
const permissions = settings.permissions;
|
|
856
|
+
if (permissions && Array.isArray(permissions.allow)) {
|
|
857
|
+
const before = permissions.allow.length;
|
|
858
|
+
permissions.allow = permissions.allow.filter(
|
|
859
|
+
(rule) => !SKILL_PERMISSIONS.includes(rule)
|
|
860
|
+
);
|
|
861
|
+
if (permissions.allow.length !== before) {
|
|
862
|
+
if (permissions.allow.length === 0) delete permissions.allow;
|
|
863
|
+
if (Object.keys(permissions).length === 0) delete settings.permissions;
|
|
864
|
+
changed = true;
|
|
865
|
+
}
|
|
788
866
|
}
|
|
789
|
-
if (
|
|
790
|
-
|
|
867
|
+
if (changed) {
|
|
868
|
+
fs6.writeFileSync(CLAUDE_SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
869
|
+
check("Removed hook and permissions from ~/.claude/settings.json");
|
|
870
|
+
} else {
|
|
871
|
+
warn("No Basestream hook installed");
|
|
791
872
|
}
|
|
792
|
-
|
|
793
|
-
|
|
873
|
+
}
|
|
874
|
+
function removeSkill() {
|
|
875
|
+
if (fs6.existsSync(CLAUDE_SKILLS_PATH2)) {
|
|
876
|
+
fs6.rmSync(CLAUDE_SKILLS_PATH2);
|
|
877
|
+
check("Removed /bs-summarize skill from ~/.claude/skills/");
|
|
794
878
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
return
|
|
879
|
+
}
|
|
880
|
+
function removeClaudeMdRule() {
|
|
881
|
+
if (!fs6.existsSync(CLAUDE_MD_PATH2)) return;
|
|
882
|
+
const existing = fs6.readFileSync(CLAUDE_MD_PATH2, "utf-8");
|
|
883
|
+
if (!existing.includes(CLAUDE_MD_MARKER2)) return;
|
|
884
|
+
const updated = existing.replace(/\n\n<!-- basestream -->[\s\S]*?<!-- \/basestream -->\n?/, "").replace(/<!-- basestream -->[\s\S]*?<!-- \/basestream -->\n?/, "").trimEnd();
|
|
885
|
+
fs6.writeFileSync(CLAUDE_MD_PATH2, updated ? updated + "\n" : "");
|
|
886
|
+
check("Removed Basestream rules from ~/.claude/CLAUDE.md");
|
|
798
887
|
}
|
|
799
888
|
async function uninstall() {
|
|
800
889
|
console.log();
|
|
801
890
|
removeClaudeCodeHook();
|
|
891
|
+
removeSkill();
|
|
892
|
+
removeClaudeMdRule();
|
|
802
893
|
console.log();
|
|
803
894
|
console.log(
|
|
804
895
|
` ${c.dim("Basestream will no longer track new Claude Code sessions.")}`
|
|
@@ -834,7 +925,7 @@ async function main() {
|
|
|
834
925
|
process.exit(0);
|
|
835
926
|
}
|
|
836
927
|
if (command === "--version" || command === "-v") {
|
|
837
|
-
console.log(true ? "0.2.
|
|
928
|
+
console.log(true ? "0.2.6" : "dev");
|
|
838
929
|
process.exit(0);
|
|
839
930
|
}
|
|
840
931
|
switch (command || "init") {
|
package/package.json
CHANGED