@basou/cli 0.13.0 → 0.14.0
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/index.js +392 -95
- package/dist/index.js.map +1 -1
- package/dist/program.js +392 -95
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -633,6 +633,9 @@ function printNoApprovals(options) {
|
|
|
633
633
|
}
|
|
634
634
|
|
|
635
635
|
// src/commands/decision.ts
|
|
636
|
+
import { readFile } from "fs/promises";
|
|
637
|
+
import { homedir } from "os";
|
|
638
|
+
import { resolve } from "path";
|
|
636
639
|
import {
|
|
637
640
|
acquireLock as acquireLock2,
|
|
638
641
|
appendEventToExistingSession,
|
|
@@ -640,12 +643,34 @@ import {
|
|
|
640
643
|
basouPaths as basouPaths2,
|
|
641
644
|
createAdHocSessionWithEvent,
|
|
642
645
|
findErrorCode as findErrorCode2,
|
|
646
|
+
isValidPrefixedId,
|
|
643
647
|
prefixedUlid as prefixedUlid2,
|
|
644
648
|
readManifest,
|
|
645
649
|
resolveRepositoryRoot as resolveRepositoryRoot2,
|
|
646
|
-
resolveSessionId
|
|
650
|
+
resolveSessionId,
|
|
651
|
+
sanitizePath
|
|
647
652
|
} from "@basou/core";
|
|
648
653
|
import { InvalidArgumentError } from "commander";
|
|
654
|
+
|
|
655
|
+
// src/lib/repo-root.ts
|
|
656
|
+
import { resolveBasouRepositoryRoot } from "@basou/core";
|
|
657
|
+
async function resolveBasouRootForCommand(cwd, commandName) {
|
|
658
|
+
try {
|
|
659
|
+
return await resolveBasouRepositoryRoot(cwd, {
|
|
660
|
+
onRedirect: ({ via, root }) => console.error(`Resolved workspace view to ${root} (via ${via}).`)
|
|
661
|
+
});
|
|
662
|
+
} catch (error) {
|
|
663
|
+
if (error instanceof Error && error.message === "Not a git repository") {
|
|
664
|
+
throw new Error(
|
|
665
|
+
`Not a git repository. Run 'git init' first, then re-run 'basou ${commandName}'.`,
|
|
666
|
+
{ cause: error }
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
throw error;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// src/commands/decision.ts
|
|
649
674
|
var LABEL_TITLE_MAX = 80;
|
|
650
675
|
var LABEL_TRUNCATE_HEAD = LABEL_TITLE_MAX - 3;
|
|
651
676
|
function registerDecisionCommand(program2) {
|
|
@@ -675,7 +700,34 @@ function registerDecisionCommand(program2) {
|
|
|
675
700
|
).option("--json", "Output the result as JSON").option("-v, --verbose", "Show error causes").action(async (options) => {
|
|
676
701
|
await runDecisionRecord(options);
|
|
677
702
|
});
|
|
703
|
+
decision.command("capture").description(
|
|
704
|
+
"Capture a batch of decisions from a JSON array (stdin or --file). The in-loop agent extracts a session's conversational decisions -- with rationale, alternatives, and rejected reasons -- and pipes them in; basou writes them deterministically into one ad-hoc session."
|
|
705
|
+
).option("--file <path>", "Read the JSON array from a file instead of stdin").option("--dry-run", "Validate and preview the decisions without writing them").option("--json", "Output the result as JSON").option("-v, --verbose", "Show error causes").addHelpText("after", CAPTURE_HELP).action(async (options) => {
|
|
706
|
+
await runDecisionCapture(options);
|
|
707
|
+
});
|
|
678
708
|
}
|
|
709
|
+
var CAPTURE_HELP = `
|
|
710
|
+
Input format (a JSON array; one object per decision):
|
|
711
|
+
[
|
|
712
|
+
{
|
|
713
|
+
"title": "Adopt pnpm for the monorepo",
|
|
714
|
+
"rationale": "Workspace protocol and a content-addressed store fit our layout.",
|
|
715
|
+
"alternatives": ["npm workspaces", "yarn"],
|
|
716
|
+
"rejected_reason": "npm hoisting caused phantom-dependency bugs",
|
|
717
|
+
"linked_files": ["pnpm-workspace.yaml"]
|
|
718
|
+
}
|
|
719
|
+
]
|
|
720
|
+
|
|
721
|
+
Only "title" is required; every other field is optional. All decisions are
|
|
722
|
+
written into one ad-hoc session timestamped now, so orientation surfaces them
|
|
723
|
+
as the latest decisions. Run from a workspace-view directory and it resolves to
|
|
724
|
+
the planning repo, like 'basou orient' / 'basou refresh' / 'basou note'.
|
|
725
|
+
|
|
726
|
+
Example (heredoc on stdin):
|
|
727
|
+
basou decision capture <<'JSON'
|
|
728
|
+
[{ "title": "Ship the capture command", "rationale": "Close the why-capture gap" }]
|
|
729
|
+
JSON
|
|
730
|
+
`;
|
|
679
731
|
async function runDecisionRecord(options, ctx = {}) {
|
|
680
732
|
try {
|
|
681
733
|
await doRunDecisionRecord(options, ctx);
|
|
@@ -761,6 +813,259 @@ async function doRunDecisionRecord(options, ctx) {
|
|
|
761
813
|
rich
|
|
762
814
|
});
|
|
763
815
|
}
|
|
816
|
+
async function runDecisionCapture(options, ctx = {}) {
|
|
817
|
+
try {
|
|
818
|
+
await doRunDecisionCapture(options, ctx);
|
|
819
|
+
} catch (error) {
|
|
820
|
+
renderCliError(error, {
|
|
821
|
+
verbose: isVerbose(options),
|
|
822
|
+
classifiers: [failedToFinalizeClassifier]
|
|
823
|
+
});
|
|
824
|
+
process.exitCode = 1;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
async function doRunDecisionCapture(options, ctx) {
|
|
828
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
829
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "decision capture");
|
|
830
|
+
const paths = basouPaths2(repositoryRoot);
|
|
831
|
+
await assertWorkspaceInitialized2(paths.root);
|
|
832
|
+
const raw = await readCaptureInput(options, ctx);
|
|
833
|
+
const decisions = parseCaptureInput(raw);
|
|
834
|
+
if (options.dryRun === true) {
|
|
835
|
+
printCapturePreview(options, decisions);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
839
|
+
const occurredAt = now.toISOString();
|
|
840
|
+
const decisionIds = decisions.map(() => prefixedUlid2("decision"));
|
|
841
|
+
const manifest = await readManifest(paths);
|
|
842
|
+
const invocationArgs = options.file !== void 0 ? [
|
|
843
|
+
"--file",
|
|
844
|
+
sanitizePath(resolve(cwd, options.file), {
|
|
845
|
+
workingDirectory: repositoryRoot,
|
|
846
|
+
homedir: homedir()
|
|
847
|
+
})
|
|
848
|
+
] : [];
|
|
849
|
+
const adHoc = await createAdHocSessionWithEvent({
|
|
850
|
+
paths,
|
|
851
|
+
manifest,
|
|
852
|
+
label: buildCaptureLabel(decisions.length),
|
|
853
|
+
occurredAt,
|
|
854
|
+
sessionSource: "human",
|
|
855
|
+
workingDirectory: repositoryRoot,
|
|
856
|
+
invocation: {
|
|
857
|
+
command: "basou decision capture",
|
|
858
|
+
args: invocationArgs
|
|
859
|
+
},
|
|
860
|
+
targetEventBuilders: decisions.map(
|
|
861
|
+
(decision, index) => (sessionId, eventId) => buildDecisionEvent({
|
|
862
|
+
eventId,
|
|
863
|
+
sessionId,
|
|
864
|
+
decisionId: decisionIds[index],
|
|
865
|
+
title: decision.title,
|
|
866
|
+
occurredAt,
|
|
867
|
+
rich: toRichFields(decision)
|
|
868
|
+
})
|
|
869
|
+
)
|
|
870
|
+
});
|
|
871
|
+
printCaptureResult(options, {
|
|
872
|
+
sessionId: adHoc.sessionId,
|
|
873
|
+
items: decisions.map((decision, index) => ({
|
|
874
|
+
decisionId: decisionIds[index],
|
|
875
|
+
eventId: adHoc.targetEventIds[index],
|
|
876
|
+
input: decision
|
|
877
|
+
}))
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
async function readCaptureInput(options, ctx) {
|
|
881
|
+
if (options.file !== void 0) {
|
|
882
|
+
try {
|
|
883
|
+
return await readFile(options.file, "utf8");
|
|
884
|
+
} catch (error) {
|
|
885
|
+
if (findErrorCode2(error, "ENOENT")) {
|
|
886
|
+
throw new Error(`Input file not found: ${options.file}`);
|
|
887
|
+
}
|
|
888
|
+
throw error;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (ctx.readInput !== void 0) {
|
|
892
|
+
return await ctx.readInput();
|
|
893
|
+
}
|
|
894
|
+
if (process.stdin.isTTY === true) {
|
|
895
|
+
throw new Error(NO_INPUT_HINT);
|
|
896
|
+
}
|
|
897
|
+
return await readStdinToEnd();
|
|
898
|
+
}
|
|
899
|
+
async function readStdinToEnd() {
|
|
900
|
+
const chunks = [];
|
|
901
|
+
for await (const chunk of process.stdin) {
|
|
902
|
+
chunks.push(Buffer.from(chunk));
|
|
903
|
+
}
|
|
904
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
905
|
+
}
|
|
906
|
+
var NO_INPUT_HINT = "No input: pipe a JSON array of decisions to stdin or pass --file <path>.";
|
|
907
|
+
var CAPTURE_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
908
|
+
"title",
|
|
909
|
+
"rationale",
|
|
910
|
+
"rejected_reason",
|
|
911
|
+
"alternatives",
|
|
912
|
+
"linked_events",
|
|
913
|
+
"linked_files"
|
|
914
|
+
]);
|
|
915
|
+
function parseCaptureInput(raw) {
|
|
916
|
+
if (raw.trim().length === 0) {
|
|
917
|
+
throw new Error(NO_INPUT_HINT);
|
|
918
|
+
}
|
|
919
|
+
let parsed;
|
|
920
|
+
try {
|
|
921
|
+
parsed = JSON.parse(raw);
|
|
922
|
+
} catch (error) {
|
|
923
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
924
|
+
throw new Error(`Input is not valid JSON: ${detail}`);
|
|
925
|
+
}
|
|
926
|
+
if (!Array.isArray(parsed)) {
|
|
927
|
+
throw new Error("Input must be a JSON array of decision objects.");
|
|
928
|
+
}
|
|
929
|
+
if (parsed.length === 0) {
|
|
930
|
+
throw new Error("Input array must contain at least one decision.");
|
|
931
|
+
}
|
|
932
|
+
return parsed.map((item, index) => validateCaptureItem(item, index));
|
|
933
|
+
}
|
|
934
|
+
function validateCaptureItem(item, index) {
|
|
935
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
936
|
+
throw new Error(`decision[${index}] must be a JSON object.`);
|
|
937
|
+
}
|
|
938
|
+
const obj = item;
|
|
939
|
+
for (const key of Object.keys(obj)) {
|
|
940
|
+
if (!CAPTURE_ALLOWED_KEYS.has(key)) {
|
|
941
|
+
throw new Error(
|
|
942
|
+
`decision[${index}]: unknown field '${key}'. Allowed: title, rationale, rejected_reason, alternatives, linked_events, linked_files.`
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (typeof obj.title !== "string" || isBlank(obj.title)) {
|
|
947
|
+
throw new Error(`decision[${index}].title must be a non-empty string.`);
|
|
948
|
+
}
|
|
949
|
+
const out = { title: obj.title };
|
|
950
|
+
if (obj.rationale !== void 0) {
|
|
951
|
+
out.rationale = requireNonEmptyString(obj.rationale, index, "rationale");
|
|
952
|
+
}
|
|
953
|
+
if (obj.rejected_reason !== void 0) {
|
|
954
|
+
out.rejected_reason = requireNonEmptyString(obj.rejected_reason, index, "rejected_reason");
|
|
955
|
+
}
|
|
956
|
+
if (obj.alternatives !== void 0) {
|
|
957
|
+
out.alternatives = validateStringArray(obj.alternatives, index, "alternatives", (value, i) => {
|
|
958
|
+
if (isBlank(value)) {
|
|
959
|
+
throw new Error(`decision[${index}].alternatives[${i}] must not be empty.`);
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
if (obj.linked_events !== void 0) {
|
|
964
|
+
out.linked_events = validateStringArray(
|
|
965
|
+
obj.linked_events,
|
|
966
|
+
index,
|
|
967
|
+
"linked_events",
|
|
968
|
+
(value, i) => {
|
|
969
|
+
if (!isValidEventId(value)) {
|
|
970
|
+
throw new Error(
|
|
971
|
+
`decision[${index}].linked_events[${i}] must match evt_<ULID>, got '${value}'.`
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
if (obj.linked_files !== void 0) {
|
|
978
|
+
out.linked_files = validateStringArray(obj.linked_files, index, "linked_files", (value, i) => {
|
|
979
|
+
if (isBlank(value)) {
|
|
980
|
+
throw new Error(`decision[${index}].linked_files[${i}] must not be empty.`);
|
|
981
|
+
}
|
|
982
|
+
if (value.length > 4096) {
|
|
983
|
+
throw new Error(`decision[${index}].linked_files[${i}] exceeds 4096 chars.`);
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
return out;
|
|
988
|
+
}
|
|
989
|
+
function requireNonEmptyString(value, index, field) {
|
|
990
|
+
if (typeof value !== "string" || isBlank(value)) {
|
|
991
|
+
throw new Error(`decision[${index}].${field} must be a non-empty string.`);
|
|
992
|
+
}
|
|
993
|
+
return value;
|
|
994
|
+
}
|
|
995
|
+
function isBlank(value) {
|
|
996
|
+
return value.trim().length === 0;
|
|
997
|
+
}
|
|
998
|
+
function validateStringArray(value, index, field, checkEach) {
|
|
999
|
+
if (!Array.isArray(value)) {
|
|
1000
|
+
throw new Error(`decision[${index}].${field} must be an array of strings.`);
|
|
1001
|
+
}
|
|
1002
|
+
return value.map((entry, i) => {
|
|
1003
|
+
if (typeof entry !== "string") {
|
|
1004
|
+
throw new Error(`decision[${index}].${field}[${i}] must be a string.`);
|
|
1005
|
+
}
|
|
1006
|
+
checkEach(entry, i);
|
|
1007
|
+
return entry;
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
function toRichFields(decision) {
|
|
1011
|
+
const out = {};
|
|
1012
|
+
if (decision.rationale !== void 0) out.rationale = decision.rationale;
|
|
1013
|
+
if (decision.rejected_reason !== void 0) out.rejected_reason = decision.rejected_reason;
|
|
1014
|
+
if (decision.alternatives !== void 0) out.alternatives = [...decision.alternatives];
|
|
1015
|
+
if (decision.linked_events !== void 0) out.linked_events = [...decision.linked_events];
|
|
1016
|
+
if (decision.linked_files !== void 0) out.linked_files = [...decision.linked_files];
|
|
1017
|
+
return out;
|
|
1018
|
+
}
|
|
1019
|
+
function buildCaptureLabel(count) {
|
|
1020
|
+
return `Ad-hoc capture: ${count} decision${count === 1 ? "" : "s"}`;
|
|
1021
|
+
}
|
|
1022
|
+
function captureItemToPayload(item) {
|
|
1023
|
+
const payload = {
|
|
1024
|
+
decision_id: item.decisionId,
|
|
1025
|
+
event_id: item.eventId,
|
|
1026
|
+
title: item.input.title
|
|
1027
|
+
};
|
|
1028
|
+
if (item.input.rationale !== void 0) payload.rationale = item.input.rationale;
|
|
1029
|
+
if (item.input.alternatives !== void 0) payload.alternatives = item.input.alternatives;
|
|
1030
|
+
if (item.input.rejected_reason !== void 0)
|
|
1031
|
+
payload.rejected_reason = item.input.rejected_reason;
|
|
1032
|
+
if (item.input.linked_events !== void 0) payload.linked_events = item.input.linked_events;
|
|
1033
|
+
if (item.input.linked_files !== void 0) payload.linked_files = item.input.linked_files;
|
|
1034
|
+
return payload;
|
|
1035
|
+
}
|
|
1036
|
+
function printCapturePreview(options, decisions) {
|
|
1037
|
+
if (options.json === true) {
|
|
1038
|
+
console.log(JSON.stringify({ dry_run: true, count: decisions.length, decisions }));
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
console.log(
|
|
1042
|
+
`Would capture ${decisions.length} decision${decisions.length === 1 ? "" : "s"} (dry run; nothing written):`
|
|
1043
|
+
);
|
|
1044
|
+
for (const decision of decisions) {
|
|
1045
|
+
console.log(`- ${decision.title}`);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
function printCaptureResult(options, result) {
|
|
1049
|
+
const sid = shortSessionId(result.sessionId);
|
|
1050
|
+
if (options.json === true) {
|
|
1051
|
+
console.log(
|
|
1052
|
+
JSON.stringify({
|
|
1053
|
+
mode: "ad-hoc",
|
|
1054
|
+
session_id: result.sessionId,
|
|
1055
|
+
session_status: "completed",
|
|
1056
|
+
count: result.items.length,
|
|
1057
|
+
decisions: result.items.map(captureItemToPayload)
|
|
1058
|
+
})
|
|
1059
|
+
);
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
console.log(
|
|
1063
|
+
`Captured ${result.items.length} decision${result.items.length === 1 ? "" : "s"} in ad-hoc session ${sid}:`
|
|
1064
|
+
);
|
|
1065
|
+
for (const item of result.items) {
|
|
1066
|
+
console.log(`- ${item.decisionId}: ${item.input.title}`);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
764
1069
|
function pickRichFields(options) {
|
|
765
1070
|
const out = {};
|
|
766
1071
|
if (options.rationale !== void 0) out.rationale = options.rationale;
|
|
@@ -798,38 +1103,40 @@ function buildAdHocLabel(title) {
|
|
|
798
1103
|
return `Ad-hoc decision: ${truncated}`;
|
|
799
1104
|
}
|
|
800
1105
|
function parseTitle(raw) {
|
|
801
|
-
if (raw
|
|
1106
|
+
if (isBlank(raw)) {
|
|
802
1107
|
throw new InvalidArgumentError("Title must not be empty");
|
|
803
1108
|
}
|
|
804
1109
|
return raw;
|
|
805
1110
|
}
|
|
806
1111
|
function parseRationale(raw) {
|
|
807
|
-
if (raw
|
|
1112
|
+
if (isBlank(raw)) {
|
|
808
1113
|
throw new InvalidArgumentError("Rationale must not be empty");
|
|
809
1114
|
}
|
|
810
1115
|
return raw;
|
|
811
1116
|
}
|
|
812
1117
|
function parseRejectedReason(raw) {
|
|
813
|
-
if (raw
|
|
1118
|
+
if (isBlank(raw)) {
|
|
814
1119
|
throw new InvalidArgumentError("Rejected reason must not be empty");
|
|
815
1120
|
}
|
|
816
1121
|
return raw;
|
|
817
1122
|
}
|
|
818
1123
|
function collectAlternative(value, prev) {
|
|
819
|
-
if (value
|
|
1124
|
+
if (isBlank(value)) {
|
|
820
1125
|
throw new InvalidArgumentError("Alternative must not be empty");
|
|
821
1126
|
}
|
|
822
1127
|
return prev.concat(value);
|
|
823
1128
|
}
|
|
824
|
-
|
|
1129
|
+
function isValidEventId(value) {
|
|
1130
|
+
return isValidPrefixedId(value) && value.startsWith("evt_");
|
|
1131
|
+
}
|
|
825
1132
|
function collectLinkedEvent(value, prev) {
|
|
826
|
-
if (!
|
|
1133
|
+
if (!isValidEventId(value)) {
|
|
827
1134
|
throw new InvalidArgumentError(`Linked event id must match evt_<ULID>, got '${value}'`);
|
|
828
1135
|
}
|
|
829
1136
|
return prev.concat(value);
|
|
830
1137
|
}
|
|
831
1138
|
function collectLinkedFile(value, prev) {
|
|
832
|
-
if (value
|
|
1139
|
+
if (isBlank(value)) {
|
|
833
1140
|
throw new InvalidArgumentError("Linked file path must not be empty");
|
|
834
1141
|
}
|
|
835
1142
|
if (value.length > 4096) {
|
|
@@ -960,7 +1267,7 @@ async function assertWorkspaceInitialized3(basouRoot) {
|
|
|
960
1267
|
|
|
961
1268
|
// src/commands/exec.ts
|
|
962
1269
|
import { mkdir } from "fs/promises";
|
|
963
|
-
import { homedir } from "os";
|
|
1270
|
+
import { homedir as homedir2 } from "os";
|
|
964
1271
|
import { join as join2 } from "path";
|
|
965
1272
|
import {
|
|
966
1273
|
acquireLock as acquireLock3,
|
|
@@ -1212,7 +1519,7 @@ function buildInitialSession(input) {
|
|
|
1212
1519
|
source: { kind: "terminal", version: "0.1.0" },
|
|
1213
1520
|
started_at: input.startedAt,
|
|
1214
1521
|
status: "initialized",
|
|
1215
|
-
working_directory: sanitizeWorkingDirectory(input.cwd, { homedir:
|
|
1522
|
+
working_directory: sanitizeWorkingDirectory(input.cwd, { homedir: homedir2() }),
|
|
1216
1523
|
invocation: {
|
|
1217
1524
|
command: input.command,
|
|
1218
1525
|
args: [...input.args],
|
|
@@ -1355,9 +1662,9 @@ async function assertWorkspaceInitialized4(basouRoot) {
|
|
|
1355
1662
|
|
|
1356
1663
|
// src/commands/import.ts
|
|
1357
1664
|
import { createReadStream } from "fs";
|
|
1358
|
-
import { readdir, readFile, rm, stat } from "fs/promises";
|
|
1359
|
-
import { homedir as
|
|
1360
|
-
import { basename, join as join3, resolve } from "path";
|
|
1665
|
+
import { readdir, readFile as readFile2, rm, stat } from "fs/promises";
|
|
1666
|
+
import { homedir as homedir3 } from "os";
|
|
1667
|
+
import { basename, join as join3, resolve as resolve2 } from "path";
|
|
1361
1668
|
import { createInterface } from "readline";
|
|
1362
1669
|
import {
|
|
1363
1670
|
assertBasouRootSafe as assertBasouRootSafe6,
|
|
@@ -1425,10 +1732,10 @@ function resolveSourceRoots(args) {
|
|
|
1425
1732
|
const { projectFlags, manifest, repoRoot, cwd } = args;
|
|
1426
1733
|
let resolved;
|
|
1427
1734
|
if (projectFlags.length > 0) {
|
|
1428
|
-
resolved = projectFlags.map((p) =>
|
|
1735
|
+
resolved = projectFlags.map((p) => resolve2(cwd, p));
|
|
1429
1736
|
} else {
|
|
1430
1737
|
const roots = manifest.import?.source_roots;
|
|
1431
|
-
resolved = roots !== void 0 && roots.length > 0 ? roots.map((r) =>
|
|
1738
|
+
resolved = roots !== void 0 && roots.length > 0 ? roots.map((r) => resolve2(repoRoot, r)) : [repoRoot];
|
|
1432
1739
|
}
|
|
1433
1740
|
return [...new Set(resolved)];
|
|
1434
1741
|
}
|
|
@@ -1441,8 +1748,9 @@ async function doRunImportClaudeCode(options, ctx) {
|
|
|
1441
1748
|
repoRoot: repositoryRoot,
|
|
1442
1749
|
cwd: ctx.cwd ?? process.cwd()
|
|
1443
1750
|
});
|
|
1444
|
-
const projectsRoot = ctx.claudeProjectsDir ?? join3(
|
|
1751
|
+
const projectsRoot = ctx.claudeProjectsDir ?? join3(homedir3(), ".claude", "projects");
|
|
1445
1752
|
const files = await selectTranscriptFiles(projectsRoot, projectPaths, options);
|
|
1753
|
+
const projectSet = new Set(projectPaths);
|
|
1446
1754
|
const candidates = files.map((file) => {
|
|
1447
1755
|
const externalId = basename(file, ".jsonl");
|
|
1448
1756
|
return {
|
|
@@ -1450,6 +1758,8 @@ async function doRunImportClaudeCode(options, ctx) {
|
|
|
1450
1758
|
sourcePath: file,
|
|
1451
1759
|
toPayload: async () => {
|
|
1452
1760
|
const { records, sizeBytes } = await readJsonlRecords(file);
|
|
1761
|
+
const cwd = firstTranscriptCwd(records);
|
|
1762
|
+
if (cwd === void 0 || !projectSet.has(cwd)) return null;
|
|
1453
1763
|
return claudeTranscriptToImportPayload(records, {
|
|
1454
1764
|
workspaceId: manifest.workspace.id,
|
|
1455
1765
|
externalId,
|
|
@@ -1469,7 +1779,7 @@ async function doRunImportCodex(options, ctx) {
|
|
|
1469
1779
|
repoRoot: repositoryRoot,
|
|
1470
1780
|
cwd: ctx.cwd ?? process.cwd()
|
|
1471
1781
|
});
|
|
1472
|
-
const sessionsRoot = ctx.codexSessionsDir ?? join3(
|
|
1782
|
+
const sessionsRoot = ctx.codexSessionsDir ?? join3(homedir3(), ".codex", "sessions");
|
|
1473
1783
|
const rollouts = await discoverCodexRollouts(sessionsRoot, projectPaths, options);
|
|
1474
1784
|
const candidates = rollouts.map(({ file, externalId }) => ({
|
|
1475
1785
|
externalId,
|
|
@@ -1623,7 +1933,14 @@ async function classifyReimport(priors, sourcePath, externalId, counts) {
|
|
|
1623
1933
|
return prior;
|
|
1624
1934
|
}
|
|
1625
1935
|
function encodeProjectDir(projectPath) {
|
|
1626
|
-
return projectPath.
|
|
1936
|
+
return projectPath.replace(/[^a-zA-Z0-9]/g, "-");
|
|
1937
|
+
}
|
|
1938
|
+
function firstTranscriptCwd(records) {
|
|
1939
|
+
for (const record of records) {
|
|
1940
|
+
const cwd = record.cwd;
|
|
1941
|
+
if (typeof cwd === "string" && cwd.length > 0) return cwd;
|
|
1942
|
+
}
|
|
1943
|
+
return void 0;
|
|
1627
1944
|
}
|
|
1628
1945
|
async function loadExistingByExternalId(paths, sourceKind) {
|
|
1629
1946
|
const byExternalId = /* @__PURE__ */ new Map();
|
|
@@ -1789,7 +2106,7 @@ async function readFirstLine(file) {
|
|
|
1789
2106
|
async function readJsonlRecords(file) {
|
|
1790
2107
|
let buffer;
|
|
1791
2108
|
try {
|
|
1792
|
-
buffer = await
|
|
2109
|
+
buffer = await readFile2(file);
|
|
1793
2110
|
} catch (error) {
|
|
1794
2111
|
if (findErrorCode5(error, "ENOENT")) {
|
|
1795
2112
|
throw new Error("Source log not found", { cause: error });
|
|
@@ -1919,7 +2236,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
|
|
|
1919
2236
|
}
|
|
1920
2237
|
|
|
1921
2238
|
// src/commands/init.ts
|
|
1922
|
-
import { basename as basename2, relative, resolve as
|
|
2239
|
+
import { basename as basename2, relative, resolve as resolve3 } from "path";
|
|
1923
2240
|
import {
|
|
1924
2241
|
appendBasouGitignore,
|
|
1925
2242
|
createManifest,
|
|
@@ -1966,7 +2283,7 @@ async function doRunInit(options, ctx) {
|
|
|
1966
2283
|
repositoryUrl = await tryRemoteUrl(repositoryRoot);
|
|
1967
2284
|
}
|
|
1968
2285
|
const sourceRoots = (options.sourceRoot ?? []).map((p) => {
|
|
1969
|
-
const rel = relative(repositoryRoot,
|
|
2286
|
+
const rel = relative(repositoryRoot, resolve3(cwd, p));
|
|
1970
2287
|
return rel === "" ? "." : rel;
|
|
1971
2288
|
});
|
|
1972
2289
|
const paths = await ensureBasouDirectory(repositoryRoot);
|
|
@@ -2020,26 +2337,6 @@ import {
|
|
|
2020
2337
|
resolveSessionId as resolveSessionId2
|
|
2021
2338
|
} from "@basou/core";
|
|
2022
2339
|
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
2023
|
-
|
|
2024
|
-
// src/lib/repo-root.ts
|
|
2025
|
-
import { resolveBasouRepositoryRoot } from "@basou/core";
|
|
2026
|
-
async function resolveBasouRootForCommand(cwd, commandName) {
|
|
2027
|
-
try {
|
|
2028
|
-
return await resolveBasouRepositoryRoot(cwd, {
|
|
2029
|
-
onRedirect: ({ via, root }) => console.error(`Resolved workspace view to ${root} (via ${via}).`)
|
|
2030
|
-
});
|
|
2031
|
-
} catch (error) {
|
|
2032
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
2033
|
-
throw new Error(
|
|
2034
|
-
`Not a git repository. Run 'git init' first, then re-run 'basou ${commandName}'.`,
|
|
2035
|
-
{ cause: error }
|
|
2036
|
-
);
|
|
2037
|
-
}
|
|
2038
|
-
throw error;
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
|
-
// src/commands/note.ts
|
|
2043
2340
|
var LABEL_BODY_MAX = 80;
|
|
2044
2341
|
var LABEL_TRUNCATE_HEAD2 = LABEL_BODY_MAX - 3;
|
|
2045
2342
|
function registerNoteCommand(program2) {
|
|
@@ -2433,7 +2730,7 @@ import {
|
|
|
2433
2730
|
unlinkSync,
|
|
2434
2731
|
writeFileSync
|
|
2435
2732
|
} from "fs";
|
|
2436
|
-
import { basename as basename3, dirname, isAbsolute, join as join4, relative as relative2, resolve as
|
|
2733
|
+
import { basename as basename3, dirname, isAbsolute, join as join4, relative as relative2, resolve as resolve4 } from "path";
|
|
2437
2734
|
import {
|
|
2438
2735
|
basouPaths as basouPaths9,
|
|
2439
2736
|
GENERATED_END,
|
|
@@ -2685,7 +2982,7 @@ async function runProjectAdopt(options, ctx = {}) {
|
|
|
2685
2982
|
}
|
|
2686
2983
|
}
|
|
2687
2984
|
function classifySourceRoot(repositoryRoot, declaredPath) {
|
|
2688
|
-
const absolute =
|
|
2985
|
+
const absolute = resolve4(repositoryRoot, declaredPath);
|
|
2689
2986
|
let real;
|
|
2690
2987
|
try {
|
|
2691
2988
|
real = realpathSync(absolute);
|
|
@@ -2787,7 +3084,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
2787
3084
|
};
|
|
2788
3085
|
let real;
|
|
2789
3086
|
try {
|
|
2790
|
-
real = realpathSync(
|
|
3087
|
+
real = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
2791
3088
|
} catch {
|
|
2792
3089
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
2793
3090
|
}
|
|
@@ -2892,7 +3189,7 @@ function gatherRepoGitignore(repositoryRoot, entry) {
|
|
|
2892
3189
|
};
|
|
2893
3190
|
let real;
|
|
2894
3191
|
try {
|
|
2895
|
-
real = realpathSync(
|
|
3192
|
+
real = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
2896
3193
|
} catch {
|
|
2897
3194
|
return { ...base, reachable: false, currentLines: [] };
|
|
2898
3195
|
}
|
|
@@ -2913,7 +3210,7 @@ function readGitignoreLines(file) {
|
|
|
2913
3210
|
}
|
|
2914
3211
|
}
|
|
2915
3212
|
function applyGitignorePlan(repositoryRoot, plan) {
|
|
2916
|
-
const file = join4(realpathSync(
|
|
3213
|
+
const file = join4(realpathSync(resolve4(repositoryRoot, plan.path)), ".gitignore");
|
|
2917
3214
|
let existing = "";
|
|
2918
3215
|
try {
|
|
2919
3216
|
existing = readFileSync(file, "utf8");
|
|
@@ -3025,7 +3322,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
3025
3322
|
const base = { path: entry.path };
|
|
3026
3323
|
let real;
|
|
3027
3324
|
try {
|
|
3028
|
-
real = realpathSync(
|
|
3325
|
+
real = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
3029
3326
|
} catch {
|
|
3030
3327
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
3031
3328
|
}
|
|
@@ -3062,7 +3359,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
3062
3359
|
function applySymlinkPlan(repositoryRoot, plan) {
|
|
3063
3360
|
let real;
|
|
3064
3361
|
try {
|
|
3065
|
-
real = realpathSync(
|
|
3362
|
+
real = realpathSync(resolve4(repositoryRoot, plan.path));
|
|
3066
3363
|
} catch (error) {
|
|
3067
3364
|
const message = failureReason(error);
|
|
3068
3365
|
return { created: [], failed: plan.toCreate.map((c) => ({ file: c.name, message })) };
|
|
@@ -3207,7 +3504,7 @@ async function runProjectWorkspace(options, ctx = {}) {
|
|
|
3207
3504
|
}
|
|
3208
3505
|
}
|
|
3209
3506
|
function resolveViewDir(repositoryRoot, viewPath) {
|
|
3210
|
-
const abs =
|
|
3507
|
+
const abs = resolve4(repositoryRoot, viewPath);
|
|
3211
3508
|
try {
|
|
3212
3509
|
return realpathSync(abs);
|
|
3213
3510
|
} catch {
|
|
@@ -3221,7 +3518,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
|
|
|
3221
3518
|
function gatherViewRepo(repositoryRoot, viewDir, entry) {
|
|
3222
3519
|
let repoReal;
|
|
3223
3520
|
try {
|
|
3224
|
-
repoReal = realpathSync(
|
|
3521
|
+
repoReal = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
3225
3522
|
} catch {
|
|
3226
3523
|
return { path: entry.path, reachable: false };
|
|
3227
3524
|
}
|
|
@@ -3273,7 +3570,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
|
3273
3570
|
} catch {
|
|
3274
3571
|
return null;
|
|
3275
3572
|
}
|
|
3276
|
-
const resolved = isAbsolute(target) ? target :
|
|
3573
|
+
const resolved = isAbsolute(target) ? target : resolve4(viewDir, target);
|
|
3277
3574
|
try {
|
|
3278
3575
|
if (rosterRealpaths.has(realpathSync(resolved))) return null;
|
|
3279
3576
|
} catch {
|
|
@@ -3359,11 +3656,11 @@ async function doRunProjectWorkspace(options, ctx) {
|
|
|
3359
3656
|
} else {
|
|
3360
3657
|
const viewDir = resolveViewDir(repositoryRoot, viewPath);
|
|
3361
3658
|
const facts = roster.map((entry) => gatherViewRepo(repositoryRoot, viewDir, entry));
|
|
3362
|
-
const rosterNames = roster.map((entry) => basename3(
|
|
3659
|
+
const rosterNames = roster.map((entry) => basename3(resolve4(repositoryRoot, entry.path)));
|
|
3363
3660
|
const rosterRealpaths = /* @__PURE__ */ new Set();
|
|
3364
3661
|
for (const entry of roster) {
|
|
3365
3662
|
try {
|
|
3366
|
-
rosterRealpaths.add(realpathSync(
|
|
3663
|
+
rosterRealpaths.add(realpathSync(resolve4(repositoryRoot, entry.path)));
|
|
3367
3664
|
} catch {
|
|
3368
3665
|
}
|
|
3369
3666
|
}
|
|
@@ -3538,7 +3835,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
|
3538
3835
|
};
|
|
3539
3836
|
let real;
|
|
3540
3837
|
try {
|
|
3541
|
-
real = realpathSync(
|
|
3838
|
+
real = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
3542
3839
|
} catch {
|
|
3543
3840
|
return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
|
|
3544
3841
|
}
|
|
@@ -3767,7 +4064,7 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
3767
4064
|
};
|
|
3768
4065
|
let real;
|
|
3769
4066
|
try {
|
|
3770
|
-
real = realpathSync(
|
|
4067
|
+
real = realpathSync(resolve4(repositoryRoot, target));
|
|
3771
4068
|
} catch {
|
|
3772
4069
|
return empty;
|
|
3773
4070
|
}
|
|
@@ -3835,7 +4132,7 @@ async function doRunProjectArchive(target, options, ctx) {
|
|
|
3835
4132
|
const roster = manifest.repos ?? [];
|
|
3836
4133
|
let targetIsAnchor = false;
|
|
3837
4134
|
try {
|
|
3838
|
-
targetIsAnchor = realpathSync(
|
|
4135
|
+
targetIsAnchor = realpathSync(resolve4(repositoryRoot, target)) === realpathSync(repositoryRoot);
|
|
3839
4136
|
} catch {
|
|
3840
4137
|
targetIsAnchor = false;
|
|
3841
4138
|
}
|
|
@@ -3986,7 +4283,7 @@ async function doRunProjectRename(oldPath, newPath, options, ctx) {
|
|
|
3986
4283
|
const roster = manifest.repos ?? [];
|
|
3987
4284
|
let oldIsAnchor = false;
|
|
3988
4285
|
try {
|
|
3989
|
-
oldIsAnchor = realpathSync(
|
|
4286
|
+
oldIsAnchor = realpathSync(resolve4(repositoryRoot, oldPath)) === realpathSync(repositoryRoot);
|
|
3990
4287
|
} catch {
|
|
3991
4288
|
oldIsAnchor = false;
|
|
3992
4289
|
}
|
|
@@ -4101,13 +4398,13 @@ import { assertBasouRootSafe as assertBasouRootSafe9, basouPaths as basouPaths10
|
|
|
4101
4398
|
import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
4102
4399
|
|
|
4103
4400
|
// src/lib/portfolio-config.ts
|
|
4104
|
-
import { homedir as
|
|
4105
|
-
import { isAbsolute as isAbsolute2, join as join5, resolve as
|
|
4401
|
+
import { homedir as homedir4 } from "os";
|
|
4402
|
+
import { isAbsolute as isAbsolute2, join as join5, resolve as resolve5 } from "path";
|
|
4106
4403
|
import { readYamlFile as readYamlFile3 } from "@basou/core";
|
|
4107
|
-
var DEFAULT_PORTFOLIO_CONFIG_PATH = join5(
|
|
4404
|
+
var DEFAULT_PORTFOLIO_CONFIG_PATH = join5(homedir4(), ".basou", "portfolio.yaml");
|
|
4108
4405
|
function expandTilde(p) {
|
|
4109
|
-
if (p === "~") return
|
|
4110
|
-
if (p.startsWith("~/")) return join5(
|
|
4406
|
+
if (p === "~") return homedir4();
|
|
4407
|
+
if (p.startsWith("~/")) return join5(homedir4(), p.slice(2));
|
|
4111
4408
|
return p;
|
|
4112
4409
|
}
|
|
4113
4410
|
function isRecord(value) {
|
|
@@ -4146,7 +4443,7 @@ async function loadPortfolioConfig(configPath = DEFAULT_PORTFOLIO_CONFIG_PATH) {
|
|
|
4146
4443
|
"Portfolio workspace paths must be absolute (or start with '~'); use --workspace for relative ad-hoc paths."
|
|
4147
4444
|
);
|
|
4148
4445
|
}
|
|
4149
|
-
const abs =
|
|
4446
|
+
const abs = resolve5(expanded);
|
|
4150
4447
|
if (seen.has(abs)) continue;
|
|
4151
4448
|
seen.add(abs);
|
|
4152
4449
|
result.push(entry.label !== void 0 ? { path: abs, label: entry.label } : { path: abs });
|
|
@@ -4159,7 +4456,7 @@ async function loadPortfolioConfig(configPath = DEFAULT_PORTFOLIO_CONFIG_PATH) {
|
|
|
4159
4456
|
|
|
4160
4457
|
// src/commands/refresh-watch.ts
|
|
4161
4458
|
import { readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
4162
|
-
import { homedir as
|
|
4459
|
+
import { homedir as homedir5 } from "os";
|
|
4163
4460
|
import { join as join6 } from "path";
|
|
4164
4461
|
import { findErrorCode as findErrorCode8 } from "@basou/core";
|
|
4165
4462
|
var DEFAULT_WATCH_INTERVAL_SEC = 30;
|
|
@@ -4167,8 +4464,8 @@ var MIN_WATCH_INTERVAL_SEC = 5;
|
|
|
4167
4464
|
var MAX_WATCH_INTERVAL_SEC = 86400;
|
|
4168
4465
|
function watchedRoots(ctx) {
|
|
4169
4466
|
return [
|
|
4170
|
-
ctx.codexSessionsDir ?? join6(
|
|
4171
|
-
ctx.claudeProjectsDir ?? join6(
|
|
4467
|
+
ctx.codexSessionsDir ?? join6(homedir5(), ".codex", "sessions"),
|
|
4468
|
+
ctx.claudeProjectsDir ?? join6(homedir5(), ".claude", "projects")
|
|
4172
4469
|
];
|
|
4173
4470
|
}
|
|
4174
4471
|
async function scanSourceLogs(roots) {
|
|
@@ -4289,19 +4586,19 @@ function parseInterval(value) {
|
|
|
4289
4586
|
return seconds;
|
|
4290
4587
|
}
|
|
4291
4588
|
function abortableSleep(ms, signal) {
|
|
4292
|
-
return new Promise((
|
|
4589
|
+
return new Promise((resolve9) => {
|
|
4293
4590
|
if (signal.aborted) {
|
|
4294
|
-
|
|
4591
|
+
resolve9();
|
|
4295
4592
|
return;
|
|
4296
4593
|
}
|
|
4297
4594
|
let timer;
|
|
4298
4595
|
const onAbort = () => {
|
|
4299
4596
|
clearTimeout(timer);
|
|
4300
|
-
|
|
4597
|
+
resolve9();
|
|
4301
4598
|
};
|
|
4302
4599
|
timer = setTimeout(() => {
|
|
4303
4600
|
signal.removeEventListener("abort", onAbort);
|
|
4304
|
-
|
|
4601
|
+
resolve9();
|
|
4305
4602
|
}, ms);
|
|
4306
4603
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
4307
4604
|
});
|
|
@@ -4472,7 +4769,7 @@ function printRefreshSummary(result) {
|
|
|
4472
4769
|
if (result.decisions.decisionCount === 0) {
|
|
4473
4770
|
const hasSessions = result.handoff.status === "generated" && result.handoff.sessionCount > 0;
|
|
4474
4771
|
console.log(
|
|
4475
|
-
hasSessions ? "decisions: 0 (none auto-recorded from these sessions;
|
|
4772
|
+
hasSessions ? "decisions: 0 (none auto-recorded from these sessions; capture any made with 'basou decision capture')" : "decisions: 0"
|
|
4476
4773
|
);
|
|
4477
4774
|
} else {
|
|
4478
4775
|
console.log(`decisions: regenerated (${result.decisions.decisionCount})`);
|
|
@@ -4500,7 +4797,7 @@ async function assertWorkspaceInitialized8(basouRoot) {
|
|
|
4500
4797
|
}
|
|
4501
4798
|
|
|
4502
4799
|
// src/commands/report.ts
|
|
4503
|
-
import { isAbsolute as isAbsolute3, resolve as
|
|
4800
|
+
import { isAbsolute as isAbsolute3, resolve as resolve6 } from "path";
|
|
4504
4801
|
import {
|
|
4505
4802
|
assertBasouRootSafe as assertBasouRootSafe10,
|
|
4506
4803
|
basouPaths as basouPaths11,
|
|
@@ -4540,7 +4837,7 @@ async function doRunReportGenerate(options, ctx) {
|
|
|
4540
4837
|
onTaskSkip: (taskId, reason) => printTaskSkip(taskId, reason)
|
|
4541
4838
|
});
|
|
4542
4839
|
if (options.out !== void 0) {
|
|
4543
|
-
const outPath = isAbsolute3(options.out) ? options.out :
|
|
4840
|
+
const outPath = isAbsolute3(options.out) ? options.out : resolve6(cwd, options.out);
|
|
4544
4841
|
await writeMarkdownFile6(outPath, result.body);
|
|
4545
4842
|
const { sessions, decisions, tasks } = result.data;
|
|
4546
4843
|
console.error(
|
|
@@ -4703,7 +5000,7 @@ function renderReviewGaps(summary) {
|
|
|
4703
5000
|
|
|
4704
5001
|
// src/commands/run.ts
|
|
4705
5002
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
4706
|
-
import { homedir as
|
|
5003
|
+
import { homedir as homedir6 } from "os";
|
|
4707
5004
|
import { join as join7 } from "path";
|
|
4708
5005
|
import {
|
|
4709
5006
|
acquireLock as acquireLock5,
|
|
@@ -4889,7 +5186,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
4889
5186
|
const rawRelated = computeRelatedFiles(preSnapshot, postSnapshot, diff);
|
|
4890
5187
|
const relatedFiles = sanitizeRelatedFiles(rawRelated, {
|
|
4891
5188
|
workingDirectory: repoRoot,
|
|
4892
|
-
homedir:
|
|
5189
|
+
homedir: homedir6()
|
|
4893
5190
|
}).sanitized;
|
|
4894
5191
|
const finalStatus = decideFinalStatus2(result, signalReceived);
|
|
4895
5192
|
await appendEvent(sessionDir, {
|
|
@@ -5033,7 +5330,7 @@ function buildInitialSession2(input) {
|
|
|
5033
5330
|
source: { ...claudeCodeAdapterMetadata },
|
|
5034
5331
|
started_at: input.startedAt,
|
|
5035
5332
|
status: "initialized",
|
|
5036
|
-
working_directory: sanitizeWorkingDirectory2(input.cwd, { homedir:
|
|
5333
|
+
working_directory: sanitizeWorkingDirectory2(input.cwd, { homedir: homedir6() }),
|
|
5037
5334
|
invocation: {
|
|
5038
5335
|
command: input.command,
|
|
5039
5336
|
args: [...input.args],
|
|
@@ -5105,7 +5402,7 @@ async function resolveRepositoryRootForRun(cwd) {
|
|
|
5105
5402
|
}
|
|
5106
5403
|
|
|
5107
5404
|
// src/commands/session.ts
|
|
5108
|
-
import { readFile as
|
|
5405
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
5109
5406
|
import { basename as basename4, isAbsolute as isAbsolute4, join as join8, relative as relative3 } from "path";
|
|
5110
5407
|
import {
|
|
5111
5408
|
acquireLock as acquireLock6,
|
|
@@ -5545,7 +5842,7 @@ async function doRunSessionImport(options, ctx) {
|
|
|
5545
5842
|
}
|
|
5546
5843
|
async function readInputFile(path) {
|
|
5547
5844
|
try {
|
|
5548
|
-
return await
|
|
5845
|
+
return await readFile3(path, "utf8");
|
|
5549
5846
|
} catch (error) {
|
|
5550
5847
|
if (findErrorCode11(error, "ENOENT")) {
|
|
5551
5848
|
throw new Error("Import source not found", { cause: error });
|
|
@@ -5662,7 +5959,7 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
5662
5959
|
}
|
|
5663
5960
|
async function readNoteFile(path) {
|
|
5664
5961
|
try {
|
|
5665
|
-
return await
|
|
5962
|
+
return await readFile3(path, "utf8");
|
|
5666
5963
|
} catch (error) {
|
|
5667
5964
|
if (findErrorCode11(error, "ENOENT")) {
|
|
5668
5965
|
throw new Error("Note source not found", { cause: error });
|
|
@@ -5969,7 +6266,7 @@ async function resolveRepositoryRootForStatus(cwd) {
|
|
|
5969
6266
|
}
|
|
5970
6267
|
|
|
5971
6268
|
// src/commands/task.ts
|
|
5972
|
-
import { readFile as
|
|
6269
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
5973
6270
|
import { join as join9 } from "path";
|
|
5974
6271
|
import {
|
|
5975
6272
|
archiveTask,
|
|
@@ -6985,7 +7282,7 @@ function parsePositiveInt2(raw) {
|
|
|
6985
7282
|
}
|
|
6986
7283
|
async function readDescriptionFile(path) {
|
|
6987
7284
|
try {
|
|
6988
|
-
return await
|
|
7285
|
+
return await readFile4(path, "utf8");
|
|
6989
7286
|
} catch (error) {
|
|
6990
7287
|
if (findErrorCode14(error, "ENOENT")) {
|
|
6991
7288
|
throw new Error("Description source not found", { cause: error });
|
|
@@ -7200,7 +7497,7 @@ async function assertWorkspaceInitialized13(basouRoot) {
|
|
|
7200
7497
|
// src/commands/view.ts
|
|
7201
7498
|
import { spawn } from "child_process";
|
|
7202
7499
|
import { createHash } from "crypto";
|
|
7203
|
-
import { basename as basename5, resolve as
|
|
7500
|
+
import { basename as basename5, resolve as resolve8 } from "path";
|
|
7204
7501
|
import {
|
|
7205
7502
|
assertBasouRootSafe as assertBasouRootSafe17,
|
|
7206
7503
|
basouPaths as basouPaths19,
|
|
@@ -7213,7 +7510,7 @@ import { InvalidArgumentError as InvalidArgumentError7 } from "commander";
|
|
|
7213
7510
|
// src/lib/portfolio-safety.ts
|
|
7214
7511
|
import { execFile } from "child_process";
|
|
7215
7512
|
import { lstat, realpath } from "fs/promises";
|
|
7216
|
-
import { isAbsolute as isAbsolute5, join as join10, relative as relative4, resolve as
|
|
7513
|
+
import { isAbsolute as isAbsolute5, join as join10, relative as relative4, resolve as resolve7 } from "path";
|
|
7217
7514
|
import { promisify } from "util";
|
|
7218
7515
|
import { readManifest as readManifest10 } from "@basou/core";
|
|
7219
7516
|
var execFileAsync = promisify(execFile);
|
|
@@ -7224,7 +7521,7 @@ async function canonical(p) {
|
|
|
7224
7521
|
try {
|
|
7225
7522
|
return await realpath(p);
|
|
7226
7523
|
} catch {
|
|
7227
|
-
return
|
|
7524
|
+
return resolve7(p);
|
|
7228
7525
|
}
|
|
7229
7526
|
}
|
|
7230
7527
|
function isInside(child, parent) {
|
|
@@ -7286,7 +7583,7 @@ async function checkPortfolioSafety(workspaces) {
|
|
|
7286
7583
|
}
|
|
7287
7584
|
const monitored = /* @__PURE__ */ new Map();
|
|
7288
7585
|
for (const root of sourceRoots) {
|
|
7289
|
-
const display =
|
|
7586
|
+
const display = resolve7(ws.repoRoot, root);
|
|
7290
7587
|
const real = await canonical(display);
|
|
7291
7588
|
if (real !== wsReal) monitored.set(real, display);
|
|
7292
7589
|
}
|
|
@@ -7995,7 +8292,7 @@ function startViewServer(opts) {
|
|
|
7995
8292
|
};
|
|
7996
8293
|
let boundPort = port;
|
|
7997
8294
|
const getPort = () => boundPort;
|
|
7998
|
-
return new Promise((
|
|
8295
|
+
return new Promise((resolve9, reject) => {
|
|
7999
8296
|
const server = createServer((req, res) => {
|
|
8000
8297
|
handleRequest(req, res, deps, getPort, runExclusive).catch((error) => {
|
|
8001
8298
|
sendError(res, error instanceof HttpError ? error.status : 500, pathlessMessage(error));
|
|
@@ -8006,7 +8303,7 @@ function startViewServer(opts) {
|
|
|
8006
8303
|
const address = server.address();
|
|
8007
8304
|
boundPort = isAddressInfo(address) ? address.port : port;
|
|
8008
8305
|
server.off("error", reject);
|
|
8009
|
-
|
|
8306
|
+
resolve9({
|
|
8010
8307
|
url: `http://${host}:${boundPort}`,
|
|
8011
8308
|
port: boundPort,
|
|
8012
8309
|
close: () => closeServer(server)
|
|
@@ -8018,8 +8315,8 @@ function isAddressInfo(value) {
|
|
|
8018
8315
|
return value !== null && typeof value === "object";
|
|
8019
8316
|
}
|
|
8020
8317
|
function closeServer(server) {
|
|
8021
|
-
return new Promise((
|
|
8022
|
-
server.close(() =>
|
|
8318
|
+
return new Promise((resolve9) => {
|
|
8319
|
+
server.close(() => resolve9());
|
|
8023
8320
|
server.closeAllConnections();
|
|
8024
8321
|
});
|
|
8025
8322
|
}
|
|
@@ -8513,12 +8810,12 @@ async function buildSingleDeps(ctx, cwd) {
|
|
|
8513
8810
|
return { workspaces: [entry], mode: "single", nowProvider: nowProviderOf(ctx) };
|
|
8514
8811
|
}
|
|
8515
8812
|
async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
|
|
8516
|
-
const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path:
|
|
8813
|
+
const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path: resolve8(cwd, p) })) : await loadPortfolioConfig(ctx.portfolioConfigPath);
|
|
8517
8814
|
const entries = [];
|
|
8518
8815
|
const seenPath = /* @__PURE__ */ new Set();
|
|
8519
8816
|
const seenKey = /* @__PURE__ */ new Set();
|
|
8520
8817
|
for (const spec of specs) {
|
|
8521
|
-
const repoRoot =
|
|
8818
|
+
const repoRoot = resolve8(spec.path);
|
|
8522
8819
|
if (seenPath.has(repoRoot)) continue;
|
|
8523
8820
|
seenPath.add(repoRoot);
|
|
8524
8821
|
const entry = await buildWorkspaceEntry(repoRoot, ctx, spec.label);
|
|
@@ -8590,7 +8887,7 @@ function openInBrowser(url, override) {
|
|
|
8590
8887
|
}
|
|
8591
8888
|
}
|
|
8592
8889
|
function waitForShutdown(signal) {
|
|
8593
|
-
return new Promise((
|
|
8890
|
+
return new Promise((resolve9) => {
|
|
8594
8891
|
const cleanup = () => {
|
|
8595
8892
|
process.off("SIGINT", onSignal);
|
|
8596
8893
|
process.off("SIGTERM", onSignal);
|
|
@@ -8598,18 +8895,18 @@ function waitForShutdown(signal) {
|
|
|
8598
8895
|
};
|
|
8599
8896
|
const onSignal = () => {
|
|
8600
8897
|
cleanup();
|
|
8601
|
-
|
|
8898
|
+
resolve9();
|
|
8602
8899
|
};
|
|
8603
8900
|
const onAbort = () => {
|
|
8604
8901
|
cleanup();
|
|
8605
|
-
|
|
8902
|
+
resolve9();
|
|
8606
8903
|
};
|
|
8607
8904
|
process.on("SIGINT", onSignal);
|
|
8608
8905
|
process.on("SIGTERM", onSignal);
|
|
8609
8906
|
if (signal !== void 0) {
|
|
8610
8907
|
if (signal.aborted) {
|
|
8611
8908
|
cleanup();
|
|
8612
|
-
|
|
8909
|
+
resolve9();
|
|
8613
8910
|
return;
|
|
8614
8911
|
}
|
|
8615
8912
|
signal.addEventListener("abort", onAbort);
|