@sushanglewis/lincoln 1.8.0 → 1.8.3
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/main.js +353 -108
- package/dist/main.js.map +1 -1
- package/dist/postinstall.js +312 -22
- package/dist/postinstall.js.map +1 -1
- package/framework/.claude/harnesses/command-map.yaml +5 -1
- package/framework/.claude/hooks/on-session-start.sh +15 -0
- package/framework/.claude/policies/less-is-more.md +24 -0
- package/framework/.claude/skills/dependencies.yaml +8 -8
- package/framework/.claude-plugin/marketplace.json +2 -2
- package/framework/.claude-plugin/plugin.json +1 -1
- package/framework/.version-bump.json +1 -1
- package/framework/README.en.md +9 -2
- package/framework/README.md +13 -4
- package/framework/RELEASE.md +36 -0
- package/framework/manifest.json +19 -15
- package/framework/scripts/sync-external-agents.sh +7 -1
- package/framework/tools/lincoln/package-lock.json +2 -2
- package/framework/tools/lincoln/package.json +1 -1
- package/framework/tools/lincoln-installer/package.json +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import fs12 from "fs";
|
|
5
|
+
import path13 from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
@@ -399,8 +399,8 @@ function initProject(projectRoot, options = {}) {
|
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
// src/commands/install.ts
|
|
402
|
-
import
|
|
403
|
-
import
|
|
402
|
+
import fs8 from "fs";
|
|
403
|
+
import path9 from "path";
|
|
404
404
|
import { spawn as spawn3 } from "child_process";
|
|
405
405
|
|
|
406
406
|
// src/lib/syncClaude.ts
|
|
@@ -438,7 +438,12 @@ var COPY_SUBDIRS = [
|
|
|
438
438
|
"stages",
|
|
439
439
|
"schemas",
|
|
440
440
|
"templates",
|
|
441
|
-
"harnesses"
|
|
441
|
+
"harnesses",
|
|
442
|
+
// Policies are data files consumed by hooks at runtime (e.g.
|
|
443
|
+
// .claude/policies/less-is-more.md read by on-session-start.sh,
|
|
444
|
+
// security.yaml read by pre-tool-use.sh). Without syncing them into the
|
|
445
|
+
// project, vendored installs silently skip those guardrails (#126).
|
|
446
|
+
"policies"
|
|
442
447
|
];
|
|
443
448
|
function sha256(filePath) {
|
|
444
449
|
const data = fs5.readFileSync(filePath);
|
|
@@ -659,9 +664,265 @@ async function syncHarness(harnessId, opts, syncClaude) {
|
|
|
659
664
|
return report;
|
|
660
665
|
}
|
|
661
666
|
|
|
662
|
-
// src/lib/
|
|
667
|
+
// src/lib/syncSkills.ts
|
|
663
668
|
import fs6 from "fs";
|
|
664
669
|
import path7 from "path";
|
|
670
|
+
import { execFile } from "child_process";
|
|
671
|
+
import yaml2 from "js-yaml";
|
|
672
|
+
|
|
673
|
+
// src/lib/semver.ts
|
|
674
|
+
var SEMVER_CORE = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
675
|
+
var NUMERIC_IDENTIFIER = /^\d+$/;
|
|
676
|
+
function compareVersions(a, b) {
|
|
677
|
+
const pa = parseVersion(a);
|
|
678
|
+
const pb = parseVersion(b);
|
|
679
|
+
if (!pa || !pb) {
|
|
680
|
+
return void 0;
|
|
681
|
+
}
|
|
682
|
+
if (pa.major !== pb.major) return pa.major - pb.major;
|
|
683
|
+
if (pa.minor !== pb.minor) return pa.minor - pb.minor;
|
|
684
|
+
if (pa.patch !== pb.patch) return pa.patch - pb.patch;
|
|
685
|
+
return comparePrerelease(pa.prerelease, pb.prerelease);
|
|
686
|
+
}
|
|
687
|
+
function parseVersion(version) {
|
|
688
|
+
const match = SEMVER_CORE.exec(version.trim());
|
|
689
|
+
if (!match) {
|
|
690
|
+
return void 0;
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
major: Number(match[1]),
|
|
694
|
+
minor: Number(match[2]),
|
|
695
|
+
patch: Number(match[3]),
|
|
696
|
+
prerelease: match[4] ? match[4].split(".") : []
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
function comparePrerelease(a, b) {
|
|
700
|
+
if (a.length === 0 && b.length === 0) return 0;
|
|
701
|
+
if (a.length === 0) return 1;
|
|
702
|
+
if (b.length === 0) return -1;
|
|
703
|
+
const shared = Math.min(a.length, b.length);
|
|
704
|
+
for (let i = 0; i < shared; i++) {
|
|
705
|
+
const x = a[i];
|
|
706
|
+
const y = b[i];
|
|
707
|
+
if (x === y) continue;
|
|
708
|
+
const xNumeric = NUMERIC_IDENTIFIER.test(x);
|
|
709
|
+
const yNumeric = NUMERIC_IDENTIFIER.test(y);
|
|
710
|
+
if (xNumeric && yNumeric) return Number(x) - Number(y);
|
|
711
|
+
if (xNumeric) return -1;
|
|
712
|
+
if (yNumeric) return 1;
|
|
713
|
+
return x < y ? -1 : 1;
|
|
714
|
+
}
|
|
715
|
+
return a.length - b.length;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// src/lib/syncSkills.ts
|
|
719
|
+
var GIT_SOURCE = /^(https?:\/\/|git@)/;
|
|
720
|
+
var FULL_SHA = /^[0-9a-f]{40}$/i;
|
|
721
|
+
var PINNED_SEMVER = /^v?(\d+\.\d+\.\d+)$/;
|
|
722
|
+
var NPM_GLOBAL_INSTALL = /^npm install -g (\S+)$/;
|
|
723
|
+
var MAX_BUFFER = 16 * 1024 * 1024;
|
|
724
|
+
var defaultRunner = (cmd, args) => new Promise((resolvePromise, rejectPromise) => {
|
|
725
|
+
execFile(cmd, args, { maxBuffer: MAX_BUFFER }, (err, stdout, stderr) => {
|
|
726
|
+
if (err && !("code" in err && typeof err.code === "number")) {
|
|
727
|
+
rejectPromise(err);
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const code = err && typeof err.code === "number" ? err.code : 0;
|
|
731
|
+
resolvePromise({ code, stdout: String(stdout), stderr: String(stderr) });
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
async function syncExternalSkills(options) {
|
|
735
|
+
const report = { entries: [], warnings: [] };
|
|
736
|
+
const manifestPath = path7.join(options.payloadRoot, ".claude", "skills", "dependencies.yaml");
|
|
737
|
+
if (!fs6.existsSync(manifestPath)) {
|
|
738
|
+
report.warnings.push(`skill dependency manifest not found at ${manifestPath}; skipping external skill sync`);
|
|
739
|
+
return report;
|
|
740
|
+
}
|
|
741
|
+
let manifest;
|
|
742
|
+
try {
|
|
743
|
+
manifest = yaml2.load(fs6.readFileSync(manifestPath, "utf8")) ?? {};
|
|
744
|
+
} catch (err) {
|
|
745
|
+
report.warnings.push(
|
|
746
|
+
`failed to parse ${manifestPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
747
|
+
);
|
|
748
|
+
return report;
|
|
749
|
+
}
|
|
750
|
+
const runCommand3 = options.runCommand ?? defaultRunner;
|
|
751
|
+
const platform = options.platform ?? (process.platform === "darwin" ? "macos" : process.platform === "linux" ? "linux" : process.platform);
|
|
752
|
+
for (const [name, cfg] of Object.entries(manifest.skills ?? {})) {
|
|
753
|
+
if (!cfg.source || cfg.source === "inline" || !GIT_SOURCE.test(cfg.source)) {
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
const enabled = cfg.default_install ?? cfg.required ?? true;
|
|
757
|
+
if (!enabled) {
|
|
758
|
+
report.entries.push({ name, status: "skipped-optional", detail: "default_install is false" });
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
if (cfg.type === "cli") {
|
|
762
|
+
await syncCli(name, cfg, options, platform, runCommand3, report);
|
|
763
|
+
} else {
|
|
764
|
+
await syncGitSkill(name, cfg, options, runCommand3, report);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return report;
|
|
768
|
+
}
|
|
769
|
+
async function syncGitSkill(name, cfg, options, runCommand3, report) {
|
|
770
|
+
const source = cfg.source;
|
|
771
|
+
const ref = cfg.ref ?? "";
|
|
772
|
+
const target = path7.join(options.skillsDir, name);
|
|
773
|
+
if (options.dryRun) {
|
|
774
|
+
const planned = fs6.existsSync(target) ? "verify pinned ref and update if behind" : `clone and pin to ${ref}`;
|
|
775
|
+
report.entries.push({ name, status: "dry-run", detail: `would ${planned}` });
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
if (!fs6.existsSync(target)) {
|
|
779
|
+
const clone = await runCommand3("git", ["clone", source, target]);
|
|
780
|
+
if (clone.code !== 0) {
|
|
781
|
+
fail(report, name, `git clone failed: ${clone.stderr.trim() || `exit code ${clone.code}`}`);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
const checkout = await runCommand3("git", ["-C", target, "checkout", ref]);
|
|
785
|
+
if (checkout.code !== 0) {
|
|
786
|
+
fail(report, name, `git checkout ${ref} failed: ${checkout.stderr.trim() || `exit code ${checkout.code}`}`);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
report.entries.push({ name, status: "installed", detail: ref });
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
const status = await runCommand3("git", ["-C", target, "status", "--porcelain"]);
|
|
793
|
+
if (status.code !== 0) {
|
|
794
|
+
fail(report, name, `git status failed (not a git repository?): ${status.stderr.trim() || `exit code ${status.code}`}`);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
if (status.stdout.trim() !== "") {
|
|
798
|
+
const detail = "working tree is dirty; skipping update (commit or stash local changes first)";
|
|
799
|
+
report.entries.push({ name, status: "skipped-dirty", detail });
|
|
800
|
+
report.warnings.push(`${name}: ${detail}`);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
const head = await runCommand3("git", ["-C", target, "rev-parse", "HEAD"]);
|
|
804
|
+
if (head.code !== 0) {
|
|
805
|
+
fail(report, name, `git rev-parse failed: ${head.stderr.trim() || `exit code ${head.code}`}`);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (head.stdout.trim() === ref) {
|
|
809
|
+
report.entries.push({ name, status: "current", detail: ref });
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
const advertised = await isPinAdvertised(source, ref, runCommand3);
|
|
813
|
+
if (advertised) {
|
|
814
|
+
const fetch3 = await runCommand3("git", ["-C", target, "fetch", "origin", ref]);
|
|
815
|
+
if (fetch3.code === 0 && await checkoutFetchHead(name, target, runCommand3, report)) {
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
if (fetch3.code !== 0) {
|
|
819
|
+
fail(report, name, `git fetch origin ${ref} failed: ${fetch3.stderr.trim() || `exit code ${fetch3.code}`}`);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
const fetch2 = await runCommand3("git", ["-C", target, "fetch", "origin", ref]);
|
|
825
|
+
if (fetch2.code === 0) {
|
|
826
|
+
await checkoutFetchHead(name, target, runCommand3, report);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
if (FULL_SHA.test(ref)) {
|
|
830
|
+
const fetchAll = await runCommand3("git", ["-C", target, "fetch", "origin"]);
|
|
831
|
+
if (fetchAll.code !== 0) {
|
|
832
|
+
fail(report, name, `git fetch origin failed: ${fetchAll.stderr.trim() || `exit code ${fetchAll.code}`}`);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
const checkout = await runCommand3("git", ["-C", target, "checkout", ref]);
|
|
836
|
+
if (checkout.code !== 0) {
|
|
837
|
+
fail(report, name, `git checkout ${ref} failed: ${checkout.stderr.trim() || `exit code ${checkout.code}`}`);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
report.entries.push({ name, status: "updated", detail: ref });
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
fail(report, name, `git fetch origin ${ref} failed: ${fetch2.stderr.trim() || `exit code ${fetch2.code}`}`);
|
|
844
|
+
}
|
|
845
|
+
async function isPinAdvertised(source, ref, runCommand3) {
|
|
846
|
+
const remote = await runCommand3("git", ["ls-remote", source]);
|
|
847
|
+
if (remote.code !== 0) {
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
return remote.stdout.split("\n").some((line) => line.split(" ")[0]?.trim() === ref);
|
|
851
|
+
}
|
|
852
|
+
async function checkoutFetchHead(name, target, runCommand3, report) {
|
|
853
|
+
const checkout = await runCommand3("git", ["-C", target, "checkout", "FETCH_HEAD"]);
|
|
854
|
+
if (checkout.code !== 0) {
|
|
855
|
+
fail(report, name, `git checkout FETCH_HEAD failed: ${checkout.stderr.trim() || `exit code ${checkout.code}`}`);
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
report.entries.push({ name, status: "updated", detail: "checked out FETCH_HEAD" });
|
|
859
|
+
return true;
|
|
860
|
+
}
|
|
861
|
+
async function syncCli(name, cfg, options, platform, runCommand3, report) {
|
|
862
|
+
const binary = cfg.binary ?? name;
|
|
863
|
+
const installCommand = cfg.platforms?.[platform] ?? cfg.platforms?.linux;
|
|
864
|
+
const npmPackage = installCommand ? parseNpmGlobalInstall(installCommand) : void 0;
|
|
865
|
+
const pinned = cfg.ref ? pinnedSemver(cfg.ref) : void 0;
|
|
866
|
+
if (options.dryRun) {
|
|
867
|
+
const managed = npmPackage && pinned ? `install ${npmPackage}@${pinned} if missing or older` : "verify presence";
|
|
868
|
+
report.entries.push({ name, status: "dry-run", detail: `would ${managed}` });
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
872
|
+
const found = await runCommand3(whichCmd, [binary]);
|
|
873
|
+
if (found.code !== 0) {
|
|
874
|
+
if (npmPackage && pinned) {
|
|
875
|
+
await npmInstall(name, npmPackage, pinned, runCommand3, report, "installed");
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
fail(report, name, `CLI '${binary}' not found. Install manually (${platform}): ${installCommand ?? "see dependency manifest"}`);
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
if (!npmPackage || !pinned) {
|
|
882
|
+
report.entries.push({ name, status: "current", detail: `${binary} present` });
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
const version = await runCommand3(binary, ["--version"]);
|
|
886
|
+
const installed = version.code === 0 ? version.stdout.split("\n")[0]?.trim() ?? "" : "";
|
|
887
|
+
const comparison = installed ? compareVersions(installed, pinned) : void 0;
|
|
888
|
+
if (comparison === void 0) {
|
|
889
|
+
report.entries.push({
|
|
890
|
+
name,
|
|
891
|
+
status: "current",
|
|
892
|
+
detail: `could not parse installed version '${installed}'; leaving ${binary} as-is`
|
|
893
|
+
});
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (comparison >= 0) {
|
|
897
|
+
report.entries.push({ name, status: "current", detail: `${binary} ${installed}` });
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
await npmInstall(name, npmPackage, pinned, runCommand3, report, "updated");
|
|
901
|
+
}
|
|
902
|
+
async function npmInstall(name, npmPackage, version, runCommand3, report, status) {
|
|
903
|
+
const result = await runCommand3("npm", ["install", "-g", `${npmPackage}@${version}`]);
|
|
904
|
+
if (result.code !== 0) {
|
|
905
|
+
fail(report, name, `npm install -g ${npmPackage}@${version} failed: ${result.stderr.trim() || `exit code ${result.code}`}`);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
report.entries.push({ name, status, detail: `${npmPackage}@${version}` });
|
|
909
|
+
}
|
|
910
|
+
function parseNpmGlobalInstall(command) {
|
|
911
|
+
const match = NPM_GLOBAL_INSTALL.exec(command.trim());
|
|
912
|
+
return match?.[1];
|
|
913
|
+
}
|
|
914
|
+
function pinnedSemver(ref) {
|
|
915
|
+
const match = PINNED_SEMVER.exec(ref.trim());
|
|
916
|
+
return match?.[0].replace(/^v/, "");
|
|
917
|
+
}
|
|
918
|
+
function fail(report, name, detail) {
|
|
919
|
+
report.entries.push({ name, status: "failed", detail });
|
|
920
|
+
report.warnings.push(`${name}: ${detail}`);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// src/lib/harnessDetect.ts
|
|
924
|
+
import fs7 from "fs";
|
|
925
|
+
import path8 from "path";
|
|
665
926
|
import os2 from "os";
|
|
666
927
|
var HARNESS_SPECS = [
|
|
667
928
|
{
|
|
@@ -675,9 +936,9 @@ var HARNESS_SPECS = [
|
|
|
675
936
|
function detectGlobalHarnesses(overrideHomeDir) {
|
|
676
937
|
const homeDir = overrideHomeDir ?? os2.homedir();
|
|
677
938
|
return HARNESS_SPECS.map((spec) => {
|
|
678
|
-
const configDir =
|
|
679
|
-
const installed =
|
|
680
|
-
const pluginManaged = spec.pluginMarker ?
|
|
939
|
+
const configDir = path8.join(homeDir, spec.configDirName);
|
|
940
|
+
const installed = fs7.existsSync(configDir);
|
|
941
|
+
const pluginManaged = spec.pluginMarker ? fs7.existsSync(path8.join(configDir, spec.pluginMarker)) : false;
|
|
681
942
|
return {
|
|
682
943
|
id: spec.id,
|
|
683
944
|
installed,
|
|
@@ -869,6 +1130,7 @@ function createDefaultDeps2() {
|
|
|
869
1130
|
paths: resolveLincolnPaths(),
|
|
870
1131
|
payloadRoot: resolvePayloadRoot() || "",
|
|
871
1132
|
syncHarnesses,
|
|
1133
|
+
syncExternalSkills,
|
|
872
1134
|
createPrompt,
|
|
873
1135
|
resolvePythonForVenv,
|
|
874
1136
|
isTTY: Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
@@ -881,7 +1143,7 @@ async function install(options, deps = createDefaultDeps2()) {
|
|
|
881
1143
|
return 1;
|
|
882
1144
|
}
|
|
883
1145
|
const payloadRoot = deps.payloadRoot;
|
|
884
|
-
if (!payloadRoot || !
|
|
1146
|
+
if (!payloadRoot || !fs8.existsSync(payloadRoot)) {
|
|
885
1147
|
console.error('Lincoln payload not found. Run "npm install -g @sushanglewis/lincoln" first.');
|
|
886
1148
|
return 1;
|
|
887
1149
|
}
|
|
@@ -900,15 +1162,15 @@ async function install(options, deps = createDefaultDeps2()) {
|
|
|
900
1162
|
return 1;
|
|
901
1163
|
}
|
|
902
1164
|
if (!options.dryRun) {
|
|
903
|
-
const versionDir =
|
|
904
|
-
|
|
905
|
-
|
|
1165
|
+
const versionDir = path9.join(deps.paths.versionsDir, version);
|
|
1166
|
+
fs8.mkdirSync(versionDir, { recursive: true });
|
|
1167
|
+
fs8.cpSync(payloadRoot, versionDir, { recursive: true, force: true });
|
|
906
1168
|
try {
|
|
907
|
-
|
|
908
|
-
|
|
1169
|
+
fs8.rmSync(deps.paths.currentDir, { recursive: true, force: true });
|
|
1170
|
+
fs8.symlinkSync(versionDir, deps.paths.currentDir);
|
|
909
1171
|
} catch {
|
|
910
|
-
|
|
911
|
-
|
|
1172
|
+
fs8.rmSync(deps.paths.currentDir, { recursive: true, force: true });
|
|
1173
|
+
fs8.cpSync(versionDir, deps.paths.currentDir, { recursive: true, force: true });
|
|
912
1174
|
}
|
|
913
1175
|
}
|
|
914
1176
|
const reports = await deps.syncHarnesses({
|
|
@@ -928,9 +1190,30 @@ async function install(options, deps = createDefaultDeps2()) {
|
|
|
928
1190
|
managedFiles: collectManagedFiles(reports)
|
|
929
1191
|
});
|
|
930
1192
|
}
|
|
931
|
-
|
|
1193
|
+
const skillReport = await syncSkillLibraries(options, deps, harnesses);
|
|
1194
|
+
printSummary(options, version, harnesses, reports, skillReport);
|
|
932
1195
|
return 0;
|
|
933
1196
|
}
|
|
1197
|
+
async function syncSkillLibraries(options, deps, harnesses) {
|
|
1198
|
+
if (!harnesses.includes("claude-code") || !deps.syncExternalSkills) {
|
|
1199
|
+
return void 0;
|
|
1200
|
+
}
|
|
1201
|
+
const payloadRoot = deps.paths.currentDir;
|
|
1202
|
+
const manifestPath = path9.join(payloadRoot, ".claude", "skills", "dependencies.yaml");
|
|
1203
|
+
if (!fs8.existsSync(manifestPath)) {
|
|
1204
|
+
return void 0;
|
|
1205
|
+
}
|
|
1206
|
+
try {
|
|
1207
|
+
return await deps.syncExternalSkills({
|
|
1208
|
+
payloadRoot,
|
|
1209
|
+
skillsDir: path9.join(deps.paths.claudeDir, "skills"),
|
|
1210
|
+
dryRun: options.dryRun
|
|
1211
|
+
});
|
|
1212
|
+
} catch (err) {
|
|
1213
|
+
console.warn(` warning: external skill sync failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1214
|
+
return void 0;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
934
1217
|
async function resolvePythonIfNeeded(options, deps, harnesses) {
|
|
935
1218
|
const needsPython = !options.dryRun && !options.noVenv && harnesses.some((id) => id === "codex" || id === "opencode");
|
|
936
1219
|
if (!needsPython) {
|
|
@@ -991,7 +1274,7 @@ function collectManagedFiles(reports) {
|
|
|
991
1274
|
}
|
|
992
1275
|
return files;
|
|
993
1276
|
}
|
|
994
|
-
function printSummary(options, version, harnesses, reports) {
|
|
1277
|
+
function printSummary(options, version, harnesses, reports, skillReport) {
|
|
995
1278
|
const warnings = collectWarnings(reports);
|
|
996
1279
|
if (options.dryRun) {
|
|
997
1280
|
console.log(`Dry run: would install Lincoln ${version} for harnesses: ${harnesses.join(", ")}`);
|
|
@@ -1002,6 +1285,13 @@ function printSummary(options, version, harnesses, reports) {
|
|
|
1002
1285
|
const count = report.written.length + report.skipped.length + report.preserved.length;
|
|
1003
1286
|
console.log(` ${harnessId}: ${count} file(s)`);
|
|
1004
1287
|
}
|
|
1288
|
+
if (skillReport) {
|
|
1289
|
+
for (const entry of skillReport.entries) {
|
|
1290
|
+
const detail = entry.detail ? ` (${entry.detail})` : "";
|
|
1291
|
+
console.log(` skill ${entry.name}: ${entry.status}${detail}`);
|
|
1292
|
+
}
|
|
1293
|
+
warnings.push(...skillReport.warnings);
|
|
1294
|
+
}
|
|
1005
1295
|
for (const warning of warnings) {
|
|
1006
1296
|
console.warn(` warning: ${warning}`);
|
|
1007
1297
|
}
|
|
@@ -1062,7 +1352,7 @@ async function resolvePythonForVenv(envOverride = process.env.LINCOLN_PYTHON, ca
|
|
|
1062
1352
|
return void 0;
|
|
1063
1353
|
}
|
|
1064
1354
|
function venvPythonPath(paths) {
|
|
1065
|
-
return
|
|
1355
|
+
return path9.join(
|
|
1066
1356
|
paths.venvDir,
|
|
1067
1357
|
process.platform === "win32" ? "Scripts" : "bin",
|
|
1068
1358
|
process.platform === "win32" ? "python.exe" : "python"
|
|
@@ -1070,17 +1360,17 @@ function venvPythonPath(paths) {
|
|
|
1070
1360
|
}
|
|
1071
1361
|
async function setupVenv(paths, payloadRoot, pythonCommand) {
|
|
1072
1362
|
const pythonExe = venvPythonPath(paths);
|
|
1073
|
-
if (!
|
|
1363
|
+
if (!fs8.existsSync(pythonExe)) {
|
|
1074
1364
|
console.log("Creating Lincoln virtual environment...");
|
|
1075
1365
|
const venvCode = await runCommand2(pythonCommand, ["-m", "venv", paths.venvDir]);
|
|
1076
1366
|
if (venvCode !== 0) {
|
|
1077
1367
|
throw new Error(`Failed to create virtual environment (exit code ${venvCode})`);
|
|
1078
1368
|
}
|
|
1079
1369
|
}
|
|
1080
|
-
const requirementsPath =
|
|
1081
|
-
if (
|
|
1370
|
+
const requirementsPath = path9.join(payloadRoot, "requirements.txt");
|
|
1371
|
+
if (fs8.existsSync(requirementsPath)) {
|
|
1082
1372
|
console.log("Installing Python dependencies...");
|
|
1083
|
-
const pipExe =
|
|
1373
|
+
const pipExe = path9.join(
|
|
1084
1374
|
paths.venvDir,
|
|
1085
1375
|
process.platform === "win32" ? "Scripts" : "bin",
|
|
1086
1376
|
process.platform === "win32" ? "pip.exe" : "pip"
|
|
@@ -1152,51 +1442,6 @@ function encodePackageName(packageName) {
|
|
|
1152
1442
|
return encodeURIComponent(packageName);
|
|
1153
1443
|
}
|
|
1154
1444
|
|
|
1155
|
-
// src/lib/semver.ts
|
|
1156
|
-
var SEMVER_CORE = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
1157
|
-
var NUMERIC_IDENTIFIER = /^\d+$/;
|
|
1158
|
-
function compareVersions(a, b) {
|
|
1159
|
-
const pa = parseVersion(a);
|
|
1160
|
-
const pb = parseVersion(b);
|
|
1161
|
-
if (!pa || !pb) {
|
|
1162
|
-
return void 0;
|
|
1163
|
-
}
|
|
1164
|
-
if (pa.major !== pb.major) return pa.major - pb.major;
|
|
1165
|
-
if (pa.minor !== pb.minor) return pa.minor - pb.minor;
|
|
1166
|
-
if (pa.patch !== pb.patch) return pa.patch - pb.patch;
|
|
1167
|
-
return comparePrerelease(pa.prerelease, pb.prerelease);
|
|
1168
|
-
}
|
|
1169
|
-
function parseVersion(version) {
|
|
1170
|
-
const match = SEMVER_CORE.exec(version.trim());
|
|
1171
|
-
if (!match) {
|
|
1172
|
-
return void 0;
|
|
1173
|
-
}
|
|
1174
|
-
return {
|
|
1175
|
-
major: Number(match[1]),
|
|
1176
|
-
minor: Number(match[2]),
|
|
1177
|
-
patch: Number(match[3]),
|
|
1178
|
-
prerelease: match[4] ? match[4].split(".") : []
|
|
1179
|
-
};
|
|
1180
|
-
}
|
|
1181
|
-
function comparePrerelease(a, b) {
|
|
1182
|
-
if (a.length === 0 && b.length === 0) return 0;
|
|
1183
|
-
if (a.length === 0) return 1;
|
|
1184
|
-
if (b.length === 0) return -1;
|
|
1185
|
-
const shared = Math.min(a.length, b.length);
|
|
1186
|
-
for (let i = 0; i < shared; i++) {
|
|
1187
|
-
const x = a[i];
|
|
1188
|
-
const y = b[i];
|
|
1189
|
-
if (x === y) continue;
|
|
1190
|
-
const xNumeric = NUMERIC_IDENTIFIER.test(x);
|
|
1191
|
-
const yNumeric = NUMERIC_IDENTIFIER.test(y);
|
|
1192
|
-
if (xNumeric && yNumeric) return Number(x) - Number(y);
|
|
1193
|
-
if (xNumeric) return -1;
|
|
1194
|
-
if (yNumeric) return 1;
|
|
1195
|
-
return x < y ? -1 : 1;
|
|
1196
|
-
}
|
|
1197
|
-
return a.length - b.length;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
1445
|
// src/commands/update.ts
|
|
1201
1446
|
var PACKAGE_NAME = "@sushanglewis/lincoln";
|
|
1202
1447
|
function createDefaultDeps3() {
|
|
@@ -1276,8 +1521,8 @@ function runNpmInstallGlobal(packageName, version) {
|
|
|
1276
1521
|
}
|
|
1277
1522
|
|
|
1278
1523
|
// src/commands/use.ts
|
|
1279
|
-
import
|
|
1280
|
-
import
|
|
1524
|
+
import fs9 from "fs";
|
|
1525
|
+
import path10 from "path";
|
|
1281
1526
|
import { spawn as spawn5 } from "child_process";
|
|
1282
1527
|
var PACKAGE_NAME2 = "@sushanglewis/lincoln";
|
|
1283
1528
|
var SEMVER_REGEX = /^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -1319,8 +1564,8 @@ async function use(spec, options, deps = {}) {
|
|
|
1319
1564
|
console.error(`Failed to resolve version ${spec}: ${message}`);
|
|
1320
1565
|
return 1;
|
|
1321
1566
|
}
|
|
1322
|
-
const versionDir =
|
|
1323
|
-
const alreadyInstalled =
|
|
1567
|
+
const versionDir = path10.join(resolved.paths.versionsDir, version);
|
|
1568
|
+
const alreadyInstalled = fs9.existsSync(versionDir);
|
|
1324
1569
|
if (options.dryRun) {
|
|
1325
1570
|
if (alreadyInstalled) {
|
|
1326
1571
|
console.log(`Dry run: would switch ${resolved.paths.currentDir} to ${versionDir}`);
|
|
@@ -1343,7 +1588,7 @@ async function use(spec, options, deps = {}) {
|
|
|
1343
1588
|
console.error(`Failed to install ${PACKAGE_NAME2}@${version}: ${message}`);
|
|
1344
1589
|
return 1;
|
|
1345
1590
|
}
|
|
1346
|
-
if (!
|
|
1591
|
+
if (!fs9.existsSync(versionDir)) {
|
|
1347
1592
|
try {
|
|
1348
1593
|
await copyVersionFromNpmGlobal(resolved, versionDir);
|
|
1349
1594
|
} catch (err) {
|
|
@@ -1352,7 +1597,7 @@ async function use(spec, options, deps = {}) {
|
|
|
1352
1597
|
return 1;
|
|
1353
1598
|
}
|
|
1354
1599
|
}
|
|
1355
|
-
if (!
|
|
1600
|
+
if (!fs9.existsSync(versionDir)) {
|
|
1356
1601
|
console.error(
|
|
1357
1602
|
`Provisioning ${PACKAGE_NAME2}@${version} did not produce ${versionDir}; cannot switch versions.`
|
|
1358
1603
|
);
|
|
@@ -1389,21 +1634,21 @@ async function copyVersionFromNpmGlobal(deps, versionDir) {
|
|
|
1389
1634
|
if (!globalRoot) {
|
|
1390
1635
|
return;
|
|
1391
1636
|
}
|
|
1392
|
-
const packageRoot =
|
|
1393
|
-
if (!
|
|
1637
|
+
const packageRoot = path10.join(globalRoot, ...PACKAGE_NAME2.split("/"));
|
|
1638
|
+
if (!fs9.existsSync(packageRoot)) {
|
|
1394
1639
|
return;
|
|
1395
1640
|
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1641
|
+
fs9.mkdirSync(path10.dirname(versionDir), { recursive: true });
|
|
1642
|
+
fs9.cpSync(packageRoot, versionDir, { recursive: true });
|
|
1398
1643
|
}
|
|
1399
1644
|
function switchCurrentLink(paths, versionDir) {
|
|
1400
|
-
|
|
1645
|
+
fs9.mkdirSync(paths.lincolnHome, { recursive: true });
|
|
1401
1646
|
try {
|
|
1402
|
-
|
|
1403
|
-
|
|
1647
|
+
fs9.rmSync(paths.currentDir, { recursive: true, force: true });
|
|
1648
|
+
fs9.symlinkSync(versionDir, paths.currentDir);
|
|
1404
1649
|
} catch {
|
|
1405
|
-
|
|
1406
|
-
|
|
1650
|
+
fs9.rmSync(paths.currentDir, { recursive: true, force: true });
|
|
1651
|
+
fs9.cpSync(versionDir, paths.currentDir, { recursive: true, force: true });
|
|
1407
1652
|
}
|
|
1408
1653
|
}
|
|
1409
1654
|
function defaultInstallOptions2() {
|
|
@@ -1439,12 +1684,12 @@ function runNpmRootGlobal() {
|
|
|
1439
1684
|
}
|
|
1440
1685
|
|
|
1441
1686
|
// src/commands/migrateProject.ts
|
|
1442
|
-
import
|
|
1443
|
-
import
|
|
1687
|
+
import fs11 from "fs";
|
|
1688
|
+
import path12 from "path";
|
|
1444
1689
|
|
|
1445
1690
|
// src/lib/projectFiles.ts
|
|
1446
|
-
import
|
|
1447
|
-
import
|
|
1691
|
+
import fs10 from "fs";
|
|
1692
|
+
import path11 from "path";
|
|
1448
1693
|
import crypto2 from "crypto";
|
|
1449
1694
|
var FRAMEWORK_ALLOWLIST_DIRS = [".claude", ".claude-plugin", "scripts", "tools"];
|
|
1450
1695
|
var FRAMEWORK_ALLOWLIST_FILES = [
|
|
@@ -1479,10 +1724,10 @@ var FRAMEWORK_SKIP_PATHS = /* @__PURE__ */ new Set([
|
|
|
1479
1724
|
"tools/lincoln-installer/coverage"
|
|
1480
1725
|
]);
|
|
1481
1726
|
function sha2562(filePath) {
|
|
1482
|
-
return crypto2.createHash("sha256").update(
|
|
1727
|
+
return crypto2.createHash("sha256").update(fs10.readFileSync(filePath)).digest("hex");
|
|
1483
1728
|
}
|
|
1484
1729
|
function isDenied(relativePath) {
|
|
1485
|
-
const parts = relativePath.split(
|
|
1730
|
+
const parts = relativePath.split(path11.sep);
|
|
1486
1731
|
for (const part of parts) {
|
|
1487
1732
|
if (FRAMEWORK_DENYLIST_NAMES.has(part)) return true;
|
|
1488
1733
|
for (const prefix of FRAMEWORK_DENYLIST_PREFIXES) {
|
|
@@ -1490,14 +1735,14 @@ function isDenied(relativePath) {
|
|
|
1490
1735
|
}
|
|
1491
1736
|
}
|
|
1492
1737
|
for (const skip of FRAMEWORK_SKIP_PATHS) {
|
|
1493
|
-
if (relativePath === skip || relativePath.startsWith(skip +
|
|
1738
|
+
if (relativePath === skip || relativePath.startsWith(skip + path11.sep)) return true;
|
|
1494
1739
|
}
|
|
1495
1740
|
return false;
|
|
1496
1741
|
}
|
|
1497
1742
|
function isAllowlisted(relativePath) {
|
|
1498
1743
|
if (FRAMEWORK_ALLOWLIST_FILES.includes(relativePath)) return true;
|
|
1499
1744
|
for (const dir of FRAMEWORK_ALLOWLIST_DIRS) {
|
|
1500
|
-
if (relativePath === dir || relativePath.startsWith(dir +
|
|
1745
|
+
if (relativePath === dir || relativePath.startsWith(dir + path11.sep)) return true;
|
|
1501
1746
|
}
|
|
1502
1747
|
return false;
|
|
1503
1748
|
}
|
|
@@ -1518,10 +1763,10 @@ function isPreserved(relativePath) {
|
|
|
1518
1763
|
function listVendoredFrameworkFiles(payloadRoot, projectRoot) {
|
|
1519
1764
|
const report = { removable: [], modified: [], preserved: [] };
|
|
1520
1765
|
function walk(dir) {
|
|
1521
|
-
const entries =
|
|
1766
|
+
const entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
1522
1767
|
for (const entry of entries) {
|
|
1523
|
-
const fullPath =
|
|
1524
|
-
const relativePath =
|
|
1768
|
+
const fullPath = path11.join(dir, entry.name);
|
|
1769
|
+
const relativePath = path11.relative(projectRoot, fullPath);
|
|
1525
1770
|
if (entry.isDirectory()) {
|
|
1526
1771
|
if (isPreservedDir(relativePath)) {
|
|
1527
1772
|
walk(fullPath);
|
|
@@ -1536,8 +1781,8 @@ function listVendoredFrameworkFiles(payloadRoot, projectRoot) {
|
|
|
1536
1781
|
}
|
|
1537
1782
|
if (isDenied(relativePath)) continue;
|
|
1538
1783
|
if (!isAllowlisted(relativePath)) continue;
|
|
1539
|
-
const payloadFile =
|
|
1540
|
-
if (!
|
|
1784
|
+
const payloadFile = path11.join(payloadRoot, relativePath);
|
|
1785
|
+
if (!fs10.existsSync(payloadFile)) {
|
|
1541
1786
|
report.modified.push(relativePath);
|
|
1542
1787
|
continue;
|
|
1543
1788
|
}
|
|
@@ -1548,7 +1793,7 @@ function listVendoredFrameworkFiles(payloadRoot, projectRoot) {
|
|
|
1548
1793
|
}
|
|
1549
1794
|
}
|
|
1550
1795
|
}
|
|
1551
|
-
if (
|
|
1796
|
+
if (fs10.existsSync(projectRoot)) {
|
|
1552
1797
|
walk(projectRoot);
|
|
1553
1798
|
}
|
|
1554
1799
|
return report;
|
|
@@ -1564,11 +1809,11 @@ function createDefaultDeps5() {
|
|
|
1564
1809
|
}
|
|
1565
1810
|
async function migrateProject(projectRoot, options = {}, deps = createDefaultDeps5()) {
|
|
1566
1811
|
const payloadRoot = deps.paths.currentDir;
|
|
1567
|
-
if (!
|
|
1812
|
+
if (!fs11.existsSync(payloadRoot)) {
|
|
1568
1813
|
console.error(`Lincoln payload not found at ${payloadRoot}. Run "lincoln install" first.`);
|
|
1569
1814
|
return 1;
|
|
1570
1815
|
}
|
|
1571
|
-
const absoluteProjectRoot =
|
|
1816
|
+
const absoluteProjectRoot = path12.resolve(projectRoot);
|
|
1572
1817
|
const report = deps.listVendoredFrameworkFiles(payloadRoot, absoluteProjectRoot);
|
|
1573
1818
|
if (options.dryRun) {
|
|
1574
1819
|
console.log(`Dry run: would migrate ${absoluteProjectRoot}`);
|
|
@@ -1592,15 +1837,15 @@ async function migrateProject(projectRoot, options = {}, deps = createDefaultDep
|
|
|
1592
1837
|
return 1;
|
|
1593
1838
|
}
|
|
1594
1839
|
for (const file of report.removable) {
|
|
1595
|
-
const filePath =
|
|
1840
|
+
const filePath = path12.join(absoluteProjectRoot, file);
|
|
1596
1841
|
try {
|
|
1597
|
-
|
|
1598
|
-
let dir =
|
|
1599
|
-
while (dir !== absoluteProjectRoot &&
|
|
1600
|
-
const entries =
|
|
1842
|
+
fs11.rmSync(filePath);
|
|
1843
|
+
let dir = path12.dirname(filePath);
|
|
1844
|
+
while (dir !== absoluteProjectRoot && fs11.existsSync(dir)) {
|
|
1845
|
+
const entries = fs11.readdirSync(dir);
|
|
1601
1846
|
if (entries.length === 0) {
|
|
1602
|
-
|
|
1603
|
-
dir =
|
|
1847
|
+
fs11.rmdirSync(dir);
|
|
1848
|
+
dir = path12.dirname(dir);
|
|
1604
1849
|
} else {
|
|
1605
1850
|
break;
|
|
1606
1851
|
}
|
|
@@ -1749,7 +1994,7 @@ async function main(argv) {
|
|
|
1749
1994
|
console.log(USAGE);
|
|
1750
1995
|
return 0;
|
|
1751
1996
|
}
|
|
1752
|
-
if (process.argv[1] &&
|
|
1997
|
+
if (process.argv[1] && path13.resolve(fs12.realpathSync(fileURLToPath2(import.meta.url))) === path13.resolve(fs12.realpathSync(process.argv[1]))) {
|
|
1753
1998
|
main(process.argv).then((code) => process.exit(code)).catch((err) => {
|
|
1754
1999
|
console.error(err instanceof Error ? err.message : String(err));
|
|
1755
2000
|
process.exit(1);
|