@deftai/directive-content 0.55.2 → 0.56.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.
Files changed (217) hide show
  1. package/.githooks/pre-commit +143 -0
  2. package/.githooks/pre-push +121 -0
  3. package/QUICK-START.md +2 -2
  4. package/Taskfile.yml +934 -0
  5. package/UPGRADING.md +47 -1
  6. package/events/README.md +3 -3
  7. package/package.json +5 -4
  8. package/scripts/_agents_md.py +494 -0
  9. package/scripts/_cache_fetch.py +635 -0
  10. package/scripts/_cache_quota.py +529 -0
  11. package/scripts/_cache_refresh.py +163 -0
  12. package/scripts/_cache_validate.py +209 -0
  13. package/scripts/_content_root.py +42 -0
  14. package/scripts/_doctor_state.py +277 -0
  15. package/scripts/_event_detect.py +305 -0
  16. package/scripts/_events.py +514 -0
  17. package/scripts/_lifecycle_hygiene.py +568 -0
  18. package/scripts/_pathspec.py +91 -0
  19. package/scripts/_policy_show_cli.py +266 -0
  20. package/scripts/_precutover.py +92 -0
  21. package/scripts/_project_context.py +224 -0
  22. package/scripts/_project_definition_io.py +164 -0
  23. package/scripts/_relocate_snapshot.py +209 -0
  24. package/scripts/_relocate_states.py +343 -0
  25. package/scripts/_resolve_preflight_path.py +152 -0
  26. package/scripts/_safe_subprocess.py +167 -0
  27. package/scripts/_session_start_hook.py +205 -0
  28. package/scripts/_sor_gate_diff.py +365 -0
  29. package/scripts/_stdio_utf8.py +59 -0
  30. package/scripts/_triage_bootstrap_gitignore.py +904 -0
  31. package/scripts/_triage_classify_cli.py +122 -0
  32. package/scripts/_triage_queue_cli.py +625 -0
  33. package/scripts/_triage_scope_cli.py +343 -0
  34. package/scripts/_triage_scope_drift_cli.py +121 -0
  35. package/scripts/_triage_scope_ignores.py +286 -0
  36. package/scripts/_triage_scope_milestone.py +432 -0
  37. package/scripts/_triage_scope_mutations.py +337 -0
  38. package/scripts/_triage_scope_renderers.py +207 -0
  39. package/scripts/_triage_smoketest_stages.py +674 -0
  40. package/scripts/_triage_subscribe_cli.py +140 -0
  41. package/scripts/_triage_welcome_cli.py +421 -0
  42. package/scripts/_vbrief_build.py +239 -0
  43. package/scripts/_vbrief_fidelity.py +479 -0
  44. package/scripts/_vbrief_legacy.py +589 -0
  45. package/scripts/_vbrief_reconciliation.py +883 -0
  46. package/scripts/_vbrief_routing.py +277 -0
  47. package/scripts/_vbrief_safety.py +778 -0
  48. package/scripts/_vbrief_sources.py +312 -0
  49. package/scripts/_vbrief_speckit.py +262 -0
  50. package/scripts/_vbrief_story_quality.py +353 -0
  51. package/scripts/_vbrief_validation.py +299 -0
  52. package/scripts/build_dist.py +412 -0
  53. package/scripts/cache.py +1078 -0
  54. package/scripts/cache_scanner.py +745 -0
  55. package/scripts/candidates_log.py +432 -0
  56. package/scripts/capacity_backfill.py +680 -0
  57. package/scripts/capacity_show.py +653 -0
  58. package/scripts/ci_local.py +689 -0
  59. package/scripts/code_structure_validate.py +765 -0
  60. package/scripts/codebase_default_extractor.py +495 -0
  61. package/scripts/codebase_map.py +304 -0
  62. package/scripts/codebase_map_fresh.py +104 -0
  63. package/scripts/codebase_projection_registry.py +94 -0
  64. package/scripts/codebase_provider.py +582 -0
  65. package/scripts/doctor.py +2257 -0
  66. package/scripts/framework_commands.py +505 -0
  67. package/scripts/gh_rest.py +882 -0
  68. package/scripts/github_auth_modes.py +437 -0
  69. package/scripts/github_body.py +292 -0
  70. package/scripts/ip_risk.py +531 -0
  71. package/scripts/issue_emit.py +670 -0
  72. package/scripts/issue_ingest.py +1064 -0
  73. package/scripts/migrate_preflight.py +418 -0
  74. package/scripts/migrate_vbrief.py +2677 -0
  75. package/scripts/monitor_pr.py +401 -0
  76. package/scripts/pack_migrate_lessons.py +336 -0
  77. package/scripts/pack_migrate_patterns.py +254 -0
  78. package/scripts/pack_migrate_rules.py +350 -0
  79. package/scripts/pack_migrate_skills.py +423 -0
  80. package/scripts/pack_migrate_strategies.py +311 -0
  81. package/scripts/pack_migrate_swarm_spec.py +250 -0
  82. package/scripts/pack_render.py +434 -0
  83. package/scripts/packs_slice.py +712 -0
  84. package/scripts/platform_capabilities.py +336 -0
  85. package/scripts/policy.py +2826 -0
  86. package/scripts/policy_set.py +324 -0
  87. package/scripts/pr_check_closing_keywords.py +524 -0
  88. package/scripts/pr_check_protected_issues.py +267 -0
  89. package/scripts/pr_merge_readiness.py +1004 -0
  90. package/scripts/pr_wait_mergeable.py +669 -0
  91. package/scripts/prd_render.py +159 -0
  92. package/scripts/preflight_architecture_sor.py +974 -0
  93. package/scripts/preflight_branch.py +289 -0
  94. package/scripts/preflight_cache.py +974 -0
  95. package/scripts/preflight_gh.py +721 -0
  96. package/scripts/preflight_implementation.py +272 -0
  97. package/scripts/preflight_story_start.py +838 -0
  98. package/scripts/preflight_wip_cap.py +149 -0
  99. package/scripts/probe_session.py +545 -0
  100. package/scripts/project_render.py +293 -0
  101. package/scripts/quarantine_ext.py +237 -0
  102. package/scripts/reconcile_issues.py +1442 -0
  103. package/scripts/refresh-path.ps1 +107 -0
  104. package/scripts/release.py +2030 -0
  105. package/scripts/release_e2e.py +1011 -0
  106. package/scripts/release_publish.py +486 -0
  107. package/scripts/release_rollback.py +980 -0
  108. package/scripts/relocate.py +1034 -0
  109. package/scripts/resolve_changelog_unreleased.py +667 -0
  110. package/scripts/resolve_version.py +490 -0
  111. package/scripts/resume_conditions.py +706 -0
  112. package/scripts/ritual_sentinel.py +609 -0
  113. package/scripts/roadmap_render.py +635 -0
  114. package/scripts/rule_ownership_lint.py +325 -0
  115. package/scripts/scm.py +591 -0
  116. package/scripts/scope_audit_log.py +387 -0
  117. package/scripts/scope_decompose.py +654 -0
  118. package/scripts/scope_demote.py +509 -0
  119. package/scripts/scope_lifecycle.py +1126 -0
  120. package/scripts/scope_undo.py +772 -0
  121. package/scripts/session_start.py +406 -0
  122. package/scripts/setup_ghx.py +339 -0
  123. package/scripts/setup_windows.ps1 +220 -0
  124. package/scripts/slice_audit.py +585 -0
  125. package/scripts/slice_record.py +530 -0
  126. package/scripts/slice_record_existing.py +692 -0
  127. package/scripts/slug_normalize.py +178 -0
  128. package/scripts/spec_render.py +477 -0
  129. package/scripts/spec_validate.py +238 -0
  130. package/scripts/subagent_monitor.py +658 -0
  131. package/scripts/swarm_complete_cohort.py +644 -0
  132. package/scripts/swarm_launch.py +1206 -0
  133. package/scripts/swarm_readiness.py +554 -0
  134. package/scripts/swarm_verify_review_clean.py +438 -0
  135. package/scripts/swarm_worktrees.py +497 -0
  136. package/scripts/toolchain-check.py +52 -0
  137. package/scripts/triage_actions.py +871 -0
  138. package/scripts/triage_bootstrap.py +1153 -0
  139. package/scripts/triage_bulk.py +630 -0
  140. package/scripts/triage_classify.py +932 -0
  141. package/scripts/triage_help.py +1685 -0
  142. package/scripts/triage_queue.py +1944 -0
  143. package/scripts/triage_reconcile.py +581 -0
  144. package/scripts/triage_refresh.py +643 -0
  145. package/scripts/triage_scope.py +999 -0
  146. package/scripts/triage_scope_drift.py +575 -0
  147. package/scripts/triage_smoketest.py +396 -0
  148. package/scripts/triage_subscribe.py +399 -0
  149. package/scripts/triage_summary.py +1011 -0
  150. package/scripts/triage_welcome.py +1178 -0
  151. package/scripts/ts_check_lane.py +86 -0
  152. package/scripts/validate-links.py +64 -0
  153. package/scripts/validate_strategy_output.py +212 -0
  154. package/scripts/vbrief_activate.py +228 -0
  155. package/scripts/vbrief_migrate_conformance.py +368 -0
  156. package/scripts/vbrief_reconcile_graph.py +306 -0
  157. package/scripts/vbrief_reconcile_labels.py +460 -0
  158. package/scripts/vbrief_reconcile_umbrellas.py +741 -0
  159. package/scripts/vbrief_validate.py +1195 -0
  160. package/scripts/verify-stubs.py +61 -0
  161. package/scripts/verify_capacity.py +160 -0
  162. package/scripts/verify_encoding.py +699 -0
  163. package/scripts/verify_hooks_installed.py +206 -0
  164. package/scripts/verify_investigation.py +360 -0
  165. package/scripts/verify_judgment_gates.py +827 -0
  166. package/scripts/verify_no_task_runtime.py +171 -0
  167. package/scripts/verify_scm_boundary.py +509 -0
  168. package/scripts/verify_session_ritual.py +389 -0
  169. package/scripts/verify_tools.py +426 -0
  170. package/scripts/verify_vbrief_conformance.py +478 -0
  171. package/tasks/architecture.yml +13 -0
  172. package/tasks/cache.yml +69 -0
  173. package/tasks/capacity.yml +38 -0
  174. package/tasks/change.yml +46 -0
  175. package/tasks/changelog.yml +24 -0
  176. package/tasks/ci.yml +49 -0
  177. package/tasks/codebase.yml +47 -0
  178. package/tasks/commit.yml +30 -0
  179. package/tasks/core.yml +126 -0
  180. package/tasks/deployments.yml +54 -0
  181. package/tasks/framework.yml +74 -0
  182. package/tasks/install.yml +60 -0
  183. package/tasks/issue.yml +50 -0
  184. package/tasks/migrate.yml +73 -0
  185. package/tasks/packs.yml +92 -0
  186. package/tasks/policy.yml +75 -0
  187. package/tasks/pr.yml +89 -0
  188. package/tasks/prd.yml +39 -0
  189. package/tasks/project.yml +27 -0
  190. package/tasks/reconcile.yml +32 -0
  191. package/tasks/relocate.yml +56 -0
  192. package/tasks/roadmap.yml +28 -0
  193. package/tasks/scm.yml +126 -0
  194. package/tasks/scope-undo.yml +36 -0
  195. package/tasks/scope.yml +141 -0
  196. package/tasks/session.yml +19 -0
  197. package/tasks/setup.yml +37 -0
  198. package/tasks/slice.yml +69 -0
  199. package/tasks/spec.yml +41 -0
  200. package/tasks/swarm.yml +85 -0
  201. package/tasks/toolchain.yml +13 -0
  202. package/tasks/triage-actions.yml +94 -0
  203. package/tasks/triage-bootstrap.yml +43 -0
  204. package/tasks/triage-bulk.yml +75 -0
  205. package/tasks/triage-classify.yml +30 -0
  206. package/tasks/triage-queue.yml +50 -0
  207. package/tasks/triage-reconcile.yml +29 -0
  208. package/tasks/triage-scope-drift.yml +29 -0
  209. package/tasks/triage-scope.yml +31 -0
  210. package/tasks/triage-smoketest.yml +33 -0
  211. package/tasks/triage-subscribe.yml +36 -0
  212. package/tasks/triage-summary.yml +29 -0
  213. package/tasks/triage-welcome.yml +32 -0
  214. package/tasks/ts.yml +328 -0
  215. package/tasks/vbrief.yml +206 -0
  216. package/tasks/verify.yml +292 -0
  217. package/templates/agents-entry.md +1 -1
