@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,335 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Dependency manager for Lincoln setup automation.
|
|
3
|
+
|
|
4
|
+
Reads `.claude/skills/dependencies.yaml` as the single source of truth and
|
|
5
|
+
provides functions to check, install, and configure external skills, CLIs,
|
|
6
|
+
and repository settings used by the Lincoln workflow.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import platform
|
|
12
|
+
import re
|
|
13
|
+
import shlex
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
import yaml
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Placeholder values that signal an unconfigured OpenSpec config file.
|
|
23
|
+
_PLACEHOLDER_OWNERS = {"your-org", "sushanglewis"}
|
|
24
|
+
_PLACEHOLDER_NAMES = {"your-repo", "your-product-repo", "product-manager"}
|
|
25
|
+
|
|
26
|
+
_DEFAULT_OPENSPEC_CONFIG = {
|
|
27
|
+
"repository": {
|
|
28
|
+
"owner": "your-org",
|
|
29
|
+
"name": "your-product-repo",
|
|
30
|
+
"default_branch": "main",
|
|
31
|
+
},
|
|
32
|
+
"workflow": {
|
|
33
|
+
"auto_create_issues": True,
|
|
34
|
+
"default_labels": ["from-interview", "openspec"],
|
|
35
|
+
"knowledge_sync": {"enabled": True, "actor": "github-actions"},
|
|
36
|
+
},
|
|
37
|
+
"openspec": {"profile": "default", "telemetry": False},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_dependencies(project_root: Path) -> dict[str, Any]:
|
|
42
|
+
"""Load and parse `.claude/skills/dependencies.yaml`."""
|
|
43
|
+
dep_file = project_root / ".claude" / "skills" / "dependencies.yaml"
|
|
44
|
+
if not dep_file.exists():
|
|
45
|
+
raise FileNotFoundError(f"Dependency manifest not found: {dep_file}")
|
|
46
|
+
return yaml.safe_load(dep_file.read_text(encoding="utf-8"))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def detect_platform() -> str | None:
|
|
50
|
+
"""Return 'macos', 'linux', or None for unsupported platforms."""
|
|
51
|
+
system = platform.system().lower()
|
|
52
|
+
if system == "darwin":
|
|
53
|
+
return "macos"
|
|
54
|
+
if system == "linux":
|
|
55
|
+
return "linux"
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _get_platform(dep: dict[str, Any], platform_name: str | None) -> str | None:
|
|
60
|
+
"""Return the install command for the given platform, if declared."""
|
|
61
|
+
platforms = dep.get("platforms")
|
|
62
|
+
if not platforms:
|
|
63
|
+
return None
|
|
64
|
+
return platforms.get(platform_name) if platform_name else None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_install_command(dep: dict[str, Any], platform_name: str | None) -> str | None:
|
|
68
|
+
"""Build the best-effort install command for a dependency.
|
|
69
|
+
|
|
70
|
+
For skills/plugins without a platform map, fall back to a git clone command.
|
|
71
|
+
For CLIs, use the platform map. Returns None when no command can be derived.
|
|
72
|
+
"""
|
|
73
|
+
typ = dep.get("type", "skill")
|
|
74
|
+
source = dep.get("source", "")
|
|
75
|
+
ref = dep.get("ref", "")
|
|
76
|
+
|
|
77
|
+
if typ in ("skill", "plugin"):
|
|
78
|
+
platform_cmd = _get_platform(dep, platform_name)
|
|
79
|
+
if platform_cmd:
|
|
80
|
+
return platform_cmd
|
|
81
|
+
if source and source != "inline":
|
|
82
|
+
target = Path.home() / ".claude" / "skills" / dep.get("_name", "unknown")
|
|
83
|
+
branch_arg = f"--branch {ref}" if ref else ""
|
|
84
|
+
return f"git clone {branch_arg} --single-branch --depth 1 {source} {target}"
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
if typ == "cli":
|
|
88
|
+
return _get_platform(dep, platform_name)
|
|
89
|
+
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def check_clis(manifest: dict[str, Any], platform_name: str | None = None) -> list[dict[str, Any]]:
|
|
94
|
+
"""Return a list of missing CLI dependencies with install instructions."""
|
|
95
|
+
missing: list[dict[str, Any]] = []
|
|
96
|
+
for name, cfg in manifest.get("skills", {}).items():
|
|
97
|
+
if cfg.get("type") != "cli":
|
|
98
|
+
continue
|
|
99
|
+
binary = cfg.get("binary", name)
|
|
100
|
+
if shutil.which(binary):
|
|
101
|
+
continue
|
|
102
|
+
missing.append(
|
|
103
|
+
{
|
|
104
|
+
"name": name,
|
|
105
|
+
"type": "cli",
|
|
106
|
+
"binary": binary,
|
|
107
|
+
"required": cfg.get("required", True),
|
|
108
|
+
"install_command": get_install_command(cfg, platform_name),
|
|
109
|
+
"install_note": cfg.get("install_note", ""),
|
|
110
|
+
"source": cfg.get("source", ""),
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
return missing
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def check_skills(
|
|
117
|
+
manifest: dict[str, Any], project_root: Path, skills_dir: Path
|
|
118
|
+
) -> list[dict[str, Any]]:
|
|
119
|
+
"""Return a list of missing skill/plugin dependencies with install instructions."""
|
|
120
|
+
missing: list[dict[str, Any]] = []
|
|
121
|
+
for name, cfg in manifest.get("skills", {}).items():
|
|
122
|
+
typ = cfg.get("type", "skill")
|
|
123
|
+
if typ not in ("skill", "plugin"):
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
source = cfg.get("source", "")
|
|
127
|
+
is_required = cfg.get("required", True)
|
|
128
|
+
default_install = cfg.get("default_install", is_required)
|
|
129
|
+
|
|
130
|
+
if source == "inline":
|
|
131
|
+
path = project_root / cfg.get("path", f".claude/skills/{name}")
|
|
132
|
+
expected = path / "SKILL.md"
|
|
133
|
+
else:
|
|
134
|
+
expected = skills_dir / name / "SKILL.md"
|
|
135
|
+
|
|
136
|
+
if expected.exists():
|
|
137
|
+
continue
|
|
138
|
+
|
|
139
|
+
cfg_with_name = dict(cfg)
|
|
140
|
+
cfg_with_name["_name"] = name
|
|
141
|
+
missing.append(
|
|
142
|
+
{
|
|
143
|
+
"name": name,
|
|
144
|
+
"type": typ,
|
|
145
|
+
"required": is_required,
|
|
146
|
+
"default_install": default_install,
|
|
147
|
+
"install_command": get_install_command(cfg_with_name, detect_platform()),
|
|
148
|
+
"install_note": cfg.get("install_note", ""),
|
|
149
|
+
"source": source,
|
|
150
|
+
"ref": cfg.get("ref", ""),
|
|
151
|
+
"expected_path": str(expected),
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
return missing
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _run_command(args: list[str], **kwargs: Any) -> subprocess.CompletedProcess[str]:
|
|
158
|
+
"""Run a shell command and return the completed process."""
|
|
159
|
+
return subprocess.run(
|
|
160
|
+
args,
|
|
161
|
+
capture_output=True,
|
|
162
|
+
text=True,
|
|
163
|
+
check=False,
|
|
164
|
+
**kwargs,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _resolve_remote_sha(target: Path, ref: str) -> str:
|
|
169
|
+
"""Resolve a remote ref (branch or tag) to a commit SHA; empty on failure."""
|
|
170
|
+
proc = _run_command(["git", "-C", str(target), "ls-remote", "origin", ref])
|
|
171
|
+
if proc.returncode != 0 or not proc.stdout.strip():
|
|
172
|
+
return ""
|
|
173
|
+
return proc.stdout.split()[0]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def install_skill(
|
|
177
|
+
name: str,
|
|
178
|
+
source: str,
|
|
179
|
+
ref: str,
|
|
180
|
+
skills_dir: Path,
|
|
181
|
+
offline_cache: Path | None = None,
|
|
182
|
+
dry_run: bool = False,
|
|
183
|
+
) -> dict[str, Any]:
|
|
184
|
+
"""Install or update an external skill/plugin into `skills_dir/name`.
|
|
185
|
+
|
|
186
|
+
Returns a result dict with keys: name, installed, skipped, error.
|
|
187
|
+
"""
|
|
188
|
+
target = skills_dir / name
|
|
189
|
+
result: dict[str, Any] = {"name": name, "installed": False, "skipped": False, "error": ""}
|
|
190
|
+
|
|
191
|
+
if target.exists():
|
|
192
|
+
# Check current ref and working tree state.
|
|
193
|
+
status_proc = _run_command(["git", "-C", str(target), "status", "--porcelain"])
|
|
194
|
+
if status_proc.stdout.strip():
|
|
195
|
+
result["error"] = f"Working tree of {target} is dirty; aborting."
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
describe_proc = _run_command(
|
|
199
|
+
["git", "-C", str(target), "describe", "--tags", "--exact-match"]
|
|
200
|
+
)
|
|
201
|
+
current_tag = describe_proc.stdout.strip() if describe_proc.returncode == 0 else ""
|
|
202
|
+
if current_tag == ref:
|
|
203
|
+
result["skipped"] = True
|
|
204
|
+
return result
|
|
205
|
+
|
|
206
|
+
rev_proc = _run_command(["git", "-C", str(target), "rev-parse", "HEAD"])
|
|
207
|
+
local_sha = rev_proc.stdout.strip() if rev_proc.returncode == 0 else ""
|
|
208
|
+
remote_sha = _resolve_remote_sha(target, ref)
|
|
209
|
+
if remote_sha and local_sha == remote_sha:
|
|
210
|
+
result["skipped"] = True
|
|
211
|
+
return result
|
|
212
|
+
|
|
213
|
+
current_ref = current_tag or local_sha
|
|
214
|
+
if dry_run:
|
|
215
|
+
result["error"] = f"Would update {name} from {current_ref} to {ref}."
|
|
216
|
+
return result
|
|
217
|
+
|
|
218
|
+
fetch_proc = _run_command(["git", "-C", str(target), "fetch", "origin", ref])
|
|
219
|
+
if fetch_proc.returncode != 0:
|
|
220
|
+
result["error"] = f"Failed to fetch {ref}: {fetch_proc.stderr.strip()}"
|
|
221
|
+
return result
|
|
222
|
+
|
|
223
|
+
checkout_proc = _run_command(["git", "-C", str(target), "checkout", "FETCH_HEAD"])
|
|
224
|
+
if checkout_proc.returncode != 0:
|
|
225
|
+
result["error"] = f"Failed to checkout {ref}: {checkout_proc.stderr.strip()}"
|
|
226
|
+
return result
|
|
227
|
+
|
|
228
|
+
result["installed"] = True
|
|
229
|
+
return result
|
|
230
|
+
|
|
231
|
+
# Fresh install.
|
|
232
|
+
if dry_run:
|
|
233
|
+
result["error"] = f"Would clone {name} at {ref}."
|
|
234
|
+
result["dry_run"] = True
|
|
235
|
+
return result
|
|
236
|
+
|
|
237
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
238
|
+
|
|
239
|
+
# Try offline cache first when provided.
|
|
240
|
+
if offline_cache:
|
|
241
|
+
cached = offline_cache / name
|
|
242
|
+
if cached.exists():
|
|
243
|
+
clone_proc = _run_command(
|
|
244
|
+
["git", "clone", "--branch", ref, "--single-branch", "--depth", "1", str(cached), str(target)]
|
|
245
|
+
)
|
|
246
|
+
if clone_proc.returncode == 0:
|
|
247
|
+
result["installed"] = True
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
branch_args = ["--branch", ref] if ref else []
|
|
251
|
+
args = ["git", "clone", *branch_args, "--single-branch", "--depth", "1", source, str(target)]
|
|
252
|
+
clone_proc = _run_command(args)
|
|
253
|
+
if clone_proc.returncode != 0:
|
|
254
|
+
result["error"] = f"Failed to clone {name}: {clone_proc.stderr.strip()}"
|
|
255
|
+
return result
|
|
256
|
+
|
|
257
|
+
result["installed"] = True
|
|
258
|
+
return result
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def install_cli(name: str, command: str, dry_run: bool = False) -> dict[str, Any]:
|
|
262
|
+
"""Install a CLI dependency using the platform-specific command.
|
|
263
|
+
|
|
264
|
+
Returns a result dict with keys: name, installed, dry_run, error.
|
|
265
|
+
"""
|
|
266
|
+
result: dict[str, Any] = {"name": name, "installed": False, "dry_run": dry_run, "error": ""}
|
|
267
|
+
|
|
268
|
+
if dry_run:
|
|
269
|
+
return result
|
|
270
|
+
|
|
271
|
+
# Tokenize the command string, preserving quoted arguments.
|
|
272
|
+
args = shlex.split(command)
|
|
273
|
+
proc = _run_command(args)
|
|
274
|
+
if proc.returncode != 0:
|
|
275
|
+
result["error"] = proc.stderr.strip() or f"Command exited with {proc.returncode}"
|
|
276
|
+
return result
|
|
277
|
+
|
|
278
|
+
result["installed"] = True
|
|
279
|
+
return result
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def init_openspec_config(
|
|
283
|
+
owner: str, name: str, branch: str, config_path: Path
|
|
284
|
+
) -> dict[str, Any]:
|
|
285
|
+
"""Write or update `.github/openspec-config.yml` when it still has placeholders.
|
|
286
|
+
|
|
287
|
+
Returns a result dict with keys: updated, skipped, error.
|
|
288
|
+
"""
|
|
289
|
+
result: dict[str, Any] = {"updated": False, "skipped": False, "error": ""}
|
|
290
|
+
|
|
291
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
292
|
+
|
|
293
|
+
if config_path.exists():
|
|
294
|
+
try:
|
|
295
|
+
data = yaml.safe_load(config_path.read_text(encoding="utf-8")) or {}
|
|
296
|
+
except Exception as exc:
|
|
297
|
+
result["error"] = f"Failed to parse {config_path}: {exc}"
|
|
298
|
+
return result
|
|
299
|
+
else:
|
|
300
|
+
data = {}
|
|
301
|
+
|
|
302
|
+
repository = data.get("repository") or {}
|
|
303
|
+
current_owner = repository.get("owner", "")
|
|
304
|
+
current_name = repository.get("name", "")
|
|
305
|
+
|
|
306
|
+
if (
|
|
307
|
+
current_owner not in _PLACEHOLDER_OWNERS
|
|
308
|
+
and current_name not in _PLACEHOLDER_NAMES
|
|
309
|
+
and current_owner
|
|
310
|
+
and current_name
|
|
311
|
+
):
|
|
312
|
+
result["skipped"] = True
|
|
313
|
+
return result
|
|
314
|
+
|
|
315
|
+
base = _DEFAULT_OPENSPEC_CONFIG.copy()
|
|
316
|
+
base["repository"] = {
|
|
317
|
+
"owner": owner,
|
|
318
|
+
"name": name,
|
|
319
|
+
"default_branch": branch,
|
|
320
|
+
}
|
|
321
|
+
# Merge with any existing non-repository sections to preserve user edits.
|
|
322
|
+
merged = {**base, **{k: v for k, v in data.items() if k != "repository"}}
|
|
323
|
+
merged["repository"] = base["repository"]
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
config_path.write_text(
|
|
327
|
+
yaml.safe_dump(merged, allow_unicode=True, sort_keys=False),
|
|
328
|
+
encoding="utf-8",
|
|
329
|
+
)
|
|
330
|
+
except Exception as exc:
|
|
331
|
+
result["error"] = f"Failed to write {config_path}: {exc}"
|
|
332
|
+
return result
|
|
333
|
+
|
|
334
|
+
result["updated"] = True
|
|
335
|
+
return result
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Maintain the per-package document index (documents.yaml).
|
|
3
|
+
|
|
4
|
+
The index lives in the issue-package root and summarizes, per document, which
|
|
5
|
+
stage produced it and whether it passed human confirmation, so agents can
|
|
6
|
+
understand package contents from a single file instead of scanning the tree.
|
|
7
|
+
It is regenerated from workflow-stage.yaml on every state mutation.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
import yaml
|
|
20
|
+
|
|
21
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
22
|
+
from scripts.lincoln_paths import STATE_FILENAME
|
|
23
|
+
|
|
24
|
+
DOCUMENTS_FILENAME = "documents.yaml"
|
|
25
|
+
|
|
26
|
+
# Matches master-handoff-pm-to-ux-v{MAJOR}.{MINOR}.html under pages/docs/
|
|
27
|
+
HANDOFF_DOC_PATTERN = re.compile(r"pages/docs/master-handoff-pm-to-ux-v(\d+)\.(\d+)\.html$")
|
|
28
|
+
HANDOFF_YAML_PATTERN = re.compile(r"handoffs/pm-to-ux/pm-to-ux\.handoff\.yaml$")
|
|
29
|
+
|
|
30
|
+
# Matches prd-v{MAJOR}.{MINOR}.html under pages/docs/snapshots/
|
|
31
|
+
PRD_SNAPSHOT_PATTERN = re.compile(r"pages/docs/snapshots/prd-v(\d+)\.(\d+)\.html$")
|
|
32
|
+
# Matches current HTML PRD or legacy Markdown PRD paths
|
|
33
|
+
PRD_PATH_PATTERN = re.compile(r"^(pages/docs/prd\.html|prd\.md|requirements/.*/prd\.md)$")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def extract_markdown_version(path: Path) -> str | None:
|
|
37
|
+
"""Look for `<!-- version: vX.Y -->` in a markdown file."""
|
|
38
|
+
if not path.exists():
|
|
39
|
+
return None
|
|
40
|
+
text = path.read_text(encoding="utf-8")
|
|
41
|
+
match = re.search(r"<!--\s*version:\s*(v\d+\.\d+)\s*-->", text, re.IGNORECASE)
|
|
42
|
+
return match.group(1) if match else None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def now_iso() -> str:
|
|
46
|
+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def package_relative(path: str, process_slug: str) -> str:
|
|
50
|
+
"""Strip the '<process_slug>/' prefix from a repo-relative artifact path."""
|
|
51
|
+
prefix = f"{process_slug}/"
|
|
52
|
+
return path[len(prefix):] if path.startswith(prefix) else path
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def is_team_package_state(state_path: Path) -> bool:
|
|
56
|
+
"""Only team issue packages (<slug>/workflow-stage.yaml) carry a document index."""
|
|
57
|
+
return state_path.name == STATE_FILENAME and state_path.parent.name != ".claude"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _extract_document_version(rel_path: str, project_root: Path, process_slug: str) -> str | None:
|
|
61
|
+
"""Detect version for PM→UX handoff documents and PRDs; returns None for others."""
|
|
62
|
+
full_path = project_root / process_slug / rel_path
|
|
63
|
+
if not full_path.exists():
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
md_match = HANDOFF_DOC_PATTERN.search(rel_path)
|
|
67
|
+
if md_match:
|
|
68
|
+
return f"v{md_match.group(1)}.{md_match.group(2)}"
|
|
69
|
+
|
|
70
|
+
snapshot_match = PRD_SNAPSHOT_PATTERN.search(rel_path)
|
|
71
|
+
if snapshot_match:
|
|
72
|
+
return f"v{snapshot_match.group(1)}.{snapshot_match.group(2)}"
|
|
73
|
+
|
|
74
|
+
if PRD_PATH_PATTERN.search(rel_path):
|
|
75
|
+
return extract_markdown_version(full_path)
|
|
76
|
+
|
|
77
|
+
if HANDOFF_YAML_PATTERN.search(rel_path):
|
|
78
|
+
try:
|
|
79
|
+
data = yaml.safe_load(full_path.read_text(encoding="utf-8"))
|
|
80
|
+
if isinstance(data, dict):
|
|
81
|
+
return data.get("human_master_doc", {}).get("version")
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def build_documents_index(
|
|
89
|
+
state: dict[str, Any],
|
|
90
|
+
process_slug: str,
|
|
91
|
+
generated_at: str | None = None,
|
|
92
|
+
project_root: Path | None = None,
|
|
93
|
+
) -> dict[str, Any]:
|
|
94
|
+
"""Build the index dict from state nodes; later/higher approvals win per path."""
|
|
95
|
+
current_run = state.get("current_run", {})
|
|
96
|
+
documents: dict[str, dict[str, Any]] = {}
|
|
97
|
+
root = project_root or Path(__file__).resolve().parents[1]
|
|
98
|
+
for node in state.get("nodes") or []:
|
|
99
|
+
stage_id = str(node.get("stage_id") or "")
|
|
100
|
+
for artifact in node.get("artifacts") or []:
|
|
101
|
+
rel = package_relative(str(artifact), process_slug)
|
|
102
|
+
entry = documents.setdefault(
|
|
103
|
+
rel,
|
|
104
|
+
{
|
|
105
|
+
"path": rel,
|
|
106
|
+
"stage": stage_id,
|
|
107
|
+
"status": "",
|
|
108
|
+
"gate_passed": False,
|
|
109
|
+
"human_confirmed": False,
|
|
110
|
+
"approved_by": None,
|
|
111
|
+
"version": None,
|
|
112
|
+
},
|
|
113
|
+
)
|
|
114
|
+
entry["stage"] = stage_id or entry["stage"]
|
|
115
|
+
entry["status"] = str(node.get("status") or "") or entry["status"]
|
|
116
|
+
version = _extract_document_version(rel, root, process_slug)
|
|
117
|
+
if version:
|
|
118
|
+
entry["version"] = version
|
|
119
|
+
if not node.get("gate_passed"):
|
|
120
|
+
continue
|
|
121
|
+
entry["gate_passed"] = True
|
|
122
|
+
approver = node.get("approved_by")
|
|
123
|
+
if approver and str(approver).startswith("human"):
|
|
124
|
+
entry["human_confirmed"] = True
|
|
125
|
+
entry["approved_by"] = approver
|
|
126
|
+
elif not entry["approved_by"]:
|
|
127
|
+
entry["approved_by"] = approver
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
"process_slug": process_slug,
|
|
131
|
+
"issue_number": str(current_run.get("issue_number") or ""),
|
|
132
|
+
"current_stage": str(current_run.get("current_stage") or ""),
|
|
133
|
+
"last_updated": generated_at or now_iso(),
|
|
134
|
+
"documents": sorted(documents.values(), key=lambda d: d["path"]),
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def write_documents_index(
|
|
139
|
+
state: dict[str, Any],
|
|
140
|
+
state_path: Path,
|
|
141
|
+
generated_at: str | None = None,
|
|
142
|
+
) -> Path | None:
|
|
143
|
+
"""Regenerate documents.yaml next to a team package state file; else return None."""
|
|
144
|
+
if not is_team_package_state(state_path):
|
|
145
|
+
return None
|
|
146
|
+
project_root = state_path.parents[1]
|
|
147
|
+
index = build_documents_index(state, state_path.parent.name, generated_at, project_root)
|
|
148
|
+
index_path = state_path.parent / DOCUMENTS_FILENAME
|
|
149
|
+
index_path.write_text(
|
|
150
|
+
yaml.dump(index, allow_unicode=True, sort_keys=False, width=120),
|
|
151
|
+
encoding="utf-8",
|
|
152
|
+
)
|
|
153
|
+
return index_path
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def main() -> int:
|
|
157
|
+
parser = argparse.ArgumentParser(description="Regenerate the issue-package document index")
|
|
158
|
+
parser.add_argument("--state-file", required=True, help="Path to the package workflow-stage.yaml")
|
|
159
|
+
args = parser.parse_args()
|
|
160
|
+
|
|
161
|
+
state_path = Path(args.state_file).resolve()
|
|
162
|
+
if not state_path.is_file():
|
|
163
|
+
print(f"ERROR: state file not found: {state_path}", file=sys.stderr)
|
|
164
|
+
return 1
|
|
165
|
+
state = yaml.safe_load(state_path.read_text(encoding="utf-8"))
|
|
166
|
+
index_path = write_documents_index(state, state_path)
|
|
167
|
+
if index_path is None:
|
|
168
|
+
print(f"SKIP: {state_path} is not a team issue-package state file")
|
|
169
|
+
return 0
|
|
170
|
+
print(f"Updated {index_path}")
|
|
171
|
+
return 0
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
sys.exit(main())
|