tcw-cli 2.4.0__tar.gz → 2.5.1__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 (186) hide show
  1. {tcw_cli-2.4.0/tcw_cli.egg-info → tcw_cli-2.5.1}/PKG-INFO +30 -6
  2. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/README.md +29 -5
  3. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/pyproject.toml +1 -1
  4. tcw_cli-2.5.1/tcw/__init__.py +1 -0
  5. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/__init__.py +50 -0
  6. tcw_cli-2.4.0/tcw/serve/dist/client/assets/index-CCjF_dHr.js → tcw_cli-2.5.1/tcw/serve/dist/client/assets/index-CNxlcI8A.js +1 -1
  7. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/dist/client/index.html +1 -1
  8. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/base.py +418 -6
  9. tcw_cli-2.5.1/tcw/store/config_edit.py +641 -0
  10. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/fs.py +612 -166
  11. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/taxonomy/cli.py +2 -2
  12. tcw_cli-2.5.1/tcw/tracker/create.py +207 -0
  13. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/intake.py +329 -7
  14. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/jira.py +20 -0
  15. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/sync.py +48 -18
  16. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/validate.py +23 -1
  17. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/cli.py +738 -43
  18. tcw_cli-2.5.1/tcw/work/procedures/decompose.md +23 -0
  19. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/projection.py +21 -1
  20. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/recursion.py +195 -35
  21. {tcw_cli-2.4.0 → tcw_cli-2.5.1/tcw_cli.egg-info}/PKG-INFO +30 -6
  22. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/SOURCES.txt +11 -1
  23. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities.py +39 -0
  24. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities_federation.py +56 -14
  25. tcw_cli-2.5.1/tests/test_capability_gate_children.py +623 -0
  26. tcw_cli-2.5.1/tests/test_check_versions.py +308 -0
  27. tcw_cli-2.5.1/tests/test_child_status.py +649 -0
  28. tcw_cli-2.5.1/tests/test_config_edit.py +466 -0
  29. tcw_cli-2.5.1/tests/test_config_edit_writers.py +384 -0
  30. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_environment_hardness.py +10 -5
  31. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_epic_completable.py +36 -0
  32. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_external_work_store.py +56 -0
  33. tcw_cli-2.5.1/tests/test_legacy_store_config.py +346 -0
  34. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_multiproject.py +4 -1
  35. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_projection.py +18 -0
  36. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_repo_lifecycle.py +9 -4
  37. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_retention.py +125 -0
  38. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve.py +4 -2
  39. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_write.py +122 -0
  40. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_session_bootstrap.py +179 -1
  41. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_editor.py +23 -2
  42. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_provisioning.py +129 -1
  43. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_taxonomy.py +238 -2
  44. tcw_cli-2.5.1/tests/test_tracker_cli.py +1657 -0
  45. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_client.py +7 -1
  46. tcw_cli-2.5.1/tests/test_tracker_create.py +250 -0
  47. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_help.py +2 -2
  48. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_inheritance.py +51 -0
  49. tcw_cli-2.5.1/tests/test_tracker_pre_backlog.py +769 -0
  50. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_surface.py +42 -0
  51. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_sync.py +80 -1
  52. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_sync_config.py +4 -1
  53. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_validate.py +133 -0
  54. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_validate.py +14 -3
  55. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_work.py +160 -47
  56. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_worktree_completion.py +66 -0
  57. tcw_cli-2.4.0/tcw/__init__.py +0 -1
  58. tcw_cli-2.4.0/tcw/work/procedures/decompose.md +0 -18
  59. tcw_cli-2.4.0/tests/test_tracker_cli.py +0 -468
  60. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/LICENSE +0 -0
  61. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/setup.cfg +0 -0
  62. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/capabilities/__init__.py +0 -0
  63. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/capabilities/cli.py +0 -0
  64. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/cli.py +0 -0
  65. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/harness.py +0 -0
  66. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/refs.py +0 -0
  67. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/dist/client/assets/index-CXNiOIeg.css +0 -0
  68. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/dist/client/theme-init.js +0 -0
  69. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/dist/server.cjs +0 -0
  70. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/runtime.py +0 -0
  71. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/stdin.py +0 -0
  72. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/__init__.py +0 -0
  73. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/checkouts.py +0 -0
  74. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/project.py +0 -0
  75. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/taxonomy/__init__.py +0 -0
  76. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/__init__.py +0 -0
  77. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/claim.py +0 -0
  78. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/ownership.py +0 -0
  79. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/progress.py +0 -0
  80. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/__init__.py +0 -0
  81. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/generate.py +0 -0
  82. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/hooks.py +0 -0
  83. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/audit-backlog.md +0 -0
  84. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/consolidate-plans.md +0 -0
  85. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/create-work.md +0 -0
  86. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/delegation.md +0 -0
  87. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/documentation-sync.md +0 -0
  88. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/post-mortem.md +0 -0
  89. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/search.md +0 -0
  90. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/triage-issues.md +0 -0
  91. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/unattended-work.md +0 -0
  92. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/implement.md +0 -0
  93. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/inbox.md +0 -0
  94. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/plan.md +0 -0
  95. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/postmortem.md +0 -0
  96. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/request.md +0 -0
  97. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/spec.md +0 -0
  98. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/verify.md +0 -0
  99. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/resolve.py +0 -0
  100. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/templates.py +0 -0
  101. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/dependency_links.txt +0 -0
  102. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/entry_points.txt +0 -0
  103. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/requires.txt +0 -0
  104. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/top_level.txt +0 -0
  105. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_body_prompt.py +0 -0
  106. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities_reset.py +0 -0
  107. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities_rm.py +0 -0
  108. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities_sidecar.py +0 -0
  109. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capability_ref_wording.py +0 -0
  110. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_cli_help_coverage.py +0 -0
  111. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_committed_paths.py +0 -0
  112. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_configuration_text_home.py +0 -0
  113. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_cut_version.py +0 -0
  114. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_documentation_config.py +0 -0
  115. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_documentation_prompt.py +0 -0
  116. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_documentation_sync_wiring.py +0 -0
  117. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_documented_cli_surface.py +0 -0
  118. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_dynamic_skill_marker.py +0 -0
  119. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_edit_type.py +0 -0
  120. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_coverage.py +0 -0
  121. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_files_changed.py +0 -0
  122. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_fixture.py +0 -0
  123. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_grading.py +0 -0
  124. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_runner.py +0 -0
  125. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_falsification_rule.py +0 -0
  126. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_generate_hook.py +0 -0
  127. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_harness.py +0 -0
  128. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_inbox_title.py +0 -0
  129. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_baseline.py +0 -0
  130. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_hooks.py +0 -0
  131. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_inert.py +0 -0
  132. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_policy.py +0 -0
  133. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_validation.py +0 -0
  134. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_non_git_writes.py +0 -0
  135. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_plugin_manifests.py +0 -0
  136. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_procedure_config.py +0 -0
  137. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_procedure_verb.py +0 -0
  138. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_project_overrides.py +0 -0
  139. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_project_registry.py +0 -0
  140. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_prompt_fallback.py +0 -0
  141. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_qualified_ref.py +0 -0
  142. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_recursion.py +0 -0
  143. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_refs.py +0 -0
  144. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_remote_session_setup.py +0 -0
  145. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_resolve.py +0 -0
  146. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_resolve_procedure.py +0 -0
  147. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_scaffold.py +0 -0
  148. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_descendants.py +0 -0
  149. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_projection.py +0 -0
  150. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_resolve.py +0 -0
  151. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_runtime.py +0 -0
  152. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_shipped_procedures.py +0 -0
  153. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_shipped_prompts.py +0 -0
  154. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_show_json.py +0 -0
  155. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_skill_flow.py +0 -0
  156. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_skill_lifecycle_parity.py +0 -0
  157. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_skill_path_pointers.py +0 -0
  158. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_smoke.py +0 -0
  159. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_stage_validate.py +0 -0
  160. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_stage_verb.py +0 -0
  161. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_status_parity.py +0 -0
  162. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_stdin.py +0 -0
  163. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_stdin_cli.py +0 -0
  164. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_bounds.py +0 -0
  165. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_nodes.py +0 -0
  166. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_publication.py +0 -0
  167. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_subprocess_stdin.py +0 -0
  168. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tombstone.py +0 -0
  169. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_absent.py +0 -0
  170. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_binding.py +0 -0
  171. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_claim.py +0 -0
  172. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_claimability.py +0 -0
  173. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_comment.py +0 -0
  174. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_config.py +0 -0
  175. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_hold.py +0 -0
  176. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_import.py +0 -0
  177. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_link.py +0 -0
  178. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_ownership.py +0 -0
  179. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_replay.py +0 -0
  180. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_strict.py +0 -0
  181. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_unattended_work_skill.py +0 -0
  182. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_unpushed_version_script.py +0 -0
  183. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_validate_target.py +0 -0
  184. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_work_autocommit.py +0 -0
  185. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_work_review.py +0 -0
  186. {tcw_cli-2.4.0 → tcw_cli-2.5.1}/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.5.1
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,6 +730,7 @@ 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 |
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 |
729
734
  | `start` | moves the item | also claims a linked ticket, and posts a comment if `comments: true` |
