tcw-cli 2.4.0__tar.gz → 2.6.0__tar.gz

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 (190) hide show
  1. {tcw_cli-2.4.0/tcw_cli.egg-info → tcw_cli-2.6.0}/PKG-INFO +45 -12
  2. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/README.md +44 -11
  3. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/pyproject.toml +1 -1
  4. tcw_cli-2.6.0/tcw/__init__.py +1 -0
  5. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/serve/__init__.py +50 -0
  6. tcw_cli-2.4.0/tcw/serve/dist/client/assets/index-CCjF_dHr.js → tcw_cli-2.6.0/tcw/serve/dist/client/assets/index-CNxlcI8A.js +1 -1
  7. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/serve/dist/client/index.html +1 -1
  8. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/store/base.py +478 -29
  9. tcw_cli-2.6.0/tcw/store/config_edit.py +641 -0
  10. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/store/fs.py +617 -168
  11. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/taxonomy/cli.py +2 -2
  12. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/tracker/claim.py +18 -0
  13. tcw_cli-2.6.0/tcw/tracker/create.py +207 -0
  14. tcw_cli-2.6.0/tcw/tracker/intake.py +747 -0
  15. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/tracker/jira.py +20 -0
  16. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/tracker/ownership.py +51 -14
  17. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/tracker/progress.py +2 -2
  18. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/tracker/sync.py +476 -174
  19. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/validate.py +23 -1
  20. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/cli.py +1018 -102
  21. tcw_cli-2.6.0/tcw/work/procedures/decompose.md +23 -0
  22. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/projection.py +21 -1
  23. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/recursion.py +195 -35
  24. {tcw_cli-2.4.0 → tcw_cli-2.6.0/tcw_cli.egg-info}/PKG-INFO +45 -12
  25. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw_cli.egg-info/SOURCES.txt +13 -1
  26. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_capabilities.py +39 -0
  27. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_capabilities_federation.py +56 -14
  28. tcw_cli-2.6.0/tests/test_capability_gate_children.py +623 -0
  29. tcw_cli-2.6.0/tests/test_check_versions.py +308 -0
  30. tcw_cli-2.6.0/tests/test_child_status.py +649 -0
  31. tcw_cli-2.6.0/tests/test_config_edit.py +466 -0
  32. tcw_cli-2.6.0/tests/test_config_edit_writers.py +384 -0
  33. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_environment_hardness.py +10 -5
  34. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_epic_completable.py +36 -0
  35. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_external_work_store.py +56 -0
  36. tcw_cli-2.6.0/tests/test_legacy_store_config.py +346 -0
  37. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_multiproject.py +4 -1
  38. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_projection.py +18 -0
  39. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_repo_lifecycle.py +9 -4
  40. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_retention.py +125 -0
  41. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_serve.py +4 -2
  42. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_serve_write.py +122 -0
  43. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_session_bootstrap.py +179 -1
  44. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_store_editor.py +23 -2
  45. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_store_provisioning.py +129 -1
  46. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_taxonomy.py +238 -2
  47. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_claimability.py +18 -1
  48. tcw_cli-2.6.0/tests/test_tracker_cli.py +1683 -0
  49. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_client.py +7 -1
  50. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_comment.py +14 -1
  51. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_config.py +42 -19
  52. tcw_cli-2.6.0/tests/test_tracker_create.py +250 -0
  53. tcw_cli-2.6.0/tests/test_tracker_gate.py +125 -0
  54. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_help.py +24 -7
  55. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_import.py +67 -3
  56. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_inheritance.py +61 -12
  57. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_ownership.py +74 -0
  58. tcw_cli-2.6.0/tests/test_tracker_pre_backlog.py +852 -0
  59. tcw_cli-2.6.0/tests/test_tracker_strict.py +1687 -0
  60. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_surface.py +42 -0
  61. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_sync.py +1028 -170
  62. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_sync_config.py +4 -1
  63. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_validate.py +147 -2
  64. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_validate.py +14 -3
  65. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_work.py +160 -47
  66. tcw_cli-2.6.0/tests/test_work_start.py +48 -0
  67. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_worktree_completion.py +66 -0
  68. tcw_cli-2.4.0/tcw/__init__.py +0 -1
  69. tcw_cli-2.4.0/tcw/tracker/intake.py +0 -411
  70. tcw_cli-2.4.0/tcw/work/procedures/decompose.md +0 -18
  71. tcw_cli-2.4.0/tests/test_tracker_cli.py +0 -468
  72. tcw_cli-2.4.0/tests/test_tracker_strict.py +0 -834
  73. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/LICENSE +0 -0
  74. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/setup.cfg +0 -0
  75. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/capabilities/__init__.py +0 -0
  76. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/capabilities/cli.py +0 -0
  77. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/cli.py +0 -0
  78. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/harness.py +0 -0
  79. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/refs.py +0 -0
  80. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/serve/dist/client/assets/index-CXNiOIeg.css +0 -0
  81. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/serve/dist/client/theme-init.js +0 -0
  82. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/serve/dist/server.cjs +0 -0
  83. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/serve/runtime.py +0 -0
  84. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/stdin.py +0 -0
  85. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/store/__init__.py +0 -0
  86. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/store/checkouts.py +0 -0
  87. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/store/project.py +0 -0
  88. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/taxonomy/__init__.py +0 -0
  89. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/tracker/__init__.py +0 -0
  90. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/__init__.py +0 -0
  91. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/generate.py +0 -0
  92. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/hooks.py +0 -0
  93. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/procedures/audit-backlog.md +0 -0
  94. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/procedures/consolidate-plans.md +0 -0
  95. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/procedures/create-work.md +0 -0
  96. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/procedures/delegation.md +0 -0
  97. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/procedures/documentation-sync.md +0 -0
  98. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/procedures/post-mortem.md +0 -0
  99. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/procedures/search.md +0 -0
  100. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/procedures/triage-issues.md +0 -0
  101. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/procedures/unattended-work.md +0 -0
  102. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/prompts/implement.md +0 -0
  103. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/prompts/inbox.md +0 -0
  104. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/prompts/plan.md +0 -0
  105. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/prompts/postmortem.md +0 -0
  106. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/prompts/request.md +0 -0
  107. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/prompts/spec.md +0 -0
  108. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/prompts/verify.md +0 -0
  109. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/resolve.py +0 -0
  110. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw/work/templates.py +0 -0
  111. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw_cli.egg-info/dependency_links.txt +0 -0
  112. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw_cli.egg-info/entry_points.txt +0 -0
  113. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw_cli.egg-info/requires.txt +0 -0
  114. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tcw_cli.egg-info/top_level.txt +0 -0
  115. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_body_prompt.py +0 -0
  116. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_capabilities_reset.py +0 -0
  117. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_capabilities_rm.py +0 -0
  118. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_capabilities_sidecar.py +0 -0
  119. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_capability_ref_wording.py +0 -0
  120. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_cli_help_coverage.py +0 -0
  121. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_committed_paths.py +0 -0
  122. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_configuration_text_home.py +0 -0
  123. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_cut_version.py +0 -0
  124. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_documentation_config.py +0 -0
  125. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_documentation_prompt.py +0 -0
  126. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_documentation_sync_wiring.py +0 -0
  127. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_documented_cli_surface.py +0 -0
  128. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_dynamic_skill_marker.py +0 -0
  129. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_edit_type.py +0 -0
  130. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_eval_coverage.py +0 -0
  131. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_eval_files_changed.py +0 -0
  132. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_eval_fixture.py +0 -0
  133. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_eval_grading.py +0 -0
  134. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_eval_runner.py +0 -0
  135. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_falsification_rule.py +0 -0
  136. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_generate_hook.py +0 -0
  137. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_harness.py +0 -0
  138. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_inbox_title.py +0 -0
  139. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_lifecycle_baseline.py +0 -0
  140. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_lifecycle_hooks.py +0 -0
  141. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_lifecycle_inert.py +0 -0
  142. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_lifecycle_policy.py +0 -0
  143. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_lifecycle_validation.py +0 -0
  144. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_non_git_writes.py +0 -0
  145. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_plugin_manifests.py +0 -0
  146. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_procedure_config.py +0 -0
  147. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_procedure_verb.py +0 -0
  148. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_project_overrides.py +0 -0
  149. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_project_registry.py +0 -0
  150. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_prompt_fallback.py +0 -0
  151. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_qualified_ref.py +0 -0
  152. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_recursion.py +0 -0
  153. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_refs.py +0 -0
  154. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_remote_session_setup.py +0 -0
  155. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_resolve.py +0 -0
  156. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_resolve_procedure.py +0 -0
  157. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_scaffold.py +0 -0
  158. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_serve_descendants.py +0 -0
  159. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_serve_projection.py +0 -0
  160. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_serve_resolve.py +0 -0
  161. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_serve_runtime.py +0 -0
  162. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_shipped_procedures.py +0 -0
  163. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_shipped_prompts.py +0 -0
  164. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_show_json.py +0 -0
  165. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_skill_flow.py +0 -0
  166. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_skill_lifecycle_parity.py +0 -0
  167. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_skill_path_pointers.py +0 -0
  168. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_smoke.py +0 -0
  169. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_stage_validate.py +0 -0
  170. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_stage_verb.py +0 -0
  171. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_status_parity.py +0 -0
  172. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_stdin.py +0 -0
  173. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_stdin_cli.py +0 -0
  174. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_store_bounds.py +0 -0
  175. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_store_nodes.py +0 -0
  176. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_store_publication.py +0 -0
  177. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_subprocess_stdin.py +0 -0
  178. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tombstone.py +0 -0
  179. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_absent.py +0 -0
  180. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_binding.py +0 -0
  181. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_claim.py +0 -0
  182. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_hold.py +0 -0
  183. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_link.py +0 -0
  184. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_tracker_replay.py +0 -0
  185. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_unattended_work_skill.py +0 -0
  186. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_unpushed_version_script.py +0 -0
  187. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_validate_target.py +0 -0
  188. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_work_autocommit.py +0 -0
  189. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_work_review.py +0 -0
  190. {tcw_cli-2.4.0 → tcw_cli-2.6.0}/tests/test_work_tags.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tcw-cli
3
- Version: 2.4.0
3
+ Version: 2.6.0
4
4
  Summary: Taxonomy · Capabilities · Work — a storage-abstracted framework for describing and evolving a software project.
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -293,7 +293,9 @@ cannot run until the next one begins. That first session needs network access. I
293
293
  installs over an existing `pipx install tcw-cli` rather than beside it, and
294
294
  leaves a development checkout (`pip install -e .`) alone. If `tcw` goes missing
295
295
  anyway, `pipx install tcw-cli` is the whole fix — the **`setup`** skill
296
- carries the cases where it is not.
296
+ carries the cases where it is not. When the `tcw` CLI and the plugin's skills
297
+ come from different releases, the session tells you which versions they are and
298
+ what to run to line them up; it never stops your work.
297
299
 
298
300
  In **Codex**:
299
301
 
@@ -303,7 +305,9 @@ codex plugin add tcw@tcw
303
305
  ```
304
306
 
305
307
  Codex has no session-start hook, so ask the agent to run the **`setup`**
306
- skill — it runs the same install script Claude runs automatically.
308
+ skill — it runs the same install script Claude runs automatically. Every tcw
309
+ skill also asks the agent to run the plugin's version check once per session,
310
+ which warns when the CLI and the skills come from different releases.
307
311
 
308
312
  The plugin ships the skills and read-only review agents described in
309
313
  [Skills and Agents](#skills-and-agents).
@@ -609,8 +613,8 @@ behavior, changes to its internals, or changes to how the project itself runs.
609
613
  - **Blockers** are recorded on an item (`tcw work edit --blocked-by`). An item
610
614
  with an unresolved blocker cannot start, or be completed as done, without
611
615
  `--force`; there is no separate "blocked" status.
612
- - **Large items split.** A child item lives inside its parent's folder and moves
613
- with it. An **epic** groups related items, including items in other
616
+ - **Large items split.** A child item has its own status and keeps a link to its
617
+ parent, which cannot be closed while the child is open. An **epic** groups related items, including items in other
614
618
  repositories, and reports their combined status.
615
619
  - **Completing is checked.** `tcw work complete` prints the project's Definition
616
620
  of Done (a list you set in `docs/work/dod.yaml`) and refuses until you confirm
@@ -726,10 +730,12 @@ to the transitions.
726
730
  | an item is created | `tcw work inbox accept`, `new` | `tcw work tracker import <ticket>` claims the ticket (starts it and assigns it to you), then creates a backlog item whose intake is the ticket. With `inbox-query` set, `tcw work inbox accept <ticket>` does the same |
727
731
  | `request`, `spec`, `plan` stages | unchanged | unchanged |
728
732
  | at any time | — | `tcw work tracker link` / `unlink` records or removes the link between an existing item and a ticket; Jira itself is not touched |
729
- | `start` | moves the item | also claims a linked ticket, and posts a comment if `comments: true` |
730
- | `submit`, `rework`, `complete`, discard | moves the item | also moves a linked ticket to the Jira status mapped under `statuses`, and posts a comment if `comments: true` |
733
+ | an item has no ticket | — | `tcw work tracker create <slug>` makes one for it and binds it; `--all` sweeps every open item without one. Needs `work.tracker.create` and `statuses.backlog`. With `create.on-new`, filing an item makes its ticket |
734
+ | `start` | moves the item | also claims a linked ticket (assigns it to you) and moves it to the status mapped for `active`; a ticket already past that stays where it is. Posts a comment if `comments: true` |
735
+ | `submit`, `rework` | moves the item | refused before the item moves unless a linked ticket is assigned to you (`tcw work tracker claim` takes it). Then moves the ticket to the Jira status mapped under `statuses`, and posts a comment if `comments: true` |
736
+ | `complete`, discard | moves the item | moves a linked ticket to its mapped status whoever holds it — finishing work needs no claim — and posts a comment if `comments: true` |
731
737
  | the ticket could not follow | — | the item still moves; the command exits 1 and records the ticket as pending or conflicting; `tcw work tracker sync` retries |
732
- | the ticket is behind its item | — | with `tcw work tracker link --sync-status`, a ticket linked to work already under way is claimed and moved forward to where the item is |
738
+ | the ticket is behind its item | — | for a ticket linked to work already under way: `tcw work tracker claim <slug>`, then `tcw work tracker sync <slug>` |
733
739
  | the ticket does not match its item | — | `tcw work tracker sync <slug>` puts it where the item's status says, whether the ticket is ahead of the item or behind it; a backwards move says so |
734
740
 
735
741
  Setting `strict: true` changes several steps, so that no work happens without a
@@ -739,9 +745,17 @@ claimed ticket:
739
745
  and point you at `tcw work tracker import`. Where `inbox-query` is set, `inbox accept`
740
746
  of a ticket is allowed, because it claims the ticket.
741
747
  - `start` refuses an item with no ticket. For a linked item it claims the ticket
742
- first, and moves the item only if the claim worked.
743
- - `submit`, `rework` and `complete --resolution done` read the ticket first, and
744
- refuse unless it is assigned to you and where the item's last move left it.
748
+ first, through the transition `exclusive-claim-transition` names, and moves the
749
+ item only if the claim worked and the workflow would refuse a second person —
750
+ that is, it does not offer that transition again once the ticket has it. Every
751
+ strict claim checks this, `tracker claim` and `tracker import` included.
752
+ - Strict mode needs `exclusive-claim-transition` and `transitions.start` set, and
753
+ `tcw validate` reports either one missing: `tracker import`, the way work is
754
+ created, claims through `transitions.start`.
755
+ - `submit` and `rework` read the ticket first, and refuse unless it is assigned to
756
+ you and where the item's last move left it. `complete --resolution done` refuses
757
+ only if the ticket is not where the item's last move left it: who holds it does
758
+ not matter for finishing work.
745
759
  - Discarding is always allowed. `drop` refuses an item that was ever linked, so
746
760
  the record stays.
747
761
  - While Jira cannot be reached, those commands refuse, and `tcw serve` refuses
@@ -770,7 +784,11 @@ triaged. When it is set, `tcw work inbox list` prints two sections, `raw intake:
770
784
  `inbox accept` then take a ticket key as well as an inbox entry: an inbox entry with
