@deftai/directive-content 0.58.0 → 0.60.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.
Files changed (187) hide show
  1. package/.githooks/pre-push +10 -9
  2. package/Taskfile.yml +57 -67
  3. package/UPGRADING.md +1 -1
  4. package/docs/assets/directive-lifecycle-diagram.png +0 -0
  5. package/docs/directive-lifecycle.md +73 -0
  6. package/docs/getting-started.md +5 -1
  7. package/package.json +3 -3
  8. package/packs/rules/rules-pack-0.1.json +3 -3
  9. package/packs/skills/skills-pack-0.1.json +22 -22
  10. package/scm/github.md +20 -2
  11. package/tasks/change.yml +16 -31
  12. package/tasks/ci.yml +8 -0
  13. package/tasks/commit.yml +12 -19
  14. package/tasks/core.yml +10 -0
  15. package/tasks/engine.yml +42 -0
  16. package/tasks/framework.yml +3 -0
  17. package/tasks/install.yml +20 -19
  18. package/tasks/migrate.yml +26 -15
  19. package/tasks/project.yml +16 -0
  20. package/tasks/relocate.yml +18 -48
  21. package/tasks/toolchain.yml +15 -5
  22. package/tasks/vbrief.yml +4 -3
  23. package/tasks/verify.yml +12 -14
  24. package/templates/agents-entry.md +1 -2
  25. package/scripts/_agents_md.py +0 -494
  26. package/scripts/_cache_fetch.py +0 -635
  27. package/scripts/_cache_quota.py +0 -529
  28. package/scripts/_cache_refresh.py +0 -163
  29. package/scripts/_cache_validate.py +0 -209
  30. package/scripts/_content_root.py +0 -42
  31. package/scripts/_doctor_state.py +0 -277
  32. package/scripts/_event_detect.py +0 -305
  33. package/scripts/_events.py +0 -514
  34. package/scripts/_lifecycle_hygiene.py +0 -568
  35. package/scripts/_pathspec.py +0 -91
  36. package/scripts/_policy_show_cli.py +0 -266
  37. package/scripts/_precutover.py +0 -92
  38. package/scripts/_project_context.py +0 -224
  39. package/scripts/_project_definition_io.py +0 -164
  40. package/scripts/_relocate_snapshot.py +0 -209
  41. package/scripts/_relocate_states.py +0 -343
  42. package/scripts/_resolve_preflight_path.py +0 -152
  43. package/scripts/_safe_subprocess.py +0 -167
  44. package/scripts/_session_start_hook.py +0 -205
  45. package/scripts/_sor_gate_diff.py +0 -365
  46. package/scripts/_stdio_utf8.py +0 -59
  47. package/scripts/_triage_bootstrap_gitignore.py +0 -904
  48. package/scripts/_triage_classify_cli.py +0 -122
  49. package/scripts/_triage_queue_cli.py +0 -625
  50. package/scripts/_triage_scope_cli.py +0 -343
  51. package/scripts/_triage_scope_drift_cli.py +0 -121
  52. package/scripts/_triage_scope_ignores.py +0 -286
  53. package/scripts/_triage_scope_milestone.py +0 -432
  54. package/scripts/_triage_scope_mutations.py +0 -337
  55. package/scripts/_triage_scope_renderers.py +0 -207
  56. package/scripts/_triage_smoketest_stages.py +0 -674
  57. package/scripts/_triage_subscribe_cli.py +0 -140
  58. package/scripts/_triage_welcome_cli.py +0 -421
  59. package/scripts/_vbrief_build.py +0 -239
  60. package/scripts/_vbrief_fidelity.py +0 -479
  61. package/scripts/_vbrief_legacy.py +0 -589
  62. package/scripts/_vbrief_reconciliation.py +0 -883
  63. package/scripts/_vbrief_routing.py +0 -277
  64. package/scripts/_vbrief_safety.py +0 -778
  65. package/scripts/_vbrief_sources.py +0 -312
  66. package/scripts/_vbrief_speckit.py +0 -262
  67. package/scripts/_vbrief_story_quality.py +0 -353
  68. package/scripts/_vbrief_validation.py +0 -299
  69. package/scripts/build_dist.py +0 -412
  70. package/scripts/cache.py +0 -1078
  71. package/scripts/cache_scanner.py +0 -745
  72. package/scripts/candidates_log.py +0 -432
  73. package/scripts/capacity_backfill.py +0 -680
  74. package/scripts/capacity_show.py +0 -653
  75. package/scripts/ci_local.py +0 -689
  76. package/scripts/code_structure_validate.py +0 -765
  77. package/scripts/codebase_default_extractor.py +0 -495
  78. package/scripts/codebase_map.py +0 -304
  79. package/scripts/codebase_map_fresh.py +0 -104
  80. package/scripts/codebase_projection_registry.py +0 -94
  81. package/scripts/codebase_provider.py +0 -582
  82. package/scripts/doctor.py +0 -2551
  83. package/scripts/framework_commands.py +0 -505
  84. package/scripts/gh_rest.py +0 -882
  85. package/scripts/github_auth_modes.py +0 -437
  86. package/scripts/github_body.py +0 -292
  87. package/scripts/ip_risk.py +0 -531
  88. package/scripts/issue_emit.py +0 -670
  89. package/scripts/issue_ingest.py +0 -1064
  90. package/scripts/migrate_preflight.py +0 -418
  91. package/scripts/migrate_vbrief.py +0 -2677
  92. package/scripts/monitor_pr.py +0 -401
  93. package/scripts/pack_migrate_lessons.py +0 -336
  94. package/scripts/pack_migrate_patterns.py +0 -254
  95. package/scripts/pack_migrate_rules.py +0 -350
  96. package/scripts/pack_migrate_skills.py +0 -423
  97. package/scripts/pack_migrate_strategies.py +0 -311
  98. package/scripts/pack_migrate_swarm_spec.py +0 -250
  99. package/scripts/pack_render.py +0 -434
  100. package/scripts/packs_slice.py +0 -712
  101. package/scripts/platform_capabilities.py +0 -336
  102. package/scripts/policy.py +0 -2826
  103. package/scripts/policy_set.py +0 -324
  104. package/scripts/pr_check_closing_keywords.py +0 -524
  105. package/scripts/pr_check_protected_issues.py +0 -267
  106. package/scripts/pr_merge_readiness.py +0 -1004
  107. package/scripts/pr_wait_mergeable.py +0 -669
  108. package/scripts/prd_render.py +0 -159
  109. package/scripts/preflight_architecture_sor.py +0 -974
  110. package/scripts/preflight_branch.py +0 -289
  111. package/scripts/preflight_cache.py +0 -974
  112. package/scripts/preflight_gh.py +0 -721
  113. package/scripts/preflight_implementation.py +0 -272
  114. package/scripts/preflight_story_start.py +0 -838
  115. package/scripts/preflight_wip_cap.py +0 -149
  116. package/scripts/probe_session.py +0 -545
  117. package/scripts/project_render.py +0 -293
  118. package/scripts/quarantine_ext.py +0 -237
  119. package/scripts/reconcile_issues.py +0 -1442
  120. package/scripts/refresh-path.ps1 +0 -107
  121. package/scripts/release.py +0 -2030
  122. package/scripts/release_e2e.py +0 -1011
  123. package/scripts/release_publish.py +0 -486
  124. package/scripts/release_rollback.py +0 -980
  125. package/scripts/relocate.py +0 -1034
  126. package/scripts/resolve_changelog_unreleased.py +0 -667
  127. package/scripts/resolve_version.py +0 -490
  128. package/scripts/resume_conditions.py +0 -706
  129. package/scripts/ritual_sentinel.py +0 -609
  130. package/scripts/roadmap_render.py +0 -635
  131. package/scripts/rule_ownership_lint.py +0 -325
  132. package/scripts/scm.py +0 -591
  133. package/scripts/scope_audit_log.py +0 -387
  134. package/scripts/scope_decompose.py +0 -654
  135. package/scripts/scope_demote.py +0 -509
  136. package/scripts/scope_lifecycle.py +0 -1126
  137. package/scripts/scope_undo.py +0 -772
  138. package/scripts/session_start.py +0 -406
  139. package/scripts/setup_ghx.py +0 -339
  140. package/scripts/setup_windows.ps1 +0 -220
  141. package/scripts/slice_audit.py +0 -585
  142. package/scripts/slice_record.py +0 -530
  143. package/scripts/slice_record_existing.py +0 -692
  144. package/scripts/slug_normalize.py +0 -178
  145. package/scripts/spec_render.py +0 -477
  146. package/scripts/spec_validate.py +0 -238
  147. package/scripts/subagent_monitor.py +0 -658
  148. package/scripts/swarm_complete_cohort.py +0 -644
  149. package/scripts/swarm_launch.py +0 -1206
  150. package/scripts/swarm_readiness.py +0 -554
  151. package/scripts/swarm_verify_review_clean.py +0 -438
  152. package/scripts/swarm_worktrees.py +0 -497
  153. package/scripts/toolchain-check.py +0 -52
  154. package/scripts/triage_actions.py +0 -871
  155. package/scripts/triage_bootstrap.py +0 -1153
  156. package/scripts/triage_bulk.py +0 -630
  157. package/scripts/triage_classify.py +0 -932
  158. package/scripts/triage_help.py +0 -1685
  159. package/scripts/triage_queue.py +0 -1944
  160. package/scripts/triage_reconcile.py +0 -581
  161. package/scripts/triage_refresh.py +0 -643
  162. package/scripts/triage_scope.py +0 -999
  163. package/scripts/triage_scope_drift.py +0 -575
  164. package/scripts/triage_smoketest.py +0 -396
  165. package/scripts/triage_subscribe.py +0 -399
  166. package/scripts/triage_summary.py +0 -1011
  167. package/scripts/triage_welcome.py +0 -1178
  168. package/scripts/ts_check_lane.py +0 -86
  169. package/scripts/validate-links.py +0 -64
  170. package/scripts/validate_strategy_output.py +0 -212
  171. package/scripts/vbrief_activate.py +0 -228
  172. package/scripts/vbrief_migrate_conformance.py +0 -368
  173. package/scripts/vbrief_reconcile_graph.py +0 -306
  174. package/scripts/vbrief_reconcile_labels.py +0 -460
  175. package/scripts/vbrief_reconcile_umbrellas.py +0 -741
  176. package/scripts/vbrief_validate.py +0 -1144
  177. package/scripts/verify-stubs.py +0 -61
  178. package/scripts/verify_capacity.py +0 -160
  179. package/scripts/verify_encoding.py +0 -699
  180. package/scripts/verify_hooks_installed.py +0 -206
  181. package/scripts/verify_investigation.py +0 -360
  182. package/scripts/verify_judgment_gates.py +0 -827
  183. package/scripts/verify_no_task_runtime.py +0 -171
  184. package/scripts/verify_scm_boundary.py +0 -509
  185. package/scripts/verify_session_ritual.py +0 -389
  186. package/scripts/verify_tools.py +0 -426
  187. package/scripts/verify_vbrief_conformance.py +0 -478
