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.
- {tcw_cli-2.4.0/tcw_cli.egg-info → tcw_cli-2.5.1}/PKG-INFO +30 -6
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/README.md +29 -5
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/pyproject.toml +1 -1
- tcw_cli-2.5.1/tcw/__init__.py +1 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/__init__.py +50 -0
- 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
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/dist/client/index.html +1 -1
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/base.py +418 -6
- tcw_cli-2.5.1/tcw/store/config_edit.py +641 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/fs.py +612 -166
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/taxonomy/cli.py +2 -2
- tcw_cli-2.5.1/tcw/tracker/create.py +207 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/intake.py +329 -7
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/jira.py +20 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/sync.py +48 -18
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/validate.py +23 -1
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/cli.py +738 -43
- tcw_cli-2.5.1/tcw/work/procedures/decompose.md +23 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/projection.py +21 -1
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/recursion.py +195 -35
- {tcw_cli-2.4.0 → tcw_cli-2.5.1/tcw_cli.egg-info}/PKG-INFO +30 -6
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/SOURCES.txt +11 -1
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities.py +39 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities_federation.py +56 -14
- tcw_cli-2.5.1/tests/test_capability_gate_children.py +623 -0
- tcw_cli-2.5.1/tests/test_check_versions.py +308 -0
- tcw_cli-2.5.1/tests/test_child_status.py +649 -0
- tcw_cli-2.5.1/tests/test_config_edit.py +466 -0
- tcw_cli-2.5.1/tests/test_config_edit_writers.py +384 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_environment_hardness.py +10 -5
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_epic_completable.py +36 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_external_work_store.py +56 -0
- tcw_cli-2.5.1/tests/test_legacy_store_config.py +346 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_multiproject.py +4 -1
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_projection.py +18 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_repo_lifecycle.py +9 -4
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_retention.py +125 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve.py +4 -2
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_write.py +122 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_session_bootstrap.py +179 -1
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_editor.py +23 -2
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_provisioning.py +129 -1
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_taxonomy.py +238 -2
- tcw_cli-2.5.1/tests/test_tracker_cli.py +1657 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_client.py +7 -1
- tcw_cli-2.5.1/tests/test_tracker_create.py +250 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_help.py +2 -2
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_inheritance.py +51 -0
- tcw_cli-2.5.1/tests/test_tracker_pre_backlog.py +769 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_surface.py +42 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_sync.py +80 -1
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_sync_config.py +4 -1
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_validate.py +133 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_validate.py +14 -3
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_work.py +160 -47
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_worktree_completion.py +66 -0
- tcw_cli-2.4.0/tcw/__init__.py +0 -1
- tcw_cli-2.4.0/tcw/work/procedures/decompose.md +0 -18
- tcw_cli-2.4.0/tests/test_tracker_cli.py +0 -468
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/LICENSE +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/setup.cfg +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/capabilities/__init__.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/capabilities/cli.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/cli.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/harness.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/refs.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/dist/client/assets/index-CXNiOIeg.css +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/dist/client/theme-init.js +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/dist/server.cjs +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/serve/runtime.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/stdin.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/__init__.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/checkouts.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/store/project.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/taxonomy/__init__.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/__init__.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/claim.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/ownership.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/tracker/progress.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/__init__.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/generate.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/hooks.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/audit-backlog.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/consolidate-plans.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/create-work.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/delegation.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/documentation-sync.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/post-mortem.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/search.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/triage-issues.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/procedures/unattended-work.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/implement.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/inbox.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/plan.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/postmortem.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/request.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/spec.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/prompts/verify.md +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/resolve.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw/work/templates.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/dependency_links.txt +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/entry_points.txt +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/requires.txt +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tcw_cli.egg-info/top_level.txt +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_body_prompt.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities_reset.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities_rm.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capabilities_sidecar.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_capability_ref_wording.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_cli_help_coverage.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_committed_paths.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_configuration_text_home.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_cut_version.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_documentation_config.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_documentation_prompt.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_documentation_sync_wiring.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_documented_cli_surface.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_dynamic_skill_marker.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_edit_type.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_coverage.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_files_changed.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_fixture.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_grading.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_eval_runner.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_falsification_rule.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_generate_hook.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_harness.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_inbox_title.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_baseline.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_hooks.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_inert.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_policy.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_lifecycle_validation.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_non_git_writes.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_plugin_manifests.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_procedure_config.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_procedure_verb.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_project_overrides.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_project_registry.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_prompt_fallback.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_qualified_ref.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_recursion.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_refs.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_remote_session_setup.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_resolve.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_resolve_procedure.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_scaffold.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_descendants.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_projection.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_resolve.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_serve_runtime.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_shipped_procedures.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_shipped_prompts.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_show_json.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_skill_flow.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_skill_lifecycle_parity.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_skill_path_pointers.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_smoke.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_stage_validate.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_stage_verb.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_status_parity.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_stdin.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_stdin_cli.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_bounds.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_nodes.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_store_publication.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_subprocess_stdin.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tombstone.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_absent.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_binding.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_claim.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_claimability.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_comment.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_config.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_hold.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_import.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_link.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_ownership.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_replay.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_tracker_strict.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_unattended_work_skill.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_unpushed_version_script.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_validate_target.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_work_autocommit.py +0 -0
- {tcw_cli-2.4.0 → tcw_cli-2.5.1}/tests/test_work_review.py +0 -0
- {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.
|
|
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
|
|
613
|
-
|
|
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
|
|
397
|
-
|
|
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.
|
|
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-
|
|
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>
|