@sushanglewis/lincoln 1.5.0 → 1.5.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/main.d.ts +3 -0
- package/dist/main.js +1231 -906
- package/dist/main.js.map +1 -1
- package/dist/postinstall.d.ts +3 -0
- package/dist/postinstall.js +18 -0
- package/dist/postinstall.js.map +1 -0
- package/framework/.claude/agents/_contract.md +79 -0
- package/framework/.claude/agents/architect.md +31 -0
- package/framework/.claude/agents/default.md +151 -0
- package/framework/.claude/agents/designer.md +33 -0
- package/framework/.claude/agents/engineer.md +42 -0
- package/framework/.claude/agents/external/NOTICES.md +12 -0
- package/framework/.claude/agents/external/everything-claude-code.manifest.yaml +23 -0
- package/framework/.claude/agents/external/oh-my-claudecode.manifest.yaml +26 -0
- package/framework/.claude/agents/external/wshobson-agents.manifest.yaml +23 -0
- package/framework/.claude/agents/frontend-engineer.md +32 -0
- package/framework/.claude/agents/knowledge-curator.md +28 -0
- package/framework/.claude/agents/pm.md +38 -0
- package/framework/.claude/agents/qa.md +28 -0
- package/framework/.claude/agents/release-coordinator.md +25 -0
- package/framework/.claude/agents/researcher.md +41 -0
- package/framework/.claude/harnesses/claude-code.yaml +15 -0
- package/framework/.claude/harnesses/codex.yaml +40 -0
- package/framework/.claude/harnesses/command-map.yaml +255 -0
- package/framework/.claude/harnesses/opencode.yaml +32 -0
- package/framework/.claude/harnesses/scenarios.yaml +45 -0
- package/framework/.claude/hooks/on-pr-event.sh +69 -0
- package/framework/.claude/hooks/on-session-start.sh +427 -0
- package/framework/.claude/hooks/on-stop.sh +104 -0
- package/framework/.claude/hooks/post-tool-use.sh +161 -0
- package/framework/.claude/hooks/pre-tool-use.sh +246 -0
- package/framework/.claude/schemas/agent-definition.schema.json +35 -0
- package/framework/.claude/schemas/benchmark-report.schema.json +148 -0
- package/framework/.claude/schemas/pm-to-ux-handoff.schema.yaml +128 -0
- package/framework/.claude/schemas/stage-definition.schema.json +233 -0
- package/framework/.claude/schemas/workflow-stage.schema.json +86 -0
- package/framework/.claude/schemas/workflow-template.schema.json +88 -0
- package/framework/.claude/settings.json +14 -0
- package/framework/.claude/skills/build-product-prototype/SKILL.md +35 -0
- package/framework/.claude/skills/build-product-prototype/prompts/main.md +78 -0
- package/framework/.claude/skills/clarify-requirements/SKILL.md +32 -0
- package/framework/.claude/skills/clarify-requirements/prompts/main.md +81 -0
- package/framework/.claude/skills/dependencies.yaml +139 -0
- package/framework/.claude/skills/draft-product-design/SKILL.md +40 -0
- package/framework/.claude/skills/draft-product-design/prompts/main.md +47 -0
- package/framework/.claude/skills/lc-analyze-frameworks/SKILL.md +43 -0
- package/framework/.claude/skills/lc-analyze-frameworks/prompts/main.md +95 -0
- package/framework/.claude/skills/lc-benchmark/SKILL.md +61 -0
- package/framework/.claude/skills/lc-build-codebase-knowledge/SKILL.md +38 -0
- package/framework/.claude/skills/lc-build-codebase-knowledge/prompts/build-codebase-knowledge.md +61 -0
- package/framework/.claude/skills/lc-collect-intelligence/SKILL.md +43 -0
- package/framework/.claude/skills/lc-collect-intelligence/prompts/main.md +72 -0
- package/framework/.claude/skills/lc-competitive-analysis/SKILL.md +43 -0
- package/framework/.claude/skills/lc-competitive-analysis/prompts/main.md +70 -0
- package/framework/.claude/skills/lc-explore-opensource/SKILL.md +36 -0
- package/framework/.claude/skills/lc-explore-opensource/prompts/explore-opensource.md +77 -0
- package/framework/.claude/skills/lc-first-principles/SKILL.md +43 -0
- package/framework/.claude/skills/lc-first-principles/prompts/main.md +73 -0
- package/framework/.claude/skills/lc-handoff/SKILL.md +34 -0
- package/framework/.claude/skills/lc-init-branch/SKILL.md +52 -0
- package/framework/.claude/skills/lc-market-research/SKILL.md +43 -0
- package/framework/.claude/skills/lc-market-research/prompts/main.md +79 -0
- package/framework/.claude/skills/lc-product-research/SKILL.md +43 -0
- package/framework/.claude/skills/lc-product-research/prompts/main.md +80 -0
- package/framework/.claude/skills/lc-research-report/SKILL.md +43 -0
- package/framework/.claude/skills/lc-research-report/prompts/main.md +90 -0
- package/framework/.claude/skills/lc-research-scope/SKILL.md +43 -0
- package/framework/.claude/skills/lc-research-scope/prompts/main.md +82 -0
- package/framework/.claude/skills/lc-setup/SKILL.md +68 -0
- package/framework/.claude/skills/lc-setup/prompts/main.md +88 -0
- package/framework/.claude/skills/lc-stage/SKILL.md +45 -0
- package/framework/.claude/skills/lc-stakeholder-research/SKILL.md +43 -0
- package/framework/.claude/skills/lc-stakeholder-research/prompts/main.md +65 -0
- package/framework/.claude/skills/lc-status/SKILL.md +25 -0
- package/framework/.claude/skills/lc-storytelling/SKILL.md +43 -0
- package/framework/.claude/skills/lc-storytelling/prompts/main.md +77 -0
- package/framework/.claude/skills/lc-wf/SKILL.md +54 -0
- package/framework/.claude/skills/lc-workflow/SKILL.md +48 -0
- package/framework/.claude/skills/lc-workflow/validators/validate.py +755 -0
- package/framework/.claude/skills/lc-workflow-router/SKILL.md +41 -0
- package/framework/.claude/skills/lc-workflow-router/prompts/intake-prompt.md +91 -0
- package/framework/.claude/skills/lc-workflow-router/prompts/router-prompt.md +44 -0
- package/framework/.claude/skills/plan-tdd-development/SKILL.md +31 -0
- package/framework/.claude/skills/plan-tdd-development/prompts/main.md +44 -0
- package/framework/.claude/skills/process-interview/SKILL.md +32 -0
- package/framework/.claude/skills/process-interview/prompts/main.md +47 -0
- package/framework/.claude/skills/propose-with-openspec/SKILL.md +37 -0
- package/framework/.claude/skills/propose-with-openspec/prompts/main.md +53 -0
- package/framework/.claude/skills/split-to-github/SKILL.md +32 -0
- package/framework/.claude/skills/split-to-github/prompts/main.md +42 -0
- package/framework/.claude/skills/sync-to-knowledge/SKILL.md +35 -0
- package/framework/.claude/skills/sync-to-knowledge/prompts/main.md +54 -0
- package/framework/.claude/skills/workflow-continue/SKILL.md +24 -0
- package/framework/.claude/skills/workflow-continue/prompts/main.md +25 -0
- package/framework/.claude/stages/build-codebase-knowledge.yaml +42 -0
- package/framework/.claude/stages/clarify.yaml +59 -0
- package/framework/.claude/stages/explore-opensource.yaml +40 -0
- package/framework/.claude/stages/implement.yaml +60 -0
- package/framework/.claude/stages/ingest.yaml +40 -0
- package/framework/.claude/stages/lc-analyze-frameworks.yaml +37 -0
- package/framework/.claude/stages/lc-collect-intelligence.yaml +37 -0
- package/framework/.claude/stages/lc-competitive-analysis.yaml +37 -0
- package/framework/.claude/stages/lc-first-principles.yaml +37 -0
- package/framework/.claude/stages/lc-market-research.yaml +37 -0
- package/framework/.claude/stages/lc-product-research.yaml +37 -0
- package/framework/.claude/stages/lc-research-report.yaml +38 -0
- package/framework/.claude/stages/lc-research-scope.yaml +38 -0
- package/framework/.claude/stages/lc-stakeholder-research.yaml +37 -0
- package/framework/.claude/stages/lc-storytelling.yaml +37 -0
- package/framework/.claude/stages/product-design-docs.yaml +66 -0
- package/framework/.claude/stages/product-prototype.yaml +53 -0
- package/framework/.claude/stages/propose.yaml +45 -0
- package/framework/.claude/stages/split.yaml +41 -0
- package/framework/.claude/stages/sync-knowledge.yaml +49 -0
- package/framework/.claude/stages/tdd-development-plan.yaml +43 -0
- package/framework/.claude/stages/workflow-router.yaml +45 -0
- package/framework/.claude/templates/solo-workflow-context.yaml +23 -0
- package/framework/.claude/workflows/README.md +93 -0
- package/framework/.claude/workflows/bug-fix.yaml +81 -0
- package/framework/.claude/workflows/design-spike.yaml +55 -0
- package/framework/.claude/workflows/existing-project-iteration.yaml +106 -0
- package/framework/.claude/workflows/interview-to-knowledge.yaml +189 -0
- package/framework/.claude/workflows/oss-first-design.yaml +100 -0
- package/framework/.claude/workflows/pm-research.yaml +115 -0
- package/framework/.claude-plugin/hooks.json +6 -0
- package/framework/.claude-plugin/marketplace.json +32 -0
- package/framework/.claude-plugin/plugin.json +57 -0
- package/framework/.version-bump.json +43 -0
- package/framework/CLAUDE.md +140 -0
- package/framework/CONTRIBUTING.md +219 -0
- package/framework/LICENSE +21 -0
- package/framework/README.en.md +139 -0
- package/framework/README.md +139 -0
- package/framework/RELEASE.md +334 -0
- package/framework/SKILL.md +24 -0
- package/framework/USAGE.md +356 -0
- package/framework/manifest.json +1214 -0
- package/framework/requirements.txt +4 -0
- package/framework/scripts/__init__.py +0 -0
- package/framework/scripts/build_codebase_knowledge.py +76 -0
- package/framework/scripts/bump_version.py +181 -0
- package/framework/scripts/check-dependency-drift.py +69 -0
- package/framework/scripts/check-harness-drift.sh +57 -0
- package/framework/scripts/check-main-merge-hygiene.py +128 -0
- package/framework/scripts/check-skill-dependencies.sh +124 -0
- package/framework/scripts/init-lincoln-branch.sh +300 -0
- package/framework/scripts/init-project.sh +184 -0
- package/framework/scripts/lc-benchmark-cli.py +66 -0
- package/framework/scripts/lincoln-audit.py +288 -0
- package/framework/scripts/lincoln-setup.py +651 -0
- package/framework/scripts/lincoln-status.py +448 -0
- package/framework/scripts/lincoln_benchmark.py +224 -0
- package/framework/scripts/lincoln_benchmark_eval.py +104 -0
- package/framework/scripts/lincoln_benchmark_metrics.py +818 -0
- package/framework/scripts/lincoln_benchmark_report.py +141 -0
- package/framework/scripts/lincoln_command_map.py +289 -0
- package/framework/scripts/lincoln_dependency_manager.py +335 -0
- package/framework/scripts/lincoln_documents.py +175 -0
- package/framework/scripts/lincoln_harness_adapter.py +319 -0
- package/framework/scripts/lincoln_index.py +255 -0
- package/framework/scripts/lincoln_paths.py +198 -0
- package/framework/scripts/lincoln_prd.py +227 -0
- package/framework/scripts/lincoln_role.py +46 -0
- package/framework/scripts/lincoln_scenario.py +59 -0
- package/framework/scripts/lincoln_session_start_metrics.py +71 -0
- package/framework/scripts/lincoln_skill_prompt.py +48 -0
- package/framework/scripts/lincoln_trace.py +335 -0
- package/framework/scripts/lincoln_workflow.py +199 -0
- package/framework/scripts/list-active-lincoln-branches.sh +87 -0
- package/framework/scripts/package-lincoln-plugin.py +340 -0
- package/framework/scripts/run-infrastructure-tests.py +84 -0
- package/framework/scripts/stage_loader.py +1071 -0
- package/framework/scripts/static-check.sh +356 -0
- package/framework/scripts/sync-external-agents.sh +210 -0
- package/framework/scripts/sync-framework-package.mjs +218 -0
- package/framework/scripts/task_tool_guard.py +213 -0
- package/framework/scripts/track-artifacts.py +140 -0
- package/framework/scripts/validate_stage.py +350 -0
- package/{README.md → framework/tools/lincoln/README.md} +4 -4
- package/framework/tools/lincoln/package-lock.json +3349 -0
- package/framework/tools/lincoln/package.json +48 -0
- package/framework/tools/lincoln/src/cli.tsx +60 -0
- package/framework/tools/lincoln/src/components/AudioMeter.tsx +34 -0
- package/framework/tools/lincoln/src/components/CancelledScreen.tsx +11 -0
- package/framework/tools/lincoln/src/components/DevicesMenu.tsx +79 -0
- package/framework/tools/lincoln/src/components/HintBar.tsx +25 -0
- package/framework/tools/lincoln/src/components/LogView.tsx +36 -0
- package/framework/tools/lincoln/src/components/ModelMenu.tsx +122 -0
- package/framework/tools/lincoln/src/components/ReadyScreen.tsx +54 -0
- package/framework/tools/lincoln/src/components/RecordingApp.tsx +211 -0
- package/framework/tools/lincoln/src/components/RecordingScreen.tsx +41 -0
- package/framework/tools/lincoln/src/components/SelectMenu.tsx +53 -0
- package/framework/tools/lincoln/src/components/StatusPanel.tsx +52 -0
- package/framework/tools/lincoln/src/components/StopConfirmation.tsx +32 -0
- package/framework/tools/lincoln/src/config/args.ts +35 -0
- package/framework/tools/lincoln/src/config/loadConfig.ts +81 -0
- package/framework/tools/lincoln/src/config/resolveConfig.ts +85 -0
- package/framework/tools/lincoln/src/devices/listDevices.ts +11 -0
- package/framework/tools/lincoln/src/devices/parseDevices.ts +25 -0
- package/framework/tools/lincoln/src/hooks/useKeyHandler.ts +63 -0
- package/framework/tools/lincoln/src/main.tsx +4 -0
- package/framework/tools/lincoln/src/models/models.ts +34 -0
- package/framework/tools/lincoln/src/models/runWarmup.ts +46 -0
- package/framework/tools/lincoln/src/models/warmupProgress.ts +30 -0
- package/framework/tools/lincoln/src/recording/spawnRecorder.ts +127 -0
- package/framework/tools/lincoln/src/recording/useRecorder.ts +146 -0
- package/framework/tools/lincoln/src/utils/amplitudeToMeter.ts +4 -0
- package/framework/tools/lincoln/src/utils/formatDuration.ts +6 -0
- package/framework/tools/lincoln/src/utils/sessionId.ts +25 -0
- package/framework/tools/lincoln/tests/cli.test.tsx +52 -0
- package/framework/tools/lincoln/tests/components/AudioMeter.test.tsx +31 -0
- package/framework/tools/lincoln/tests/components/CancelledScreen.test.tsx +12 -0
- package/framework/tools/lincoln/tests/components/ReadyScreen.test.tsx +68 -0
- package/framework/tools/lincoln/tests/components/RecordingApp.menus.test.tsx +158 -0
- package/framework/tools/lincoln/tests/components/RecordingApp.test.tsx +248 -0
- package/framework/tools/lincoln/tests/components/RecordingScreen.test.tsx +37 -0
- package/framework/tools/lincoln/tests/components/SelectMenu.test.tsx +59 -0
- package/framework/tools/lincoln/tests/components/StopConfirmation.test.tsx +38 -0
- package/framework/tools/lincoln/tests/config/args.test.ts +49 -0
- package/framework/tools/lincoln/tests/config/loadConfig.test.ts +59 -0
- package/framework/tools/lincoln/tests/config/resolveConfig.test.ts +72 -0
- package/framework/tools/lincoln/tests/devices/parseDevices.test.ts +21 -0
- package/framework/tools/lincoln/tests/hooks/useKeyHandler.enabled.test.tsx +44 -0
- package/framework/tools/lincoln/tests/hooks/useKeyHandler.test.tsx +130 -0
- package/framework/tools/lincoln/tests/models/models.test.ts +27 -0
- package/framework/tools/lincoln/tests/models/warmupProgress.test.ts +25 -0
- package/framework/tools/lincoln/tests/recording/spawnRecorder.test.ts +85 -0
- package/framework/tools/lincoln/tests/recording/useRecorder.test.tsx +172 -0
- package/framework/tools/lincoln/tests/utils/amplitudeToMeter.test.ts +22 -0
- package/framework/tools/lincoln/tests/utils/formatDuration.test.ts +11 -0
- package/framework/tools/lincoln/tests/utils/sessionId.test.ts +30 -0
- package/framework/tools/lincoln/tsconfig.json +22 -0
- package/framework/tools/lincoln/tsconfig.tests.json +10 -0
- package/framework/tools/lincoln/tsup.config.ts +17 -0
- package/framework/tools/lincoln/vitest.config.ts +20 -0
- package/framework/tools/lincoln-installer/package-lock.json +3611 -0
- package/framework/tools/lincoln-installer/package.json +53 -0
- package/framework/tools/lincoln-installer/src/cli.tsx +63 -0
- package/framework/tools/lincoln-installer/src/components/HarnessSelectScreen.tsx +43 -0
- package/framework/tools/lincoln-installer/src/components/InstallerApp.tsx +98 -0
- package/framework/tools/lincoln-installer/src/components/OptionsScreen.tsx +56 -0
- package/framework/tools/lincoln-installer/src/components/ProgressScreen.tsx +25 -0
- package/framework/tools/lincoln-installer/src/components/ResultScreen.tsx +40 -0
- package/framework/tools/lincoln-installer/src/components/SelectMenu.tsx +73 -0
- package/framework/tools/lincoln-installer/src/components/SummaryScreen.tsx +49 -0
- package/framework/tools/lincoln-installer/src/components/UpdateApp.tsx +135 -0
- package/framework/tools/lincoln-installer/src/components/WelcomeScreen.tsx +22 -0
- package/framework/tools/lincoln-installer/src/config/args.ts +76 -0
- package/framework/tools/lincoln-installer/src/config/resolveConfig.ts +33 -0
- package/framework/tools/lincoln-installer/src/config/updateArgs.ts +67 -0
- package/framework/tools/lincoln-installer/src/hooks/useSetup.ts +149 -0
- package/framework/tools/lincoln-installer/src/main.tsx +11 -0
- package/framework/tools/lincoln-installer/src/types.ts +19 -0
- package/framework/tools/lincoln-installer/src/update-main.tsx +11 -0
- package/framework/tools/lincoln-installer/src/update.tsx +90 -0
- package/framework/tools/lincoln-installer/src/utils/detectHarness.ts +51 -0
- package/framework/tools/lincoln-installer/src/utils/runSetup.ts +62 -0
- package/framework/tools/lincoln-installer/src/utils/updateRelease.ts +454 -0
- package/framework/tools/lincoln-installer/tests/components/InstallerApp.test.tsx +88 -0
- package/framework/tools/lincoln-installer/tests/components/SelectMenu.test.tsx +26 -0
- package/framework/tools/lincoln-installer/tests/components/UpdateApp.test.tsx +170 -0
- package/framework/tools/lincoln-installer/tests/components/screens.test.tsx +310 -0
- package/framework/tools/lincoln-installer/tests/config/args.test.ts +66 -0
- package/framework/tools/lincoln-installer/tests/config/resolveConfig.test.ts +75 -0
- package/framework/tools/lincoln-installer/tests/config/updateArgs.test.ts +52 -0
- package/framework/tools/lincoln-installer/tests/hooks/useSetup.test.tsx +213 -0
- package/framework/tools/lincoln-installer/tests/update.test.tsx +108 -0
- package/framework/tools/lincoln-installer/tests/utils/detectHarness.test.ts +40 -0
- package/framework/tools/lincoln-installer/tests/utils/runSetup.test.ts +45 -0
- package/framework/tools/lincoln-installer/tests/utils/updateRelease.test.ts +559 -0
- package/framework/tools/lincoln-installer/tsconfig.json +22 -0
- package/framework/tools/lincoln-installer/tsconfig.tests.json +8 -0
- package/framework/tools/lincoln-installer/tsup.config.ts +17 -0
- package/framework/tools/lincoln-installer/vitest.config.ts +20 -0
- package/framework/tools/lincoln-record/Cargo.lock +1896 -0
- package/framework/tools/lincoln-record/Cargo.toml +24 -0
- package/framework/tools/lincoln-record/diarize/diarize.py +117 -0
- package/framework/tools/lincoln-record/diarize/merge.py +55 -0
- package/framework/tools/lincoln-record/src/audio/capture/microphone.rs +194 -0
- package/framework/tools/lincoln-record/src/audio/capture/mod.rs +1 -0
- package/framework/tools/lincoln-record/src/audio/devices.rs +20 -0
- package/framework/tools/lincoln-record/src/audio/mod.rs +5 -0
- package/framework/tools/lincoln-record/src/audio/resampler.rs +44 -0
- package/framework/tools/lincoln-record/src/audio/saver.rs +87 -0
- package/framework/tools/lincoln-record/src/audio/source.rs +104 -0
- package/framework/tools/lincoln-record/src/cli.rs +96 -0
- package/framework/tools/lincoln-record/src/config.rs +135 -0
- package/framework/tools/lincoln-record/src/lib.rs +6 -0
- package/framework/tools/lincoln-record/src/main.rs +242 -0
- package/framework/tools/lincoln-record/src/model/mod.rs +248 -0
- package/framework/tools/lincoln-record/src/output/metadata.rs +30 -0
- package/framework/tools/lincoln-record/src/output/mod.rs +2 -0
- package/framework/tools/lincoln-record/src/output/transcript.rs +42 -0
- package/framework/tools/lincoln-record/src/transcription/diarization.rs +64 -0
- package/framework/tools/lincoln-record/src/transcription/mod.rs +3 -0
- package/framework/tools/lincoln-record/src/transcription/provider.rs +43 -0
- package/framework/tools/lincoln-record/src/transcription/whisper.rs +176 -0
- package/framework/tools/lincoln-record/tests/cli_args.rs +103 -0
- package/framework/tools/lincoln-record/tests/config_test.rs +70 -0
- package/framework/tools/lincoln-record/tests/devices_test.rs +23 -0
- package/framework/tools/lincoln-record/tests/diarization_tests.rs +101 -0
- package/framework/tools/lincoln-record/tests/integration_tests.rs +44 -0
- package/framework/tools/lincoln-record/tests/model_test.rs +68 -0
- package/framework/tools/lincoln-record/tests/transcription_tests.rs +73 -0
- package/package.json +19 -13
package/dist/main.js
CHANGED
|
@@ -1,1009 +1,1334 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/
|
|
4
|
-
import
|
|
3
|
+
// src/main.ts
|
|
4
|
+
import fs11 from "fs";
|
|
5
|
+
import path12 from "path";
|
|
6
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5
7
|
|
|
6
|
-
// src/
|
|
7
|
-
|
|
8
|
+
// src/cli.ts
|
|
9
|
+
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
10
|
+
"yes",
|
|
11
|
+
"y",
|
|
12
|
+
"dry-run",
|
|
13
|
+
"force",
|
|
14
|
+
"check",
|
|
15
|
+
"json",
|
|
16
|
+
"help",
|
|
17
|
+
"h",
|
|
18
|
+
"no-venv"
|
|
19
|
+
]);
|
|
8
20
|
function parseArgs(argv) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const args = argv.slice(2);
|
|
22
|
+
const flags = {};
|
|
23
|
+
const positional = [];
|
|
24
|
+
for (let i = 0; i < args.length; i++) {
|
|
25
|
+
const arg = args[i];
|
|
26
|
+
if (arg.startsWith("--")) {
|
|
27
|
+
const key = arg.replace(/^--/, "");
|
|
28
|
+
const next = args[i + 1];
|
|
29
|
+
if (!BOOLEAN_FLAGS.has(key) && next && !next.startsWith("-")) {
|
|
30
|
+
flags[key] = next;
|
|
31
|
+
i++;
|
|
32
|
+
} else {
|
|
33
|
+
flags[key] = true;
|
|
34
|
+
}
|
|
35
|
+
} else if (arg.startsWith("-")) {
|
|
36
|
+
const key = arg.replace(/^-/, "");
|
|
37
|
+
const next = args[i + 1];
|
|
38
|
+
if (!BOOLEAN_FLAGS.has(key) && next && !next.startsWith("-")) {
|
|
39
|
+
flags[key] = next;
|
|
40
|
+
i++;
|
|
41
|
+
} else {
|
|
42
|
+
flags[key] = true;
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
positional.push(arg);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const command = positional[0] || "help";
|
|
49
|
+
const validCommands = /* @__PURE__ */ new Set([
|
|
50
|
+
"install",
|
|
51
|
+
"update",
|
|
52
|
+
"use",
|
|
53
|
+
"doctor",
|
|
54
|
+
"init-project",
|
|
55
|
+
"migrate-project",
|
|
56
|
+
"record",
|
|
57
|
+
"help",
|
|
58
|
+
"--help",
|
|
59
|
+
"-h"
|
|
60
|
+
]);
|
|
61
|
+
if (command === "help" || command === "--help" || command === "-h") {
|
|
62
|
+
return { command: "help", args: positional.slice(1), flags };
|
|
63
|
+
}
|
|
64
|
+
if (!validCommands.has(command)) {
|
|
65
|
+
return { command: "help", args: positional, flags };
|
|
66
|
+
}
|
|
67
|
+
return { command, args: positional.slice(1), flags };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/commands/doctor.ts
|
|
71
|
+
import fs2 from "fs";
|
|
72
|
+
import path3 from "path";
|
|
73
|
+
import { spawn } from "child_process";
|
|
74
|
+
|
|
75
|
+
// src/lib/paths.ts
|
|
76
|
+
import path from "path";
|
|
77
|
+
import os from "os";
|
|
78
|
+
function resolveLincolnPaths(overrideHomeDir) {
|
|
79
|
+
const homeDir = overrideHomeDir ?? os.homedir();
|
|
80
|
+
const lincolnHome = process.env.LINCOLN_HOME ? path.resolve(process.env.LINCOLN_HOME) : path.join(homeDir, ".lincoln");
|
|
22
81
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
82
|
+
homeDir,
|
|
83
|
+
lincolnHome,
|
|
84
|
+
versionsDir: path.join(lincolnHome, "versions"),
|
|
85
|
+
currentDir: path.join(lincolnHome, "current"),
|
|
86
|
+
versionMarker: path.join(lincolnHome, "marker.json"),
|
|
87
|
+
venvDir: path.join(lincolnHome, "venv"),
|
|
88
|
+
claudeDir: path.join(homeDir, ".claude"),
|
|
89
|
+
codexDir: path.join(homeDir, ".codex"),
|
|
90
|
+
opencodeDir: path.join(homeDir, ".opencode")
|
|
29
91
|
};
|
|
30
92
|
}
|
|
31
93
|
|
|
32
|
-
// src/
|
|
33
|
-
import
|
|
34
|
-
import
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const isJson = trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
46
|
-
const raw = isJson ? JSON.parse(trimmed) : yaml.load(trimmed);
|
|
47
|
-
if (raw === null || typeof raw !== "object") {
|
|
48
|
-
return {};
|
|
94
|
+
// src/lib/versionMarker.ts
|
|
95
|
+
import fs from "fs";
|
|
96
|
+
import path2 from "path";
|
|
97
|
+
function readVersionMarker(paths) {
|
|
98
|
+
try {
|
|
99
|
+
const raw = fs.readFileSync(paths.versionMarker, "utf8");
|
|
100
|
+
const parsed = JSON.parse(raw);
|
|
101
|
+
if (typeof parsed.version === "string" && typeof parsed.installedAt === "string" && Array.isArray(parsed.harnesses)) {
|
|
102
|
+
return parsed;
|
|
103
|
+
}
|
|
104
|
+
return void 0;
|
|
105
|
+
} catch {
|
|
106
|
+
return void 0;
|
|
49
107
|
}
|
|
50
|
-
|
|
108
|
+
}
|
|
109
|
+
function writeVersionMarker(paths, marker) {
|
|
110
|
+
fs.mkdirSync(path2.dirname(paths.versionMarker), { recursive: true });
|
|
111
|
+
fs.writeFileSync(paths.versionMarker, JSON.stringify(marker, null, 2) + "\n");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/commands/doctor.ts
|
|
115
|
+
var MIN_NODE_MAJOR = 20;
|
|
116
|
+
var MIN_PYTHON_MAJOR = 3;
|
|
117
|
+
var MIN_PYTHON_MINOR = 10;
|
|
118
|
+
function createDefaultDeps() {
|
|
119
|
+
const paths = resolveLincolnPaths();
|
|
120
|
+
const pythonCommand2 = resolvePythonCommand(paths);
|
|
51
121
|
return {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
122
|
+
paths,
|
|
123
|
+
nodeVersion: () => process.version,
|
|
124
|
+
pythonVersion: () => queryCommandVersion(pythonCommand2, ["--version"]),
|
|
125
|
+
pyyamlVersion: () => queryPyyamlVersion(pythonCommand2),
|
|
126
|
+
npmVersion: () => queryCommandVersion("npm", ["--version"]),
|
|
127
|
+
projectRoot: process.cwd()
|
|
58
128
|
};
|
|
59
129
|
}
|
|
60
|
-
function
|
|
61
|
-
|
|
130
|
+
async function doctor(options = {}, deps = createDefaultDeps()) {
|
|
131
|
+
const checks = [];
|
|
132
|
+
checks.push(checkNode(deps.nodeVersion()));
|
|
133
|
+
checks.push(await checkPython(deps.pythonVersion));
|
|
134
|
+
checks.push(await checkPyyaml(deps.pyyamlVersion));
|
|
135
|
+
checks.push(await checkNpm(deps.npmVersion));
|
|
136
|
+
checks.push(checkGlobalMarker(deps.paths));
|
|
137
|
+
checks.push(checkPayloadHooks(deps.paths));
|
|
138
|
+
checks.push(checkVenv(deps.paths));
|
|
139
|
+
checks.push(checkProjectMarker(deps.projectRoot));
|
|
140
|
+
const hasError = checks.some((c) => c.status === "error");
|
|
141
|
+
const result = { code: hasError ? 1 : 0, checks };
|
|
142
|
+
if (options.json) {
|
|
143
|
+
console.log(JSON.stringify({ checks }, null, 2));
|
|
144
|
+
} else {
|
|
145
|
+
for (const check of checks) {
|
|
146
|
+
const icon = check.status === "ok" ? "\u2713" : check.status === "error" ? "\u2717" : check.status === "warn" ? "!" : "-";
|
|
147
|
+
console.log(`${icon} ${check.name}: ${check.message}`);
|
|
148
|
+
}
|
|
149
|
+
if (hasError) {
|
|
150
|
+
console.error("\nOne or more checks failed.");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
62
154
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
155
|
+
function checkNode(version) {
|
|
156
|
+
const major = parseNodeMajor(version);
|
|
157
|
+
if (major === void 0) {
|
|
158
|
+
return { name: "node", status: "warn", message: `Could not parse Node version: ${version}` };
|
|
159
|
+
}
|
|
160
|
+
if (major < MIN_NODE_MAJOR) {
|
|
161
|
+
return { name: "node", status: "error", message: `Node ${version} is below the minimum required v${MIN_NODE_MAJOR}` };
|
|
162
|
+
}
|
|
163
|
+
return { name: "node", status: "ok", message: `Node ${version}` };
|
|
65
164
|
}
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
165
|
+
async function checkPython(query) {
|
|
166
|
+
const version = (await query())?.trim();
|
|
167
|
+
if (!version) {
|
|
168
|
+
return { name: "python", status: "error", message: "Python 3 is not available" };
|
|
69
169
|
}
|
|
70
|
-
|
|
170
|
+
const parsed = parsePythonVersion(version);
|
|
171
|
+
if (!parsed) {
|
|
172
|
+
return { name: "python", status: "warn", message: `Could not parse Python version: ${version}` };
|
|
173
|
+
}
|
|
174
|
+
if (parsed.major < MIN_PYTHON_MAJOR || parsed.major === MIN_PYTHON_MAJOR && parsed.minor < MIN_PYTHON_MINOR) {
|
|
175
|
+
return {
|
|
176
|
+
name: "python",
|
|
177
|
+
status: "error",
|
|
178
|
+
message: `Python ${version} is below the minimum required ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}`
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const display = version.replace(/^Python\s+/, "");
|
|
182
|
+
return { name: "python", status: "ok", message: `Python ${display}` };
|
|
71
183
|
}
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
} catch (error) {
|
|
77
|
-
throw new Error(`Config file not found: ${String(path)}`);
|
|
184
|
+
async function checkPyyaml(query) {
|
|
185
|
+
const version = await query();
|
|
186
|
+
if (!version) {
|
|
187
|
+
return { name: "pyyaml", status: "error", message: "PyYAML is not installed for Python 3" };
|
|
78
188
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
189
|
+
return { name: "pyyaml", status: "ok", message: `PyYAML ${version}` };
|
|
190
|
+
}
|
|
191
|
+
async function checkNpm(query) {
|
|
192
|
+
const version = await query();
|
|
193
|
+
if (!version) {
|
|
194
|
+
return { name: "npm", status: "warn", message: "npm is not available" };
|
|
84
195
|
}
|
|
196
|
+
return { name: "npm", status: "ok", message: `npm ${version}` };
|
|
85
197
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
91
|
-
if (message.includes("Config file not found")) {
|
|
92
|
-
return {};
|
|
93
|
-
}
|
|
94
|
-
throw error;
|
|
198
|
+
function checkGlobalMarker(paths) {
|
|
199
|
+
const marker = readVersionMarker(paths);
|
|
200
|
+
if (!marker) {
|
|
201
|
+
return { name: "global-marker", status: "warn", message: `No version marker at ${paths.versionMarker}` };
|
|
95
202
|
}
|
|
203
|
+
return { name: "global-marker", status: "ok", message: `Lincoln ${marker.version} installed` };
|
|
96
204
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
205
|
+
function checkPayloadHooks(paths) {
|
|
206
|
+
const hooksDir = path3.join(paths.currentDir, ".claude", "hooks");
|
|
207
|
+
if (!fs2.existsSync(hooksDir)) {
|
|
208
|
+
return { name: "payload-hooks", status: "warn", message: `Payload hooks not found at ${hooksDir}` };
|
|
209
|
+
}
|
|
210
|
+
const required = ["on-session-start.sh"];
|
|
211
|
+
const missing = required.filter((f) => !fs2.existsSync(path3.join(hooksDir, f)));
|
|
212
|
+
if (missing.length > 0) {
|
|
213
|
+
return { name: "payload-hooks", status: "warn", message: `Missing hooks: ${missing.join(", ")}` };
|
|
214
|
+
}
|
|
215
|
+
return { name: "payload-hooks", status: "ok", message: `Payload hooks present at ${hooksDir}` };
|
|
104
216
|
}
|
|
105
|
-
function
|
|
106
|
-
if (!
|
|
107
|
-
return "
|
|
217
|
+
function checkVenv(paths) {
|
|
218
|
+
if (!fs2.existsSync(paths.venvDir)) {
|
|
219
|
+
return { name: "venv", status: "warn", message: `Virtual environment not found at ${paths.venvDir}` };
|
|
108
220
|
}
|
|
109
|
-
const
|
|
110
|
-
|
|
221
|
+
const pythonExe = path3.join(paths.venvDir, process.platform === "win32" ? "Scripts" : "bin", "python");
|
|
222
|
+
if (!fs2.existsSync(pythonExe)) {
|
|
223
|
+
return { name: "venv", status: "warn", message: `Virtual environment exists but python executable is missing` };
|
|
224
|
+
}
|
|
225
|
+
return { name: "venv", status: "ok", message: `Virtual environment present at ${paths.venvDir}` };
|
|
226
|
+
}
|
|
227
|
+
function checkProjectMarker(projectRoot) {
|
|
228
|
+
const markerPath = path3.join(projectRoot, ".lincoln.yaml");
|
|
229
|
+
if (!fs2.existsSync(markerPath)) {
|
|
230
|
+
return { name: "project-marker", status: "skip", message: `No .lincoln.yaml in ${projectRoot}` };
|
|
231
|
+
}
|
|
232
|
+
return { name: "project-marker", status: "ok", message: `Project marker found at ${markerPath}` };
|
|
233
|
+
}
|
|
234
|
+
function resolvePythonCommand(paths) {
|
|
235
|
+
const venvPython = path3.join(
|
|
236
|
+
paths.venvDir,
|
|
237
|
+
process.platform === "win32" ? "Scripts" : "bin",
|
|
238
|
+
process.platform === "win32" ? "python.exe" : "python"
|
|
239
|
+
);
|
|
240
|
+
if (fs2.existsSync(venvPython)) {
|
|
241
|
+
return venvPython;
|
|
242
|
+
}
|
|
243
|
+
return "python3";
|
|
244
|
+
}
|
|
245
|
+
function parseNodeMajor(version) {
|
|
246
|
+
const match = /^v?(\d+)/.exec(version);
|
|
247
|
+
if (!match) {
|
|
248
|
+
return void 0;
|
|
249
|
+
}
|
|
250
|
+
return Number(match[1]);
|
|
251
|
+
}
|
|
252
|
+
function parsePythonVersion(version) {
|
|
253
|
+
const match = /(\d+)\.(\d+)/.exec(version);
|
|
254
|
+
if (!match) {
|
|
255
|
+
return void 0;
|
|
256
|
+
}
|
|
257
|
+
return { major: Number(match[1]), minor: Number(match[2]) };
|
|
258
|
+
}
|
|
259
|
+
async function queryCommandVersion(command, args) {
|
|
260
|
+
return new Promise((resolvePromise) => {
|
|
261
|
+
const child = spawn(command, args, {
|
|
262
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
263
|
+
shell: process.platform === "win32"
|
|
264
|
+
});
|
|
265
|
+
let stdout = "";
|
|
266
|
+
child.stdout?.on("data", (chunk) => {
|
|
267
|
+
stdout += chunk.toString();
|
|
268
|
+
});
|
|
269
|
+
child.on("close", (code) => {
|
|
270
|
+
const text = stdout.trim();
|
|
271
|
+
resolvePromise(code === 0 && text.length > 0 ? text : void 0);
|
|
272
|
+
});
|
|
273
|
+
child.on("error", () => resolvePromise(void 0));
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
async function queryPyyamlVersion(pythonCommand2) {
|
|
277
|
+
return new Promise((resolvePromise) => {
|
|
278
|
+
const child = spawn(
|
|
279
|
+
pythonCommand2,
|
|
280
|
+
["-c", "import yaml; print(yaml.__version__)"],
|
|
281
|
+
{ stdio: ["ignore", "pipe", "ignore"], shell: process.platform === "win32" }
|
|
282
|
+
);
|
|
283
|
+
let stdout = "";
|
|
284
|
+
child.stdout?.on("data", (chunk) => {
|
|
285
|
+
stdout += chunk.toString();
|
|
286
|
+
});
|
|
287
|
+
child.on("close", (code) => {
|
|
288
|
+
const text = stdout.trim();
|
|
289
|
+
resolvePromise(code === 0 && text.length > 0 ? text : void 0);
|
|
290
|
+
});
|
|
291
|
+
child.on("error", () => resolvePromise(void 0));
|
|
292
|
+
});
|
|
111
293
|
}
|
|
112
294
|
|
|
113
|
-
// src/
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
295
|
+
// src/lib/packageInfo.ts
|
|
296
|
+
import fs3 from "fs";
|
|
297
|
+
import path4 from "path";
|
|
298
|
+
import { fileURLToPath } from "url";
|
|
299
|
+
function readLocalPackageRoot() {
|
|
300
|
+
try {
|
|
301
|
+
let dir = path4.dirname(fileURLToPath(import.meta.url));
|
|
302
|
+
const root = path4.parse(dir).root;
|
|
303
|
+
while (dir !== root) {
|
|
304
|
+
const pkgPath = path4.join(dir, "package.json");
|
|
305
|
+
if (fs3.existsSync(pkgPath)) {
|
|
306
|
+
const parsed = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
|
|
307
|
+
if (parsed.name === "@sushanglewis/lincoln") {
|
|
308
|
+
return dir;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
dir = path4.dirname(dir);
|
|
118
312
|
}
|
|
313
|
+
} catch {
|
|
314
|
+
}
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
function readLocalPackageVersion() {
|
|
318
|
+
try {
|
|
319
|
+
const root = readLocalPackageRoot();
|
|
320
|
+
if (!root) return void 0;
|
|
321
|
+
const parsed = JSON.parse(fs3.readFileSync(path4.join(root, "package.json"), "utf8"));
|
|
322
|
+
return typeof parsed.version === "string" ? parsed.version : void 0;
|
|
323
|
+
} catch {
|
|
324
|
+
return void 0;
|
|
119
325
|
}
|
|
120
|
-
return "";
|
|
121
326
|
}
|
|
122
|
-
function
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
327
|
+
function resolvePayloadRoot() {
|
|
328
|
+
const root = readLocalPackageRoot();
|
|
329
|
+
if (!root) return void 0;
|
|
330
|
+
const payload = path4.join(root, "framework");
|
|
331
|
+
return fs3.existsSync(payload) ? payload : void 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/lib/marker.ts
|
|
335
|
+
import fs4 from "fs";
|
|
336
|
+
import path5 from "path";
|
|
337
|
+
import yaml from "js-yaml";
|
|
338
|
+
function readProjectMarker(projectRoot) {
|
|
339
|
+
const markerPath = path5.join(projectRoot, ".lincoln.yaml");
|
|
340
|
+
if (!fs4.existsSync(markerPath)) {
|
|
341
|
+
return void 0;
|
|
127
342
|
}
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return candidate;
|
|
343
|
+
try {
|
|
344
|
+
const parsed = yaml.load(fs4.readFileSync(markerPath, "utf8"));
|
|
345
|
+
if (!parsed || typeof parsed !== "object") {
|
|
346
|
+
return void 0;
|
|
133
347
|
}
|
|
348
|
+
return {
|
|
349
|
+
enabled: Boolean(parsed.enabled),
|
|
350
|
+
workflow_template: typeof parsed.workflow_template === "string" ? parsed.workflow_template : void 0,
|
|
351
|
+
min_lincoln_version: typeof parsed.min_lincoln_version === "string" ? parsed.min_lincoln_version : void 0
|
|
352
|
+
};
|
|
353
|
+
} catch {
|
|
354
|
+
return void 0;
|
|
134
355
|
}
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
356
|
+
}
|
|
357
|
+
function writeProjectMarker(projectRoot, marker, opts = {}) {
|
|
358
|
+
const markerPath = path5.join(projectRoot, ".lincoln.yaml");
|
|
359
|
+
if (fs4.existsSync(markerPath) && !opts.force) {
|
|
360
|
+
throw new Error(`.lincoln.yaml already exists at ${markerPath}; use --force to overwrite`);
|
|
361
|
+
}
|
|
362
|
+
const content = yaml.dump(marker, { sortKeys: false, lineWidth: -1 });
|
|
363
|
+
if (!opts.dryRun) {
|
|
364
|
+
fs4.mkdirSync(projectRoot, { recursive: true });
|
|
365
|
+
fs4.writeFileSync(markerPath, content);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// src/commands/initProject.ts
|
|
370
|
+
function initProject(projectRoot, options = {}) {
|
|
371
|
+
if (!options.force && readProjectMarker(projectRoot) !== void 0) {
|
|
372
|
+
console.error(`Project already has a .lincoln.yaml. Use --force to overwrite.`);
|
|
373
|
+
return 1;
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
writeProjectMarker(
|
|
377
|
+
projectRoot,
|
|
378
|
+
{
|
|
379
|
+
enabled: true,
|
|
380
|
+
workflow_template: "interview-to-knowledge",
|
|
381
|
+
min_lincoln_version: readLocalPackageVersion() ?? "1.5.0"
|
|
382
|
+
},
|
|
383
|
+
{ force: options.force, dryRun: options.dryRun }
|
|
384
|
+
);
|
|
385
|
+
} catch (err) {
|
|
386
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
387
|
+
console.error(`Failed to write .lincoln.yaml: ${message}`);
|
|
388
|
+
return 1;
|
|
389
|
+
}
|
|
390
|
+
if (options.dryRun) {
|
|
391
|
+
console.log(`Dry run: would create ${projectRoot}/.lincoln.yaml`);
|
|
392
|
+
} else {
|
|
393
|
+
console.log(`Created ${projectRoot}/.lincoln.yaml`);
|
|
394
|
+
}
|
|
395
|
+
return 0;
|
|
159
396
|
}
|
|
160
397
|
|
|
161
|
-
// src/
|
|
162
|
-
import
|
|
163
|
-
import
|
|
398
|
+
// src/commands/install.ts
|
|
399
|
+
import fs7 from "fs";
|
|
400
|
+
import path8 from "path";
|
|
401
|
+
import { spawn as spawn2 } from "child_process";
|
|
164
402
|
|
|
165
|
-
// src/
|
|
166
|
-
import
|
|
403
|
+
// src/lib/syncClaude.ts
|
|
404
|
+
import fs5 from "fs";
|
|
405
|
+
import path6 from "path";
|
|
406
|
+
import crypto from "crypto";
|
|
167
407
|
|
|
168
|
-
// src/
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
}
|
|
408
|
+
// src/lib/claudeMdMerge.ts
|
|
409
|
+
var BEGIN = "<!-- lincoln:begin -->";
|
|
410
|
+
var END = "<!-- lincoln:end -->";
|
|
411
|
+
function mergeManagedBlock(existing, block) {
|
|
412
|
+
const wrapped = `${BEGIN}
|
|
413
|
+
${block}
|
|
414
|
+
${END}`;
|
|
415
|
+
const text = existing ?? "";
|
|
416
|
+
const beginIndex = text.indexOf(BEGIN);
|
|
417
|
+
const endIndex = text.indexOf(END);
|
|
418
|
+
if (beginIndex !== -1 && endIndex !== -1 && endIndex > beginIndex) {
|
|
419
|
+
return text.slice(0, beginIndex) + wrapped + text.slice(endIndex + END.length);
|
|
420
|
+
}
|
|
421
|
+
if (text.trim().length === 0) {
|
|
422
|
+
return wrapped;
|
|
423
|
+
}
|
|
424
|
+
return `${text.trimEnd()}
|
|
175
425
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
function StatusPanel({
|
|
179
|
-
sessionId,
|
|
180
|
-
topic,
|
|
181
|
-
designId,
|
|
182
|
-
branch,
|
|
183
|
-
status,
|
|
184
|
-
duration
|
|
185
|
-
}) {
|
|
186
|
-
const statusLabel = {
|
|
187
|
-
idle: "Ready",
|
|
188
|
-
recording: `Recording ${formatDuration(duration)}`,
|
|
189
|
-
stopped: "Stopped",
|
|
190
|
-
cancelled: "Cancelled",
|
|
191
|
-
error: "Error"
|
|
192
|
-
}[status];
|
|
193
|
-
const statusColor = status === "recording" ? "red" : status === "error" ? "red" : status === "cancelled" ? "yellow" : "green";
|
|
194
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
|
|
195
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", justifyContent: "space-between", alignItems: "center", children: [
|
|
196
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: "white", children: "Lincoln Recorder" }),
|
|
197
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
198
|
-
/* @__PURE__ */ jsx(Text, { color: statusColor, children: status === "recording" ? "\u25CF" : "\u25CB" }),
|
|
199
|
-
/* @__PURE__ */ jsx(Text, { children: statusLabel })
|
|
200
|
-
] })
|
|
201
|
-
] }),
|
|
202
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingY: 1, children: [
|
|
203
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
204
|
-
"Session: ",
|
|
205
|
-
sessionId
|
|
206
|
-
] }),
|
|
207
|
-
topic ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
208
|
-
"Topic: ",
|
|
209
|
-
topic
|
|
210
|
-
] }) : null,
|
|
211
|
-
designId ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
212
|
-
"Design: ",
|
|
213
|
-
designId
|
|
214
|
-
] }) : null,
|
|
215
|
-
branch ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
216
|
-
"Branch: ",
|
|
217
|
-
branch
|
|
218
|
-
] }) : null
|
|
219
|
-
] })
|
|
220
|
-
] });
|
|
426
|
+
${wrapped}
|
|
427
|
+
`;
|
|
221
428
|
}
|
|
222
429
|
|
|
223
|
-
// src/
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
visible.length === 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "No events yet." }) : visible.map((log) => /* @__PURE__ */ jsx2(Text2, { color: TYPE_COLORS[log.type ?? "info"], children: log.message }, log.id))
|
|
237
|
-
] });
|
|
430
|
+
// src/lib/syncClaude.ts
|
|
431
|
+
var COPY_SUBDIRS = [
|
|
432
|
+
"agents",
|
|
433
|
+
"skills",
|
|
434
|
+
"workflows",
|
|
435
|
+
"stages",
|
|
436
|
+
"schemas",
|
|
437
|
+
"templates",
|
|
438
|
+
"harnesses"
|
|
439
|
+
];
|
|
440
|
+
function sha256(filePath) {
|
|
441
|
+
const data = fs5.readFileSync(filePath);
|
|
442
|
+
return crypto.createHash("sha256").update(data).digest("hex");
|
|
238
443
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
import { Box as Box3, Text as Text3 } from "ink";
|
|
242
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
243
|
-
var HINTS = {
|
|
244
|
-
idle: "[r] record [d] devices [m] model [q] quit",
|
|
245
|
-
recording: "[s] stop [c] cancel",
|
|
246
|
-
stopped: "[any key] exit",
|
|
247
|
-
cancelled: "[q] exit",
|
|
248
|
-
error: "[q] exit",
|
|
249
|
-
menu: "[\u2191\u2193] select [enter] confirm [esc] back"
|
|
250
|
-
};
|
|
251
|
-
function HintBar({ mode }) {
|
|
252
|
-
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: HINTS[mode] }) });
|
|
444
|
+
function sameContent(a, b) {
|
|
445
|
+
return sha256(a) === sha256(b);
|
|
253
446
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (key.escape) {
|
|
267
|
-
onClose();
|
|
268
|
-
return;
|
|
447
|
+
function deepMergeSettings(existing, incoming) {
|
|
448
|
+
if (Array.isArray(existing) && Array.isArray(incoming)) {
|
|
449
|
+
return incoming;
|
|
450
|
+
}
|
|
451
|
+
if (typeof existing === "object" && existing !== null && typeof incoming === "object" && incoming !== null) {
|
|
452
|
+
const result = { ...existing };
|
|
453
|
+
for (const [key, value] of Object.entries(incoming)) {
|
|
454
|
+
if (key in result) {
|
|
455
|
+
result[key] = deepMergeSettings(result[key], value);
|
|
456
|
+
} else {
|
|
457
|
+
result[key] = value;
|
|
458
|
+
}
|
|
269
459
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
462
|
+
return incoming;
|
|
463
|
+
}
|
|
464
|
+
function syncClaudeCode(opts) {
|
|
465
|
+
const report = {
|
|
466
|
+
written: [],
|
|
467
|
+
skipped: [],
|
|
468
|
+
preserved: [],
|
|
469
|
+
warnings: [],
|
|
470
|
+
settingsTouched: []
|
|
471
|
+
};
|
|
472
|
+
const payloadClaude = path6.join(opts.payloadRoot, ".claude");
|
|
473
|
+
if (!fs5.existsSync(payloadClaude)) {
|
|
474
|
+
report.warnings.push(`payload .claude directory not found: ${payloadClaude}`);
|
|
475
|
+
return report;
|
|
476
|
+
}
|
|
477
|
+
if (!opts.dryRun) {
|
|
478
|
+
fs5.mkdirSync(opts.targetDir, { recursive: true });
|
|
479
|
+
}
|
|
480
|
+
for (const subdir of COPY_SUBDIRS) {
|
|
481
|
+
const srcDir = path6.join(payloadClaude, subdir);
|
|
482
|
+
if (!fs5.existsSync(srcDir)) {
|
|
483
|
+
continue;
|
|
273
484
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
485
|
+
const destDir = path6.join(opts.targetDir, subdir);
|
|
486
|
+
if (!opts.dryRun) {
|
|
487
|
+
fs5.mkdirSync(destDir, { recursive: true });
|
|
277
488
|
}
|
|
278
|
-
|
|
279
|
-
|
|
489
|
+
for (const entry of walkSync(srcDir)) {
|
|
490
|
+
const relativePath = path6.relative(srcDir, entry);
|
|
491
|
+
const srcFile = entry;
|
|
492
|
+
const destFile = path6.join(destDir, relativePath);
|
|
493
|
+
if (fs5.existsSync(destFile) && sameContent(srcFile, destFile)) {
|
|
494
|
+
report.skipped.push(path6.join(".claude", subdir, relativePath));
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (!opts.dryRun) {
|
|
498
|
+
fs5.mkdirSync(path6.dirname(destFile), { recursive: true });
|
|
499
|
+
fs5.copyFileSync(srcFile, destFile);
|
|
500
|
+
}
|
|
501
|
+
report.written.push(path6.join(".claude", subdir, relativePath));
|
|
280
502
|
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
function DevicesMenu({ listDevicesFn, onSelect, onClose }) {
|
|
296
|
-
const [loadState, setLoadState] = useState2({ status: "loading" });
|
|
297
|
-
useEffect(() => {
|
|
298
|
-
let cancelled = false;
|
|
299
|
-
listDevicesFn().then((list2) => {
|
|
300
|
-
if (!cancelled) setLoadState({ status: "loaded", list: list2 });
|
|
301
|
-
}).catch((error) => {
|
|
302
|
-
if (!cancelled) {
|
|
303
|
-
setLoadState({
|
|
304
|
-
status: "error",
|
|
305
|
-
message: error instanceof Error ? error.message : String(error)
|
|
306
|
-
});
|
|
503
|
+
}
|
|
504
|
+
const payloadSettings = path6.join(payloadClaude, "settings.json");
|
|
505
|
+
const targetSettings = path6.join(opts.targetDir, "settings.json");
|
|
506
|
+
if (fs5.existsSync(payloadSettings)) {
|
|
507
|
+
const incoming = JSON.parse(fs5.readFileSync(payloadSettings, "utf8"));
|
|
508
|
+
let merged = incoming;
|
|
509
|
+
if (fs5.existsSync(targetSettings)) {
|
|
510
|
+
const existing = JSON.parse(fs5.readFileSync(targetSettings, "utf8"));
|
|
511
|
+
merged = deepMergeSettings(existing, incoming);
|
|
512
|
+
}
|
|
513
|
+
const wouldChange = !fs5.existsSync(targetSettings) || JSON.stringify(merged) !== JSON.stringify(JSON.parse(fs5.readFileSync(targetSettings, "utf8")));
|
|
514
|
+
if (wouldChange) {
|
|
515
|
+
if (!opts.dryRun) {
|
|
516
|
+
fs5.writeFileSync(targetSettings, JSON.stringify(merged, null, 2) + "\n");
|
|
307
517
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}, [listDevicesFn]);
|
|
313
|
-
useInput2(
|
|
314
|
-
(_input, key) => {
|
|
315
|
-
if (key.escape) onClose();
|
|
316
|
-
},
|
|
317
|
-
{ isActive: loadState.status !== "loaded" }
|
|
318
|
-
);
|
|
319
|
-
if (loadState.status === "loading") {
|
|
320
|
-
return /* @__PURE__ */ jsx5(Box5, { paddingX: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Loading devices\u2026" }) });
|
|
321
|
-
}
|
|
322
|
-
if (loadState.status === "error") {
|
|
323
|
-
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", paddingX: 1, children: [
|
|
324
|
-
/* @__PURE__ */ jsxs4(Text5, { color: "red", children: [
|
|
325
|
-
"Failed to list devices: ",
|
|
326
|
-
loadState.message
|
|
327
|
-
] }),
|
|
328
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[esc] back" })
|
|
329
|
-
] });
|
|
330
|
-
}
|
|
331
|
-
const { list } = loadState;
|
|
332
|
-
const items = list.devices.map((name) => ({
|
|
333
|
-
key: name,
|
|
334
|
-
label: name === list.defaultDevice ? `${name} (default)` : name
|
|
335
|
-
}));
|
|
336
|
-
return /* @__PURE__ */ jsx5(
|
|
337
|
-
SelectMenu,
|
|
338
|
-
{
|
|
339
|
-
title: "Devices",
|
|
340
|
-
items,
|
|
341
|
-
onSelect,
|
|
342
|
-
onClose,
|
|
343
|
-
footer: "[\u2191\u2193] select [enter] confirm [esc] back"
|
|
518
|
+
report.written.push(".claude/settings.json");
|
|
519
|
+
report.settingsTouched = [".claude/settings.json"];
|
|
520
|
+
} else {
|
|
521
|
+
report.skipped.push(".claude/settings.json");
|
|
344
522
|
}
|
|
345
|
-
|
|
523
|
+
}
|
|
524
|
+
const payloadClaudeMd = path6.join(opts.payloadRoot, "CLAUDE.md");
|
|
525
|
+
const targetClaudeMd = path6.join(path6.dirname(opts.targetDir), "CLAUDE.md");
|
|
526
|
+
if (fs5.existsSync(payloadClaudeMd)) {
|
|
527
|
+
const incomingBlock = fs5.readFileSync(payloadClaudeMd, "utf8");
|
|
528
|
+
const existing = fs5.existsSync(targetClaudeMd) ? fs5.readFileSync(targetClaudeMd, "utf8") : void 0;
|
|
529
|
+
const merged = mergeManagedBlock(existing, incomingBlock);
|
|
530
|
+
if (merged !== existing) {
|
|
531
|
+
if (!opts.dryRun) {
|
|
532
|
+
fs5.writeFileSync(targetClaudeMd, merged);
|
|
533
|
+
}
|
|
534
|
+
report.written.push("CLAUDE.md");
|
|
535
|
+
} else {
|
|
536
|
+
report.skipped.push("CLAUDE.md");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return report;
|
|
540
|
+
}
|
|
541
|
+
function* walkSync(dir) {
|
|
542
|
+
const entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
543
|
+
for (const entry of entries) {
|
|
544
|
+
const fullPath = path6.join(dir, entry.name);
|
|
545
|
+
if (entry.isDirectory()) {
|
|
546
|
+
yield* walkSync(fullPath);
|
|
547
|
+
} else {
|
|
548
|
+
yield fullPath;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
346
551
|
}
|
|
347
552
|
|
|
348
|
-
// src/
|
|
349
|
-
import
|
|
350
|
-
import
|
|
351
|
-
import
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
553
|
+
// src/lib/harnessDetect.ts
|
|
554
|
+
import fs6 from "fs";
|
|
555
|
+
import path7 from "path";
|
|
556
|
+
import os2 from "os";
|
|
557
|
+
var HARNESS_SPECS = [
|
|
558
|
+
{
|
|
559
|
+
id: "claude-code",
|
|
560
|
+
configDirName: ".claude",
|
|
561
|
+
pluginMarker: "plugins/lincoln"
|
|
562
|
+
},
|
|
563
|
+
{ id: "codex", configDirName: ".codex" },
|
|
564
|
+
{ id: "opencode", configDirName: ".opencode" }
|
|
565
|
+
];
|
|
566
|
+
function detectGlobalHarnesses(overrideHomeDir) {
|
|
567
|
+
const homeDir = overrideHomeDir ?? os2.homedir();
|
|
568
|
+
return HARNESS_SPECS.map((spec) => {
|
|
569
|
+
const configDir = path7.join(homeDir, spec.configDirName);
|
|
570
|
+
const installed = fs6.existsSync(configDir);
|
|
571
|
+
const pluginManaged = spec.pluginMarker ? fs6.existsSync(path7.join(configDir, spec.pluginMarker)) : false;
|
|
572
|
+
return {
|
|
573
|
+
id: spec.id,
|
|
574
|
+
installed,
|
|
575
|
+
configDir,
|
|
576
|
+
pluginManaged
|
|
368
577
|
};
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
onSelect(name);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
setMenuState({ status: "downloading", model: name, percent: null, downloaded: 0 });
|
|
385
|
-
runWarmupFn(name, (progress) => {
|
|
386
|
-
setMenuState({
|
|
387
|
-
status: "downloading",
|
|
388
|
-
model: name,
|
|
389
|
-
percent: progress.percent,
|
|
390
|
-
downloaded: progress.downloaded
|
|
391
|
-
});
|
|
392
|
-
}).then(() => onSelect(name)).catch((error) => {
|
|
393
|
-
setMenuState({
|
|
394
|
-
status: "error",
|
|
395
|
-
message: error instanceof Error ? error.message : String(error)
|
|
396
|
-
});
|
|
397
|
-
});
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
function installedHarnessIds(homeDir) {
|
|
581
|
+
return detectGlobalHarnesses(homeDir).filter((h) => h.installed).map((h) => h.id);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/commands/install.ts
|
|
585
|
+
function createDefaultDeps2() {
|
|
586
|
+
return {
|
|
587
|
+
paths: resolveLincolnPaths(),
|
|
588
|
+
payloadRoot: resolvePayloadRoot() || "",
|
|
589
|
+
syncClaude: syncClaudeCode
|
|
398
590
|
};
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
return
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
{
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
591
|
+
}
|
|
592
|
+
async function install(options, deps = createDefaultDeps2()) {
|
|
593
|
+
if (!options.dryRun && !options.yes) {
|
|
594
|
+
console.error("This will install Lincoln globally. Run with --yes to proceed.");
|
|
595
|
+
return 1;
|
|
596
|
+
}
|
|
597
|
+
const version = readLocalPackageVersion();
|
|
598
|
+
if (!version) {
|
|
599
|
+
console.error("Could not determine Lincoln version");
|
|
600
|
+
return 1;
|
|
601
|
+
}
|
|
602
|
+
const harnesses = options.harnesses.length > 0 ? options.harnesses : installedHarnessIds(deps.paths.homeDir);
|
|
603
|
+
if (harnesses.length === 0) {
|
|
604
|
+
console.error("No agent harness detected. Install Claude Code, Codex, or OpenCode first.");
|
|
605
|
+
return 1;
|
|
606
|
+
}
|
|
607
|
+
const payloadRoot = deps.payloadRoot;
|
|
608
|
+
if (!payloadRoot || !fs7.existsSync(payloadRoot)) {
|
|
609
|
+
console.error(
|
|
610
|
+
`Lincoln payload not found. Run "npm install -g @sushanglewis/lincoln" first.`
|
|
611
|
+
);
|
|
612
|
+
return 1;
|
|
613
|
+
}
|
|
614
|
+
const versionDir = path8.join(deps.paths.versionsDir, version);
|
|
615
|
+
if (!options.dryRun) {
|
|
616
|
+
fs7.mkdirSync(versionDir, { recursive: true });
|
|
617
|
+
fs7.cpSync(payloadRoot, versionDir, { recursive: true, force: true });
|
|
618
|
+
try {
|
|
619
|
+
fs7.rmSync(deps.paths.currentDir, { recursive: true, force: true });
|
|
620
|
+
fs7.symlinkSync(versionDir, deps.paths.currentDir);
|
|
621
|
+
} catch {
|
|
622
|
+
fs7.rmSync(deps.paths.currentDir, { recursive: true, force: true });
|
|
623
|
+
fs7.cpSync(versionDir, deps.paths.currentDir, { recursive: true, force: true });
|
|
432
624
|
}
|
|
625
|
+
}
|
|
626
|
+
const report = deps.syncClaude({
|
|
627
|
+
payloadRoot: deps.paths.currentDir,
|
|
628
|
+
targetDir: deps.paths.claudeDir,
|
|
629
|
+
version,
|
|
630
|
+
dryRun: options.dryRun
|
|
631
|
+
});
|
|
632
|
+
if (!options.dryRun && !options.noVenv) {
|
|
633
|
+
await setupVenv(deps.paths, payloadRoot);
|
|
634
|
+
}
|
|
635
|
+
if (!options.dryRun) {
|
|
636
|
+
writeVersionMarker(deps.paths, {
|
|
637
|
+
version,
|
|
638
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
639
|
+
harnesses,
|
|
640
|
+
managedFiles: report.written
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
if (options.dryRun) {
|
|
644
|
+
console.log(`Dry run: would install Lincoln ${version} for harnesses: ${harnesses.join(", ")}`);
|
|
645
|
+
console.log(`Would write ${report.written.length} files`);
|
|
646
|
+
} else {
|
|
647
|
+
console.log(`Installed Lincoln ${version} for harnesses: ${harnesses.join(", ")}`);
|
|
648
|
+
console.log(`Wrote ${report.written.length} files`);
|
|
649
|
+
}
|
|
650
|
+
return 0;
|
|
651
|
+
}
|
|
652
|
+
function runCommand(command, args) {
|
|
653
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
654
|
+
const child = spawn2(command, args, { stdio: "inherit" });
|
|
655
|
+
child.on("close", (code) => resolvePromise(code ?? 1));
|
|
656
|
+
child.on("error", (err) => rejectPromise(err));
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
function pythonCommand() {
|
|
660
|
+
return process.env.LINCOLN_PYTHON || "python3";
|
|
661
|
+
}
|
|
662
|
+
async function setupVenv(paths, payloadRoot) {
|
|
663
|
+
const pythonExe = path8.join(
|
|
664
|
+
paths.venvDir,
|
|
665
|
+
process.platform === "win32" ? "Scripts" : "bin",
|
|
666
|
+
process.platform === "win32" ? "python.exe" : "python"
|
|
433
667
|
);
|
|
668
|
+
if (!fs7.existsSync(pythonExe)) {
|
|
669
|
+
console.log("Creating Lincoln virtual environment...");
|
|
670
|
+
await runCommand(pythonCommand(), ["-m", "venv", paths.venvDir]);
|
|
671
|
+
}
|
|
672
|
+
const requirementsPath = path8.join(payloadRoot, "requirements.txt");
|
|
673
|
+
if (fs7.existsSync(requirementsPath)) {
|
|
674
|
+
console.log("Installing Python dependencies...");
|
|
675
|
+
const pipExe = path8.join(
|
|
676
|
+
paths.venvDir,
|
|
677
|
+
process.platform === "win32" ? "Scripts" : "bin",
|
|
678
|
+
process.platform === "win32" ? "pip.exe" : "pip"
|
|
679
|
+
);
|
|
680
|
+
await runCommand(pipExe, ["install", "-r", requirementsPath]);
|
|
681
|
+
}
|
|
434
682
|
}
|
|
435
683
|
|
|
436
|
-
// src/
|
|
437
|
-
import {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
684
|
+
// src/commands/update.ts
|
|
685
|
+
import { spawn as spawn3 } from "child_process";
|
|
686
|
+
|
|
687
|
+
// src/lib/npmRegistry.ts
|
|
688
|
+
var DEFAULT_REGISTRY_URL = "https://registry.npmjs.org";
|
|
689
|
+
var FETCH_TIMEOUT_MS = 15e3;
|
|
690
|
+
function createRegistryClient(registryUrl = DEFAULT_REGISTRY_URL, fetchImpl = fetch) {
|
|
691
|
+
const baseUrl = registryUrl.replace(/\/+$/, "");
|
|
692
|
+
async function fetchPackument(packageName) {
|
|
693
|
+
const url = `${baseUrl}/${encodePackageName(packageName)}`;
|
|
694
|
+
const response = await fetchImpl(url, {
|
|
695
|
+
headers: { Accept: "application/json" },
|
|
696
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
697
|
+
});
|
|
698
|
+
if (!response.ok) {
|
|
699
|
+
throw new Error(
|
|
700
|
+
`npm registry returned ${response.status} ${response.statusText} for ${packageName}`
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
return await response.json();
|
|
704
|
+
}
|
|
705
|
+
return {
|
|
706
|
+
async latestVersion(packageName) {
|
|
707
|
+
const packument = await fetchPackument(packageName);
|
|
708
|
+
const latest = packument["dist-tags"]?.latest;
|
|
709
|
+
if (!latest) {
|
|
710
|
+
throw new Error(`npm registry has no latest dist-tag for ${packageName}`);
|
|
453
711
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
712
|
+
return latest;
|
|
713
|
+
},
|
|
714
|
+
async versions(packageName) {
|
|
715
|
+
const packument = await fetchPackument(packageName);
|
|
716
|
+
return Object.keys(packument.versions ?? {});
|
|
717
|
+
},
|
|
718
|
+
async resolveVersion(packageName, spec) {
|
|
719
|
+
const packument = await fetchPackument(packageName);
|
|
720
|
+
const tagTarget = packument["dist-tags"]?.[spec];
|
|
721
|
+
if (tagTarget) {
|
|
722
|
+
return tagTarget;
|
|
457
723
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
case "R":
|
|
461
|
-
onRecord?.();
|
|
462
|
-
return;
|
|
463
|
-
case "s":
|
|
464
|
-
case "S":
|
|
465
|
-
onStop?.();
|
|
466
|
-
return;
|
|
467
|
-
case "c":
|
|
468
|
-
case "C":
|
|
469
|
-
onCancel?.();
|
|
470
|
-
return;
|
|
471
|
-
case "d":
|
|
472
|
-
case "D":
|
|
473
|
-
onDevices?.();
|
|
474
|
-
return;
|
|
475
|
-
case "m":
|
|
476
|
-
case "M":
|
|
477
|
-
onModel?.();
|
|
478
|
-
return;
|
|
724
|
+
if (packument.versions && spec in packument.versions) {
|
|
725
|
+
return spec;
|
|
479
726
|
}
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
|
|
727
|
+
throw new Error(`Version not found for ${packageName}: ${spec}`);
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function encodePackageName(packageName) {
|
|
732
|
+
if (packageName.startsWith("@")) {
|
|
733
|
+
const slash = packageName.indexOf("/");
|
|
734
|
+
if (slash === -1) {
|
|
735
|
+
return encodeURIComponent(packageName);
|
|
736
|
+
}
|
|
737
|
+
const scope = packageName.slice(1, slash);
|
|
738
|
+
const name = packageName.slice(slash + 1);
|
|
739
|
+
return `@${encodeURIComponent(scope)}%2F${encodeURIComponent(name)}`;
|
|
740
|
+
}
|
|
741
|
+
return encodeURIComponent(packageName);
|
|
484
742
|
}
|
|
485
743
|
|
|
486
|
-
// src/
|
|
487
|
-
|
|
744
|
+
// src/lib/semver.ts
|
|
745
|
+
var SEMVER_CORE = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
746
|
+
var NUMERIC_IDENTIFIER = /^\d+$/;
|
|
747
|
+
function compareVersions(a, b) {
|
|
748
|
+
const pa = parseVersion(a);
|
|
749
|
+
const pb = parseVersion(b);
|
|
750
|
+
if (!pa || !pb) {
|
|
751
|
+
return void 0;
|
|
752
|
+
}
|
|
753
|
+
if (pa.major !== pb.major) return pa.major - pb.major;
|
|
754
|
+
if (pa.minor !== pb.minor) return pa.minor - pb.minor;
|
|
755
|
+
if (pa.patch !== pb.patch) return pa.patch - pb.patch;
|
|
756
|
+
return comparePrerelease(pa.prerelease, pb.prerelease);
|
|
757
|
+
}
|
|
758
|
+
function parseVersion(version) {
|
|
759
|
+
const match = SEMVER_CORE.exec(version.trim());
|
|
760
|
+
if (!match) {
|
|
761
|
+
return void 0;
|
|
762
|
+
}
|
|
763
|
+
return {
|
|
764
|
+
major: Number(match[1]),
|
|
765
|
+
minor: Number(match[2]),
|
|
766
|
+
patch: Number(match[3]),
|
|
767
|
+
prerelease: match[4] ? match[4].split(".") : []
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
function comparePrerelease(a, b) {
|
|
771
|
+
if (a.length === 0 && b.length === 0) return 0;
|
|
772
|
+
if (a.length === 0) return 1;
|
|
773
|
+
if (b.length === 0) return -1;
|
|
774
|
+
const shared = Math.min(a.length, b.length);
|
|
775
|
+
for (let i = 0; i < shared; i++) {
|
|
776
|
+
const x = a[i];
|
|
777
|
+
const y = b[i];
|
|
778
|
+
if (x === y) continue;
|
|
779
|
+
const xNumeric = NUMERIC_IDENTIFIER.test(x);
|
|
780
|
+
const yNumeric = NUMERIC_IDENTIFIER.test(y);
|
|
781
|
+
if (xNumeric && yNumeric) return Number(x) - Number(y);
|
|
782
|
+
if (xNumeric) return -1;
|
|
783
|
+
if (yNumeric) return 1;
|
|
784
|
+
return x < y ? -1 : 1;
|
|
785
|
+
}
|
|
786
|
+
return a.length - b.length;
|
|
787
|
+
}
|
|
488
788
|
|
|
489
|
-
// src/
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
789
|
+
// src/commands/update.ts
|
|
790
|
+
var PACKAGE_NAME = "@sushanglewis/lincoln";
|
|
791
|
+
function createDefaultDeps3() {
|
|
792
|
+
const paths = resolveLincolnPaths();
|
|
793
|
+
const registry = createRegistryClient();
|
|
794
|
+
return {
|
|
795
|
+
paths,
|
|
796
|
+
latestVersion: () => registry.latestVersion(PACKAGE_NAME),
|
|
797
|
+
currentVersion: () => readVersionMarker(paths)?.version ?? readLocalPackageVersion(),
|
|
798
|
+
npmInstallGlobal: (version) => runNpmInstallGlobal(PACKAGE_NAME, version),
|
|
799
|
+
runInstall: (options) => install(options)
|
|
497
800
|
};
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
});
|
|
509
|
-
this.child.on("exit", (code, signal) => {
|
|
510
|
-
this.emit("exit", code, signal);
|
|
511
|
-
this.settleExit();
|
|
512
|
-
});
|
|
513
|
-
this.child.stdout.on("data", (data) => {
|
|
514
|
-
process.stdout.write(data);
|
|
515
|
-
});
|
|
516
|
-
this.child.stderr.on("data", (data) => {
|
|
517
|
-
process.stderr.write(data);
|
|
518
|
-
});
|
|
519
|
-
process.nextTick(() => this.emit("ready"));
|
|
801
|
+
}
|
|
802
|
+
async function update(options, deps = {}) {
|
|
803
|
+
const resolved = { ...createDefaultDeps3(), ...deps };
|
|
804
|
+
let latest;
|
|
805
|
+
try {
|
|
806
|
+
latest = await resolved.latestVersion();
|
|
807
|
+
} catch (err) {
|
|
808
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
809
|
+
console.error(`Failed to query npm registry: ${message}`);
|
|
810
|
+
return 1;
|
|
520
811
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
812
|
+
const current = resolved.currentVersion();
|
|
813
|
+
if (current !== void 0) {
|
|
814
|
+
const comparison = compareVersions(current, latest);
|
|
815
|
+
if (comparison !== void 0 ? comparison > 0 : false) {
|
|
816
|
+
console.log(
|
|
817
|
+
`Lincoln ${current} is ahead of the latest release (${latest}); nothing to do.`
|
|
818
|
+
);
|
|
819
|
+
return 0;
|
|
820
|
+
}
|
|
821
|
+
if (comparison !== void 0 ? comparison === 0 : current === latest) {
|
|
822
|
+
console.log(`Lincoln ${current} is already up to date.`);
|
|
823
|
+
return 0;
|
|
525
824
|
}
|
|
526
825
|
}
|
|
527
|
-
|
|
528
|
-
|
|
826
|
+
const summary = `${current ?? "unknown"} -> ${latest}`;
|
|
827
|
+
if (options.check) {
|
|
828
|
+
console.log(`Lincoln update available: ${summary}`);
|
|
829
|
+
return 0;
|
|
529
830
|
}
|
|
530
|
-
|
|
531
|
-
|
|
831
|
+
if (options.dryRun) {
|
|
832
|
+
console.log(`Dry run: would update Lincoln ${summary}`);
|
|
833
|
+
return 0;
|
|
532
834
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
}
|
|
537
|
-
this.stopped = true;
|
|
538
|
-
if (this.child.killed) {
|
|
539
|
-
return this.exitPromise;
|
|
540
|
-
}
|
|
541
|
-
this.child.kill(signal);
|
|
542
|
-
const timeout = setTimeout(() => {
|
|
543
|
-
if (!this.child.killed) {
|
|
544
|
-
this.child.kill("SIGKILL");
|
|
545
|
-
}
|
|
546
|
-
}, 5e3);
|
|
547
|
-
this.exitPromise.finally(() => clearTimeout(timeout));
|
|
548
|
-
return this.exitPromise;
|
|
835
|
+
if (!options.yes) {
|
|
836
|
+
console.error(`Will update Lincoln ${summary}. Run with --yes to proceed.`);
|
|
837
|
+
return 1;
|
|
549
838
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
839
|
+
const installCode = await resolved.npmInstallGlobal(latest);
|
|
840
|
+
if (installCode !== 0) {
|
|
841
|
+
console.error(`npm install -g ${PACKAGE_NAME}@${latest} failed with exit code ${installCode}`);
|
|
842
|
+
return 1;
|
|
843
|
+
}
|
|
844
|
+
const syncCode = await resolved.runInstall(defaultInstallOptions());
|
|
845
|
+
if (syncCode !== 0) {
|
|
846
|
+
console.error(`lincoln install failed with exit code ${syncCode}`);
|
|
847
|
+
return 1;
|
|
848
|
+
}
|
|
849
|
+
console.log(`Updated Lincoln to ${latest}`);
|
|
850
|
+
return 0;
|
|
851
|
+
}
|
|
852
|
+
function defaultInstallOptions() {
|
|
853
|
+
return { yes: true, dryRun: false, force: false, harnesses: [], noVenv: true };
|
|
854
|
+
}
|
|
855
|
+
function runNpmInstallGlobal(packageName, version) {
|
|
856
|
+
return new Promise((resolvePromise) => {
|
|
857
|
+
const child = spawn3("npm", ["install", "-g", `${packageName}@${version}`], {
|
|
858
|
+
stdio: "inherit",
|
|
859
|
+
shell: process.platform === "win32"
|
|
860
|
+
});
|
|
861
|
+
child.on("close", (code) => resolvePromise(code ?? 1));
|
|
862
|
+
child.on("error", () => resolvePromise(1));
|
|
575
863
|
});
|
|
576
|
-
return new RecorderProcessImpl(child);
|
|
577
864
|
}
|
|
578
865
|
|
|
579
|
-
// src/
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
866
|
+
// src/commands/use.ts
|
|
867
|
+
import fs8 from "fs";
|
|
868
|
+
import path9 from "path";
|
|
869
|
+
import { spawn as spawn4 } from "child_process";
|
|
870
|
+
var PACKAGE_NAME2 = "@sushanglewis/lincoln";
|
|
871
|
+
var SEMVER_REGEX = /^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
872
|
+
var STRICT_SEMVER_REGEX = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
873
|
+
var DIST_TAG_REGEX = /^[a-zA-Z][a-zA-Z0-9._-]*$/;
|
|
874
|
+
function createDefaultDeps4() {
|
|
875
|
+
const paths = resolveLincolnPaths();
|
|
876
|
+
const registry = createRegistryClient();
|
|
877
|
+
return {
|
|
878
|
+
paths,
|
|
879
|
+
resolveVersion: async (spec) => {
|
|
880
|
+
const normalized = spec.startsWith("v") ? spec.slice(1) : spec;
|
|
881
|
+
if (STRICT_SEMVER_REGEX.test(normalized)) {
|
|
882
|
+
return normalized;
|
|
883
|
+
}
|
|
884
|
+
return registry.resolveVersion(PACKAGE_NAME2, spec);
|
|
885
|
+
},
|
|
886
|
+
ensureVersionAvailable: async (version) => {
|
|
887
|
+
const code = await runNpmInstallGlobal2(PACKAGE_NAME2, version);
|
|
888
|
+
if (code !== 0) {
|
|
889
|
+
throw new Error(`npm install -g ${PACKAGE_NAME2}@${version} exited with ${code}`);
|
|
890
|
+
}
|
|
891
|
+
},
|
|
892
|
+
npmGlobalRoot: () => runNpmRootGlobal(),
|
|
893
|
+
runInstall: (options) => install(options)
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
async function use(spec, options, deps = {}) {
|
|
897
|
+
if (!isValidVersion(spec)) {
|
|
898
|
+
console.error(`Invalid version: ${spec}`);
|
|
899
|
+
return 1;
|
|
900
|
+
}
|
|
901
|
+
const resolved = { ...createDefaultDeps4(), ...deps };
|
|
902
|
+
let version;
|
|
903
|
+
try {
|
|
904
|
+
version = await resolved.resolveVersion(spec);
|
|
905
|
+
} catch (err) {
|
|
906
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
907
|
+
console.error(`Failed to resolve version ${spec}: ${message}`);
|
|
908
|
+
return 1;
|
|
909
|
+
}
|
|
910
|
+
const versionDir = path9.join(resolved.paths.versionsDir, version);
|
|
911
|
+
const alreadyInstalled = fs8.existsSync(versionDir);
|
|
912
|
+
if (options.dryRun) {
|
|
913
|
+
if (alreadyInstalled) {
|
|
914
|
+
console.log(`Dry run: would switch ${resolved.paths.currentDir} to ${versionDir}`);
|
|
915
|
+
} else {
|
|
916
|
+
console.log(
|
|
917
|
+
`Dry run: would install ${PACKAGE_NAME2}@${version} globally, then switch ${resolved.paths.currentDir} to ${versionDir}`
|
|
918
|
+
);
|
|
611
919
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
920
|
+
return 0;
|
|
921
|
+
}
|
|
922
|
+
if (!options.yes) {
|
|
923
|
+
console.error(`Will switch Lincoln to ${version}. Run with --yes to proceed.`);
|
|
924
|
+
return 1;
|
|
925
|
+
}
|
|
926
|
+
if (!alreadyInstalled) {
|
|
615
927
|
try {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
model
|
|
622
|
-
});
|
|
623
|
-
recorderRef.current = recorder;
|
|
624
|
-
recorder.on("ready", () => {
|
|
625
|
-
});
|
|
626
|
-
recorder.on("error", (error) => {
|
|
627
|
-
setState((s) => ({ ...s, status: "error", errorMessage: error.message }));
|
|
628
|
-
clearRecordingInterval();
|
|
629
|
-
});
|
|
630
|
-
recorder.on("exit", (code, signal) => {
|
|
631
|
-
recorderRef.current = null;
|
|
632
|
-
clearRecordingInterval();
|
|
633
|
-
if (terminatingRef.current) {
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
const reason = code !== null && code !== void 0 ? `lincoln-record exited unexpectedly with code ${code}` : `lincoln-record exited unexpectedly with signal ${signal}`;
|
|
637
|
-
setState((s) => ({ ...s, status: "error", errorMessage: reason }));
|
|
638
|
-
});
|
|
639
|
-
intervalRef.current = setInterval(() => {
|
|
640
|
-
durationRef.current += 1;
|
|
641
|
-
setState((s) => ({
|
|
642
|
-
...s,
|
|
643
|
-
duration: durationRef.current,
|
|
644
|
-
amplitude: mockAmplitude(durationRef.current)
|
|
645
|
-
}));
|
|
646
|
-
}, 1e3);
|
|
647
|
-
} catch (error) {
|
|
648
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
649
|
-
setState((s) => ({ ...s, status: "error", errorMessage: message }));
|
|
650
|
-
}
|
|
651
|
-
}, [workspaceRoot, sessionId, lincolnRecordPath, mic, model, clearRecordingInterval]);
|
|
652
|
-
const stop = useCallback(async () => {
|
|
653
|
-
const recorder = recorderRef.current;
|
|
654
|
-
if (!recorder) return;
|
|
655
|
-
terminatingRef.current = true;
|
|
656
|
-
clearRecordingInterval();
|
|
657
|
-
setState((s) => ({ ...s, status: "stopped" }));
|
|
658
|
-
await recorder.stop();
|
|
659
|
-
}, [clearRecordingInterval]);
|
|
660
|
-
const cancel = useCallback(async () => {
|
|
661
|
-
const recorder = recorderRef.current;
|
|
662
|
-
if (!recorder) return;
|
|
663
|
-
terminatingRef.current = true;
|
|
664
|
-
clearRecordingInterval();
|
|
665
|
-
setState((s) => ({ ...s, status: "cancelled" }));
|
|
666
|
-
await recorder.cancel();
|
|
667
|
-
}, [clearRecordingInterval]);
|
|
668
|
-
useEffect3(() => {
|
|
669
|
-
if (startOnMount) {
|
|
670
|
-
start();
|
|
928
|
+
await resolved.ensureVersionAvailable(version);
|
|
929
|
+
} catch (err) {
|
|
930
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
931
|
+
console.error(`Failed to install ${PACKAGE_NAME2}@${version}: ${message}`);
|
|
932
|
+
return 1;
|
|
671
933
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
934
|
+
if (!fs8.existsSync(versionDir)) {
|
|
935
|
+
try {
|
|
936
|
+
await copyVersionFromNpmGlobal(resolved, versionDir);
|
|
937
|
+
} catch (err) {
|
|
938
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
939
|
+
console.error(`Failed to populate ${versionDir}: ${message}`);
|
|
940
|
+
return 1;
|
|
678
941
|
}
|
|
679
|
-
};
|
|
680
|
-
}, [startOnMount, start, clearRecordingInterval]);
|
|
681
|
-
return { state, start, stop, cancel };
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// src/devices/listDevices.ts
|
|
685
|
-
import { execFile } from "child_process";
|
|
686
|
-
import { promisify } from "util";
|
|
687
|
-
|
|
688
|
-
// src/devices/parseDevices.ts
|
|
689
|
-
var DEFAULT_PREFIX = "Default input: ";
|
|
690
|
-
function parseDevicesOutput(output) {
|
|
691
|
-
let defaultDevice = null;
|
|
692
|
-
const devices = [];
|
|
693
|
-
for (const rawLine of output.split("\n")) {
|
|
694
|
-
const line = rawLine.trim();
|
|
695
|
-
if (!line) continue;
|
|
696
|
-
if (line.startsWith(DEFAULT_PREFIX)) {
|
|
697
|
-
defaultDevice = line.slice(DEFAULT_PREFIX.length).trim() || null;
|
|
698
|
-
continue;
|
|
699
942
|
}
|
|
700
|
-
|
|
943
|
+
if (!fs8.existsSync(versionDir)) {
|
|
944
|
+
console.error(
|
|
945
|
+
`Provisioning ${PACKAGE_NAME2}@${version} did not produce ${versionDir}; cannot switch versions.`
|
|
946
|
+
);
|
|
947
|
+
return 1;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
try {
|
|
951
|
+
switchCurrentLink(resolved.paths, versionDir);
|
|
952
|
+
} catch (err) {
|
|
953
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
954
|
+
console.error(`Failed to switch ${resolved.paths.currentDir}: ${message}`);
|
|
955
|
+
return 1;
|
|
956
|
+
}
|
|
957
|
+
const syncCode = await resolved.runInstall(defaultInstallOptions2());
|
|
958
|
+
if (syncCode !== 0) {
|
|
959
|
+
console.error(`lincoln install failed with exit code ${syncCode}`);
|
|
960
|
+
return 1;
|
|
961
|
+
}
|
|
962
|
+
try {
|
|
963
|
+
switchCurrentLink(resolved.paths, versionDir);
|
|
964
|
+
} catch (err) {
|
|
965
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
966
|
+
console.error(`Failed to switch ${resolved.paths.currentDir}: ${message}`);
|
|
967
|
+
return 1;
|
|
701
968
|
}
|
|
702
|
-
|
|
969
|
+
console.log(`Switched Lincoln to ${version}`);
|
|
970
|
+
return 0;
|
|
703
971
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
var execFileAsync = promisify(execFile);
|
|
707
|
-
async function listDevices(lincolnRecordPath = "lincoln-record") {
|
|
708
|
-
const { stdout } = await execFileAsync(lincolnRecordPath, ["devices"]);
|
|
709
|
-
return parseDevicesOutput(stdout);
|
|
972
|
+
function isValidVersion(version) {
|
|
973
|
+
return SEMVER_REGEX.test(version) || DIST_TAG_REGEX.test(version);
|
|
710
974
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
975
|
+
async function copyVersionFromNpmGlobal(deps, versionDir) {
|
|
976
|
+
const globalRoot = await deps.npmGlobalRoot();
|
|
977
|
+
if (!globalRoot) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
const packageRoot = path9.join(globalRoot, ...PACKAGE_NAME2.split("/"));
|
|
981
|
+
if (!fs8.existsSync(packageRoot)) {
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
fs8.mkdirSync(path9.dirname(versionDir), { recursive: true });
|
|
985
|
+
fs8.cpSync(packageRoot, versionDir, { recursive: true });
|
|
986
|
+
}
|
|
987
|
+
function switchCurrentLink(paths, versionDir) {
|
|
988
|
+
fs8.mkdirSync(paths.lincolnHome, { recursive: true });
|
|
989
|
+
try {
|
|
990
|
+
fs8.rmSync(paths.currentDir, { recursive: true, force: true });
|
|
991
|
+
fs8.symlinkSync(versionDir, paths.currentDir);
|
|
992
|
+
} catch {
|
|
993
|
+
fs8.rmSync(paths.currentDir, { recursive: true, force: true });
|
|
994
|
+
fs8.cpSync(versionDir, paths.currentDir, { recursive: true, force: true });
|
|
995
|
+
}
|
|
728
996
|
}
|
|
729
|
-
function
|
|
730
|
-
return
|
|
997
|
+
function defaultInstallOptions2() {
|
|
998
|
+
return { yes: true, dryRun: false, force: false, harnesses: [], noVenv: true };
|
|
731
999
|
}
|
|
732
|
-
function
|
|
733
|
-
return
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
1000
|
+
function runNpmInstallGlobal2(packageName, version) {
|
|
1001
|
+
return new Promise((resolvePromise) => {
|
|
1002
|
+
const child = spawn4("npm", ["install", "-g", `${packageName}@${version}`], {
|
|
1003
|
+
stdio: "inherit",
|
|
1004
|
+
shell: process.platform === "win32"
|
|
1005
|
+
});
|
|
1006
|
+
child.on("close", (code) => resolvePromise(code ?? 1));
|
|
1007
|
+
child.on("error", () => resolvePromise(1));
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
function runNpmRootGlobal() {
|
|
1011
|
+
return new Promise((resolvePromise) => {
|
|
1012
|
+
const child = spawn4("npm", ["root", "-g"], {
|
|
1013
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1014
|
+
shell: process.platform === "win32"
|
|
1015
|
+
});
|
|
1016
|
+
let stdout = "";
|
|
1017
|
+
child.stdout?.on("data", (chunk) => {
|
|
1018
|
+
stdout += chunk.toString();
|
|
1019
|
+
});
|
|
1020
|
+
child.on("close", (code) => {
|
|
1021
|
+
const root = stdout.trim();
|
|
1022
|
+
resolvePromise(code === 0 && root.length > 0 ? root : void 0);
|
|
1023
|
+
});
|
|
1024
|
+
child.on("error", () => resolvePromise(void 0));
|
|
1025
|
+
});
|
|
737
1026
|
}
|
|
738
1027
|
|
|
739
|
-
// src/
|
|
740
|
-
import
|
|
1028
|
+
// src/commands/migrateProject.ts
|
|
1029
|
+
import fs10 from "fs";
|
|
1030
|
+
import path11 from "path";
|
|
741
1031
|
|
|
742
|
-
// src/
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
1032
|
+
// src/lib/projectFiles.ts
|
|
1033
|
+
import fs9 from "fs";
|
|
1034
|
+
import path10 from "path";
|
|
1035
|
+
import crypto2 from "crypto";
|
|
1036
|
+
var FRAMEWORK_ALLOWLIST_DIRS = [".claude", ".claude-plugin", "scripts", "tools"];
|
|
1037
|
+
var FRAMEWORK_ALLOWLIST_FILES = [
|
|
1038
|
+
"README.md",
|
|
1039
|
+
"README.en.md",
|
|
1040
|
+
"USAGE.md",
|
|
1041
|
+
"CONTRIBUTING.md",
|
|
1042
|
+
"CLAUDE.md",
|
|
1043
|
+
"LICENSE",
|
|
1044
|
+
"RELEASE.md",
|
|
1045
|
+
".version-bump.json",
|
|
1046
|
+
"requirements.txt",
|
|
1047
|
+
"SKILL.md"
|
|
1048
|
+
];
|
|
1049
|
+
var FRAMEWORK_DENYLIST_NAMES = /* @__PURE__ */ new Set([
|
|
1050
|
+
".git",
|
|
1051
|
+
".context",
|
|
1052
|
+
".venv",
|
|
1053
|
+
"venv",
|
|
1054
|
+
"dist",
|
|
1055
|
+
"oss",
|
|
1056
|
+
".pytest_cache",
|
|
1057
|
+
"__pycache__",
|
|
1058
|
+
".DS_Store",
|
|
1059
|
+
"node_modules"
|
|
1060
|
+
]);
|
|
1061
|
+
var FRAMEWORK_DENYLIST_PREFIXES = ["issue-"];
|
|
1062
|
+
var FRAMEWORK_SKIP_PATHS = /* @__PURE__ */ new Set([
|
|
1063
|
+
".claude/templates/issue-package",
|
|
1064
|
+
"tools/lincoln-installer/dist",
|
|
1065
|
+
"tools/lincoln-installer/node_modules",
|
|
1066
|
+
"tools/lincoln-installer/coverage"
|
|
1067
|
+
]);
|
|
1068
|
+
function sha2562(filePath) {
|
|
1069
|
+
return crypto2.createHash("sha256").update(fs9.readFileSync(filePath)).digest("hex");
|
|
1070
|
+
}
|
|
1071
|
+
function isDenied(relativePath) {
|
|
1072
|
+
const parts = relativePath.split(path10.sep);
|
|
1073
|
+
for (const part of parts) {
|
|
1074
|
+
if (FRAMEWORK_DENYLIST_NAMES.has(part)) return true;
|
|
1075
|
+
for (const prefix of FRAMEWORK_DENYLIST_PREFIXES) {
|
|
1076
|
+
if (part.startsWith(prefix)) return true;
|
|
1077
|
+
}
|
|
753
1078
|
}
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
return {
|
|
757
|
-
downloaded: Number(withoutTotal[1]),
|
|
758
|
-
total: null,
|
|
759
|
-
percent: null
|
|
760
|
-
};
|
|
1079
|
+
for (const skip of FRAMEWORK_SKIP_PATHS) {
|
|
1080
|
+
if (relativePath === skip || relativePath.startsWith(skip + path10.sep)) return true;
|
|
761
1081
|
}
|
|
762
|
-
return
|
|
1082
|
+
return false;
|
|
763
1083
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
return
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
1084
|
+
function isAllowlisted(relativePath) {
|
|
1085
|
+
if (FRAMEWORK_ALLOWLIST_FILES.includes(relativePath)) return true;
|
|
1086
|
+
for (const dir of FRAMEWORK_ALLOWLIST_DIRS) {
|
|
1087
|
+
if (relativePath === dir || relativePath.startsWith(dir + path10.sep)) return true;
|
|
1088
|
+
}
|
|
1089
|
+
return false;
|
|
1090
|
+
}
|
|
1091
|
+
function isPreservedDir(relativePath) {
|
|
1092
|
+
const normalized = relativePath.replace(/\\/g, "/");
|
|
1093
|
+
if (normalized === ".context" || normalized.startsWith(".context/")) return true;
|
|
1094
|
+
const parts = normalized.split("/");
|
|
1095
|
+
for (const part of parts) {
|
|
1096
|
+
if (part.startsWith("issue-")) return true;
|
|
1097
|
+
}
|
|
1098
|
+
return false;
|
|
1099
|
+
}
|
|
1100
|
+
function isPreserved(relativePath) {
|
|
1101
|
+
const normalized = relativePath.replace(/\\/g, "/");
|
|
1102
|
+
if (normalized === ".github/openspec-config.yml") return true;
|
|
1103
|
+
return isPreservedDir(relativePath);
|
|
1104
|
+
}
|
|
1105
|
+
function listVendoredFrameworkFiles(payloadRoot, projectRoot) {
|
|
1106
|
+
const report = { removable: [], modified: [], preserved: [] };
|
|
1107
|
+
function walk(dir) {
|
|
1108
|
+
const entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
1109
|
+
for (const entry of entries) {
|
|
1110
|
+
const fullPath = path10.join(dir, entry.name);
|
|
1111
|
+
const relativePath = path10.relative(projectRoot, fullPath);
|
|
1112
|
+
if (entry.isDirectory()) {
|
|
1113
|
+
if (isPreservedDir(relativePath)) {
|
|
1114
|
+
walk(fullPath);
|
|
1115
|
+
} else if (!isDenied(relativePath)) {
|
|
1116
|
+
walk(fullPath);
|
|
1117
|
+
}
|
|
1118
|
+
continue;
|
|
778
1119
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
if (progress) {
|
|
783
|
-
onProgress?.(progress);
|
|
1120
|
+
if (isPreserved(relativePath)) {
|
|
1121
|
+
report.preserved.push(relativePath);
|
|
1122
|
+
continue;
|
|
784
1123
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
if (
|
|
789
|
-
|
|
1124
|
+
if (isDenied(relativePath)) continue;
|
|
1125
|
+
if (!isAllowlisted(relativePath)) continue;
|
|
1126
|
+
const payloadFile = path10.join(payloadRoot, relativePath);
|
|
1127
|
+
if (!fs9.existsSync(payloadFile)) {
|
|
1128
|
+
report.modified.push(relativePath);
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
if (sha2562(fullPath) === sha2562(payloadFile)) {
|
|
1132
|
+
report.removable.push(relativePath);
|
|
790
1133
|
} else {
|
|
791
|
-
|
|
1134
|
+
report.modified.push(relativePath);
|
|
792
1135
|
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
if (fs9.existsSync(projectRoot)) {
|
|
1139
|
+
walk(projectRoot);
|
|
1140
|
+
}
|
|
1141
|
+
return report;
|
|
795
1142
|
}
|
|
796
1143
|
|
|
797
|
-
// src/
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
const [model, setModel] = useState5(null);
|
|
819
|
-
const addLog = useCallback2((message, type = "info") => {
|
|
820
|
-
setLogs((prev) => [...prev, { id: createLogId(), message, type }]);
|
|
821
|
-
}, []);
|
|
822
|
-
const { state, start, stop, cancel } = useRecorder({
|
|
823
|
-
workspaceRoot,
|
|
824
|
-
sessionId,
|
|
825
|
-
lincolnRecordPath,
|
|
826
|
-
mic,
|
|
827
|
-
model,
|
|
828
|
-
startOnMount: false
|
|
829
|
-
});
|
|
830
|
-
useEffect4(() => {
|
|
831
|
-
switch (state.status) {
|
|
832
|
-
case "recording":
|
|
833
|
-
addLog("Recording started.", "info");
|
|
834
|
-
break;
|
|
835
|
-
case "stopped":
|
|
836
|
-
addLog(`Saved to ${workspaceRoot}/${sessionId}/audio.wav`, "success");
|
|
837
|
-
addLog(`Run: claude process-interview ${sessionId}`, "command");
|
|
838
|
-
break;
|
|
839
|
-
case "cancelled":
|
|
840
|
-
addLog("Recording cancelled. No files were saved.", "error");
|
|
841
|
-
break;
|
|
842
|
-
case "error":
|
|
843
|
-
addLog(`Recording error: ${state.errorMessage ?? "unknown error"}`, "error");
|
|
844
|
-
break;
|
|
845
|
-
}
|
|
846
|
-
}, [state.status, state.errorMessage, addLog, workspaceRoot, sessionId]);
|
|
847
|
-
const handleQuit = useCallback2(() => {
|
|
848
|
-
exit();
|
|
849
|
-
}, [exit]);
|
|
850
|
-
const handleRecord = useCallback2(() => {
|
|
851
|
-
if (state.status === "idle") {
|
|
852
|
-
start();
|
|
853
|
-
}
|
|
854
|
-
}, [state.status, start]);
|
|
855
|
-
const handleStop = useCallback2(() => {
|
|
856
|
-
if (state.status === "recording") {
|
|
857
|
-
stop().catch(() => {
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
}, [state.status, stop]);
|
|
861
|
-
const handleCancel = useCallback2(() => {
|
|
862
|
-
if (state.status === "recording") {
|
|
863
|
-
cancel().catch(() => {
|
|
864
|
-
});
|
|
1144
|
+
// src/commands/migrateProject.ts
|
|
1145
|
+
function createDefaultDeps5() {
|
|
1146
|
+
return {
|
|
1147
|
+
paths: resolveLincolnPaths(),
|
|
1148
|
+
listVendoredFrameworkFiles,
|
|
1149
|
+
writeProjectMarker
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
async function migrateProject(projectRoot, options = {}, deps = createDefaultDeps5()) {
|
|
1153
|
+
const payloadRoot = deps.paths.currentDir;
|
|
1154
|
+
if (!fs10.existsSync(payloadRoot)) {
|
|
1155
|
+
console.error(`Lincoln payload not found at ${payloadRoot}. Run "lincoln install" first.`);
|
|
1156
|
+
return 1;
|
|
1157
|
+
}
|
|
1158
|
+
const absoluteProjectRoot = path11.resolve(projectRoot);
|
|
1159
|
+
const report = deps.listVendoredFrameworkFiles(payloadRoot, absoluteProjectRoot);
|
|
1160
|
+
if (options.dryRun) {
|
|
1161
|
+
console.log(`Dry run: would migrate ${absoluteProjectRoot}`);
|
|
1162
|
+
if (report.removable.length > 0) {
|
|
1163
|
+
console.log("Would remove unmodified framework files:");
|
|
1164
|
+
for (const f of report.removable) console.log(` - ${f}`);
|
|
865
1165
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
setMode("devices");
|
|
1166
|
+
if (report.modified.length > 0) {
|
|
1167
|
+
console.log("Would preserve modified framework files:");
|
|
1168
|
+
for (const f of report.modified) console.log(` - ${f}`);
|
|
870
1169
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
setMode("model");
|
|
1170
|
+
if (report.preserved.length > 0) {
|
|
1171
|
+
console.log("Would preserve project-local files:");
|
|
1172
|
+
for (const f of report.preserved) console.log(` - ${f}`);
|
|
875
1173
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
const isTerminal = state.status === "stopped" || state.status === "cancelled" || state.status === "error";
|
|
897
|
-
useKeyHandler({
|
|
898
|
-
enabled: mode === "main",
|
|
899
|
-
onRecord: handleRecord,
|
|
900
|
-
onStop: handleStop,
|
|
901
|
-
onCancel: handleCancel,
|
|
902
|
-
onQuit: handleQuit,
|
|
903
|
-
onDevices: handleDevices,
|
|
904
|
-
onModel: handleModel,
|
|
905
|
-
onAnyKey: isTerminal ? handleQuit : void 0
|
|
906
|
-
});
|
|
907
|
-
const hintMode = useMemo(() => {
|
|
908
|
-
if (mode !== "main") return "menu";
|
|
909
|
-
if (state.status === "idle") return "idle";
|
|
910
|
-
if (state.status === "recording") return "recording";
|
|
911
|
-
if (state.status === "stopped") return "stopped";
|
|
912
|
-
if (state.status === "cancelled") return "cancelled";
|
|
913
|
-
return "error";
|
|
914
|
-
}, [mode, state.status]);
|
|
915
|
-
const devicesLoader = useCallback2(
|
|
916
|
-
() => (listDevicesFn ?? (() => listDevices(lincolnRecordPath)))(),
|
|
917
|
-
[listDevicesFn, lincolnRecordPath]
|
|
918
|
-
);
|
|
919
|
-
const modelsLoader = useCallback2(
|
|
920
|
-
() => (listModelStatusesFn ?? (() => Promise.resolve(listModelStatuses(defaultModelCacheDir()))))(),
|
|
921
|
-
[listModelStatusesFn]
|
|
922
|
-
);
|
|
923
|
-
const warmupRunner = useCallback2(
|
|
924
|
-
(name, onProgress) => runWarmupFn ? runWarmupFn(name, onProgress) : runWarmup({ model: name, lincolnRecordPath, onProgress }),
|
|
925
|
-
[runWarmupFn, lincolnRecordPath]
|
|
926
|
-
);
|
|
927
|
-
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "gray", height: 24, children: [
|
|
928
|
-
/* @__PURE__ */ jsx7(
|
|
929
|
-
StatusPanel,
|
|
930
|
-
{
|
|
931
|
-
sessionId,
|
|
932
|
-
topic,
|
|
933
|
-
designId,
|
|
934
|
-
branch,
|
|
935
|
-
status: state.status,
|
|
936
|
-
duration: state.duration
|
|
937
|
-
}
|
|
938
|
-
),
|
|
939
|
-
mode === "devices" && /* @__PURE__ */ jsx7(
|
|
940
|
-
DevicesMenu,
|
|
941
|
-
{
|
|
942
|
-
listDevicesFn: devicesLoader,
|
|
943
|
-
onSelect: handleDeviceSelect,
|
|
944
|
-
onClose: handleMenuClose
|
|
1174
|
+
console.log("Would create .lincoln.yaml");
|
|
1175
|
+
return 0;
|
|
1176
|
+
}
|
|
1177
|
+
if (!options.yes) {
|
|
1178
|
+
console.error("This will remove vendored framework files. Run with --yes to proceed.");
|
|
1179
|
+
return 1;
|
|
1180
|
+
}
|
|
1181
|
+
for (const file of report.removable) {
|
|
1182
|
+
const filePath = path11.join(absoluteProjectRoot, file);
|
|
1183
|
+
try {
|
|
1184
|
+
fs10.rmSync(filePath);
|
|
1185
|
+
let dir = path11.dirname(filePath);
|
|
1186
|
+
while (dir !== absoluteProjectRoot && fs10.existsSync(dir)) {
|
|
1187
|
+
const entries = fs10.readdirSync(dir);
|
|
1188
|
+
if (entries.length === 0) {
|
|
1189
|
+
fs10.rmdirSync(dir);
|
|
1190
|
+
dir = path11.dirname(dir);
|
|
1191
|
+
} else {
|
|
1192
|
+
break;
|
|
1193
|
+
}
|
|
945
1194
|
}
|
|
946
|
-
)
|
|
947
|
-
|
|
948
|
-
|
|
1195
|
+
} catch (err) {
|
|
1196
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1197
|
+
console.error(`Failed to remove ${file}: ${message}`);
|
|
1198
|
+
return 1;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
if (report.modified.length > 0) {
|
|
1202
|
+
console.log("Preserved modified framework files:");
|
|
1203
|
+
for (const f of report.modified) console.log(` - ${f}`);
|
|
1204
|
+
}
|
|
1205
|
+
if (report.preserved.length > 0) {
|
|
1206
|
+
console.log("Preserved project-local files:");
|
|
1207
|
+
for (const f of report.preserved) console.log(` - ${f}`);
|
|
1208
|
+
}
|
|
1209
|
+
try {
|
|
1210
|
+
deps.writeProjectMarker(
|
|
1211
|
+
absoluteProjectRoot,
|
|
949
1212
|
{
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
}
|
|
955
|
-
)
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1213
|
+
enabled: true,
|
|
1214
|
+
workflow_template: "interview-to-knowledge",
|
|
1215
|
+
min_lincoln_version: readLocalPackageVersion() ?? "1.5.0"
|
|
1216
|
+
},
|
|
1217
|
+
{ force: options.force }
|
|
1218
|
+
);
|
|
1219
|
+
} catch (err) {
|
|
1220
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1221
|
+
console.error(`Failed to write .lincoln.yaml: ${message}`);
|
|
1222
|
+
return 1;
|
|
1223
|
+
}
|
|
1224
|
+
console.log(`Migrated project at ${absoluteProjectRoot}`);
|
|
1225
|
+
console.log(`Removed ${report.removable.length} unmodified framework files`);
|
|
1226
|
+
return 0;
|
|
960
1227
|
}
|
|
961
1228
|
|
|
962
|
-
// src/
|
|
963
|
-
import {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
/* @__PURE__ */ jsx8(Text7, { bold: true, children: "lincoln" }),
|
|
968
|
-
/* @__PURE__ */ jsx8(Text7, { children: "Cross-tool TUI for Lincoln interview recording." }),
|
|
969
|
-
/* @__PURE__ */ jsx8(Text7, {}),
|
|
970
|
-
/* @__PURE__ */ jsx8(Text7, { bold: true, children: "Usage:" }),
|
|
971
|
-
/* @__PURE__ */ jsx8(Text7, { children: " lincoln [options]" }),
|
|
972
|
-
/* @__PURE__ */ jsx8(Text7, {}),
|
|
973
|
-
/* @__PURE__ */ jsx8(Text7, { bold: true, children: "Options:" }),
|
|
974
|
-
/* @__PURE__ */ jsx8(Text7, { children: " --topic Interview topic" }),
|
|
975
|
-
/* @__PURE__ */ jsx8(Text7, { children: " --design-id Design ID for the session" }),
|
|
976
|
-
/* @__PURE__ */ jsx8(Text7, { children: " --branch Current branch name" }),
|
|
977
|
-
/* @__PURE__ */ jsx8(Text7, { children: " --session-id Explicit session ID" }),
|
|
978
|
-
/* @__PURE__ */ jsx8(Text7, { children: " --no-tui Run without the terminal UI" }),
|
|
979
|
-
/* @__PURE__ */ jsx8(Text7, { children: " --help, -h Show this help message" }),
|
|
980
|
-
/* @__PURE__ */ jsx8(Text7, {}),
|
|
981
|
-
/* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "Configuration files: ~/.lincolnrc, ./.lincolnrc" })
|
|
982
|
-
] });
|
|
983
|
-
}
|
|
984
|
-
return /* @__PURE__ */ jsx8(
|
|
985
|
-
RecordingApp,
|
|
986
|
-
{
|
|
987
|
-
workspaceRoot: config.workspaceRoot,
|
|
988
|
-
sessionId: config.sessionId,
|
|
989
|
-
topic: config.topic,
|
|
990
|
-
designId: config.designId,
|
|
991
|
-
branch: config.branch,
|
|
992
|
-
audioMeterStyle: config.audioMeterStyle
|
|
993
|
-
}
|
|
994
|
-
);
|
|
1229
|
+
// src/commands/record.ts
|
|
1230
|
+
import { spawn as spawn5 } from "child_process";
|
|
1231
|
+
import { EventEmitter } from "events";
|
|
1232
|
+
function createDefaultDeps6() {
|
|
1233
|
+
return { spawn: spawn5 };
|
|
995
1234
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1235
|
+
function record(args, deps = createDefaultDeps6()) {
|
|
1236
|
+
return new Promise((resolve) => {
|
|
1237
|
+
const child = deps.spawn("lincoln-record", args, { stdio: "inherit" });
|
|
1238
|
+
child.on("error", (err) => {
|
|
1239
|
+
console.error(`Failed to start lincoln-record: ${err.message}`);
|
|
1240
|
+
resolve(1);
|
|
1241
|
+
});
|
|
1242
|
+
child.on("close", (code) => {
|
|
1243
|
+
resolve(code ?? 0);
|
|
1244
|
+
});
|
|
1245
|
+
});
|
|
1005
1246
|
}
|
|
1006
1247
|
|
|
1007
|
-
// src/main.
|
|
1008
|
-
|
|
1248
|
+
// src/main.ts
|
|
1249
|
+
var USAGE = `Lincoln \u2014 Global agent harness plugin CLI
|
|
1250
|
+
|
|
1251
|
+
Usage:
|
|
1252
|
+
lincoln <command> [options]
|
|
1253
|
+
|
|
1254
|
+
Commands:
|
|
1255
|
+
install One-time global setup: sync to ~/.claude/, ~/.codex/, ~/.opencode/
|
|
1256
|
+
update Pull latest release and re-sync globally
|
|
1257
|
+
use <version> Switch active global version
|
|
1258
|
+
doctor Diagnose harness integration
|
|
1259
|
+
init-project Create .lincoln.yaml in current project
|
|
1260
|
+
migrate-project Remove vendored framework files from an existing project
|
|
1261
|
+
record Launch the Lincoln interview recorder TUI
|
|
1262
|
+
help Show this help message
|
|
1263
|
+
|
|
1264
|
+
Options:
|
|
1265
|
+
--yes, -y Skip confirmation prompts
|
|
1266
|
+
--dry-run Show what would be done without making changes
|
|
1267
|
+
--force Force re-install even if up to date
|
|
1268
|
+
--check Check for available updates without installing (update)
|
|
1269
|
+
--json Output machine-readable JSON (where supported)
|
|
1270
|
+
--help, -h Show this help message
|
|
1271
|
+
`;
|
|
1272
|
+
async function main(argv) {
|
|
1273
|
+
const parsed = parseArgs(argv);
|
|
1274
|
+
if (parsed.command === "help") {
|
|
1275
|
+
console.log(USAGE);
|
|
1276
|
+
return 0;
|
|
1277
|
+
}
|
|
1278
|
+
if (parsed.command === "install") {
|
|
1279
|
+
return install({
|
|
1280
|
+
yes: Boolean(parsed.flags.yes || parsed.flags.y),
|
|
1281
|
+
dryRun: Boolean(parsed.flags["dry-run"]),
|
|
1282
|
+
force: Boolean(parsed.flags.force),
|
|
1283
|
+
harnesses: parsed.args,
|
|
1284
|
+
noVenv: Boolean(parsed.flags["no-venv"])
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
if (parsed.command === "update") {
|
|
1288
|
+
return update({
|
|
1289
|
+
check: Boolean(parsed.flags.check),
|
|
1290
|
+
dryRun: Boolean(parsed.flags["dry-run"]),
|
|
1291
|
+
yes: Boolean(parsed.flags.yes || parsed.flags.y)
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
if (parsed.command === "use") {
|
|
1295
|
+
const version = parsed.args[0];
|
|
1296
|
+
if (!version) {
|
|
1297
|
+
console.error("Usage: lincoln use <version> [--yes] [--dry-run]");
|
|
1298
|
+
return 1;
|
|
1299
|
+
}
|
|
1300
|
+
return use(version, {
|
|
1301
|
+
dryRun: Boolean(parsed.flags["dry-run"]),
|
|
1302
|
+
yes: Boolean(parsed.flags.yes || parsed.flags.y)
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
if (parsed.command === "doctor") {
|
|
1306
|
+
const result = await doctor({ json: Boolean(parsed.flags.json) });
|
|
1307
|
+
return result.code;
|
|
1308
|
+
}
|
|
1309
|
+
if (parsed.command === "init-project") {
|
|
1310
|
+
return initProject(process.cwd(), {
|
|
1311
|
+
force: Boolean(parsed.flags.force),
|
|
1312
|
+
dryRun: Boolean(parsed.flags["dry-run"])
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
if (parsed.command === "migrate-project") {
|
|
1316
|
+
return migrateProject(process.cwd(), {
|
|
1317
|
+
dryRun: Boolean(parsed.flags["dry-run"]),
|
|
1318
|
+
yes: Boolean(parsed.flags.yes || parsed.flags.y),
|
|
1319
|
+
force: Boolean(parsed.flags.force)
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
if (parsed.command === "record") {
|
|
1323
|
+
return record(parsed.args);
|
|
1324
|
+
}
|
|
1325
|
+
console.log(USAGE);
|
|
1326
|
+
return 0;
|
|
1327
|
+
}
|
|
1328
|
+
if (process.argv[1] && path12.resolve(fs11.realpathSync(fileURLToPath2(import.meta.url))) === path12.resolve(fs11.realpathSync(process.argv[1]))) {
|
|
1329
|
+
main(process.argv).then((code) => process.exit(code));
|
|
1330
|
+
}
|
|
1331
|
+
export {
|
|
1332
|
+
main
|
|
1333
|
+
};
|
|
1009
1334
|
//# sourceMappingURL=main.js.map
|