package/scm/github.md CHANGED
@@ -214,6 +214,26 @@ Following a v1.0.0 release, commits:
214
214
  - ⊗ Allow force pushes
215
215
  - ⊗ Allow deletions
216
216
 
217
+ ## ghx cache proxy (#884)
218
+
219
+ [ghx](https://github.com/brunoborges/ghx) is a **supported, recommended** read-only cache proxy for the GitHub CLI. Deft's SCM layer (`resolveBinary` in `@deftai/directive-core/scm`) prefers `ghx` over `gh` when both are on PATH, so consumers benefit automatically once ghx is installed. ghx is optional for consumer projects — only `gh` is required — but strongly recommended for maintainers and multi-agent swarms that issue many read-only `gh api` / `gh issue view` calls.
220
+
221
+ **Install (consent-gated, default deny):**
222
+
223
+ ```bash
224
+ directive setup:ghx # interactive y/N prompt (default: no)
225
+ task setup:ghx # same via Taskfile
226
+ task setup:ghx -- --yes # non-interactive CI / scripted approval
227
+ ```
228
+
229
+ `task setup` runs a detection-only `--check` pass and nudges when `gh` is present but `ghx` is missing. Set `DEFT_SETUP_GHX_SKIP=1` to suppress the interactive install path in non-interactive shells.
230
+
231
+ **Surface rules:**
232
+
233
+ - ! Prefer `ghx` over `gh` for read-only GET operations when ghx is on PATH
234
+ - ! Use live `gh` for mutations (POST/PATCH/PUT/DELETE) and for immediate read-back after a mutation — ghx is a cached GET proxy only
235
+ - ⊗ Use `ghx api` for multi-arg write invocations — ghx accepts a single positional path arg; writes fall through to `gh`
236
+
217
237
  ## Destructive gh verbs (#1019)
218
238
 
219
239
  A detection-bound gate (`scripts/preflight_gh.py`) refuses three classes of destructive surface before they execute, complementing the #747 branch-protection gate which already refuses commits to the default branch:
@@ -234,8 +254,6 @@ Three enforcement surfaces back the gate:
234
254
  - For repo deletion specifically: prefer the GitHub web UI's archive-or-delete prompt -- archiving is reversible, the gate is not opining on it.
235
255
  - For an admin merge: the canonical recovery is to request review through the normal flow. The `--admin` flag is gated because the most-common legitimate use case (release hot-fix) is rare enough that documenting an explicit bypass is cheaper than letting the verb pass by default.
236
256
 
237
- **Out of scope (v1):** a PATH-shim that intercepts `gh` at the command layer is deferred -- the consent-gated install path mirrors `setup_ghx.py` (#884) and lands additively when a follow-up issue is opened. The current gate is detection-bound (pre-push hook + agent-side `--command` classifier + self-test contract) rather than execution-bound, which is sufficient to refuse the recurring failure mode that motivated #1019 (an agent with an unrestricted `gh` token executing a destructive verb).
238
-
239
257
  ## Release Workflow (UCCPR)
240
258
 
241
259
  **UCCPR** = Update Changelog, Commit, Push, Release
package/tasks/change.yml CHANGED
@@ -3,44 +3,29 @@ version: '3'
3
3
  vars:
4
4
  # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
5
5
  # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
6
- # resolves correctly under uv on Windows (#566). Used here to pin
7
- # `uv --project` against ancestor pyproject.toml leakage (#1011).
6
+ # resolves correctly under node on Windows (#566).
8
7
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
9
8
 
10
9
  tasks:
10
+ _ts-build:
11
+ internal: true
12
+ desc: "Build @deftai/cli dist/ before TS-backed gates run (#1828 s2)."
13
+ dir: '{{.USER_WORKING_DIR}}'
14
+ cmds:
15
+ - pnpm --dir "{{.DEFT_ROOT}}" run build
16
+
11
17
  changelog:check:
12
18
  desc: Verify CHANGELOG.md has an [Unreleased] section with at least one entry
13
- env:
14
- PYTHONUTF8: "1"
19
+ dir: '{{.USER_WORKING_DIR}}'
20
+ deps:
21
+ - _ts-build
15
22
  cmds:
16
- - cmd: >-
17
- uv --project "{{.DEFT_ROOT}}" run python -c
18
- "import sys, pathlib, re;
19
- p = pathlib.Path('CHANGELOG.md');
20
- (not p.exists()) and (print('FAIL: CHANGELOG.md not found'), sys.exit(1));
21
- text = p.read_text('utf-8');
22
- m = re.search(r'## \[Unreleased\][ \t]*\n(.*?)(?=\n## \[|$)', text, re.DOTALL);
23
- (m is None) and (print('FAIL: No [Unreleased] section found in CHANGELOG.md'), sys.exit(1));
24
- body = m.group(1);
25
- entries = [l for l in body.splitlines() if l.strip().startswith('- ')];
26
- (len(entries) == 0) and (print('FAIL: [Unreleased] section has no entries (no lines starting with \"- \")'), sys.exit(1));
27
- print(f'OK: CHANGELOG.md [Unreleased] section has {len(entries)} entries')"
23
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" changelog-check --project-root "{{.USER_WORKING_DIR}}"
28
24
 
29
25
  change:init:
30
26
  desc: Create a new change proposal directory structure in history/changes/<name>/
27
+ dir: '{{.USER_WORKING_DIR}}'
28
+ deps:
29
+ - _ts-build
31
30
  cmds:
32
- - cmd: >-
33
- uv --project "{{.DEFT_ROOT}}" run python -c
34
- "import sys, pathlib, json, re;
35
- name = '{{.CLI_ARGS}}'.strip();
36
- (not name) and (print('FAIL: Usage: task change:init -- <name>'), sys.exit(1));
37
- (not re.match(r'^[\w][\w-]*$', name)) and (print('FAIL: Name must contain only alphanumeric characters, underscores, and hyphens'), sys.exit(1));
38
- base = pathlib.Path('history/changes') / name;
39
- base.exists() and (print(f'FAIL: {base} already exists'), sys.exit(1));
40
- (base / 'specs').mkdir(parents=True, exist_ok=True);
41
- proposal = {'vBRIEFInfo': {'version': '0.5'}, 'plan': {'title': name, 'status': 'draft', 'narratives': {'Problem': 'What is wrong or missing.', 'Change': 'What this proposal does about it.', 'Scope': 'In scope: ... Out of scope: ...', 'Impact': 'What existing code/specs are affected.', 'Risks': 'What could go wrong.', 'Approach': 'How to implement the change.', 'Alternatives': 'What else was considered and why not.', 'Dependencies': 'What must exist before this works.'}}};
42
- (base / 'proposal.vbrief.json').write_text(json.dumps(proposal, indent=2) + chr(10), encoding='utf-8');
43
- tasks = {'vBRIEFInfo': {'version': '0.5'}, 'plan': {'title': name, 'status': 'draft', 'items': [], 'edges': []}};
44
- (base / 'tasks.vbrief.json').write_text(json.dumps(tasks, indent=2) + chr(10), encoding='utf-8');
45
- print(f'OK: Created change proposal at {base}/');
46
- [print(f' - {f}') for f in ['proposal.vbrief.json', 'tasks.vbrief.json', 'specs/']]"
31
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" change-init --project-root "{{.USER_WORKING_DIR}}" --name {{.CLI_ARGS}}
package/tasks/ci.yml CHANGED
@@ -1,5 +1,13 @@
1
1
  version: '3'
2
2
 
3
+ # Maintainer-only local CI mirror (#1813 contributor path / #2022 Phase 2).
4
+ #
5
+ # `ci:local` shells into scripts/ci_local.py via `uv run python`. Release
6
+ # Step-5 pre-flight now uses the TS `task check` path (py-purge-ci-local-ts);
7
+ # this fragment remains for explicit maintainer local CI only. Included from
8
+ # the root Taskfile with `internal: true` — not wired into `check:consumer`
9
+ # or the default `task check` aggregate.
10
+
3
11
  vars:
4
12
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
5
13
 
package/tasks/commit.yml CHANGED
@@ -3,28 +3,21 @@ version: '3'
3
3
  vars:
4
4
  # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
5
5
  # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
6
- # resolves correctly under uv on Windows (#566). Used here to pin
7
- # `uv --project` against ancestor pyproject.toml leakage (#1011).
6
+ # resolves correctly under node on Windows (#566).
8
7
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
9
8
 
10
9
  tasks:
10
+ _ts-build:
11
+ internal: true
12
+ desc: "Build @deftai/cli dist/ before TS-backed gates run (#1828 s2)."
13
+ dir: '{{.USER_WORKING_DIR}}'
14
+ cmds:
15
+ - pnpm --dir "{{.DEFT_ROOT}}" run build
16
+
11
17
  commit:lint:
12
18
  desc: Validate HEAD commit message against conventional commit format
19
+ dir: '{{.USER_WORKING_DIR}}'
20
+ deps:
21
+ - _ts-build
13
22
  cmds:
14
- - cmd: >-
15
- uv --project "{{.DEFT_ROOT}}" run python -c
16
- "import sys, re, subprocess;
17
- result = subprocess.run(['git', 'log', '--format=%B', '-1'], capture_output=True, text=True);
18
- (result.returncode != 0) and (print('FAIL: Could not read HEAD commit message'), sys.exit(1));
19
- msg = result.stdout.strip();
20
- subject = msg.splitlines()[0] if msg else '';
21
- types = 'feat|fix|docs|chore|refactor|test|style|perf|ci|build|revert';
22
- pattern = r'^(' + types + r')(\(.+\))?!?: .+';
23
- ok = re.match(pattern, subject);
24
- (not ok) and (print('FAIL: Commit message does not match conventional commit format'),
25
- print(f' Got: {subject}'),
26
- print(f' Expected: type(scope): description'),
27
- print(f' Types: feat, fix, docs, chore, refactor, test, style, perf, ci, build, revert'),
28
- sys.exit(1));
29
- print(f'OK: Commit message is valid conventional commit');
30
- print(f' Subject: {subject}')"
23
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" commit-lint --project-root "{{.USER_WORKING_DIR}}"
package/tasks/core.yml CHANGED
@@ -1,5 +1,15 @@
1
1
  version: '3'
2
2
 
3
+ # Maintainer-only Python self-test lane (#1813 contributor path / #2022 Phase 2).
4
+ #
5
+ # pytest, ruff, black, and mypy run here via `uv run` for framework-source-repo
6
+ # pre-commit checks. This fragment is included from the root Taskfile with
7
+ # `internal: true` so `core:*` tasks are NOT listed or callable from the CLI on
8
+ # consumer installs — only `task check:framework-source` wires them as deps.
9
+ #
10
+ # Consumer `task check` dispatches to `check:consumer` (TS / deft verbs only).
11
+ # Do NOT add these tasks to the consumer check aggregate.
12
+
3
13
  vars:
4
14
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
5
15
 
@@ -0,0 +1,42 @@
1
+ version: '3'
2
+
3
+ # Consumer engine resolution (#2022 Phase 3).
4
+ #
5
+ # Framework-source checkouts invoke the vendored `packages/cli/dist/bin.js`.
6
+ # npm consumer deposits copy @deftai/directive-content only — no bundled CLI —
7
+ # so tasks fall back to the globally installed `deft` / `directive` command.
8
+
9
+ vars:
10
+ DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
11
+
12
+ tasks:
13
+ _ts-build:
14
+ internal: true
15
+ desc: "Build CLI dist when running from the framework source checkout; no-op on npm consumer deposits (#2022 Phase 3)."
16
+ dir: '{{.DEFT_ROOT}}'
17
+ cmds:
18
+ - |
19
+ set -eu
20
+ if [ -f packages/cli/dist/bin.js ]; then
21
+ pnpm run build
22
+ fi
23
+
24
+ invoke:
25
+ internal: true
26
+ desc: "Run a deft-ts verb from vendored bin.js (source checkout) or global deft (npm consumer deposit)."
27
+ # Run from the operator project root so deft verbs resolve USER_WORKING_DIR
28
+ # correctly; without this, included engine.yml defaults cwd to tasks/ (#2022).
29
+ dir: '{{.USER_WORKING_DIR}}'
30
+ cmds:
31
+ - |
32
+ set -eu
33
+ bin="{{.DEFT_ROOT}}/packages/cli/dist/bin.js"
34
+ if [ -f "$bin" ]; then
35
+ node "$bin" {{.ENGINE_CMD}}
36
+ elif command -v deft >/dev/null 2>&1; then
37
+ deft {{.ENGINE_CMD}}
38
+ else
39
+ echo "deft: neither {{.DEFT_ROOT}}/packages/cli/dist/bin.js nor a global deft command is available." >&2
40
+ echo " Install with: npm i -g @deftai/directive" >&2
41
+ exit 2
42
+ fi
@@ -13,6 +13,9 @@ version: '3'
13
13
  # `cmds:` skip would silently discard the recovery flag.
14
14
 
15
15
  vars:
16
+ # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
17
+ # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
18
+ # resolves correctly under node on Windows (#566).
16
19
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
17
20
 
18
21
  tasks:
package/tasks/install.yml CHANGED
@@ -3,11 +3,17 @@ version: '3'
3
3
  vars:
4
4
  # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
5
5
  # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
6
- # resolves correctly under uv on Windows (#566). Used here to pin
7
- # `uv --project` against ancestor pyproject.toml leakage (#1011).
6
+ # resolves correctly under node on Windows (#566).
8
7
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
9
8
 
10
9
  tasks:
10
+ _ts-build:
11
+ internal: true
12
+ desc: "Build @deftai/cli dist/ before TS-backed gates run (#1828 s2)."
13
+ dir: '{{.USER_WORKING_DIR}}'
14
+ cmds:
15
+ - pnpm --dir "{{.DEFT_ROOT}}" run build
16
+
11
17
  install:
12
18
  desc: Install deft (dev convenience -- end users should use the compiled binary from GitHub Releases)
13
19
  cmds:
@@ -15,23 +21,18 @@ tasks:
15
21
 
16
22
  uninstall:
17
23
  desc: Remove deft entry from AGENTS.md
24
+ dir: '{{.USER_WORKING_DIR}}'
25
+ deps:
26
+ - _ts-build
18
27
  cmds:
19
- - cmd: >-
20
- uv --project "{{.DEFT_ROOT}}" run python -c "from pathlib import Path;
21
- f=Path('AGENTS.md');
22
- t=f.read_text('utf-8') if f.exists() else '';
23
- lines=t.splitlines(keepends=True);
24
- out=''.join(l for l in lines if not l.startswith('See deft/main.md') and not l.startswith('Skills: deft/skills/'));
25
- f.write_text(out,'utf-8') if f.exists() else None;
26
- print('Removed deft entry from AGENTS.md' if out!=t else 'No deft entry found in AGENTS.md')"
28
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" install-uninstall --project-root "{{.USER_WORKING_DIR}}"
27
29
 
28
- # User-facing upgrade entrypoint (#1061). Wraps the canonical
29
- # ``run upgrade`` command (`cmd_upgrade` in ./run) which:
30
+ # User-facing upgrade entrypoint (#1061). Native deft-ts handler (#2022 Phase 2):
30
31
  # 1. Writes / refreshes the bare ``.deft-version`` marker.
31
32
  # 2. Writes the canonical YAML provenance manifest at
32
33
  # ``<install>/VERSION`` (#1046 PR-B AC-4) -- including the
33
34
  # ``install_root`` field added in #1062.
34
- # 3. Delegates to ``cmd_agents_refresh`` so the AGENTS.md managed
35
+ # 3. Delegates to ``agents:refresh`` so the AGENTS.md managed
35
36
  # section is brought to the current rendered template (v3 marker
36
37
  # with sha/refreshed/session attributes, #1046 PR-B AC-5).
37
38
  # This wrapper exists so the ``framework:doctor`` failure prose and
@@ -43,18 +44,18 @@ tasks:
43
44
  # ``generates:`` because the task is mutating + must run on every
44
45
  # invocation. CLI_ARGS is explicitly cleared (``vars: { CLI_ARGS: "" }``)
45
46
  # so any operator-supplied ``-- <flags>`` are deterministically dropped
46
- # before reaching ``run upgrade``: go-task only forwards ``{{.CLI_ARGS}}``
47
+ # before reaching the upgrade handler: go-task only forwards ``{{.CLI_ARGS}}``
47
48
  # when a ``cmds`` line references it, but pinning the var to empty makes
48
49
  # the contract explicit AND defends against a future edit that
49
50
  # accidentally appends ``{{.CLI_ARGS}}`` to the dispatch line (SLizard P1
50
- # on PR #1067 -- ``cmd_upgrade`` does not currently accept flags; future
51
+ # on PR #1067 -- the upgrade handler does not currently accept flags; future
51
52
  # flag additions land deliberately, not implicitly via Taskfile drift).
52
53
  upgrade:
53
- desc: "Upgrade deft framework: refresh AGENTS.md to current marker, write canonical install manifest, regenerate .deft-version derivative (#1061). Wraps `run upgrade`."
54
+ desc: "Upgrade deft framework: refresh AGENTS.md to current marker, write canonical install manifest, regenerate .deft-version derivative (#1061). Dispatches via deft-ts install-upgrade."
54
55
  dir: '{{.USER_WORKING_DIR}}'
55
- env:
56
- PYTHONUTF8: "1"
57
56
  vars:
58
57
  CLI_ARGS: ""
58
+ deps:
59
+ - _ts-build
59
60
  cmds:
60
- - uv --project "{{.DEFT_ROOT}}" run python "{{.DEFT_ROOT}}/run" upgrade
61
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" install-upgrade --project-root "{{.USER_WORKING_DIR}}" --framework-root "{{.DEFT_ROOT}}"
package/tasks/migrate.yml CHANGED
@@ -1,13 +1,22 @@
1
1
  version: '3'
2
2
 
3
3
  vars:
4
- # See deft/Taskfile.yml for the rationale behind per-subfile DEFT_ROOT
5
- # (go-task re-evaluates var templates at use site; joinPath is eager and
6
- # produces a clean, native-separator path so `uv --project "{{.DEFT_ROOT}}" run python` resolves it
7
- # correctly on Windows -- #566).
4
+ # Per ../Taskfile.yml header: joinPath is evaluated eagerly by go-task
5
+ # templating and yields a native-separator absolute path so {{.DEFT_ROOT}}
6
+ # resolves correctly under node on Windows (#566).
8
7
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
9
8
 
10
9
  tasks:
10
+ _ts-build:
11
+ internal: true
12
+ desc: "Build @deftai/cli dist/ before TS-backed gates run (#1828 s2)."
13
+ # Match scope.yml _ensure-ts: run from USER_WORKING_DIR with pnpm --dir so
14
+ # Windows consumer invocations via `-t <abs Taskfile>` do not double-prefix
15
+ # DEFT_ROOT under go-task's dir: handling (#566 / Windows CI regression).
16
+ dir: '{{.USER_WORKING_DIR}}'
17
+ cmds:
18
+ - pnpm --dir "{{.DEFT_ROOT}}" run build
19
+
11
20
  preflight:
12
21
  # Agent-side environment preflight for `task migrate:vbrief` (#793).
13
22
  # Reifies the prose contract documented in
@@ -18,16 +27,19 @@ tasks:
18
27
  # any destructive mutation runs. Three-state exit (0 ready / 1 not-ready / 2 config
19
28
  # error) mirrors `scripts/preflight_branch.py` (#747).
20
29
  #
30
+ # Consumer path dispatches through the native deft-ts handler (#2022 Phase 2);
31
+ # the legacy Python script remains for maintainer parity only.
32
+ #
21
33
  # NO `sources:` / `generates:` per `conventions/task-caching.md`: the
22
34
  # script forwards user-facing CLI flags (`--project-root`, `--deft-root`,
23
35
  # `--quiet`) via `{{.CLI_ARGS}}` and a cached cmds skip would silently
24
36
  # swallow them.
25
37
  desc: "Verify environment readiness for `task migrate:vbrief` (uv on PATH, v0.20+ layout, document model, git tree). Three-state exit (#793)."
26
38
  dir: '{{.USER_WORKING_DIR}}'
27
- env:
28
- PYTHONUTF8: "1"
39
+ deps:
40
+ - _ts-build
29
41
  cmds:
30
- - uv --project "{{.DEFT_ROOT}}" run --frozen python "{{.DEFT_ROOT}}/scripts/migrate_preflight.py" --project-root "{{.USER_WORKING_DIR}}" --deft-root "{{.DEFT_ROOT}}" {{.CLI_ARGS}}
42
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" migrate-preflight --project-root "{{.USER_WORKING_DIR}}" --deft-root "{{.DEFT_ROOT}}" {{.CLI_ARGS}}
31
43
 
32
44
  vbrief:
33
45
  desc: >
@@ -44,12 +56,15 @@ tasks:
44
56
  run::_check_upgrade_gate is consulted by the CLI flow). See
45
57
  events/registry.json for the payload contracts and `DEFT_EVENT_LOG` for
46
58
  opt-in JSON-line capture (#635).
59
+ #2013 HOLDOUT: `migrate:vbrief` remains the sole consumer-path Python
60
+ entrypoint until the spec-authority umbrella resolves the
61
+ `migrate_vbrief.py` port-vs-cutoff decision (#2022 Phase 2).
47
62
  deps:
48
63
  # #793: gate destructive migration on the environment preflight. A
49
64
  # non-zero exit from the preflight task aborts this task before any
50
65
  # `cmds:` line runs. Operator-supplied `{{.CLI_ARGS}}` (e.g.
51
66
  # `--dry-run`, `--force`, `--rollback`, `--strict`) belong to the
52
- # migrator and are NOT valid migrate_preflight.py flags. go-task
67
+ # migrator and are NOT valid migrate:preflight flags. go-task
53
68
  # forwards parent CLI_ARGS into dependency tasks by default, so we
54
69
  # explicitly clear CLI_ARGS here; otherwise `task migrate:vbrief --
55
70
  # --dry-run` reaches the preflight argparse surface as
@@ -63,11 +78,7 @@ tasks:
63
78
  env:
64
79
  PYTHONUTF8: "1"
65
80
  cmds:
66
- # Pass Taskfile CLI args through so operators can opt into safety flags
67
- # (#497) or --strict reconciliation (#496) without editing this file.
68
- # Script path uses {{.DEFT_ROOT}} (defined locally in this file's
69
- # `vars:` block above via `joinPath .TASKFILE_DIR ".."` -- see
70
- # ../Taskfile.yml for why root-level definition is avoided) rather
71
- # than {{.TASKFILE_DIR}}/.. to keep the path traversal-free; the
72
- # mixed-separator form breaks `uv --project "{{.DEFT_ROOT}}" run python` on Windows (#566).
81
+ # #2013 holdout: migrate_vbrief.py stays on the consumer path until the
82
+ # spec-authority umbrella closes the port-vs-cutoff decision (#2022).
83
+ # Preflight is TS-native; the migrator body remains Python for now.
73
84
  - uv --project "{{.DEFT_ROOT}}" run python "{{.DEFT_ROOT}}/scripts/migrate_vbrief.py" "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}
package/tasks/project.yml CHANGED
@@ -9,6 +9,12 @@ vars:
9
9
  # own repo root when invoked via ``includes:`` -- consumers got their data
10
10
  # scanned from ``deft/vbrief/``. Switched to ``{{.USER_WORKING_DIR}}`` to
11
11
  # match the convention used by roadmap:render / spec:render / migrate:vbrief.
12
+ #
13
+ # Staleness acknowledgement (#640): ``task project:ack-staleness`` records a
14
+ # completed-scope watermark on PROJECT-DEFINITION ``plan.metadata.staleness_review``
15
+ # so ``task project:render`` stops re-emitting the same narrative staleness flags
16
+ # after review. This is distinct from ``task reconcile:issues``, which reconciles
17
+ # origin freshness on scope vBRIEFs — not PROJECT-DEFINITION narrative heuristics.
12
18
 
13
19
  tasks:
14
20
  _ts-build:
@@ -25,3 +31,13 @@ tasks:
25
31
  - _ts-build
26
32
  cmds:
27
33
  - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" project-render "{{.USER_WORKING_DIR}}/vbrief"
34
+
35
+ ack-staleness:
36
+ desc: >-
37
+ Acknowledge reviewed PROJECT-DEFINITION narrative staleness (#640).
38
+ Distinct from task reconcile:issues (scope origin freshness).
39
+ dir: '{{.USER_WORKING_DIR}}'
40
+ deps:
41
+ - _ts-build
42
+ cmds:
43
+ - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" project-render --acknowledge-staleness "{{.USER_WORKING_DIR}}/vbrief"
@@ -1,56 +1,26 @@
1
1
  version: '3'
2
2
 
3
- # tasks/relocate.yml -- wipe-and-reinstall relocator surface (#992 PR2).
3
+ # tasks/relocate.yml -- DROPPED from the consumer task surface (#2022).
4
4
  #
5
- # Wires `task relocate` to scripts/relocate.py with full {{.CLI_ARGS}}
6
- # pass-through so operators can compose flags as documented in the script
7
- # docstring (--project-root / --framework-source / --force / --confirm /
8
- # --no-confirm / --dry-run / --rollback / --snapshot / --no-snapshot /
9
- # --json / --quiet).
5
+ # The relocate task previously wired `task relocate` (namespaced
6
+ # `relocate:relocate`) to `uv run python scripts/relocate.py` -- the sole
7
+ # remaining consumer-exposed Python coupling on the deft task surface. Per
8
+ # the #2022 Python-purge readiness gate it has been REMOVED from the
9
+ # consumer-exposed task graph: this fragment is no longer referenced by the
10
+ # root Taskfile.yml `includes:` block and exposes no task.
10
11
  #
11
- # Per `conventions/task-caching.md` (#574): NO `sources:` / `generates:`
12
- # declarations because user-facing recovery flags (`--force`, `--confirm`,
13
- # `--rollback`, `--dry-run`) flow via {{.CLI_ARGS}} -- a go-task cache
14
- # short-circuit would silently drop the recovery flag and leave the
15
- # operator following the docs against an exit-0 no-op (the same hazard
16
- # `task prd:render -- --force` hit at #574).
12
+ # Canonical consumer (re)install / relocate path is now the npm installer
13
+ # (`npm i -g @deftai/directive@latest`), NOT a bundled task -- see
14
+ # content/UPGRADING.md "Manual drift repair" and #1912 (the relocator is a
15
+ # back-compat / legacy bridge only, never a routine-upgrade path). The
16
+ # underlying helper scripts/relocate.py is intentionally retained for #1860
17
+ # (big-bang Python delete) to remove; this story only drops the consumer
18
+ # task surface.
17
19
  #
18
- # Path resolution uses the same {{.TASKFILE_DIR}} / joinPath idiom every
19
- # other fragment uses (#566) so the dispatch resolves correctly under
20
- # Windows / macOS / Linux. The dispatched script lives at
21
- # {{.DEFT_ROOT}}/scripts/relocate.py with helper modules at
22
- # {{.DEFT_ROOT}}/scripts/_relocate_states.py and
23
- # {{.DEFT_ROOT}}/scripts/_relocate_snapshot.py.
24
- #
25
- # Usage notes for operators:
26
- #
27
- # - Bare invocation against a project root that needs relocating prompts
28
- # `[y/N]` on stdin (auto-prompt never auto-wipe per the active vBRIEF).
29
- # - For scripted use, pass `--confirm` to skip the prompt (or
30
- # `--no-snapshot` for hermetic test fixtures).
31
- # - For consumer projects: the canonical entry point is the webinstaller
32
- # bootstrap (`upgrade.sh` / `upgrade.ps1` in the separate `webinstaller`
33
- # repo) which fetches a fresh framework copy to a temp dir and runs the
34
- # relocator from THAT copy -- the in-place framework about to be wiped
35
- # never executes its own wipe.
36
- # - `task relocate -- --rollback` extracts the most recent snapshot back
37
- # into project root.
38
- #
39
- # Companion script: scripts/relocate.py
40
- # Companion tests: tests/relocate/test_state_matrix.py
41
- # tests/relocate/test_preflight.py
42
- # Refs: #992 (parent issue), #11 (.deft/core/ origin),
43
- # #768 (managed-section v2 contract), #794 (_wrap_legacy_in_markers),
44
- # #884 (consent-gate convention).
20
+ # This file is retained (un-wired) so the framework's own Taskfile
21
+ # content-contract tests keep a stable fixture. The DEFT_ROOT joinPath var
22
+ # below preserves the #566 per-subfile path-resolution idiom for any future
23
+ # maintainer-side re-wiring.
45
24
 
46
25
  vars:
47
26
  DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
48
-
49
- tasks:
50
- relocate:
51
- desc: "Wipe-and-reinstall relocator (#992 PR2) -- task relocate [-- --confirm | --dry-run | --force | --rollback | --json | --quiet]"
52
- dir: '{{.USER_WORKING_DIR}}'
53
- env:
54
- PYTHONUTF8: "1"
55
- cmds:
56
- - uv --project "{{.DEFT_ROOT}}" run python "{{.DEFT_ROOT}}/scripts/relocate.py" {{.CLI_ARGS}}
@@ -5,9 +5,19 @@ vars:
5
5
 
6
6
  tasks:
7
7
  check:
8
- desc: Verify required toolchain is installed (go, uv, git, gh, node, pnpm)
9
- deps: [":ts:build"]
10
- env:
11
- PYTHONUTF8: "1"
8
+ desc: Verify required maintainer toolchain is installed (go, uv, git, gh, node, pnpm)
9
+ deps:
10
+ - task: :engine:_ts-build
12
11
  cmds:
13
- - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" toolchain:check
12
+ - task: :engine:invoke
13
+ vars:
14
+ ENGINE_CMD: 'toolchain-check'
15
+
16
+ check-consumer:
17
+ desc: Verify required consumer toolchain is installed (git, gh, node, pnpm, task) without Python/go/uv (#2022 Phase 3).
18
+ deps:
19
+ - task: :engine:_ts-build
20
+ cmds:
21
+ - task: :engine:invoke
22
+ vars:
23
+ ENGINE_CMD: 'toolchain-check --consumer'
package/tasks/vbrief.yml CHANGED
@@ -15,10 +15,11 @@ tasks:
15
15
  desc: Validate vBRIEF lifecycle folder structure and cross-file consistency
16
16
  dir: '{{.USER_WORKING_DIR}}'
17
17
  deps:
18
- - _ts-build
18
+ - task: :engine:_ts-build
19
19
  cmds:
20
- # Oracle/fallback (parity): scripts/vbrief_validate.py (#1828 Wave 8).
21
- - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" vbrief:validate --vbrief-dir "{{.USER_WORKING_DIR}}/vbrief"
20
+ - task: :engine:invoke
21
+ vars:
22
+ ENGINE_CMD: 'vbrief:validate --vbrief-dir "{{.USER_WORKING_DIR}}/vbrief"'
22
23
 
23
24
  preflight:
24
25
  # Implementation-intent preflight wrapper (#810 / PR #812 fixup;
package/tasks/verify.yml CHANGED
@@ -70,15 +70,11 @@ tasks:
70
70
  desc: "Detection-bound branch-protection gate (#747). Reads plan.policy.allowDirectCommitsToMaster from PROJECT-DEFINITION."
71
71
  dir: '{{.USER_WORKING_DIR}}'
72
72
  deps:
73
- - _ts-build
73
+ - task: :engine:_ts-build
74
74
  cmds:
75
- # Oracle/fallback (parity): scripts/preflight_branch.py (#1828 Wave 8).
76
- # `--allow-missing-project-definition` keeps the framework's own check
77
- # green for fresh checkouts that have not yet run `task setup` (the
78
- # bootstrap fallback path documented in #746 acceptance criterion E).
79
- # Production projects with PROJECT-DEFINITION present will still get
80
- # the full enforcement -- the flag is a no-op when the file exists.
81
- - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" verify:branch --project-root "{{.USER_WORKING_DIR}}" --allow-missing-project-definition
75
+ - task: :engine:invoke
76
+ vars:
77
+ ENGINE_CMD: 'verify:branch --project-root "{{.USER_WORKING_DIR}}" --allow-missing-project-definition'
82
78
 
83
79
  routing:
84
80
  desc: "Operator coding sub-agent model routing gate (#1739). Pre-dispatch (default): fails when a dispatched worker role has no decision in .deft/routing.local.json. Pass --advise for the non-blocking session-start disclosure; --roles a,b to widen the gated set; --provider to override the runtime."
@@ -145,7 +141,7 @@ tasks:
145
141
  desc: "Pre-`start_agent` cache-freshness gate (#1127). Refuses implementation dispatch when the triage cache is stale, missing, or the target issue's latest decision is not `accept`. Subscription-aware via plan.policy.triageScope[] (D12 / #1131). Flags: --for-issue N / --max-age-hours N / --allow-stale / --repo OWNER/NAME / --allow-missing-bootstrap (consumed by the framework's own `task check` so a fresh checkout passes -- consumers leave it OFF)."
146
142
  dir: '{{.USER_WORKING_DIR}}'
147
143
  deps:
148
- - _ts-build
144
+ - task: :engine:_ts-build
149
145
  # Per `conventions/task-caching.md` (#574): NO `sources:` / `generates:`
150
146
  # because the gate forwards user-facing flags via {{.CLI_ARGS}}
151
147
  # (--for-issue / --max-age-hours / --allow-stale / --repo) that
@@ -155,8 +151,9 @@ tasks:
155
151
  # `task triage:bootstrap`; consumer projects leave it off so a
156
152
  # missing cache fails their `task check` loudly.
157
153
  cmds:
158
- # Oracle/fallback (parity): scripts/preflight_cache.py --allow-missing-bootstrap (#1854 s5).
159
- - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" preflight-cache --project-root "{{.USER_WORKING_DIR}}" --allow-missing-bootstrap {{.CLI_ARGS}}
154
+ - task: :engine:invoke
155
+ vars:
156
+ ENGINE_CMD: 'preflight-cache --project-root "{{.USER_WORKING_DIR}}" --allow-missing-bootstrap {{.CLI_ARGS}}'
160
157
 
161
158
  codebase-map-fresh:
162
159
  desc: "Drift gate for the generated .planning/codebase/MAP.md projection (#1595 PR4)."
@@ -276,7 +273,7 @@ tasks:
276
273
  desc: "Pre-merge re-validation that pending/+active/ count is within plan.policy.wipCap (#1124 / D4 of #1119). Catches stale-branch merges + --force overrides. Default cap is 10 per umbrella #1119 Current Shape v3. The framework's own task check passes --allow-over-cap during landing-day overage; consumer projects MUST NOT pass that flag."
277
274
  dir: '{{.USER_WORKING_DIR}}'
278
275
  deps:
279
- - _ts-build
276
+ - task: :engine:_ts-build
280
277
  # Per `conventions/task-caching.md` (#574): NO `sources:` /
281
278
  # `generates:` because the gate forwards user-facing flags via
282
279
  # {{.CLI_ARGS}} (--allow-over-cap / --quiet) that go-task's
@@ -288,5 +285,6 @@ tasks:
288
285
  # OFF so a stale-branch / --force-merge over-cap state fails their
289
286
  # `task check` loudly.
290
287
  cmds:
291
- # Oracle/fallback (parity): scripts/preflight_wip_cap.py (#1828 Wave 8).
292
- - node "{{.DEFT_ROOT}}/packages/cli/dist/bin.js" verify:wip-cap --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}
288
+ - task: :engine:invoke
289
+ vars:
290
+ ENGINE_CMD: 'verify:wip-cap --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
@@ -206,6 +206,5 @@ Directive product commands use the `/deft:directive:*` namespace (#418 / #1670).
206
206
 
207
207
  **CLI compatibility:**
208
208
 
209
- - .deft/core/run bootstrap — CLI setup (terminal users)
210
- - .deft/core/run spec — CLI spec generation
209
+ The legacy Python `.deft/core/run` CLI is deprecated and is no longer a load-bearing operator path (#1933 Option 1, deprecate-by-disuse). Use the agent-driven setup skill for first-time setup and project/spec generation; if `deft` or `directive` will not run, read `.deft/core/UPGRADING.md`.
211
210
  <!-- /deft:managed-section -->