730
735
  | `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` |
731
736
  | 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 |
@@ -770,7 +775,11 @@ triaged. When it is set, `tcw work inbox list` prints two sections, `raw intake:
770
775
  `inbox accept` then take a ticket key as well as an inbox entry: an inbox entry with
771
776
  the same name wins, and `--ticket` reads the name as a ticket anyway. Accepting a
772
777
  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.
778
+ leaves out tickets that already have a work item; TCW does not filter them. A
779
+ triage status does not offer the transition that starts work, so to take tickets
780
+ straight from it, name the status and its way out under `pre-backlog` (for example
781
+ `pre-backlog: { Triage: Accept }`, with `statuses.backlog` set): every claim then
782
+ moves the ticket out of Triage to the backlog first, and says so.
774
783
 
775
784
  The credentials entries hold the **names** of environment variables, never the
776
785
  e-mail address or token themselves.
@@ -795,6 +804,21 @@ tcw work tracker link <slug> ENG-517 # TCW: records the link.
795
804
  tcw work start <slug> # TCW: backlog → active. Jira: if unassigned or yours, ENG-517 → In Progress, assigned to you
796
805
  ```
797
806
 
807
+ **Example 2a: making a ticket for an item that has none.**
808
+
809
+ ```sh
810
+ tcw work new "Speed up the checkout page" # TCW: creates a backlog item. Jira: unchanged
811
+ tcw work tracker create <slug> --dry-run # TCW: says what it would do. Jira: unchanged
812
+ tcw work tracker create <slug> # TCW: binds the new ticket. Jira: creates it, then moves it to statuses.backlog
813
+ tcw work tracker create --all # TCW: the same for every open item. Jira: one new ticket per item without one
814
+ ```
815
+
816
+ The move out of whatever status Jira starts issues in is part of creating the
817
+ ticket, not a courtesy: in a project with a triage column, that starting status
818
+ is usually the one `inbox-query` selects, so a ticket left there would come back
819
+ through `tcw work inbox` as new inbound work. Run `--all` with `--dry-run`
820
+ first — nothing here can delete a ticket.
821
+
798
822
  **Example 3: taking a ticket without starting it, and handing it back.**
