@sushanglewis/lincoln 1.7.0 → 1.8.2
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 +347 -107
- package/dist/main.js.map +1 -1
- package/dist/postinstall.js +306 -21
- package/dist/postinstall.js.map +1 -1
- package/framework/.claude/skills/build-product-prototype/prompts/main.md +6 -1
- package/framework/.claude/skills/clarify-requirements/prompts/main.md +6 -0
- package/framework/.claude/skills/dependencies.yaml +8 -8
- package/framework/.claude/skills/draft-product-design/SKILL.md +1 -0
- package/framework/.claude/skills/draft-product-design/prompts/main.md +26 -3
- package/framework/.claude/skills/lc-workflow/validators/validate.py +1 -0
- package/framework/.claude/stages/product-design-docs.yaml +2 -0
- package/framework/.claude/workflows/bug-fix.yaml +1 -0
- package/framework/.claude/workflows/design-spike.yaml +1 -0
- package/framework/.claude/workflows/existing-project-iteration.yaml +1 -0
- package/framework/.claude/workflows/interview-to-knowledge.yaml +1 -0
- package/framework/.claude/workflows/oss-first-design.yaml +1 -0
- 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 +21 -3
- package/framework/README.md +23 -3
- package/framework/RELEASE.md +41 -1
- package/framework/manifest.json +24 -24
- package/framework/scripts/lincoln_index.py +8 -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
|
|
@@ -659,9 +659,265 @@ async function syncHarness(harnessId, opts, syncClaude) {
|
|
|
659
659
|
return report;
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
-
// src/lib/
|
|
662
|
+
// src/lib/syncSkills.ts
|
|
663
663
|
import fs6 from "fs";
|
|
664
664
|
import path7 from "path";
|
|
665
|
+
import { execFile } from "child_process";
|
|
666
|
+
import yaml2 from "js-yaml";
|
|
667
|
+
|
|
668
|
+
// src/lib/semver.ts
|
|
669
|
+
var SEMVER_CORE = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
670
|
+
var NUMERIC_IDENTIFIER = /^\d+$/;
|
|
671
|
+
function compareVersions(a, b) {
|
|
672
|
+
const pa = parseVersion(a);
|
|
673
|
+
const pb = parseVersion(b);
|
|
674
|
+
if (!pa || !pb) {
|
|
675
|
+
return void 0;
|
|
676
|
+
}
|
|
677
|
+
if (pa.major !== pb.major) return pa.major - pb.major;
|
|
678
|
+
if (pa.minor !== pb.minor) return pa.minor - pb.minor;
|
|
679
|
+
if (pa.patch !== pb.patch) return pa.patch - pb.patch;
|
|
680
|
+
return comparePrerelease(pa.prerelease, pb.prerelease);
|
|
681
|
+
}
|
|
682
|
+
function parseVersion(version) {
|
|
683
|
+
const match = SEMVER_CORE.exec(version.trim());
|
|
684
|
+
if (!match) {
|
|
685
|
+
return void 0;
|
|
686
|
+
}
|
|
687
|
+
return {
|
|
688
|
+
major: Number(match[1]),
|
|
689
|
+
minor: Number(match[2]),
|
|
690
|
+
patch: Number(match[3]),
|
|
691
|
+
prerelease: match[4] ? match[4].split(".") : []
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function comparePrerelease(a, b) {
|
|
695
|
+
if (a.length === 0 && b.length === 0) return 0;
|
|
696
|
+
if (a.length === 0) return 1;
|
|
697
|
+
if (b.length === 0) return -1;
|
|
698
|
+
const shared = Math.min(a.length, b.length);
|
|
699
|
+
for (let i = 0; i < shared; i++) {
|
|
700
|
+
const x = a[i];
|
|
701
|
+
const y = b[i];
|
|
702
|
+
if (x === y) continue;
|
|
703
|
+
const xNumeric = NUMERIC_IDENTIFIER.test(x);
|
|
704
|
+
const yNumeric = NUMERIC_IDENTIFIER.test(y);
|
|
705
|
+
if (xNumeric && yNumeric) return Number(x) - Number(y);
|
|
706
|
+
if (xNumeric) return -1;
|
|
707
|
+
if (yNumeric) return 1;
|
|
708
|
+
return x < y ? -1 : 1;
|
|
709
|
+
}
|
|
710
|
+
return a.length - b.length;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// src/lib/syncSkills.ts
|
|
714
|
+
var GIT_SOURCE = /^(https?:\/\/|git@)/;
|
|
715
|
+
var FULL_SHA = /^[0-9a-f]{40}$/i;
|
|
716
|
+
var PINNED_SEMVER = /^v?(\d+\.\d+\.\d+)$/;
|
|
717
|
+
var NPM_GLOBAL_INSTALL = /^npm install -g (\S+)$/;
|
|
718
|
+
var MAX_BUFFER = 16 * 1024 * 1024;
|
|
719
|
+
var defaultRunner = (cmd, args) => new Promise((resolvePromise, rejectPromise) => {
|
|
720
|
+
execFile(cmd, args, { maxBuffer: MAX_BUFFER }, (err, stdout, stderr) => {
|
|
721
|
+
if (err && !("code" in err && typeof err.code === "number")) {
|
|
722
|
+
rejectPromise(err);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
const code = err && typeof err.code === "number" ? err.code : 0;
|
|
726
|
+
resolvePromise({ code, stdout: String(stdout), stderr: String(stderr) });
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
async function syncExternalSkills(options) {
|
|
730
|
+
const report = { entries: [], warnings: [] };
|
|
731
|
+
const manifestPath = path7.join(options.payloadRoot, ".claude", "skills", "dependencies.yaml");
|
|
732
|
+
if (!fs6.existsSync(manifestPath)) {
|
|
733
|
+
report.warnings.push(`skill dependency manifest not found at ${manifestPath}; skipping external skill sync`);
|
|
734
|
+
return report;
|
|
735
|
+
}
|
|
736
|
+
let manifest;
|
|
737
|
+
try {
|
|
738
|
+
manifest = yaml2.load(fs6.readFileSync(manifestPath, "utf8")) ?? {};
|
|
739
|
+
} catch (err) {
|
|
740
|
+
report.warnings.push(
|
|
741
|
+
`failed to parse ${manifestPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
742
|
+
);
|
|
743
|
+
return report;
|
|
744
|
+
}
|
|
745
|
+
const runCommand3 = options.runCommand ?? defaultRunner;
|
|
746
|
+
const platform = options.platform ?? (process.platform === "darwin" ? "macos" : process.platform === "linux" ? "linux" : process.platform);
|
|
747
|
+
for (const [name, cfg] of Object.entries(manifest.skills ?? {})) {
|
|
748
|
+
if (!cfg.source || cfg.source === "inline" || !GIT_SOURCE.test(cfg.source)) {
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
const enabled = cfg.default_install ?? cfg.required ?? true;
|
|
752
|
+
if (!enabled) {
|
|
753
|
+
report.entries.push({ name, status: "skipped-optional", detail: "default_install is false" });
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if (cfg.type === "cli") {
|
|
757
|
+
await syncCli(name, cfg, options, platform, runCommand3, report);
|
|
758
|
+
} else {
|
|
759
|
+
await syncGitSkill(name, cfg, options, runCommand3, report);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return report;
|
|
763
|
+
}
|
|
764
|
+
async function syncGitSkill(name, cfg, options, runCommand3, report) {
|
|
765
|
+
const source = cfg.source;
|
|
766
|
+
const ref = cfg.ref ?? "";
|
|
767
|
+
const target = path7.join(options.skillsDir, name);
|
|
768
|
+
if (options.dryRun) {
|
|
769
|
+
const planned = fs6.existsSync(target) ? "verify pinned ref and update if behind" : `clone and pin to ${ref}`;
|
|
770
|
+
report.entries.push({ name, status: "dry-run", detail: `would ${planned}` });
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
if (!fs6.existsSync(target)) {
|
|
774
|
+
const clone = await runCommand3("git", ["clone", source, target]);
|
|
775
|
+
if (clone.code !== 0) {
|
|
776
|
+
fail(report, name, `git clone failed: ${clone.stderr.trim() || `exit code ${clone.code}`}`);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const checkout = await runCommand3("git", ["-C", target, "checkout", ref]);
|
|
780
|
+
if (checkout.code !== 0) {
|
|
781
|
+
fail(report, name, `git checkout ${ref} failed: ${checkout.stderr.trim() || `exit code ${checkout.code}`}`);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
report.entries.push({ name, status: "installed", detail: ref });
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const status = await runCommand3("git", ["-C", target, "status", "--porcelain"]);
|
|
788
|
+
if (status.code !== 0) {
|
|
789
|
+
fail(report, name, `git status failed (not a git repository?): ${status.stderr.trim() || `exit code ${status.code}`}`);
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (status.stdout.trim() !== "") {
|
|
793
|
+
const detail = "working tree is dirty; skipping update (commit or stash local changes first)";
|
|
794
|
+
report.entries.push({ name, status: "skipped-dirty", detail });
|
|
795
|
+
report.warnings.push(`${name}: ${detail}`);
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
const head = await runCommand3("git", ["-C", target, "rev-parse", "HEAD"]);
|
|
799
|
+
if (head.code !== 0) {
|
|
800
|
+
fail(report, name, `git rev-parse failed: ${head.stderr.trim() || `exit code ${head.code}`}`);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
if (head.stdout.trim() === ref) {
|
|
804
|
+
report.entries.push({ name, status: "current", detail: ref });
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
const advertised = await isPinAdvertised(source, ref, runCommand3);
|
|
808
|
+
if (advertised) {
|
|
809
|
+
const fetch3 = await runCommand3("git", ["-C", target, "fetch", "origin", ref]);
|
|
810
|
+
if (fetch3.code === 0 && await checkoutFetchHead(name, target, runCommand3, report)) {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
if (fetch3.code !== 0) {
|
|
814
|
+
fail(report, name, `git fetch origin ${ref} failed: ${fetch3.stderr.trim() || `exit code ${fetch3.code}`}`);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
const fetch2 = await runCommand3("git", ["-C", target, "fetch", "origin", ref]);
|
|
820
|
+
if (fetch2.code === 0) {
|
|
821
|
+
await checkoutFetchHead(name, target, runCommand3, report);
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
if (FULL_SHA.test(ref)) {
|
|
825
|
+
const fetchAll = await runCommand3("git", ["-C", target, "fetch", "origin"]);
|
|
826
|
+
if (fetchAll.code !== 0) {
|
|
827
|
+
fail(report, name, `git fetch origin failed: ${fetchAll.stderr.trim() || `exit code ${fetchAll.code}`}`);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
const checkout = await runCommand3("git", ["-C", target, "checkout", ref]);
|
|
831
|
+
if (checkout.code !== 0) {
|
|
832
|
+
fail(report, name, `git checkout ${ref} failed: ${checkout.stderr.trim() || `exit code ${checkout.code}`}`);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
report.entries.push({ name, status: "updated", detail: ref });
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
fail(report, name, `git fetch origin ${ref} failed: ${fetch2.stderr.trim() || `exit code ${fetch2.code}`}`);
|
|
839
|
+
}
|
|
840
|
+
async function isPinAdvertised(source, ref, runCommand3) {
|
|
841
|
+
const remote = await runCommand3("git", ["ls-remote", source]);
|
|
842
|
+
if (remote.code !== 0) {
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
return remote.stdout.split("\n").some((line) => line.split(" ")[0]?.trim() === ref);
|
|
846
|
+
}
|
|
847
|
+
async function checkoutFetchHead(name, target, runCommand3, report) {
|
|
848
|
+
const checkout = await runCommand3("git", ["-C", target, "checkout", "FETCH_HEAD"]);
|
|
849
|
+
if (checkout.code !== 0) {
|
|
850
|
+
fail(report, name, `git checkout FETCH_HEAD failed: ${checkout.stderr.trim() || `exit code ${checkout.code}`}`);
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
report.entries.push({ name, status: "updated", detail: "checked out FETCH_HEAD" });
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
856
|
+
async function syncCli(name, cfg, options, platform, runCommand3, report) {
|
|
857
|
+
const binary = cfg.binary ?? name;
|
|
858
|
+
const installCommand = cfg.platforms?.[platform] ?? cfg.platforms?.linux;
|
|
859
|
+
const npmPackage = installCommand ? parseNpmGlobalInstall(installCommand) : void 0;
|
|
860
|
+
const pinned = cfg.ref ? pinnedSemver(cfg.ref) : void 0;
|
|
861
|
+
if (options.dryRun) {
|
|
862
|
+
const managed = npmPackage && pinned ? `install ${npmPackage}@${pinned} if missing or older` : "verify presence";
|
|
863
|
+
report.entries.push({ name, status: "dry-run", detail: `would ${managed}` });
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
867
|
+
const found = await runCommand3(whichCmd, [binary]);
|
|
868
|
+
if (found.code !== 0) {
|
|
869
|
+
if (npmPackage && pinned) {
|
|
870
|
+
await npmInstall(name, npmPackage, pinned, runCommand3, report, "installed");
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
fail(report, name, `CLI '${binary}' not found. Install manually (${platform}): ${installCommand ?? "see dependency manifest"}`);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
if (!npmPackage || !pinned) {
|
|
877
|
+
report.entries.push({ name, status: "current", detail: `${binary} present` });
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
const version = await runCommand3(binary, ["--version"]);
|
|
881
|
+
const installed = version.code === 0 ? version.stdout.split("\n")[0]?.trim() ?? "" : "";
|
|
882
|
+
const comparison = installed ? compareVersions(installed, pinned) : void 0;
|
|
883
|
+
if (comparison === void 0) {
|
|
884
|
+
report.entries.push({
|
|
885
|
+
name,
|
|
886
|
+
status: "current",
|
|
887
|
+
detail: `could not parse installed version '${installed}'; leaving ${binary} as-is`
|
|
888
|
+
});
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
if (comparison >= 0) {
|
|
892
|
+
report.entries.push({ name, status: "current", detail: `${binary} ${installed}` });
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
await npmInstall(name, npmPackage, pinned, runCommand3, report, "updated");
|
|
896
|
+
}
|
|
897
|
+
async function npmInstall(name, npmPackage, version, runCommand3, report, status) {
|
|
898
|
+
const result = await runCommand3("npm", ["install", "-g", `${npmPackage}@${version}`]);
|
|
899
|
+
if (result.code !== 0) {
|
|
900
|
+
fail(report, name, `npm install -g ${npmPackage}@${version} failed: ${result.stderr.trim() || `exit code ${result.code}`}`);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
report.entries.push({ name, status, detail: `${npmPackage}@${version}` });
|
|
904
|
+
}
|
|
905
|
+
function parseNpmGlobalInstall(command) {
|
|
906
|
+
const match = NPM_GLOBAL_INSTALL.exec(command.trim());
|
|
907
|
+
return match?.[1];
|
|
908
|
+
}
|
|
909
|
+
function pinnedSemver(ref) {
|
|
910
|
+
const match = PINNED_SEMVER.exec(ref.trim());
|
|
911
|
+
return match?.[0].replace(/^v/, "");
|
|
912
|
+
}
|
|
913
|
+
function fail(report, name, detail) {
|
|
914
|
+
report.entries.push({ name, status: "failed", detail });
|
|
915
|
+
report.warnings.push(`${name}: ${detail}`);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/lib/harnessDetect.ts
|
|
919
|
+
import fs7 from "fs";
|
|
920
|
+
import path8 from "path";
|
|
665
921
|
import os2 from "os";
|
|
666
922
|
var HARNESS_SPECS = [
|
|
667
923
|
{
|
|
@@ -675,9 +931,9 @@ var HARNESS_SPECS = [
|
|
|
675
931
|
function detectGlobalHarnesses(overrideHomeDir) {
|
|
676
932
|
const homeDir = overrideHomeDir ?? os2.homedir();
|
|
677
933
|
return HARNESS_SPECS.map((spec) => {
|
|
678
|
-
const configDir =
|
|
679
|
-
const installed =
|
|
680
|
-
const pluginManaged = spec.pluginMarker ?
|
|
934
|
+
const configDir = path8.join(homeDir, spec.configDirName);
|
|
935
|
+
const installed = fs7.existsSync(configDir);
|
|
936
|
+
const pluginManaged = spec.pluginMarker ? fs7.existsSync(path8.join(configDir, spec.pluginMarker)) : false;
|
|
681
937
|
return {
|
|
682
938
|
id: spec.id,
|
|
683
939
|
installed,
|
|
@@ -869,6 +1125,7 @@ function createDefaultDeps2() {
|
|
|
869
1125
|
paths: resolveLincolnPaths(),
|
|
870
1126
|
payloadRoot: resolvePayloadRoot() || "",
|
|
871
1127
|
syncHarnesses,
|
|
1128
|
+
syncExternalSkills,
|
|
872
1129
|
createPrompt,
|
|
873
1130
|
resolvePythonForVenv,
|
|
874
1131
|
isTTY: Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
@@ -881,7 +1138,7 @@ async function install(options, deps = createDefaultDeps2()) {
|
|
|
881
1138
|
return 1;
|
|
882
1139
|
}
|
|
883
1140
|
const payloadRoot = deps.payloadRoot;
|
|
884
|
-
if (!payloadRoot || !
|
|
1141
|
+
if (!payloadRoot || !fs8.existsSync(payloadRoot)) {
|
|
885
1142
|
console.error('Lincoln payload not found. Run "npm install -g @sushanglewis/lincoln" first.');
|
|
886
1143
|
return 1;
|
|
887
1144
|
}
|
|
@@ -900,15 +1157,15 @@ async function install(options, deps = createDefaultDeps2()) {
|
|
|
900
1157
|
return 1;
|
|
901
1158
|
}
|
|
902
1159
|
if (!options.dryRun) {
|
|
903
|
-
const versionDir =
|
|
904
|
-
|
|
905
|
-
|
|
1160
|
+
const versionDir = path9.join(deps.paths.versionsDir, version);
|
|
1161
|
+
fs8.mkdirSync(versionDir, { recursive: true });
|
|
1162
|
+
fs8.cpSync(payloadRoot, versionDir, { recursive: true, force: true });
|
|
906
1163
|
try {
|
|
907
|
-
|
|
908
|
-
|
|
1164
|
+
fs8.rmSync(deps.paths.currentDir, { recursive: true, force: true });
|
|
1165
|
+
fs8.symlinkSync(versionDir, deps.paths.currentDir);
|
|
909
1166
|
} catch {
|
|
910
|
-
|
|
911
|
-
|
|
1167
|
+
fs8.rmSync(deps.paths.currentDir, { recursive: true, force: true });
|
|
1168
|
+
fs8.cpSync(versionDir, deps.paths.currentDir, { recursive: true, force: true });
|
|
912
1169
|
}
|
|
913
1170
|
}
|
|
914
1171
|
const reports = await deps.syncHarnesses({
|
|
@@ -928,9 +1185,30 @@ async function install(options, deps = createDefaultDeps2()) {
|
|
|
928
1185
|
managedFiles: collectManagedFiles(reports)
|
|
929
1186
|
});
|
|
930
1187
|
}
|
|
931
|
-
|
|
1188
|
+
const skillReport = await syncSkillLibraries(options, deps, harnesses);
|
|
1189
|
+
printSummary(options, version, harnesses, reports, skillReport);
|
|
932
1190
|
return 0;
|
|
933
1191
|
}
|
|
1192
|
+
async function syncSkillLibraries(options, deps, harnesses) {
|
|
1193
|
+
if (!harnesses.includes("claude-code") || !deps.syncExternalSkills) {
|
|
1194
|
+
return void 0;
|
|
1195
|
+
}
|
|
1196
|
+
const payloadRoot = deps.paths.currentDir;
|
|
1197
|
+
const manifestPath = path9.join(payloadRoot, ".claude", "skills", "dependencies.yaml");
|
|
1198
|
+
if (!fs8.existsSync(manifestPath)) {
|
|
1199
|
+
return void 0;
|
|
1200
|
+
}
|
|
1201
|
+
try {
|
|
1202
|
+
return await deps.syncExternalSkills({
|
|
1203
|
+
payloadRoot,
|
|
1204
|
+
skillsDir: path9.join(deps.paths.claudeDir, "skills"),
|
|
1205
|
+
dryRun: options.dryRun
|
|
1206
|
+
});
|
|
1207
|
+
} catch (err) {
|
|
1208
|
+
console.warn(` warning: external skill sync failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1209
|
+
return void 0;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
934
1212
|
async function resolvePythonIfNeeded(options, deps, harnesses) {
|
|
935
1213
|
const needsPython = !options.dryRun && !options.noVenv && harnesses.some((id) => id === "codex" || id === "opencode");
|
|
936
1214
|
if (!needsPython) {
|
|
@@ -991,7 +1269,7 @@ function collectManagedFiles(reports) {
|
|
|
991
1269
|
}
|
|
992
1270
|
return files;
|
|
993
1271
|
}
|
|
994
|
-
function printSummary(options, version, harnesses, reports) {
|
|
1272
|
+
function printSummary(options, version, harnesses, reports, skillReport) {
|
|
995
1273
|
const warnings = collectWarnings(reports);
|
|
996
1274
|
if (options.dryRun) {
|
|
997
1275
|
console.log(`Dry run: would install Lincoln ${version} for harnesses: ${harnesses.join(", ")}`);
|
|
@@ -1002,6 +1280,13 @@ function printSummary(options, version, harnesses, reports) {
|
|
|
1002
1280
|
const count = report.written.length + report.skipped.length + report.preserved.length;
|
|
1003
1281
|
console.log(` ${harnessId}: ${count} file(s)`);
|
|
1004
1282
|
}
|
|
1283
|
+
if (skillReport) {
|
|
1284
|
+
for (const entry of skillReport.entries) {
|
|
1285
|
+
const detail = entry.detail ? ` (${entry.detail})` : "";
|
|
1286
|
+
console.log(` skill ${entry.name}: ${entry.status}${detail}`);
|
|
1287
|
+
}
|
|
1288
|
+
warnings.push(...skillReport.warnings);
|
|
1289
|
+
}
|
|
1005
1290
|
for (const warning of warnings) {
|
|
1006
1291
|
console.warn(` warning: ${warning}`);
|
|
1007
1292
|
}
|
|
@@ -1062,7 +1347,7 @@ async function resolvePythonForVenv(envOverride = process.env.LINCOLN_PYTHON, ca
|
|
|
1062
1347
|
return void 0;
|
|
1063
1348
|
}
|
|
1064
1349
|
function venvPythonPath(paths) {
|
|
1065
|
-
return
|
|
1350
|
+
return path9.join(
|
|
1066
1351
|
paths.venvDir,
|
|
1067
1352
|
process.platform === "win32" ? "Scripts" : "bin",
|
|
1068
1353
|
process.platform === "win32" ? "python.exe" : "python"
|
|
@@ -1070,17 +1355,17 @@ function venvPythonPath(paths) {
|
|
|
1070
1355
|
}
|
|
1071
1356
|
async function setupVenv(paths, payloadRoot, pythonCommand) {
|
|
1072
1357
|
const pythonExe = venvPythonPath(paths);
|
|
1073
|
-
if (!
|
|
1358
|
+
if (!fs8.existsSync(pythonExe)) {
|
|
1074
1359
|
console.log("Creating Lincoln virtual environment...");
|
|
1075
1360
|
const venvCode = await runCommand2(pythonCommand, ["-m", "venv", paths.venvDir]);
|
|
1076
1361
|
if (venvCode !== 0) {
|
|
1077
1362
|
throw new Error(`Failed to create virtual environment (exit code ${venvCode})`);
|
|
1078
1363
|
}
|
|
1079
1364
|
}
|
|
1080
|
-
const requirementsPath =
|
|
1081
|
-
if (
|
|
1365
|
+
const requirementsPath = path9.join(payloadRoot, "requirements.txt");
|
|
1366
|
+
if (fs8.existsSync(requirementsPath)) {
|
|
1082
1367
|
console.log("Installing Python dependencies...");
|
|
1083
|
-
const pipExe =
|
|
1368
|
+
const pipExe = path9.join(
|
|
1084
1369
|
paths.venvDir,
|
|
1085
1370
|
process.platform === "win32" ? "Scripts" : "bin",
|
|
1086
1371
|
process.platform === "win32" ? "pip.exe" : "pip"
|
|
@@ -1152,51 +1437,6 @@ function encodePackageName(packageName) {
|
|
|
1152
1437
|
return encodeURIComponent(packageName);
|
|
1153
1438
|
}
|
|
1154
1439
|
|
|
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
1440
|
// src/commands/update.ts
|
|
1201
1441
|
var PACKAGE_NAME = "@sushanglewis/lincoln";
|
|
1202
1442
|
function createDefaultDeps3() {
|
|
@@ -1276,8 +1516,8 @@ function runNpmInstallGlobal(packageName, version) {
|
|
|
1276
1516
|
}
|
|
1277
1517
|
|
|
1278
1518
|
// src/commands/use.ts
|
|
1279
|
-
import
|
|
1280
|
-
import
|
|
1519
|
+
import fs9 from "fs";
|
|
1520
|
+
import path10 from "path";
|
|
1281
1521
|
import { spawn as spawn5 } from "child_process";
|
|
1282
1522
|
var PACKAGE_NAME2 = "@sushanglewis/lincoln";
|
|
1283
1523
|
var SEMVER_REGEX = /^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -1319,8 +1559,8 @@ async function use(spec, options, deps = {}) {
|
|
|
1319
1559
|
console.error(`Failed to resolve version ${spec}: ${message}`);
|
|
1320
1560
|
return 1;
|
|
1321
1561
|
}
|
|
1322
|
-
const versionDir =
|
|
1323
|
-
const alreadyInstalled =
|
|
1562
|
+
const versionDir = path10.join(resolved.paths.versionsDir, version);
|
|
1563
|
+
const alreadyInstalled = fs9.existsSync(versionDir);
|
|
1324
1564
|
if (options.dryRun) {
|
|
1325
1565
|
if (alreadyInstalled) {
|
|
1326
1566
|
console.log(`Dry run: would switch ${resolved.paths.currentDir} to ${versionDir}`);
|
|
@@ -1343,7 +1583,7 @@ async function use(spec, options, deps = {}) {
|
|
|
1343
1583
|
console.error(`Failed to install ${PACKAGE_NAME2}@${version}: ${message}`);
|
|
1344
1584
|
return 1;
|
|
1345
1585
|
}
|
|
1346
|
-
if (!
|
|
1586
|
+
if (!fs9.existsSync(versionDir)) {
|
|
1347
1587
|
try {
|
|
1348
1588
|
await copyVersionFromNpmGlobal(resolved, versionDir);
|
|
1349
1589
|
} catch (err) {
|
|
@@ -1352,7 +1592,7 @@ async function use(spec, options, deps = {}) {
|
|
|
1352
1592
|
return 1;
|
|
1353
1593
|
}
|
|
1354
1594
|
}
|
|
1355
|
-
if (!
|
|
1595
|
+
if (!fs9.existsSync(versionDir)) {
|
|
1356
1596
|
console.error(
|
|
1357
1597
|
`Provisioning ${PACKAGE_NAME2}@${version} did not produce ${versionDir}; cannot switch versions.`
|
|
1358
1598
|
);
|
|
@@ -1389,21 +1629,21 @@ async function copyVersionFromNpmGlobal(deps, versionDir) {
|
|
|
1389
1629
|
if (!globalRoot) {
|
|
1390
1630
|
return;
|
|
1391
1631
|
}
|
|
1392
|
-
const packageRoot =
|
|
1393
|
-
if (!
|
|
1632
|
+
const packageRoot = path10.join(globalRoot, ...PACKAGE_NAME2.split("/"));
|
|
1633
|
+
if (!fs9.existsSync(packageRoot)) {
|
|
1394
1634
|
return;
|
|
1395
1635
|
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1636
|
+
fs9.mkdirSync(path10.dirname(versionDir), { recursive: true });
|
|
1637
|
+
fs9.cpSync(packageRoot, versionDir, { recursive: true });
|
|
1398
1638
|
}
|
|
1399
1639
|
function switchCurrentLink(paths, versionDir) {
|
|
1400
|
-
|
|
1640
|
+
fs9.mkdirSync(paths.lincolnHome, { recursive: true });
|
|
1401
1641
|
try {
|
|
1402
|
-
|
|
1403
|
-
|
|
1642
|
+
fs9.rmSync(paths.currentDir, { recursive: true, force: true });
|
|
1643
|
+
fs9.symlinkSync(versionDir, paths.currentDir);
|
|
1404
1644
|
} catch {
|
|
1405
|
-
|
|
1406
|
-
|
|
1645
|
+
fs9.rmSync(paths.currentDir, { recursive: true, force: true });
|
|
1646
|
+
fs9.cpSync(versionDir, paths.currentDir, { recursive: true, force: true });
|
|
1407
1647
|
}
|
|
1408
1648
|
}
|
|
1409
1649
|
function defaultInstallOptions2() {
|
|
@@ -1439,12 +1679,12 @@ function runNpmRootGlobal() {
|
|
|
1439
1679
|
}
|
|
1440
1680
|
|
|
1441
1681
|
// src/commands/migrateProject.ts
|
|
1442
|
-
import
|
|
1443
|
-
import
|
|
1682
|
+
import fs11 from "fs";
|
|
1683
|
+
import path12 from "path";
|
|
1444
1684
|
|
|
1445
1685
|
// src/lib/projectFiles.ts
|
|
1446
|
-
import
|
|
1447
|
-
import
|
|
1686
|
+
import fs10 from "fs";
|
|
1687
|
+
import path11 from "path";
|
|
1448
1688
|
import crypto2 from "crypto";
|
|
1449
1689
|
var FRAMEWORK_ALLOWLIST_DIRS = [".claude", ".claude-plugin", "scripts", "tools"];
|
|
1450
1690
|
var FRAMEWORK_ALLOWLIST_FILES = [
|
|
@@ -1479,10 +1719,10 @@ var FRAMEWORK_SKIP_PATHS = /* @__PURE__ */ new Set([
|
|
|
1479
1719
|
"tools/lincoln-installer/coverage"
|
|
1480
1720
|
]);
|
|
1481
1721
|
function sha2562(filePath) {
|
|
1482
|
-
return crypto2.createHash("sha256").update(
|
|
1722
|
+
return crypto2.createHash("sha256").update(fs10.readFileSync(filePath)).digest("hex");
|
|
1483
1723
|
}
|
|
1484
1724
|
function isDenied(relativePath) {
|
|
1485
|
-
const parts = relativePath.split(
|
|
1725
|
+
const parts = relativePath.split(path11.sep);
|
|
1486
1726
|
for (const part of parts) {
|
|
1487
1727
|
if (FRAMEWORK_DENYLIST_NAMES.has(part)) return true;
|
|
1488
1728
|
for (const prefix of FRAMEWORK_DENYLIST_PREFIXES) {
|
|
@@ -1490,14 +1730,14 @@ function isDenied(relativePath) {
|
|
|
1490
1730
|
}
|
|
1491
1731
|
}
|
|
1492
1732
|
for (const skip of FRAMEWORK_SKIP_PATHS) {
|
|
1493
|
-
if (relativePath === skip || relativePath.startsWith(skip +
|
|
1733
|
+
if (relativePath === skip || relativePath.startsWith(skip + path11.sep)) return true;
|
|
1494
1734
|
}
|
|
1495
1735
|
return false;
|
|
1496
1736
|
}
|
|
1497
1737
|
function isAllowlisted(relativePath) {
|
|
1498
1738
|
if (FRAMEWORK_ALLOWLIST_FILES.includes(relativePath)) return true;
|
|
1499
1739
|
for (const dir of FRAMEWORK_ALLOWLIST_DIRS) {
|
|
1500
|
-
if (relativePath === dir || relativePath.startsWith(dir +
|
|
1740
|
+
if (relativePath === dir || relativePath.startsWith(dir + path11.sep)) return true;
|
|
1501
1741
|
}
|
|
1502
1742
|
return false;
|
|
1503
1743
|
}
|
|
@@ -1518,10 +1758,10 @@ function isPreserved(relativePath) {
|
|
|
1518
1758
|
function listVendoredFrameworkFiles(payloadRoot, projectRoot) {
|
|
1519
1759
|
const report = { removable: [], modified: [], preserved: [] };
|
|
1520
1760
|
function walk(dir) {
|
|
1521
|
-
const entries =
|
|
1761
|
+
const entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
1522
1762
|
for (const entry of entries) {
|
|
1523
|
-
const fullPath =
|
|
1524
|
-
const relativePath =
|
|
1763
|
+
const fullPath = path11.join(dir, entry.name);
|
|
1764
|
+
const relativePath = path11.relative(projectRoot, fullPath);
|
|
1525
1765
|
if (entry.isDirectory()) {
|
|
1526
1766
|
if (isPreservedDir(relativePath)) {
|
|
1527
1767
|
walk(fullPath);
|
|
@@ -1536,8 +1776,8 @@ function listVendoredFrameworkFiles(payloadRoot, projectRoot) {
|
|
|
1536
1776
|
}
|
|
1537
1777
|
if (isDenied(relativePath)) continue;
|
|
1538
1778
|
if (!isAllowlisted(relativePath)) continue;
|
|
1539
|
-
const payloadFile =
|
|
1540
|
-
if (!
|
|
1779
|
+
const payloadFile = path11.join(payloadRoot, relativePath);
|
|
1780
|
+
if (!fs10.existsSync(payloadFile)) {
|
|
1541
1781
|
report.modified.push(relativePath);
|
|
1542
1782
|
continue;
|
|
1543
1783
|
}
|
|
@@ -1548,7 +1788,7 @@ function listVendoredFrameworkFiles(payloadRoot, projectRoot) {
|
|
|
1548
1788
|
}
|
|
1549
1789
|
}
|
|
1550
1790
|
}
|
|
1551
|
-
if (
|
|
1791
|
+
if (fs10.existsSync(projectRoot)) {
|
|
1552
1792
|
walk(projectRoot);
|
|
1553
1793
|
}
|
|
1554
1794
|
return report;
|
|
@@ -1564,11 +1804,11 @@ function createDefaultDeps5() {
|
|
|
1564
1804
|
}
|
|
1565
1805
|
async function migrateProject(projectRoot, options = {}, deps = createDefaultDeps5()) {
|
|
1566
1806
|
const payloadRoot = deps.paths.currentDir;
|
|
1567
|
-
if (!
|
|
1807
|
+
if (!fs11.existsSync(payloadRoot)) {
|
|
1568
1808
|
console.error(`Lincoln payload not found at ${payloadRoot}. Run "lincoln install" first.`);
|
|
1569
1809
|
return 1;
|
|
1570
1810
|
}
|
|
1571
|
-
const absoluteProjectRoot =
|
|
1811
|
+
const absoluteProjectRoot = path12.resolve(projectRoot);
|
|
1572
1812
|
const report = deps.listVendoredFrameworkFiles(payloadRoot, absoluteProjectRoot);
|
|
1573
1813
|
if (options.dryRun) {
|
|
1574
1814
|
console.log(`Dry run: would migrate ${absoluteProjectRoot}`);
|
|
@@ -1592,15 +1832,15 @@ async function migrateProject(projectRoot, options = {}, deps = createDefaultDep
|
|
|
1592
1832
|
return 1;
|
|
1593
1833
|
}
|
|
1594
1834
|
for (const file of report.removable) {
|
|
1595
|
-
const filePath =
|
|
1835
|
+
const filePath = path12.join(absoluteProjectRoot, file);
|
|
1596
1836
|
try {
|
|
1597
|
-
|
|
1598
|
-
let dir =
|
|
1599
|
-
while (dir !== absoluteProjectRoot &&
|
|
1600
|
-
const entries =
|
|
1837
|
+
fs11.rmSync(filePath);
|
|
1838
|
+
let dir = path12.dirname(filePath);
|
|
1839
|
+
while (dir !== absoluteProjectRoot && fs11.existsSync(dir)) {
|
|
1840
|
+
const entries = fs11.readdirSync(dir);
|
|
1601
1841
|
if (entries.length === 0) {
|
|
1602
|
-
|
|
1603
|
-
dir =
|
|
1842
|
+
fs11.rmdirSync(dir);
|
|
1843
|
+
dir = path12.dirname(dir);
|
|
1604
1844
|
} else {
|
|
1605
1845
|
break;
|
|
1606
1846
|
}
|
|
@@ -1749,7 +1989,7 @@ async function main(argv) {
|
|
|
1749
1989
|
console.log(USAGE);
|
|
1750
1990
|
return 0;
|
|
1751
1991
|
}
|
|
1752
|
-
if (process.argv[1] &&
|
|
1992
|
+
if (process.argv[1] && path13.resolve(fs12.realpathSync(fileURLToPath2(import.meta.url))) === path13.resolve(fs12.realpathSync(process.argv[1]))) {
|
|
1753
1993
|
main(process.argv).then((code) => process.exit(code)).catch((err) => {
|
|
1754
1994
|
console.error(err instanceof Error ? err.message : String(err));
|
|
1755
1995
|
process.exit(1);
|