its-magic 0.1.2-9 → 0.1.3-0
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/README.md +1602 -755
- package/bin/its-magic.js +121 -11
- package/bin/postinstall.js +93 -0
- package/installer.ps1 +759 -0
- package/installer.py +1036 -0
- package/installer.sh +649 -0
- package/package.json +18 -14
- package/scripts/check_intake_template_parity.py +312 -0
- package/scripts/doc_profile_lib.py +415 -0
- package/scripts/guard_installer_publish.py +88 -0
- package/scripts/intake_bug_routing_guard.py +67 -0
- package/scripts/intake_evidence_lib.py +802 -0
- package/scripts/intake_evidence_validate.py +73 -0
- package/scripts/materialize_codebase_map.py +184 -0
- package/scripts/remote_config_summary.py +243 -0
- package/template/.cursor/agents/curator.mdc +35 -0
- package/template/.cursor/agents/dev.mdc +29 -0
- package/template/.cursor/agents/po.mdc +141 -0
- package/template/.cursor/agents/qa.mdc +28 -0
- package/template/.cursor/agents/release.mdc +28 -0
- package/template/.cursor/agents/security.mdc +98 -0
- package/template/.cursor/agents/tech-lead.mdc +57 -0
- package/template/.cursor/commands/architecture.md +127 -0
- package/template/.cursor/commands/ask.md +55 -0
- package/template/.cursor/commands/auto.md +533 -0
- package/template/.cursor/commands/discovery.md +47 -0
- package/template/.cursor/commands/execute.md +343 -0
- package/template/.cursor/commands/intake.md +323 -0
- package/template/.cursor/commands/map-codebase.md +46 -0
- package/template/.cursor/commands/memory-audit.md +175 -0
- package/template/.cursor/commands/milestone-complete.md +51 -0
- package/template/.cursor/commands/milestone-start.md +59 -0
- package/template/.cursor/commands/pause.md +64 -0
- package/{.cursor/commands/gsd-phase-context.md → template/.cursor/commands/phase-context.md} +6 -2
- package/template/.cursor/commands/plan-verify.md +34 -0
- package/template/.cursor/commands/qa.md +226 -0
- package/template/.cursor/commands/quick.md +41 -0
- package/template/.cursor/commands/refresh-context.md +82 -0
- package/template/.cursor/commands/release.md +523 -0
- package/template/.cursor/commands/research.md +49 -0
- package/template/.cursor/commands/resume.md +67 -0
- package/template/.cursor/commands/security-review.md +81 -0
- package/template/.cursor/commands/sprint-plan.md +103 -0
- package/template/.cursor/commands/status-reconcile.md +95 -0
- package/template/.cursor/commands/verify-work.md +152 -0
- package/template/.cursor/dev-environment.json.example +22 -0
- package/{.cursor → template/.cursor}/hooks/README.md +3 -2
- package/{.cursor/hooks/gsd-hook.py → template/.cursor/hooks/hook.py} +15 -6
- package/template/.cursor/hooks.json +26 -0
- package/template/.cursor/remote.json +31 -0
- package/template/.cursor/rules/caveman.mdc +184 -0
- package/template/.cursor/rules/coding-standards.mdc +39 -0
- package/template/.cursor/rules/core.mdc +111 -0
- package/template/.cursor/rules/handoffs.mdc +27 -0
- package/template/.cursor/rules/quality.mdc +49 -0
- package/template/.cursor/scratchpad.local.example.md +323 -0
- package/template/.cursor/scratchpad.md +324 -0
- package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/SKILL.md +2 -2
- package/template/.cursorignore +6 -0
- package/template/.env.example +28 -0
- package/template/.github/workflows/ci.yml +194 -0
- package/{.github → template/.github}/workflows/deploy.yml +5 -2
- package/template/.its-magic-version +1 -0
- package/template/CHANGELOG.md +11 -0
- package/template/README.md +1602 -0
- package/template/decisions/DEC-0001.md +11 -0
- package/template/decisions/DEC-0002.md +11 -0
- package/template/docs/developer/README.md +44 -0
- package/template/docs/engineering/architecture.md +9 -0
- package/template/docs/engineering/artifact-ordering-policy.md +48 -0
- package/template/docs/engineering/artifact-ownership-policy.md +57 -0
- package/template/docs/engineering/auto-orchestration-reference.md +1211 -0
- package/{docs → template/docs}/engineering/codebase-map.md +0 -5
- package/template/docs/engineering/compatibility-report.md +20 -0
- package/template/docs/engineering/compatibility-signals.md +7 -0
- package/template/docs/engineering/component-scope-report.md +16 -0
- package/template/docs/engineering/component-scope.md +19 -0
- package/template/docs/engineering/context/installer-owned-paths.manifest +96 -0
- package/template/docs/engineering/context/readme-section-affinity.json +30 -0
- package/template/docs/engineering/decisions.md +16 -0
- package/template/docs/engineering/legacy-drift-audit.md +21 -0
- package/template/docs/engineering/manifests/components/api-gateway.manifest.yaml +12 -0
- package/template/docs/engineering/manifests/registry.manifest.yaml +20 -0
- package/template/docs/engineering/manifests/repo.manifest.yaml +11 -0
- package/template/docs/engineering/phase-context.md +16 -0
- package/template/docs/engineering/release-targets.json +123 -0
- package/template/docs/engineering/research.md +29 -0
- package/template/docs/engineering/runbook.md +2391 -0
- package/template/docs/engineering/runtime-connectivity.md +81 -0
- package/template/docs/engineering/security-review.md +33 -0
- package/template/docs/engineering/spec-pack/README.md +20 -0
- package/template/docs/engineering/state-archive/README.md +15 -0
- package/template/docs/engineering/state.md +20 -0
- package/template/docs/engineering/status-normalization-report.md +19 -0
- package/template/docs/engineering/token-cost-parity-manifest.md +16 -0
- package/template/docs/engineering/us-0084-remote-e2e.md +44 -0
- package/template/docs/product/acceptance.md +1 -0
- package/template/docs/product/backlog.md +5 -0
- package/template/docs/product/vision.md +11 -0
- package/template/docs/user-guides/README.md +40 -0
- package/{handoffs → template/handoffs}/dev_to_qa.md +0 -3
- package/{handoffs → template/handoffs}/po_to_tl.md +0 -3
- package/{handoffs → template/handoffs}/qa_to_dev.md +1 -2
- package/template/handoffs/release_notes.md +51 -0
- package/template/handoffs/release_queue.md +47 -0
- package/template/handoffs/release_to_dev.md +31 -0
- package/template/handoffs/releases/Sxxxx-release-notes.md +62 -0
- package/template/handoffs/releases/vX.Y.Z-release-notes.md.example +21 -0
- package/template/handoffs/resume_brief.md +14 -0
- package/{handoffs → template/handoffs}/tl_to_dev.md +0 -2
- package/template/handoffs/token_cost_runs/README.md +26 -0
- package/template/its_magic/.its-magic-version +1 -0
- package/template/its_magic/README.md +9 -0
- package/template/scripts/auto_outer_driver.py +521 -0
- package/template/scripts/caveman_compress_input.py +903 -0
- package/template/scripts/check_downstream_ci_guard.py +67 -0
- package/template/scripts/check_intake_template_parity.py +312 -0
- package/template/scripts/check_token_cost_parity.py +69 -0
- package/template/scripts/dev_environment_lib.py +601 -0
- package/template/scripts/doc_profile_lib.py +415 -0
- package/template/scripts/downstream_ci_guard_lib.py +222 -0
- package/template/scripts/enforce-triad-hot-surface.py +753 -0
- package/template/scripts/guard_installer_publish.py +88 -0
- package/template/scripts/intake_bug_resume_brief_refresh.py +303 -0
- package/template/scripts/intake_bug_routing_guard.py +67 -0
- package/template/scripts/intake_evidence_lib.py +802 -0
- package/template/scripts/intake_evidence_validate.py +73 -0
- package/template/scripts/materialize_codebase_map.py +184 -0
- package/template/scripts/pack_json_validate.py +130 -0
- package/template/scripts/project_readme_coverage_lib.py +417 -0
- package/template/scripts/readme_feature_coverage_lib.py +608 -0
- package/{scripts → template/scripts}/release-all.sh +27 -4
- package/template/scripts/release_changelog_backfill.py +153 -0
- package/template/scripts/release_changelog_lib.py +544 -0
- package/template/scripts/release_changelog_validate.py +134 -0
- package/template/scripts/remote_config_summary.py +243 -0
- package/template/scripts/sync_push_gates.py +198 -0
- package/template/scripts/token_cost_compare.py +40 -0
- package/template/scripts/token_cost_lib.py +108 -0
- package/template/scripts/uat_probe_lib.py +868 -0
- package/template/scripts/validate-and-push.ps1 +280 -0
- package/template/scripts/validate-and-push.sh +243 -0
- package/template/scripts/validate_doc_profile.py +103 -0
- package/template/scripts/validate_project_readme_coverage.py +151 -0
- package/template/scripts/validate_readme_feature_coverage.py +140 -0
- package/template/sprints/S0001/progress.md +1 -0
- package/template/sprints/S0001/qa-findings.md +9 -0
- package/template/sprints/S0001/release-findings.md +24 -0
- package/template/sprints/S0001/sprint.md +9 -0
- package/template/sprints/S0001/summary.md +7 -0
- package/template/sprints/S0001/tasks.md +1 -0
- package/template/sprints/S0001/uat.json +6 -0
- package/template/sprints/S0001/uat.md +5 -0
- package/template/sprints/quick/Q0001/summary.md +1 -0
- package/{sprints → template/sprints}/quick/Q0001/task.json +0 -1
- package/.cursor/agents/curator.mdc +0 -21
- package/.cursor/agents/dev.mdc +0 -20
- package/.cursor/agents/po.mdc +0 -19
- package/.cursor/agents/qa.mdc +0 -19
- package/.cursor/agents/release.mdc +0 -19
- package/.cursor/agents/tech-lead.mdc +0 -21
- package/.cursor/commands/gsd-architecture.md +0 -29
- package/.cursor/commands/gsd-auto.md +0 -27
- package/.cursor/commands/gsd-discovery.md +0 -27
- package/.cursor/commands/gsd-execute.md +0 -32
- package/.cursor/commands/gsd-intake.md +0 -28
- package/.cursor/commands/gsd-map-codebase.md +0 -25
- package/.cursor/commands/gsd-milestone-complete.md +0 -24
- package/.cursor/commands/gsd-milestone-start.md +0 -26
- package/.cursor/commands/gsd-pause.md +0 -25
- package/.cursor/commands/gsd-plan-verify.md +0 -26
- package/.cursor/commands/gsd-qa.md +0 -28
- package/.cursor/commands/gsd-quick.md +0 -24
- package/.cursor/commands/gsd-refresh-context.md +0 -26
- package/.cursor/commands/gsd-release.md +0 -29
- package/.cursor/commands/gsd-research.md +0 -28
- package/.cursor/commands/gsd-resume.md +0 -26
- package/.cursor/commands/gsd-sprint-plan.md +0 -30
- package/.cursor/commands/gsd-verify-work.md +0 -25
- package/.cursor/hooks.json +0 -26
- package/.cursor/plans/cursor-gsd-team-kit_8cfee9b8.plan.md +0 -57
- package/.cursor/remote.json +0 -18
- package/.cursor/rules/gsd-core.mdc +0 -18
- package/.cursor/rules/gsd-handoffs.mdc +0 -10
- package/.cursor/rules/gsd-quality.mdc +0 -15
- package/.cursor/scratchpad.md +0 -34
- package/.github/workflows/ci.yml +0 -47
- package/decisions/DEC-0001.md +0 -21
- package/decisions/DEC-0002.md +0 -21
- package/docs/engineering/architecture.md +0 -354
- package/docs/engineering/decisions.md +0 -6
- package/docs/engineering/research.md +0 -11
- package/docs/engineering/runbook.md +0 -32
- package/docs/engineering/state.md +0 -33
- package/docs/product/acceptance.md +0 -6
- package/docs/product/backlog.md +0 -7
- package/docs/product/vision.md +0 -46
- package/gsd-installer.ps1 +0 -189
- package/gsd-installer.py +0 -195
- package/gsd-installer.sh +0 -201
- package/handoffs/release_notes.md +0 -14
- package/handoffs/resume_brief.md +0 -8
- package/milestones/M0001/milestone.json +0 -7
- package/milestones/M0001/phases.json +0 -9
- package/milestones/M0001/progress.md +0 -3
- package/milestones/M0001/summary.md +0 -3
- package/scripts/generate-release-notes.ps1 +0 -74
- package/scripts/generate-release-notes.sh +0 -63
- package/scripts/release-all.ps1 +0 -423
- package/sprints/S0001/progress.md +0 -4
- package/sprints/S0001/qa-findings.md +0 -113
- package/sprints/S0001/sprint.md +0 -70
- package/sprints/S0001/summary.md +0 -46
- package/sprints/S0001/tasks.md +0 -35
- package/sprints/S0001/uat.json +0 -8
- package/sprints/S0001/uat.md +0 -8
- package/sprints/quick/Q0001/summary.md +0 -3
- /package/{.cursor/rules/gsd-escalation.mdc → template/.cursor/rules/escalation.mdc} +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/acceptance.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/acceptance.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/architecture.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/architecture.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/decision.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/decision.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/handoff.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/handoff.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/milestone.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/phase-context.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/plan-verify.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/sprint.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/sprint.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/story.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/story.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/uat.json +0 -0
- /package/{docs → template/docs}/engineering/context/phase-template.json +0 -0
- /package/{docs → template/docs}/engineering/dependencies.json +0 -0
- /package/{sprints → template/sprints}/S0001/plan-verify.json +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Validate intake_evidence JSON bundles (US-0078 / US-0083 / DEC-0060 / DEC-0067).
|
|
4
|
+
|
|
5
|
+
Used by PO workflow preflight and CI fixtures.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
16
|
+
_REPO_ROOT = os.path.normpath(os.path.join(_SCRIPT_DIR, ".."))
|
|
17
|
+
if _SCRIPT_DIR not in sys.path:
|
|
18
|
+
sys.path.insert(0, _SCRIPT_DIR)
|
|
19
|
+
|
|
20
|
+
import intake_evidence_lib # noqa: E402
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main() -> int:
|
|
24
|
+
p = argparse.ArgumentParser(description="Validate intake_evidence JSON (US-0078/US-0083).")
|
|
25
|
+
p.add_argument("--file", help="Path to JSON file containing one intake_evidence object.")
|
|
26
|
+
p.add_argument(
|
|
27
|
+
"--stdin",
|
|
28
|
+
action="store_true",
|
|
29
|
+
help="Read JSON object from stdin.",
|
|
30
|
+
)
|
|
31
|
+
p.add_argument(
|
|
32
|
+
"--self-test",
|
|
33
|
+
action="store_true",
|
|
34
|
+
help="Run library sanity checks and exit.",
|
|
35
|
+
)
|
|
36
|
+
args = p.parse_args()
|
|
37
|
+
|
|
38
|
+
if args.self_test:
|
|
39
|
+
intake_evidence_lib.self_test()
|
|
40
|
+
print("[INTAKE_EVIDENCE_SELF_TEST_OK]")
|
|
41
|
+
return 0
|
|
42
|
+
|
|
43
|
+
raw = ""
|
|
44
|
+
if args.file:
|
|
45
|
+
with open(os.path.abspath(args.file), encoding="utf-8") as f:
|
|
46
|
+
raw = f.read()
|
|
47
|
+
elif args.stdin:
|
|
48
|
+
raw = sys.stdin.read()
|
|
49
|
+
else:
|
|
50
|
+
print("error: specify --file, --stdin, or --self-test", file=sys.stderr)
|
|
51
|
+
return 2
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
bundle = json.loads(raw)
|
|
55
|
+
except json.JSONDecodeError as e:
|
|
56
|
+
print(f"error: invalid JSON: {e}", file=sys.stderr)
|
|
57
|
+
return 2
|
|
58
|
+
|
|
59
|
+
if not isinstance(bundle, dict):
|
|
60
|
+
print("error: root must be a JSON object", file=sys.stderr)
|
|
61
|
+
return 2
|
|
62
|
+
|
|
63
|
+
r = intake_evidence_lib.validate_intake_evidence(bundle)
|
|
64
|
+
if r.ok:
|
|
65
|
+
print("[INTAKE_EVIDENCE_VALIDATION_OK]")
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
print(intake_evidence_lib.format_blocked_message(r), file=sys.stderr)
|
|
69
|
+
return 1
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Idempotent codebase map bootstrap (US-0082 / DEC-0065).
|
|
3
|
+
|
|
4
|
+
Writes only docs/engineering/codebase-map.md and docs/engineering/dependencies.json
|
|
5
|
+
per /map-codebase contract. Does not append docs/engineering/state.md.
|
|
6
|
+
|
|
7
|
+
Diagnostics on stdout use deterministic tokens: [CODEBASE_MAP_OK] ... and CODEBASE_MAP_BLOCKED:...
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
BOOTSTRAP_SENTINEL = "<!-- its-magic:codebase-map-bootstrap v1 -->"
|
|
18
|
+
|
|
19
|
+
REMEDIATION = (
|
|
20
|
+
"Remediation: run `/map-codebase` for a full pass; see "
|
|
21
|
+
"`docs/engineering/runbook.md` (Codebase map bootstrap) and "
|
|
22
|
+
"`docs/engineering/architecture.md` (# US-0082)."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _bootstrap_map_markdown() -> str:
|
|
27
|
+
lines = [
|
|
28
|
+
"# Codebase Map",
|
|
29
|
+
"",
|
|
30
|
+
BOOTSTRAP_SENTINEL,
|
|
31
|
+
"",
|
|
32
|
+
"This is a **bootstrap** codebase map created by the lifecycle materializer "
|
|
33
|
+
"(**US-0082** / **DEC-0065**). It satisfies the “map exists” contract for fresh repos.",
|
|
34
|
+
"",
|
|
35
|
+
"For a full repository analysis, run **`/map-codebase`** (explicit/manual).",
|
|
36
|
+
"",
|
|
37
|
+
"## Stack",
|
|
38
|
+
"",
|
|
39
|
+
"| Aspect | Detail |",
|
|
40
|
+
"|--------|--------|",
|
|
41
|
+
"| (pending) | Run `/map-codebase` or edit this table after local analysis |",
|
|
42
|
+
"",
|
|
43
|
+
"## Entry Points",
|
|
44
|
+
"",
|
|
45
|
+
"| Entry | File | Purpose |",
|
|
46
|
+
"|-------|------|---------|",
|
|
47
|
+
"| (pending) | — | Run `/map-codebase` to populate |",
|
|
48
|
+
"",
|
|
49
|
+
"## Next steps",
|
|
50
|
+
"",
|
|
51
|
+
"1. Run **`/map-codebase`** for a deep pass, **or**",
|
|
52
|
+
"2. Edit this file directly with project-specific structure.",
|
|
53
|
+
"",
|
|
54
|
+
]
|
|
55
|
+
return "\n".join(lines)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _bootstrap_dependencies_obj() -> dict:
|
|
59
|
+
return {"libraries": [], "runtime": [], "tooling": []}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _bootstrap_dependencies_text() -> str:
|
|
63
|
+
return json.dumps(_bootstrap_dependencies_obj(), sort_keys=True, indent=2) + "\n"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _normalize_newlines(text: str) -> str:
|
|
67
|
+
return text.replace("\r\n", "\n").replace("\r", "\n")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _json_stable(obj: object) -> str:
|
|
71
|
+
return json.dumps(obj, sort_keys=True, separators=(",", ":"))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def sync_map(map_path: Path, desired: str) -> tuple[str, bool]:
|
|
75
|
+
"""Returns (status_token, content_mutated)."""
|
|
76
|
+
if not map_path.is_file():
|
|
77
|
+
map_path.write_text(desired, encoding="utf-8", newline="\n")
|
|
78
|
+
return "created", True
|
|
79
|
+
existing = _normalize_newlines(map_path.read_text(encoding="utf-8"))
|
|
80
|
+
if BOOTSTRAP_SENTINEL in existing:
|
|
81
|
+
if existing == desired:
|
|
82
|
+
return "noop", False
|
|
83
|
+
map_path.write_text(desired, encoding="utf-8", newline="\n")
|
|
84
|
+
return "refreshed_bootstrap", True
|
|
85
|
+
return "preserved_existing", False
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def sync_dependencies(deps_path: Path, map_allows_bootstrap_deps: bool) -> tuple[str, bool]:
|
|
89
|
+
"""When map_allows_bootstrap_deps, deps may be created/updated to bootstrap empty schema."""
|
|
90
|
+
desired_txt = _bootstrap_dependencies_text()
|
|
91
|
+
want_obj = _bootstrap_dependencies_obj()
|
|
92
|
+
want_stable = _json_stable(want_obj)
|
|
93
|
+
|
|
94
|
+
if not deps_path.is_file():
|
|
95
|
+
deps_path.write_text(desired_txt, encoding="utf-8", newline="\n")
|
|
96
|
+
return "deps_created", True
|
|
97
|
+
|
|
98
|
+
raw = deps_path.read_text(encoding="utf-8")
|
|
99
|
+
try:
|
|
100
|
+
cur = json.loads(raw)
|
|
101
|
+
except json.JSONDecodeError:
|
|
102
|
+
if map_allows_bootstrap_deps:
|
|
103
|
+
deps_path.write_text(desired_txt, encoding="utf-8", newline="\n")
|
|
104
|
+
return "deps_repaired", True
|
|
105
|
+
return "deps_preserved_existing", False
|
|
106
|
+
|
|
107
|
+
if _json_stable(cur) == want_stable:
|
|
108
|
+
return "deps_noop", False
|
|
109
|
+
|
|
110
|
+
if map_allows_bootstrap_deps:
|
|
111
|
+
deps_path.write_text(desired_txt, encoding="utf-8", newline="\n")
|
|
112
|
+
return "deps_refreshed", True
|
|
113
|
+
return "deps_preserved_existing", False
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def main(argv: list[str] | None = None) -> int:
|
|
117
|
+
p = argparse.ArgumentParser(description="Materialize idempotent codebase map bootstrap.")
|
|
118
|
+
p.add_argument("--repo", default=".", help="Repository root")
|
|
119
|
+
p.add_argument(
|
|
120
|
+
"--trigger",
|
|
121
|
+
choices=("architecture", "map-codebase", "refresh-context"),
|
|
122
|
+
default="architecture",
|
|
123
|
+
help="Lifecycle trigger label (stdout diagnostics only)",
|
|
124
|
+
)
|
|
125
|
+
p.add_argument("--dry-run", action="store_true", help="Print actions; do not write files")
|
|
126
|
+
p.add_argument(
|
|
127
|
+
"--simulate-block",
|
|
128
|
+
metavar="SUBREASON",
|
|
129
|
+
default=None,
|
|
130
|
+
help="Emit CODEBASE_MAP_BLOCKED:SUBREASON and exit 2 (tests)",
|
|
131
|
+
)
|
|
132
|
+
p.add_argument(
|
|
133
|
+
"--check-present",
|
|
134
|
+
action="store_true",
|
|
135
|
+
help="Read-only: exit 0 if codebase-map.md exists, else CODEBASE_MAP_MISSING + exit 2",
|
|
136
|
+
)
|
|
137
|
+
args = p.parse_args(argv)
|
|
138
|
+
|
|
139
|
+
if args.simulate_block:
|
|
140
|
+
print(f"[CODEBASE_MAP_BLOCKED:{args.simulate_block}] trigger={args.trigger}")
|
|
141
|
+
print(REMEDIATION)
|
|
142
|
+
return 2
|
|
143
|
+
|
|
144
|
+
if os.environ.get("CODEBASE_MAP_LIFECYCLE_SKIP", "").strip() in ("1", "true", "yes"):
|
|
145
|
+
print(f"[CODEBASE_MAP_BLOCKED:policy_skip] trigger={args.trigger}")
|
|
146
|
+
print(REMEDIATION)
|
|
147
|
+
return 2
|
|
148
|
+
|
|
149
|
+
root = Path(args.repo).resolve()
|
|
150
|
+
eng = root / "docs" / "engineering"
|
|
151
|
+
map_path = eng / "codebase-map.md"
|
|
152
|
+
deps_path = eng / "dependencies.json"
|
|
153
|
+
|
|
154
|
+
if args.check_present:
|
|
155
|
+
if map_path.is_file():
|
|
156
|
+
print(f"[CODEBASE_MAP_OK] check_present trigger={args.trigger} path={map_path}")
|
|
157
|
+
return 0
|
|
158
|
+
print("[CODEBASE_MAP_MISSING]")
|
|
159
|
+
print(REMEDIATION)
|
|
160
|
+
return 2
|
|
161
|
+
|
|
162
|
+
desired_map = _bootstrap_map_markdown()
|
|
163
|
+
|
|
164
|
+
if args.dry_run:
|
|
165
|
+
print(f"[CODEBASE_MAP_OK] dry_run trigger={args.trigger} map={map_path} deps={deps_path}")
|
|
166
|
+
return 0
|
|
167
|
+
|
|
168
|
+
eng.mkdir(parents=True, exist_ok=True)
|
|
169
|
+
|
|
170
|
+
map_status, _ = sync_map(map_path, desired_map)
|
|
171
|
+
print(f"[CODEBASE_MAP_OK] {map_status} trigger={args.trigger} path={map_path}")
|
|
172
|
+
|
|
173
|
+
map_text = _normalize_newlines(map_path.read_text(encoding="utf-8"))
|
|
174
|
+
map_allows_bootstrap_deps = BOOTSTRAP_SENTINEL in map_text
|
|
175
|
+
|
|
176
|
+
dep_status, _ = sync_dependencies(deps_path, map_allows_bootstrap_deps)
|
|
177
|
+
if dep_status not in ("deps_noop", "deps_preserved_existing"):
|
|
178
|
+
print(f"[CODEBASE_MAP_OK] {dep_status} trigger={args.trigger} path={deps_path}")
|
|
179
|
+
|
|
180
|
+
return 0
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
if __name__ == "__main__":
|
|
184
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Summarize .cursor/remote.json for operators (US-0084 / US-0064 alignment).
|
|
4
|
+
|
|
5
|
+
Stdout: non-secret labels and env-reference names only (no key material).
|
|
6
|
+
Stderr: errors and skip reasons.
|
|
7
|
+
|
|
8
|
+
Exit codes:
|
|
9
|
+
0 OK or REMOTE_EXECUTION=0 skip (DEC-0070)
|
|
10
|
+
1 usage / CLI error
|
|
11
|
+
2 config missing or unreadable
|
|
12
|
+
3 invalid JSON
|
|
13
|
+
4 schema / contract mismatch vs runbook remote.json contract
|
|
14
|
+
5 reserved (unused; DEC-0070 maps REMOTE_EXECUTION=0 to 0)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
import sys
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
ENV_VAR_NAME = re.compile(r"^[A-Z][A-Z0-9_]*$")
|
|
28
|
+
ALLOWED_TYPES = frozenset({"docker", "ssh", "vm"})
|
|
29
|
+
AUTH_MODES = frozenset({"none", "env"})
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _truthy_remote_execution(raw: str | None) -> bool:
|
|
33
|
+
if raw is None:
|
|
34
|
+
return False
|
|
35
|
+
return raw.strip() in {"1", "true", "TRUE", "yes", "YES", "on", "ON"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _parse_args(argv: list[str]) -> tuple[argparse.Namespace, list[str]]:
|
|
39
|
+
p = argparse.ArgumentParser(description=__doc__.split("\n\n")[0], add_help=True)
|
|
40
|
+
p.add_argument(
|
|
41
|
+
"--config",
|
|
42
|
+
default=None,
|
|
43
|
+
help="Path to remote JSON (default: REMOTE_CONFIG env or .cursor/remote.json)",
|
|
44
|
+
)
|
|
45
|
+
return p.parse_known_args(argv)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _config_path(explicit: str | None) -> Path:
|
|
49
|
+
if explicit:
|
|
50
|
+
return Path(explicit).expanduser()
|
|
51
|
+
env = os.environ.get("REMOTE_CONFIG")
|
|
52
|
+
if env:
|
|
53
|
+
return Path(env).expanduser()
|
|
54
|
+
return Path(".cursor/remote.json")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _validate_env_ref(name: str, value: Any, prefix: str) -> str | None:
|
|
58
|
+
if not isinstance(value, str):
|
|
59
|
+
return f"{prefix}: {name} must be a string env-reference name"
|
|
60
|
+
if not ENV_VAR_NAME.match(value):
|
|
61
|
+
return f"{prefix}: {name} must look like an env var name (A-Z_0-9)"
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def validate_and_summarize(path: Path, data: dict[str, Any]) -> tuple[list[str], list[str]]:
|
|
66
|
+
"""Return (stdout_lines, error_messages)."""
|
|
67
|
+
errs: list[str] = []
|
|
68
|
+
out: list[str] = []
|
|
69
|
+
out.append(f"remote_config={path.as_posix()}")
|
|
70
|
+
|
|
71
|
+
if not isinstance(data.get("version"), int):
|
|
72
|
+
errs.append("[REMOTE_CONFIG_ERROR] root.version: expected integer")
|
|
73
|
+
if not isinstance(data.get("defaultTarget"), str) or not data["defaultTarget"]:
|
|
74
|
+
errs.append("[REMOTE_CONFIG_ERROR] root.defaultTarget: expected non-empty string")
|
|
75
|
+
targets = data.get("targets")
|
|
76
|
+
if not isinstance(targets, list) or not targets:
|
|
77
|
+
errs.append("[REMOTE_CONFIG_ERROR] root.targets: expected non-empty array")
|
|
78
|
+
|
|
79
|
+
if errs:
|
|
80
|
+
return out, errs
|
|
81
|
+
|
|
82
|
+
by_id: dict[str, dict[str, Any]] = {}
|
|
83
|
+
for i, t in enumerate(targets):
|
|
84
|
+
prefix = f"targets[{i}]"
|
|
85
|
+
if not isinstance(t, dict):
|
|
86
|
+
errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}: expected object")
|
|
87
|
+
continue
|
|
88
|
+
tid = t.get("id")
|
|
89
|
+
if not isinstance(tid, str) or not tid:
|
|
90
|
+
errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.id: expected non-empty string")
|
|
91
|
+
ttype = t.get("type")
|
|
92
|
+
if ttype not in ALLOWED_TYPES:
|
|
93
|
+
errs.append(
|
|
94
|
+
f"[REMOTE_CONFIG_ERROR] {prefix}.type: expected one of {sorted(ALLOWED_TYPES)}"
|
|
95
|
+
)
|
|
96
|
+
if not isinstance(t.get("enabled"), bool):
|
|
97
|
+
errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.enabled: expected boolean")
|
|
98
|
+
if not isinstance(t.get("host"), str) or not t["host"]:
|
|
99
|
+
errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.host: expected non-empty string")
|
|
100
|
+
port = t.get("port")
|
|
101
|
+
if not isinstance(port, int) or not (1 <= port <= 65535):
|
|
102
|
+
errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.port: expected integer 1..65535")
|
|
103
|
+
if not isinstance(t.get("workspaceRoot"), str) or not t["workspaceRoot"]:
|
|
104
|
+
errs.append(
|
|
105
|
+
f"[REMOTE_CONFIG_ERROR] {prefix}.workspaceRoot: expected non-empty string"
|
|
106
|
+
)
|
|
107
|
+
auth = t.get("auth")
|
|
108
|
+
if auth is not None:
|
|
109
|
+
if not isinstance(auth, dict):
|
|
110
|
+
errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.auth: expected object")
|
|
111
|
+
else:
|
|
112
|
+
mode = auth.get("mode")
|
|
113
|
+
if mode not in AUTH_MODES:
|
|
114
|
+
errs.append(
|
|
115
|
+
f"[REMOTE_CONFIG_ERROR] {prefix}.auth.mode: expected one of {sorted(AUTH_MODES)}"
|
|
116
|
+
)
|
|
117
|
+
elif mode == "env":
|
|
118
|
+
env_keys = (
|
|
119
|
+
"tokenEnv",
|
|
120
|
+
"passwordEnv",
|
|
121
|
+
"privateKeyPathEnv",
|
|
122
|
+
"usernameEnv",
|
|
123
|
+
)
|
|
124
|
+
any_ref = False
|
|
125
|
+
for k in env_keys:
|
|
126
|
+
if k not in auth:
|
|
127
|
+
continue
|
|
128
|
+
any_ref = True
|
|
129
|
+
err = _validate_env_ref(k, auth[k], f"{prefix}.auth")
|
|
130
|
+
if err:
|
|
131
|
+
errs.append(f"[REMOTE_CONFIG_ERROR] {err}")
|
|
132
|
+
if not any_ref:
|
|
133
|
+
errs.append(
|
|
134
|
+
f"[REMOTE_CONFIG_ERROR] {prefix}.auth: mode=env requires at least one *Env field"
|
|
135
|
+
)
|
|
136
|
+
if isinstance(tid, str) and tid:
|
|
137
|
+
by_id[tid] = t
|
|
138
|
+
|
|
139
|
+
default = data["defaultTarget"]
|
|
140
|
+
if default not in by_id:
|
|
141
|
+
errs.append(
|
|
142
|
+
f"[REMOTE_CONFIG_ERROR] defaultTarget={default!r} not found in targets[].id"
|
|
143
|
+
)
|
|
144
|
+
elif not by_id[default].get("enabled"):
|
|
145
|
+
errs.append(
|
|
146
|
+
f"[REMOTE_CONFIG_ERROR] defaultTarget={default!r} must reference an enabled target"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if errs:
|
|
150
|
+
return out, errs
|
|
151
|
+
|
|
152
|
+
out.append(f"defaultTarget={data['defaultTarget']}")
|
|
153
|
+
for t in targets:
|
|
154
|
+
assert isinstance(t, dict)
|
|
155
|
+
tid = t["id"]
|
|
156
|
+
parts = [
|
|
157
|
+
f"id={tid}",
|
|
158
|
+
f"type={t['type']}",
|
|
159
|
+
f"enabled={t['enabled']!s}",
|
|
160
|
+
f"host={t['host']}",
|
|
161
|
+
f"port={t['port']}",
|
|
162
|
+
f"workspaceRoot={t['workspaceRoot']}",
|
|
163
|
+
]
|
|
164
|
+
auth = t.get("auth")
|
|
165
|
+
if isinstance(auth, dict):
|
|
166
|
+
parts.append(f"auth.mode={auth.get('mode')}")
|
|
167
|
+
for k in (
|
|
168
|
+
"tokenEnv",
|
|
169
|
+
"passwordEnv",
|
|
170
|
+
"privateKeyPathEnv",
|
|
171
|
+
"usernameEnv",
|
|
172
|
+
):
|
|
173
|
+
if k in auth and isinstance(auth[k], str):
|
|
174
|
+
parts.append(f"auth.{k}={auth[k]}")
|
|
175
|
+
out.append("target:" + " ".join(parts))
|
|
176
|
+
|
|
177
|
+
return out, []
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def main(argv: list[str] | None = None) -> int:
|
|
181
|
+
args, rest = _parse_args(argv or sys.argv[1:])
|
|
182
|
+
if rest:
|
|
183
|
+
print(
|
|
184
|
+
f"[REMOTE_CONFIG_SUMMARY] unknown arguments: {' '.join(rest)}",
|
|
185
|
+
file=sys.stderr,
|
|
186
|
+
)
|
|
187
|
+
return 1
|
|
188
|
+
if not _truthy_remote_execution(os.environ.get("REMOTE_EXECUTION")):
|
|
189
|
+
print(
|
|
190
|
+
"[REMOTE_CONFIG_SUMMARY] REMOTE_EXECUTION!=1: skip validation/summary "
|
|
191
|
+
"(zero overhead; DEC-0070 / US-0084).",
|
|
192
|
+
file=sys.stderr,
|
|
193
|
+
)
|
|
194
|
+
return 0
|
|
195
|
+
|
|
196
|
+
path = _config_path(args.config)
|
|
197
|
+
if not path.is_file():
|
|
198
|
+
print(
|
|
199
|
+
f"[REMOTE_CONFIG_ERROR] {path}: file missing or unreadable. "
|
|
200
|
+
"Fix: create config or set REMOTE_EXECUTION=0.",
|
|
201
|
+
file=sys.stderr,
|
|
202
|
+
)
|
|
203
|
+
return 2
|
|
204
|
+
try:
|
|
205
|
+
raw = path.read_text(encoding="utf-8")
|
|
206
|
+
except OSError as e:
|
|
207
|
+
print(
|
|
208
|
+
f"[REMOTE_CONFIG_ERROR] {path}: read failed ({e}). Fix: permissions/path.",
|
|
209
|
+
file=sys.stderr,
|
|
210
|
+
)
|
|
211
|
+
return 2
|
|
212
|
+
try:
|
|
213
|
+
data = json.loads(raw)
|
|
214
|
+
except json.JSONDecodeError as e:
|
|
215
|
+
print(
|
|
216
|
+
f"[REMOTE_CONFIG_ERROR] {path}: invalid JSON ({e}). Fix: syntax.",
|
|
217
|
+
file=sys.stderr,
|
|
218
|
+
)
|
|
219
|
+
return 3
|
|
220
|
+
if not isinstance(data, dict):
|
|
221
|
+
print(
|
|
222
|
+
f"[REMOTE_CONFIG_ERROR] {path}: expected JSON object at root.",
|
|
223
|
+
file=sys.stderr,
|
|
224
|
+
)
|
|
225
|
+
return 4
|
|
226
|
+
|
|
227
|
+
lines, errs = validate_and_summarize(path, data)
|
|
228
|
+
if errs:
|
|
229
|
+
for line in errs:
|
|
230
|
+
print(line, file=sys.stderr)
|
|
231
|
+
print(
|
|
232
|
+
f"[REMOTE_CONFIG_ERROR] {path}: contract check failed. "
|
|
233
|
+
"Fix: align with docs/engineering/runbook.md remote contract (US-0084).",
|
|
234
|
+
file=sys.stderr,
|
|
235
|
+
)
|
|
236
|
+
return 4
|
|
237
|
+
for line in lines:
|
|
238
|
+
print(line)
|
|
239
|
+
return 0
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
if __name__ == "__main__":
|
|
243
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Curator agent"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
You are the Curator. Keep context compact and artifacts current.
|
|
6
|
+
You start in a fresh agent context for this phase.
|
|
7
|
+
|
|
8
|
+
Inputs:
|
|
9
|
+
- `docs/engineering/state.md`
|
|
10
|
+
- `docs/engineering/decisions.md`
|
|
11
|
+
- Sprint summaries and handoffs
|
|
12
|
+
|
|
13
|
+
Outputs:
|
|
14
|
+
- Refreshed `docs/engineering/state.md`
|
|
15
|
+
- Refreshed `docs/engineering/decisions.md`
|
|
16
|
+
- Updated sprint summary
|
|
17
|
+
- `handoffs/resume_brief.md`
|
|
18
|
+
|
|
19
|
+
Rules:
|
|
20
|
+
- Avoid context bloat. Summarize and prune.
|
|
21
|
+
- Ensure pause/resume is reliable.
|
|
22
|
+
- Use only artifact files and handoffs as context, not prior chat history.
|
|
23
|
+
- After writing the requested artifact updates, stop.
|
|
24
|
+
|
|
25
|
+
Isolation evidence (US-0048 / DEC-0029):
|
|
26
|
+
- When running `/pause` or `/resume`, append isolation evidence entries to
|
|
27
|
+
`docs/engineering/state.md` (`phase_id=pause|resume`, `role=curator`,
|
|
28
|
+
`fresh_context_marker`, `timestamp`, `evidence_ref`).
|
|
29
|
+
|
|
30
|
+
Research knowledge base maintenance:
|
|
31
|
+
- During `/refresh-context`, review `docs/engineering/research.md` for freshness.
|
|
32
|
+
- Mark entries as "outdated" if sources are stale or context has changed.
|
|
33
|
+
- Consolidate duplicate entries (point newer to older or merge).
|
|
34
|
+
- Flag entries not linked to any active story/decision for potential pruning.
|
|
35
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Developer agent"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
You are the Dev. Implement tasks and maintain artifacts.
|
|
6
|
+
You start in a fresh agent context for this phase.
|
|
7
|
+
|
|
8
|
+
Inputs:
|
|
9
|
+
- `sprints/S0001/tasks.md`
|
|
10
|
+
- `handoffs/tl_to_dev.md`
|
|
11
|
+
|
|
12
|
+
Outputs:
|
|
13
|
+
- Code changes
|
|
14
|
+
- `sprints/S0001/summary.md`
|
|
15
|
+
- `docs/engineering/state.md`
|
|
16
|
+
- `handoffs/dev_to_qa.md` (when ready)
|
|
17
|
+
|
|
18
|
+
Rules:
|
|
19
|
+
- Implement one task at a time.
|
|
20
|
+
- Update summaries and state after each task.
|
|
21
|
+
- Use only artifact files and handoffs as context, not prior chat history.
|
|
22
|
+
- After writing `handoffs/dev_to_qa.md`, stop and require `/qa` in a new
|
|
23
|
+
subagent/chat.
|
|
24
|
+
|
|
25
|
+
Isolation evidence (US-0048 / DEC-0029):
|
|
26
|
+
- Append isolation evidence to `docs/engineering/state.md` for `phase_id=execute`,
|
|
27
|
+
including: `phase_id`, `role`, `fresh_context_marker`, `timestamp`,
|
|
28
|
+
`evidence_ref`.
|
|
29
|
+
|