@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
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
cd "$ROOT"
|
|
6
|
+
|
|
7
|
+
STATIC_CHECK_RESULT="$ROOT/.context/static-check-result.json"
|
|
8
|
+
mkdir -p "$ROOT/.context"
|
|
9
|
+
|
|
10
|
+
# Prefer project venv if available; otherwise rely on system python3 having pytest/pyyaml.
|
|
11
|
+
if [ -d "$ROOT/.venv" ] && [ -x "$ROOT/.venv/bin/python3" ]; then
|
|
12
|
+
PYTHON="$ROOT/.venv/bin/python3"
|
|
13
|
+
elif [ -d "$ROOT/venv" ] && [ -x "$ROOT/venv/bin/python3" ]; then
|
|
14
|
+
PYTHON="$ROOT/venv/bin/python3"
|
|
15
|
+
else
|
|
16
|
+
PYTHON="python3"
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Record a structured pass/fail result so benchmark metrics can consume it
|
|
20
|
+
# instead of regex-scanning the command text.
|
|
21
|
+
write_static_check_result() {
|
|
22
|
+
local passed="$1"
|
|
23
|
+
"$PYTHON" - "$passed" <<'PY'
|
|
24
|
+
import sys, json
|
|
25
|
+
from datetime import datetime, timezone
|
|
26
|
+
passed = sys.argv[1] == "true"
|
|
27
|
+
print(json.dumps({
|
|
28
|
+
"passed": passed,
|
|
29
|
+
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
30
|
+
}, ensure_ascii=False))
|
|
31
|
+
PY
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
trap 'write_static_check_result false > "$STATIC_CHECK_RESULT"' ERR
|
|
35
|
+
|
|
36
|
+
echo "==> Validate workflow YAML"
|
|
37
|
+
"$PYTHON" -c "import yaml; yaml.safe_load(open('.claude/workflows/interview-to-knowledge.yaml'))"
|
|
38
|
+
|
|
39
|
+
echo "==> Validate skill manifests"
|
|
40
|
+
for skill in .claude/skills/*/SKILL.md; do
|
|
41
|
+
[ -f "$skill" ] || continue
|
|
42
|
+
"$PYTHON" - "$skill" <<'PY'
|
|
43
|
+
import sys, yaml
|
|
44
|
+
path = sys.argv[1]
|
|
45
|
+
text = open(path, encoding="utf-8").read()
|
|
46
|
+
if text.startswith("---"):
|
|
47
|
+
end = text.find("---", 3)
|
|
48
|
+
if end != -1:
|
|
49
|
+
frontmatter = text[3:end]
|
|
50
|
+
try:
|
|
51
|
+
yaml.safe_load(frontmatter)
|
|
52
|
+
except Exception as exc:
|
|
53
|
+
print(f"Invalid frontmatter in {path}: {exc}")
|
|
54
|
+
sys.exit(1)
|
|
55
|
+
print(f"OK: {path}")
|
|
56
|
+
PY
|
|
57
|
+
if [ $? -ne 0 ]; then
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
echo "==> Validate skill dependency manifest"
|
|
63
|
+
DEP_FILE=".claude/skills/dependencies.yaml"
|
|
64
|
+
if [ ! -f "$DEP_FILE" ]; then
|
|
65
|
+
DEP_FILE=".claude/skill-dependencies.yaml"
|
|
66
|
+
fi
|
|
67
|
+
"$PYTHON" -c "import yaml; yaml.safe_load(open('$DEP_FILE'))"
|
|
68
|
+
|
|
69
|
+
if [ -x "scripts/check-skill-dependencies.sh" ]; then
|
|
70
|
+
echo "==> Check skill dependencies (non-blocking)"
|
|
71
|
+
bash scripts/check-skill-dependencies.sh || true
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
echo "==> Validate workflow templates reference stage YAMLs"
|
|
75
|
+
"$PYTHON" - "$ROOT" <<'PY'
|
|
76
|
+
import sys
|
|
77
|
+
from pathlib import Path
|
|
78
|
+
import yaml
|
|
79
|
+
|
|
80
|
+
root = Path(sys.argv[1])
|
|
81
|
+
templates_dir = root / ".claude" / "workflows" / "templates"
|
|
82
|
+
main_workflow = root / ".claude" / "workflows" / "interview-to-knowledge.yaml"
|
|
83
|
+
errors = []
|
|
84
|
+
|
|
85
|
+
for path in list(templates_dir.glob("*.yaml")) + [main_workflow]:
|
|
86
|
+
if not path.exists():
|
|
87
|
+
continue
|
|
88
|
+
try:
|
|
89
|
+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
90
|
+
except Exception as exc:
|
|
91
|
+
errors.append(f"{path.name}: YAML parse error: {exc}")
|
|
92
|
+
continue
|
|
93
|
+
workflow = data.get("workflow", data)
|
|
94
|
+
for step in workflow.get("steps", []):
|
|
95
|
+
stage_id = step.get("id")
|
|
96
|
+
if not stage_id:
|
|
97
|
+
errors.append(f"{path.name}: step missing id")
|
|
98
|
+
continue
|
|
99
|
+
stage_yaml = root / ".claude" / "stages" / f"{stage_id}.yaml"
|
|
100
|
+
if not stage_yaml.exists():
|
|
101
|
+
errors.append(f"{path.name}: missing stage YAML {stage_yaml}")
|
|
102
|
+
|
|
103
|
+
if errors:
|
|
104
|
+
print("Workflow template issues:")
|
|
105
|
+
for e in errors:
|
|
106
|
+
print(f" - {e}")
|
|
107
|
+
sys.exit(1)
|
|
108
|
+
|
|
109
|
+
print("All workflow templates reference valid stage YAMLs.")
|
|
110
|
+
PY
|
|
111
|
+
|
|
112
|
+
echo "==> Validate JSON schemas are valid JSON"
|
|
113
|
+
"$PYTHON" - "$ROOT" <<'PY'
|
|
114
|
+
import sys, json
|
|
115
|
+
from pathlib import Path
|
|
116
|
+
root = Path(sys.argv[1])
|
|
117
|
+
errors = []
|
|
118
|
+
for schema_path in (root / ".claude" / "schemas").glob("*.json"):
|
|
119
|
+
try:
|
|
120
|
+
json.loads(schema_path.read_text(encoding="utf-8"))
|
|
121
|
+
except Exception as exc:
|
|
122
|
+
errors.append(f"{schema_path.name}: {exc}")
|
|
123
|
+
if errors:
|
|
124
|
+
print("Invalid JSON schemas:")
|
|
125
|
+
for e in errors:
|
|
126
|
+
print(f" - {e}")
|
|
127
|
+
sys.exit(1)
|
|
128
|
+
print("All JSON schemas are valid.")
|
|
129
|
+
PY
|
|
130
|
+
|
|
131
|
+
echo "==> Validate agent frontmatters"
|
|
132
|
+
"$PYTHON" - "$ROOT" <<'PY'
|
|
133
|
+
import sys, yaml
|
|
134
|
+
from pathlib import Path
|
|
135
|
+
|
|
136
|
+
root = Path(sys.argv[1])
|
|
137
|
+
errors = []
|
|
138
|
+
for agent_md in (root / ".claude" / "agents").glob("*.md"):
|
|
139
|
+
text = agent_md.read_text(encoding="utf-8")
|
|
140
|
+
if not text.startswith("---"):
|
|
141
|
+
errors.append(f"{agent_md.name}: missing frontmatter")
|
|
142
|
+
continue
|
|
143
|
+
end = text.find("---", 3)
|
|
144
|
+
if end == -1:
|
|
145
|
+
errors.append(f"{agent_md.name}: unterminated frontmatter")
|
|
146
|
+
continue
|
|
147
|
+
try:
|
|
148
|
+
fm = yaml.safe_load(text[3:end])
|
|
149
|
+
except Exception as exc:
|
|
150
|
+
errors.append(f"{agent_md.name}: invalid frontmatter: {exc}")
|
|
151
|
+
continue
|
|
152
|
+
if not isinstance(fm, dict):
|
|
153
|
+
errors.append(f"{agent_md.name}: frontmatter is not a mapping")
|
|
154
|
+
continue
|
|
155
|
+
for key in ["name", "description"]:
|
|
156
|
+
if key not in fm:
|
|
157
|
+
errors.append(f"{agent_md.name}: missing {key}")
|
|
158
|
+
if errors:
|
|
159
|
+
print("Agent frontmatter issues:")
|
|
160
|
+
for e in errors:
|
|
161
|
+
print(f" - {e}")
|
|
162
|
+
sys.exit(1)
|
|
163
|
+
print("All agent frontmatters valid.")
|
|
164
|
+
PY
|
|
165
|
+
|
|
166
|
+
echo "==> Validate Python syntax"
|
|
167
|
+
"$PYTHON" -m py_compile scripts/validate_stage.py
|
|
168
|
+
|
|
169
|
+
echo "==> Validate prompt paths in skills"
|
|
170
|
+
"$PYTHON" - "$ROOT" <<'PY'
|
|
171
|
+
import sys
|
|
172
|
+
from pathlib import Path
|
|
173
|
+
|
|
174
|
+
root = Path(sys.argv[1])
|
|
175
|
+
missing = []
|
|
176
|
+
for skill_dir in (root / ".claude" / "skills").iterdir():
|
|
177
|
+
if not skill_dir.is_dir():
|
|
178
|
+
continue
|
|
179
|
+
skill_md = skill_dir / "SKILL.md"
|
|
180
|
+
if not skill_md.exists():
|
|
181
|
+
continue
|
|
182
|
+
text = skill_md.read_text(encoding="utf-8")
|
|
183
|
+
# Only enforce prompts/main.md when the skill explicitly declares it as entry
|
|
184
|
+
if "prompts/main.md" not in text:
|
|
185
|
+
continue
|
|
186
|
+
main_prompt = skill_dir / "prompts" / "main.md"
|
|
187
|
+
if not main_prompt.exists():
|
|
188
|
+
missing.append(str(main_prompt))
|
|
189
|
+
if missing:
|
|
190
|
+
print("Missing prompts:")
|
|
191
|
+
for m in missing:
|
|
192
|
+
print(f" - {m}")
|
|
193
|
+
sys.exit(1)
|
|
194
|
+
print("All prompt paths resolved.")
|
|
195
|
+
PY
|
|
196
|
+
|
|
197
|
+
echo "==> Validate workflow-stage.yaml schema"
|
|
198
|
+
"$PYTHON" - "$ROOT" <<'PY'
|
|
199
|
+
import sys
|
|
200
|
+
from pathlib import Path
|
|
201
|
+
import yaml
|
|
202
|
+
|
|
203
|
+
root = Path(sys.argv[1])
|
|
204
|
+
state_file = root / ".claude" / "workflow-stage.yaml"
|
|
205
|
+
legacy_state_file = root / ".claude" / "workflow-state.yaml"
|
|
206
|
+
if not state_file.exists() and legacy_state_file.exists():
|
|
207
|
+
state_file = legacy_state_file
|
|
208
|
+
|
|
209
|
+
if not state_file.exists():
|
|
210
|
+
print("workflow-stage.yaml not found; skipping")
|
|
211
|
+
sys.exit(0)
|
|
212
|
+
|
|
213
|
+
state = yaml.safe_load(state_file.read_text(encoding="utf-8"))
|
|
214
|
+
required_top_legacy = ["schema_version", "workflow", "current_run", "stages", "recovery"]
|
|
215
|
+
required_top_new = ["schema_version", "workflow", "current_run", "nodes", "recovery"]
|
|
216
|
+
if "nodes" in state:
|
|
217
|
+
missing = [k for k in required_top_new if k not in state]
|
|
218
|
+
elif "stages" in state:
|
|
219
|
+
missing = [k for k in required_top_legacy if k not in state]
|
|
220
|
+
else:
|
|
221
|
+
missing = [k for k in required_top_new if k not in state]
|
|
222
|
+
if missing:
|
|
223
|
+
print(f"Missing top-level keys: {', '.join(missing)}")
|
|
224
|
+
sys.exit(1)
|
|
225
|
+
|
|
226
|
+
print("workflow-stage.yaml schema valid.")
|
|
227
|
+
PY
|
|
228
|
+
|
|
229
|
+
echo "==> Validate stage YAML files"
|
|
230
|
+
"$PYTHON" - "$ROOT" <<'PY'
|
|
231
|
+
import sys
|
|
232
|
+
from pathlib import Path
|
|
233
|
+
import yaml
|
|
234
|
+
|
|
235
|
+
root = Path(sys.argv[1])
|
|
236
|
+
errors = []
|
|
237
|
+
for stage_yaml in sorted((root / ".claude" / "stages").glob("*.yaml")):
|
|
238
|
+
if stage_yaml.name == "stage-manifest.yaml":
|
|
239
|
+
continue
|
|
240
|
+
try:
|
|
241
|
+
data = yaml.safe_load(stage_yaml.read_text(encoding="utf-8"))
|
|
242
|
+
except Exception as exc:
|
|
243
|
+
errors.append(f"{stage_yaml.name}: YAML parse error: {exc}")
|
|
244
|
+
continue
|
|
245
|
+
if not isinstance(data, dict):
|
|
246
|
+
errors.append(f"{stage_yaml.name}: root is not a mapping")
|
|
247
|
+
continue
|
|
248
|
+
for key in ["schema_version", "id", "name", "description", "templates", "prerequisite_stage", "next_stage", "human_gate", "agent", "skills", "artifacts", "context", "gates"]:
|
|
249
|
+
if key not in data:
|
|
250
|
+
errors.append(f"{stage_yaml.name}: missing key '{key}'")
|
|
251
|
+
|
|
252
|
+
if errors:
|
|
253
|
+
print("Stage YAML issues:")
|
|
254
|
+
for e in errors:
|
|
255
|
+
print(f" - {e}")
|
|
256
|
+
sys.exit(1)
|
|
257
|
+
print("All stage YAML files valid.")
|
|
258
|
+
PY
|
|
259
|
+
|
|
260
|
+
echo "==> Validate stage YAML files against schema"
|
|
261
|
+
"$PYTHON" - "$ROOT" <<'PY'
|
|
262
|
+
import sys
|
|
263
|
+
from pathlib import Path
|
|
264
|
+
import yaml
|
|
265
|
+
import json
|
|
266
|
+
|
|
267
|
+
root = Path(sys.argv[1])
|
|
268
|
+
schema_path = root / ".claude" / "schemas" / "stage-definition.schema.json"
|
|
269
|
+
schema = json.loads(schema_path.read_text(encoding="utf-8"))
|
|
270
|
+
|
|
271
|
+
# Minimal structural validation
|
|
272
|
+
required = set(schema.get("required", []))
|
|
273
|
+
errors = []
|
|
274
|
+
for stage_yaml in sorted((root / ".claude" / "stages").glob("*.yaml")):
|
|
275
|
+
if stage_yaml.name == "stage-manifest.yaml":
|
|
276
|
+
continue
|
|
277
|
+
try:
|
|
278
|
+
data = yaml.safe_load(stage_yaml.read_text(encoding="utf-8"))
|
|
279
|
+
except Exception as exc:
|
|
280
|
+
errors.append(f"{stage_yaml.name}: YAML parse error: {exc}")
|
|
281
|
+
continue
|
|
282
|
+
missing = required - set(data.keys())
|
|
283
|
+
if missing:
|
|
284
|
+
errors.append(f"{stage_yaml.name}: missing required keys: {', '.join(sorted(missing))}")
|
|
285
|
+
|
|
286
|
+
if errors:
|
|
287
|
+
print("Stage schema issues:")
|
|
288
|
+
for e in errors:
|
|
289
|
+
print(f" - {e}")
|
|
290
|
+
sys.exit(1)
|
|
291
|
+
print("All stage YAML files match required schema.")
|
|
292
|
+
PY
|
|
293
|
+
|
|
294
|
+
echo "==> Validate hook scripts are executable"
|
|
295
|
+
for hook in "$ROOT/.claude/hooks/"*.sh; do
|
|
296
|
+
if [ -f "$hook" ] && [ ! -x "$hook" ]; then
|
|
297
|
+
echo "Hook not executable: $hook"
|
|
298
|
+
exit 1
|
|
299
|
+
fi
|
|
300
|
+
done
|
|
301
|
+
echo "All hooks executable."
|
|
302
|
+
|
|
303
|
+
echo "==> Validate Python syntax for scripts"
|
|
304
|
+
# Compile every Python script under scripts/ (skip __pycache__ and non-.py files)
|
|
305
|
+
py_errors=0
|
|
306
|
+
for pyfile in scripts/*.py; do
|
|
307
|
+
[ -f "$pyfile" ] || continue
|
|
308
|
+
if ! "$PYTHON" -m py_compile "$pyfile"; then
|
|
309
|
+
py_errors=$((py_errors + 1))
|
|
310
|
+
fi
|
|
311
|
+
done
|
|
312
|
+
if [ $py_errors -ne 0 ]; then
|
|
313
|
+
echo "Python syntax errors detected: $py_errors"
|
|
314
|
+
exit 1
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
echo "==> Check command-map drift"
|
|
318
|
+
"$PYTHON" scripts/lincoln_command_map.py --check
|
|
319
|
+
|
|
320
|
+
echo "==> Check main merge hygiene"
|
|
321
|
+
"$PYTHON" scripts/check-main-merge-hygiene.py || true
|
|
322
|
+
|
|
323
|
+
echo "==> Validate bash syntax for scripts"
|
|
324
|
+
bash -n scripts/init-lincoln-branch.sh
|
|
325
|
+
bash -n scripts/init-project.sh
|
|
326
|
+
bash -n scripts/list-active-lincoln-branches.sh
|
|
327
|
+
bash -n scripts/check-skill-dependencies.sh
|
|
328
|
+
bash -n scripts/sync-external-agents.sh
|
|
329
|
+
bash -n scripts/check-harness-drift.sh
|
|
330
|
+
for hook in "$ROOT/.claude/hooks/"*.sh; do
|
|
331
|
+
bash -n "$hook"
|
|
332
|
+
done
|
|
333
|
+
|
|
334
|
+
echo "==> Check harness drift"
|
|
335
|
+
bash scripts/check-harness-drift.sh
|
|
336
|
+
|
|
337
|
+
echo "==> Check version lockstep"
|
|
338
|
+
"$PYTHON" scripts/bump_version.py --check
|
|
339
|
+
|
|
340
|
+
echo "==> Check plugin package manifest"
|
|
341
|
+
find .claude scripts tools -type d -name __pycache__ -prune -exec rm -rf {} + 2>/dev/null || true
|
|
342
|
+
"$PYTHON" scripts/package-lincoln-plugin.py check
|
|
343
|
+
|
|
344
|
+
echo "==> Run pytest"
|
|
345
|
+
"$PYTHON" -m pytest tests/ -v
|
|
346
|
+
pytest_exit=$?
|
|
347
|
+
|
|
348
|
+
if [ $pytest_exit -eq 0 ]; then
|
|
349
|
+
write_static_check_result true > "$STATIC_CHECK_RESULT"
|
|
350
|
+
echo "==> All static checks passed"
|
|
351
|
+
else
|
|
352
|
+
write_static_check_result false > "$STATIC_CHECK_RESULT"
|
|
353
|
+
echo "==> Static checks failed"
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
exit $pytest_exit
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Sync external agent definitions declared by .claude/agents/external/*.manifest.yaml.
|
|
5
|
+
# Usage:
|
|
6
|
+
# scripts/sync-external-agents.sh
|
|
7
|
+
# scripts/sync-external-agents.sh --dry-run
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
11
|
+
cd "$ROOT"
|
|
12
|
+
|
|
13
|
+
DRY_RUN=false
|
|
14
|
+
if [[ "${1:-}" == "--dry-run" ]]; then
|
|
15
|
+
DRY_RUN=true
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
AGENTS_DIR="$ROOT/.claude/agents/external"
|
|
19
|
+
mkdir -p "$AGENTS_DIR"
|
|
20
|
+
|
|
21
|
+
"$ROOT/.venv/bin/python3" - "$AGENTS_DIR" "$DRY_RUN" <<'PY'
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
import re
|
|
25
|
+
import shutil
|
|
26
|
+
import subprocess
|
|
27
|
+
import yaml
|
|
28
|
+
|
|
29
|
+
agents_dir = Path(sys.argv[1])
|
|
30
|
+
dry_run = sys.argv[2].lower() == "true"
|
|
31
|
+
|
|
32
|
+
KNOWN_FIELDS = {"name", "description", "extends", "model", "tools", "disallowed_tools"}
|
|
33
|
+
PERMISSIVE_LICENSES = {"mit", "apache-2.0", "bsd-3-clause", "bsd-2-clause", "isc"}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def slugify(value: str) -> str:
|
|
37
|
+
value = re.sub(r"[^\w\s-]", "", value).strip().lower()
|
|
38
|
+
return re.sub(r"[-\s]+", "-", value)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def normalize_frontmatter(text: str, prefix: str, source: str, ref: str, license_key: str, original_name: str) -> str:
|
|
42
|
+
if not text.startswith("---"):
|
|
43
|
+
return text
|
|
44
|
+
parts = text.split("---", 2)
|
|
45
|
+
if len(parts) < 3:
|
|
46
|
+
return text
|
|
47
|
+
try:
|
|
48
|
+
front = yaml.safe_load(parts[1]) or {}
|
|
49
|
+
except yaml.YAMLError:
|
|
50
|
+
return text
|
|
51
|
+
|
|
52
|
+
if not isinstance(front, dict):
|
|
53
|
+
front = {}
|
|
54
|
+
|
|
55
|
+
normalized = {k: v for k, v in front.items() if k in KNOWN_FIELDS}
|
|
56
|
+
|
|
57
|
+
# Prefix the agent name to avoid collisions.
|
|
58
|
+
normalized["name"] = f"{prefix}-{slugify(original_name)}"
|
|
59
|
+
normalized["original_name"] = original_name
|
|
60
|
+
normalized["source"] = source
|
|
61
|
+
normalized["ref"] = ref
|
|
62
|
+
normalized["license"] = license_key
|
|
63
|
+
|
|
64
|
+
# Map camelCase / legacy fields.
|
|
65
|
+
if "disallowedTools" in front and "disallowed_tools" not in normalized:
|
|
66
|
+
normalized["disallowed_tools"] = front["disallowedTools"]
|
|
67
|
+
|
|
68
|
+
# Resolve inherited models.
|
|
69
|
+
model = normalized.get("model")
|
|
70
|
+
if isinstance(model, str):
|
|
71
|
+
model = model.lower().strip()
|
|
72
|
+
if model in {"inherit", "default", "auto"}:
|
|
73
|
+
normalized["model"] = "sonnet"
|
|
74
|
+
if "model" not in normalized:
|
|
75
|
+
normalized["model"] = "sonnet"
|
|
76
|
+
|
|
77
|
+
# Remove extends from imported standalone agents; Lincoln wrappers handle composition.
|
|
78
|
+
normalized.pop("extends", None)
|
|
79
|
+
|
|
80
|
+
normalized_yaml = yaml.safe_dump(normalized, sort_keys=False, allow_unicode=True)
|
|
81
|
+
attribution = (
|
|
82
|
+
f"<!-- Imported from {source} @ {ref}, licensed under {license_key}. "
|
|
83
|
+
f"Original name: {original_name}. -->"
|
|
84
|
+
)
|
|
85
|
+
body = parts[2].lstrip("\n")
|
|
86
|
+
return f"---\n{normalized_yaml}---\n\n{attribution}\n\n{body}"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def parse_manifest(manifest_path: Path) -> dict | None:
|
|
90
|
+
docs = list(yaml.safe_load_all(manifest_path.read_text(encoding="utf-8")))
|
|
91
|
+
data = docs[0] if docs else None
|
|
92
|
+
if not isinstance(data, dict):
|
|
93
|
+
print(f"Skipping {manifest_path.name}: invalid manifest")
|
|
94
|
+
return None
|
|
95
|
+
return data
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def agent_source_path(agents_subdir: str, imported: str) -> str:
|
|
99
|
+
if imported.endswith(".md"):
|
|
100
|
+
return f"{agents_subdir}/{imported}"
|
|
101
|
+
return f"{agents_subdir}/{imported}.md"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def agent_basename(imported: str) -> str:
|
|
105
|
+
name = imported[:-3] if imported.endswith(".md") else imported
|
|
106
|
+
return Path(name).name
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
notices = []
|
|
110
|
+
notices.append("# 外部 Agent 来源声明\n")
|
|
111
|
+
notices.append("本文件由 `scripts/sync-external-agents.sh` 自动生成,请勿手动修改。\n")
|
|
112
|
+
notices.append("| Lincoln 文件名 | 原始名称 | 来源仓库 | 引用 | 许可证 |")
|
|
113
|
+
notices.append("|---|---|---|---|---|")
|
|
114
|
+
|
|
115
|
+
for manifest_path in sorted(agents_dir.glob("*.manifest.yaml")):
|
|
116
|
+
data = parse_manifest(manifest_path)
|
|
117
|
+
if data is None:
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
framework = manifest_path.stem.replace(".manifest", "")
|
|
121
|
+
source = data.get("source", "")
|
|
122
|
+
ref = data.get("ref", "main") or "main"
|
|
123
|
+
license_key = (data.get("license", "") or "").lower()
|
|
124
|
+
prefix = data.get("prefix", framework)
|
|
125
|
+
agents_subdir = data.get("paths", {}).get("agents", ".claude/agents")
|
|
126
|
+
imported_agents = data.get("imported_agents", [])
|
|
127
|
+
|
|
128
|
+
if not source:
|
|
129
|
+
print(f"Skipping {manifest_path.name}: no source declared")
|
|
130
|
+
continue
|
|
131
|
+
if license_key not in PERMISSIVE_LICENSES:
|
|
132
|
+
print(f"Skipping {manifest_path.name}: license '{license_key}' is not permissive")
|
|
133
|
+
continue
|
|
134
|
+
if not imported_agents:
|
|
135
|
+
print(f"Skipping {manifest_path.name}: no imported_agents")
|
|
136
|
+
continue
|
|
137
|
+
|
|
138
|
+
target_dir = agents_dir / framework / "agents"
|
|
139
|
+
print(f"Syncing {framework} from {source} @ {ref}")
|
|
140
|
+
|
|
141
|
+
if dry_run:
|
|
142
|
+
print(f" [dry-run] Would clone/sparse-checkout into {agents_dir / framework}")
|
|
143
|
+
for imported in imported_agents:
|
|
144
|
+
print(f" [dry-run] Would import {imported} as {prefix}-{slugify(agent_basename(imported))}")
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
if target_dir.exists():
|
|
148
|
+
shutil.rmtree(target_dir.parent)
|
|
149
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
150
|
+
|
|
151
|
+
repo_dir = agents_dir / framework / ".repo"
|
|
152
|
+
if repo_dir.exists():
|
|
153
|
+
shutil.rmtree(repo_dir)
|
|
154
|
+
repo_dir.mkdir(parents=True, exist_ok=True)
|
|
155
|
+
|
|
156
|
+
sparse_paths = [agent_source_path(agents_subdir, imp) for imp in imported_agents]
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
subprocess.run(
|
|
160
|
+
[
|
|
161
|
+
"git", "clone", "--depth", "1", "--branch", ref,
|
|
162
|
+
"--filter=blob:none", "--sparse", source, str(repo_dir),
|
|
163
|
+
],
|
|
164
|
+
check=True,
|
|
165
|
+
capture_output=True,
|
|
166
|
+
)
|
|
167
|
+
subprocess.run(
|
|
168
|
+
["git", "sparse-checkout", "set", "--no-cone", *sparse_paths],
|
|
169
|
+
cwd=repo_dir,
|
|
170
|
+
check=True,
|
|
171
|
+
capture_output=True,
|
|
172
|
+
)
|
|
173
|
+
except subprocess.CalledProcessError as exc:
|
|
174
|
+
print(f" Failed to sync {framework}: {exc}")
|
|
175
|
+
if exc.stderr:
|
|
176
|
+
print(exc.stderr.decode("utf-8", errors="replace"))
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
# Copy and normalize each imported agent.
|
|
180
|
+
for imported in imported_agents:
|
|
181
|
+
src_rel = agent_source_path(agents_subdir, imported)
|
|
182
|
+
src_file = repo_dir / src_rel
|
|
183
|
+
original_name = agent_basename(imported)
|
|
184
|
+
normalized_name = f"{prefix}-{slugify(original_name)}"
|
|
185
|
+
dest_file = target_dir / f"{normalized_name}.md"
|
|
186
|
+
|
|
187
|
+
if not src_file.exists():
|
|
188
|
+
print(f" Warning: expected agent file not found: {src_rel}")
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
raw_text = src_file.read_text(encoding="utf-8")
|
|
192
|
+
normalized_text = normalize_frontmatter(
|
|
193
|
+
raw_text, prefix, source, ref, license_key, original_name
|
|
194
|
+
)
|
|
195
|
+
dest_file.write_text(normalized_text, encoding="utf-8")
|
|
196
|
+
notices.append(
|
|
197
|
+
f"| {normalized_name}.md | {original_name} | {source} | {ref} | {license_key.upper()} |"
|
|
198
|
+
)
|
|
199
|
+
print(f" Imported {normalized_name}")
|
|
200
|
+
|
|
201
|
+
# Clean up the temporary clone.
|
|
202
|
+
shutil.rmtree(repo_dir)
|
|
203
|
+
|
|
204
|
+
# Write NOTICES.md
|
|
205
|
+
notices_path = agents_dir / "NOTICES.md"
|
|
206
|
+
notices_path.write_text("\n".join(notices) + "\n", encoding="utf-8")
|
|
207
|
+
print(f"Wrote {notices_path}")
|
|
208
|
+
|
|
209
|
+
print("External agent sync complete.")
|
|
210
|
+
PY
|