tcw-cli 2.0.2__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.
- {tcw_cli-2.0.2/tcw_cli.egg-info → tcw_cli-2.1.1}/PKG-INFO +160 -18
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/README.md +159 -17
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/pyproject.toml +8 -1
- tcw_cli-2.1.1/tcw/__init__.py +1 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/cli.py +9 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/store/base.py +170 -1
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/store/fs.py +166 -27
- tcw_cli-2.1.1/tcw/tracker/__init__.py +12 -0
- tcw_cli-2.1.1/tcw/tracker/claim.py +180 -0
- tcw_cli-2.1.1/tcw/tracker/jira.py +266 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/validate.py +43 -9
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/cli.py +144 -14
- {tcw_cli-2.0.2 → tcw_cli-2.1.1/tcw_cli.egg-info}/PKG-INFO +160 -18
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw_cli.egg-info/SOURCES.txt +15 -0
- tcw_cli-2.1.1/tests/test_committed_paths.py +38 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_documented_cli_surface.py +18 -3
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_environment_hardness.py +130 -0
- tcw_cli-2.1.1/tests/test_eval_coverage.py +48 -0
- tcw_cli-2.1.1/tests/test_eval_fixture.py +235 -0
- tcw_cli-2.1.1/tests/test_eval_grading.py +177 -0
- tcw_cli-2.1.1/tests/test_eval_runner.py +228 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_external_work_store.py +109 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_non_git_writes.py +73 -3
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_plugin_manifests.py +1 -1
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_remote_session_setup.py +65 -11
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_scaffold.py +6 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_session_bootstrap.py +1 -1
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_skill_lifecycle_parity.py +5 -1
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_store_editor.py +30 -22
- tcw_cli-2.1.1/tests/test_tracker_absent.py +163 -0
- tcw_cli-2.1.1/tests/test_tracker_claimability.py +192 -0
- tcw_cli-2.1.1/tests/test_tracker_cli.py +258 -0
- tcw_cli-2.1.1/tests/test_tracker_client.py +377 -0
- tcw_cli-2.1.1/tests/test_tracker_config.py +191 -0
- tcw_cli-2.1.1/tests/test_tracker_replay.py +147 -0
- tcw_cli-2.1.1/tests/test_tracker_validate.py +178 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_validate.py +99 -0
- tcw_cli-2.1.1/tests/test_work_tags.py +398 -0
- tcw_cli-2.0.2/tcw/__init__.py +0 -1
- tcw_cli-2.0.2/tests/test_work_tags.py +0 -182
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/LICENSE +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/setup.cfg +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/capabilities/__init__.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/capabilities/cli.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/refs.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/serve/__init__.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/serve/dist/client/assets/index-CXNiOIeg.css +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/serve/dist/client/assets/index-Dz5B7M8G.js +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/serve/dist/client/index.html +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/serve/dist/client/theme-init.js +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/serve/dist/server.cjs +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/serve/runtime.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/stdin.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/store/__init__.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/store/checkouts.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/store/project.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/taxonomy/__init__.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/taxonomy/cli.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/__init__.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/generate.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/hooks.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/projection.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/prompts/implement.md +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/prompts/inbox.md +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/prompts/plan.md +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/prompts/postmortem.md +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/prompts/request.md +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/prompts/spec.md +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/prompts/verify.md +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/recursion.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/resolve.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw/work/templates.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw_cli.egg-info/dependency_links.txt +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw_cli.egg-info/entry_points.txt +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw_cli.egg-info/requires.txt +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tcw_cli.egg-info/top_level.txt +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_body_prompt.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_capabilities.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_capabilities_federation.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_capabilities_reset.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_capabilities_sidecar.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_capability_ref_wording.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_cut_version.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_documentation_config.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_documentation_prompt.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_documentation_sync_wiring.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_epic_completable.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_falsification_rule.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_generate_hook.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_inbox_title.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_lifecycle_baseline.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_lifecycle_hooks.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_lifecycle_inert.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_lifecycle_policy.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_lifecycle_validation.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_multiproject.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_project_overrides.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_project_registry.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_projection.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_prompt_fallback.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_qualified_ref.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_recursion.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_refs.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_repo_lifecycle.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_resolve.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_retention.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_serve.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_serve_descendants.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_serve_projection.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_serve_resolve.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_serve_runtime.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_serve_write.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_shipped_prompts.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_show_json.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_skill_flow.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_smoke.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_stage_verb.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_status_parity.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_stdin.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_stdin_cli.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_store_bounds.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_store_nodes.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_store_provisioning.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_store_publication.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_subprocess_stdin.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_taxonomy.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_tombstone.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_unpushed_version_script.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_validate_target.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_work.py +0 -0
- {tcw_cli-2.0.2 → tcw_cli-2.1.1}/tests/test_work_autocommit.py +0 -0
- {tcw_cli-2.0.2 → 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.
|
|
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.
|
|
411
|
-
|
|
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_.
|
|
487
|
-
_judgment_ that drives it — the parts a deterministic tool cannot decide.
|
|
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
|
|
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-
|
|
523
|
-
`/tcw-
|
|
524
|
-
`/tcw-
|
|
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.
|
|
195
|
-
|
|
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_.
|
|
271
|
-
_judgment_ that drives it — the parts a deterministic tool cannot decide.
|
|
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
|
|
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-
|
|
307
|
-
`/tcw-
|
|
308
|
-
`/tcw-
|
|
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.
|
|
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
|