tcw-cli 2.0.1__tar.gz → 2.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. {tcw_cli-2.0.1/tcw_cli.egg-info → tcw_cli-2.1.1}/PKG-INFO +160 -18
  2. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/README.md +159 -17
  3. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/pyproject.toml +8 -1
  4. tcw_cli-2.1.1/tcw/__init__.py +1 -0
  5. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/cli.py +9 -0
  6. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/store/base.py +170 -1
  7. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/store/checkouts.py +39 -0
  8. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/store/fs.py +272 -40
  9. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/store/project.py +40 -1
  10. tcw_cli-2.1.1/tcw/tracker/__init__.py +12 -0
  11. tcw_cli-2.1.1/tcw/tracker/claim.py +180 -0
  12. tcw_cli-2.1.1/tcw/tracker/jira.py +266 -0
  13. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/validate.py +101 -9
  14. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/cli.py +144 -14
  15. {tcw_cli-2.0.1 → tcw_cli-2.1.1/tcw_cli.egg-info}/PKG-INFO +160 -18
  16. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw_cli.egg-info/SOURCES.txt +15 -0
  17. tcw_cli-2.1.1/tests/test_committed_paths.py +38 -0
  18. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_documented_cli_surface.py +18 -3
  19. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_environment_hardness.py +130 -0
  20. tcw_cli-2.1.1/tests/test_eval_coverage.py +48 -0
  21. tcw_cli-2.1.1/tests/test_eval_fixture.py +235 -0
  22. tcw_cli-2.1.1/tests/test_eval_grading.py +177 -0
  23. tcw_cli-2.1.1/tests/test_eval_runner.py +228 -0
  24. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_external_work_store.py +138 -0
  25. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_non_git_writes.py +73 -3
  26. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_plugin_manifests.py +1 -1
  27. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_project_registry.py +103 -0
  28. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_remote_session_setup.py +65 -11
  29. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_scaffold.py +6 -0
  30. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_session_bootstrap.py +1 -1
  31. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_skill_lifecycle_parity.py +5 -1
  32. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_store_editor.py +30 -22
  33. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_store_provisioning.py +226 -7
  34. tcw_cli-2.1.1/tests/test_tracker_absent.py +163 -0
  35. tcw_cli-2.1.1/tests/test_tracker_claimability.py +192 -0
  36. tcw_cli-2.1.1/tests/test_tracker_cli.py +258 -0
  37. tcw_cli-2.1.1/tests/test_tracker_client.py +377 -0
  38. tcw_cli-2.1.1/tests/test_tracker_config.py +191 -0
  39. tcw_cli-2.1.1/tests/test_tracker_replay.py +147 -0
  40. tcw_cli-2.1.1/tests/test_tracker_validate.py +178 -0
  41. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_validate.py +200 -0
  42. tcw_cli-2.1.1/tests/test_work_tags.py +398 -0
  43. tcw_cli-2.0.1/tcw/__init__.py +0 -1
  44. tcw_cli-2.0.1/tests/test_work_tags.py +0 -182
  45. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/LICENSE +0 -0
  46. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/setup.cfg +0 -0
  47. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/capabilities/__init__.py +0 -0
  48. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/capabilities/cli.py +0 -0
  49. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/refs.py +0 -0
  50. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/serve/__init__.py +0 -0
  51. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/serve/dist/client/assets/index-CXNiOIeg.css +0 -0
  52. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/serve/dist/client/assets/index-Dz5B7M8G.js +0 -0
  53. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/serve/dist/client/index.html +0 -0
  54. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/serve/dist/client/theme-init.js +0 -0
  55. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/serve/dist/server.cjs +0 -0
  56. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/serve/runtime.py +0 -0
  57. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/stdin.py +0 -0
  58. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/store/__init__.py +0 -0
  59. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/taxonomy/__init__.py +0 -0
  60. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/taxonomy/cli.py +0 -0
  61. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/__init__.py +0 -0
  62. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/generate.py +0 -0
  63. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/hooks.py +0 -0
  64. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/projection.py +0 -0
  65. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/prompts/implement.md +0 -0
  66. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/prompts/inbox.md +0 -0
  67. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/prompts/plan.md +0 -0
  68. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/prompts/postmortem.md +0 -0
  69. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/prompts/request.md +0 -0
  70. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/prompts/spec.md +0 -0
  71. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/prompts/verify.md +0 -0
  72. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/recursion.py +0 -0
  73. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/resolve.py +0 -0
  74. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw/work/templates.py +0 -0
  75. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw_cli.egg-info/dependency_links.txt +0 -0
  76. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw_cli.egg-info/entry_points.txt +0 -0
  77. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw_cli.egg-info/requires.txt +0 -0
  78. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tcw_cli.egg-info/top_level.txt +0 -0
  79. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_body_prompt.py +0 -0
  80. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_capabilities.py +0 -0
  81. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_capabilities_federation.py +0 -0
  82. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_capabilities_reset.py +0 -0
  83. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_capabilities_sidecar.py +0 -0
  84. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_capability_ref_wording.py +0 -0
  85. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_cut_version.py +0 -0
  86. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_documentation_config.py +0 -0
  87. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_documentation_prompt.py +0 -0
  88. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_documentation_sync_wiring.py +0 -0
  89. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_epic_completable.py +0 -0
  90. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_falsification_rule.py +0 -0
  91. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_generate_hook.py +0 -0
  92. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_inbox_title.py +0 -0
  93. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_lifecycle_baseline.py +0 -0
  94. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_lifecycle_hooks.py +0 -0
  95. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_lifecycle_inert.py +0 -0
  96. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_lifecycle_policy.py +0 -0
  97. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_lifecycle_validation.py +0 -0
  98. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_multiproject.py +0 -0
  99. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_project_overrides.py +0 -0
  100. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_projection.py +0 -0
  101. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_prompt_fallback.py +0 -0
  102. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_qualified_ref.py +0 -0
  103. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_recursion.py +0 -0
  104. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_refs.py +0 -0
  105. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_repo_lifecycle.py +0 -0
  106. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_resolve.py +0 -0
  107. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_retention.py +0 -0
  108. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_serve.py +0 -0
  109. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_serve_descendants.py +0 -0
  110. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_serve_projection.py +0 -0
  111. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_serve_resolve.py +0 -0
  112. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_serve_runtime.py +0 -0
  113. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_serve_write.py +0 -0
  114. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_shipped_prompts.py +0 -0
  115. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_show_json.py +0 -0
  116. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_skill_flow.py +0 -0
  117. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_smoke.py +0 -0
  118. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_stage_verb.py +0 -0
  119. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_status_parity.py +0 -0
  120. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_stdin.py +0 -0
  121. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_stdin_cli.py +0 -0
  122. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_store_bounds.py +0 -0
  123. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_store_nodes.py +0 -0
  124. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_store_publication.py +0 -0
  125. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_subprocess_stdin.py +0 -0
  126. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_taxonomy.py +0 -0
  127. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_tombstone.py +0 -0
  128. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_unpushed_version_script.py +0 -0
  129. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_validate_target.py +0 -0
  130. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_work.py +0 -0
  131. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_work_autocommit.py +0 -0
  132. {tcw_cli-2.0.1 → tcw_cli-2.1.1}/tests/test_work_review.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tcw-cli
