@basou/cli 0.27.0 → 0.28.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 +67 -10
- package/dist/index.js.map +1 -1
- package/dist/program.js +67 -10
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.js
CHANGED
|
@@ -3391,6 +3391,7 @@ import {
|
|
|
3391
3391
|
ensureBasouDirectory as ensureBasouDirectory2,
|
|
3392
3392
|
GENERATED_END,
|
|
3393
3393
|
GENERATED_START,
|
|
3394
|
+
instructionMode,
|
|
3394
3395
|
isGitNotFound,
|
|
3395
3396
|
parseMarkers,
|
|
3396
3397
|
pathBasename,
|
|
@@ -3780,7 +3781,8 @@ async function isTrackedByGit(repoRoot, relPath) {
|
|
|
3780
3781
|
async function gatherRepoWiring(repositoryRoot, entry) {
|
|
3781
3782
|
const base = {
|
|
3782
3783
|
path: entry.path,
|
|
3783
|
-
...entry.visibility !== void 0 ? { visibility: entry.visibility } : {}
|
|
3784
|
+
...entry.visibility !== void 0 ? { visibility: entry.visibility } : {},
|
|
3785
|
+
...instructionMode(entry) === "self" ? { self: true } : {}
|
|
3784
3786
|
};
|
|
3785
3787
|
let real;
|
|
3786
3788
|
try {
|
|
@@ -3863,6 +3865,13 @@ function renderProjectWiring(result) {
|
|
|
3863
3865
|
for (const p of result.unknown) lines.push(`- ${p}`);
|
|
3864
3866
|
lines.push("");
|
|
3865
3867
|
}
|
|
3868
|
+
if (result.self.length > 0) {
|
|
3869
|
+
lines.push(
|
|
3870
|
+
`## instructions: self (${result.self.length}) \u2014 committed instruction files are intentional (no leak risk)`
|
|
3871
|
+
);
|
|
3872
|
+
for (const p of result.self) lines.push(`- ${p}`);
|
|
3873
|
+
lines.push("");
|
|
3874
|
+
}
|
|
3866
3875
|
if (result.incomplete.length > 0) {
|
|
3867
3876
|
lines.push(
|
|
3868
3877
|
`## Missing instruction files (${result.incomplete.length}) \u2014 to be filled by a later generation slice`
|
|
@@ -3891,7 +3900,8 @@ async function runProjectGitignore(options, ctx = {}) {
|
|
|
3891
3900
|
function gatherRepoGitignore(repositoryRoot, entry) {
|
|
3892
3901
|
const base = {
|
|
3893
3902
|
path: entry.path,
|
|
3894
|
-
...entry.visibility !== void 0 ? { visibility: entry.visibility } : {}
|
|
3903
|
+
...entry.visibility !== void 0 ? { visibility: entry.visibility } : {},
|
|
3904
|
+
...instructionMode(entry) === "self" ? { self: true } : {}
|
|
3895
3905
|
};
|
|
3896
3906
|
let real;
|
|
3897
3907
|
try {
|
|
@@ -3986,6 +3996,13 @@ function renderProjectGitignore(result) {
|
|
|
3986
3996
|
for (const p of result.unknown) lines.push(`- ${p}`);
|
|
3987
3997
|
lines.push("");
|
|
3988
3998
|
}
|
|
3999
|
+
if (result.self.length > 0) {
|
|
4000
|
+
lines.push(
|
|
4001
|
+
`## instructions: self (${result.self.length}) \u2014 skipped by design; their committed instruction files are shared, never gitignored`
|
|
4002
|
+
);
|
|
4003
|
+
for (const p of result.self) lines.push(`- ${p}`);
|
|
4004
|
+
lines.push("");
|
|
4005
|
+
}
|
|
3989
4006
|
if (result.unreachable.length > 0) {
|
|
3990
4007
|
lines.push(`## Unreachable (${result.unreachable.length}) \u2014 path unresolved / not a git repo`);
|
|
3991
4008
|
for (const p of result.unreachable) lines.push(`- ${p}`);
|
|
@@ -4007,12 +4024,13 @@ async function runProjectSymlinks(options, ctx = {}) {
|
|
|
4007
4024
|
process.exitCode = 1;
|
|
4008
4025
|
}
|
|
4009
4026
|
}
|
|
4010
|
-
function expectedSymlinkTargets(repoDirReal, canonicalFile) {
|
|
4011
|
-
|
|
4012
|
-
{ name: "AGENTS.md", target: relative2(repoDirReal, canonicalFile) },
|
|
4027
|
+
function expectedSymlinkTargets(repoDirReal, canonicalFile, mode = "hub") {
|
|
4028
|
+
const spokes = [
|
|
4013
4029
|
{ name: "CLAUDE.md", target: CANONICAL_FILE },
|
|
4014
4030
|
{ name: ".github/copilot-instructions.md", target: `../${CANONICAL_FILE}` }
|
|
4015
4031
|
];
|
|
4032
|
+
if (mode === "self") return spokes;
|
|
4033
|
+
return [{ name: "AGENTS.md", target: relative2(repoDirReal, canonicalFile) }, ...spokes];
|
|
4016
4034
|
}
|
|
4017
4035
|
function inspectSymlink(filePath, expectedTarget) {
|
|
4018
4036
|
let isLink;
|
|
@@ -4027,7 +4045,9 @@ function inspectSymlink(filePath, expectedTarget) {
|
|
|
4027
4045
|
return actual === expectedTarget ? { state: "correct" } : { state: "mismatch", actualTarget: actual };
|
|
4028
4046
|
}
|
|
4029
4047
|
function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
4030
|
-
const
|
|
4048
|
+
const mode = instructionMode(entry);
|
|
4049
|
+
const isSelf = mode === "self";
|
|
4050
|
+
const base = { path: entry.path, ...isSelf ? { self: true } : {} };
|
|
4031
4051
|
let real;
|
|
4032
4052
|
try {
|
|
4033
4053
|
real = realpathSync(resolve7(repositoryRoot, entry.path));
|
|
@@ -4040,11 +4060,11 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
4040
4060
|
if (!existsSync(join7(real, ".git"))) {
|
|
4041
4061
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
4042
4062
|
}
|
|
4043
|
-
const canonicalFile = join7(anchorReal, "agents", basename4(real), CANONICAL_FILE);
|
|
4063
|
+
const canonicalFile = isSelf ? join7(real, CANONICAL_FILE) : join7(anchorReal, "agents", basename4(real), CANONICAL_FILE);
|
|
4044
4064
|
if (!existsSync(canonicalFile)) {
|
|
4045
4065
|
return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
|
|
4046
4066
|
}
|
|
4047
|
-
const files = expectedSymlinkTargets(real, canonicalFile).map(
|
|
4067
|
+
const files = expectedSymlinkTargets(real, canonicalFile, mode).map(
|
|
4048
4068
|
(spec) => {
|
|
4049
4069
|
const { state, actualTarget } = inspectSymlink(join7(real, spec.name), spec.target);
|
|
4050
4070
|
return {
|
|
@@ -4197,6 +4217,13 @@ function renderProjectSymlinks(result) {
|
|
|
4197
4217
|
for (const p of result.missingCanonical) lines.push(`- ${p}`);
|
|
4198
4218
|
lines.push("");
|
|
4199
4219
|
}
|
|
4220
|
+
if (result.selfAgentsMissing.length > 0) {
|
|
4221
|
+
lines.push(
|
|
4222
|
+
`## AGENTS.md missing (${result.selfAgentsMissing.length}) \u2014 these \`instructions: self\` repos have no committed AGENTS.md yet; author it, then re-run to wire the spokes`
|
|
4223
|
+
);
|
|
4224
|
+
for (const p of result.selfAgentsMissing) lines.push(`- ${p}`);
|
|
4225
|
+
lines.push("");
|
|
4226
|
+
}
|
|
4200
4227
|
if (result.unreachable.length > 0) {
|
|
4201
4228
|
lines.push(`## Unreachable (${result.unreachable.length}) \u2014 path unresolved / not a git repo`);
|
|
4202
4229
|
for (const p of result.unreachable) lines.push(`- ${p}`);
|
|
@@ -4547,6 +4574,9 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
|
4547
4574
|
...entry.language !== void 0 ? { language: entry.language } : {},
|
|
4548
4575
|
...entry.publishes !== void 0 ? { publishes: entry.publishes } : {}
|
|
4549
4576
|
};
|
|
4577
|
+
if (instructionMode(entry) === "self") {
|
|
4578
|
+
return { ...declared, self: true, isAnchor: false, reachable: true, canonicalPresent: false };
|
|
4579
|
+
}
|
|
4550
4580
|
let real;
|
|
4551
4581
|
try {
|
|
4552
4582
|
real = realpathSync(resolve7(repositoryRoot, entry.path));
|
|
@@ -4754,6 +4784,13 @@ function renderProjectPreset(result) {
|
|
|
4754
4784
|
for (const p of result.anchors) lines.push(`- ${p}`);
|
|
4755
4785
|
lines.push("");
|
|
4756
4786
|
}
|
|
4787
|
+
if (result.self.length > 0) {
|
|
4788
|
+
lines.push(
|
|
4789
|
+
`## instructions: self (${result.self.length}) \u2014 hands-off; their hand-authored AGENTS.md is never written by basou`
|
|
4790
|
+
);
|
|
4791
|
+
for (const p of result.self) lines.push(`- ${p}`);
|
|
4792
|
+
lines.push("");
|
|
4793
|
+
}
|
|
4757
4794
|
if (result.unreachable.length > 0) {
|
|
4758
4795
|
lines.push(`## Unreachable (${result.unreachable.length}) \u2014 path unresolved / not a git repo`);
|
|
4759
4796
|
for (const p of result.unreachable) lines.push(`- ${p}`);
|
|
@@ -4859,13 +4896,15 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
4859
4896
|
const targetAbs = resolve7(repositoryRoot, target);
|
|
4860
4897
|
const canonicalName = basename4(repoReal ?? targetAbs);
|
|
4861
4898
|
const roster = manifest.repos ?? [];
|
|
4862
|
-
const
|
|
4899
|
+
const declaredEntry = roster.find((r) => {
|
|
4863
4900
|
try {
|
|
4864
4901
|
return realpathSync(resolve7(repositoryRoot, r.path)) === (repoReal ?? "\0");
|
|
4865
4902
|
} catch {
|
|
4866
4903
|
return resolve7(repositoryRoot, r.path) === targetAbs;
|
|
4867
4904
|
}
|
|
4868
4905
|
});
|
|
4906
|
+
const inRoster = declaredEntry !== void 0;
|
|
4907
|
+
const isSelf = declaredEntry !== void 0 && instructionMode(declaredEntry) === "self";
|
|
4869
4908
|
const cnFold = canonicalName.toLowerCase();
|
|
4870
4909
|
const canonicalShared = roster.some((r) => {
|
|
4871
4910
|
let rReal = null;
|
|
@@ -4887,6 +4926,16 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
4887
4926
|
if (repoReal !== void 0) {
|
|
4888
4927
|
for (const spec of teardownExpectedTargets(repoReal, anchorReal, canonicalName)) {
|
|
4889
4928
|
const { state, actualTarget } = inspectSymlink(join7(repoReal, spec.name), spec.target);
|
|
4929
|
+
if (isSelf) {
|
|
4930
|
+
if (state !== "missing")
|
|
4931
|
+
items.push({
|
|
4932
|
+
kind: "instruction-symlink",
|
|
4933
|
+
label: spec.name,
|
|
4934
|
+
state: "foreign",
|
|
4935
|
+
note: "instructions: self \u2014 committed, left untouched"
|
|
4936
|
+
});
|
|
4937
|
+
continue;
|
|
4938
|
+
}
|
|
4890
4939
|
if (state === "correct")
|
|
4891
4940
|
items.push({ kind: "instruction-symlink", label: spec.name, state: "removable" });
|
|
4892
4941
|
else if (state === "mismatch")
|
|
@@ -5735,7 +5784,7 @@ function pathPresent(p) {
|
|
|
5735
5784
|
}
|
|
5736
5785
|
}
|
|
5737
5786
|
function gatherRetrofit(repositoryRoot, anchorReal, roster, argPath, argAbs, argReal) {
|
|
5738
|
-
const
|
|
5787
|
+
const declaredEntry = roster.find((entry) => {
|
|
5739
5788
|
const entryAbs = resolve7(repositoryRoot, entry.path);
|
|
5740
5789
|
if (argReal !== void 0) {
|
|
5741
5790
|
try {
|
|
@@ -5745,6 +5794,8 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argPath, argAbs, arg
|
|
|
5745
5794
|
}
|
|
5746
5795
|
return entryAbs === argAbs;
|
|
5747
5796
|
});
|
|
5797
|
+
const declared = declaredEntry !== void 0;
|
|
5798
|
+
const self = declaredEntry !== void 0 && instructionMode(declaredEntry) === "self";
|
|
5748
5799
|
const displayRel = argReal !== void 0 ? relative2(anchorReal, argReal) : relative2(repositoryRoot, argAbs);
|
|
5749
5800
|
const path = displayRel === "" ? "." : displayRel;
|
|
5750
5801
|
const canonicalName = basename4(argReal ?? argAbs);
|
|
@@ -5752,6 +5803,7 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argPath, argAbs, arg
|
|
|
5752
5803
|
return {
|
|
5753
5804
|
path,
|
|
5754
5805
|
declared,
|
|
5806
|
+
...self ? { self: true } : {},
|
|
5755
5807
|
isAnchor: false,
|
|
5756
5808
|
reachable: false,
|
|
5757
5809
|
canonicalName,
|
|
@@ -5766,6 +5818,7 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argPath, argAbs, arg
|
|
|
5766
5818
|
return {
|
|
5767
5819
|
path,
|
|
5768
5820
|
declared,
|
|
5821
|
+
...self ? { self: true } : {},
|
|
5769
5822
|
isAnchor,
|
|
5770
5823
|
reachable,
|
|
5771
5824
|
canonicalName,
|
|
@@ -5868,6 +5921,10 @@ function renderProjectRetrofit(result) {
|
|
|
5868
5921
|
lines.push(
|
|
5869
5922
|
`\u2139\uFE0F \`${result.path}\` is not declared in the roster (manifest \`repos\`). Add it first with \`basou project new\` / \`basou project adopt\`, then re-run.`
|
|
5870
5923
|
);
|
|
5924
|
+
} else if (result.reason === "self") {
|
|
5925
|
+
lines.push(
|
|
5926
|
+
`\u2139\uFE0F \`${result.path}\` declares \`instructions: self\` \u2014 its \`${CANONICAL_FILE}\` is a hand-authored committed file that stays in the repo, so there is no anchor canonical to relocate it to. Retrofit does not apply (its CLAUDE.md / Copilot spokes are wired by \`basou project symlinks\`).`
|
|
5927
|
+
);
|
|
5871
5928
|
} else if (result.reason === "anchor") {
|
|
5872
5929
|
lines.push(
|
|
5873
5930
|
`\u26A0\uFE0F \`${result.path}\` is the anchor (the project root). It owns its canonical directly \u2014 there is nothing to relocate.`
|