@sushanglewis/lincoln 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.d.ts +3 -0
- package/dist/main.js +1286 -916
- 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,198 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Shared path helpers for Lincoln process packages."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import subprocess
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import yaml
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
+
LEGACY_STATE_PATH = PROJECT_ROOT / ".claude" / "workflow-state.yaml"
|
|
17
|
+
ROOT_STATE_PATH = PROJECT_ROOT / ".claude" / "workflow-stage.yaml"
|
|
18
|
+
STATE_FILENAME = "workflow-stage.yaml"
|
|
19
|
+
SOLO_STATE_DIR = ".context/workflow"
|
|
20
|
+
SOLO_PROCESS_SLUG = ".context"
|
|
21
|
+
|
|
22
|
+
RESERVED_PROCESS_DIRS = {
|
|
23
|
+
".claude",
|
|
24
|
+
".conductor",
|
|
25
|
+
".context",
|
|
26
|
+
".git",
|
|
27
|
+
".github",
|
|
28
|
+
".pytest_cache",
|
|
29
|
+
".venv",
|
|
30
|
+
"docs",
|
|
31
|
+
"knowledge",
|
|
32
|
+
"node_modules",
|
|
33
|
+
"oss",
|
|
34
|
+
"products",
|
|
35
|
+
"scripts",
|
|
36
|
+
"tests",
|
|
37
|
+
"tools",
|
|
38
|
+
"venv",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def slugify(value: str) -> str:
|
|
43
|
+
slug = re.sub(r"[^a-zA-Z0-9]+", "-", value.strip()).strip("-").lower()
|
|
44
|
+
return slug or "lc-process"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def validate_slug(value: str) -> str:
|
|
48
|
+
slug = slugify(value)
|
|
49
|
+
if not re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", slug):
|
|
50
|
+
raise ValueError(f"Invalid process slug: {value}")
|
|
51
|
+
return slug
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
LC_NAME_RE = re.compile(r"[a-z0-9][a-z0-9-]*")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def validate_name(value: str, kind: str = "name") -> str:
|
|
58
|
+
"""Validate a Lincoln identifier (workflow, role, skill, scenario key)."""
|
|
59
|
+
if not LC_NAME_RE.fullmatch(value):
|
|
60
|
+
raise ValueError(f"Invalid {kind}: {value!r}")
|
|
61
|
+
return value
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def assert_contained(path: Path, parent: Path, desc: str = "path") -> Path:
|
|
65
|
+
"""Raise ValueError if resolved path escapes parent directory."""
|
|
66
|
+
resolved = path.resolve()
|
|
67
|
+
base = parent.resolve()
|
|
68
|
+
if base not in resolved.parents and resolved != base:
|
|
69
|
+
raise ValueError(f"{desc} escapes allowed directory: {path}")
|
|
70
|
+
return resolved
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def atomic_write_text(path: Path, content: str, encoding: str = "utf-8") -> None:
|
|
74
|
+
"""Atomically write text to path using a temporary file and rename."""
|
|
75
|
+
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
76
|
+
try:
|
|
77
|
+
tmp.write_text(content, encoding=encoding)
|
|
78
|
+
tmp.replace(path)
|
|
79
|
+
except Exception:
|
|
80
|
+
if tmp.exists():
|
|
81
|
+
tmp.unlink()
|
|
82
|
+
raise
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def branch_slug(project_root: Path = PROJECT_ROOT) -> str:
|
|
86
|
+
try:
|
|
87
|
+
branch = subprocess.check_output(
|
|
88
|
+
["git", "branch", "--show-current"],
|
|
89
|
+
cwd=project_root,
|
|
90
|
+
text=True,
|
|
91
|
+
stderr=subprocess.DEVNULL,
|
|
92
|
+
).strip()
|
|
93
|
+
except Exception:
|
|
94
|
+
branch = ""
|
|
95
|
+
if branch.startswith("lincoln/"):
|
|
96
|
+
branch = branch.split("/", 1)[1]
|
|
97
|
+
return slugify(branch or project_root.name)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def default_process_slug(project_root: Path = PROJECT_ROOT) -> str:
|
|
101
|
+
"""Return a sensible default process package slug for a Conductor workspace."""
|
|
102
|
+
env_slug = os.environ.get("LINCOLN_PROCESS_SLUG") or os.environ.get("CONDUCTOR_WORKSPACE_NAME")
|
|
103
|
+
if env_slug:
|
|
104
|
+
return validate_slug(env_slug)
|
|
105
|
+
|
|
106
|
+
# Conductor paths commonly look like .../workspaces/<workspace-slug>/<repo-name>.
|
|
107
|
+
if project_root.parent.name and project_root.parent.parent.name == "workspaces":
|
|
108
|
+
return validate_slug(project_root.parent.name)
|
|
109
|
+
|
|
110
|
+
return validate_slug(branch_slug(project_root))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def discover_process_state_paths(project_root: Path = PROJECT_ROOT) -> list[Path]:
|
|
114
|
+
"""Find branch process package state files at <process_slug>/workflow-stage.yaml."""
|
|
115
|
+
paths: list[Path] = []
|
|
116
|
+
for candidate in project_root.iterdir():
|
|
117
|
+
if not candidate.is_dir() or candidate.name in RESERVED_PROCESS_DIRS:
|
|
118
|
+
continue
|
|
119
|
+
state_path = candidate / STATE_FILENAME
|
|
120
|
+
if state_path.is_file():
|
|
121
|
+
paths.append(state_path)
|
|
122
|
+
return sorted(paths, key=lambda p: p.stat().st_mtime, reverse=True)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def discover_solo_state_paths(project_root: Path = PROJECT_ROOT) -> list[Path]:
|
|
126
|
+
"""Find session-scoped solo workflow instances under .context/workflow/."""
|
|
127
|
+
solo_dir = project_root / SOLO_STATE_DIR
|
|
128
|
+
if not solo_dir.is_dir():
|
|
129
|
+
return []
|
|
130
|
+
paths = [p for p in solo_dir.glob("*.yaml") if p.is_file()]
|
|
131
|
+
return sorted(paths, key=lambda p: p.stat().st_mtime, reverse=True)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def resolve_state_path(path: Path | None = None, project_root: Path = PROJECT_ROOT) -> Path:
|
|
135
|
+
"""Resolve canonical process state, falling back to legacy root state files."""
|
|
136
|
+
env_path = os.environ.get("LINCOLN_STATE_FILE")
|
|
137
|
+
if path is None and env_path:
|
|
138
|
+
path = Path(env_path)
|
|
139
|
+
|
|
140
|
+
if path is not None:
|
|
141
|
+
path = path if path.is_absolute() else project_root / path
|
|
142
|
+
if path.exists():
|
|
143
|
+
return path
|
|
144
|
+
if path not in (ROOT_STATE_PATH, LEGACY_STATE_PATH):
|
|
145
|
+
return path
|
|
146
|
+
|
|
147
|
+
discovered = discover_process_state_paths(project_root)
|
|
148
|
+
if discovered:
|
|
149
|
+
return discovered[0]
|
|
150
|
+
solo = discover_solo_state_paths(project_root)
|
|
151
|
+
if solo:
|
|
152
|
+
return solo[0]
|
|
153
|
+
if ROOT_STATE_PATH.exists():
|
|
154
|
+
return ROOT_STATE_PATH
|
|
155
|
+
if LEGACY_STATE_PATH.exists():
|
|
156
|
+
return LEGACY_STATE_PATH
|
|
157
|
+
return ROOT_STATE_PATH
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def load_yaml(path: Path) -> Any:
|
|
161
|
+
return yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def get_process_slug(state: dict[str, Any] | None = None, state_path: Path | None = None) -> str:
|
|
165
|
+
if state:
|
|
166
|
+
variables = state.get("current_run", {}).get("variables", {})
|
|
167
|
+
if variables.get("process_slug"):
|
|
168
|
+
raw = str(variables["process_slug"])
|
|
169
|
+
if raw == SOLO_PROCESS_SLUG:
|
|
170
|
+
return raw
|
|
171
|
+
return validate_slug(raw)
|
|
172
|
+
if state_path and state_path.name == STATE_FILENAME and state_path.parent != PROJECT_ROOT / ".claude":
|
|
173
|
+
return validate_slug(state_path.parent.name)
|
|
174
|
+
return default_process_slug(PROJECT_ROOT)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def interpolate_process_path(path: str, state: dict[str, Any] | None = None, state_path: Path | None = None) -> str:
|
|
178
|
+
return path.replace("{process_slug}", get_process_slug(state, state_path))
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def process_package_root(
|
|
182
|
+
process_slug: str | None = None,
|
|
183
|
+
state: dict[str, Any] | None = None,
|
|
184
|
+
state_path: Path | None = None,
|
|
185
|
+
project_root: Path = PROJECT_ROOT,
|
|
186
|
+
) -> Path:
|
|
187
|
+
slug = process_slug or get_process_slug(state, state_path)
|
|
188
|
+
if slug == SOLO_PROCESS_SLUG:
|
|
189
|
+
return project_root / SOLO_PROCESS_SLUG
|
|
190
|
+
return project_root / validate_slug(slug)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def is_process_state_path(path: Path, project_root: Path = PROJECT_ROOT) -> bool:
|
|
194
|
+
try:
|
|
195
|
+
rel = path.resolve().relative_to(project_root.resolve())
|
|
196
|
+
except ValueError:
|
|
197
|
+
return False
|
|
198
|
+
return len(rel.parts) == 2 and rel.parts[1] == STATE_FILENAME and rel.parts[0] not in RESERVED_PROCESS_DIRS
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""PRD lifecycle helper: freeze snapshots, read current version, migrate legacy paths."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
13
|
+
if str(PROJECT_ROOT) not in sys.path:
|
|
14
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
15
|
+
|
|
16
|
+
from scripts.lincoln_index import VERSION_COMMENT_RE
|
|
17
|
+
from scripts.lincoln_paths import (
|
|
18
|
+
atomic_write_text,
|
|
19
|
+
get_process_slug,
|
|
20
|
+
load_yaml,
|
|
21
|
+
process_package_root,
|
|
22
|
+
resolve_state_path,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def extract_prd_version(prd_path: Path) -> str | None:
|
|
27
|
+
"""Return the version marker from an HTML PRD or a legacy Markdown PRD."""
|
|
28
|
+
if not prd_path.exists():
|
|
29
|
+
return None
|
|
30
|
+
text = prd_path.read_text(encoding="utf-8")
|
|
31
|
+
match = VERSION_COMMENT_RE.search(text)
|
|
32
|
+
return match.group(1) if match else None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Backwards-compatible alias used by early tests.
|
|
36
|
+
extract_version = extract_prd_version
|
|
37
|
+
|
|
38
|
+
VERSION_MARKER_RE = re.compile(r"\n?\s*\n?\Z", re.MULTILINE)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _error(message: str) -> None:
|
|
42
|
+
raise RuntimeError(message)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _package_root(args: argparse.Namespace) -> Path:
|
|
46
|
+
if args.package:
|
|
47
|
+
return PROJECT_ROOT / args.package
|
|
48
|
+
|
|
49
|
+
env_slug = os.environ.get("LINCOLN_PROCESS_SLUG")
|
|
50
|
+
if env_slug:
|
|
51
|
+
root = PROJECT_ROOT / env_slug
|
|
52
|
+
if root.exists():
|
|
53
|
+
return root
|
|
54
|
+
cwd_root = Path.cwd() / env_slug
|
|
55
|
+
if cwd_root.exists():
|
|
56
|
+
return cwd_root
|
|
57
|
+
return cwd_root
|
|
58
|
+
|
|
59
|
+
state_path = resolve_state_path(None, PROJECT_ROOT)
|
|
60
|
+
if state_path is not None and state_path.parent.exists():
|
|
61
|
+
try:
|
|
62
|
+
if state_path.exists():
|
|
63
|
+
state = load_yaml(state_path)
|
|
64
|
+
return process_package_root(
|
|
65
|
+
state=state, state_path=state_path, project_root=PROJECT_ROOT
|
|
66
|
+
)
|
|
67
|
+
return state_path.parent
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
_error(
|
|
72
|
+
"Could not determine package root. "
|
|
73
|
+
"Use --package or run from a Lincoln project with a state file."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _prd_path(root: Path) -> Path:
|
|
78
|
+
"""Return the primary HTML PRD path, falling back to legacy root prd.md."""
|
|
79
|
+
html_path = root / "pages" / "docs" / "prd.html"
|
|
80
|
+
if html_path.exists():
|
|
81
|
+
return html_path
|
|
82
|
+
legacy_path = root / "prd.md"
|
|
83
|
+
if legacy_path.exists():
|
|
84
|
+
return legacy_path
|
|
85
|
+
return html_path
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _snapshot_path(prd_path: Path, version: str) -> Path:
|
|
89
|
+
"""Return the immutable snapshot path next to the PRD source."""
|
|
90
|
+
if prd_path.name == "prd.html":
|
|
91
|
+
return prd_path.parent / "snapshots" / f"prd-{version}.html"
|
|
92
|
+
return prd_path.with_name(f"prd-{version}.md")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def freeze(package_root: Path | None = None, *, args: argparse.Namespace | None = None) -> Path:
|
|
96
|
+
"""Copy the PRD to an immutable versioned snapshot based on its version marker."""
|
|
97
|
+
root = package_root or (args and _package_root(args))
|
|
98
|
+
if not root:
|
|
99
|
+
_error("package_root is required")
|
|
100
|
+
prd_path = _prd_path(root)
|
|
101
|
+
if not prd_path.exists():
|
|
102
|
+
_error(f"PRD not found: {prd_path}")
|
|
103
|
+
|
|
104
|
+
version = extract_prd_version(prd_path)
|
|
105
|
+
if not version:
|
|
106
|
+
_error(f"No version marker found in {prd_path}; add '<!-- version: vX.Y -->' before freezing.")
|
|
107
|
+
|
|
108
|
+
snapshot_path = _snapshot_path(prd_path, version)
|
|
109
|
+
snapshot_path.parent.mkdir(parents=True, exist_ok=True)
|
|
110
|
+
if snapshot_path.exists():
|
|
111
|
+
_error(
|
|
112
|
+
f"Snapshot already exists: {snapshot_path}. "
|
|
113
|
+
"PRD snapshots are protected by an immutability guarantee. "
|
|
114
|
+
"Bump the version marker in the PRD and freeze again."
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
atomic_write_text(snapshot_path, prd_path.read_text(encoding="utf-8"))
|
|
118
|
+
print(f"Frozen {snapshot_path}")
|
|
119
|
+
return snapshot_path
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def current_version(package_root: Path | None = None, *, args: argparse.Namespace | None = None) -> str:
|
|
123
|
+
"""Print the version marker from the PRD."""
|
|
124
|
+
root = package_root or (args and _package_root(args))
|
|
125
|
+
if not root:
|
|
126
|
+
_error("package_root is required")
|
|
127
|
+
prd_path = _prd_path(root)
|
|
128
|
+
if not prd_path.exists():
|
|
129
|
+
_error(f"PRD not found: {prd_path}")
|
|
130
|
+
|
|
131
|
+
version = extract_prd_version(prd_path)
|
|
132
|
+
if not version:
|
|
133
|
+
_error(f"No version marker found in {prd_path}.")
|
|
134
|
+
|
|
135
|
+
print(version)
|
|
136
|
+
return version
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _render_prd_html(title: str, version: str, markdown: str) -> str:
|
|
140
|
+
"""Wrap legacy Markdown PRD content in the page-doc HTML template."""
|
|
141
|
+
template_path = PROJECT_ROOT / ".claude" / "templates" / "issue-package" / "page-doc.html.tpl"
|
|
142
|
+
if not template_path.exists():
|
|
143
|
+
return markdown
|
|
144
|
+
template = template_path.read_text(encoding="utf-8")
|
|
145
|
+
replacements = {
|
|
146
|
+
"{TITLE}": title,
|
|
147
|
+
"{NAV_GROUP}": "Docs",
|
|
148
|
+
"{VERSION}": version,
|
|
149
|
+
"{STAGE}": "clarify",
|
|
150
|
+
"{UID}": "",
|
|
151
|
+
"{MARKDOWN_SOURCE}": markdown,
|
|
152
|
+
}
|
|
153
|
+
for placeholder, value in replacements.items():
|
|
154
|
+
template = template.replace(placeholder, value)
|
|
155
|
+
return template
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def migrate(
|
|
159
|
+
package_root: Path | None = None,
|
|
160
|
+
session_id: str | None = None,
|
|
161
|
+
*,
|
|
162
|
+
args: argparse.Namespace | None = None,
|
|
163
|
+
) -> Path:
|
|
164
|
+
"""Move a legacy requirements/{session_id}/prd.md into the HTML portal."""
|
|
165
|
+
root = package_root or (args and _package_root(args))
|
|
166
|
+
if not root:
|
|
167
|
+
_error("package_root is required")
|
|
168
|
+
|
|
169
|
+
sid = session_id or (args and args.session_id)
|
|
170
|
+
if not sid:
|
|
171
|
+
_error("--session-id is required for migrate")
|
|
172
|
+
|
|
173
|
+
legacy_path = root / "requirements" / sid / "prd.md"
|
|
174
|
+
if not legacy_path.exists():
|
|
175
|
+
_error(f"Legacy PRD not found: {legacy_path}")
|
|
176
|
+
|
|
177
|
+
target_path = root / "pages" / "docs" / "prd.html"
|
|
178
|
+
if target_path.exists():
|
|
179
|
+
_error(f"Root PRD already exists: {target_path}. Manual merge required.")
|
|
180
|
+
|
|
181
|
+
content = legacy_path.read_text(encoding="utf-8")
|
|
182
|
+
version = extract_prd_version(legacy_path)
|
|
183
|
+
if not version:
|
|
184
|
+
version = "v1.0"
|
|
185
|
+
content = f"<!-- version: {version} -->\n\n" + content.lstrip("\n")
|
|
186
|
+
else:
|
|
187
|
+
# Avoid duplicating the version marker once the template injects it.
|
|
188
|
+
content = VERSION_COMMENT_RE.sub("", content, count=1).strip("\n")
|
|
189
|
+
|
|
190
|
+
html = _render_prd_html("PRD", version, content)
|
|
191
|
+
|
|
192
|
+
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
193
|
+
atomic_write_text(target_path, html)
|
|
194
|
+
legacy_path.unlink()
|
|
195
|
+
print(f"Migrated {legacy_path} -> {target_path}")
|
|
196
|
+
return target_path
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def main() -> int:
|
|
200
|
+
parser = argparse.ArgumentParser(description="Lincoln PRD lifecycle helper")
|
|
201
|
+
parser.add_argument("--package", help="Process package slug (e.g., issue-85)")
|
|
202
|
+
parser.add_argument("--session-id", help="Interview session id (required for migrate)")
|
|
203
|
+
subparsers = parser.add_subparsers(dest="action", required=True)
|
|
204
|
+
|
|
205
|
+
subparsers.add_parser("freeze", help="Freeze pages/docs/prd.html into an immutable versioned snapshot")
|
|
206
|
+
subparsers.add_parser("current-version", help="Print the current PRD version marker")
|
|
207
|
+
migrate_parser = subparsers.add_parser("migrate", help="Move legacy requirements/{session_id}/prd.md into pages/docs/prd.html")
|
|
208
|
+
migrate_parser.add_argument("--session-id", required=True, help="Legacy session directory name")
|
|
209
|
+
|
|
210
|
+
args = parser.parse_args()
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
if args.action == "freeze":
|
|
214
|
+
freeze(args=args)
|
|
215
|
+
elif args.action == "current-version":
|
|
216
|
+
current_version(args=args)
|
|
217
|
+
elif args.action == "migrate":
|
|
218
|
+
migrate(args=args)
|
|
219
|
+
except RuntimeError as exc:
|
|
220
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
221
|
+
sys.exit(1)
|
|
222
|
+
|
|
223
|
+
sys.exit(0)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
main()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Print the content of a Lincoln agent role.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 scripts/lincoln_role.py --role pm [topic words...]
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
15
|
+
|
|
16
|
+
from lincoln_paths import PROJECT_ROOT, assert_contained, validate_name # noqa: E402
|
|
17
|
+
|
|
18
|
+
AGENTS_DIR = PROJECT_ROOT / ".claude" / "agents"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main() -> int:
|
|
22
|
+
parser = argparse.ArgumentParser(description="Print a Lincoln agent role")
|
|
23
|
+
parser.add_argument("--role", required=True, help="Agent role filename stem")
|
|
24
|
+
parser.add_argument("topic", nargs="*", help="Optional topic/context")
|
|
25
|
+
args = parser.parse_args()
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
validate_name(args.role, "role name")
|
|
29
|
+
path = AGENTS_DIR / f"{args.role}.md"
|
|
30
|
+
assert_contained(path, AGENTS_DIR, "agent role path")
|
|
31
|
+
if not path.exists():
|
|
32
|
+
print(f"ERROR: agent role not found: {path}", file=sys.stderr)
|
|
33
|
+
return 1
|
|
34
|
+
|
|
35
|
+
text = path.read_text(encoding="utf-8")
|
|
36
|
+
if args.topic:
|
|
37
|
+
print(f"Topic/context: {' '.join(args.topic)}\n")
|
|
38
|
+
print(text)
|
|
39
|
+
return 0
|
|
40
|
+
except ValueError as exc:
|
|
41
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
sys.exit(main())
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Print the prompt for a Lincoln scenario.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 scripts/lincoln_scenario.py --scenario make-prd [topic words...]
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import yaml
|
|
15
|
+
|
|
16
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
17
|
+
|
|
18
|
+
from lincoln_paths import PROJECT_ROOT, validate_name # noqa: E402
|
|
19
|
+
|
|
20
|
+
SCENARIOS_PATH = PROJECT_ROOT / ".claude" / "harnesses" / "scenarios.yaml"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main() -> int:
|
|
24
|
+
parser = argparse.ArgumentParser(description="Print a Lincoln scenario prompt")
|
|
25
|
+
parser.add_argument("--scenario", required=True, help="Scenario id")
|
|
26
|
+
parser.add_argument("topic", nargs="*", help="Optional topic/context")
|
|
27
|
+
args = parser.parse_args()
|
|
28
|
+
|
|
29
|
+
if not SCENARIOS_PATH.exists():
|
|
30
|
+
print(f"ERROR: scenarios file not found: {SCENARIOS_PATH}", file=sys.stderr)
|
|
31
|
+
return 1
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
validate_name(args.scenario, "scenario id")
|
|
35
|
+
except ValueError as exc:
|
|
36
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
37
|
+
return 1
|
|
38
|
+
|
|
39
|
+
data = yaml.safe_load(SCENARIOS_PATH.read_text(encoding="utf-8")) or {}
|
|
40
|
+
scenarios = data.get("scenarios", {})
|
|
41
|
+
scenario = scenarios.get(args.scenario)
|
|
42
|
+
if not scenario:
|
|
43
|
+
available = ", ".join(sorted(scenarios))
|
|
44
|
+
print(f"ERROR: scenario '{args.scenario}' not found. Available: {available}", file=sys.stderr)
|
|
45
|
+
return 1
|
|
46
|
+
|
|
47
|
+
if args.topic:
|
|
48
|
+
print(f"Topic/context: {' '.join(args.topic)}\n")
|
|
49
|
+
print(f"# Scenario: {args.scenario}")
|
|
50
|
+
print(f"\nDescription: {scenario.get('description', '')}")
|
|
51
|
+
print(f"Recommended role: {scenario.get('role', '')}")
|
|
52
|
+
print(f"Skills: {', '.join(scenario.get('skills', []))}")
|
|
53
|
+
print(f"\nGoal: {scenario.get('goal', '')}")
|
|
54
|
+
print("\nExecute the recommended skills in order. Follow Lincoln stage gates and human_gate rules.")
|
|
55
|
+
return 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
sys.exit(main())
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Estimate the token cost of a Lincoln session-start hook output (#63).
|
|
3
|
+
|
|
4
|
+
Tries tiktoken for an accurate count; falls back to a stable heuristic
|
|
5
|
+
(CJK characters as one token each, whitespace-separated non-CJK words as
|
|
6
|
+
tokens) so the metric stays deterministic without requiring an extra dependency.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
CJK_RE = re.compile(
|
|
19
|
+
r"[一-鿿-ゟ゠-ヿ가-]"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def estimate_tokens(text: str) -> int:
|
|
24
|
+
"""Return a deterministic token estimate for *text*."""
|
|
25
|
+
try:
|
|
26
|
+
import tiktoken # type: ignore
|
|
27
|
+
|
|
28
|
+
enc = tiktoken.get_encoding("cl100k_base")
|
|
29
|
+
return len(enc.encode(text))
|
|
30
|
+
except Exception:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
cjk_tokens = len(CJK_RE.findall(text))
|
|
34
|
+
non_cjk = CJK_RE.sub(" ", text)
|
|
35
|
+
word_tokens = len(non_cjk.split())
|
|
36
|
+
return cjk_tokens + word_tokens
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def compute_metrics(text: str, stage: str = "", status: str = "") -> dict[str, Any]:
|
|
40
|
+
return {
|
|
41
|
+
"schema_version": "1.0.0",
|
|
42
|
+
"stage": stage,
|
|
43
|
+
"status": status,
|
|
44
|
+
"bytes": len(text.encode("utf-8")),
|
|
45
|
+
"chars": len(text),
|
|
46
|
+
"tokens": estimate_tokens(text),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def main(argv: list[str] | None = None) -> int:
|
|
51
|
+
parser = argparse.ArgumentParser(
|
|
52
|
+
description="Estimate token count for a Lincoln session-start output."
|
|
53
|
+
)
|
|
54
|
+
parser.add_argument("--input", type=Path, required=True, help="File to analyze.")
|
|
55
|
+
parser.add_argument("--output", type=Path, required=True, help="JSON output path.")
|
|
56
|
+
parser.add_argument("--stage", default="", help="Current stage id.")
|
|
57
|
+
parser.add_argument("--status", default="", help="Current stage status.")
|
|
58
|
+
args = parser.parse_args(argv)
|
|
59
|
+
|
|
60
|
+
text = args.input.read_text(encoding="utf-8")
|
|
61
|
+
metrics = compute_metrics(text, stage=args.stage, status=args.status)
|
|
62
|
+
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
args.output.write_text(
|
|
64
|
+
json.dumps(metrics, ensure_ascii=False, indent=2) + "\n",
|
|
65
|
+
encoding="utf-8",
|
|
66
|
+
)
|
|
67
|
+
return 0
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
sys.exit(main())
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Print the content of a Lincoln skill.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 scripts/lincoln_skill_prompt.py --skill lc-first-principles
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
15
|
+
|
|
16
|
+
from lincoln_paths import PROJECT_ROOT, assert_contained, validate_name # noqa: E402
|
|
17
|
+
|
|
18
|
+
SKILLS_DIR = PROJECT_ROOT / ".claude" / "skills"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main() -> int:
|
|
22
|
+
parser = argparse.ArgumentParser(description="Print a Lincoln skill prompt")
|
|
23
|
+
parser.add_argument("--skill", required=True, help="Skill folder name")
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
validate_name(args.skill, "skill name")
|
|
28
|
+
skill_dir = SKILLS_DIR / args.skill
|
|
29
|
+
skill_md = skill_dir / "SKILL.md"
|
|
30
|
+
assert_contained(skill_md, SKILLS_DIR, "skill path")
|
|
31
|
+
if not skill_md.exists():
|
|
32
|
+
print(f"ERROR: skill not found: {skill_dir}", file=sys.stderr)
|
|
33
|
+
return 1
|
|
34
|
+
|
|
35
|
+
parts = [skill_md.read_text(encoding="utf-8")]
|
|
36
|
+
prompt_path = skill_dir / "prompts" / "main.md"
|
|
37
|
+
if prompt_path.exists():
|
|
38
|
+
parts.append(prompt_path.read_text(encoding="utf-8"))
|
|
39
|
+
|
|
40
|
+
print("\n\n".join(parts))
|
|
41
|
+
return 0
|
|
42
|
+
except ValueError as exc:
|
|
43
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
44
|
+
return 1
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
sys.exit(main())
|