3
- Version: 2.0.1
3
+ Version: 2.1.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
@@ -407,8 +407,9 @@ Or, in the Claude **web app** or **desktop app**, open the plugin directory, add
407
407
  by installing `tcw-cli` from PyPI with `pipx`, so one installed mid-session
408
408
  cannot run until the next one begins. That first session needs network access. It
409
409
  installs over an existing `pipx install tcw-cli` rather than beside it, and
410
- leaves a development checkout (`pip install -e .`) alone. Run `/tcw-doctor` any
411
- time `tcw` goes missing or looks wrong.
410
+ leaves a development checkout (`pip install -e .`) alone. If `tcw` goes missing
411
+ anyway, `pipx install tcw-cli` is the whole fix — the **`tcw-plugin`** skill
412
+ carries the cases where it is not.
412
413
 
413
414
  In **Codex** (skills only, no slash commands):
414
415
 
@@ -439,6 +440,89 @@ additionally requires **Node.js ≥ 22.12**; every other command is Python-only.
439
440
  Released wheels carry the prebuilt web assets, so there is no frontend build step
440
441
  and no network needed after install.
441
442
 
443
+ ### In a cloud environment
444
+
445
+ A cloud agent session — Claude Code on the web, a Codex container, a CI job —
446
+ starts from a clean image and is thrown away when it ends, so a `tcw` you install
447
+ by hand is gone by the next one. Install it **from your repository**, with a
448
+ session-start hook, and every session gets it without anyone remembering to.
449
+
450
+ Add a script to your repository:
451
+
452
+ ```sh
453
+ #!/usr/bin/env bash
454
+ # Make `tcw` available in a disposable agent container.
455
+ # Exit 0 on every path — a session must start even when this cannot finish.
456
+ set -u
457
+
458
+ command -v tcw >/dev/null 2>&1 && exit 0
459
+
460
+ # pipx where it exists; otherwise install into the container's own interpreter.
461
+ # That is the right answer *here* and nowhere else: the container is disposable
462
+ # and single-purpose, so there is no user environment to damage.
463
+ if command -v pipx >/dev/null 2>&1; then
464
+ pipx install tcw-cli >/dev/null 2>&1 || echo "tcw: pipx install tcw-cli failed"
465
+ elif ! python3 -m pip install tcw-cli >/dev/null 2>&1 &&
466
+ ! python3 -m pip install tcw-cli --break-system-packages >/dev/null 2>&1; then
467
+ echo "tcw: pip install tcw-cli failed — the tcw CLI is not available."
468
+ fi
469
+
470
+ # An install that landed outside PATH is installed and unusable. $CLAUDE_ENV_FILE
471
+ # is the harness's own channel for repairing that.
472
+ if ! command -v tcw >/dev/null 2>&1 && [ -n "${CLAUDE_ENV_FILE:-}" ]; then
473
+ userbin="$(python3 -m site --user-base 2>/dev/null)/bin"
474
+ [ -x "$userbin/tcw" ] && echo "export PATH=\"$userbin:\$PATH\"" >>"$CLAUDE_ENV_FILE"
475
+ fi
476
+
477
+ exit 0
478
+ ```
479
+
480
+ Then wire it to `SessionStart` in your repository's `.claude/settings.json`:
481
+
482
+ ```json
483
+ {
484
+ "hooks": {
485
+ "SessionStart": [
486
+ {
487
+ "hooks": [
488
+ {
489
+ "type": "command",
490
+ "command": "\"${CLAUDE_PROJECT_DIR}\"/scripts/tcw_session_setup.sh"
491
+ }
492
+ ]
493
+ }
494
+ ]
495
+ }
496
+ }
497
+ ```
498
+
499
+ Three things make the difference between a hook that helps and one that wastes a
500
+ session:
501
+
502
+ - **Exit 0 on every path, and print only on failure.** A hook that fails the
503
+ session start over a missing CLI has cost more than the CLI was worth.
504
+ - **Print to stdout.** Claude Code adds a `SessionStart` hook's stdout to the
505
+ agent's context, where it will be read; stderr becomes a transcript notice
506
+ nobody sees.
507
+ - **Check for `tcw` first.** The hook runs every session, including the ones
508
+ where a previous install is still there.
509
+
510
+ **If your work store lives in another repository**, the container holds only the
511
+ repository it cloned, so the board is declared but absent. Run `tcw provision`
512
+ after the install — it obtains what the checkout does not have, and does nothing
513
+ on a machine that already holds it. See
514
+ [Working across repositories](docs/guide/multi-repo.md).
515
+
516
+ Installing the **plugin** in such a session is a separate step from installing the
517
+ CLI, and only needed where the harness does not carry your plugins in: append
518
+ `claude plugin marketplace add brocef/TCW` and `claude plugin install tcw@tcw` to
519
+ the same script, guarded on `command -v claude`.
520
+
521
+ This repository's own [`scripts/remote_session_setup.sh`](scripts/remote_session_setup.sh)
522
+ is the same pattern written for a _contributor_ rather than a user — it installs
523
+ the checkout with `pip install -e` instead of the release from PyPI, and installs
524
+ the plugin from the checkout too. Read it as the worked example.
525
+
442
526
  ## Quickstart
