@basou/cli 0.13.1 → 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 +381 -94
- package/dist/index.js.map +1 -1
- package/dist/program.js +381 -94
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.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(program) {
|
|
@@ -675,7 +700,34 @@ function registerDecisionCommand(program) {
|
|
|
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,7 +1748,7 @@ 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);
|
|
1446
1753
|
const projectSet = new Set(projectPaths);
|
|
1447
1754
|
const candidates = files.map((file) => {
|
|
@@ -1472,7 +1779,7 @@ async function doRunImportCodex(options, ctx) {
|
|
|
1472
1779
|
repoRoot: repositoryRoot,
|
|
1473
1780
|
cwd: ctx.cwd ?? process.cwd()
|
|
1474
1781
|
});
|
|
1475
|
-
const sessionsRoot = ctx.codexSessionsDir ?? join3(
|
|
1782
|
+
const sessionsRoot = ctx.codexSessionsDir ?? join3(homedir3(), ".codex", "sessions");
|
|
1476
1783
|
const rollouts = await discoverCodexRollouts(sessionsRoot, projectPaths, options);
|
|
1477
1784
|
const candidates = rollouts.map(({ file, externalId }) => ({
|
|
1478
1785
|
externalId,
|
|
@@ -1799,7 +2106,7 @@ async function readFirstLine(file) {
|
|
|
1799
2106
|
async function readJsonlRecords(file) {
|
|
1800
2107
|
let buffer;
|
|
1801
2108
|
try {
|
|
1802
|
-
buffer = await
|
|
2109
|
+
buffer = await readFile2(file);
|
|
1803
2110
|
} catch (error) {
|
|
1804
2111
|
if (findErrorCode5(error, "ENOENT")) {
|
|
1805
2112
|
throw new Error("Source log not found", { cause: error });
|
|
@@ -1929,7 +2236,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
|
|
|
1929
2236
|
}
|
|
1930
2237
|
|
|
1931
2238
|
// src/commands/init.ts
|
|
1932
|
-
import { basename as basename2, relative, resolve as
|
|
2239
|
+
import { basename as basename2, relative, resolve as resolve3 } from "path";
|
|
1933
2240
|
import {
|
|
1934
2241
|
appendBasouGitignore,
|
|
1935
2242
|
createManifest,
|
|
@@ -1976,7 +2283,7 @@ async function doRunInit(options, ctx) {
|
|
|
1976
2283
|
repositoryUrl = await tryRemoteUrl(repositoryRoot);
|
|
1977
2284
|
}
|
|
1978
2285
|
const sourceRoots = (options.sourceRoot ?? []).map((p) => {
|
|
1979
|
-
const rel = relative(repositoryRoot,
|
|
2286
|
+
const rel = relative(repositoryRoot, resolve3(cwd, p));
|
|
1980
2287
|
return rel === "" ? "." : rel;
|
|
1981
2288
|
});
|
|
1982
2289
|
const paths = await ensureBasouDirectory(repositoryRoot);
|
|
@@ -2030,26 +2337,6 @@ import {
|
|
|
2030
2337
|
resolveSessionId as resolveSessionId2
|
|
2031
2338
|
} from "@basou/core";
|
|
2032
2339
|
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
2033
|
-
|
|
2034
|
-
// src/lib/repo-root.ts
|
|
2035
|
-
import { resolveBasouRepositoryRoot } from "@basou/core";
|
|
2036
|
-
async function resolveBasouRootForCommand(cwd, commandName) {
|
|
2037
|
-
try {
|
|
2038
|
-
return await resolveBasouRepositoryRoot(cwd, {
|
|
2039
|
-
onRedirect: ({ via, root }) => console.error(`Resolved workspace view to ${root} (via ${via}).`)
|
|
2040
|
-
});
|
|
2041
|
-
} catch (error) {
|
|
2042
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
2043
|
-
throw new Error(
|
|
2044
|
-
`Not a git repository. Run 'git init' first, then re-run 'basou ${commandName}'.`,
|
|
2045
|
-
{ cause: error }
|
|
2046
|
-
);
|
|
2047
|
-
}
|
|
2048
|
-
throw error;
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
|
|
2052
|
-
// src/commands/note.ts
|
|
2053
2340
|
var LABEL_BODY_MAX = 80;
|
|
2054
2341
|
var LABEL_TRUNCATE_HEAD2 = LABEL_BODY_MAX - 3;
|
|
2055
2342
|
function registerNoteCommand(program) {
|
|
@@ -2443,7 +2730,7 @@ import {
|
|
|
2443
2730
|
unlinkSync,
|
|
2444
2731
|
writeFileSync
|
|
2445
2732
|
} from "fs";
|
|
2446
|
-
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";
|
|
2447
2734
|
import {
|
|
2448
2735
|
basouPaths as basouPaths9,
|
|
2449
2736
|
GENERATED_END,
|
|
@@ -2695,7 +2982,7 @@ async function runProjectAdopt(options, ctx = {}) {
|
|
|
2695
2982
|
}
|
|
2696
2983
|
}
|
|
2697
2984
|
function classifySourceRoot(repositoryRoot, declaredPath) {
|
|
2698
|
-
const absolute =
|
|
2985
|
+
const absolute = resolve4(repositoryRoot, declaredPath);
|
|
2699
2986
|
let real;
|
|
2700
2987
|
try {
|
|
2701
2988
|
real = realpathSync(absolute);
|
|
@@ -2797,7 +3084,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
2797
3084
|
};
|
|
2798
3085
|
let real;
|
|
2799
3086
|
try {
|
|
2800
|
-
real = realpathSync(
|
|
3087
|
+
real = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
2801
3088
|
} catch {
|
|
2802
3089
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
2803
3090
|
}
|
|
@@ -2902,7 +3189,7 @@ function gatherRepoGitignore(repositoryRoot, entry) {
|
|
|
2902
3189
|
};
|
|
2903
3190
|
let real;
|
|
2904
3191
|
try {
|
|
2905
|
-
real = realpathSync(
|
|
3192
|
+
real = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
2906
3193
|
} catch {
|
|
2907
3194
|
return { ...base, reachable: false, currentLines: [] };
|
|
2908
3195
|
}
|
|
@@ -2923,7 +3210,7 @@ function readGitignoreLines(file) {
|
|
|
2923
3210
|
}
|
|
2924
3211
|
}
|
|
2925
3212
|
function applyGitignorePlan(repositoryRoot, plan) {
|
|
2926
|
-
const file = join4(realpathSync(
|
|
3213
|
+
const file = join4(realpathSync(resolve4(repositoryRoot, plan.path)), ".gitignore");
|
|
2927
3214
|
let existing = "";
|
|
2928
3215
|
try {
|
|
2929
3216
|
existing = readFileSync(file, "utf8");
|
|
@@ -3035,7 +3322,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
3035
3322
|
const base = { path: entry.path };
|
|
3036
3323
|
let real;
|
|
3037
3324
|
try {
|
|
3038
|
-
real = realpathSync(
|
|
3325
|
+
real = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
3039
3326
|
} catch {
|
|
3040
3327
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
3041
3328
|
}
|
|
@@ -3072,7 +3359,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
3072
3359
|
function applySymlinkPlan(repositoryRoot, plan) {
|
|
3073
3360
|
let real;
|
|
3074
3361
|
try {
|
|
3075
|
-
real = realpathSync(
|
|
3362
|
+
real = realpathSync(resolve4(repositoryRoot, plan.path));
|
|
3076
3363
|
} catch (error) {
|
|
3077
3364
|
const message = failureReason(error);
|
|
3078
3365
|
return { created: [], failed: plan.toCreate.map((c) => ({ file: c.name, message })) };
|
|
@@ -3217,7 +3504,7 @@ async function runProjectWorkspace(options, ctx = {}) {
|
|
|
3217
3504
|
}
|
|
3218
3505
|
}
|
|
3219
3506
|
function resolveViewDir(repositoryRoot, viewPath) {
|
|
3220
|
-
const abs =
|
|
3507
|
+
const abs = resolve4(repositoryRoot, viewPath);
|
|
3221
3508
|
try {
|
|
3222
3509
|
return realpathSync(abs);
|
|
3223
3510
|
} catch {
|
|
@@ -3231,7 +3518,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
|
|
|
3231
3518
|
function gatherViewRepo(repositoryRoot, viewDir, entry) {
|
|
3232
3519
|
let repoReal;
|
|
3233
3520
|
try {
|
|
3234
|
-
repoReal = realpathSync(
|
|
3521
|
+
repoReal = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
3235
3522
|
} catch {
|
|
3236
3523
|
return { path: entry.path, reachable: false };
|
|
3237
3524
|
}
|
|
@@ -3283,7 +3570,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
|
3283
3570
|
} catch {
|
|
3284
3571
|
return null;
|
|
3285
3572
|
}
|
|
3286
|
-
const resolved = isAbsolute(target) ? target :
|
|
3573
|
+
const resolved = isAbsolute(target) ? target : resolve4(viewDir, target);
|
|
3287
3574
|
try {
|
|
3288
3575
|
if (rosterRealpaths.has(realpathSync(resolved))) return null;
|
|
3289
3576
|
} catch {
|
|
@@ -3369,11 +3656,11 @@ async function doRunProjectWorkspace(options, ctx) {
|
|
|
3369
3656
|
} else {
|
|
3370
3657
|
const viewDir = resolveViewDir(repositoryRoot, viewPath);
|
|
3371
3658
|
const facts = roster.map((entry) => gatherViewRepo(repositoryRoot, viewDir, entry));
|
|
3372
|
-
const rosterNames = roster.map((entry) => basename3(
|
|
3659
|
+
const rosterNames = roster.map((entry) => basename3(resolve4(repositoryRoot, entry.path)));
|
|
3373
3660
|
const rosterRealpaths = /* @__PURE__ */ new Set();
|
|
3374
3661
|
for (const entry of roster) {
|
|
3375
3662
|
try {
|
|
3376
|
-
rosterRealpaths.add(realpathSync(
|
|
3663
|
+
rosterRealpaths.add(realpathSync(resolve4(repositoryRoot, entry.path)));
|
|
3377
3664
|
} catch {
|
|
3378
3665
|
}
|
|
3379
3666
|
}
|
|
@@ -3548,7 +3835,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
|
3548
3835
|
};
|
|
3549
3836
|
let real;
|
|
3550
3837
|
try {
|
|
3551
|
-
real = realpathSync(
|
|
3838
|
+
real = realpathSync(resolve4(repositoryRoot, entry.path));
|
|
3552
3839
|
} catch {
|
|
3553
3840
|
return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
|
|
3554
3841
|
}
|
|
@@ -3777,7 +4064,7 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
3777
4064
|
};
|
|
3778
4065
|
let real;
|
|
3779
4066
|
try {
|
|
3780
|
-
real = realpathSync(
|
|
4067
|
+
real = realpathSync(resolve4(repositoryRoot, target));
|
|
3781
4068
|
} catch {
|
|
3782
4069
|
return empty;
|
|
3783
4070
|
}
|
|
@@ -3845,7 +4132,7 @@ async function doRunProjectArchive(target, options, ctx) {
|
|
|
3845
4132
|
const roster = manifest.repos ?? [];
|
|
3846
4133
|
let targetIsAnchor = false;
|
|
3847
4134
|
try {
|
|
3848
|
-
targetIsAnchor = realpathSync(
|
|
4135
|
+
targetIsAnchor = realpathSync(resolve4(repositoryRoot, target)) === realpathSync(repositoryRoot);
|
|
3849
4136
|
} catch {
|
|
3850
4137
|
targetIsAnchor = false;
|
|
3851
4138
|
}
|
|
@@ -3996,7 +4283,7 @@ async function doRunProjectRename(oldPath, newPath, options, ctx) {
|
|
|
3996
4283
|
const roster = manifest.repos ?? [];
|
|
3997
4284
|
let oldIsAnchor = false;
|
|
3998
4285
|
try {
|
|
3999
|
-
oldIsAnchor = realpathSync(
|
|
4286
|
+
oldIsAnchor = realpathSync(resolve4(repositoryRoot, oldPath)) === realpathSync(repositoryRoot);
|
|
4000
4287
|
} catch {
|
|
4001
4288
|
oldIsAnchor = false;
|
|
4002
4289
|
}
|
|
@@ -4111,13 +4398,13 @@ import { assertBasouRootSafe as assertBasouRootSafe9, basouPaths as basouPaths10
|
|
|
4111
4398
|
import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
4112
4399
|
|
|
4113
4400
|
// src/lib/portfolio-config.ts
|
|
4114
|
-
import { homedir as
|
|
4115
|
-
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";
|
|
4116
4403
|
import { readYamlFile as readYamlFile3 } from "@basou/core";
|
|
4117
|
-
var DEFAULT_PORTFOLIO_CONFIG_PATH = join5(
|
|
4404
|
+
var DEFAULT_PORTFOLIO_CONFIG_PATH = join5(homedir4(), ".basou", "portfolio.yaml");
|
|
4118
4405
|
function expandTilde(p) {
|
|
4119
|
-
if (p === "~") return
|
|
4120
|
-
if (p.startsWith("~/")) return join5(
|
|
4406
|
+
if (p === "~") return homedir4();
|
|
4407
|
+
if (p.startsWith("~/")) return join5(homedir4(), p.slice(2));
|
|
4121
4408
|
return p;
|
|
4122
4409
|
}
|
|
4123
4410
|
function isRecord(value) {
|
|
@@ -4156,7 +4443,7 @@ async function loadPortfolioConfig(configPath = DEFAULT_PORTFOLIO_CONFIG_PATH) {
|
|
|
4156
4443
|
"Portfolio workspace paths must be absolute (or start with '~'); use --workspace for relative ad-hoc paths."
|
|
4157
4444
|
);
|
|
4158
4445
|
}
|
|
4159
|
-
const abs =
|
|
4446
|
+
const abs = resolve5(expanded);
|
|
4160
4447
|
if (seen.has(abs)) continue;
|
|
4161
4448
|
seen.add(abs);
|
|
4162
4449
|
result.push(entry.label !== void 0 ? { path: abs, label: entry.label } : { path: abs });
|
|
@@ -4169,7 +4456,7 @@ async function loadPortfolioConfig(configPath = DEFAULT_PORTFOLIO_CONFIG_PATH) {
|
|
|
4169
4456
|
|
|
4170
4457
|
// src/commands/refresh-watch.ts
|
|
4171
4458
|
import { readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
4172
|
-
import { homedir as
|
|
4459
|
+
import { homedir as homedir5 } from "os";
|
|
4173
4460
|
import { join as join6 } from "path";
|
|
4174
4461
|
import { findErrorCode as findErrorCode8 } from "@basou/core";
|
|
4175
4462
|
var DEFAULT_WATCH_INTERVAL_SEC = 30;
|
|
@@ -4177,8 +4464,8 @@ var MIN_WATCH_INTERVAL_SEC = 5;
|
|
|
4177
4464
|
var MAX_WATCH_INTERVAL_SEC = 86400;
|
|
4178
4465
|
function watchedRoots(ctx) {
|
|
4179
4466
|
return [
|
|
4180
|
-
ctx.codexSessionsDir ?? join6(
|
|
4181
|
-
ctx.claudeProjectsDir ?? join6(
|
|
4467
|
+
ctx.codexSessionsDir ?? join6(homedir5(), ".codex", "sessions"),
|
|
4468
|
+
ctx.claudeProjectsDir ?? join6(homedir5(), ".claude", "projects")
|
|
4182
4469
|
];
|
|
4183
4470
|
}
|
|
4184
4471
|
async function scanSourceLogs(roots) {
|
|
@@ -4299,19 +4586,19 @@ function parseInterval(value) {
|
|
|
4299
4586
|
return seconds;
|
|
4300
4587
|
}
|
|
4301
4588
|
function abortableSleep(ms, signal) {
|
|
4302
|
-
return new Promise((
|
|
4589
|
+
return new Promise((resolve9) => {
|
|
4303
4590
|
if (signal.aborted) {
|
|
4304
|
-
|
|
4591
|
+
resolve9();
|
|
4305
4592
|
return;
|
|
4306
4593
|
}
|
|
4307
4594
|
let timer;
|
|
4308
4595
|
const onAbort = () => {
|
|
4309
4596
|
clearTimeout(timer);
|
|
4310
|
-
|
|
4597
|
+
resolve9();
|
|
4311
4598
|
};
|
|
4312
4599
|
timer = setTimeout(() => {
|
|
4313
4600
|
signal.removeEventListener("abort", onAbort);
|
|
4314
|
-
|
|
4601
|
+
resolve9();
|
|
4315
4602
|
}, ms);
|
|
4316
4603
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
4317
4604
|
});
|
|
@@ -4482,7 +4769,7 @@ function printRefreshSummary(result) {
|
|
|
4482
4769
|
if (result.decisions.decisionCount === 0) {
|
|
4483
4770
|
const hasSessions = result.handoff.status === "generated" && result.handoff.sessionCount > 0;
|
|
4484
4771
|
console.log(
|
|
4485
|
-
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"
|
|
4486
4773
|
);
|
|
4487
4774
|
} else {
|
|
4488
4775
|
console.log(`decisions: regenerated (${result.decisions.decisionCount})`);
|
|
@@ -4510,7 +4797,7 @@ async function assertWorkspaceInitialized8(basouRoot) {
|
|
|
4510
4797
|
}
|
|
4511
4798
|
|
|
4512
4799
|
// src/commands/report.ts
|
|
4513
|
-
import { isAbsolute as isAbsolute3, resolve as
|
|
4800
|
+
import { isAbsolute as isAbsolute3, resolve as resolve6 } from "path";
|
|
4514
4801
|
import {
|
|
4515
4802
|
assertBasouRootSafe as assertBasouRootSafe10,
|
|
4516
4803
|
basouPaths as basouPaths11,
|
|
@@ -4550,7 +4837,7 @@ async function doRunReportGenerate(options, ctx) {
|
|
|
4550
4837
|
onTaskSkip: (taskId, reason) => printTaskSkip(taskId, reason)
|
|
4551
4838
|
});
|
|
4552
4839
|
if (options.out !== void 0) {
|
|
4553
|
-
const outPath = isAbsolute3(options.out) ? options.out :
|
|
4840
|
+
const outPath = isAbsolute3(options.out) ? options.out : resolve6(cwd, options.out);
|
|
4554
4841
|
await writeMarkdownFile6(outPath, result.body);
|
|
4555
4842
|
const { sessions, decisions, tasks } = result.data;
|
|
4556
4843
|
console.error(
|
|
@@ -4713,7 +5000,7 @@ function renderReviewGaps(summary) {
|
|
|
4713
5000
|
|
|
4714
5001
|
// src/commands/run.ts
|
|
4715
5002
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
4716
|
-
import { homedir as
|
|
5003
|
+
import { homedir as homedir6 } from "os";
|
|
4717
5004
|
import { join as join7 } from "path";
|
|
4718
5005
|
import {
|
|
4719
5006
|
acquireLock as acquireLock5,
|
|
@@ -4899,7 +5186,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
4899
5186
|
const rawRelated = computeRelatedFiles(preSnapshot, postSnapshot, diff);
|
|
4900
5187
|
const relatedFiles = sanitizeRelatedFiles(rawRelated, {
|
|
4901
5188
|
workingDirectory: repoRoot,
|
|
4902
|
-
homedir:
|
|
5189
|
+
homedir: homedir6()
|
|
4903
5190
|
}).sanitized;
|
|
4904
5191
|
const finalStatus = decideFinalStatus2(result, signalReceived);
|
|
4905
5192
|
await appendEvent(sessionDir, {
|
|
@@ -5043,7 +5330,7 @@ function buildInitialSession2(input) {
|
|
|
5043
5330
|
source: { ...claudeCodeAdapterMetadata },
|
|
5044
5331
|
started_at: input.startedAt,
|
|
5045
5332
|
status: "initialized",
|
|
5046
|
-
working_directory: sanitizeWorkingDirectory2(input.cwd, { homedir:
|
|
5333
|
+
working_directory: sanitizeWorkingDirectory2(input.cwd, { homedir: homedir6() }),
|
|
5047
5334
|
invocation: {
|
|
5048
5335
|
command: input.command,
|
|
5049
5336
|
args: [...input.args],
|
|
@@ -5115,7 +5402,7 @@ async function resolveRepositoryRootForRun(cwd) {
|
|
|
5115
5402
|
}
|
|
5116
5403
|
|
|
5117
5404
|
// src/commands/session.ts
|
|
5118
|
-
import { readFile as
|
|
5405
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
5119
5406
|
import { basename as basename4, isAbsolute as isAbsolute4, join as join8, relative as relative3 } from "path";
|
|
5120
5407
|
import {
|
|
5121
5408
|
acquireLock as acquireLock6,
|
|
@@ -5555,7 +5842,7 @@ async function doRunSessionImport(options, ctx) {
|
|
|
5555
5842
|
}
|
|
5556
5843
|
async function readInputFile(path) {
|
|
5557
5844
|
try {
|
|
5558
|
-
return await
|
|
5845
|
+
return await readFile3(path, "utf8");
|
|
5559
5846
|
} catch (error) {
|
|
5560
5847
|
if (findErrorCode11(error, "ENOENT")) {
|
|
5561
5848
|
throw new Error("Import source not found", { cause: error });
|
|
@@ -5672,7 +5959,7 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
5672
5959
|
}
|
|
5673
5960
|
async function readNoteFile(path) {
|
|
5674
5961
|
try {
|
|
5675
|
-
return await
|
|
5962
|
+
return await readFile3(path, "utf8");
|
|
5676
5963
|
} catch (error) {
|
|
5677
5964
|
if (findErrorCode11(error, "ENOENT")) {
|
|
5678
5965
|
throw new Error("Note source not found", { cause: error });
|
|
@@ -5979,7 +6266,7 @@ async function resolveRepositoryRootForStatus(cwd) {
|
|
|
5979
6266
|
}
|
|
5980
6267
|
|
|
5981
6268
|
// src/commands/task.ts
|
|
5982
|
-
import { readFile as
|
|
6269
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
5983
6270
|
import { join as join9 } from "path";
|
|
5984
6271
|
import {
|
|
5985
6272
|
archiveTask,
|
|
@@ -6995,7 +7282,7 @@ function parsePositiveInt2(raw) {
|
|
|
6995
7282
|
}
|
|
6996
7283
|
async function readDescriptionFile(path) {
|
|
6997
7284
|
try {
|
|
6998
|
-
return await
|
|
7285
|
+
return await readFile4(path, "utf8");
|
|
6999
7286
|
} catch (error) {
|
|
7000
7287
|
if (findErrorCode14(error, "ENOENT")) {
|
|
7001
7288
|
throw new Error("Description source not found", { cause: error });
|
|
@@ -7210,7 +7497,7 @@ async function assertWorkspaceInitialized13(basouRoot) {
|
|
|
7210
7497
|
// src/commands/view.ts
|
|
7211
7498
|
import { spawn } from "child_process";
|
|
7212
7499
|
import { createHash } from "crypto";
|
|
7213
|
-
import { basename as basename5, resolve as
|
|
7500
|
+
import { basename as basename5, resolve as resolve8 } from "path";
|
|
7214
7501
|
import {
|
|
7215
7502
|
assertBasouRootSafe as assertBasouRootSafe17,
|
|
7216
7503
|
basouPaths as basouPaths19,
|
|
@@ -7223,7 +7510,7 @@ import { InvalidArgumentError as InvalidArgumentError7 } from "commander";
|
|
|
7223
7510
|
// src/lib/portfolio-safety.ts
|
|
7224
7511
|
import { execFile } from "child_process";
|
|
7225
7512
|
import { lstat, realpath } from "fs/promises";
|
|
7226
|
-
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";
|
|
7227
7514
|
import { promisify } from "util";
|
|
7228
7515
|
import { readManifest as readManifest10 } from "@basou/core";
|
|
7229
7516
|
var execFileAsync = promisify(execFile);
|
|
@@ -7234,7 +7521,7 @@ async function canonical(p) {
|
|
|
7234
7521
|
try {
|
|
7235
7522
|
return await realpath(p);
|
|
7236
7523
|
} catch {
|
|
7237
|
-
return
|
|
7524
|
+
return resolve7(p);
|
|
7238
7525
|
}
|
|
7239
7526
|
}
|
|
7240
7527
|
function isInside(child, parent) {
|
|
@@ -7296,7 +7583,7 @@ async function checkPortfolioSafety(workspaces) {
|
|
|
7296
7583
|
}
|
|
7297
7584
|
const monitored = /* @__PURE__ */ new Map();
|
|
7298
7585
|
for (const root of sourceRoots) {
|
|
7299
|
-
const display =
|
|
7586
|
+
const display = resolve7(ws.repoRoot, root);
|
|
7300
7587
|
const real = await canonical(display);
|
|
7301
7588
|
if (real !== wsReal) monitored.set(real, display);
|
|
7302
7589
|
}
|
|
@@ -8005,7 +8292,7 @@ function startViewServer(opts) {
|
|
|
8005
8292
|
};
|
|
8006
8293
|
let boundPort = port;
|
|
8007
8294
|
const getPort = () => boundPort;
|
|
8008
|
-
return new Promise((
|
|
8295
|
+
return new Promise((resolve9, reject) => {
|
|
8009
8296
|
const server = createServer((req, res) => {
|
|
8010
8297
|
handleRequest(req, res, deps, getPort, runExclusive).catch((error) => {
|
|
8011
8298
|
sendError(res, error instanceof HttpError ? error.status : 500, pathlessMessage(error));
|
|
@@ -8016,7 +8303,7 @@ function startViewServer(opts) {
|
|
|
8016
8303
|
const address = server.address();
|
|
8017
8304
|
boundPort = isAddressInfo(address) ? address.port : port;
|
|
8018
8305
|
server.off("error", reject);
|
|
8019
|
-
|
|
8306
|
+
resolve9({
|
|
8020
8307
|
url: `http://${host}:${boundPort}`,
|
|
8021
8308
|
port: boundPort,
|
|
8022
8309
|
close: () => closeServer(server)
|
|
@@ -8028,8 +8315,8 @@ function isAddressInfo(value) {
|
|
|
8028
8315
|
return value !== null && typeof value === "object";
|
|
8029
8316
|
}
|
|
8030
8317
|
function closeServer(server) {
|
|
8031
|
-
return new Promise((
|
|
8032
|
-
server.close(() =>
|
|
8318
|
+
return new Promise((resolve9) => {
|
|
8319
|
+
server.close(() => resolve9());
|
|
8033
8320
|
server.closeAllConnections();
|
|
8034
8321
|
});
|
|
8035
8322
|
}
|
|
@@ -8523,12 +8810,12 @@ async function buildSingleDeps(ctx, cwd) {
|
|
|
8523
8810
|
return { workspaces: [entry], mode: "single", nowProvider: nowProviderOf(ctx) };
|
|
8524
8811
|
}
|
|
8525
8812
|
async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
|
|
8526
|
-
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);
|
|
8527
8814
|
const entries = [];
|
|
8528
8815
|
const seenPath = /* @__PURE__ */ new Set();
|
|
8529
8816
|
const seenKey = /* @__PURE__ */ new Set();
|
|
8530
8817
|
for (const spec of specs) {
|
|
8531
|
-
const repoRoot =
|
|
8818
|
+
const repoRoot = resolve8(spec.path);
|
|
8532
8819
|
if (seenPath.has(repoRoot)) continue;
|
|
8533
8820
|
seenPath.add(repoRoot);
|
|
8534
8821
|
const entry = await buildWorkspaceEntry(repoRoot, ctx, spec.label);
|
|
@@ -8600,7 +8887,7 @@ function openInBrowser(url, override) {
|
|
|
8600
8887
|
}
|
|
8601
8888
|
}
|
|
8602
8889
|
function waitForShutdown(signal) {
|
|
8603
|
-
return new Promise((
|
|
8890
|
+
return new Promise((resolve9) => {
|
|
8604
8891
|
const cleanup = () => {
|
|
8605
8892
|
process.off("SIGINT", onSignal);
|
|
8606
8893
|
process.off("SIGTERM", onSignal);
|
|
@@ -8608,18 +8895,18 @@ function waitForShutdown(signal) {
|
|
|
8608
8895
|
};
|
|
8609
8896
|
const onSignal = () => {
|
|
8610
8897
|
cleanup();
|
|
8611
|
-
|
|
8898
|
+
resolve9();
|
|
8612
8899
|
};
|
|
8613
8900
|
const onAbort = () => {
|
|
8614
8901
|
cleanup();
|
|
8615
|
-
|
|
8902
|
+
resolve9();
|
|
8616
8903
|
};
|
|
8617
8904
|
process.on("SIGINT", onSignal);
|
|
8618
8905
|
process.on("SIGTERM", onSignal);
|
|
8619
8906
|
if (signal !== void 0) {
|
|
8620
8907
|
if (signal.aborted) {
|
|
8621
8908
|
cleanup();
|
|
8622
|
-
|
|
8909
|
+
resolve9();
|
|
8623
8910
|
return;
|
|
8624
8911
|
}
|
|
8625
8912
|
signal.addEventListener("abort", onAbort);
|