771
785
  the same name wins, and `--ticket` reads the name as a ticket anyway. Accepting a
772
786
  ticket claims it, exactly as `tcw work tracker import` does. Write the query so it
773
- leaves out tickets that already have a work item; TCW does not filter them.
787
+ leaves out tickets that already have a work item; TCW does not filter them. A
788
+ triage status does not offer the transition that starts work, so to take tickets
789
+ straight from it, name the status and its way out under `pre-backlog` (for example
790
+ `pre-backlog: { Triage: Accept }`, with `statuses.backlog` set): every claim then
791
+ moves the ticket out of Triage to the backlog first, and says so.
774
792
 
775
793
  The credentials entries hold the **names** of environment variables, never the
776
794
  e-mail address or token themselves.
@@ -795,6 +813,21 @@ tcw work tracker link <slug> ENG-517 # TCW: records the link.
795
813
  tcw work start <slug> # TCW: backlog → active. Jira: if unassigned or yours, ENG-517 → In Progress, assigned to you
796
814
  ```
797
815
 
816
+ **Example 2a: making a ticket for an item that has none.**
817
+
818
+ ```sh
819
+ tcw work new "Speed up the checkout page" # TCW: creates a backlog item. Jira: unchanged
820
+ tcw work tracker create <slug> --dry-run # TCW: says what it would do. Jira: unchanged
821
+ tcw work tracker create <slug> # TCW: binds the new ticket. Jira: creates it, then moves it to statuses.backlog
822
+ tcw work tracker create --all # TCW: the same for every open item. Jira: one new ticket per item without one
823
+ ```
824
+
825
+ The move out of whatever status Jira starts issues in is part of creating the
826
+ ticket, not a courtesy: in a project with a triage column, that starting status
827
+ is usually the one `inbox-query` selects, so a ticket left there would come back
828
+ through `tcw work inbox` as new inbound work. Run `--all` with `--dry-run`
829
+ first — nothing here can delete a ticket.
830
+
798
831
  **Example 3: taking a ticket without starting it, and handing it back.**
799
832
 
800
833
  ```sh
@@ -77,7 +77,9 @@ cannot run until the next one begins. That first session needs network access. I
77
77
  installs over an existing `pipx install tcw-cli` rather than beside it, and
78
78
  leaves a development checkout (`pip install -e .`) alone. If `tcw` goes missing
79
79
  anyway, `pipx install tcw-cli` is the whole fix — the **`setup`** skill
80
- carries the cases where it is not.
80
+ carries the cases where it is not. When the `tcw` CLI and the plugin's skills
81
+ come from different releases, the session tells you which versions they are and
82
+ what to run to line them up; it never stops your work.
81
83
 
82
84
  In **Codex**:
83
85
 
@@ -87,7 +89,9 @@ codex plugin add tcw@tcw
87
89
  ```
88
90
 
89
91
  Codex has no session-start hook, so ask the agent to run the **`setup`**
90
- skill — it runs the same install script Claude runs automatically.
92
+ skill — it runs the same install script Claude runs automatically. Every tcw
93
+ skill also asks the agent to run the plugin's version check once per session,
94
+ which warns when the CLI and the skills come from different releases.
91
95
 
92
96
  The plugin ships the skills and read-only review agents described in
93
97
  [Skills and Agents](#skills-and-agents).
@@ -393,8 +397,8 @@ behavior, changes to its internals, or changes to how the project itself runs.
393
397
  - **Blockers** are recorded on an item (`tcw work edit --blocked-by`). An item
394
398
  with an unresolved blocker cannot start, or be completed as done, without
395
399
  `--force`; there is no separate "blocked" status.
396
- - **Large items split.** A child item lives inside its parent's folder and moves
397
- with it. An **epic** groups related items, including items in other
400
+ - **Large items split.** A child item has its own status and keeps a link to its
401
+ parent, which cannot be closed while the child is open. An **epic** groups related items, including items in other
398
402
  repositories, and reports their combined status.
399
403
  - **Completing is checked.** `tcw work complete` prints the project's Definition
400
404
  of Done (a list you set in `docs/work/dod.yaml`) and refuses until you confirm
@@ -510,10 +514,12 @@ to the transitions.
510
514
  | an item is created | `tcw work inbox accept`, `new` | `tcw work tracker import <ticket>` claims the ticket (starts it and assigns it to you), then creates a backlog item whose intake is the ticket. With `inbox-query` set, `tcw work inbox accept <ticket>` does the same |
511
515
  | `request`, `spec`, `plan` stages | unchanged | unchanged |
512
516
  | at any time | — | `tcw work tracker link` / `unlink` records or removes the link between an existing item and a ticket; Jira itself is not touched |
513
- | `start` | moves the item | also claims a linked ticket, and posts a comment if `comments: true` |
514
- | `submit`, `rework`, `complete`, discard | moves the item | also moves a linked ticket to the Jira status mapped under `statuses`, and posts a comment if `comments: true` |
517
+ | an item has no ticket | — | `tcw work tracker create <slug>` makes one for it and binds it; `--all` sweeps every open item without one. Needs `work.tracker.create` and `statuses.backlog`. With `create.on-new`, filing an item makes its ticket |
518
+ | `start` | moves the item | also claims a linked ticket (assigns it to you) and moves it to the status mapped for `active`; a ticket already past that stays where it is. Posts a comment if `comments: true` |
519
+ | `submit`, `rework` | moves the item | refused before the item moves unless a linked ticket is assigned to you (`tcw work tracker claim` takes it). Then moves the ticket to the Jira status mapped under `statuses`, and posts a comment if `comments: true` |
520
+ | `complete`, discard | moves the item | moves a linked ticket to its mapped status whoever holds it — finishing work needs no claim — and posts a comment if `comments: true` |
515
521
  | the ticket could not follow | — | the item still moves; the command exits 1 and records the ticket as pending or conflicting; `tcw work tracker sync` retries |
516
- | the ticket is behind its item | — | with `tcw work tracker link --sync-status`, a ticket linked to work already under way is claimed and moved forward to where the item is |
522
+ | the ticket is behind its item | — | for a ticket linked to work already under way: `tcw work tracker claim <slug>`, then `tcw work tracker sync <slug>` |
517
523
  | the ticket does not match its item | — | `tcw work tracker sync <slug>` puts it where the item's status says, whether the ticket is ahead of the item or behind it; a backwards move says so |
518
524
 
519
525
  Setting `strict: true` changes several steps, so that no work happens without a
@@ -523,9 +529,17 @@ claimed ticket:
523
529
  and point you at `tcw work tracker import`. Where `inbox-query` is set, `inbox accept`
524
530
  of a ticket is allowed, because it claims the ticket.
525
531
  - `start` refuses an item with no ticket. For a linked item it claims the ticket
526
- first, and moves the item only if the claim worked.
527
- - `submit`, `rework` and `complete --resolution done` read the ticket first, and
528
- refuse unless it is assigned to you and where the item's last move left it.
532
+ first, through the transition `exclusive-claim-transition` names, and moves the
533
+ item only if the claim worked and the workflow would refuse a second person —
534
+ that is, it does not offer that transition again once the ticket has it. Every
535
+ strict claim checks this, `tracker claim` and `tracker import` included.
536
+ - Strict mode needs `exclusive-claim-transition` and `transitions.start` set, and
537
+ `tcw validate` reports either one missing: `tracker import`, the way work is
538
+ created, claims through `transitions.start`.
539
+ - `submit` and `rework` read the ticket first, and refuse unless it is assigned to
540
+ you and where the item's last move left it. `complete --resolution done` refuses
541
+ only if the ticket is not where the item's last move left it: who holds it does
542
+ not matter for finishing work.
529
543
  - Discarding is always allowed. `drop` refuses an item that was ever linked, so
530
544
  the record stays.
531
545
  - While Jira cannot be reached, those commands refuse, and `tcw serve` refuses
@@ -554,7 +568,11 @@ triaged. When it is set, `tcw work inbox list` prints two sections, `raw intake:
554
568
  `inbox accept` then take a ticket key as well as an inbox entry: an inbox entry with
555
569
  the same name wins, and `--ticket` reads the name as a ticket anyway. Accepting a
556
570
  ticket claims it, exactly as `tcw work tracker import` does. Write the query so it
557
- leaves out tickets that already have a work item; TCW does not filter them.
571
+ leaves out tickets that already have a work item; TCW does not filter them. A
572
+ triage status does not offer the transition that starts work, so to take tickets
573
+ straight from it, name the status and its way out under `pre-backlog` (for example
574
+ `pre-backlog: { Triage: Accept }`, with `statuses.backlog` set): every claim then
575
+ moves the ticket out of Triage to the backlog first, and says so.
558
576
 
559
577
  The credentials entries hold the **names** of environment variables, never the
560
578
  e-mail address or token themselves.
@@ -579,6 +597,21 @@ tcw work tracker link <slug> ENG-517 # TCW: records the link.
579
597
  tcw work start <slug> # TCW: backlog → active. Jira: if unassigned or yours, ENG-517 → In Progress, assigned to you
580
598
  ```
581
599
 
600
+ **Example 2a: making a ticket for an item that has none.**
601
+
602
+ ```sh
603
+ tcw work new "Speed up the checkout page" # TCW: creates a backlog item. Jira: unchanged
604
+ tcw work tracker create <slug> --dry-run # TCW: says what it would do. Jira: unchanged
605
+ tcw work tracker create <slug> # TCW: binds the new ticket. Jira: creates it, then moves it to statuses.backlog
606
+ tcw work tracker create --all # TCW: the same for every open item. Jira: one new ticket per item without one
607
+ ```
608
+
609
+ The move out of whatever status Jira starts issues in is part of creating the
610
+ ticket, not a courtesy: in a project with a triage column, that starting status
611
+ is usually the one `inbox-query` selects, so a ticket left there would come back
612
+ through `tcw work inbox` as new inbound work. Run `--all` with `--dry-run`
613
+ first — nothing here can delete a ticket.
614
+
582
615
  **Example 3: taking a ticket without starting it, and handing it back.**
583
616
 
584
617
  ```sh
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tcw-cli"
7
- version = "2.4.0"
7
+ version = "2.6.0"
8
8
  description = "Taxonomy · Capabilities · Work — a storage-abstracted framework for describing and evolving a software project."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1 @@
1
+ __version__ = "2.6.0"
@@ -219,6 +219,14 @@ def _strict_refuses(work, action: str, slug: str = "", body: dict | None = None)
219
219
  strict mode each change that needs one is sent to the `tcw work` command that
220
220
  does. Epics and discards are not gated, as on the command line.
221
221
  """
222
+ # Asked first, and not a strict-mode rule: see `created_but_unbound_refusal`.
223
+ # The CLI offers two ways out of this and the web app used to offer one — the
224
+ # one that makes the item permanently undroppable — so both now say the same
225
+ # words, from the same place.
226
+ from tcw.tracker.intake import created_but_unbound_refusal
227
+ if action == "drop" and slug:
228
+ if refusal := created_but_unbound_refusal(work, slug):
229
+ return refusal
222
230
  if not work.tracker_strict():
223
231
  return None
224
232
  body = body or {}
@@ -239,6 +247,41 @@ def _strict_refuses(work, action: str, slug: str = "", body: dict | None = None)
239
247
  return None
240
248
 
241
249
 
250
+ def _owe_ticket_if_configured(work, slug: str) -> bool:
251
+ """Record that a web-filed item is owed a ticket, when the project wants one.
252
+
253
+ Returns whether anything was written, so the caller knows to re-read.
254
+
255
+ `work.tracker.create.on-new` says filing an item makes its ticket. The web
256
+ app deliberately runs no tracker code — no credentials, no network, no page
257
+ render waiting on Jira — so it cannot make it. What it can do is leave the
258
+ debt where the board and `tcw work tracker create --all` will find it, which
259
+ is what the owed record is for.
260
+
261
+ Nothing here may fail the creation that just succeeded. The item exists and
262
+ the response is about to be sent; an item the user never sees because
263
+ recording a note failed is worse than a note that is missing.
264
+ """
265
+ from contextlib import suppress
266
+ from datetime import date
267
+
268
+ from tcw.tracker.intake import record_owed
269
+
270
+ # Reading the configuration is inside the guard too. It fails closed today,
271
+ # returning None rather than raising, but the promise above is "nothing here
272
+ # may fail the creation that just succeeded" — and a promise that holds only
273
+ # because of what another function happens to do is not the promise.
274
+ with suppress(Exception): # see the docstring
275
+ config = work.tracker_config()
276
+ if config is None or config.create is None or not config.create.on_new:
277
+ return False
278
+ record_owed(work, slug, since=date.today().isoformat(),
279
+ reason="filed in the web app, which does not reach the "
280
+ "tracker; `tcw work tracker create` makes it")
281
+ return True
282
+ return False
283
+
284
+
242
285
  def _map_store_error(e: Exception) -> tuple[int, bytes]:
243
286
  """Map store-level exceptions to HTTP status codes and JSON error bodies.
244
287
 
@@ -867,6 +910,13 @@ class TcwHandler(BaseHTTPRequestHandler):
867
910
  type=type_val if type_val else "",
868
911
  tags=tags,
869
912
  )