443
527
 
444
528
  ```sh
@@ -464,6 +548,60 @@ studies your code, proposes a first draft, refines it with you, and writes it.
464
548
 
465
549
  ---
466
550
 
551
+ ## Reading your Jira tickets
552
+
553
+ A project can name the Jira Cloud site it uses, and then read from it. Both
554
+ commands are read-only: nothing here changes a ticket, and nothing yet turns a
555
+ ticket into a work item.
556
+
557
+ ```sh
558
+ tcw work tracker list # tickets the configured query selects
559
+ tcw work tracker show ENG-482 # one ticket, and whether it is yours to take
560
+ ```
561
+
562
+ Configuration goes in the node's `tcw-config.yaml`:
563
+
564
+ ```yaml
565
+ work:
566
+ tracker:
567
+ provider: jira-cloud
568
+ base-url: https://yourcompany.atlassian.net
569
+ candidate-query: assignee = currentUser() AND status = "To Do"
570
+ credentials:
571
+ email-env: TCW_JIRA_EMAIL
572
+ token-env: TCW_JIRA_API_TOKEN
573
+ transitions:
574
+ claim: Start Progress
575
+ timeout-seconds: 15 # optional
576
+ ```
577
+
578
+ **Credentials are named, never stored.** The file holds the names of two
579
+ environment variables; TCW reads them at the moment it makes a request, so a token
580
+ cannot be committed by accident.
581
+
582
+ `tracker show` reports two different things, and the distinction matters:
583
+
584
+ - **claimable** — whether this ticket currently offers the transition you
585
+ configured as the claim.
586
+ - **exclusive** — whether your Jira workflow would actually refuse a second person
587
+ trying to take the same ticket.
588
+
589
+ Many Jira workflows allow a status change from any status, including the one it
590
+ leads to. On a workflow like that, two people who both take one ticket both
591
+ succeed and neither is told. So a ticket that has been started reports
592
+ `not exclusive` when that is the truth, and a ticket that has not been started
593
+ reports `not determined`, because it cannot show you what happens to the second
594
+ person. Making a workflow exclusive is a Jira administration change, not something
595
+ TCW can do for you.
596
+
597
+ Two guarantees worth stating plainly. A project with no `tracker` block behaves
598
+ exactly as before, with no new required setting and no network access. And
599
+ `tcw validate` never contacts the tracker, which matters because projects commonly
600
+ run it when closing a work item — finishing your work must not depend on Jira being
601
+ reachable.
602
+
603
+ ---
604
+
467
605
  ## Documentation
468
606
 
469
607
  | Document | Covers |
@@ -483,21 +621,22 @@ Every command group also has `--help`, and a `check` that validates its tree.
483
621
 
484
622
  ## Skills — the judgment layer
485
623
 
486
- The CLI is the _mechanism_. Fourteen skills in [`skills/`](skills/) supply the
487
- _judgment_ that drives it — the parts a deterministic tool cannot decide. Eight
624
+ The CLI is the _mechanism_. Fifteen skills in [`skills/`](skills/) supply the
625
+ _judgment_ that drives it — the parts a deterministic tool cannot decide. Nine
488
626
  carry a distinct procedure; the other six all compose one lifecycle stage and
489
627
  are listed together at the end.
490
628
 
491
- | Skill | What it does |
492
- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
493
- | [`tcw-work`](skills/tcw-work/SKILL.md) | Plans a request through spec and plan, drives implementation and verification, triages the inbox, runs the lifecycle, decomposes epics |
494
- | [`tcw-capabilities`](skills/tcw-capabilities/SKILL.md) | The capability-delta planning check, contradiction detection, and the ledger flip at completion |
495
- | [`tcw-taxonomy`](skills/tcw-taxonomy/SKILL.md) | Declaring vocabulary and features, linking them, and federating shared vocabulary |
496
- | [`tcw-plugin`](skills/tcw-plugin/SKILL.md) | Installs and repairs the CLI; the source of the `/tcw-doctor` procedure |
497
- | [`tcw-report`](skills/tcw-report/SKILL.md) | Reporting a `tcw` bug or suggestion upstream to [this project's issues](https://github.com/brocef/TCW/issues) |
498
- | [`tcw-triage-issues`](skills/tcw-triage-issues/SKILL.md) | Sweeps **your** project's GitHub issues and turns the ones worth doing into work items |
499
- | [`documentation-sync`](skills/documentation-sync/SKILL.md) | Keeps README, changelogs, release notes, and driving skills moving with the code that changes them |
500
- | [`tcw-post-mortem`](skills/tcw-post-mortem/SKILL.md) | Finds which lifecycle stage could first have caught a problem, once one has surfaced |
629
+ | Skill | What it does |
630
+ | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
631
+ | [`tcw-work`](skills/tcw-work/SKILL.md) | Plans a request through spec and plan, drives implementation and verification, triages the inbox, runs the lifecycle, decomposes epics, searches the board |
632
+ | [`tcw-capabilities`](skills/tcw-capabilities/SKILL.md) | The capability-delta planning check, contradiction detection, and the ledger flip at completion |
633
+ | [`tcw-taxonomy`](skills/tcw-taxonomy/SKILL.md) | Declaring vocabulary and features, linking them, and federating shared vocabulary |
634
+ | [`tcw-plugin`](skills/tcw-plugin/SKILL.md) | Installs the CLI from PyPI, and maps the other skills |
635
+ | [`tcw-report`](skills/tcw-report/SKILL.md) | Reporting a `tcw` bug or suggestion upstream to [this project's issues](https://github.com/brocef/TCW/issues) |
636
+ | [`tcw-triage-issues`](skills/tcw-triage-issues/SKILL.md) | Sweeps **your** project's GitHub issues and turns the ones worth doing into work items |
637
+ | [`documentation-sync`](skills/documentation-sync/SKILL.md) | Keeps README, changelogs, release notes, and driving skills moving with the code that changes them |
638
+ | [`tcw-post-mortem`](skills/tcw-post-mortem/SKILL.md) | Finds which lifecycle stage could first have caught a problem, once one has surfaced |
639
+ | [`autonomous-work`](skills/autonomous-work/SKILL.md) | Drives work items to completion unattended, consulting two read-only advisors wherever the lifecycle would ask you |
501
640
 
502
641
  They name `tcw` commands and never reimplement tool logic: mechanism stays in the
503
642
  binary, judgment stays in the skills.
@@ -515,13 +654,16 @@ The other five bake their stage in and ask only for the work item, which is
515
654
  optional: `tcw-work-stage-request`, `tcw-work-stage-spec`,
516
655
  `tcw-work-stage-plan`, `tcw-work-stage-implement`, `tcw-work-stage-verify`.
517
656
 
657
+ ### Review agents and slash commands
658
+
518
659
  Three read-only review agents ship alongside them — `tcw-verifier`,
519
660
  `tcw-backlog-auditor`, and `tcw-post-mortem`, which accelerates the skill of the
520
661
  same name — plus slash commands for each skill's main procedure
521
662
  (`/tcw-plan-work`, `/tcw-drive-work-to-completion`, `/tcw-verify-work`,
522
- `/tcw-process-inbox`, `/tcw-triage-issues`, `/tcw-audit-work-backlog`,
523
- `/tcw-taxonomy-init`, `/tcw-capabilities-init`, `/tcw-docs-sync-setup`,
524
- `/tcw-cut-version`, `/tcw-post-mortem`, `/tcw-doctor`).
663
+ `/tcw-process-inbox`, `/tcw-work-search`, `/tcw-triage-issues`,
664
+ `/tcw-audit-work-backlog`, `/tcw-consolidate-plans`, `/tcw-taxonomy-init`,
665
+ `/tcw-capabilities-init`, `/tcw-docs-sync-setup`, `/tcw-cut-version`,
666
+ `/tcw-post-mortem`).
525
667
 
526
668
  ---
527
669
 
@@ -191,8 +191,9 @@ Or, in the Claude **web app** or **desktop app**, open the plugin directory, add
191
191
  by installing `tcw-cli` from PyPI with `pipx`, so one installed mid-session
192
192
  cannot run until the next one begins. That first session needs network access. It
193
193
  installs over an existing `pipx install tcw-cli` rather than beside it, and
194
- leaves a development checkout (`pip install -e .`) alone. Run `/tcw-doctor` any
195
- time `tcw` goes missing or looks wrong.
194
+ leaves a development checkout (`pip install -e .`) alone. If `tcw` goes missing
195
+ anyway, `pipx install tcw-cli` is the whole fix — the **`tcw-plugin`** skill
196
+ carries the cases where it is not.
196
197
 
197
198
  In **Codex** (skills only, no slash commands):
198
199
 
@@ -223,6 +224,89 @@ additionally requires **Node.js ≥ 22.12**; every other command is Python-only.
223
224
  Released wheels carry the prebuilt web assets, so there is no frontend build step
224
225
  and no network needed after install.
225
226
 
227
+ ### In a cloud environment
228
+
229
+ A cloud agent session — Claude Code on the web, a Codex container, a CI job —
230
+ starts from a clean image and is thrown away when it ends, so a `tcw` you install
231
+ by hand is gone by the next one. Install it **from your repository**, with a
232
+ session-start hook, and every session gets it without anyone remembering to.
233
+
234
+ Add a script to your repository:
235
+
236
+ ```sh
237
+ #!/usr/bin/env bash
238
+ # Make `tcw` available in a disposable agent container.
239
+ # Exit 0 on every path — a session must start even when this cannot finish.
240
+ set -u
241
+
242
+ command -v tcw >/dev/null 2>&1 && exit 0
243
+
244
+ # pipx where it exists; otherwise install into the container's own interpreter.
245
+ # That is the right answer *here* and nowhere else: the container is disposable
246
+ # and single-purpose, so there is no user environment to damage.
247
+ if command -v pipx >/dev/null 2>&1; then
248
+ pipx install tcw-cli >/dev/null 2>&1 || echo "tcw: pipx install tcw-cli failed"
249
+ elif ! python3 -m pip install tcw-cli >/dev/null 2>&1 &&
250
+ ! python3 -m pip install tcw-cli --break-system-packages >/dev/null 2>&1; then
251
+ echo "tcw: pip install tcw-cli failed — the tcw CLI is not available."
252
+ fi
253
+
254
+ # An install that landed outside PATH is installed and unusable. $CLAUDE_ENV_FILE
255
+ # is the harness's own channel for repairing that.
256
+ if ! command -v tcw >/dev/null 2>&1 && [ -n "${CLAUDE_ENV_FILE:-}" ]; then
257
+ userbin="$(python3 -m site --user-base 2>/dev/null)/bin"
258
+ [ -x "$userbin/tcw" ] && echo "export PATH=\"$userbin:\$PATH\"" >>"$CLAUDE_ENV_FILE"
259
+ fi
260
+
261
+ exit 0
262
+ ```
263
+
264
+ Then wire it to `SessionStart` in your repository's `.claude/settings.json`:
265
+
266
+ ```json
267
+ {
268
+ "hooks": {
269
+ "SessionStart": [
270
+ {
271
+ "hooks": [
272
+ {
273
+ "type": "command",
274
+ "command": "\"${CLAUDE_PROJECT_DIR}\"/scripts/tcw_session_setup.sh"
275
+ }
276
+ ]
277
+ }
278
+ ]
279
+ }
280
+ }
281
+ ```
282
+
283
+ Three things make the difference between a hook that helps and one that wastes a
284
+ session:
285
+
286
+ - **Exit 0 on every path, and print only on failure.** A hook that fails the
287
+ session start over a missing CLI has cost more than the CLI was worth.
288
+ - **Print to stdout.** Claude Code adds a `SessionStart` hook's stdout to the
289
+ agent's context, where it will be read; stderr becomes a transcript notice
290
+ nobody sees.
291
+ - **Check for `tcw` first.** The hook runs every session, including the ones
292
+ where a previous install is still there.
293
+
294
+ **If your work store lives in another repository**, the container holds only the
295
+ repository it cloned, so the board is declared but absent. Run `tcw provision`
296
+ after the install — it obtains what the checkout does not have, and does nothing
297
+ on a machine that already holds it. See
298
+ [Working across repositories](docs/guide/multi-repo.md).
299
+
300
+ Installing the **plugin** in such a session is a separate step from installing the
301
+ CLI, and only needed where the harness does not carry your plugins in: append
302
+ `claude plugin marketplace add brocef/TCW` and `claude plugin install tcw@tcw` to
303
+ the same script, guarded on `command -v claude`.
304
+
305
+ This repository's own [`scripts/remote_session_setup.sh`](scripts/remote_session_setup.sh)
306
+ is the same pattern written for a _contributor_ rather than a user — it installs
307
+ the checkout with `pip install -e` instead of the release from PyPI, and installs
308
+ the plugin from the checkout too. Read it as the worked example.
309
+
226
310
  ## Quickstart
227
311
 
228
312
  ```sh
