@basou/cli 0.34.0 → 0.35.1
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 +181 -80
- package/dist/index.js.map +1 -1
- package/dist/program.js +181 -80
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3415,6 +3415,8 @@ async function runImport(adapter, fn) {
|
|
|
3415
3415
|
skippedNoAction: readCount(json.skipped_no_action),
|
|
3416
3416
|
skippedAlreadyImported: readCount(json.skipped_already_imported),
|
|
3417
3417
|
skippedLegacyUntracked: readCount(json.skipped_legacy_untracked),
|
|
3418
|
+
skippedDecreased: readCount(json.skipped_decreased),
|
|
3419
|
+
skippedDuplicate: readCount(json.skipped_duplicate),
|
|
3418
3420
|
skippedUnverifiable: readCount(json.skipped_unverifiable),
|
|
3419
3421
|
eventTotal: readCount(json.event_total),
|
|
3420
3422
|
dryRun: json.dry_run === true
|
|
@@ -3616,11 +3618,104 @@ async function assertWorkspaceInitialized7(basouRoot) {
|
|
|
3616
3618
|
}
|
|
3617
3619
|
}
|
|
3618
3620
|
|
|
3621
|
+
// src/commands/portfolio.ts
|
|
3622
|
+
import { existsSync, statSync } from "fs";
|
|
3623
|
+
import { join as join9 } from "path";
|
|
3624
|
+
function registerPortfolioCommand(program2) {
|
|
3625
|
+
program2.command("portfolio").description(
|
|
3626
|
+
"List the workspaces you orient across (read-only): every planning master registered in ~/.basou/portfolio.yaml, with its path and whether it exists / is initialized. The headless text/JSON counterpart to the `basou view --portfolio` GUI \u2014 for discovering where a sibling project lives without opening a browser"
|
|
3627
|
+
).argument("[action]", "optional literal `list` (the only, and default, action)").option("--json", "Output the result as JSON").option(
|
|
3628
|
+
"--check",
|
|
3629
|
+
"moved: the redundancy/footprint safety preflight is `basou view --portfolio --check` (this prints that pointer and exits)"
|
|
3630
|
+
).option("-v, --verbose", "Show error causes").action(async (action, opts) => {
|
|
3631
|
+
await runPortfolioCommand(action, opts);
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
function isDirectory(path) {
|
|
3635
|
+
try {
|
|
3636
|
+
return statSync(path).isDirectory();
|
|
3637
|
+
} catch {
|
|
3638
|
+
return false;
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
async function runPortfolioCommand(action, options, ctx = {}) {
|
|
3642
|
+
if (options.check === true) {
|
|
3643
|
+
console.error(
|
|
3644
|
+
"`basou portfolio` is a read-only listing; it has no safety preflight.\nRun `basou view --portfolio --check` for the redundancy/footprint check."
|
|
3645
|
+
);
|
|
3646
|
+
process.exitCode = 1;
|
|
3647
|
+
return;
|
|
3648
|
+
}
|
|
3649
|
+
if (action !== void 0 && action !== "list") {
|
|
3650
|
+
console.error(
|
|
3651
|
+
`Unknown portfolio action '${action}'. Run \`basou portfolio\` or \`basou portfolio list\` to list the registered workspaces.`
|
|
3652
|
+
);
|
|
3653
|
+
process.exitCode = 1;
|
|
3654
|
+
return;
|
|
3655
|
+
}
|
|
3656
|
+
await runPortfolioList(options, ctx);
|
|
3657
|
+
}
|
|
3658
|
+
async function runPortfolioList(options, ctx = {}) {
|
|
3659
|
+
try {
|
|
3660
|
+
await doRunPortfolioList(options, ctx);
|
|
3661
|
+
} catch (error) {
|
|
3662
|
+
renderCliError(error, { verbose: isVerbose(options) });
|
|
3663
|
+
process.exitCode = 1;
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
async function doRunPortfolioList(options, ctx) {
|
|
3667
|
+
const configPath = ctx.configPath ?? DEFAULT_PORTFOLIO_CONFIG_PATH;
|
|
3668
|
+
const pathExists2 = ctx.pathExists ?? ((p) => existsSync(p));
|
|
3669
|
+
const isInitialized = ctx.isInitialized ?? ((p) => isDirectory(join9(p, ".basou")));
|
|
3670
|
+
const workspaces = await loadPortfolioConfig(configPath);
|
|
3671
|
+
const result = {
|
|
3672
|
+
configPath,
|
|
3673
|
+
workspaces: workspaces.map((w) => {
|
|
3674
|
+
const exists = pathExists2(w.path);
|
|
3675
|
+
return {
|
|
3676
|
+
label: w.label ?? null,
|
|
3677
|
+
path: w.path,
|
|
3678
|
+
exists,
|
|
3679
|
+
initialized: exists && isInitialized(w.path)
|
|
3680
|
+
};
|
|
3681
|
+
})
|
|
3682
|
+
};
|
|
3683
|
+
if (options.json === true) {
|
|
3684
|
+
console.log(JSON.stringify(result));
|
|
3685
|
+
} else {
|
|
3686
|
+
console.log(renderPortfolioList(result));
|
|
3687
|
+
}
|
|
3688
|
+
return result;
|
|
3689
|
+
}
|
|
3690
|
+
var LABEL_COLUMN_CAP = 24;
|
|
3691
|
+
function renderPortfolioList(result) {
|
|
3692
|
+
const lines = [];
|
|
3693
|
+
const n = result.workspaces.length;
|
|
3694
|
+
lines.push("# Portfolio (workspaces you orient across)");
|
|
3695
|
+
lines.push("");
|
|
3696
|
+
lines.push(`${n} workspace${n === 1 ? "" : "s"} registered in ~/.basou/portfolio.yaml:`);
|
|
3697
|
+
lines.push("");
|
|
3698
|
+
const labelWidth = Math.min(
|
|
3699
|
+
LABEL_COLUMN_CAP,
|
|
3700
|
+
Math.max(0, ...result.workspaces.map((w) => (w.label ?? "(no label)").length))
|
|
3701
|
+
);
|
|
3702
|
+
for (const w of result.workspaces) {
|
|
3703
|
+
const label = (w.label ?? "(no label)").padEnd(labelWidth);
|
|
3704
|
+
const status = !w.exists ? "\u26A0 path not found" : w.initialized ? "\u2713 initialized" : "\u26A0 no .basou (not a planning master?)";
|
|
3705
|
+
lines.push(`- ${label} ${w.path} ${status}`);
|
|
3706
|
+
}
|
|
3707
|
+
lines.push("");
|
|
3708
|
+
lines.push(
|
|
3709
|
+
"Note: read-only listing of ~/.basou/portfolio.yaml. Run `basou view --portfolio` for the cross-workspace GUI, or `basou view --portfolio --check` for the redundancy/footprint safety preflight."
|
|
3710
|
+
);
|
|
3711
|
+
return lines.join("\n");
|
|
3712
|
+
}
|
|
3713
|
+
|
|
3619
3714
|
// src/commands/project.ts
|
|
3620
3715
|
import {
|
|
3621
3716
|
closeSync,
|
|
3622
3717
|
copyFileSync,
|
|
3623
|
-
existsSync,
|
|
3718
|
+
existsSync as existsSync2,
|
|
3624
3719
|
constants as fsConstants,
|
|
3625
3720
|
ftruncateSync,
|
|
3626
3721
|
lstatSync,
|
|
@@ -3630,13 +3725,13 @@ import {
|
|
|
3630
3725
|
readFileSync,
|
|
3631
3726
|
readlinkSync,
|
|
3632
3727
|
realpathSync,
|
|
3633
|
-
statSync,
|
|
3728
|
+
statSync as statSync2,
|
|
3634
3729
|
symlinkSync,
|
|
3635
3730
|
unlinkSync,
|
|
3636
3731
|
writeFileSync,
|
|
3637
3732
|
writeSync
|
|
3638
3733
|
} from "fs";
|
|
3639
|
-
import { basename as basename5, dirname as dirname3, isAbsolute as isAbsolute3, join as
|
|
3734
|
+
import { basename as basename5, dirname as dirname3, isAbsolute as isAbsolute3, join as join10, relative as relative2, resolve as resolve7 } from "path";
|
|
3640
3735
|
import {
|
|
3641
3736
|
appendBasouGitignore as appendBasouGitignore2,
|
|
3642
3737
|
basouPaths as basouPaths10,
|
|
@@ -4018,7 +4113,7 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
|
|
|
4018
4113
|
} catch {
|
|
4019
4114
|
return { path: declaredPath, kind: "unresolved" };
|
|
4020
4115
|
}
|
|
4021
|
-
return { path: declaredPath, kind:
|
|
4116
|
+
return { path: declaredPath, kind: existsSync2(join10(real, ".git")) ? "repo" : "non-repo" };
|
|
4022
4117
|
}
|
|
4023
4118
|
async function doRunProjectAdopt(options, ctx) {
|
|
4024
4119
|
const cwd = ctx.cwd ?? process.cwd();
|
|
@@ -4122,7 +4217,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
4122
4217
|
} catch {
|
|
4123
4218
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
4124
4219
|
}
|
|
4125
|
-
if (!
|
|
4220
|
+
if (!existsSync2(join10(real, ".git"))) {
|
|
4126
4221
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
4127
4222
|
}
|
|
4128
4223
|
try {
|
|
@@ -4130,7 +4225,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
4130
4225
|
for (const name of INSTRUCTION_FILES) {
|
|
4131
4226
|
let present = true;
|
|
4132
4227
|
try {
|
|
4133
|
-
lstatSync(
|
|
4228
|
+
lstatSync(join10(real, name));
|
|
4134
4229
|
} catch {
|
|
4135
4230
|
present = false;
|
|
4136
4231
|
}
|
|
@@ -4241,10 +4336,10 @@ function gatherRepoGitignore(repositoryRoot, entry) {
|
|
|
4241
4336
|
} catch {
|
|
4242
4337
|
return { ...base, reachable: false, currentLines: [] };
|
|
4243
4338
|
}
|
|
4244
|
-
if (!
|
|
4339
|
+
if (!existsSync2(join10(real, ".git"))) {
|
|
4245
4340
|
return { ...base, reachable: false, currentLines: [] };
|
|
4246
4341
|
}
|
|
4247
|
-
return { ...base, reachable: true, currentLines: readGitignoreLines(
|
|
4342
|
+
return { ...base, reachable: true, currentLines: readGitignoreLines(join10(real, ".gitignore")) };
|
|
4248
4343
|
}
|
|
4249
4344
|
function hasErrorCode(error) {
|
|
4250
4345
|
return error instanceof Error && typeof error.code === "string";
|
|
@@ -4258,7 +4353,7 @@ function readGitignoreLines(file) {
|
|
|
4258
4353
|
}
|
|
4259
4354
|
}
|
|
4260
4355
|
function applyGitignorePlan(repositoryRoot, plan) {
|
|
4261
|
-
const file =
|
|
4356
|
+
const file = join10(realpathSync(resolve7(repositoryRoot, plan.path)), ".gitignore");
|
|
4262
4357
|
let existing = "";
|
|
4263
4358
|
try {
|
|
4264
4359
|
existing = readFileSync(file, "utf8");
|
|
@@ -4384,7 +4479,7 @@ function anchorCanonicalState(filePath) {
|
|
|
4384
4479
|
if (hasErrorCode(error) && error.code === "ENOENT") return "absent";
|
|
4385
4480
|
return "broken";
|
|
4386
4481
|
}
|
|
4387
|
-
if (st.isSymbolicLink()) return
|
|
4482
|
+
if (st.isSymbolicLink()) return existsSync2(filePath) ? "usable" : "broken";
|
|
4388
4483
|
return st.isFile() ? "usable" : "broken";
|
|
4389
4484
|
}
|
|
4390
4485
|
function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
@@ -4398,7 +4493,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
4398
4493
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
4399
4494
|
}
|
|
4400
4495
|
if (real === anchorReal) {
|
|
4401
|
-
const anchorCanonical =
|
|
4496
|
+
const anchorCanonical = join10(real, CANONICAL_FILE);
|
|
4402
4497
|
const anchorState = anchorCanonicalState(anchorCanonical);
|
|
4403
4498
|
if (anchorState === "absent") {
|
|
4404
4499
|
return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
|
|
@@ -4418,7 +4513,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
4418
4513
|
anchorCanonical,
|
|
4419
4514
|
"self"
|
|
4420
4515
|
).map((spec) => {
|
|
4421
|
-
const { state, actualTarget } = inspectSymlink(
|
|
4516
|
+
const { state, actualTarget } = inspectSymlink(join10(real, spec.name), spec.target);
|
|
4422
4517
|
return {
|
|
4423
4518
|
name: spec.name,
|
|
4424
4519
|
expectedTarget: spec.target,
|
|
@@ -4435,16 +4530,16 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
4435
4530
|
files: anchorFiles
|
|
4436
4531
|
};
|
|
4437
4532
|
}
|
|
4438
|
-
if (!
|
|
4533
|
+
if (!existsSync2(join10(real, ".git"))) {
|
|
4439
4534
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
4440
4535
|
}
|
|
4441
|
-
const canonicalFile = isSelf ?
|
|
4442
|
-
if (!
|
|
4536
|
+
const canonicalFile = isSelf ? join10(real, CANONICAL_FILE) : join10(anchorReal, "agents", basename5(real), CANONICAL_FILE);
|
|
4537
|
+
if (!existsSync2(canonicalFile)) {
|
|
4443
4538
|
return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
|
|
4444
4539
|
}
|
|
4445
4540
|
const files = expectedSymlinkTargets(real, canonicalFile, mode).map(
|
|
4446
4541
|
(spec) => {
|
|
4447
|
-
const { state, actualTarget } = inspectSymlink(
|
|
4542
|
+
const { state, actualTarget } = inspectSymlink(join10(real, spec.name), spec.target);
|
|
4448
4543
|
return {
|
|
4449
4544
|
name: spec.name,
|
|
4450
4545
|
expectedTarget: spec.target,
|
|
@@ -4473,7 +4568,7 @@ function applySymlinkPlan(repositoryRoot, plan) {
|
|
|
4473
4568
|
const created = [];
|
|
4474
4569
|
const failed = [];
|
|
4475
4570
|
for (const { name, target } of plan.toCreate) {
|
|
4476
|
-
const filePath =
|
|
4571
|
+
const filePath = join10(real, name);
|
|
4477
4572
|
try {
|
|
4478
4573
|
mkdirSync(dirname3(filePath), { recursive: true });
|
|
4479
4574
|
symlinkSync(target, filePath);
|
|
@@ -4504,10 +4599,10 @@ function gatherViewSymlinks(repositoryRoot, anchorReal, roster, viewDir) {
|
|
|
4504
4599
|
const collision = viewCanonicalCollision(repositoryRoot, roster, viewName);
|
|
4505
4600
|
if (collision !== void 0) return { kind: "collision", viewName, repoPath: collision };
|
|
4506
4601
|
const canonicalFile = canonicalFileFor(anchorReal, viewName);
|
|
4507
|
-
if (!
|
|
4602
|
+
if (!existsSync2(canonicalFile)) return { kind: "missing-canonical", viewName };
|
|
4508
4603
|
const files = expectedSymlinkTargets(viewDir, canonicalFile, "hub").map(
|
|
4509
4604
|
(spec) => {
|
|
4510
|
-
const { state, actualTarget } = inspectSymlink(
|
|
4605
|
+
const { state, actualTarget } = inspectSymlink(join10(viewDir, spec.name), spec.target);
|
|
4511
4606
|
return {
|
|
4512
4607
|
name: spec.name,
|
|
4513
4608
|
expectedTarget: spec.target,
|
|
@@ -4523,7 +4618,7 @@ function applyViewSymlinks(viewDir, files) {
|
|
|
4523
4618
|
const failed = [];
|
|
4524
4619
|
for (const f of files) {
|
|
4525
4620
|
if (f.state !== "missing") continue;
|
|
4526
|
-
const filePath =
|
|
4621
|
+
const filePath = join10(viewDir, f.name);
|
|
4527
4622
|
try {
|
|
4528
4623
|
mkdirSync(dirname3(filePath), { recursive: true });
|
|
4529
4624
|
symlinkSync(f.expectedTarget, filePath);
|
|
@@ -4745,7 +4840,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
|
|
|
4745
4840
|
return realpathSync(abs);
|
|
4746
4841
|
} catch {
|
|
4747
4842
|
try {
|
|
4748
|
-
return
|
|
4843
|
+
return join10(realpathSync(dirname3(abs)), basename5(abs));
|
|
4749
4844
|
} catch {
|
|
4750
4845
|
return abs;
|
|
4751
4846
|
}
|
|
@@ -4763,7 +4858,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
|
|
|
4763
4858
|
return { path: entry.path, reachable: false };
|
|
4764
4859
|
}
|
|
4765
4860
|
const linkName = basename5(repoReal);
|
|
4766
|
-
const { state, actualTarget } = inspectSymlink(
|
|
4861
|
+
const { state, actualTarget } = inspectSymlink(join10(viewDir, linkName), expectedTarget);
|
|
4767
4862
|
return {
|
|
4768
4863
|
path: entry.path,
|
|
4769
4864
|
reachable: true,
|
|
@@ -4777,7 +4872,7 @@ function applyViewPlan(viewDir, toCreate) {
|
|
|
4777
4872
|
const created = [];
|
|
4778
4873
|
const failed = [];
|
|
4779
4874
|
for (const { name, target } of toCreate) {
|
|
4780
|
-
const filePath =
|
|
4875
|
+
const filePath = join10(viewDir, name);
|
|
4781
4876
|
try {
|
|
4782
4877
|
mkdirSync(dirname3(filePath), { recursive: true });
|
|
4783
4878
|
symlinkSync(target, filePath);
|
|
@@ -4792,7 +4887,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
|
|
|
4792
4887
|
INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
|
|
4793
4888
|
);
|
|
4794
4889
|
function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
4795
|
-
const filePath =
|
|
4890
|
+
const filePath = join10(viewDir, name);
|
|
4796
4891
|
let isLink;
|
|
4797
4892
|
try {
|
|
4798
4893
|
isLink = lstatSync(filePath).isSymbolicLink();
|
|
@@ -4814,14 +4909,14 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
|
4814
4909
|
if (isAbsolute3(target)) return { target, kind: "absolute" };
|
|
4815
4910
|
let isDir = false;
|
|
4816
4911
|
try {
|
|
4817
|
-
isDir =
|
|
4912
|
+
isDir = statSync2(resolved).isDirectory();
|
|
4818
4913
|
} catch {
|
|
4819
4914
|
isDir = false;
|
|
4820
4915
|
}
|
|
4821
4916
|
if (!isDir) {
|
|
4822
|
-
return { target, kind:
|
|
4917
|
+
return { target, kind: existsSync2(resolved) ? "non-repo" : "broken" };
|
|
4823
4918
|
}
|
|
4824
|
-
return { target, kind:
|
|
4919
|
+
return { target, kind: existsSync2(join10(resolved, ".git")) ? "repo" : "non-repo" };
|
|
4825
4920
|
}
|
|
4826
4921
|
function gatherExistingViewLinks(viewDir, rosterRealpaths) {
|
|
4827
4922
|
let names;
|
|
@@ -4846,7 +4941,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
|
|
|
4846
4941
|
const pruned = [];
|
|
4847
4942
|
const failed = [];
|
|
4848
4943
|
for (const { name } of toPrune) {
|
|
4849
|
-
const filePath =
|
|
4944
|
+
const filePath = join10(viewDir, name);
|
|
4850
4945
|
const c = classifyViewLink(viewDir, name, rosterRealpaths);
|
|
4851
4946
|
if (c === null || c.kind !== "repo") {
|
|
4852
4947
|
failed.push({
|
|
@@ -5059,10 +5154,10 @@ async function runProjectPreset(options, ctx = {}) {
|
|
|
5059
5154
|
}
|
|
5060
5155
|
}
|
|
5061
5156
|
function canonicalFileFor(anchorReal, canonicalName) {
|
|
5062
|
-
return
|
|
5157
|
+
return join10(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
5063
5158
|
}
|
|
5064
5159
|
function canonicalLabelFor(canonicalName) {
|
|
5065
|
-
return
|
|
5160
|
+
return join10("agents", canonicalName, CANONICAL_FILE);
|
|
5066
5161
|
}
|
|
5067
5162
|
async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
5068
5163
|
const declared = {
|
|
@@ -5083,7 +5178,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
|
5083
5178
|
if (real === anchorReal) {
|
|
5084
5179
|
return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
|
|
5085
5180
|
}
|
|
5086
|
-
if (!
|
|
5181
|
+
if (!existsSync2(join10(real, ".git"))) {
|
|
5087
5182
|
return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
|
|
5088
5183
|
}
|
|
5089
5184
|
const canonicalName = basename5(real);
|
|
@@ -5465,24 +5560,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
5465
5560
|
const instructionFiles = [];
|
|
5466
5561
|
for (const name of INSTRUCTION_FILES) {
|
|
5467
5562
|
try {
|
|
5468
|
-
lstatSync(
|
|
5563
|
+
lstatSync(join10(real, name));
|
|
5469
5564
|
instructionFiles.push(name);
|
|
5470
5565
|
} catch {
|
|
5471
5566
|
}
|
|
5472
5567
|
}
|
|
5473
5568
|
let ignored;
|
|
5474
5569
|
try {
|
|
5475
|
-
ignored = new Set(readGitignoreLines(
|
|
5570
|
+
ignored = new Set(readGitignoreLines(join10(real, ".gitignore")).map((l) => l.trim()));
|
|
5476
5571
|
} catch {
|
|
5477
5572
|
ignored = /* @__PURE__ */ new Set();
|
|
5478
5573
|
}
|
|
5479
5574
|
const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
|
|
5480
|
-
const canonical2 =
|
|
5575
|
+
const canonical2 = existsSync2(join10(anchorReal, "agents", canonicalName, CANONICAL_FILE));
|
|
5481
5576
|
let viewLink = false;
|
|
5482
5577
|
const viewPath = manifest.workspace.view;
|
|
5483
5578
|
if (viewPath !== void 0) {
|
|
5484
5579
|
try {
|
|
5485
|
-
lstatSync(
|
|
5580
|
+
lstatSync(join10(resolveViewDir(repositoryRoot, viewPath), canonicalName));
|
|
5486
5581
|
viewLink = true;
|
|
5487
5582
|
} catch {
|
|
5488
5583
|
}
|
|
@@ -5496,11 +5591,11 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
5496
5591
|
};
|
|
5497
5592
|
}
|
|
5498
5593
|
function teardownExpectedTargets(repoReal, anchorReal, canonicalName) {
|
|
5499
|
-
const canonicalFile =
|
|
5594
|
+
const canonicalFile = join10(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
5500
5595
|
return expectedSymlinkTargets(repoReal, canonicalFile);
|
|
5501
5596
|
}
|
|
5502
5597
|
function viewLinkPointsAt(viewDir, name, repoReal) {
|
|
5503
|
-
const filePath =
|
|
5598
|
+
const filePath = join10(viewDir, name);
|
|
5504
5599
|
try {
|
|
5505
5600
|
if (!lstatSync(filePath).isSymbolicLink()) return false;
|
|
5506
5601
|
const target = readlinkSync(filePath);
|
|
@@ -5511,7 +5606,7 @@ function viewLinkPointsAt(viewDir, name, repoReal) {
|
|
|
5511
5606
|
}
|
|
5512
5607
|
}
|
|
5513
5608
|
function viewLinkPointsAtPath(viewDir, name, expectedRepoPath) {
|
|
5514
|
-
const filePath =
|
|
5609
|
+
const filePath = join10(viewDir, name);
|
|
5515
5610
|
try {
|
|
5516
5611
|
if (!lstatSync(filePath).isSymbolicLink()) return false;
|
|
5517
5612
|
const target = readlinkSync(filePath);
|
|
@@ -5562,7 +5657,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
5562
5657
|
if (!isAnchor) {
|
|
5563
5658
|
if (repoReal !== void 0) {
|
|
5564
5659
|
for (const spec of teardownExpectedTargets(repoReal, anchorReal, canonicalName)) {
|
|
5565
|
-
const { state, actualTarget } = inspectSymlink(
|
|
5660
|
+
const { state, actualTarget } = inspectSymlink(join10(repoReal, spec.name), spec.target);
|
|
5566
5661
|
if (isSelf) {
|
|
5567
5662
|
if (state !== "missing")
|
|
5568
5663
|
items.push({
|
|
@@ -5599,7 +5694,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
5599
5694
|
}
|
|
5600
5695
|
let ignored;
|
|
5601
5696
|
try {
|
|
5602
|
-
ignored = new Set(readGitignoreLines(
|
|
5697
|
+
ignored = new Set(readGitignoreLines(join10(repoReal, ".gitignore")).map((l) => l.trim()));
|
|
5603
5698
|
for (const p of INSTRUCTION_FILES) {
|
|
5604
5699
|
if (ignored.has(p) || ignored.has(`/${p}`)) {
|
|
5605
5700
|
items.push({
|
|
@@ -5622,7 +5717,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
5622
5717
|
const viewPath = manifest.workspace.view;
|
|
5623
5718
|
if (viewPath !== void 0) {
|
|
5624
5719
|
const viewDir = resolveViewDir(repositoryRoot, viewPath);
|
|
5625
|
-
const linkPath =
|
|
5720
|
+
const linkPath = join10(viewDir, canonicalName);
|
|
5626
5721
|
let isLink = false;
|
|
5627
5722
|
try {
|
|
5628
5723
|
isLink = lstatSync(linkPath).isSymbolicLink();
|
|
@@ -5648,8 +5743,8 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
5648
5743
|
else items.push({ kind: "view-symlink", label: canonicalName, state: "removable" });
|
|
5649
5744
|
}
|
|
5650
5745
|
}
|
|
5651
|
-
const canonicalFile =
|
|
5652
|
-
const canonicalLabel =
|
|
5746
|
+
const canonicalFile = join10(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
5747
|
+
const canonicalLabel = join10("agents", canonicalName, CANONICAL_FILE);
|
|
5653
5748
|
let canonicalIsLink = false;
|
|
5654
5749
|
try {
|
|
5655
5750
|
canonicalIsLink = lstatSync(canonicalFile).isSymbolicLink();
|
|
@@ -5663,7 +5758,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
5663
5758
|
state: "foreign",
|
|
5664
5759
|
note: "the canonical is a symlink (not generated)"
|
|
5665
5760
|
});
|
|
5666
|
-
} else if (
|
|
5761
|
+
} else if (existsSync2(canonicalFile)) {
|
|
5667
5762
|
let content;
|
|
5668
5763
|
try {
|
|
5669
5764
|
content = readFileSync(canonicalFile, "utf8");
|
|
@@ -5753,12 +5848,12 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
5753
5848
|
);
|
|
5754
5849
|
for (const item of removable.filter((i) => i.kind === "instruction-symlink")) {
|
|
5755
5850
|
const expected = expectedByName.get(item.label);
|
|
5756
|
-
if (repoReal === null || expected === void 0 || inspectSymlink(
|
|
5851
|
+
if (repoReal === null || expected === void 0 || inspectSymlink(join10(repoReal, item.label), expected).state !== "correct") {
|
|
5757
5852
|
changed(item.label);
|
|
5758
5853
|
continue;
|
|
5759
5854
|
}
|
|
5760
5855
|
try {
|
|
5761
|
-
unlinkSync(
|
|
5856
|
+
unlinkSync(join10(repoReal, item.label));
|
|
5762
5857
|
removed.push(item.label);
|
|
5763
5858
|
} catch (error) {
|
|
5764
5859
|
failed.push({ label: item.label, message: failureReason(error) });
|
|
@@ -5777,7 +5872,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
5777
5872
|
continue;
|
|
5778
5873
|
}
|
|
5779
5874
|
try {
|
|
5780
|
-
unlinkSync(
|
|
5875
|
+
unlinkSync(join10(viewDir, item.label));
|
|
5781
5876
|
removed.push(`view/${item.label}`);
|
|
5782
5877
|
} catch (error) {
|
|
5783
5878
|
failed.push({ label: `view/${item.label}`, message: failureReason(error) });
|
|
@@ -5785,7 +5880,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
5785
5880
|
}
|
|
5786
5881
|
const NOFOLLOW = fsConstants.O_NOFOLLOW ?? 0;
|
|
5787
5882
|
for (const item of removable.filter((i) => i.kind === "canonical-block")) {
|
|
5788
|
-
const canonicalFile =
|
|
5883
|
+
const canonicalFile = join10(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
5789
5884
|
try {
|
|
5790
5885
|
if (lstatSync(canonicalFile).isSymbolicLink()) {
|
|
5791
5886
|
changed(item.label);
|
|
@@ -6059,12 +6154,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
|
|
|
6059
6154
|
} catch {
|
|
6060
6155
|
return { canonicalDirOld: false, viewLinkOld: false };
|
|
6061
6156
|
}
|
|
6062
|
-
const canonicalDirOld =
|
|
6157
|
+
const canonicalDirOld = existsSync2(join10(anchorReal, "agents", oldBasename));
|
|
6063
6158
|
let viewLinkOld = false;
|
|
6064
6159
|
const viewPath = manifest.workspace.view;
|
|
6065
6160
|
if (viewPath !== void 0) {
|
|
6066
6161
|
try {
|
|
6067
|
-
lstatSync(
|
|
6162
|
+
lstatSync(join10(resolveViewDir(repositoryRoot, viewPath), oldBasename));
|
|
6068
6163
|
viewLinkOld = true;
|
|
6069
6164
|
} catch {
|
|
6070
6165
|
}
|
|
@@ -6262,7 +6357,7 @@ async function doRunProjectNew(repos, options, ctx) {
|
|
|
6262
6357
|
const viewPath = options.view === false ? null : options.view ?? `../${viewStem}-workspace`;
|
|
6263
6358
|
const sourceRoots = [...rosterPaths, ...viewPath !== null ? [viewPath] : []];
|
|
6264
6359
|
const paths = basouPaths10(repositoryRoot);
|
|
6265
|
-
const existed =
|
|
6360
|
+
const existed = existsSync2(paths.files.manifest);
|
|
6266
6361
|
const manifest = createManifest2({
|
|
6267
6362
|
workspaceName,
|
|
6268
6363
|
sourceRoots,
|
|
@@ -6422,7 +6517,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
|
|
|
6422
6517
|
console.log("\u2139\uFE0F No repo roster declared \u2014 nothing to seed.");
|
|
6423
6518
|
return;
|
|
6424
6519
|
}
|
|
6425
|
-
const anchorDoc =
|
|
6520
|
+
const anchorDoc = join10(repositoryRoot, CANONICAL_FILE);
|
|
6426
6521
|
if (pathPresent(anchorDoc)) {
|
|
6427
6522
|
console.log(
|
|
6428
6523
|
`\u2705 The anchor's own \`${CANONICAL_FILE}\` already exists \u2014 hand-maintained, left untouched.`
|
|
@@ -6484,7 +6579,7 @@ function regularFileSpokes(repoReal) {
|
|
|
6484
6579
|
const out = [];
|
|
6485
6580
|
for (const spoke of ["CLAUDE.md", ".github/copilot-instructions.md"]) {
|
|
6486
6581
|
try {
|
|
6487
|
-
const st = lstatSync(
|
|
6582
|
+
const st = lstatSync(join10(repoReal, spoke));
|
|
6488
6583
|
if (!st.isSymbolicLink() && st.isFile()) out.push(spoke);
|
|
6489
6584
|
} catch {
|
|
6490
6585
|
}
|
|
@@ -6530,8 +6625,8 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argPath, argAbs, arg
|
|
|
6530
6625
|
};
|
|
6531
6626
|
}
|
|
6532
6627
|
const isAnchor = argReal === anchorReal;
|
|
6533
|
-
const reachable =
|
|
6534
|
-
const canonicalFile =
|
|
6628
|
+
const reachable = existsSync2(join10(argReal, ".git"));
|
|
6629
|
+
const canonicalFile = join10(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
6535
6630
|
return {
|
|
6536
6631
|
path,
|
|
6537
6632
|
declared,
|
|
@@ -6540,13 +6635,13 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argPath, argAbs, arg
|
|
|
6540
6635
|
reachable,
|
|
6541
6636
|
canonicalName,
|
|
6542
6637
|
...viewCanonicalName !== void 0 ? { viewCanonicalName } : {},
|
|
6543
|
-
agentsState: inspectAgentsState(
|
|
6638
|
+
agentsState: inspectAgentsState(join10(argReal, CANONICAL_FILE)),
|
|
6544
6639
|
canonicalExists: pathPresent(canonicalFile),
|
|
6545
6640
|
regularSpokes: regularFileSpokes(argReal)
|
|
6546
6641
|
};
|
|
6547
6642
|
}
|
|
6548
6643
|
function relocateAgentsFile(repoReal, canonicalFile) {
|
|
6549
|
-
const agentsFile =
|
|
6644
|
+
const agentsFile = join10(repoReal, CANONICAL_FILE);
|
|
6550
6645
|
try {
|
|
6551
6646
|
mkdirSync(dirname3(canonicalFile), { recursive: true });
|
|
6552
6647
|
} catch (error) {
|
|
@@ -6663,7 +6758,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
|
|
|
6663
6758
|
let failure;
|
|
6664
6759
|
let partial = false;
|
|
6665
6760
|
if (options.apply === true && plan.action === "relocate" && argReal !== void 0) {
|
|
6666
|
-
const canonicalFile =
|
|
6761
|
+
const canonicalFile = join10(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
|
|
6667
6762
|
const res = relocateAgentsFile(argReal, canonicalFile);
|
|
6668
6763
|
if (res.ok) {
|
|
6669
6764
|
applied = true;
|
|
@@ -6868,7 +6963,7 @@ import { PROTOCOL_END, PROTOCOL_START, parseMarkers as parseMarkers3, readMarkdo
|
|
|
6868
6963
|
|
|
6869
6964
|
// src/lib/context-channel.ts
|
|
6870
6965
|
import { homedir as homedir7 } from "os";
|
|
6871
|
-
import { join as
|
|
6966
|
+
import { join as join11 } from "path";
|
|
6872
6967
|
import {
|
|
6873
6968
|
ORIENTATION_END,
|
|
6874
6969
|
ORIENTATION_START,
|
|
@@ -6876,7 +6971,7 @@ import {
|
|
|
6876
6971
|
readMarkdownFile as readMarkdownFile5,
|
|
6877
6972
|
removeMarkerSection as removeMarkerSection2
|
|
6878
6973
|
} from "@basou/core";
|
|
6879
|
-
var CODEX_TARGET_PATH =
|
|
6974
|
+
var CODEX_TARGET_PATH = join11(homedir7(), ".codex", "AGENTS.md");
|
|
6880
6975
|
var ORIENTATION_MARKERS = { start: ORIENTATION_START, end: ORIENTATION_END };
|
|
6881
6976
|
var ORIENTATION_MANAGED_NOTE = "<!-- Managed by basou: 'basou refresh' regenerates everything between the BASOU:ORIENTATION markers with the workspace's current position. This block is transient \u2014 it changes every refresh; do not edit it. -->";
|
|
6882
6977
|
function buildTargetBody(existing, block, markers) {
|
|
@@ -6980,15 +7075,15 @@ async function renderOrientationToCodexChannel(opts) {
|
|
|
6980
7075
|
|
|
6981
7076
|
// src/lib/protocols-config.ts
|
|
6982
7077
|
import { homedir as homedir8 } from "os";
|
|
6983
|
-
import { isAbsolute as isAbsolute4, join as
|
|
7078
|
+
import { isAbsolute as isAbsolute4, join as join12, resolve as resolve8 } from "path";
|
|
6984
7079
|
import { readYamlFile as readYamlFile5 } from "@basou/core";
|
|
6985
|
-
var DEFAULT_PROTOCOLS_CONFIG_PATH =
|
|
6986
|
-
var DEFAULT_TARGET_PATH =
|
|
7080
|
+
var DEFAULT_PROTOCOLS_CONFIG_PATH = join12(homedir8(), ".basou", "protocols.yaml");
|
|
7081
|
+
var DEFAULT_TARGET_PATH = join12(homedir8(), ".claude", "CLAUDE.md");
|
|
6987
7082
|
var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
|
|
6988
7083
|
var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
|
|
6989
7084
|
function expandTilde3(p) {
|
|
6990
7085
|
if (p === "~") return homedir8();
|
|
6991
|
-
if (p.startsWith("~/")) return
|
|
7086
|
+
if (p.startsWith("~/")) return join12(homedir8(), p.slice(2));
|
|
6992
7087
|
return p;
|
|
6993
7088
|
}
|
|
6994
7089
|
function isRecord3(value) {
|
|
@@ -7193,15 +7288,15 @@ import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
|
7193
7288
|
// src/commands/refresh-watch.ts
|
|
7194
7289
|
import { readdir as readdir2, stat as stat5 } from "fs/promises";
|
|
7195
7290
|
import { homedir as homedir9 } from "os";
|
|
7196
|
-
import { join as
|
|
7291
|
+
import { join as join13 } from "path";
|
|
7197
7292
|
import { findErrorCode as findErrorCode8 } from "@basou/core";
|
|
7198
7293
|
var DEFAULT_WATCH_INTERVAL_SEC = 30;
|
|
7199
7294
|
var MIN_WATCH_INTERVAL_SEC = 5;
|
|
7200
7295
|
var MAX_WATCH_INTERVAL_SEC = 86400;
|
|
7201
7296
|
function watchedRoots(ctx) {
|
|
7202
7297
|
return [
|
|
7203
|
-
ctx.codexSessionsDir ??
|
|
7204
|
-
ctx.claudeProjectsDir ??
|
|
7298
|
+
ctx.codexSessionsDir ?? join13(homedir9(), ".codex", "sessions"),
|
|
7299
|
+
ctx.claudeProjectsDir ?? join13(homedir9(), ".claude", "projects")
|
|
7205
7300
|
];
|
|
7206
7301
|
}
|
|
7207
7302
|
async function scanSourceLogs(roots) {
|
|
@@ -7215,7 +7310,7 @@ async function scanSourceLogs(roots) {
|
|
|
7215
7310
|
throw new Error("Failed to read a source log directory", { cause: error });
|
|
7216
7311
|
}
|
|
7217
7312
|
for (const entry of entries) {
|
|
7218
|
-
const full =
|
|
7313
|
+
const full = join13(dir, entry.name);
|
|
7219
7314
|
if (entry.isDirectory()) {
|
|
7220
7315
|
await walk(full);
|
|
7221
7316
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
@@ -7503,6 +7598,11 @@ function describeImport(outcome) {
|
|
|
7503
7598
|
if (outcome.skippedAlreadyImported > 0)
|
|
7504
7599
|
parts.push(`${outcome.skippedAlreadyImported} already imported`);
|
|
7505
7600
|
if (outcome.skippedLegacyUntracked > 0) parts.push(`${outcome.skippedLegacyUntracked} legacy`);
|
|
7601
|
+
if (outcome.skippedNoAction > 0) parts.push(`${outcome.skippedNoAction} with no actions`);
|
|
7602
|
+
if (outcome.skippedDecreased > 0) parts.push(`${outcome.skippedDecreased} shrank`);
|
|
7603
|
+
if (outcome.skippedDuplicate > 0) parts.push(`${outcome.skippedDuplicate} duplicated`);
|
|
7604
|
+
if (outcome.skippedUnverifiable > 0)
|
|
7605
|
+
parts.push(`${outcome.skippedUnverifiable} unverifiable (run 'basou verify')`);
|
|
7506
7606
|
return `${outcome.adapter}: ${verb} ${parts.join(", ")}`;
|
|
7507
7607
|
}
|
|
7508
7608
|
function printRefreshSummary(result) {
|
|
@@ -7937,7 +8037,7 @@ function renderReviewGaps(summary) {
|
|
|
7937
8037
|
// src/commands/run.ts
|
|
7938
8038
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
7939
8039
|
import { homedir as homedir11 } from "os";
|
|
7940
|
-
import { join as
|
|
8040
|
+
import { join as join14 } from "path";
|
|
7941
8041
|
import {
|
|
7942
8042
|
acquireLock as acquireLock5,
|
|
7943
8043
|
assertBasouRootSafe as assertBasouRootSafe12,
|
|
@@ -8013,13 +8113,13 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
8013
8113
|
await assertBasouRootSafe12(paths.root);
|
|
8014
8114
|
const manifest = await readManifest8(paths);
|
|
8015
8115
|
const sessionId = prefixedUlid4("ses");
|
|
8016
|
-
const sessionDir =
|
|
8116
|
+
const sessionDir = join14(paths.sessions, sessionId);
|
|
8017
8117
|
await mkdir2(sessionDir, { recursive: true });
|
|
8018
8118
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
8019
8119
|
await coreAppendChainedEvent2(paths, sessionId, event);
|
|
8020
8120
|
});
|
|
8021
8121
|
const startedAt = now().toISOString();
|
|
8022
|
-
const sessionYamlPath =
|
|
8122
|
+
const sessionYamlPath = join14(sessionDir, "session.yaml");
|
|
8023
8123
|
const session = buildInitialSession2({
|
|
8024
8124
|
id: sessionId,
|
|
8025
8125
|
command,
|
|
@@ -8381,7 +8481,7 @@ async function syncCodexOrientationChannelPreSpawn(cwd, ctx) {
|
|
|
8381
8481
|
|
|
8382
8482
|
// src/commands/session.ts
|
|
8383
8483
|
import { readFile as readFile6 } from "fs/promises";
|
|
8384
|
-
import { basename as basename6, isAbsolute as isAbsolute6, join as
|
|
8484
|
+
import { basename as basename6, isAbsolute as isAbsolute6, join as join15, relative as relative3 } from "path";
|
|
8385
8485
|
import {
|
|
8386
8486
|
acquireLock as acquireLock6,
|
|
8387
8487
|
appendEventToExistingSession as appendEventToExistingSession3,
|
|
@@ -8506,8 +8606,8 @@ async function doRunSessionShow(idInput, options, ctx) {
|
|
|
8506
8606
|
const paths = basouPaths16(repositoryRoot);
|
|
8507
8607
|
await assertWorkspaceInitialized11(paths.root);
|
|
8508
8608
|
const sessionId = await resolveSessionId3(paths, idInput);
|
|
8509
|
-
const sessionDir =
|
|
8510
|
-
const sessionYamlPath =
|
|
8609
|
+
const sessionDir = join15(paths.sessions, sessionId);
|
|
8610
|
+
const sessionYamlPath = join15(sessionDir, "session.yaml");
|
|
8511
8611
|
let session;
|
|
8512
8612
|
try {
|
|
8513
8613
|
const raw = await readYamlFile7(sessionYamlPath);
|
|
@@ -9269,7 +9369,7 @@ function formatVersionGateMessage(error) {
|
|
|
9269
9369
|
|
|
9270
9370
|
// src/commands/task.ts
|
|
9271
9371
|
import { readFile as readFile7 } from "fs/promises";
|
|
9272
|
-
import { join as
|
|
9372
|
+
import { join as join16 } from "path";
|
|
9273
9373
|
import {
|
|
9274
9374
|
archiveTask,
|
|
9275
9375
|
assertBasouRootSafe as assertBasouRootSafe16,
|
|
@@ -9595,7 +9695,7 @@ async function doRunTaskShow(idInput, options, ctx) {
|
|
|
9595
9695
|
const events = [];
|
|
9596
9696
|
const linkedSessionIds = new Set(doc.task.task.linked_sessions);
|
|
9597
9697
|
for (const s of sessions) {
|
|
9598
|
-
const sessionDir =
|
|
9698
|
+
const sessionDir = join16(paths.sessions, s.sessionId);
|
|
9599
9699
|
try {
|
|
9600
9700
|
for await (const ev of replayEvents3(sessionDir, {
|
|
9601
9701
|
onWarning: (w) => printReplayWarning(w, s.sessionId)
|
|
@@ -10501,7 +10601,7 @@ import { InvalidArgumentError as InvalidArgumentError7 } from "commander";
|
|
|
10501
10601
|
// src/lib/portfolio-safety.ts
|
|
10502
10602
|
import { execFile } from "child_process";
|
|
10503
10603
|
import { lstat as lstat2, realpath as realpath2 } from "fs/promises";
|
|
10504
|
-
import { isAbsolute as isAbsolute7, join as
|
|
10604
|
+
import { isAbsolute as isAbsolute7, join as join17, relative as relative4, resolve as resolve11 } from "path";
|
|
10505
10605
|
import { promisify } from "util";
|
|
10506
10606
|
import { readManifest as readManifest12 } from "@basou/core";
|
|
10507
10607
|
var execFileAsync = promisify(execFile);
|
|
@@ -10525,7 +10625,7 @@ function isBasouPath(p) {
|
|
|
10525
10625
|
async function inspectRepo(repoPath) {
|
|
10526
10626
|
let hasEntry = false;
|
|
10527
10627
|
try {
|
|
10528
|
-
await lstat2(
|
|
10628
|
+
await lstat2(join17(repoPath, ".basou"));
|
|
10529
10629
|
hasEntry = true;
|
|
10530
10630
|
} catch (error) {
|
|
10531
10631
|
if (errorCode(error) !== "ENOENT") {
|
|
@@ -10679,7 +10779,7 @@ function formatSafetyReport(result) {
|
|
|
10679
10779
|
|
|
10680
10780
|
// src/lib/view-server.ts
|
|
10681
10781
|
import { createServer } from "http";
|
|
10682
|
-
import { basename as basename7, join as
|
|
10782
|
+
import { basename as basename7, join as join18, resolve as resolve12 } from "path";
|
|
10683
10783
|
import {
|
|
10684
10784
|
computeWorkStats as computeWorkStats2,
|
|
10685
10785
|
enumerateApprovals as enumerateApprovals2,
|
|
@@ -11719,7 +11819,7 @@ async function sessionDetail(ws, sessionId) {
|
|
|
11719
11819
|
throw error;
|
|
11720
11820
|
}
|
|
11721
11821
|
try {
|
|
11722
|
-
const events = await readAllEvents2(
|
|
11822
|
+
const events = await readAllEvents2(join18(ws.paths.sessions, sessionId));
|
|
11723
11823
|
return { session, events };
|
|
11724
11824
|
} catch {
|
|
11725
11825
|
return { session, events: [], degraded: true };
|
|
@@ -12100,6 +12200,7 @@ function buildProgram() {
|
|
|
12100
12200
|
registerDecisionsCommand(program2);
|
|
12101
12201
|
registerReportCommand(program2);
|
|
12102
12202
|
registerOrientCommand(program2);
|
|
12203
|
+
registerPortfolioCommand(program2);
|
|
12103
12204
|
registerReviewCommand(program2);
|
|
12104
12205
|
registerReviewGapsCommand(program2);
|
|
12105
12206
|
registerProjectCommand(program2);
|