@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
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Scan a codebase and emit a machine-readable feature map."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
DEFAULT_SOURCE_DIRS = ("products", "src", "lib", "app", "packages", "server", "client")
|
|
13
|
+
IGNORE_DIRS = {".git", "node_modules", ".venv", "venv", "__pycache__", "dist", "build"}
|
|
14
|
+
SOURCE_EXTENSIONS = {".py", ".ts", ".js", ".tsx", ".go", ".rs"}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def find_source_root(root: Path) -> Path | None:
|
|
18
|
+
for candidate in DEFAULT_SOURCE_DIRS:
|
|
19
|
+
path = root / candidate
|
|
20
|
+
if path.is_dir():
|
|
21
|
+
return path
|
|
22
|
+
if any(
|
|
23
|
+
p.suffix in SOURCE_EXTENSIONS for p in root.iterdir() if p.is_file()
|
|
24
|
+
):
|
|
25
|
+
return root
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def scan_features(root: Path, max_features: int = 10) -> dict[str, Any]:
|
|
30
|
+
root = root.resolve()
|
|
31
|
+
source_root = find_source_root(root)
|
|
32
|
+
features: list[dict[str, Any]] = []
|
|
33
|
+
|
|
34
|
+
if source_root:
|
|
35
|
+
for child in sorted(source_root.iterdir()):
|
|
36
|
+
if not child.is_dir() or child.name in IGNORE_DIRS:
|
|
37
|
+
continue
|
|
38
|
+
scan_root = child
|
|
39
|
+
if source_root.name == "products":
|
|
40
|
+
nested_source = find_source_root(child)
|
|
41
|
+
if nested_source:
|
|
42
|
+
scan_root = nested_source
|
|
43
|
+
entry_points = sorted(
|
|
44
|
+
p.relative_to(root)
|
|
45
|
+
for p in scan_root.rglob("*")
|
|
46
|
+
if p.is_file() and p.suffix in SOURCE_EXTENSIONS
|
|
47
|
+
)[:5]
|
|
48
|
+
features.append(
|
|
49
|
+
{
|
|
50
|
+
"name": child.name,
|
|
51
|
+
"path": str(child.relative_to(root)),
|
|
52
|
+
"entry_points": [str(ep) for ep in entry_points],
|
|
53
|
+
"has_tests": (root / "tests" / child.name).exists(),
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
if len(features) >= max_features:
|
|
57
|
+
break
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
"root": str(root),
|
|
61
|
+
"source_root": str(source_root) if source_root else None,
|
|
62
|
+
"features": features,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def main() -> None:
|
|
67
|
+
parser = argparse.ArgumentParser(description="Build a feature map from an existing codebase.")
|
|
68
|
+
parser.add_argument("--root", default=".", help="Project root")
|
|
69
|
+
parser.add_argument("--max-features", type=int, default=10, help="Maximum features to emit")
|
|
70
|
+
args = parser.parse_args()
|
|
71
|
+
result = scan_features(Path(args.root), max_features=args.max_features)
|
|
72
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
main()
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""版本锁步工具(#68:借鉴 superpowers 的 plugin/marketplace 版本漂移教训)。
|
|
3
|
+
|
|
4
|
+
`.version-bump.json` 是版本号唯一事实源,用 RFC 6901 JSON Pointer 列出所有
|
|
5
|
+
携带版本号的 manifest 位置。用法:
|
|
6
|
+
|
|
7
|
+
python3 scripts/bump_version.py bump 1.3.0 # 锁步更新事实源与所有 manifest
|
|
8
|
+
python3 scripts/bump_version.py --check # CI:验证所有 manifest 与事实源一致
|
|
9
|
+
python3 scripts/bump_version.py --audit 1.2.0 # 发布前:列出残留的旧版本引用
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import re
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
21
|
+
SEMVER = re.compile(r"^\d+\.\d+\.\d+$")
|
|
22
|
+
|
|
23
|
+
# audit 模式中合理保留旧版本号的位置(历史记录,不是漂移);
|
|
24
|
+
# package-lock 内依赖自身版本号易误报,其产品版本由 bump 按 pointer 精确维护
|
|
25
|
+
AUDIT_EXCLUDES = (
|
|
26
|
+
":!RELEASE.md",
|
|
27
|
+
":!knowledge",
|
|
28
|
+
":!issue-*",
|
|
29
|
+
":!.context",
|
|
30
|
+
":!tools/lincoln/package-lock.json",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def load_source(root: Path) -> dict:
|
|
35
|
+
source = root / ".version-bump.json"
|
|
36
|
+
if not source.exists():
|
|
37
|
+
sys.exit("missing version source: .version-bump.json")
|
|
38
|
+
data = json.loads(source.read_text(encoding="utf-8"))
|
|
39
|
+
if not SEMVER.match(data.get("version", "")):
|
|
40
|
+
sys.exit(f".version-bump.json has invalid version: {data.get('version')!r}")
|
|
41
|
+
return data
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def parse_pointer(pointer: str) -> list[str]:
|
|
45
|
+
if not pointer.startswith("/"):
|
|
46
|
+
raise ValueError(f"pointer must start with '/': {pointer!r}")
|
|
47
|
+
return [t.replace("~1", "/").replace("~0", "~") for t in pointer.split("/")[1:]]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def resolve(container, tokens: list[str]):
|
|
51
|
+
"""沿 pointer tokens 走到末段的父容器,返回 (父容器, 末段 key)。"""
|
|
52
|
+
node = container
|
|
53
|
+
for token in tokens[:-1]:
|
|
54
|
+
node = node[int(token)] if isinstance(node, list) else node[token]
|
|
55
|
+
last = tokens[-1]
|
|
56
|
+
return node, int(last) if isinstance(node, list) else last
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def iter_versions(data: dict, root: Path):
|
|
60
|
+
"""yield (manifest_path, pointer, current_value);manifest 缺失即失败。"""
|
|
61
|
+
for manifest in data["manifests"]:
|
|
62
|
+
path = root / manifest["path"]
|
|
63
|
+
if not path.exists():
|
|
64
|
+
sys.exit(f"manifest listed in .version-bump.json is missing: {manifest['path']}")
|
|
65
|
+
doc = json.loads(path.read_text(encoding="utf-8"))
|
|
66
|
+
for pointer in manifest["pointers"]:
|
|
67
|
+
parent, key = resolve(doc, parse_pointer(pointer))
|
|
68
|
+
yield manifest["path"], pointer, parent[key]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def cmd_check(data: dict, root: Path) -> int:
|
|
72
|
+
expected = data["version"]
|
|
73
|
+
drift = [
|
|
74
|
+
f" {path}{pointer}: {value!r} != {expected!r}"
|
|
75
|
+
for path, pointer, value in iter_versions(data, root)
|
|
76
|
+
if value != expected
|
|
77
|
+
]
|
|
78
|
+
if drift:
|
|
79
|
+
print("version drift detected:", file=sys.stderr)
|
|
80
|
+
print("\n".join(drift), file=sys.stderr)
|
|
81
|
+
print(f"fix: python3 scripts/bump_version.py bump {expected}", file=sys.stderr)
|
|
82
|
+
return 1
|
|
83
|
+
print(f"version lockstep OK: {expected} ({len(data['manifests'])} manifests)")
|
|
84
|
+
return 0
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def cmd_bump(data: dict, root: Path, new_version: str, dry_run: bool = False) -> int:
|
|
88
|
+
if not SEMVER.match(new_version):
|
|
89
|
+
sys.exit(f"invalid semver (want X.Y.Z): {new_version!r}")
|
|
90
|
+
old_version = data["version"]
|
|
91
|
+
for manifest in data["manifests"]:
|
|
92
|
+
path = root / manifest["path"]
|
|
93
|
+
doc = json.loads(path.read_text(encoding="utf-8"))
|
|
94
|
+
for pointer in manifest["pointers"]:
|
|
95
|
+
parent, key = resolve(doc, parse_pointer(pointer))
|
|
96
|
+
parent[key] = new_version
|
|
97
|
+
if dry_run:
|
|
98
|
+
print(f" would bump {manifest['path']}")
|
|
99
|
+
else:
|
|
100
|
+
path.write_text(
|
|
101
|
+
json.dumps(doc, indent=2, ensure_ascii=False) + "\n", encoding="utf-8"
|
|
102
|
+
)
|
|
103
|
+
print(f" bumped {manifest['path']}")
|
|
104
|
+
data["version"] = new_version
|
|
105
|
+
if dry_run:
|
|
106
|
+
print(f"would bump .version-bump.json {old_version} -> {new_version}")
|
|
107
|
+
else:
|
|
108
|
+
(root / ".version-bump.json").write_text(
|
|
109
|
+
json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8"
|
|
110
|
+
)
|
|
111
|
+
print(f"bumped {old_version} -> {new_version}")
|
|
112
|
+
|
|
113
|
+
# Keep harness artifacts in lockstep with the bumped version.
|
|
114
|
+
adapter = root / "scripts" / "lincoln_harness_adapter.py"
|
|
115
|
+
if adapter.exists():
|
|
116
|
+
for harness in ("codex", "opencode"):
|
|
117
|
+
artifact = root / ".claude" / "harnesses" / f"{harness}.yaml"
|
|
118
|
+
if not artifact.exists():
|
|
119
|
+
continue
|
|
120
|
+
if dry_run:
|
|
121
|
+
print(f" would regenerate harness {harness}.yaml")
|
|
122
|
+
continue
|
|
123
|
+
result = subprocess.run(
|
|
124
|
+
[sys.executable, str(adapter), "--harness", harness],
|
|
125
|
+
cwd=root,
|
|
126
|
+
capture_output=True,
|
|
127
|
+
text=True,
|
|
128
|
+
)
|
|
129
|
+
if result.returncode != 0:
|
|
130
|
+
print(result.stderr, file=sys.stderr)
|
|
131
|
+
sys.exit(f"harness generation failed for {harness}")
|
|
132
|
+
print(f" regenerated harness {harness}.yaml")
|
|
133
|
+
|
|
134
|
+
if dry_run:
|
|
135
|
+
print(f"next (dry run complete): review changes, then run bump {new_version}")
|
|
136
|
+
else:
|
|
137
|
+
print(f"next: python3 scripts/bump_version.py --audit {old_version}")
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def cmd_audit(root: Path, old_version: str) -> int:
|
|
142
|
+
if not SEMVER.match(old_version):
|
|
143
|
+
sys.exit(f"invalid semver (want X.Y.Z): {old_version!r}")
|
|
144
|
+
result = subprocess.run(
|
|
145
|
+
["git", "grep", "-n", "-F", old_version, "--", ".", *AUDIT_EXCLUDES],
|
|
146
|
+
cwd=root,
|
|
147
|
+
capture_output=True,
|
|
148
|
+
text=True,
|
|
149
|
+
)
|
|
150
|
+
if result.returncode not in (0, 1):
|
|
151
|
+
sys.exit(f"git grep failed: {result.stderr.strip()}")
|
|
152
|
+
if result.returncode == 1:
|
|
153
|
+
print(f"audit OK: no stale {old_version} references")
|
|
154
|
+
return 0
|
|
155
|
+
print(f"stale {old_version} references (review each; RELEASE.md/knowledge 等历史记录已排除):")
|
|
156
|
+
print(result.stdout, end="")
|
|
157
|
+
return 1
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def main(argv: list[str], root: Path = ROOT) -> int:
|
|
161
|
+
raw_args = argv[1:]
|
|
162
|
+
dry_run = "--dry-run" in raw_args
|
|
163
|
+
args = [a for a in raw_args if a != "--dry-run"]
|
|
164
|
+
|
|
165
|
+
if dry_run and not (len(args) == 2 and args[0] == "bump"):
|
|
166
|
+
print("--dry-run can only be used with 'bump X.Y.Z'", file=sys.stderr)
|
|
167
|
+
print(__doc__, file=sys.stderr)
|
|
168
|
+
return 2
|
|
169
|
+
|
|
170
|
+
if args == ["--check"]:
|
|
171
|
+
return cmd_check(load_source(root), root)
|
|
172
|
+
if len(args) == 2 and args[0] == "bump":
|
|
173
|
+
return cmd_bump(load_source(root), root, args[1], dry_run=dry_run)
|
|
174
|
+
if len(args) == 2 and args[0] == "--audit":
|
|
175
|
+
return cmd_audit(root, args[1])
|
|
176
|
+
print(__doc__, file=sys.stderr)
|
|
177
|
+
return 2
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
if __name__ == "__main__":
|
|
181
|
+
sys.exit(main(sys.argv))
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Check upstream drift for pinned external skill dependencies (#62).
|
|
3
|
+
|
|
4
|
+
Compares each git-sourced skill/plugin's pinned `ref` in
|
|
5
|
+
`.claude/skills/dependencies.yaml` against the current upstream branch head
|
|
6
|
+
(`pinned_from`, default `main`). Prints a report; exits 1 when any dependency
|
|
7
|
+
has drifted (i.e. an upgrade review is due), 0 when all pins are current.
|
|
8
|
+
|
|
9
|
+
This tool needs network access and is run manually (or from scheduled jobs),
|
|
10
|
+
never from the offline test suite.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
import yaml
|
|
20
|
+
|
|
21
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
22
|
+
DEPENDENCIES = ROOT / ".claude" / "skills" / "dependencies.yaml"
|
|
23
|
+
GIT_SOURCE_PREFIXES = ("http://", "https://", "git@")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def upstream_head(source: str, branch: str) -> str | None:
|
|
27
|
+
proc = subprocess.run(
|
|
28
|
+
["git", "ls-remote", source, branch],
|
|
29
|
+
capture_output=True,
|
|
30
|
+
text=True,
|
|
31
|
+
timeout=60,
|
|
32
|
+
)
|
|
33
|
+
if proc.returncode != 0 or not proc.stdout.strip():
|
|
34
|
+
return None
|
|
35
|
+
return proc.stdout.split()[0]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main() -> int:
|
|
39
|
+
data = yaml.safe_load(DEPENDENCIES.read_text(encoding="utf-8"))
|
|
40
|
+
drifted = False
|
|
41
|
+
for name, dep in data.get("skills", {}).items():
|
|
42
|
+
if dep.get("type") not in ("skill", "plugin"):
|
|
43
|
+
continue
|
|
44
|
+
source = str(dep.get("source", ""))
|
|
45
|
+
if not source.startswith(GIT_SOURCE_PREFIXES):
|
|
46
|
+
continue
|
|
47
|
+
ref = str(dep.get("ref", ""))
|
|
48
|
+
branch = str(dep.get("pinned_from", "main"))
|
|
49
|
+
try:
|
|
50
|
+
head = upstream_head(source, branch)
|
|
51
|
+
except (subprocess.TimeoutExpired, OSError) as exc:
|
|
52
|
+
print(f"WARN {name}: upstream check failed ({exc}); skipped")
|
|
53
|
+
continue
|
|
54
|
+
if head is None:
|
|
55
|
+
print(f"WARN {name}: cannot resolve {branch} at {source}; skipped")
|
|
56
|
+
continue
|
|
57
|
+
if head == ref:
|
|
58
|
+
print(f"OK {name}: pinned at {ref[:12]} (current {branch})")
|
|
59
|
+
else:
|
|
60
|
+
drifted = True
|
|
61
|
+
print(
|
|
62
|
+
f"DRIFT {name}: pinned {ref[:12]}, upstream {branch} is {head[:12]}"
|
|
63
|
+
" — review upstream changes, run the benchmark, then update the pin"
|
|
64
|
+
)
|
|
65
|
+
return 1 if drifted else 0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
sys.exit(main())
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Harness drift check (CI-friendly, lightweight):
|
|
3
|
+
# 1. Regenerate codex/opencode artifacts into a temp dir — proves manifests
|
|
4
|
+
# load and generation succeeds (catches broken sources / bad manifests).
|
|
5
|
+
# 2. If generated artifacts already exist in the project (someone ran
|
|
6
|
+
# generate-harness locally), verify they have not drifted from .claude/.
|
|
7
|
+
set -uo pipefail
|
|
8
|
+
|
|
9
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
10
|
+
cd "$ROOT"
|
|
11
|
+
|
|
12
|
+
if [ -d "$ROOT/.venv" ] && [ -x "$ROOT/.venv/bin/python3" ]; then
|
|
13
|
+
PYTHON="$ROOT/.venv/bin/python3"
|
|
14
|
+
else
|
|
15
|
+
PYTHON="python3"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
FAIL=0
|
|
19
|
+
TMP="$(mktemp -d)"
|
|
20
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
21
|
+
mkdir -p "$TMP/project" "$TMP/home"
|
|
22
|
+
|
|
23
|
+
for HARNESS in codex opencode; do
|
|
24
|
+
echo "==> Harness smoke generation: $HARNESS"
|
|
25
|
+
if ! "$PYTHON" scripts/lincoln_harness_adapter.py \
|
|
26
|
+
--harness "$HARNESS" --root "$ROOT" \
|
|
27
|
+
--project-dir "$TMP/project" --home-dir "$TMP/home" > /dev/null; then
|
|
28
|
+
echo "❌ generation failed for harness: $HARNESS"
|
|
29
|
+
FAIL=1
|
|
30
|
+
continue
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Drift comparison only when artifacts were generated into the project.
|
|
34
|
+
case "$HARNESS" in
|
|
35
|
+
opencode) MARKER="$ROOT/.opencode" ;;
|
|
36
|
+
codex)
|
|
37
|
+
if [ -e "$ROOT/.codex-plugin" ]; then
|
|
38
|
+
MARKER="$ROOT/.codex-plugin"
|
|
39
|
+
else
|
|
40
|
+
MARKER="$ROOT/AGENTS.md"
|
|
41
|
+
fi
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
44
|
+
if [ -e "$MARKER" ]; then
|
|
45
|
+
echo "==> Drift check: $HARNESS (project artifacts present)"
|
|
46
|
+
if ! "$PYTHON" scripts/lincoln_harness_adapter.py \
|
|
47
|
+
--harness "$HARNESS" --root "$ROOT" \
|
|
48
|
+
--project-dir "$ROOT" --home-dir "$HOME" --check; then
|
|
49
|
+
FAIL=1
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
if [ "$FAIL" -eq 0 ]; then
|
|
55
|
+
echo "✅ harness drift check passed"
|
|
56
|
+
fi
|
|
57
|
+
exit "$FAIL"
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Reject branch-only Lincoln process artifacts from main-bound PRs."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
12
|
+
from scripts.lincoln_paths import RESERVED_PROCESS_DIRS, STATE_FILENAME
|
|
13
|
+
|
|
14
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
ROOT_PROCESS_DIRS = {
|
|
18
|
+
"designs",
|
|
19
|
+
"interviews",
|
|
20
|
+
"recordings",
|
|
21
|
+
"requirements",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
PROCESS_SUBPATHS = {
|
|
25
|
+
"designs",
|
|
26
|
+
"docs/research",
|
|
27
|
+
"handoffs",
|
|
28
|
+
"interviews",
|
|
29
|
+
"openspec/changes",
|
|
30
|
+
"recordings",
|
|
31
|
+
"requirements",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
ALLOWED_ROOT_PREFIXES = (
|
|
35
|
+
".claude/",
|
|
36
|
+
".claude-plugin/",
|
|
37
|
+
".conductor/",
|
|
38
|
+
".github/",
|
|
39
|
+
"CLAUDE.md",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"README.md",
|
|
42
|
+
"RELEASE.md",
|
|
43
|
+
"knowledge/",
|
|
44
|
+
"oss/README.md",
|
|
45
|
+
"oss/projects.yaml",
|
|
46
|
+
"products/",
|
|
47
|
+
"requirements.txt",
|
|
48
|
+
"scripts/",
|
|
49
|
+
"tests/",
|
|
50
|
+
"tools/",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def changed_paths(base: str) -> list[str]:
|
|
55
|
+
result = subprocess.run(
|
|
56
|
+
["git", "diff", "--name-only", f"{base}...HEAD"],
|
|
57
|
+
capture_output=True,
|
|
58
|
+
text=True,
|
|
59
|
+
check=False,
|
|
60
|
+
)
|
|
61
|
+
if result.returncode != 0:
|
|
62
|
+
return []
|
|
63
|
+
return [line.strip() for line in result.stdout.splitlines() if line.strip()]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def is_process_package_dir(top_dir: str) -> bool:
|
|
67
|
+
"""A directory is a Lincoln process package if it contains workflow-stage.yaml."""
|
|
68
|
+
if top_dir in RESERVED_PROCESS_DIRS or top_dir in ROOT_PROCESS_DIRS:
|
|
69
|
+
return False
|
|
70
|
+
return (ROOT / top_dir / STATE_FILENAME).is_file()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def is_process_package_path(path: str) -> bool:
|
|
74
|
+
parts = Path(path).parts
|
|
75
|
+
if not parts:
|
|
76
|
+
return False
|
|
77
|
+
top = parts[0]
|
|
78
|
+
if top in RESERVED_PROCESS_DIRS or top in ROOT_PROCESS_DIRS:
|
|
79
|
+
return False
|
|
80
|
+
# Reject every file inside a process package directory, not only known subpaths.
|
|
81
|
+
if is_process_package_dir(top):
|
|
82
|
+
return True
|
|
83
|
+
if len(parts) == 2 and parts[1] == "workflow-stage.yaml":
|
|
84
|
+
return True
|
|
85
|
+
rest = "/".join(parts[1:])
|
|
86
|
+
return any(rest == sub or rest.startswith(f"{sub}/") for sub in PROCESS_SUBPATHS)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def is_root_process_artifact(path: str) -> bool:
|
|
90
|
+
return any(path.startswith(f"{prefix}/") for prefix in ROOT_PROCESS_DIRS) or path.startswith("openspec/changes/") or path.startswith("docs/research/")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def allowed_durable_path(path: str) -> bool:
|
|
94
|
+
return any(path == prefix.rstrip("/") or path.startswith(prefix) for prefix in ALLOWED_ROOT_PREFIXES)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def violations(paths: list[str]) -> list[str]:
|
|
98
|
+
rejected: list[str] = []
|
|
99
|
+
for path in paths:
|
|
100
|
+
if is_process_package_path(path) or is_root_process_artifact(path):
|
|
101
|
+
rejected.append(path)
|
|
102
|
+
continue
|
|
103
|
+
if path.startswith("oss/clones/"):
|
|
104
|
+
rejected.append(path)
|
|
105
|
+
return rejected
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def main() -> int:
|
|
109
|
+
parser = argparse.ArgumentParser(description="Check Lincoln main merge hygiene")
|
|
110
|
+
parser.add_argument("--base", default="origin/main")
|
|
111
|
+
parser.add_argument("--path", action="append", dest="paths", help="Changed path to check; repeatable")
|
|
112
|
+
args = parser.parse_args()
|
|
113
|
+
|
|
114
|
+
paths = args.paths if args.paths is not None else changed_paths(args.base)
|
|
115
|
+
bad = violations(paths)
|
|
116
|
+
if bad:
|
|
117
|
+
print("FAIL: branch-only Lincoln process artifacts must not merge to main:")
|
|
118
|
+
for path in bad:
|
|
119
|
+
print(f" - {path}")
|
|
120
|
+
print("Move durable knowledge to knowledge/ and keep process work in the feature branch.")
|
|
121
|
+
return 1
|
|
122
|
+
|
|
123
|
+
print("PASS: no branch-only Lincoln process artifacts detected.")
|
|
124
|
+
return 0
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if __name__ == "__main__":
|
|
128
|
+
sys.exit(main())
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Check Lincoln skill/CLI dependencies.
|
|
5
|
+
# Usage:
|
|
6
|
+
# scripts/check-skill-dependencies.sh
|
|
7
|
+
# scripts/check-skill-dependencies.sh --silent
|
|
8
|
+
|
|
9
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
10
|
+
cd "$ROOT"
|
|
11
|
+
|
|
12
|
+
PYTHON="${PYTHON:-python3}"
|
|
13
|
+
SILENT=false
|
|
14
|
+
if [[ "${1:-}" == "--silent" ]]; then
|
|
15
|
+
SILENT=true
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
DEP_FILE="$ROOT/.claude/skills/dependencies.yaml"
|
|
19
|
+
LEGACY_DEP_FILE="$ROOT/.claude/skill-dependencies.yaml"
|
|
20
|
+
if [[ ! -f "$DEP_FILE" && -f "$LEGACY_DEP_FILE" ]]; then
|
|
21
|
+
DEP_FILE="$LEGACY_DEP_FILE"
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if [[ "$SILENT" == false ]]; then
|
|
25
|
+
echo "==> Validate $(basename "$DEP_FILE")"
|
|
26
|
+
fi
|
|
27
|
+
"$PYTHON" -c "import yaml; yaml.safe_load(open('$DEP_FILE'))"
|
|
28
|
+
|
|
29
|
+
if [[ "$SILENT" == false ]]; then
|
|
30
|
+
echo "==> Check declared skills and CLIs"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
"$PYTHON" - "$ROOT" "$DEP_FILE" "$SILENT" <<'PY'
|
|
34
|
+
import sys
|
|
35
|
+
import shutil
|
|
36
|
+
import platform
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
import yaml
|
|
39
|
+
|
|
40
|
+
root = Path(sys.argv[1])
|
|
41
|
+
dep_file = Path(sys.argv[2])
|
|
42
|
+
silent = sys.argv[3].lower() == "true"
|
|
43
|
+
|
|
44
|
+
manifest = yaml.safe_load(dep_file.read_text(encoding="utf-8"))
|
|
45
|
+
|
|
46
|
+
skill_root = Path.home() / ".claude" / "skills"
|
|
47
|
+
errors = []
|
|
48
|
+
warnings = []
|
|
49
|
+
install_commands = []
|
|
50
|
+
|
|
51
|
+
system = platform.system().lower()
|
|
52
|
+
if system == "darwin":
|
|
53
|
+
platform_name = "macos"
|
|
54
|
+
elif system == "linux":
|
|
55
|
+
platform_name = "linux"
|
|
56
|
+
else:
|
|
57
|
+
platform_name = system
|
|
58
|
+
|
|
59
|
+
def get_install_command(cfg):
|
|
60
|
+
if "platforms" in cfg and isinstance(cfg["platforms"], dict):
|
|
61
|
+
if platform_name in cfg["platforms"]:
|
|
62
|
+
return cfg["platforms"][platform_name]
|
|
63
|
+
if "linux" in cfg["platforms"]:
|
|
64
|
+
return cfg["platforms"]["linux"]
|
|
65
|
+
return cfg.get("install")
|
|
66
|
+
|
|
67
|
+
for name, cfg in manifest.get("skills", {}).items():
|
|
68
|
+
typ = cfg.get("type", "skill")
|
|
69
|
+
is_required = cfg.get("required", True)
|
|
70
|
+
source = cfg.get("source", "")
|
|
71
|
+
if typ == "cli":
|
|
72
|
+
binary = cfg.get("binary", name)
|
|
73
|
+
if not shutil.which(binary):
|
|
74
|
+
msg = f"CLI missing: {binary} (skill: {name})"
|
|
75
|
+
cmd = get_install_command(cfg)
|
|
76
|
+
if cmd:
|
|
77
|
+
install_commands.append(f"# Install {name}: {cmd}")
|
|
78
|
+
if is_required:
|
|
79
|
+
errors.append(msg)
|
|
80
|
+
else:
|
|
81
|
+
warnings.append(msg)
|
|
82
|
+
else:
|
|
83
|
+
path = cfg.get("path")
|
|
84
|
+
if path:
|
|
85
|
+
expected = root / path / "SKILL.md"
|
|
86
|
+
else:
|
|
87
|
+
expected = skill_root / name / "SKILL.md"
|
|
88
|
+
if not expected.exists():
|
|
89
|
+
msg = f"Skill missing: {name} (expected {expected})"
|
|
90
|
+
if source and source != "inline":
|
|
91
|
+
ref = cfg.get("ref", "")
|
|
92
|
+
ref_arg = f" --branch {ref}" if ref else ""
|
|
93
|
+
install_commands.append(f"# Install {name}: git clone{ref_arg} --single-branch --depth 1 {source} {skill_root / name}")
|
|
94
|
+
if is_required:
|
|
95
|
+
errors.append(msg)
|
|
96
|
+
else:
|
|
97
|
+
warnings.append(msg)
|
|
98
|
+
|
|
99
|
+
if warnings and not silent:
|
|
100
|
+
print("Optional dependencies missing (warnings only):")
|
|
101
|
+
for w in warnings:
|
|
102
|
+
print(f" - {w}")
|
|
103
|
+
|
|
104
|
+
if errors and not silent:
|
|
105
|
+
print("Required dependencies missing:")
|
|
106
|
+
for e in errors:
|
|
107
|
+
print(f" - {e}")
|
|
108
|
+
|
|
109
|
+
if install_commands and not silent:
|
|
110
|
+
print("")
|
|
111
|
+
print("Suggested installation commands:")
|
|
112
|
+
for cmd in install_commands:
|
|
113
|
+
print(f" {cmd}")
|
|
114
|
+
|
|
115
|
+
if errors:
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
if not silent:
|
|
119
|
+
print("All declared skills/CLIs are present.")
|
|
120
|
+
PY
|
|
121
|
+
|
|
122
|
+
if [[ "$SILENT" == false ]]; then
|
|
123
|
+
echo "==> All skill dependencies satisfied"
|
|
124
|
+
fi
|