@@ -248,6 +332,60 @@ studies your code, proposes a first draft, refines it with you, and writes it.
248
332
 
249
333
  ---
250
334
 
335
+ ## Reading your Jira tickets
336
+
337
+ A project can name the Jira Cloud site it uses, and then read from it. Both
338
+ commands are read-only: nothing here changes a ticket, and nothing yet turns a
339
+ ticket into a work item.
340
+
341
+ ```sh
342
+ tcw work tracker list # tickets the configured query selects
343
+ tcw work tracker show ENG-482 # one ticket, and whether it is yours to take
344
+ ```
345
+
346
+ Configuration goes in the node's `tcw-config.yaml`:
347
+
348
+ ```yaml
349
+ work:
350
+ tracker:
351
+ provider: jira-cloud
352
+ base-url: https://yourcompany.atlassian.net
353
+ candidate-query: assignee = currentUser() AND status = "To Do"
354
+ credentials:
355
+ email-env: TCW_JIRA_EMAIL
356
+ token-env: TCW_JIRA_API_TOKEN
357
+ transitions:
358
+ claim: Start Progress
359
+ timeout-seconds: 15 # optional
360
+ ```
361
+
362
+ **Credentials are named, never stored.** The file holds the names of two
363
+ environment variables; TCW reads them at the moment it makes a request, so a token
364
+ cannot be committed by accident.
365
+
366
+ `tracker show` reports two different things, and the distinction matters:
367
+
368
+ - **claimable** — whether this ticket currently offers the transition you
369
+ configured as the claim.
370
+ - **exclusive** — whether your Jira workflow would actually refuse a second person
371
+ trying to take the same ticket.
372
+
373
+ Many Jira workflows allow a status change from any status, including the one it
374
+ leads to. On a workflow like that, two people who both take one ticket both
375
+ succeed and neither is told. So a ticket that has been started reports
376
+ `not exclusive` when that is the truth, and a ticket that has not been started
377
+ reports `not determined`, because it cannot show you what happens to the second
378
+ person. Making a workflow exclusive is a Jira administration change, not something
379
+ TCW can do for you.
380
+
381
+ Two guarantees worth stating plainly. A project with no `tracker` block behaves
382
+ exactly as before, with no new required setting and no network access. And
383
+ `tcw validate` never contacts the tracker, which matters because projects commonly
384
+ run it when closing a work item — finishing your work must not depend on Jira being
385
+ reachable.
386
+
387
+ ---
388
+
251
389
  ## Documentation