799
823
 
800
824
  ```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,6 +514,7 @@ 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 |
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 |
513
518
  | `start` | moves the item | also claims a linked ticket, and posts a comment if `comments: true` |
514
519
  | `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` |
515
520
  | 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 |
@@ -554,7 +559,11 @@ triaged. When it is set, `tcw work inbox list` prints two sections, `raw intake:
554
559
  `inbox accept` then take a ticket key as well as an inbox entry: an inbox entry with
555
560
  the same name wins, and `--ticket` reads the name as a ticket anyway. Accepting a
556
561
  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.
562
+ leaves out tickets that already have a work item; TCW does not filter them. A
563
+ triage status does not offer the transition that starts work, so to take tickets
564
+ straight from it, name the status and its way out under `pre-backlog` (for example
565
+ `pre-backlog: { Triage: Accept }`, with `statuses.backlog` set): every claim then
566
+ moves the ticket out of Triage to the backlog first, and says so.
558
567
 
559
568
  The credentials entries hold the **names** of environment variables, never the
560
569
  e-mail address or token themselves.
@@ -579,6 +588,21 @@ tcw work tracker link <slug> ENG-517 # TCW: records the link.
579
588
  tcw work start <slug> # TCW: backlog → active. Jira: if unassigned or yours, ENG-517 → In Progress, assigned to you
580
589
  ```
581
590
 
591
+ **Example 2a: making a ticket for an item that has none.**
592
+
593
+ ```sh
594
+ tcw work new "Speed up the checkout page" # TCW: creates a backlog item. Jira: unchanged
595
+ tcw work tracker create <slug> --dry-run # TCW: says what it would do. Jira: unchanged
596
+ tcw work tracker create <slug> # TCW: binds the new ticket. Jira: creates it, then moves it to statuses.backlog
597
+ tcw work tracker create --all # TCW: the same for every open item. Jira: one new ticket per item without one
598
+ ```
599
+
600
+ The move out of whatever status Jira starts issues in is part of creating the
601
+ ticket, not a courtesy: in a project with a triage column, that starting status
602
+ is usually the one `inbox-query` selects, so a ticket left there would come back
603
+ through `tcw work inbox` as new inbound work. Run `--all` with `--dry-run`
604
+ first — nothing here can delete a ticket.
605
+
582
606
  **Example 3: taking a ticket without starting it, and handing it back.**
583
607
 
584
608
  ```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.5.1"
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.5.1"
@@ -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>