@@ -0,0 +1,239 @@
1
+ """Shared helpers for building scope vBRIEF dicts.
2
+
3
+ Extracted from ``scripts/migrate_vbrief.py`` so ``scripts/issue_ingest.py`` (and
4
+ any future ingestion / generation script) can reuse them without cross-importing
5
+ the migrator. The canonical names are the public ``slugify`` / ``TODAY`` /
6
+ ``create_scope_vbrief`` surface; ``migrate_vbrief.py`` continues to re-export
7
+ the legacy underscore-prefixed aliases for backwards compatibility.
8
+
9
+ Story: #454 (task issue:ingest).
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import copy
15
+ import re
16
+ from datetime import UTC, datetime
17
+ from typing import Any
18
+
19
+ # ----------------------------------------------------------------------------
20
+ # Date helper
21
+ # ----------------------------------------------------------------------------
22
+
23
+ # Exposed for callers that want the canonical YYYY-MM-DD date used across
24
+ # ingestion / migration filenames. Kept module-level so monkeypatching in tests
25
+ # is straightforward.
26
+ TODAY: str = datetime.now(UTC).strftime("%Y-%m-%d")
27
+
28
+ # ----------------------------------------------------------------------------
29
+ # Emitted vBRIEF version (#533)
30
+ # ----------------------------------------------------------------------------
31
+
32
+ # Canonical ``vBRIEFInfo.version`` string emitted on every scope vBRIEF built
33
+ # via :func:`create_scope_vbrief`. Bumped from ``"0.5"`` to ``"0.6"`` as part
34
+ # of the Agent 2 schema vendor transition (#533). During the transition the
35
+ # validator accepts both strings; ingestion/generation paths only emit the
36
+ # newer one.
37
+ EMITTED_VBRIEF_VERSION: str = "0.6"
38
+
39
+
40
+ # ----------------------------------------------------------------------------
41
+ # Slugification
42
+ # ----------------------------------------------------------------------------
43
+
44
+
45
+ def slugify(text: str) -> str:
46
+ """Convert a title to a filename-safe slug.
47
+
48
+ - Lowercase, collapse whitespace/underscores to single hyphens
49
+ - Drop characters that are not [a-z0-9-]
50
+ - Trim to 60 characters maximum and strip leading/trailing hyphens
51
+ """
52
+ slug = text.lower().strip()
53
+ # Preserve underscores through the strip pass so the next line can fold
54
+ # them into hyphens (matches the documented contract).
55
+ slug = re.sub(r"[^a-z0-9\s_-]", "", slug)
56
+ slug = re.sub(r"[\s_]+", "-", slug)
57
+ slug = re.sub(r"-+", "-", slug)
58
+ return slug[:60].strip("-")
59
+
60
+
61
+ # ----------------------------------------------------------------------------
62
+ # Migrator provenance metadata namespace (#616)
63
+ # ----------------------------------------------------------------------------
64
+
65
+ # Per issue #616 (option A, scope-clamped), per-issue scope vBRIEFs emit
66
+ # ``plan.narratives`` as an empty object because a ROADMAP.md row does not
67
+ # carry enough data to populate any canonical narrative key meaningfully
68
+ # (Goals / ProblemStatement / UserStories need hand-authoring). Migrator-
69
+ # internal provenance (Phase, Tier, PhaseDescription plus reconciler-
70
+ # emitted fields) is relocated under ``plan.metadata`` in the
71
+ # ``x-migrator/*`` namespace so downstream readers can still access it
72
+ # without it leaking into user-visible narratives.
73
+ #
74
+ # The surface key is a string (``"x-migrator"``) rather than a dotted
75
+ # path because ``plan.metadata`` is the extension point for tool-specific
76
+ # metadata. Using the ``x-migrator`` namespace signals "this value comes
77
+ # from `task migrate:vbrief`" at a glance and keeps the payload compatible
78
+ # with the vendored v0.6 schema without touching it.
79
+ MIGRATOR_METADATA_KEY: str = "x-migrator"
80
+
81
+ # ----------------------------------------------------------------------------
82
+ # Reference provenance / trust helpers (#480)
83
+ # ----------------------------------------------------------------------------
84
+
85
+ INTERNAL_REFERENCE_TYPES = {"x-vbrief/plan", "x-vbrief/spec-section", "x-vbrief/user-request"}
86
+ EXTERNAL_REFERENCE_TYPES = {
87
+ "x-vbrief/github-issue",
88
+ "x-vbrief/github-pr",
89
+ "x-vbrief/jira-ticket",
90
+ "x-vbrief/web-page",
91
+ }
92
+
93
+
94
+ def reference_with_default_trust(ref: dict[str, Any]) -> dict[str, Any]:
95
+ """Return a copied reference with the default TrustLevel filled when known."""
96
+ normalized = copy.deepcopy(ref)
97
+ if "TrustLevel" in normalized:
98
+ return normalized
99
+ ref_type = normalized.get("type")
100
+ if ref_type in INTERNAL_REFERENCE_TYPES:
101
+ normalized["TrustLevel"] = "internal"
102
+ elif ref_type in EXTERNAL_REFERENCE_TYPES:
103
+ normalized["TrustLevel"] = "external"
104
+ return normalized
105
+
106
+
107
+ def _github_issue_reference(
108
+ *, repo_url: str, number: Any, title: Any
109
+ ) -> dict[str, str] | None:
110
+ cleaned_repo = str(repo_url or "").strip().rstrip("/")
111
+ cleaned_number = str(number or "").strip().lstrip("#").strip()
112
+ if not cleaned_repo or not cleaned_number:
113
+ return None
114
+ cleaned_title = str(title or "").strip()
115
+ ref_title = (
116
+ f"Issue #{cleaned_number}: {cleaned_title}"
117
+ if cleaned_title and cleaned_title != "Untitled"
118
+ else f"Issue #{cleaned_number}"
119
+ )
120
+ return {
121
+ "uri": f"{cleaned_repo}/issues/{cleaned_number}",
122
+ "type": "x-vbrief/github-issue",
123
+ "title": ref_title,
124
+ }
125
+
126
+
127
+ def _reference_has_required_fields(ref: dict[str, Any] | None) -> bool:
128
+ if ref is None:
129
+ return False
130
+ for key in ("uri", "type"):
131
+ value = ref.get(key)
132
+ if not isinstance(value, str) or not value.strip():
133
+ return False
134
+ return True
135
+
136
+
137
+ # ----------------------------------------------------------------------------
138
+ # Scope vBRIEF construction
139
+ # ----------------------------------------------------------------------------
140
+
141
+
142
+ def create_scope_vbrief(
143
+ item: dict,
144
+ repo_url: str = "",
145
+ status: str = "pending",
146
+ phase_description: str = "",
147
+ ) -> dict:
148
+ """Build a scope vBRIEF dict for a roadmap or issue item.
149
+
150
+ ``item`` is a generic dict with the following recognised keys:
151
+ - ``number`` (str): GitHub issue number (without '#')
152
+ - ``title`` (str): scope title (required in practice)
153
+ - ``phase`` (str): roadmap phase label (optional)
154
+ - ``tier`` (str): sub-phase tier label (optional)
155
+
156
+ The returned structure conforms to the canonical vBRIEF version emitted
157
+ by deft (``EMITTED_VBRIEF_VERSION``, currently ``"0.6"`` per #533):
158
+ - ``vBRIEFInfo.version = EMITTED_VBRIEF_VERSION``
159
+ - ``plan.title`` is ``item['title']`` verbatim
160
+ - ``plan.status`` is ``status`` (default ``pending``)
161
+ - ``plan.narratives`` is ``{}`` (empty). Per-issue scope vBRIEFs
162
+ intentionally ship with no narratives -- ROADMAP rows do not
163
+ carry enough data for any canonical narrative key, and inventing
164
+ keys leaked migrator internals into user-visible narratives
165
+ (#616). Migrator-internal provenance (``Phase`` / ``Tier`` /
166
+ ``PhaseDescription``) lives in ``plan.metadata['x-migrator']``
167
+ when populated.
168
+ - ``plan.references`` carries the canonical v0.6 origin-provenance
169
+ entry ``{uri, type: "x-vbrief/github-issue", title}`` when
170
+ ``item['number']`` is non-empty AND ``repo_url`` can be resolved
171
+ to a GitHub owner/repo (#613). Legacy readers that used to
172
+ receive a bare ``{type: "github-issue", id: "#N"}`` must migrate
173
+ to the canonical shape -- see ``scripts/reconcile_issues.py``
174
+ for a bilingual reader example.
175
+ """
176
+ number = str(item.get("number", "") or "").strip().lstrip("#").strip()
177
+ title = str(item.get("title", "Untitled") or "Untitled").strip() or "Untitled"
178
+ phase = item.get("phase", "")
179
+ tier = item.get("tier", "")
180
+
181
+ desc_label = f"#{number}: {title}" if number else title
182
+
183
+ vbrief: dict = {
184
+ "vBRIEFInfo": {
185
+ "version": EMITTED_VBRIEF_VERSION,
186
+ "description": f"Scope vBRIEF for {desc_label}",
187
+ },
188
+ "plan": {
189
+ "title": title,
190
+ "status": status,
191
+ "narratives": {},
192
+ "items": [],
193
+ },
194
+ }
195
+
196
+ # #616: relocate migrator-internal provenance from plan.narratives to
197
+ # plan.metadata['x-migrator'] so downstream tools (roadmap_render) keep
198
+ # working without leaking invented keys into user-visible narratives.
199
+ migrator_meta: dict[str, str] = {}
200
+ if phase:
201
+ migrator_meta["Phase"] = phase
202
+ if tier:
203
+ migrator_meta["Tier"] = tier
204
+ if phase_description:
205
+ migrator_meta["PhaseDescription"] = phase_description
206
+ if migrator_meta:
207
+ vbrief["plan"].setdefault("metadata", {})[MIGRATOR_METADATA_KEY] = migrator_meta
208
+
209
+ # #613: origin provenance per RFC #309 D11 + conventions/references.md.
210
+ # Canonical shape: {uri, type: "x-vbrief/github-issue", title}. A
211
+ # reference is only emitted when BOTH ``number`` and ``repo_url`` are
212
+ # present -- the schema's VBriefReference definition requires ``uri``
213
+ # so we cannot honestly emit a reference without a resolvable URL.
214
+ # ROADMAP bare-text rows (no issue number) legitimately ship with no
215
+ # origin reference; the migrator logs them as proposed/ orphans.
216
+ canonical_ref = _github_issue_reference(
217
+ repo_url=repo_url,
218
+ number=number,
219
+ title=title,
220
+ )
221
+ trusted_ref = (
222
+ reference_with_default_trust(canonical_ref)
223
+ if _reference_has_required_fields(canonical_ref)
224
+ else None
225
+ )
226
+ if trusted_ref is not None and _reference_has_required_fields(trusted_ref):
227
+ vbrief["plan"]["references"] = [trusted_ref]
228
+
229
+ return vbrief
230
+
231
+
232
+ __all__ = [
233
+ "EMITTED_VBRIEF_VERSION",
234
+ "MIGRATOR_METADATA_KEY",
235
+ "TODAY",
236
+ "reference_with_default_trust",
237
+ "slugify",
238
+ "create_scope_vbrief",
239
+ ]