252
390
 
253
391
  | Document | Covers |
@@ -267,21 +405,22 @@ Every command group also has `--help`, and a `check` that validates its tree.
267
405
 
268
406
  ## Skills — the judgment layer
269
407
 
270
- The CLI is the _mechanism_. Fourteen skills in [`skills/`](skills/) supply the
271
- _judgment_ that drives it — the parts a deterministic tool cannot decide. Eight
408
+ The CLI is the _mechanism_. Fifteen skills in [`skills/`](skills/) supply the
409
+ _judgment_ that drives it — the parts a deterministic tool cannot decide. Nine
272
410
  carry a distinct procedure; the other six all compose one lifecycle stage and
273
411
  are listed together at the end.
274
412
 
275
- | Skill | What it does |
276
- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
277
- | [`tcw-work`](skills/tcw-work/SKILL.md) | Plans a request through spec and plan, drives implementation and verification, triages the inbox, runs the lifecycle, decomposes epics |
278
- | [`tcw-capabilities`](skills/tcw-capabilities/SKILL.md) | The capability-delta planning check, contradiction detection, and the ledger flip at completion |
279
- | [`tcw-taxonomy`](skills/tcw-taxonomy/SKILL.md) | Declaring vocabulary and features, linking them, and federating shared vocabulary |
280
- | [`tcw-plugin`](skills/tcw-plugin/SKILL.md) | Installs and repairs the CLI; the source of the `/tcw-doctor` procedure |
281
- | [`tcw-report`](skills/tcw-report/SKILL.md) | Reporting a `tcw` bug or suggestion upstream to [this project's issues](https://github.com/brocef/TCW/issues) |
282
- | [`tcw-triage-issues`](skills/tcw-triage-issues/SKILL.md) | Sweeps **your** project's GitHub issues and turns the ones worth doing into work items |
283
- | [`documentation-sync`](skills/documentation-sync/SKILL.md) | Keeps README, changelogs, release notes, and driving skills moving with the code that changes them |
284
- | [`tcw-post-mortem`](skills/tcw-post-mortem/SKILL.md) | Finds which lifecycle stage could first have caught a problem, once one has surfaced |
413
+ | Skill | What it does |
414
+ | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
415
+ | [`tcw-work`](skills/tcw-work/SKILL.md) | Plans a request through spec and plan, drives implementation and verification, triages the inbox, runs the lifecycle, decomposes epics, searches the board |
416
+ | [`tcw-capabilities`](skills/tcw-capabilities/SKILL.md) | The capability-delta planning check, contradiction detection, and the ledger flip at completion |
417
+ | [`tcw-taxonomy`](skills/tcw-taxonomy/SKILL.md) | Declaring vocabulary and features, linking them, and federating shared vocabulary |
418
+ | [`tcw-plugin`](skills/tcw-plugin/SKILL.md) | Installs the CLI from PyPI, and maps the other skills |
419
+ | [`tcw-report`](skills/tcw-report/SKILL.md) | Reporting a `tcw` bug or suggestion upstream to [this project's issues](https://github.com/brocef/TCW/issues) |
420
+ | [`tcw-triage-issues`](skills/tcw-triage-issues/SKILL.md) | Sweeps **your** project's GitHub issues and turns the ones worth doing into work items |
421
+ | [`documentation-sync`](skills/documentation-sync/SKILL.md) | Keeps README, changelogs, release notes, and driving skills moving with the code that changes them |
422
+ | [`tcw-post-mortem`](skills/tcw-post-mortem/SKILL.md) | Finds which lifecycle stage could first have caught a problem, once one has surfaced |
423
+ | [`autonomous-work`](skills/autonomous-work/SKILL.md) | Drives work items to completion unattended, consulting two read-only advisors wherever the lifecycle would ask you |
285
424
 
286
425
  They name `tcw` commands and never reimplement tool logic: mechanism stays in the
287
426
  binary, judgment stays in the skills.
@@ -299,13 +438,16 @@ The other five bake their stage in and ask only for the work item, which is
299
438
  optional: `tcw-work-stage-request`, `tcw-work-stage-spec`,
300
439
  `tcw-work-stage-plan`, `tcw-work-stage-implement`, `tcw-work-stage-verify`.
301
440
 
441
+ ### Review agents and slash commands
442
+
302
443
  Three read-only review agents ship alongside them — `tcw-verifier`,
303
444
  `tcw-backlog-auditor`, and `tcw-post-mortem`, which accelerates the skill of the
304
445
  same name — plus slash commands for each skill's main procedure
305
446
  (`/tcw-plan-work`, `/tcw-drive-work-to-completion`, `/tcw-verify-work`,
306
- `/tcw-process-inbox`, `/tcw-triage-issues`, `/tcw-audit-work-backlog`,
307
- `/tcw-taxonomy-init`, `/tcw-capabilities-init`, `/tcw-docs-sync-setup`,
308
- `/tcw-cut-version`, `/tcw-post-mortem`, `/tcw-doctor`).
447
+ `/tcw-process-inbox`, `/tcw-work-search`, `/tcw-triage-issues`,
448
+ `/tcw-audit-work-backlog`, `/tcw-consolidate-plans`, `/tcw-taxonomy-init`,
449
+ `/tcw-capabilities-init`, `/tcw-docs-sync-setup`, `/tcw-cut-version`,
450
+ `/tcw-post-mortem`).
309
451
 
310
452
  ---
311
453
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tcw-cli"
7
- version = "2.0.1"
7
+ version = "2.1.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"
@@ -35,4 +35,11 @@ include = ["tcw*"]
35
35
 
36
36
  [tool.pytest.ini_options]
37
37
  testpaths = ["tests"]
38
+ # The repository root on sys.path, so `from evals import ...` resolves. The
39
+ # eval-harness tests import it, and `evals` is deliberately not a published
40
+ # package (`packages.find` includes only `tcw*`), so the editable install does
41
+ # not put it there. `python -m pytest` happened to work anyway — that form puts
42
+ # the current directory on sys.path and bare `pytest` does not — which is why
43
+ # CI, which runs bare `pytest`, was the only place this failed.
44
+ pythonpath = ["."]
38
45
  norecursedirs = [".venv", "build", "dist", "*.egg-info"]
@@ -0,0 +1 @@
1
+ __version__ = "2.1.1"
@@ -535,6 +535,15 @@ def main(argv: list[str] | None = None) -> int:
535
535
  except ValueError as error:
536
536
  print(f"tcw: {error}", file=sys.stderr)
537
537
  return 1
538
+ except yaml.YAMLError as error:
539
+ # A malformed YAML file is the user's, not a bug in TCW, so it reads as
540
+ # a message rather than a traceback. Here rather than at each of the
541
+ # twenty-odd `load_yaml` calls that do not catch it: refusing is already
542
+ # the right answer at every one of them — `init` must not overwrite a
543
+ # config it cannot read — and only the presentation was wrong. The
544
+ # loader's message names the file.
545
+ print(f"tcw: {error}", file=sys.stderr)
546
+ return 1
538
547
  except subprocess.CalledProcessError as error:
539
548
  # Deliberately generic: the only policy here is "a git subprocess
540
549
  # failed", which is true of every component. git's own diagnostic has