913
+ if _owe_ticket_if_configured(work, detail.item.slug):
914
+ # Re-read: `detail` predates the owed record, so the
915
+ # response would say `tracker: null` for an item that just
916
+ # got one, and carry a `tracker.yaml` revision already
917
+ # stale — which the next sidecar write from the page would
918
+ # be rejected on.
919
+ detail = work.get_detail(detail.item.slug) or detail
870
920
  response = {
871
921
  "item": _item_payload(work, detail.item.slug, detail.item),
872
922
  "coreRevision": detail.core_revision,
@@ -164,7 +164,7 @@ ${n}</tr>
164
164
  `}tablecell(n){let r=this.parser.parseInline(n.tokens),l=n.header?"th":"td";return(n.align?`<${l} align="${n.align}">`:`<${l}>`)+r+`</${l}>
165
165
  `}strong({tokens:n}){return`<strong>${this.parser.parseInline(n)}</strong>`}em({tokens:n}){return`<em>${this.parser.parseInline(n)}</em>`}codespan({text:n}){return`<code>${lr(n,!0)}</code>`}br(n){return"<br>"}del({tokens:n}){return`<del>${this.parser.parseInline(n)}</del>`}link({href:n,title:r,tokens:l}){let o=this.parser.parseInline(l),s=Fb(n);if(s===null)return o;n=s;let c='<a href="'+n+'"';return r&&(c+=' title="'+lr(r)+'"'),c+=">"+o+"</a>",c}image({href:n,title:r,text:l,tokens:o}){o&&(l=this.parser.parseInline(o,this.parser.textRenderer));let s=Fb(n);if(s===null)return lr(l);n=s;let c=`<img src="${n}" alt="${l}"`;return r&&(c+=` title="${lr(r)}"`),c+=">",c}text(n){return"tokens"in n&&n.tokens?this.parser.parseInline(n.tokens):"escaped"in n&&n.escaped?n.text:lr(n.text)}},Jp=class{strong({text:n}){return n}em({text:n}){return n}codespan({text:n}){return n}del({text:n}){return n}html({text:n}){return n}text({text:n}){return n}link({text:n}){return""+n}image({text:n}){return""+n}br(){return""}},Vr=class kh{options;renderer;textRenderer;constructor(r){this.options=r||Tl,this.options.renderer=this.options.renderer||new eu,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new Jp}static parse(r,l){return new kh(l).parse(r)}static parseInline(r,l){return new kh(l).parseInline(r)}parse(r,l=!0){let o="";for(let s=0;s<r.length;s++){let c=r[s];if(this.options.extensions?.renderers?.[c.type]){let d=c,h=this.options.extensions.renderers[d.type].call({parser:this},d);if(h!==!1||!["space","hr","heading","code","table","blockquote","list","html","def","paragraph","text"].includes(d.type)){o+=h||"";continue}}let u=c;switch(u.type){case"space":{o+=this.renderer.space(u);continue}case"hr":{o+=this.renderer.hr(u);continue}case"heading":{o+=this.renderer.heading(u);continue}case"code":{o+=this.renderer.code(u);continue}case"table":{o+=this.renderer.table(u);continue}case"blockquote":{o+=this.renderer.blockquote(u);continue}case"list":{o+=this.renderer.list(u);continue}case"html":{o+=this.renderer.html(u);continue}case"def":{o+=this.renderer.def(u);continue}case"paragraph":{o+=this.renderer.paragraph(u);continue}case"text":{let d=u,h=this.renderer.text(d);for(;s+1<r.length&&r[s+1].type==="text";)d=r[++s],h+=`
166
166
  `+this.renderer.text(d);l?o+=this.renderer.paragraph({type:"paragraph",raw:h,text:h,tokens:[{type:"text",raw:h,text:h,escaped:!0}]}):o+=h;continue}default:{let d='Token with "'+u.type+'" type was not found.';if(this.options.silent)return console.error(d),"";throw new Error(d)}}}return o}parseInline(r,l=this.renderer){let o="";for(let s=0;s<r.length;s++){let c=r[s];if(this.options.extensions?.renderers?.[c.type]){let d=this.options.extensions.renderers[c.type].call({parser:this},c);if(d!==!1||!["escape","html","link","image","strong","em","codespan","br","del","text"].includes(c.type)){o+=d||"";continue}}let u=c;switch(u.type){case"escape":{o+=l.text(u);break}case"html":{o+=l.html(u);break}case"link":{o+=l.link(u);break}case"image":{o+=l.image(u);break}case"strong":{o+=l.strong(u);break}case"em":{o+=l.em(u);break}case"codespan":{o+=l.codespan(u);break}case"br":{o+=l.br(u);break}case"del":{o+=l.del(u);break}case"text":{o+=l.text(u);break}default:{let d='Token with "'+u.type+'" type was not found.';if(this.options.silent)return console.error(d),"";throw new Error(d)}}}return o}},Pi=class{options;block;constructor(n){this.options=n||Tl}static passThroughHooks=new Set(["preprocess","postprocess","processAllTokens","emStrongMask"]);static passThroughHooksRespectAsync=new Set(["preprocess","postprocess","processAllTokens"]);preprocess(n){return n}postprocess(n){return n}processAllTokens(n){return n}emStrongMask(n){return n}provideLexer(){return this.block?qr.lex:qr.lexInline}provideParser(){return this.block?Vr.parse:Vr.parseInline}},Gk=class{defaults=Yp();options=this.setOptions;parse=this.parseMarkdown(!0);parseInline=this.parseMarkdown(!1);Parser=Vr;Renderer=eu;TextRenderer=Jp;Lexer=qr;Tokenizer=Jc;Hooks=Pi;constructor(...n){this.use(...n)}walkTokens(n,r){let l=[];for(let o of n)switch(l=l.concat(r.call(this,o)),o.type){case"table":{let s=o;for(let c of s.header)l=l.concat(this.walkTokens(c.tokens,r));for(let c of s.rows)for(let u of c)l=l.concat(this.walkTokens(u.tokens,r));break}case"list":{let s=o;l=l.concat(this.walkTokens(s.items,r));break}default:{let s=o;this.defaults.extensions?.childTokens?.[s.type]?this.defaults.extensions.childTokens[s.type].forEach(c=>{let u=s[c].flat(1/0);l=l.concat(this.walkTokens(u,r))}):s.tokens&&(l=l.concat(this.walkTokens(s.tokens,r)))}}return l}use(...n){let r=this.defaults.extensions||{renderers:{},childTokens:{}};return n.forEach(l=>{let o={...l};if(o.async=this.defaults.async||o.async||!1,l.extensions&&(l.extensions.forEach(s=>{if(!s.name)throw new Error("extension name required");if("renderer"in s){let c=r.renderers[s.name];c?r.renderers[s.name]=function(...u){let d=s.renderer.apply(this,u);return d===!1&&(d=c.apply(this,u)),d}:r.renderers[s.name]=s.renderer}if("tokenizer"in s){if(!s.level||s.level!=="block"&&s.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let c=r[s.level];c?c.unshift(s.tokenizer):r[s.level]=[s.tokenizer],s.start&&(s.level==="block"?r.startBlock?r.startBlock.push(s.start):r.startBlock=[s.start]:s.level==="inline"&&(r.startInline?r.startInline.push(s.start):r.startInline=[s.start]))}"childTokens"in s&&s.childTokens&&(r.childTokens[s.name]=s.childTokens)}),o.extensions=r),l.renderer){let s=this.defaults.renderer||new eu(this.defaults);for(let c in l.renderer){if(!(c in s))throw new Error(`renderer '${c}' does not exist`);if(["options","parser"].includes(c))continue;let u=c,d=l.renderer[u],h=s[u];s[u]=(...g)=>{let v=d.apply(s,g);return v===!1&&(v=h.apply(s,g)),v||""}}o.renderer=s}if(l.tokenizer){let s=this.defaults.tokenizer||new Jc(this.defaults);for(let c in l.tokenizer){if(!(c in s))throw new Error(`tokenizer '${c}' does not exist`);if(["options","rules","lexer"].includes(c))continue;let u=c,d=l.tokenizer[u],h=s[u];s[u]=(...g)=>{let v=d.apply(s,g);return v===!1&&(v=h.apply(s,g)),v}}o.tokenizer=s}if(l.hooks){let s=this.defaults.hooks||new Pi;for(let c in l.hooks){if(!(c in s))throw new Error(`hook '${c}' does not exist`);if(["options","block"].includes(c))continue;let u=c,d=l.hooks[u],h=s[u];Pi.passThroughHooks.has(c)?s[u]=g=>{if(this.defaults.async&&Pi.passThroughHooksRespectAsync.has(c))return Promise.resolve(d.call(s,g)).then(y=>h.call(s,y));let v=d.call(s,g);return h.call(s,v)}:s[u]=(...g)=>{let v=d.apply(s,g);return v===!1&&(v=h.apply(s,g)),v}}o.hooks=s}if(l.walkTokens){let s=this.defaults.walkTokens,c=l.walkTokens;o.walkTokens=function(u){let d=[];return d.push(c.call(this,u)),s&&(d=d.concat(s.call(this,u))),d}}this.defaults={...this.defaults,...o}}),this}setOptions(n){return this.defaults={...this.defaults,...n},this}lexer(n,r){return qr.lex(n,r??this.defaults)}parser(n,r){return Vr.parse(n,r??this.defaults)}parseMarkdown(n){return(r,l)=>{let o={...l},s={...this.defaults,...o},c=this.onError(!!s.silent,!!s.async);if(this.defaults.async===!0&&o.async===!1)return c(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof r>"u"||r===null)return c(new Error("marked(): input parameter is undefined or null"));if(typeof r!="string")return c(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(r)+", string expected"));s.hooks&&(s.hooks.options=s,s.hooks.block=n);let u=s.hooks?s.hooks.provideLexer():n?qr.lex:qr.lexInline,d=s.hooks?s.hooks.provideParser():n?Vr.parse:Vr.parseInline;if(s.async)return Promise.resolve(s.hooks?s.hooks.preprocess(r):r).then(h=>u(h,s)).then(h=>s.hooks?s.hooks.processAllTokens(h):h).then(h=>s.walkTokens?Promise.all(this.walkTokens(h,s.walkTokens)).then(()=>h):h).then(h=>d(h,s)).then(h=>s.hooks?s.hooks.postprocess(h):h).catch(c);try{s.hooks&&(r=s.hooks.preprocess(r));let h=u(r,s);s.hooks&&(h=s.hooks.processAllTokens(h)),s.walkTokens&&this.walkTokens(h,s.walkTokens);let g=d(h,s);return s.hooks&&(g=s.hooks.postprocess(g)),g}catch(h){return c(h)}}}onError(n,r){return l=>{if(l.message+=`
167
- Please report this to https://github.com/markedjs/marked.`,n){let o="<p>An error occurred:</p><pre>"+lr(l.message+"",!0)+"</pre>";return r?Promise.resolve(o):o}if(r)return Promise.reject(l);throw l}}},xl=new Gk;function st(n,r){return xl.parse(n,r)}st.options=st.setOptions=function(n){return xl.setOptions(n),st.defaults=xl.defaults,uw(st.defaults),st};st.getDefaults=Yp;st.defaults=Tl;st.use=function(...n){return xl.use(...n),st.defaults=xl.defaults,uw(st.defaults),st};st.walkTokens=function(n,r){return xl.walkTokens(n,r)};st.parseInline=xl.parseInline;st.Parser=Vr;st.parser=Vr.parse;st.Renderer=eu;st.TextRenderer=Jp;st.Lexer=qr;st.lexer=qr.lex;st.Tokenizer=Jc;st.Hooks=Pi;st.parse=st;st.options;st.setOptions;st.use;st.walkTokens;st.parseInline;Vr.parse;qr.lex;function Sw(n,r,l,o,s){n.preventDefault();const c=d=>{const h=r.getBoundingClientRect();s(Math.min(o,Math.max(l,(d.clientX-h.left)/h.width)))},u=()=>{window.removeEventListener("pointermove",c),window.removeEventListener("pointerup",u)};window.addEventListener("pointermove",c),window.addEventListener("pointerup",u)}function Yk(){const n=()=>({work:new Set,taxonomy:new Set,capabilities:new Set});try{const r=JSON.parse(localStorage.getItem("tcw.treeExpanded")??"{}");return{work:new Set(r.work),taxonomy:new Set(r.taxonomy),capabilities:new Set(r.capabilities)}}catch{return n()}}function Ik(){let n=0;for(;document.getElementById(`tcw-off-board-${n}`);)n+=1;return`tcw-off-board-${n}`}function Zb(n,r){n.dataset.tcwRef=r,n.removeAttribute("href")}function Fk(n,r){n.classList.add("tcw-unhosted"),n.title=`Project ${r} is not included in this board`;const l=document.createElement("span");l.className="tcw-project-badge",l.textContent=r;const o=document.createElement("span");o.className="tcw-sr-only",o.id=Ik(),o.textContent=n.title,n.setAttribute("aria-describedby",o.id),n.after(l),l.after(o)}function Qi({source:n,resolveLinks:r=!1}){const l=p.useRef(null),o=p.useMemo(()=>st.parse(n||""),[n]);return p.useEffect(()=>{if(!r||!l.current)return;const s=[...l.current.querySelectorAll('a[href^="tcw://"]')];if(!s.length)return;const c=s.map(d=>d.getAttribute("href"));let u=!1;return yo("/api/resolve","POST",{uris:c}).then(d=>{if(u)return;const h=l.current?.querySelectorAll('a[href^="tcw://"]')??[];for(const g of h){const v=g.getAttribute("href"),y=d.data?.[v];y?.ok&&y.axis&&y.key?(g.href=Nh(y.axis,y.key),g.dataset.navAxis=y.axis,g.dataset.navKey=y.key):y?.reason==="unhosted-project"&&y.project?(Zb(g,v),Fk(g,y.project)):(Zb(g,v),g.classList.add("tcw-inert"),g.title=y?.detail||v)}}),()=>{u=!0}},[o,r]),b.jsx("article",{ref:l,className:"body",dangerouslySetInnerHTML:{__html:o}})}function nh({children:n}){return b.jsx("div",{className:"fields",children:n})}function un({name:n,value:r}){return b.jsxs(cs,{className:"field",size:"1",children:[b.jsx(qe,{as:"div",color:"gray",size:"1",children:n}),b.jsx(qe,{as:"div",size:"2",children:String(r??"-")})]})}function Xk({binding:n}){return b.jsxs(cs,{className:"field",size:"1",children:[b.jsx(qe,{as:"div",color:"gray",size:"1",children:"Ticket"}),b.jsx(qe,{as:"div",size:"2",children:"problem"in n?`tracker.yaml cannot be read: ${n.problem}`:b.jsxs(b.Fragment,{children:[n.ticket.url?b.jsx("a",{href:n.ticket.url,target:"_blank",rel:"noreferrer",children:n.ticket.key}):n.ticket.key,` · ${n.provider} · part ${n.part}`,n.sync&&("problem"in n.sync?` · sync record cannot be read: ${n.sync.problem}`:` · ${n.sync.state}: ${n.sync.reason}`)]})})]})}function tu({errors:n}){return n.length?b.jsx(Br,{color:"red",role:"alert",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"Validation errors"}),b.jsx("ul",{children:n.map(r=>b.jsx("li",{children:r},r))})]})}):null}function ww({title:n,children:r,onClose:l}){return b.jsx(IS,{open:!0,onOpenChange:o=>{o||l()},children:b.jsxs(FS,{className:"modal-box",maxWidth:"560px",children:[b.jsxs(We,{justify:"between",align:"center",mb:"3",children:[b.jsx(XS,{children:n}),b.jsx(KS,{children:b.jsx(Qr,{className:"modal-dismiss",variant:"ghost",type:"button","aria-label":"Close",children:"×"})})]}),r]})})}function Kk({nodes:n,axis:r,selected:l,expanded:o,onToggle:s,onSelect:c,visible:u}){const d=(h,g)=>b.jsx("div",{className:"tree-children",role:g===0?"none":"group",children:h.map(v=>{const y=v.children.length>0,x=o.has(v.path),w=v.item?Dh(r,v.item):v.path;return b.jsxs("div",{className:"tree-node",role:"none",children:[b.jsxs("div",{className:"tree-row",role:"none",children:[Array.from({length:g},(E,T)=>b.jsx("span",{className:"tree-indent"},T)),y?b.jsx(Qr,{className:"tree-toggle",variant:"ghost",type:"button",tabIndex:-1,"aria-label":x?`Collapse ${v.name}`:`Expand ${v.name}`,"aria-expanded":x,onClick:()=>s(v.path),children:x?"▾":"▸"}):b.jsx("span",{className:"tree-spacer"}),v.item?b.jsxs("div",{className:"tree-item-content",children:[b.jsxs(et,{type:"button",variant:"soft",role:"treeitem","aria-level":g+1,"data-tree-path":v.path,"aria-expanded":y?x:void 0,"aria-selected":l===w,className:`item item-${r}${r==="work"?` st-${v.item.status}`:""}${l===w?" active":""}${u(v.item)?"":" ancestor-dim"}`,onClick:()=>c(w),children:[b.jsx("div",{className:"item-title",children:lk(r,v.item)}),b.jsx("div",{className:"item-meta",children:b.jsx(Zk,{axis:r,item:v.item})})]}),r==="work"&&b.jsx(Gp,{content:b.jsx("span",{className:"copy-slug-tooltip",children:"Copy slug"}),children:b.jsx(Qr,{className:"copy-slug",variant:"ghost",type:"button","aria-label":"Copy slug to clipboard",onClick:()=>void navigator.clipboard.writeText(w),children:"⎘"})})]}):b.jsx(et,{className:"tree-folder",variant:"ghost",type:"button",role:"treeitem","aria-level":g+1,"data-tree-path":v.path,"aria-expanded":x,onClick:()=>s(v.path),children:v.name})]}),y&&x?d(v.children,g+1):null]},v.path)})});return d(n,0)}function Zk({axis:n,item:r}){if(n==="work"){const o=r;return b.jsxs("div",{children:[b.jsx("span",{className:"work-status",children:o.status})," · ",[o.effort&&`effort ${o.effort}`,o.complexity&&`complexity ${o.complexity}`,o.tags?.length&&`tags ${o.tags.join(", ")}`].filter(Boolean).join(" · "),b.jsx(Eo,{value:o.modified})]})}if(n==="taxonomy"){const o=r;return b.jsxs("div",{children:[b.jsx("div",{children:[o.kind,o.origin].filter(Boolean).join(" · ")}),b.jsx(Eo,{value:o.modified})]})}const l=r;return b.jsxs("div",{children:[[l.status,l.origin!=="local"&&l.origin].filter(Boolean).join(" · "),b.jsx(Eo,{value:l.modified})]})}function Qb({value:n,onChange:r,placeholder:l="Write Markdown..."}){const[o,s]=p.useState(.5);return b.jsxs("div",{className:"md-editor",style:{"--md-split":`${o*100}%`},children:[b.jsx(aw,{className:"md-input","aria-label":"Markdown",value:n,placeholder:l,onChange:c=>r(c.target.value)}),b.jsx("div",{className:"md-resizer",role:"separator","aria-orientation":"vertical","aria-label":"Resize Markdown editor",onPointerDown:c=>Sw(c,c.currentTarget.parentElement,.2,.8,s)}),b.jsx("div",{className:"md-preview",children:b.jsx(Qi,{source:n})})]})}function pl({label:n,value:r,onChange:l,type:o="text",placeholder:s}){return b.jsxs("label",{className:"field-group",children:[b.jsx(qe,{color:"gray",size:"1",weight:"bold",children:n}),b.jsx(Eu,{className:"field-input","aria-label":n,type:o,value:r,placeholder:s,onChange:c=>l(c.target.value)})]})}function Ro({label:n,value:r,options:l,onChange:o}){return b.jsxs("label",{className:"field-group",children:[b.jsx(qe,{color:"gray",size:"1",weight:"bold",children:n}),b.jsxs(Hp,{value:r||"__unset__",onValueChange:s=>o(s==="__unset__"?"":s),children:[b.jsx(qp,{"aria-label":n,className:"field-select"}),b.jsx(Vp,{children:l.map(s=>b.jsx(Qc,{value:s||"__unset__",children:s||"(unset)"},s||"__unset__"))})]})]})}function Qk(){const{preference:n,setPreference:r}=IO();return b.jsxs(Lp,{children:[b.jsx(Gp,{content:"Settings",children:b.jsx(Pp,{children:b.jsx(Qr,{"aria-label":"Settings",variant:"soft",children:b.jsx(sk,{})})})}),b.jsx(Up,{align:"end",width:"220px",children:b.jsxs(We,{direction:"column",gap:"3",children:[b.jsx(Dn,{as:"h2",size:"3",children:"Appearance"}),b.jsx(JS,{value:n,onValueChange:l=>r(iw(l)),children:b.jsx(We,{direction:"column",gap:"2",children:["light","dark","system"].map(l=>b.jsx(qe,{as:"label",size:"2",children:b.jsxs(We,{align:"center",gap:"2",children:[b.jsx(ew,{value:l}),l[0].toUpperCase()+l.slice(1)]})},l))})})]})})]})}const qc=["backlog","active","review","completed","discarded"],Wk=[{name:"initial-request",label:"Initial Request"},{name:"spec",label:"Spec"},{name:"plan",label:"Implementation Plan"}];function Jk({item:n,artifacts:r,onEditInitialRequest:l,onEditArtifact:o,onReadArtifact:s}){const[c,u]=p.useState("initial-request"),[d,h]=p.useState({});p.useEffect(()=>{u("initial-request"),h({})},[n.slug]);const g=E=>{const T=r.find(A=>A.name===E),R=d[E];!T?.present||R?.content!==void 0||R?.loading||(h(A=>({...A,[E]:{loading:!0}})),s(n.slug,E).then(A=>{h(O=>({...O,[E]:{content:A.content}}))}).catch(A=>{h(O=>({...O,[E]:{error:A instanceof Error?A.message:String(A)}}))}))},v=r.find(E=>E.name==="initial-request")?.present??!1,y=c==="initial-request"?void 0:r.find(E=>E.name===c),x=c==="initial-request"?void 0:d[c],w=()=>{c!=="initial-request"&&g(c)};return b.jsxs(tw,{className:"work-document-tabs",value:c,onValueChange:E=>{(E==="initial-request"||E==="spec"||E==="plan")&&(u(E),E!=="initial-request"&&g(E))},children:[b.jsx(nw,{"aria-label":"Work content",children:Wk.map(E=>b.jsx(rw,{value:E.name,"aria-label":E.label,children:E.label},E.name))}),b.jsx("div",{className:"work-document-panel",children:c==="initial-request"?b.jsxs(b.Fragment,{children:[b.jsx(We,{justify:"end",children:b.jsx(et,{size:"1",variant:"soft",type:"button",onClick:l,children:"Edit Initial Request"})}),v?b.jsx(Qi,{source:n.body??"",resolveLinks:!0}):b.jsx(qe,{color:"gray",children:"Initial Request is not yet present."})]}):y?.present?x?.error?b.jsxs(We,{direction:"column",align:"start",gap:"2",children:[b.jsxs(qe,{color:"red",children:["Could not load this document: ",x.error]}),b.jsx(et,{size:"1",variant:"soft",type:"button",onClick:w,children:"Retry"})]}):x?.content===void 0?b.jsx(qe,{color:"gray",children:"Loading document..."}):b.jsxs(b.Fragment,{children:[b.jsx(We,{justify:"end",children:b.jsxs(et,{size:"1",variant:"soft",type:"button",onClick:()=>o(n.slug,c),children:["Edit"," ",c==="spec"?"Spec":"Implementation Plan"]})}),b.jsx(Qi,{source:x.content,resolveLinks:!0})]}):b.jsx(qe,{color:"gray",children:c==="spec"?"Spec is not yet present.":"Implementation Plan is not yet present."})})]})}const eM=[["Status",["","Supported","Partial","Missing","Blocked","Omitted"]],["Priority",["","P0","P1","P2","P3"]],["Lifecycle",["","Experimental","Stable","Deprecated"]],["Feature",null],["Subject",null],["Roles",null],["When",null],["Gaps",null],["Blocked by",null],["Tracker",null],["Planning doc",null],["Superseded by",null]];function tM({axis:n,registeredTags:r,statusFilter:l,setStatusFilter:o,kindFilter:s,setKindFilter:c,tagFilter:u,setTagFilter:d,workSortKey:h,setWorkSortKey:g,workSortDirection:v,setWorkSortDirection:y}){if(n==="capabilities")return null;const x=qc.filter(w=>l[w]);return b.jsxs(We,{className:"status-filters",role:"group","aria-label":"Filters",align:"center",gap:"2",wrap:"wrap",children:[n==="work"&&b.jsxs(b.Fragment,{children:[b.jsx(Wb,{label:"Status",options:qc,value:x,capitalize:!0,onChange:w=>o(Object.fromEntries(qc.map(E=>[E,w.includes(E)])))}),b.jsx(nM,{sortKey:h,direction:v,onSortKeyChange:g,onDirectionChange:y})]}),b.jsx(Wb,{label:n==="taxonomy"?"Kind":"Tags",options:n==="taxonomy"?["Feature","Vocabulary"]:r,value:n==="taxonomy"?s:u,onChange:n==="taxonomy"?c:d})]})}function nM({sortKey:n,direction:r,onSortKeyChange:l,onDirectionChange:o}){const s=r==="ascending"?"descending":"ascending";return b.jsxs(We,{className:"sort-control",align:"center",gap:"1",children:[b.jsxs(Hp,{value:n,onValueChange:c=>{(c==="name"||c==="modified")&&l(c)},children:[b.jsx(qp,{"aria-label":"Sort work items"}),b.jsxs(Vp,{children:[b.jsx(Qc,{value:"name",children:"Name"}),b.jsx(Qc,{value:"modified",children:"Modified"})]})]}),b.jsx(Qr,{type:"button",size:"1",variant:"soft","aria-label":`Sort ${s}`,title:`Sort ${s}`,onClick:()=>o(s),children:r==="ascending"?"↑":"↓"})]})}function Wb({label:n,options:r,value:l,onChange:o,capitalize:s=!1}){return b.jsxs(Lp,{children:[b.jsx(Pp,{children:b.jsxs(et,{size:"1",variant:"soft",children:[n,l.length?` (${l.length})`:""]})}),b.jsx(Up,{align:"start",className:"facet-panel",width:"180px",children:b.jsx(We,{direction:"column",gap:"2",children:r.length?r.map(c=>b.jsx(qe,{as:"label",className:"facet-option",size:"2",children:b.jsxs(We,{align:"center",gap:"2",children:[b.jsx(wu,{checked:l.includes(c),onCheckedChange:u=>o(u?[...l,c]:l.filter(d=>d!==c))})," ",s?c[0].toUpperCase()+c.slice(1):c]})},c)):b.jsx(qe,{className:"facet-empty",color:"gray",size:"1",children:"none available"})})})]})}function rM({axis:n,detail:r,onEdit:l,onResource:o,onOpen:s,onReadArtifact:c,onDeletePlanStage:u,onAction:d}){if(n==="work"){const g=r,v=g.item;return b.jsxs(b.Fragment,{children:[b.jsxs("div",{className:"detail-head",children:[b.jsxs("div",{children:[b.jsx(Dn,{as:"h2",size:"5",children:v.title??v.slug}),b.jsx(qe,{className:"item-meta",color:"gray",size:"2",children:v.slug}),b.jsx(Eo,{value:v.modified})]}),b.jsxs(We,{className:"detail-actions",align:"center",gap:"2",children:[b.jsx($i,{children:v.status}),b.jsx(et,{className:"edit-btn",variant:"soft",type:"button",onClick:l,children:"Edit"})]})]}),b.jsxs(We,{className:"action-buttons",gap:"2",wrap:"wrap",children:[v.status==="backlog"&&b.jsxs(b.Fragment,{children:[b.jsx(et,{className:"action-btn start",color:"green",variant:"soft",type:"button",onClick:()=>d("start"),children:"Start"}),b.jsx(et,{className:"action-btn discard",color:"amber",variant:"soft",type:"button",onClick:()=>d("discard"),children:"Discard"}),b.jsx(et,{className:"action-btn drop",color:"red",variant:"soft",type:"button",onClick:()=>d("drop"),children:"Drop"})]}),v.status==="active"&&b.jsx(et,{className:"action-btn complete",color:"green",variant:"soft",type:"button",onClick:()=>d("complete"),children:"Complete"})]}),b.jsxs(nh,{children:[b.jsx(un,{name:"Priority",value:v.priority}),b.jsx(un,{name:"Effort",value:v.effort}),b.jsx(un,{name:"Complexity",value:v.complexity}),b.jsx(un,{name:"Tags",value:v.tags?.join(", ")||"-"}),b.jsx(un,{name:"Resolution",value:v.resolution}),b.jsx(un,{name:"Blocked by",value:v.blocked_by?.map(y=>y.slug??y.external).join(", ")||"-"}),v.parent&&b.jsx(un,{name:"Parent",value:v.parent}),v.initiative&&b.jsx(un,{name:"Initiative",value:v.initiative}),v.tracker&&b.jsx(Xk,{binding:v.tracker})]}),b.jsx(Jk,{item:v,artifacts:g.artifacts,onEditInitialRequest:l,onEditArtifact:(y,x)=>o("artifacts",y,x),onReadArtifact:c},`${v.slug}:${g.coreRevision}:${g.artifacts.filter(y=>["initial-request","spec","plan"].includes(y.name)).map(y=>y.revision??"missing").join(":")}`),b.jsx("div",{className:"artifacts",children:g.artifacts.filter(y=>y.present&&!["initial-request","spec","plan"].includes(y.name)).map(y=>b.jsxs(We,{className:"artifact-group",children:[b.jsx(et,{className:"artifact",variant:"soft",type:"button",onClick:()=>s(v.slug,y.name),children:y.name}),b.jsx(Qr,{className:"artifact-edit",variant:"ghost",type:"button","aria-label":`Edit ${y.name}`,onClick:()=>o("artifacts",v.slug,y.name),children:"✎"})]},y.name))}),g.planStages?.length>0&&b.jsxs("div",{className:"plan-stages",children:[b.jsx(Dn,{as:"h3",size:"3",children:"Plan stages"}),g.planStages.map(y=>b.jsxs(cs,{className:"plan-stage",children:[b.jsxs("div",{children:[b.jsx("strong",{children:y.title}),b.jsxs("span",{className:"item-meta",children:[" ",y.id]})]}),b.jsxs("div",{className:"item-meta",children:["Depends on:"," ",y.depends_on.length?y.depends_on.join(", "):"none",y.effort&&` · effort ${y.effort}`,y.complexity&&` · complexity ${y.complexity}`,y.priority!=null&&` · priority ${y.priority}`,y.tags.length>0&&` · ${y.tags.join(", ")}`]}),g.planStages.filter(x=>x.id!==y.id&&JSON.stringify(x.depends_on)===JSON.stringify(y.depends_on)).length>0&&b.jsxs("div",{className:"item-meta",children:["Parallel with:"," ",g.planStages.filter(x=>x.id!==y.id&&JSON.stringify(x.depends_on)===JSON.stringify(y.depends_on)).map(x=>x.id).join(", ")]}),b.jsxs(We,{className:"artifact-group",align:"center",gap:"2",children:[b.jsx(qe,{size:"2",children:y.present?"Document present":"Document missing"}),y.present&&b.jsx(et,{size:"1",variant:"soft",type:"button",onClick:()=>s(v.slug,y.id,"plan-stages"),children:"Open"}),b.jsx(et,{size:"1",variant:"soft",type:"button",onClick:()=>o("plan-stages",v.slug,y.id),children:y.present?"Edit":"Create"}),y.present&&b.jsx(et,{size:"1",color:"red",variant:"soft",type:"button",onClick:()=>u(v.slug,y.id,y.revision),children:"Delete"})]})]},y.id))]}),g.sidecars?.some(y=>y.present)&&b.jsxs("div",{className:"sidecars-section",children:[b.jsx(Dn,{as:"h3",size:"3",children:"Sidecars"}),g.sidecars.filter(y=>y.present).map(y=>b.jsxs(We,{className:"sidecar-item",align:"center",gap:"2",children:[b.jsx(qe,{className:"sidecar-name",color:"gray",size:"2",children:y.name}),y.generated?b.jsx(qe,{color:"gray",size:"1",children:"generated"}):b.jsx(et,{className:"sidecar-edit-btn",size:"1",variant:"soft",type:"button",onClick:()=>o("sidecars",v.slug,y.name),children:"Edit"})]},y.name))]})]})}if(n==="taxonomy"){const g=r.term;return b.jsxs(b.Fragment,{children:[b.jsxs("div",{className:"detail-head",children:[b.jsxs("div",{children:[b.jsx(Dn,{as:"h2",size:"5",children:g.name??g.slug}),b.jsx(qe,{className:"item-meta",color:"gray",size:"2",children:g.qualified??g.slug}),b.jsx(Eo,{value:g.modified})]}),b.jsxs(We,{className:"detail-actions",align:"center",gap:"2",children:[b.jsx($i,{children:g.kind}),b.jsx(et,{className:"edit-btn",variant:"soft",type:"button",onClick:l,children:"Edit"})]})]}),b.jsxs(nh,{children:[b.jsx(un,{name:"Kind",value:g.kind}),b.jsx(un,{name:"Origin",value:g.origin}),b.jsx(un,{name:"Parent",value:g.parent}),b.jsx(un,{name:"Relates to",value:g.relates_to?.join(", ")||"-"}),b.jsx(un,{name:"Vocabulary",value:g.vocabulary?.join(", ")||"-"})]}),b.jsx(Qi,{source:g.description??"",resolveLinks:!0})]})}const h=r.capability;return b.jsxs(b.Fragment,{children:[b.jsxs("div",{className:"detail-head",children:[b.jsxs("div",{children:[b.jsx(Dn,{as:"h2",size:"5",children:h.name??h.path}),b.jsx(qe,{className:"item-meta",color:"gray",size:"2",children:h.path}),b.jsx(Eo,{value:h.modified})]}),b.jsxs(We,{className:"detail-actions",align:"center",gap:"2",children:[b.jsx($i,{children:h.status}),b.jsx(et,{className:"edit-btn",variant:"soft",type:"button",onClick:l,children:"Edit"})]})]}),b.jsx(nh,{children:Object.entries(h.fields??{}).map(([g,v])=>b.jsx(un,{name:g,value:Array.isArray(v)?v.join(", "):v},g))}),b.jsx(Qi,{source:h.body??"",resolveLinks:!0})]})}function aM({editor:n,setDraft:r,saving:l,errors:o,conflict:s,registeredTags:c,data:u,onSave:d,onCancel:h,onRefreshConflict:g}){if(n.mode==="resource")return b.jsxs("div",{className:`editor-container${l?" saving":""}`,children:[b.jsx(Jb,{title:`Edit ${n.kind==="artifacts"?"Artifact":n.kind==="sidecars"?"Sidecar":"Plan stage"}: ${n.name}`,saving:l,onSave:d,onCancel:h}),b.jsx(tu,{errors:o}),!!s&&b.jsx(e0,{onRefresh:g,onDiscard:h}),b.jsx(Qb,{value:n.draft,onChange:y=>r("draft",y)})]});const v=n.draft;return b.jsxs("div",{className:`editor-container${l?" saving":""}`,children:[b.jsx(Jb,{title:`${n.mode==="create"?"Create":"Edit"} ${bo[n.axis]}`,saving:l,onSave:d,onCancel:h}),b.jsx(tu,{errors:o}),!!s&&b.jsx(e0,{onRefresh:g,onDiscard:h}),b.jsx("div",{className:"editor-section",children:"Fields"}),b.jsxs("div",{className:"editor-fields",children:[n.axis==="work"&&b.jsx(lM,{draft:v,setDraft:r,registeredTags:c,data:u,current:n.mode==="core"?n.ref:void 0}),n.axis==="taxonomy"&&b.jsx(oM,{draft:v,setDraft:r,creating:n.mode==="create",data:u,current:n.mode==="core"?n.ref:void 0}),n.axis==="capabilities"&&b.jsx(iM,{draft:v,setDraft:r,creating:n.mode==="create",data:u,current:n.mode==="core"?n.ref:void 0})]}),b.jsx("div",{className:"editor-section",children:"Body"}),b.jsx(Qb,{value:String(v.body??""),onChange:y=>r("body",y)})]})}function Jb({title:n,saving:r,onSave:l,onCancel:o}){return b.jsxs(We,{className:"editor-header",justify:"between",align:"center",children:[b.jsx(Dn,{as:"h2",className:"editor-title",size:"3",children:n}),b.jsxs(We,{className:"editor-actions",gap:"2",children:[b.jsx(et,{variant:"soft",color:"gray",type:"button",onClick:o,children:"Cancel"}),b.jsx(et,{type:"button",disabled:r,onClick:l,children:r?"Saving...":"Save"})]})]})}function e0({onRefresh:n,onDiscard:r}){return b.jsx(Br,{className:"conflict-banner",color:"amber",role:"alert",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"Stale write detected"}),b.jsx(qe,{as:"p",size:"2",children:"The server version changed. Your draft is preserved."}),b.jsxs(We,{gap:"2",mt:"2",children:[b.jsx(et,{size:"1",variant:"soft",onClick:n,children:"Refresh from server"}),b.jsx(et,{size:"1",color:"red",variant:"soft",onClick:r,children:"Discard draft"})]})]})})}function ml(n,r,l,o){return rk(n,{...r,currentIdentifier:l,selected:o})}function lM({draft:n,setDraft:r,registeredTags:l,data:o,current:s}){const c=n.tags??[];return b.jsxs(b.Fragment,{children:[b.jsx(pl,{label:"Title",value:String(n.title??""),onChange:u=>r("title",u)}),b.jsx(pl,{label:"Priority",type:"number",value:String(n.priority??""),onChange:u=>r("priority",u)}),b.jsx(Ro,{label:"Effort",value:String(n.effort??""),options:["","low","medium","high","very-high"],onChange:u=>r("effort",u)}),b.jsx(Ro,{label:"Complexity",value:String(n.complexity??""),options:["","low","medium","high","very-high"],onChange:u=>r("complexity",u)}),b.jsx(hl,{label:"Parent",value:String(n.parent??""),options:ml("work-parent",o,s),onChange:u=>r("parent",u)}),b.jsx(hl,{label:"Initiative",value:String(n.initiative??""),options:ml("work-initiative",o,s),onChange:u=>r("initiative",u)}),b.jsx(hl,{label:"Blockers",multiple:!0,value:n.blockers??[],options:ml("work-blockers",o,s,n.blockers??[]),onChange:u=>r("blockers",u)}),b.jsxs("div",{className:"field-group",children:[b.jsx(qe,{color:"gray",size:"1",weight:"bold",children:"Tags"}),b.jsx(We,{className:"tag-checkboxes",gap:"3",wrap:"wrap",children:[...new Set([...l,...c])].map(u=>b.jsx(qe,{as:"label",className:`tag-checkbox${l.includes(u)?"":" tag-stale"}`,size:"2",children:b.jsxs(We,{align:"center",gap:"1",children:[b.jsx(wu,{checked:c.includes(u),onCheckedChange:d=>r("tags",d?[...c,u]:c.filter(h=>h!==u))})," ",u,l.includes(u)?"":" (unregistered)"]})},u))})]})]})}function oM({draft:n,setDraft:r,creating:l,data:o,current:s}){return b.jsxs(b.Fragment,{children:[b.jsx(pl,{label:"Name",value:String(n.name??""),onChange:c=>r("name",c)}),b.jsx(Ro,{label:"Kind",value:String(n.kind??"Vocabulary"),options:["Vocabulary","Feature"],onChange:c=>r("kind",c)}),l&&b.jsxs(b.Fragment,{children:[b.jsx(pl,{label:"Slug",value:String(n.slug??""),onChange:c=>r("slug",c)}),b.jsx(hl,{label:"Parent",value:String(n.parent??""),options:ml("taxonomy-parent",o,s),onChange:c=>r("parent",c)})]}),!l&&b.jsx(hl,{label:"Relates to",multiple:!0,value:n.relates_to??[],options:ml("taxonomy-relates",o,s,n.relates_to??[]),onChange:c=>r("relates_to",c)}),b.jsx(hl,{label:"Vocabulary",multiple:!0,value:n.vocabulary??[],options:ml("taxonomy-vocabulary",o,s,n.vocabulary??[]),onChange:c=>r("vocabulary",c)})]})}function iM({draft:n,setDraft:r,creating:l,data:o,current:s}){if(l)return b.jsxs(b.Fragment,{children:[b.jsx(pl,{label:"Path",value:String(n.path??""),placeholder:"e.g. web/editing",onChange:u=>r("path",u)}),b.jsx(pl,{label:"Name",value:String(n.name??""),onChange:u=>r("name",u)}),b.jsx(Ro,{label:"Status",value:String(n.status??"Missing"),options:["Supported","Partial","Missing","Blocked","Omitted"],onChange:u=>r("status",u)})]});const c={Feature:{field:"capability-feature"},Subject:{field:"capability-subject",multiple:!0},Roles:{field:"capability-roles",multiple:!0},When:{field:"capability-when",multiple:!0,negated:!0},"Blocked by":{field:"capability-blocked-by"},"Planning doc":{field:"capability-planning-doc"},"Superseded by":{field:"capability-superseded-by"}};return b.jsx(b.Fragment,{children:eM.map(([u,d])=>{if(d)return b.jsx(Ro,{label:u,value:String(n[u]??""),options:d,onChange:v=>r(u,v)},u);const h=c[u];if(!h)return b.jsx(pl,{label:u,value:String(n[u]??""),onChange:v=>r(u,v)},u);const g=h.multiple?Array.isArray(n[u])?n[u]:String(n[u]??"").split(",").map(v=>v.trim()).filter(Boolean):String(n[u]??"");return b.jsx(hl,{label:u,value:g,multiple:h.multiple,negated:h.negated,options:ml(h.field,o,s,Array.isArray(g)?g.map(v=>v.replace(/^!/,"")):void 0),onChange:v=>r(u,v)},u)})})}function sM({onClose:n,onStart:r,errors:l}){const[o,s]=p.useState(!1);return b.jsxs(ww,{title:"Start Work Item",onClose:n,children:[b.jsx(tu,{errors:l}),o&&b.jsx(Br,{color:"amber",children:b.jsx(Hr,{children:"This item may have unresolved blockers. You can force-start it."})}),b.jsxs(We,{className:"modal-actions",gap:"2",justify:"end",children:[b.jsx(et,{variant:"soft",color:"gray",type:"button",onClick:n,children:"Cancel"}),b.jsx(et,{type:"button",onClick:()=>void r(o).then(c=>{c||s(!0)}),children:o?"Start (force)":"Start"})]})]})}function cM({detail:n,onClose:r,onComplete:l,errors:o,discardOnly:s=!1}){const c=n.dodChecklist?.length?n.dodChecklist:["tests pass","docs synced","capabilities reconciled","reviewed","version offered"],[u,d]=p.useState([]),[h,g]=p.useState(s?"wontfix":""),[v,y]=p.useState(!1),x=h===""||h==="done";return b.jsxs(ww,{title:x?"Complete Work Item":"Close Work Item",onClose:r,children:[b.jsx(tu,{errors:o}),b.jsx(Br,{className:"hooks-not-run",color:"gray",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"Configured hooks do not run here"}),b.jsx("br",{}),"The web app performs and commits the transition, but does not run any commands bound to it in"," ",b.jsx("code",{children:"tcw-config.yaml"}),". Use"," ",b.jsx("code",{children:"tcw work complete"})," if you need them. If the commit itself is refused, the item still moves and the reason is printed by ",b.jsx("code",{children:"tcw serve"})," in your terminal."]})}),x?b.jsx(Br,{className:"reconciliation-reminder",color:"blue",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"Reconciliation reminder"}),b.jsx("br",{}),"Reconcile the capabilities ledger before completing."]})}):h&&b.jsx(Br,{className:"discard-warning",color:"amber",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"This item will be discarded"}),b.jsx("br",{}),"It lands in ",b.jsx("code",{children:"discarded/"}),", not"," ",b.jsx("code",{children:"completed/"}),", and this is permanent. Mark any capability it declared as ",b.jsx("code",{children:"Omitted"})," ","if it will never be built."]})}),b.jsx(Ro,{label:"Resolution",value:h,options:s?["wontfix","duplicate","superseded"]:["","done","wontfix","duplicate","superseded"],onChange:g}),x&&b.jsxs(b.Fragment,{children:[b.jsx(Dn,{as:"h3",size:"3",children:"Definition of Done"}),b.jsx(We,{className:"dod-list",direction:"column",children:c.map(w=>b.jsx(qe,{as:"label",className:"dod-item",size:"2",children:b.jsxs(We,{align:"center",gap:"2",children:[b.jsx(wu,{checked:u.includes(w),onCheckedChange:E=>d(E?[...u,w]:u.filter(T=>T!==w))})," ",w]})},w))})]}),v&&b.jsx(Br,{className:"modal-error",color:"red",children:b.jsx(Hr,{children:"Completion was blocked. You can retry while ignoring blockers."})}),b.jsxs(We,{className:"modal-actions",gap:"2",justify:"end",children:[b.jsx(et,{variant:"soft",color:"gray",type:"button",onClick:r,children:"Cancel"}),b.jsx(et,{type:"button",disabled:!h||x&&u.length!==c.length,onClick:()=>void l({resolution:h,dod_ack:x?u:[],force:v}).then(w=>{w||y(!0)}),children:v?x?"Complete (ignore blockers)":"Discard (ignore blockers)":x?"Complete":"Discard"})]})]})}async function uM(n,r,l,o){try{const s=await fetch(`/api/work/${qt(n)}/${l}/${qt(r)}/open`,{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"});if(!s.ok)throw new Error(`${s.status} ${s.statusText}`);if(s.status===204){o("Opened resource");return}const c=await s.json();c.url&&window.open(c.url,"_blank","noopener")}catch(s){o(`Could not open resource: ${s instanceof Error?s.message:String(s)}`)}}function fM(){const n=La(),r=x0(),l=p.useRef(Gb(n.pathname)).current,[o,s]=p.useState(l.axis),[c,u]=p.useState(l.key),[d,h]=p.useState({work:[],taxonomy:[],capabilities:[]}),[g,v]=p.useState([]),[y,x]=p.useState(""),[w,E]=p.useState({backlog:!0,active:!0,completed:!1,discarded:!1}),[T,R]=p.useState([]),[A,O]=p.useState([]),[N,M]=p.useState("name"),[L,D]=p.useState("ascending"),[F,te]=p.useState(Yk),[J,ae]=p.useState(()=>({work:new Set,taxonomy:new Set,capabilities:new Set})),[ce,oe]=p.useState(()=>{const q=Number.parseFloat(localStorage.getItem("tcw.listWidth")??"");return Number.isFinite(q)&&q>=.12&&q<=.6?q:.28}),[se,de]=p.useState(null),[Ce,P]=p.useState(!1),[G,Z]=p.useState(null),[ue,_]=p.useState(!1),[X,re]=p.useState(!1),[ne,ie]=p.useState([]),[_e,pe]=p.useState(null),[pt,he]=p.useState([]),[Ge,Ue]=p.useState(""),[Me,Ye]=p.useState(null),[lt,gt]=p.useState(""),vt=p.useCallback(q=>{Ue(q),window.setTimeout(()=>Ue(""),2800)},[]),Gn=p.useCallback((q,fe)=>{const me=hM(fe);he(me),vt(me.length?`${q} with ${me.length} validation issue${me.length===1?"":"s"}`:q)},[vt]),At=p.useCallback(()=>!ue||window.confirm("You have unsaved changes. Leave without saving?"),[ue]);IR(p.useCallback(q=>{ue&&q.preventDefault()},[ue]));const Wt=p.useCallback(async(q=!0)=>{try{const[fe,me,ye,Le]=await Promise.all([fl("/api/work"),fl("/api/taxonomy"),fl("/api/capabilities"),fl("/api/work/tags").catch(()=>({tags:[]}))]);h({work:fe,taxonomy:me,capabilities:ye}),v(Le.tags??[]),q||u(null),gt("")}catch(fe){gt(fe instanceof Error?fe.message:String(fe))}},[]);p.useEffect(()=>{Wt()},[Wt]),p.useEffect(()=>{localStorage.setItem("tcw.treeExpanded",JSON.stringify(Object.fromEntries(Ah.map(q=>[q,[...F[q]]]))))},[F]),p.useEffect(()=>{localStorage.setItem("tcw.listWidth",String(ce))},[ce]),p.useEffect(()=>{const q=Gb(n.pathname);if(q.axis!==o||q.key!==c){if(!At()){r(Nh(o,c),{replace:!0});return}Z(null),_(!1),de(null),s(q.axis),u(q.key)}},[n.pathname]);const Ha=d[o];p.useEffect(()=>{if(!c||!d[o].some(fe=>Dh(o,fe)===c)){de(null);return}P(!0);const q=o==="work"?`/api/work/${qt(c)}`:o==="taxonomy"?`/api/taxonomy/${qt(c)}`:`/api/capabilities/${qt(c)}`;fl(q).then(de).catch(fe=>gt(String(fe))).finally(()=>P(!1))},[o,c,d]);const Wn=(q,fe)=>{if(At()&&(Z(null),_(!1),ie([]),pe(null),de(null),s(q),u(fe),r(Nh(q,fe)),fe)){const me=q==="work"?ek(fe,d.work):JO(fe);te(ye=>({...ye,[q]:new Set([...ye[q],...me])}))}},yr=q=>{G&&!At()||(Z(null),_(!1),x(q),Wn(o,null))},gn=p.useCallback(q=>{if(o==="work"){const fe=q;if(fe.status&&w[fe.status]===!1||A.length&&!A.some(me=>fe.tags?.includes(me)))return!1}return o==="taxonomy"&&T.length&&!T.includes(q.kind??"")?!1:!y||JSON.stringify(q).toLowerCase().includes(y.toLowerCase())},[o,y,T,w,A]),Jt=p.useMemo(()=>{const q=o==="work"?cw(QO(d.work),N,L):ZO(Ha,ye=>Dh(o,ye));if(!!!(y||T.length||A.length||o==="work"&&qc.some(ye=>!w[ye])))return{nodes:q,forced:new Set};const me=WO(q,gn);return{nodes:me.nodes,forced:y?me.forceExpand:new Set}},[o,Ha,d.work,y,T,w,A,gn,L,N]),Al=p.useMemo(()=>new Set([...F[o],...Jt.forced]),[o,F,Jt.forced]);p.useEffect(()=>{const q=[],fe=ye=>ye.forEach(Le=>{Le.children.length&&(q.push(Le.path),fe(Le.children))});fe(Jt.nodes);const me=q.filter(ye=>!J[o].has(ye));me.length&&(ae(ye=>({...ye,[o]:new Set([...ye[o],...me])})),te(ye=>({...ye,[o]:new Set([...ye[o],...me])})))},[o,J,Jt.nodes]);const qa=(q,fe)=>{Z(me=>me&&(me.mode==="resource"?q==="draft"?{...me,draft:String(fe)}:me:{...me,draft:{...me.draft,[q]:fe}})),_(!0)},Kt=()=>{Z({mode:"create",axis:o,draft:o==="work"?{title:"",priority:"",effort:"",complexity:"",parent:"",initiative:"",blockers:[],tags:[],body:""}:o==="taxonomy"?{name:"",kind:"Vocabulary",slug:"",parent:"",vocabulary:[],description:"",body:""}:{path:"",name:"",status:"Missing",body:""}}),u(null),_(!1),ie([]),pe(null)},yt=()=>{if(!se)return;let q,fe,me;if(o==="work"){const ye=se,Le=ye.item,Yn=ye.artifacts.find(xr=>xr.name==="initial-request")?.present??!1;q={title:Le.title??"",priority:Le.priority??"",effort:Le.effort??"",complexity:Le.complexity??"",parent:Le.parent??"",initiative:Le.initiative??"",blockers:(Le.blocked_by??[]).map(xr=>xr.slug??xr.external??"").filter(Boolean),tags:Le.tags??[],body:Yn?Le.body??"":""},fe=Le.slug,me=ye.coreRevision}else if(o==="taxonomy"){const ye=se,Le=ye.term;q={name:Le.name??"",kind:Le.kind??"Vocabulary",relates_to:Le.relates_to??[],vocabulary:Le.vocabulary??[],body:Le.description??""},fe=Le.qualified??Le.slug,me=ye.coreRevision}else{const ye=se,Le=ye.capability;q={...Le.fields??{},body:Le.body??""},fe=Le.path,me=ye.coreRevision}Z({mode:"core",axis:o,ref:fe,revision:me,draft:q,original:structuredClone(q)}),_(!1),ie([]),pe(null)},vn=async(q,fe,me)=>{try{const ye=await fl(`/api/work/${qt(fe)}/${q}/${qt(me)}`);Z({mode:"resource",axis:"work",kind:q,slug:fe,name:me,revision:ye.revision,mediaType:ye.mediaType??(q==="artifacts"?"text/markdown":"application/yaml"),draft:ye.content,original:ye.content}),_(!1),ie([]),pe(null)}catch(ye){if(q==="plan-stages"){Z({mode:"resource",axis:"work",kind:q,slug:fe,name:me,revision:"",mediaType:"text/markdown",draft:`## Objective
167
+ Please report this to https://github.com/markedjs/marked.`,n){let o="<p>An error occurred:</p><pre>"+lr(l.message+"",!0)+"</pre>";return r?Promise.resolve(o):o}if(r)return Promise.reject(l);throw l}}},xl=new Gk;function st(n,r){return xl.parse(n,r)}st.options=st.setOptions=function(n){return xl.setOptions(n),st.defaults=xl.defaults,uw(st.defaults),st};st.getDefaults=Yp;st.defaults=Tl;st.use=function(...n){return xl.use(...n),st.defaults=xl.defaults,uw(st.defaults),st};st.walkTokens=function(n,r){return xl.walkTokens(n,r)};st.parseInline=xl.parseInline;st.Parser=Vr;st.parser=Vr.parse;st.Renderer=eu;st.TextRenderer=Jp;st.Lexer=qr;st.lexer=qr.lex;st.Tokenizer=Jc;st.Hooks=Pi;st.parse=st;st.options;st.setOptions;st.use;st.walkTokens;st.parseInline;Vr.parse;qr.lex;function Sw(n,r,l,o,s){n.preventDefault();const c=d=>{const h=r.getBoundingClientRect();s(Math.min(o,Math.max(l,(d.clientX-h.left)/h.width)))},u=()=>{window.removeEventListener("pointermove",c),window.removeEventListener("pointerup",u)};window.addEventListener("pointermove",c),window.addEventListener("pointerup",u)}function Yk(){const n=()=>({work:new Set,taxonomy:new Set,capabilities:new Set});try{const r=JSON.parse(localStorage.getItem("tcw.treeExpanded")??"{}");return{work:new Set(r.work),taxonomy:new Set(r.taxonomy),capabilities:new Set(r.capabilities)}}catch{return n()}}function Ik(){let n=0;for(;document.getElementById(`tcw-off-board-${n}`);)n+=1;return`tcw-off-board-${n}`}function Zb(n,r){n.dataset.tcwRef=r,n.removeAttribute("href")}function Fk(n,r){n.classList.add("tcw-unhosted"),n.title=`Project ${r} is not included in this board`;const l=document.createElement("span");l.className="tcw-project-badge",l.textContent=r;const o=document.createElement("span");o.className="tcw-sr-only",o.id=Ik(),o.textContent=n.title,n.setAttribute("aria-describedby",o.id),n.after(l),l.after(o)}function Qi({source:n,resolveLinks:r=!1}){const l=p.useRef(null),o=p.useMemo(()=>st.parse(n||""),[n]);return p.useEffect(()=>{if(!r||!l.current)return;const s=[...l.current.querySelectorAll('a[href^="tcw://"]')];if(!s.length)return;const c=s.map(d=>d.getAttribute("href"));let u=!1;return yo("/api/resolve","POST",{uris:c}).then(d=>{if(u)return;const h=l.current?.querySelectorAll('a[href^="tcw://"]')??[];for(const g of h){const v=g.getAttribute("href"),y=d.data?.[v];y?.ok&&y.axis&&y.key?(g.href=Nh(y.axis,y.key),g.dataset.navAxis=y.axis,g.dataset.navKey=y.key):y?.reason==="unhosted-project"&&y.project?(Zb(g,v),Fk(g,y.project)):(Zb(g,v),g.classList.add("tcw-inert"),g.title=y?.detail||v)}}),()=>{u=!0}},[o,r]),b.jsx("article",{ref:l,className:"body",dangerouslySetInnerHTML:{__html:o}})}function nh({children:n}){return b.jsx("div",{className:"fields",children:n})}function un({name:n,value:r}){return b.jsxs(cs,{className:"field",size:"1",children:[b.jsx(qe,{as:"div",color:"gray",size:"1",children:n}),b.jsx(qe,{as:"div",size:"2",children:String(r??"-")})]})}function Xk({binding:n}){return b.jsxs(cs,{className:"field",size:"1",children:[b.jsx(qe,{as:"div",color:"gray",size:"1",children:"Ticket"}),b.jsx(qe,{as:"div",size:"2",children:"ticket"in n?b.jsxs(b.Fragment,{children:[n.ticket.url?b.jsx("a",{href:n.ticket.url,target:"_blank",rel:"noreferrer",children:n.ticket.key}):n.ticket.key,` · ${n.provider} · part ${n.part}`,n.sync&&("problem"in n.sync?` · sync record cannot be read: ${n.sync.problem}`:` · ${n.sync.state}: ${n.sync.reason}`)]}):"created"in n?`none yet — ${n.created.key} was created for it and the binding did not follow; \`tcw work tracker create\` binds it`:"owed"in n?`none yet — one was to be created on filing and was not (${n.owed.reason}), owed since ${n.owed.since}`:`tracker.yaml cannot be read: ${n.problem}`})]})}function tu({errors:n}){return n.length?b.jsx(Br,{color:"red",role:"alert",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"Validation errors"}),b.jsx("ul",{children:n.map(r=>b.jsx("li",{children:r},r))})]})}):null}function ww({title:n,children:r,onClose:l}){return b.jsx(IS,{open:!0,onOpenChange:o=>{o||l()},children:b.jsxs(FS,{className:"modal-box",maxWidth:"560px",children:[b.jsxs(We,{justify:"between",align:"center",mb:"3",children:[b.jsx(XS,{children:n}),b.jsx(KS,{children:b.jsx(Qr,{className:"modal-dismiss",variant:"ghost",type:"button","aria-label":"Close",children:"×"})})]}),r]})})}function Kk({nodes:n,axis:r,selected:l,expanded:o,onToggle:s,onSelect:c,visible:u}){const d=(h,g)=>b.jsx("div",{className:"tree-children",role:g===0?"none":"group",children:h.map(v=>{const y=v.children.length>0,x=o.has(v.path),w=v.item?Dh(r,v.item):v.path;return b.jsxs("div",{className:"tree-node",role:"none",children:[b.jsxs("div",{className:"tree-row",role:"none",children:[Array.from({length:g},(E,T)=>b.jsx("span",{className:"tree-indent"},T)),y?b.jsx(Qr,{className:"tree-toggle",variant:"ghost",type:"button",tabIndex:-1,"aria-label":x?`Collapse ${v.name}`:`Expand ${v.name}`,"aria-expanded":x,onClick:()=>s(v.path),children:x?"▾":"▸"}):b.jsx("span",{className:"tree-spacer"}),v.item?b.jsxs("div",{className:"tree-item-content",children:[b.jsxs(et,{type:"button",variant:"soft",role:"treeitem","aria-level":g+1,"data-tree-path":v.path,"aria-expanded":y?x:void 0,"aria-selected":l===w,className:`item item-${r}${r==="work"?` st-${v.item.status}`:""}${l===w?" active":""}${u(v.item)?"":" ancestor-dim"}`,onClick:()=>c(w),children:[b.jsx("div",{className:"item-title",children:lk(r,v.item)}),b.jsx("div",{className:"item-meta",children:b.jsx(Zk,{axis:r,item:v.item})})]}),r==="work"&&b.jsx(Gp,{content:b.jsx("span",{className:"copy-slug-tooltip",children:"Copy slug"}),children:b.jsx(Qr,{className:"copy-slug",variant:"ghost",type:"button","aria-label":"Copy slug to clipboard",onClick:()=>void navigator.clipboard.writeText(w),children:"⎘"})})]}):b.jsx(et,{className:"tree-folder",variant:"ghost",type:"button",role:"treeitem","aria-level":g+1,"data-tree-path":v.path,"aria-expanded":x,onClick:()=>s(v.path),children:v.name})]}),y&&x?d(v.children,g+1):null]},v.path)})});return d(n,0)}function Zk({axis:n,item:r}){if(n==="work"){const o=r;return b.jsxs("div",{children:[b.jsx("span",{className:"work-status",children:o.status})," · ",[o.effort&&`effort ${o.effort}`,o.complexity&&`complexity ${o.complexity}`,o.tags?.length&&`tags ${o.tags.join(", ")}`].filter(Boolean).join(" · "),b.jsx(Eo,{value:o.modified})]})}if(n==="taxonomy"){const o=r;return b.jsxs("div",{children:[b.jsx("div",{children:[o.kind,o.origin].filter(Boolean).join(" · ")}),b.jsx(Eo,{value:o.modified})]})}const l=r;return b.jsxs("div",{children:[[l.status,l.origin!=="local"&&l.origin].filter(Boolean).join(" · "),b.jsx(Eo,{value:l.modified})]})}function Qb({value:n,onChange:r,placeholder:l="Write Markdown..."}){const[o,s]=p.useState(.5);return b.jsxs("div",{className:"md-editor",style:{"--md-split":`${o*100}%`},children:[b.jsx(aw,{className:"md-input","aria-label":"Markdown",value:n,placeholder:l,onChange:c=>r(c.target.value)}),b.jsx("div",{className:"md-resizer",role:"separator","aria-orientation":"vertical","aria-label":"Resize Markdown editor",onPointerDown:c=>Sw(c,c.currentTarget.parentElement,.2,.8,s)}),b.jsx("div",{className:"md-preview",children:b.jsx(Qi,{source:n})})]})}function pl({label:n,value:r,onChange:l,type:o="text",placeholder:s}){return b.jsxs("label",{className:"field-group",children:[b.jsx(qe,{color:"gray",size:"1",weight:"bold",children:n}),b.jsx(Eu,{className:"field-input","aria-label":n,type:o,value:r,placeholder:s,onChange:c=>l(c.target.value)})]})}function Ro({label:n,value:r,options:l,onChange:o}){return b.jsxs("label",{className:"field-group",children:[b.jsx(qe,{color:"gray",size:"1",weight:"bold",children:n}),b.jsxs(Hp,{value:r||"__unset__",onValueChange:s=>o(s==="__unset__"?"":s),children:[b.jsx(qp,{"aria-label":n,className:"field-select"}),b.jsx(Vp,{children:l.map(s=>b.jsx(Qc,{value:s||"__unset__",children:s||"(unset)"},s||"__unset__"))})]})]})}function Qk(){const{preference:n,setPreference:r}=IO();return b.jsxs(Lp,{children:[b.jsx(Gp,{content:"Settings",children:b.jsx(Pp,{children:b.jsx(Qr,{"aria-label":"Settings",variant:"soft",children:b.jsx(sk,{})})})}),b.jsx(Up,{align:"end",width:"220px",children:b.jsxs(We,{direction:"column",gap:"3",children:[b.jsx(Dn,{as:"h2",size:"3",children:"Appearance"}),b.jsx(JS,{value:n,onValueChange:l=>r(iw(l)),children:b.jsx(We,{direction:"column",gap:"2",children:["light","dark","system"].map(l=>b.jsx(qe,{as:"label",size:"2",children:b.jsxs(We,{align:"center",gap:"2",children:[b.jsx(ew,{value:l}),l[0].toUpperCase()+l.slice(1)]})},l))})})]})})]})}const qc=["backlog","active","review","completed","discarded"],Wk=[{name:"initial-request",label:"Initial Request"},{name:"spec",label:"Spec"},{name:"plan",label:"Implementation Plan"}];function Jk({item:n,artifacts:r,onEditInitialRequest:l,onEditArtifact:o,onReadArtifact:s}){const[c,u]=p.useState("initial-request"),[d,h]=p.useState({});p.useEffect(()=>{u("initial-request"),h({})},[n.slug]);const g=E=>{const T=r.find(A=>A.name===E),R=d[E];!T?.present||R?.content!==void 0||R?.loading||(h(A=>({...A,[E]:{loading:!0}})),s(n.slug,E).then(A=>{h(O=>({...O,[E]:{content:A.content}}))}).catch(A=>{h(O=>({...O,[E]:{error:A instanceof Error?A.message:String(A)}}))}))},v=r.find(E=>E.name==="initial-request")?.present??!1,y=c==="initial-request"?void 0:r.find(E=>E.name===c),x=c==="initial-request"?void 0:d[c],w=()=>{c!=="initial-request"&&g(c)};return b.jsxs(tw,{className:"work-document-tabs",value:c,onValueChange:E=>{(E==="initial-request"||E==="spec"||E==="plan")&&(u(E),E!=="initial-request"&&g(E))},children:[b.jsx(nw,{"aria-label":"Work content",children:Wk.map(E=>b.jsx(rw,{value:E.name,"aria-label":E.label,children:E.label},E.name))}),b.jsx("div",{className:"work-document-panel",children:c==="initial-request"?b.jsxs(b.Fragment,{children:[b.jsx(We,{justify:"end",children:b.jsx(et,{size:"1",variant:"soft",type:"button",onClick:l,children:"Edit Initial Request"})}),v?b.jsx(Qi,{source:n.body??"",resolveLinks:!0}):b.jsx(qe,{color:"gray",children:"Initial Request is not yet present."})]}):y?.present?x?.error?b.jsxs(We,{direction:"column",align:"start",gap:"2",children:[b.jsxs(qe,{color:"red",children:["Could not load this document: ",x.error]}),b.jsx(et,{size:"1",variant:"soft",type:"button",onClick:w,children:"Retry"})]}):x?.content===void 0?b.jsx(qe,{color:"gray",children:"Loading document..."}):b.jsxs(b.Fragment,{children:[b.jsx(We,{justify:"end",children:b.jsxs(et,{size:"1",variant:"soft",type:"button",onClick:()=>o(n.slug,c),children:["Edit"," ",c==="spec"?"Spec":"Implementation Plan"]})}),b.jsx(Qi,{source:x.content,resolveLinks:!0})]}):b.jsx(qe,{color:"gray",children:c==="spec"?"Spec is not yet present.":"Implementation Plan is not yet present."})})]})}const eM=[["Status",["","Supported","Partial","Missing","Blocked","Omitted"]],["Priority",["","P0","P1","P2","P3"]],["Lifecycle",["","Experimental","Stable","Deprecated"]],["Feature",null],["Subject",null],["Roles",null],["When",null],["Gaps",null],["Blocked by",null],["Tracker",null],["Planning doc",null],["Superseded by",null]];function tM({axis:n,registeredTags:r,statusFilter:l,setStatusFilter:o,kindFilter:s,setKindFilter:c,tagFilter:u,setTagFilter:d,workSortKey:h,setWorkSortKey:g,workSortDirection:v,setWorkSortDirection:y}){if(n==="capabilities")return null;const x=qc.filter(w=>l[w]);return b.jsxs(We,{className:"status-filters",role:"group","aria-label":"Filters",align:"center",gap:"2",wrap:"wrap",children:[n==="work"&&b.jsxs(b.Fragment,{children:[b.jsx(Wb,{label:"Status",options:qc,value:x,capitalize:!0,onChange:w=>o(Object.fromEntries(qc.map(E=>[E,w.includes(E)])))}),b.jsx(nM,{sortKey:h,direction:v,onSortKeyChange:g,onDirectionChange:y})]}),b.jsx(Wb,{label:n==="taxonomy"?"Kind":"Tags",options:n==="taxonomy"?["Feature","Vocabulary"]:r,value:n==="taxonomy"?s:u,onChange:n==="taxonomy"?c:d})]})}function nM({sortKey:n,direction:r,onSortKeyChange:l,onDirectionChange:o}){const s=r==="ascending"?"descending":"ascending";return b.jsxs(We,{className:"sort-control",align:"center",gap:"1",children:[b.jsxs(Hp,{value:n,onValueChange:c=>{(c==="name"||c==="modified")&&l(c)},children:[b.jsx(qp,{"aria-label":"Sort work items"}),b.jsxs(Vp,{children:[b.jsx(Qc,{value:"name",children:"Name"}),b.jsx(Qc,{value:"modified",children:"Modified"})]})]}),b.jsx(Qr,{type:"button",size:"1",variant:"soft","aria-label":`Sort ${s}`,title:`Sort ${s}`,onClick:()=>o(s),children:r==="ascending"?"↑":"↓"})]})}function Wb({label:n,options:r,value:l,onChange:o,capitalize:s=!1}){return b.jsxs(Lp,{children:[b.jsx(Pp,{children:b.jsxs(et,{size:"1",variant:"soft",children:[n,l.length?` (${l.length})`:""]})}),b.jsx(Up,{align:"start",className:"facet-panel",width:"180px",children:b.jsx(We,{direction:"column",gap:"2",children:r.length?r.map(c=>b.jsx(qe,{as:"label",className:"facet-option",size:"2",children:b.jsxs(We,{align:"center",gap:"2",children:[b.jsx(wu,{checked:l.includes(c),onCheckedChange:u=>o(u?[...l,c]:l.filter(d=>d!==c))})," ",s?c[0].toUpperCase()+c.slice(1):c]})},c)):b.jsx(qe,{className:"facet-empty",color:"gray",size:"1",children:"none available"})})})]})}function rM({axis:n,detail:r,onEdit:l,onResource:o,onOpen:s,onReadArtifact:c,onDeletePlanStage:u,onAction:d}){if(n==="work"){const g=r,v=g.item;return b.jsxs(b.Fragment,{children:[b.jsxs("div",{className:"detail-head",children:[b.jsxs("div",{children:[b.jsx(Dn,{as:"h2",size:"5",children:v.title??v.slug}),b.jsx(qe,{className:"item-meta",color:"gray",size:"2",children:v.slug}),b.jsx(Eo,{value:v.modified})]}),b.jsxs(We,{className:"detail-actions",align:"center",gap:"2",children:[b.jsx($i,{children:v.status}),b.jsx(et,{className:"edit-btn",variant:"soft",type:"button",onClick:l,children:"Edit"})]})]}),b.jsxs(We,{className:"action-buttons",gap:"2",wrap:"wrap",children:[v.status==="backlog"&&b.jsxs(b.Fragment,{children:[b.jsx(et,{className:"action-btn start",color:"green",variant:"soft",type:"button",onClick:()=>d("start"),children:"Start"}),b.jsx(et,{className:"action-btn discard",color:"amber",variant:"soft",type:"button",onClick:()=>d("discard"),children:"Discard"}),b.jsx(et,{className:"action-btn drop",color:"red",variant:"soft",type:"button",onClick:()=>d("drop"),children:"Drop"})]}),v.status==="active"&&b.jsx(et,{className:"action-btn complete",color:"green",variant:"soft",type:"button",onClick:()=>d("complete"),children:"Complete"})]}),b.jsxs(nh,{children:[b.jsx(un,{name:"Priority",value:v.priority}),b.jsx(un,{name:"Effort",value:v.effort}),b.jsx(un,{name:"Complexity",value:v.complexity}),b.jsx(un,{name:"Tags",value:v.tags?.join(", ")||"-"}),b.jsx(un,{name:"Resolution",value:v.resolution}),b.jsx(un,{name:"Blocked by",value:v.blocked_by?.map(y=>y.slug??y.external).join(", ")||"-"}),v.parent&&b.jsx(un,{name:"Parent",value:v.parent}),v.initiative&&b.jsx(un,{name:"Initiative",value:v.initiative}),v.tracker&&b.jsx(Xk,{binding:v.tracker})]}),b.jsx(Jk,{item:v,artifacts:g.artifacts,onEditInitialRequest:l,onEditArtifact:(y,x)=>o("artifacts",y,x),onReadArtifact:c},`${v.slug}:${g.coreRevision}:${g.artifacts.filter(y=>["initial-request","spec","plan"].includes(y.name)).map(y=>y.revision??"missing").join(":")}`),b.jsx("div",{className:"artifacts",children:g.artifacts.filter(y=>y.present&&!["initial-request","spec","plan"].includes(y.name)).map(y=>b.jsxs(We,{className:"artifact-group",children:[b.jsx(et,{className:"artifact",variant:"soft",type:"button",onClick:()=>s(v.slug,y.name),children:y.name}),b.jsx(Qr,{className:"artifact-edit",variant:"ghost",type:"button","aria-label":`Edit ${y.name}`,onClick:()=>o("artifacts",v.slug,y.name),children:"✎"})]},y.name))}),g.planStages?.length>0&&b.jsxs("div",{className:"plan-stages",children:[b.jsx(Dn,{as:"h3",size:"3",children:"Plan stages"}),g.planStages.map(y=>b.jsxs(cs,{className:"plan-stage",children:[b.jsxs("div",{children:[b.jsx("strong",{children:y.title}),b.jsxs("span",{className:"item-meta",children:[" ",y.id]})]}),b.jsxs("div",{className:"item-meta",children:["Depends on:"," ",y.depends_on.length?y.depends_on.join(", "):"none",y.effort&&` · effort ${y.effort}`,y.complexity&&` · complexity ${y.complexity}`,y.priority!=null&&` · priority ${y.priority}`,y.tags.length>0&&` · ${y.tags.join(", ")}`]}),g.planStages.filter(x=>x.id!==y.id&&JSON.stringify(x.depends_on)===JSON.stringify(y.depends_on)).length>0&&b.jsxs("div",{className:"item-meta",children:["Parallel with:"," ",g.planStages.filter(x=>x.id!==y.id&&JSON.stringify(x.depends_on)===JSON.stringify(y.depends_on)).map(x=>x.id).join(", ")]}),b.jsxs(We,{className:"artifact-group",align:"center",gap:"2",children:[b.jsx(qe,{size:"2",children:y.present?"Document present":"Document missing"}),y.present&&b.jsx(et,{size:"1",variant:"soft",type:"button",onClick:()=>s(v.slug,y.id,"plan-stages"),children:"Open"}),b.jsx(et,{size:"1",variant:"soft",type:"button",onClick:()=>o("plan-stages",v.slug,y.id),children:y.present?"Edit":"Create"}),y.present&&b.jsx(et,{size:"1",color:"red",variant:"soft",type:"button",onClick:()=>u(v.slug,y.id,y.revision),children:"Delete"})]})]},y.id))]}),g.sidecars?.some(y=>y.present)&&b.jsxs("div",{className:"sidecars-section",children:[b.jsx(Dn,{as:"h3",size:"3",children:"Sidecars"}),g.sidecars.filter(y=>y.present).map(y=>b.jsxs(We,{className:"sidecar-item",align:"center",gap:"2",children:[b.jsx(qe,{className:"sidecar-name",color:"gray",size:"2",children:y.name}),y.generated?b.jsx(qe,{color:"gray",size:"1",children:"generated"}):b.jsx(et,{className:"sidecar-edit-btn",size:"1",variant:"soft",type:"button",onClick:()=>o("sidecars",v.slug,y.name),children:"Edit"})]},y.name))]})]})}if(n==="taxonomy"){const g=r.term;return b.jsxs(b.Fragment,{children:[b.jsxs("div",{className:"detail-head",children:[b.jsxs("div",{children:[b.jsx(Dn,{as:"h2",size:"5",children:g.name??g.slug}),b.jsx(qe,{className:"item-meta",color:"gray",size:"2",children:g.qualified??g.slug}),b.jsx(Eo,{value:g.modified})]}),b.jsxs(We,{className:"detail-actions",align:"center",gap:"2",children:[b.jsx($i,{children:g.kind}),b.jsx(et,{className:"edit-btn",variant:"soft",type:"button",onClick:l,children:"Edit"})]})]}),b.jsxs(nh,{children:[b.jsx(un,{name:"Kind",value:g.kind}),b.jsx(un,{name:"Origin",value:g.origin}),b.jsx(un,{name:"Parent",value:g.parent}),b.jsx(un,{name:"Relates to",value:g.relates_to?.join(", ")||"-"}),b.jsx(un,{name:"Vocabulary",value:g.vocabulary?.join(", ")||"-"})]}),b.jsx(Qi,{source:g.description??"",resolveLinks:!0})]})}const h=r.capability;return b.jsxs(b.Fragment,{children:[b.jsxs("div",{className:"detail-head",children:[b.jsxs("div",{children:[b.jsx(Dn,{as:"h2",size:"5",children:h.name??h.path}),b.jsx(qe,{className:"item-meta",color:"gray",size:"2",children:h.path}),b.jsx(Eo,{value:h.modified})]}),b.jsxs(We,{className:"detail-actions",align:"center",gap:"2",children:[b.jsx($i,{children:h.status}),b.jsx(et,{className:"edit-btn",variant:"soft",type:"button",onClick:l,children:"Edit"})]})]}),b.jsx(nh,{children:Object.entries(h.fields??{}).map(([g,v])=>b.jsx(un,{name:g,value:Array.isArray(v)?v.join(", "):v},g))}),b.jsx(Qi,{source:h.body??"",resolveLinks:!0})]})}function aM({editor:n,setDraft:r,saving:l,errors:o,conflict:s,registeredTags:c,data:u,onSave:d,onCancel:h,onRefreshConflict:g}){if(n.mode==="resource")return b.jsxs("div",{className:`editor-container${l?" saving":""}`,children:[b.jsx(Jb,{title:`Edit ${n.kind==="artifacts"?"Artifact":n.kind==="sidecars"?"Sidecar":"Plan stage"}: ${n.name}`,saving:l,onSave:d,onCancel:h}),b.jsx(tu,{errors:o}),!!s&&b.jsx(e0,{onRefresh:g,onDiscard:h}),b.jsx(Qb,{value:n.draft,onChange:y=>r("draft",y)})]});const v=n.draft;return b.jsxs("div",{className:`editor-container${l?" saving":""}`,children:[b.jsx(Jb,{title:`${n.mode==="create"?"Create":"Edit"} ${bo[n.axis]}`,saving:l,onSave:d,onCancel:h}),b.jsx(tu,{errors:o}),!!s&&b.jsx(e0,{onRefresh:g,onDiscard:h}),b.jsx("div",{className:"editor-section",children:"Fields"}),b.jsxs("div",{className:"editor-fields",children:[n.axis==="work"&&b.jsx(lM,{draft:v,setDraft:r,registeredTags:c,data:u,current:n.mode==="core"?n.ref:void 0}),n.axis==="taxonomy"&&b.jsx(oM,{draft:v,setDraft:r,creating:n.mode==="create",data:u,current:n.mode==="core"?n.ref:void 0}),n.axis==="capabilities"&&b.jsx(iM,{draft:v,setDraft:r,creating:n.mode==="create",data:u,current:n.mode==="core"?n.ref:void 0})]}),b.jsx("div",{className:"editor-section",children:"Body"}),b.jsx(Qb,{value:String(v.body??""),onChange:y=>r("body",y)})]})}function Jb({title:n,saving:r,onSave:l,onCancel:o}){return b.jsxs(We,{className:"editor-header",justify:"between",align:"center",children:[b.jsx(Dn,{as:"h2",className:"editor-title",size:"3",children:n}),b.jsxs(We,{className:"editor-actions",gap:"2",children:[b.jsx(et,{variant:"soft",color:"gray",type:"button",onClick:o,children:"Cancel"}),b.jsx(et,{type:"button",disabled:r,onClick:l,children:r?"Saving...":"Save"})]})]})}function e0({onRefresh:n,onDiscard:r}){return b.jsx(Br,{className:"conflict-banner",color:"amber",role:"alert",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"Stale write detected"}),b.jsx(qe,{as:"p",size:"2",children:"The server version changed. Your draft is preserved."}),b.jsxs(We,{gap:"2",mt:"2",children:[b.jsx(et,{size:"1",variant:"soft",onClick:n,children:"Refresh from server"}),b.jsx(et,{size:"1",color:"red",variant:"soft",onClick:r,children:"Discard draft"})]})]})})}function ml(n,r,l,o){return rk(n,{...r,currentIdentifier:l,selected:o})}function lM({draft:n,setDraft:r,registeredTags:l,data:o,current:s}){const c=n.tags??[];return b.jsxs(b.Fragment,{children:[b.jsx(pl,{label:"Title",value:String(n.title??""),onChange:u=>r("title",u)}),b.jsx(pl,{label:"Priority",type:"number",value:String(n.priority??""),onChange:u=>r("priority",u)}),b.jsx(Ro,{label:"Effort",value:String(n.effort??""),options:["","low","medium","high","very-high"],onChange:u=>r("effort",u)}),b.jsx(Ro,{label:"Complexity",value:String(n.complexity??""),options:["","low","medium","high","very-high"],onChange:u=>r("complexity",u)}),b.jsx(hl,{label:"Parent",value:String(n.parent??""),options:ml("work-parent",o,s),onChange:u=>r("parent",u)}),b.jsx(hl,{label:"Initiative",value:String(n.initiative??""),options:ml("work-initiative",o,s),onChange:u=>r("initiative",u)}),b.jsx(hl,{label:"Blockers",multiple:!0,value:n.blockers??[],options:ml("work-blockers",o,s,n.blockers??[]),onChange:u=>r("blockers",u)}),b.jsxs("div",{className:"field-group",children:[b.jsx(qe,{color:"gray",size:"1",weight:"bold",children:"Tags"}),b.jsx(We,{className:"tag-checkboxes",gap:"3",wrap:"wrap",children:[...new Set([...l,...c])].map(u=>b.jsx(qe,{as:"label",className:`tag-checkbox${l.includes(u)?"":" tag-stale"}`,size:"2",children:b.jsxs(We,{align:"center",gap:"1",children:[b.jsx(wu,{checked:c.includes(u),onCheckedChange:d=>r("tags",d?[...c,u]:c.filter(h=>h!==u))})," ",u,l.includes(u)?"":" (unregistered)"]})},u))})]})]})}function oM({draft:n,setDraft:r,creating:l,data:o,current:s}){return b.jsxs(b.Fragment,{children:[b.jsx(pl,{label:"Name",value:String(n.name??""),onChange:c=>r("name",c)}),b.jsx(Ro,{label:"Kind",value:String(n.kind??"Vocabulary"),options:["Vocabulary","Feature"],onChange:c=>r("kind",c)}),l&&b.jsxs(b.Fragment,{children:[b.jsx(pl,{label:"Slug",value:String(n.slug??""),onChange:c=>r("slug",c)}),b.jsx(hl,{label:"Parent",value:String(n.parent??""),options:ml("taxonomy-parent",o,s),onChange:c=>r("parent",c)})]}),!l&&b.jsx(hl,{label:"Relates to",multiple:!0,value:n.relates_to??[],options:ml("taxonomy-relates",o,s,n.relates_to??[]),onChange:c=>r("relates_to",c)}),b.jsx(hl,{label:"Vocabulary",multiple:!0,value:n.vocabulary??[],options:ml("taxonomy-vocabulary",o,s,n.vocabulary??[]),onChange:c=>r("vocabulary",c)})]})}function iM({draft:n,setDraft:r,creating:l,data:o,current:s}){if(l)return b.jsxs(b.Fragment,{children:[b.jsx(pl,{label:"Path",value:String(n.path??""),placeholder:"e.g. web/editing",onChange:u=>r("path",u)}),b.jsx(pl,{label:"Name",value:String(n.name??""),onChange:u=>r("name",u)}),b.jsx(Ro,{label:"Status",value:String(n.status??"Missing"),options:["Supported","Partial","Missing","Blocked","Omitted"],onChange:u=>r("status",u)})]});const c={Feature:{field:"capability-feature"},Subject:{field:"capability-subject",multiple:!0},Roles:{field:"capability-roles",multiple:!0},When:{field:"capability-when",multiple:!0,negated:!0},"Blocked by":{field:"capability-blocked-by"},"Planning doc":{field:"capability-planning-doc"},"Superseded by":{field:"capability-superseded-by"}};return b.jsx(b.Fragment,{children:eM.map(([u,d])=>{if(d)return b.jsx(Ro,{label:u,value:String(n[u]??""),options:d,onChange:v=>r(u,v)},u);const h=c[u];if(!h)return b.jsx(pl,{label:u,value:String(n[u]??""),onChange:v=>r(u,v)},u);const g=h.multiple?Array.isArray(n[u])?n[u]:String(n[u]??"").split(",").map(v=>v.trim()).filter(Boolean):String(n[u]??"");return b.jsx(hl,{label:u,value:g,multiple:h.multiple,negated:h.negated,options:ml(h.field,o,s,Array.isArray(g)?g.map(v=>v.replace(/^!/,"")):void 0),onChange:v=>r(u,v)},u)})})}function sM({onClose:n,onStart:r,errors:l}){const[o,s]=p.useState(!1);return b.jsxs(ww,{title:"Start Work Item",onClose:n,children:[b.jsx(tu,{errors:l}),o&&b.jsx(Br,{color:"amber",children:b.jsx(Hr,{children:"This item may have unresolved blockers. You can force-start it."})}),b.jsxs(We,{className:"modal-actions",gap:"2",justify:"end",children:[b.jsx(et,{variant:"soft",color:"gray",type:"button",onClick:n,children:"Cancel"}),b.jsx(et,{type:"button",onClick:()=>void r(o).then(c=>{c||s(!0)}),children:o?"Start (force)":"Start"})]})]})}function cM({detail:n,onClose:r,onComplete:l,errors:o,discardOnly:s=!1}){const c=n.dodChecklist?.length?n.dodChecklist:["tests pass","docs synced","capabilities reconciled","reviewed"],[u,d]=p.useState([]),[h,g]=p.useState(s?"wontfix":""),[v,y]=p.useState(!1),x=h===""||h==="done";return b.jsxs(ww,{title:x?"Complete Work Item":"Close Work Item",onClose:r,children:[b.jsx(tu,{errors:o}),b.jsx(Br,{className:"hooks-not-run",color:"gray",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"Configured hooks do not run here"}),b.jsx("br",{}),"The web app performs and commits the transition, but does not run any commands bound to it in"," ",b.jsx("code",{children:"tcw-config.yaml"}),". Use"," ",b.jsx("code",{children:"tcw work complete"})," if you need them. If the commit itself is refused, the item still moves and the reason is printed by ",b.jsx("code",{children:"tcw serve"})," in your terminal."]})}),x?b.jsx(Br,{className:"reconciliation-reminder",color:"blue",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"Reconciliation reminder"}),b.jsx("br",{}),"Reconcile the capabilities ledger before completing."]})}):h&&b.jsx(Br,{className:"discard-warning",color:"amber",children:b.jsxs(Hr,{children:[b.jsx("strong",{children:"This item will be discarded"}),b.jsx("br",{}),"It lands in ",b.jsx("code",{children:"discarded/"}),", not"," ",b.jsx("code",{children:"completed/"}),", and this is permanent. Mark any capability it declared as ",b.jsx("code",{children:"Omitted"})," ","if it will never be built."]})}),b.jsx(Ro,{label:"Resolution",value:h,options:s?["wontfix","duplicate","superseded"]:["","done","wontfix","duplicate","superseded"],onChange:g}),x&&b.jsxs(b.Fragment,{children:[b.jsx(Dn,{as:"h3",size:"3",children:"Definition of Done"}),b.jsx(We,{className:"dod-list",direction:"column",children:c.map(w=>b.jsx(qe,{as:"label",className:"dod-item",size:"2",children:b.jsxs(We,{align:"center",gap:"2",children:[b.jsx(wu,{checked:u.includes(w),onCheckedChange:E=>d(E?[...u,w]:u.filter(T=>T!==w))})," ",w]})},w))})]}),v&&b.jsx(Br,{className:"modal-error",color:"red",children:b.jsx(Hr,{children:"Completion was blocked. You can retry while ignoring blockers."})}),b.jsxs(We,{className:"modal-actions",gap:"2",justify:"end",children:[b.jsx(et,{variant:"soft",color:"gray",type:"button",onClick:r,children:"Cancel"}),b.jsx(et,{type:"button",disabled:!h||x&&u.length!==c.length,onClick:()=>void l({resolution:h,dod_ack:x?u:[],force:v}).then(w=>{w||y(!0)}),children:v?x?"Complete (ignore blockers)":"Discard (ignore blockers)":x?"Complete":"Discard"})]})]})}async function uM(n,r,l,o){try{const s=await fetch(`/api/work/${qt(n)}/${l}/${qt(r)}/open`,{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"});if(!s.ok)throw new Error(`${s.status} ${s.statusText}`);if(s.status===204){o("Opened resource");return}const c=await s.json();c.url&&window.open(c.url,"_blank","noopener")}catch(s){o(`Could not open resource: ${s instanceof Error?s.message:String(s)}`)}}function fM(){const n=La(),r=x0(),l=p.useRef(Gb(n.pathname)).current,[o,s]=p.useState(l.axis),[c,u]=p.useState(l.key),[d,h]=p.useState({work:[],taxonomy:[],capabilities:[]}),[g,v]=p.useState([]),[y,x]=p.useState(""),[w,E]=p.useState({backlog:!0,active:!0,completed:!1,discarded:!1}),[T,R]=p.useState([]),[A,O]=p.useState([]),[N,M]=p.useState("name"),[L,D]=p.useState("ascending"),[F,te]=p.useState(Yk),[J,ae]=p.useState(()=>({work:new Set,taxonomy:new Set,capabilities:new Set})),[ce,oe]=p.useState(()=>{const q=Number.parseFloat(localStorage.getItem("tcw.listWidth")??"");return Number.isFinite(q)&&q>=.12&&q<=.6?q:.28}),[se,de]=p.useState(null),[Ce,P]=p.useState(!1),[G,Z]=p.useState(null),[ue,_]=p.useState(!1),[X,re]=p.useState(!1),[ne,ie]=p.useState([]),[_e,pe]=p.useState(null),[pt,he]=p.useState([]),[Ge,Ue]=p.useState(""),[Me,Ye]=p.useState(null),[lt,gt]=p.useState(""),vt=p.useCallback(q=>{Ue(q),window.setTimeout(()=>Ue(""),2800)},[]),Gn=p.useCallback((q,fe)=>{const me=hM(fe);he(me),vt(me.length?`${q} with ${me.length} validation issue${me.length===1?"":"s"}`:q)},[vt]),At=p.useCallback(()=>!ue||window.confirm("You have unsaved changes. Leave without saving?"),[ue]);IR(p.useCallback(q=>{ue&&q.preventDefault()},[ue]));const Wt=p.useCallback(async(q=!0)=>{try{const[fe,me,ye,Le]=await Promise.all([fl("/api/work"),fl("/api/taxonomy"),fl("/api/capabilities"),fl("/api/work/tags").catch(()=>({tags:[]}))]);h({work:fe,taxonomy:me,capabilities:ye}),v(Le.tags??[]),q||u(null),gt("")}catch(fe){gt(fe instanceof Error?fe.message:String(fe))}},[]);p.useEffect(()=>{Wt()},[Wt]),p.useEffect(()=>{localStorage.setItem("tcw.treeExpanded",JSON.stringify(Object.fromEntries(Ah.map(q=>[q,[...F[q]]]))))},[F]),p.useEffect(()=>{localStorage.setItem("tcw.listWidth",String(ce))},[ce]),p.useEffect(()=>{const q=Gb(n.pathname);if(q.axis!==o||q.key!==c){if(!At()){r(Nh(o,c),{replace:!0});return}Z(null),_(!1),de(null),s(q.axis),u(q.key)}},[n.pathname]);const Ha=d[o];p.useEffect(()=>{if(!c||!d[o].some(fe=>Dh(o,fe)===c)){de(null);return}P(!0);const q=o==="work"?`/api/work/${qt(c)}`:o==="taxonomy"?`/api/taxonomy/${qt(c)}`:`/api/capabilities/${qt(c)}`;fl(q).then(de).catch(fe=>gt(String(fe))).finally(()=>P(!1))},[o,c,d]);const Wn=(q,fe)=>{if(At()&&(Z(null),_(!1),ie([]),pe(null),de(null),s(q),u(fe),r(Nh(q,fe)),fe)){const me=q==="work"?ek(fe,d.work):JO(fe);te(ye=>({...ye,[q]:new Set([...ye[q],...me])}))}},yr=q=>{G&&!At()||(Z(null),_(!1),x(q),Wn(o,null))},gn=p.useCallback(q=>{if(o==="work"){const fe=q;if(fe.status&&w[fe.status]===!1||A.length&&!A.some(me=>fe.tags?.includes(me)))return!1}return o==="taxonomy"&&T.length&&!T.includes(q.kind??"")?!1:!y||JSON.stringify(q).toLowerCase().includes(y.toLowerCase())},[o,y,T,w,A]),Jt=p.useMemo(()=>{const q=o==="work"?cw(QO(d.work),N,L):ZO(Ha,ye=>Dh(o,ye));if(!!!(y||T.length||A.length||o==="work"&&qc.some(ye=>!w[ye])))return{nodes:q,forced:new Set};const me=WO(q,gn);return{nodes:me.nodes,forced:y?me.forceExpand:new Set}},[o,Ha,d.work,y,T,w,A,gn,L,N]),Al=p.useMemo(()=>new Set([...F[o],...Jt.forced]),[o,F,Jt.forced]);p.useEffect(()=>{const q=[],fe=ye=>ye.forEach(Le=>{Le.children.length&&(q.push(Le.path),fe(Le.children))});fe(Jt.nodes);const me=q.filter(ye=>!J[o].has(ye));me.length&&(ae(ye=>({...ye,[o]:new Set([...ye[o],...me])})),te(ye=>({...ye,[o]:new Set([...ye[o],...me])})))},[o,J,Jt.nodes]);const qa=(q,fe)=>{Z(me=>me&&(me.mode==="resource"?q==="draft"?{...me,draft:String(fe)}:me:{...me,draft:{...me.draft,[q]:fe}})),_(!0)},Kt=()=>{Z({mode:"create",axis:o,draft:o==="work"?{title:"",priority:"",effort:"",complexity:"",parent:"",initiative:"",blockers:[],tags:[],body:""}:o==="taxonomy"?{name:"",kind:"Vocabulary",slug:"",parent:"",vocabulary:[],description:"",body:""}:{path:"",name:"",status:"Missing",body:""}}),u(null),_(!1),ie([]),pe(null)},yt=()=>{if(!se)return;let q,fe,me;if(o==="work"){const ye=se,Le=ye.item,Yn=ye.artifacts.find(xr=>xr.name==="initial-request")?.present??!1;q={title:Le.title??"",priority:Le.priority??"",effort:Le.effort??"",complexity:Le.complexity??"",parent:Le.parent??"",initiative:Le.initiative??"",blockers:(Le.blocked_by??[]).map(xr=>xr.slug??xr.external??"").filter(Boolean),tags:Le.tags??[],body:Yn?Le.body??"":""},fe=Le.slug,me=ye.coreRevision}else if(o==="taxonomy"){const ye=se,Le=ye.term;q={name:Le.name??"",kind:Le.kind??"Vocabulary",relates_to:Le.relates_to??[],vocabulary:Le.vocabulary??[],body:Le.description??""},fe=Le.qualified??Le.slug,me=ye.coreRevision}else{const ye=se,Le=ye.capability;q={...Le.fields??{},body:Le.body??""},fe=Le.path,me=ye.coreRevision}Z({mode:"core",axis:o,ref:fe,revision:me,draft:q,original:structuredClone(q)}),_(!1),ie([]),pe(null)},vn=async(q,fe,me)=>{try{const ye=await fl(`/api/work/${qt(fe)}/${q}/${qt(me)}`);Z({mode:"resource",axis:"work",kind:q,slug:fe,name:me,revision:ye.revision,mediaType:ye.mediaType??(q==="artifacts"?"text/markdown":"application/yaml"),draft:ye.content,original:ye.content}),_(!1),ie([]),pe(null)}catch(ye){if(q==="plan-stages"){Z({mode:"resource",axis:"work",kind:q,slug:fe,name:me,revision:"",mediaType:"text/markdown",draft:`## Objective
168
168
 
169
169
 
170
170
 
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <script src="/theme-init.js"></script>
7
7
  <title>TCW</title>
8
- <script type="module" crossorigin src="/assets/index-CCjF_dHr.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-CNxlcI8A.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-CXNiOIeg.css">
10
10
  </head>
11
11
  <body>