ticketlens 0.43.6 → 0.43.8

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.
package/README.md CHANGED
@@ -42,6 +42,7 @@
42
42
  - [Schedule](#schedule)
43
43
  - [History](#history)
44
44
  - [Recall](#recall)
45
+ - [MCP Server](#mcp-server)
45
46
  - [Comment, Transition, Assign, Duplicates, Link, Update, Create & Worklog](#comment-transition-assign-duplicates-link-update-create--worklog)
46
47
  - [Response-Time Stats](#response-time-stats)
47
48
  - [Pre-fetch Issue Types](#pre-fetch-issue-types)
@@ -49,6 +50,7 @@
49
50
  - [Custom Attention Rules](#custom-attention-rules)
50
51
  - [Login](#login)
51
52
  - [License](#license)
53
+ - [AI Provider Keys](#ai-provider-keys)
52
54
  - [Update Skill](#update-skill)
53
55
  - [/jtb — Jira TicketBrief for Claude Code](#jtb--jira-ticketbrief-for-claude-code)
54
56
  - [All Examples](#all-examples)
@@ -65,7 +67,7 @@
65
67
 
66
68
  ## What is TicketLens?
67
69
 
68
- TicketLens is a local-first Jira CLI that preprocesses ticket context on your machine and hands your AI tools a clean, compressed brief — instead of dumping raw Jira API JSON into your session. It supports Jira Cloud, Server, and Data Center, works with any AI tool that accepts text, and runs independently of any AI session.
70
+ TicketLens is a local-first ticket CLI that preprocesses ticket context on your machine and hands your AI tools a clean, compressed brief — instead of dumping raw Jira API JSON into your session. It supports Jira Cloud, Server, and Data Center (plus GitHub Issues and Linear), works with any AI tool that accepts text, and runs independently of any AI session.
69
71
 
70
72
  Zero npm dependencies. Node.js built-ins only.
71
73
 
@@ -73,7 +75,7 @@ Zero npm dependencies. Node.js built-ins only.
73
75
 
74
76
  ## Why TicketLens?
75
77
 
76
- - **Privacy** — ticket content never leaves your machine; no cloud relay, no data sent to Anthropic or anyone else
78
+ - **Privacy** — ticket content stays on your machine by default; no cloud relay, no data sent to Anthropic or anyone else. Only the opt-in features that need a backend (`--cloud`, `compliance --consensus`, `triage --push`/`--share`, Recall team sync) send data to TicketLens
77
79
  - **60–80% token savings** — structured briefs instead of verbose Jira JSON; 4-hour cache by default
78
80
  - **Scriptable** — standard CLI output: pipe to cron, git hooks, CI/CD, or any LLM tool
79
81
  - **Multi-profile** — connect multiple Jira instances simultaneously; auto-route by ticket prefix or project path
@@ -85,7 +87,7 @@ Zero npm dependencies. Node.js built-ins only.
85
87
  ## Quick Start
86
88
 
87
89
  ```bash
88
- npm install -g ticketlens
90
+ npm i -g ticketlens@beta
89
91
  ticketlens init # Guided setup: Jira, GitHub Issues, or Linear — connection test included
90
92
  ticketlens CNV1-2 # Fetch a ticket brief
91
93
  ticketlens triage # Scan your assigned tickets
@@ -94,8 +96,8 @@ ticketlens triage # Scan your assigned tickets
94
96
  Or without installing:
95
97
 
96
98
  ```bash
97
- npx ticketlens init
98
- npx ticketlens CNV1-2
99
+ npx ticketlens@beta init
100
+ npx ticketlens@beta CNV1-2
99
101
  ```
100
102
 
101
103
  Tip: `tl` works everywhere `ticketlens` does — running `tl`/`ticketlens config` before anything is configured also launches guided setup, no dead end. Pass `--no-input` to force non-interactive behavior even in a terminal (scripts, CI).
@@ -120,6 +122,7 @@ Tip: `tl` works everywhere `ticketlens` does — running `tl`/`ticketlens config
120
122
  | `ticketlens switch` | Arrow-key panel to switch between configured profiles |
121
123
  | `ticketlens config [--profile=NAME]` | Edit any field on an existing profile — always re-validates the connection |
122
124
  | `ticketlens profiles` | List all configured profiles (alias: `ticketlens ls`) |
125
+ | `ticketlens profiles set-team <NAME> [team]` | Route a profile's Recall notes to one Console team (for accounts in several teams) |
123
126
  | `ticketlens delete <NAME>` | Remove a profile and its credentials (prompts `y/N` in TTY; use `--yes` in scripts/CI) |
124
127
 
125
128
  `init` collects: profile name, tracker type (Jira / GitHub Issues / Linear), URL or workspace, credentials (masked), and optional ticket prefixes, project paths, and triage statuses. On connection failure, a retry menu lets you fix credentials, URL, or skip — all inputs pre-populated. If your Jira instance sits behind a VPN and resolves to a private/internal address, you'll be asked to confirm you trust that connection before it's allowed through — a one-time confirmation, remembered per profile and scoped to that exact host (changing the URL asks again). `config` is tracker-aware and always re-validates the connection after edits.
@@ -293,12 +296,11 @@ Flag validation provides actionable hints:
293
296
  ```bash
294
297
  ticketlens compliance <TICKET-KEY> # Check ticket requirements against local diff [Pro/Free 3/mo]
295
298
  ticketlens compliance <TICKET-KEY> --profile=acme # Specify a profile
296
- ticketlens compliance <TICKET-KEY> --plain # Plain markdown output
297
299
  ticketlens compliance <TICKET-KEY> --consensus # Multi-agent AI review instead of the local matcher [Pro]
298
300
  ticketlens compliance <TICKET-KEY> --consensus -y # Same, skipping the cost-confirmation prompt
299
301
  ```
300
302
 
301
- Runs the same compliance check as `ticketlens CNV1-2 --compliance` but as a dedicated subcommand — useful when you want to check compliance without fetching the full ticket brief. Free accounts get 3 checks per month; Pro is unlimited.
303
+ Runs the same compliance check as `ticketlens CNV1-2 --compliance` but as a dedicated subcommand — useful when you want to check compliance without fetching the full ticket brief. Free accounts get 3 checks per month; Pro is unlimited. The diff used for matching (and sent for `--consensus`) excludes Markdown files (`*.md`), so requirements met only by doc changes won't register as covered.
302
304
 
303
305
  `--consensus` (Pro) replaces the local deterministic matcher with independent reviews from your team's AI provider pool, reconciled by majority vote after a disagreement-triggered refinement round. Requires `ticketlens login` and a "consensus" role set up at Console > Admin > AI Roles, with 2+ providers attached from your team's shared registry (Console > Admin > AI Provider Pool — any title/key/endpoint/model, not a fixed list). This is the only compliance path that sends your diff off-machine — to TicketLens's backend, which fans out to each provider (keys are encrypted server-side and never sent to the CLI). The diff is scanned for secrets before anything is sent; a detected secret blocks the run with no AI call made. Prompts for confirmation before spending your team's API credits unless `-y`/`--yes` is passed.
304
306
 
@@ -331,7 +333,6 @@ Installs a `pre-push` git hook that runs `ticketlens compliance` on every push.
331
333
  ```bash
332
334
  ticketlens pr <TICKET-KEY> # Generate PR description from ticket
333
335
  ticketlens pr <TICKET-KEY> --profile=acme # Specify a profile
334
- ticketlens pr <TICKET-KEY> --plain # Plain markdown output
335
336
  ticketlens pr <TICKET-KEY> | pbcopy # Copy to clipboard
336
337
  ticketlens pr <TICKET-KEY> --open # Open a real GitHub PR compare page (Pro, GitHub only)
337
338
  ```
@@ -399,7 +400,7 @@ ticketlens cache clear --profile=acme # One profile's files only
399
400
  ticketlens cache clear --older-than=30d --yes # Skip confirmation (CI/scripts)
400
401
  ```
401
402
 
402
- Age units: `d` = days · `m` = months (30d) · `y` = years (365d)
403
+ Age units: `h` = hours · `d` = days · `w` = weeks · `m` = months (30d) · `y` = years (365d)
403
404
 
404
405
  Cache locations:
405
406
  - Attachments: `~/.ticketlens/cache/TICKET-KEY/`
@@ -475,6 +476,18 @@ Every note is scanned before saving — anything shaped like a real secret (API
475
476
 
476
477
  ---
477
478
 
479
+ ### MCP Server
480
+
481
+ ```bash
482
+ ticketlens mcp # Start the MCP stdio server (every CLI action as a tool)
483
+ ticketlens mcp install # Register it in the current project's .mcp.json
484
+ ticketlens mcp install --dry-run # Preview without writing
485
+ ```
486
+
487
+ Exposes 24 tools (`fetch`, `triage`, `compliance`, `review`, `standup`, `pr`, `stats`, `issue_types`, `history`, `collisions`, `ledger`, `doctor`, `recall_*`, `ticket_*`) to any MCP-compatible AI harness. Starting the server and `mcp install` are ungated; each tool enforces its own tier at call time. Per-tool tiers and details are in the "Any MCP-capable AI harness" paragraph under [Recall](#recall).
488
+
489
+ ---
490
+
478
491
  ### Comment, Transition, Assign, Duplicates, Link, Update, Create & Worklog
479
492
 
480
493
  ```bash
@@ -524,11 +537,11 @@ Write directly to the ticket in its real tracker — Jira, GitHub, or Linear —
524
537
 
525
538
  `ticketlens create` creates a new ticket with a fixed minimal field set — no arbitrary custom fields. Unlike every other write command, there's no existing ticket to target, so `--profile` (or your default profile) picks the tracker instead of a ticket key. `--project` is the Jira project key or Linear team key — required for both, ignored on GitHub since its target repo is already fixed by the profile. `--type` is Jira's issue type (e.g. `"Task"`, `"Bug"`) — required for Jira, ignored elsewhere. No `--confirm` gate, same risk tier as `update`/`assign` — but this is the highest-blast-radius command in the whole family: a bad `--project`/`--type` fabricates a real, hard-to-walk-back item in a live tracker, so an invalid value surfaces the tracker's own error rather than a silent guess.
526
539
 
527
- `--attach=path1,path2` (comma-separated local file paths) is available on `comment` and `create` only, up to 50 files per call (Pro, Team, Enterprise). Images render as an inline thumbnail on Jira and Linear; GitHub has no attachment upload API, so `--attach` is unsupported there.
540
+ `--attach=path1,path2` (comma-separated local file paths) is available on `comment`, `create`, and `note add`/`note patch`, up to 50 files per call (Pro, Team, Enterprise). Images render as an inline thumbnail on Jira and Linear; GitHub has no attachment upload API, so `--attach` is unsupported there.
528
541
 
529
542
  **A bad `--project`/`--type` gets a better error, automatically.** If create fails because the project or issue type doesn't exist, TicketLens fetches your tracker's real, current project list (and, for Jira, the real issue types for that project) and shows them alongside the failure — e.g. `Known creatable projects: CNV1, CNV2.` — rather than a bare tracker error. This is reactive only: it never runs on a successful create, never auto-retries the write, and is cached locally per profile for 3 days (a targeted, single-project lookup — same bar `issue-types --project=KEY` uses) so a burst of failed attempts doesn't re-fetch every time. `ticketlens issue-types` (below) is the proactive counterpart — pre-fetches and caches the same data ahead of time, so a `create` right after it is a pure cache hit.
530
543
 
531
- All six write actions (comment/transition/assign/link/update/create) have a short local debounce (10s) against an accidental double-fire (a flaky retry, hitting enter twice), and every successful write is appended to a local, append-only audit log (`~/.ticketlens/ticket-action-log.jsonl`). A write that times out is never retried automatically — unlike Recall notes, ticket writes aren't naturally idempotent, so a timed-out attempt is surfaced to you instead of silently repeated. `duplicates` has neither, since nothing is written.
544
+ All seven write actions (comment/transition/assign/link/update/create/worklog) have a short local debounce (10s) against an accidental double-fire (a flaky retry, hitting enter twice), and every successful write is appended to a local, append-only audit log (`~/.ticketlens/ticket-action-log.jsonl`). A write that times out is never retried automatically — unlike Recall notes, ticket writes aren't naturally idempotent, so a timed-out attempt is surfaced to you instead of silently repeated. `duplicates` has neither, since nothing is written.
532
545
 
533
546
  ---
534
547
 
@@ -580,7 +593,7 @@ ticketlens doctor --format=json | jq '.ok'
580
593
  ticketlens doctor --mcp # Also check the MCP server handshake (spawns a subprocess)
581
594
  ```
582
595
 
583
- Runs five checks — profile configuration, license freshness, tracker connectivity, attachment cache health, and the Recall sync queue — and reports pass/fail with an actionable hint per failure, instead of a raw stack trace. Free tier, fully unrestricted; no license required.
596
+ Runs six checks — profile configuration, license freshness, tracker connectivity, attachment cache health, the Recall sync queue, and MCP registration (plus an opt-in MCP handshake check with `--mcp`) — and reports pass/fail with an actionable hint per failure, instead of a raw stack trace. Free tier, fully unrestricted; no license required.
584
597
 
585
598
  ---
586
599
 
@@ -603,7 +616,7 @@ Add an `attentionRules` array to any profile in `~/.ticketlens/profiles.json` to
603
616
  }
604
617
  ```
605
618
 
606
- Rules are evaluated in order — first match wins. Supported `match` keys: `priority`, `label`, `status`, `keyPrefix`. Supported `action` values: `force-urgent` (bumps to needs-response) and `ignore` (excludes from output). Requires a Pro license.
619
+ Rules are evaluated in order — first match wins. Supported `match` keys: `priority`, `label`, `status`, `keyPrefix`. Supported `action` values: `force-urgent` (bumps to needs-response) and `ignore` (excludes from output).
607
620
 
608
621
  ---
609
622
 
@@ -612,7 +625,7 @@ Rules are evaluated in order — first match wins. Supported `match` keys: `prio
612
625
  ```bash
613
626
  ticketlens login # Open browser → authorize in Console → token saved automatically
614
627
  ticketlens login --manual # Paste a token instead (CI/headless environments)
615
- ticketlens logout # Revoke and remove the stored CLI token
628
+ ticketlens logout # Remove the stored CLI token
616
629
  ticketlens sync # Pull your latest tracker profiles from the Console
617
630
  ```
618
631
 
@@ -624,7 +637,7 @@ Use `--manual` when there is no GUI (CI runners, SSH sessions, containers).
624
637
 
625
638
  `ticketlens sync` pulls any tracker profiles you have configured in the Console and writes them locally, keeping your CLI in sync with your team settings without re-running `init`.
626
639
 
627
- > **Console features** — `ticketlens triage --push`, `--share`, `ticketlens collisions`, and `ticketlens schedule` all require an active Console session. Run `ticketlens login` once and the token is stored automatically.
640
+ > **Console features** — `ticketlens triage --push`, `--share`, `ticketlens collisions`, and `ticketlens schedule` all require an active Console session (`schedule` falls back to local-only mode when logged out). Run `ticketlens login` once and the token is stored automatically.
628
641
 
629
642
  ---
630
643
 
@@ -637,6 +650,17 @@ ticketlens activate <KEY> # Activate a Pro or Team license
637
650
 
638
651
  ---
639
652
 
653
+ ### AI Provider Keys
654
+
655
+ ```bash
656
+ ticketlens cloud-keys list|add|remove|test|priority|timeout # Manage encrypted AI provider keys (requires login)
657
+ ticketlens config set aiProvider groq # Default provider for --summarize / --handoff
658
+ ```
659
+
660
+ Details and the full command list are under [Pro](#pro--9mo).
661
+
662
+ ---
663
+
640
664
  ### Update Skill
641
665
 
642
666
  ```bash
@@ -665,7 +689,7 @@ Supported assistants detected automatically:
665
689
  **Install:**
666
690
 
667
691
  ```bash
668
- npm install -g ticketlens && ticketlens init
692
+ npm i -g ticketlens@beta && ticketlens init
669
693
  ticketlens update-skill # copies /jtb skill into ~/.claude/commands/jtb.md
670
694
  # Restart Claude Code, then:
671
695
  # /jtb CNV1-2
@@ -674,7 +698,7 @@ ticketlens update-skill # copies /jtb skill into ~/.claude/commands/jtb.m
674
698
  **Keeping the skill up to date:**
675
699
 
676
700
  ```bash
677
- npm install -g ticketlens@latest # update the CLI
701
+ npm i -g ticketlens@beta # update the CLI
678
702
  ticketlens update-skill # sync the /jtb skill to the new version
679
703
  ```
680
704
 
@@ -714,8 +738,10 @@ ticketlens config # Edit the active profile
714
738
  ticketlens config --profile=acme # Edit a specific profile
715
739
  ticketlens config set aiProvider groq # Set default AI provider (groq|openai|anthropic)
716
740
  ticketlens config set recallStrictness strict # Tune Recall-capture strictness (loose|balanced|strict)
741
+ ticketlens config set errorReporting off # Opt out of (or `on` to opt in to) anonymous error reports
717
742
  ticketlens profiles # List all configured profiles
718
743
  ticketlens ls # Alias for profiles
744
+ ticketlens profiles set-team <NAME> "Team" # Route a profile's Recall notes to one Console team
719
745
  ticketlens profiles --plain # Tab-separated (scripts / pipes)
720
746
  ticketlens delete <PROFILE-NAME> # Remove a profile (prompts y/N in TTY)
721
747
  ticketlens delete <PROFILE-NAME> --yes # Remove without prompt (scripts/CI)
@@ -821,8 +847,8 @@ ticketlens cache clear --older-than=30d --yes # Skip confirmation (CI
821
847
 
822
848
  # ── Schedule ─────────────────────────────────────────────────────────────────
823
849
  ticketlens schedule # Interactive wizard — set time, timezone, profile [Pro]
824
- ticketlens schedule --stop # Cancel the scheduled digest [Pro]
825
- ticketlens schedule --status # Show current schedule [Pro]
850
+ ticketlens schedule --stop # Cancel the scheduled digest
851
+ ticketlens schedule --status # Show current schedule
826
852
  ticketlens schedule --local --time=07:00 --save=./triage.txt # Local-only cron/LaunchAgent, no Console auth [Pro]
827
853
 
828
854
  # ── History ───────────────────────────────────────────────────────────────────
@@ -834,9 +860,11 @@ echo "note body" | ticketlens note add --title="..." --attach=shot.png,log.txt
834
860
  ticketlens note delete --id="..." --ticket=CNV1-2 # Remove a note from your local vault [Pro]
835
861
  ticketlens recall CNV1-2 # Search saved notes by ticket key [Pro]
836
862
  ticketlens recall "retry backoff" # Free-text search across all notes [Pro]
863
+ ticketlens recall CNV1-2 --full # Print each matching note's full body [Pro]
864
+ ticketlens recall CNV1-2 --plain # Plain, pipe-safe output [Pro]
837
865
  ticketlens recall sync # Retry any notes stuck in the local queue [Team+]
838
866
  ticketlens recall settings # Show effective retry-queue settings + capture strictness, fetched live [Team+]
839
- ticketlens mcp # Start the MCP stdio server (recall/ticket write tools) [Pro]
867
+ ticketlens mcp # Start the MCP stdio server (every CLI action as a tool; each tool enforces its own tier)
840
868
  ticketlens mcp install # Register it into the current project's .mcp.json
841
869
  ticketlens mcp install --dry-run # Preview the registration without writing
842
870
 
@@ -881,6 +909,7 @@ ticketlens doctor --mcp # Also check the MCP server handsh
881
909
 
882
910
  # ── Compliance ────────────────────────────────────────────────────────────────
883
911
  ticketlens compliance <TICKET-KEY> # Check ticket requirements against local diff [Pro/Free 3/mo]
912
+ ticketlens compliance <TICKET-KEY> --consensus -y # Multi-agent AI review, skip cost prompt [Pro]
884
913
  ticketlens ledger # View local compliance audit ledger [Pro]
885
914
  ticketlens install-hooks # Install pre-push compliance gate
886
915
  ticketlens install-hooks --uninstall # Remove installed hooks
@@ -888,6 +917,7 @@ ticketlens install-hooks --uninstall # Remove installed hooks
888
917
  # ── PR Description ─────────────────────────────────────────────────────────────
889
918
  ticketlens pr <TICKET-KEY> # Generate PR description from ticket
890
919
  ticketlens pr <TICKET-KEY> | pbcopy # Copy to clipboard
920
+ ticketlens pr <TICKET-KEY> --open # Open a real GitHub PR compare page (Pro, GitHub only)
891
921
 
892
922
  # ── AI provider keys (BYOK) ───────────────────────────────────────────────────
893
923
  ticketlens cloud-keys list # List configured AI providers
@@ -904,7 +934,7 @@ ticketlens cloud-keys --help # Subcommand help
904
934
  # ── Login ─────────────────────────────────────────────────────────────────────
905
935
  ticketlens login # Browser flow — opens Console, token saved automatically
906
936
  ticketlens login --manual # Paste flow — for CI/headless environments
907
- ticketlens logout # Revoke and remove stored CLI token
937
+ ticketlens logout # Remove stored CLI token
908
938
  ticketlens sync # Pull tracker profiles from the Console
909
939
 
910
940
  # ── License and account ────────────────────────────────────────────────────────
@@ -953,7 +983,7 @@ ticketlens note delete --id="..." # Remove a note from your local vault
953
983
  ticketlens recall <query|TICKET-KEY> # Search your saved Recall notes
954
984
  ticketlens recall sync # Retry any notes stuck in the local queue
955
985
  ticketlens recall settings # Show effective retry-queue settings + capture strictness, fetched live
956
- ticketlens mcp # Start the MCP stdio server (recall/ticket write tools)
986
+ ticketlens mcp # Start the MCP stdio server (every CLI action as a tool)
957
987
  ticketlens comment CNV1-2 --body="..." # Post a comment to the tracker
958
988
  ticketlens comment CNV1-2 --body="..." --attach=./bug.png # Attach local files (comment/create only)
959
989
  ticketlens transition CNV1-2 --target="Done" --confirm # Transition ticket status
@@ -1104,8 +1134,7 @@ Credentials in `~/.ticketlens/credentials.json` (chmod 600):
1104
1134
  | 2 | Ticket prefix match | `ticketlens CNV1-2` → prefix `PROJ` → `myteam` |
1105
1135
  | 3 | Project path match | `triage` in `~/projects/myteam-app` → `myteam` |
1106
1136
  | 4 | `config.default` field | Explicit default set via `ticketlens switch` |
1107
- | 5 | First profile in file | Fallback when `config.default` is absent |
1108
- | 6 | Environment variables | `JIRA_BASE_URL`, `JIRA_EMAIL`, `JIRA_API_TOKEN` / `JIRA_PAT` |
1137
+ | 5 | Environment variables | `JIRA_BASE_URL`, `JIRA_EMAIL`, `JIRA_API_TOKEN` / `JIRA_PAT` |
1109
1138
 
1110
1139
  ---
1111
1140
 
@@ -1122,6 +1151,11 @@ npm test
1122
1151
  See [ROADMAP.md](ROADMAP.md) for the full plan.
1123
1152
 
1124
1153
  Recently shipped:
1154
+ - **Upsell tip without a price** — the `--summarize` tip after a deep `fetch` now says "(Pro)" instead of a hardcoded price; plans and prices live in the Console
1155
+ - **Docs match the code** — every doc surface (README, `--help`, SKILL.md, USER_GUIDE, MCP help) re-audited against the CLI; install snippets now use `ticketlens@beta`, Node is `>=22.6`, and the CLI tip shows the real $9/mo Pro price
1156
+ - **Console: Behavior settings** — pick when the idle-warning appears (5 min, 10 min or 1 h) and a playful or plain tone, per user, in Settings > Behavior
1157
+ - **Console: session-expiry warning** — a stay-or-logout countdown appears shortly before your session ends; any activity resets it
1158
+ - **Console: collapsed-sidebar Owner Panel** — the hover menu now opens and stays open when you click an item, like the other groups
1125
1159
  - **Tiered attachment cap** — per-call attachment count is now 10 on Free and 50 on Pro, Team and Enterprise (was a flat 20), for uploads, `fetch` downloads and Recall sync. Excess files are reported, not silently dropped
1126
1160
  - **`ticketlens pr --open`** — opens a real GitHub PR compare page prefilled with the same description `pr` already prints, instead of just printing it. GitHub only, no stored write-credential. Pro tier
1127
1161
  - **`review`/`compliance` fixes** — branch diff now uses merge-base semantics, no longer picks up unrelated unmerged-main files; AC parser now recognizes Jira wiki `Requirements`/`How to test` headers, not just literal "Acceptance Criteria"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ticketlens",
3
- "version": "0.43.6",
4
- "description": "Jira CLI for developers — fetch ticket context, triage your queue, and stop tab-switching. Zero dependencies, all local.",
3
+ "version": "0.43.8",
4
+ "description": "Ticket CLI for developers (Jira, GitHub Issues, Linear) — fetch ticket context, triage your queue, and stop tab-switching. Zero dependencies, local-first.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "ticketlens": "bin/ticketlens.mjs",
@@ -1,4 +1,4 @@
1
- <!-- jtb-skill-version: 0.49.1 -->
1
+ <!-- jtb-skill-version: 0.49.2 -->
2
2
  ---
3
3
  name: jtb
4
4
  description: Fetch a Jira ticket's full context (description, comments, linked issues, code references) and assemble a structured TicketBrief for implementation planning. Use when user types /jtb, mentions a Jira ticket key, or wants to plan work from a Jira ticket.
@@ -484,7 +484,7 @@ ticketlens worklog PROD-1234=1h30m PROD-5678=45m --comment="Sprint work" --confi
484
484
  - The MCP tool takes `entries[]` with per-ticket `comment`/`started`.
485
485
  - The CLI applies one `--comment`/`--started` to every ticket.
486
486
 
487
- `--attach=path1,path2` (comma-separated local file paths) is available on `comment` and `create` only, up to 50 files per call (Pro, Team, Enterprise). Images render as an inline thumbnail on Jira and Linear; GitHub has no attachment upload API, so `--attach` is unsupported there.
487
+ `--attach=path1,path2` (comma-separated local file paths) is available on `comment`, `create`, and `note add`/`note patch`, up to 50 files per call (Pro, Team, Enterprise). Images render as an inline thumbnail on Jira and Linear; GitHub has no attachment upload API, so `--attach` is unsupported there.
488
488
 
489
489
  The write actions (comment/transition/assign/link/update/create/worklog) have a short local debounce (10s) against an accidental double-fire, and every write is appended to a local audit log (`~/.ticketlens/ticket-action-log.jsonl`). A write that times out is never retried automatically — surface the failure to the user rather than silently re-attempting, since a ticket write isn't naturally idempotent the way a Recall note save is. `duplicates` has neither, since nothing is written.
490
490
 
@@ -588,7 +588,7 @@ Use this evaluation order:
588
588
  `--compliance` never sends data anywhere. The diff stays local. All analysis is performed by Claude Code within your session context. (The standalone command's `--consensus` opt-in below is the one exception — see that section.)
589
589
 
590
590
  ### Standalone command
591
- The same tier-gated check also runs as its own command — `ticketlens compliance PROJ-123` — independent of a full ticket fetch. This is what `ticketlens install-hooks` wires into a pre-push git hook (`ticketlens compliance "$KEY" || exit 1`, gated on a configurable coverage threshold). It shares the same `FREE_LIMIT`/Pro gate and the same compliance ledger as the `--compliance` flag above.
591
+ The same tier-gated check also runs as its own command — `ticketlens compliance PROJ-123` — independent of a full ticket fetch. This is what `ticketlens install-hooks` wires into a pre-push git hook (`ticketlens compliance "$KEY" || exit 1`, gated on a configurable coverage threshold). It shares the same `FREE_LIMIT`/Pro gate and the same compliance ledger as the `--compliance` flag above. The diff it evaluates (and sends for `--consensus`) excludes Markdown files (`*.md`), so requirements met only by doc changes won't register as covered.
592
592
 
593
593
  If this harness has TicketLens's MCP server configured (a tool named `compliance` — often shown as `mcp__ticketlens__compliance` — visible in your tool list), prefer it over the bash form: same tier gate (Free: 3 checks/month, Pro: unlimited), same report — just no shell command to construct or stdout to parse. It accepts `ticket`/`profile`/`consensus`, matching the standalone command's arguments below.
594
594
 
@@ -623,7 +623,7 @@ Generates an AI-powered summary of the full brief, collapsing verbose descriptio
623
623
 
624
624
  - **BYOK (default):** reads your AI API key from `~/.ticketlens/credentials.json`. First use will prompt for consent.
625
625
  - **`--cloud`:** routes through TicketLens cloud API (no local key needed, requires Pro).
626
- - **`--provider=NAME`:** override the AI provider. Supported values depend on your credentials (e.g. `claude`, `openai`).
626
+ - **`--provider=NAME`:** override the AI provider. Supported values: `anthropic`, `openai`, `groq` (whichever you have a key for).
627
627
  - **`--budget=N`:** prune the brief to approximately N tokens before summarising. Forces plain-text output.
628
628
 
629
629
  ### --handoff (Pro)
@@ -32,6 +32,7 @@ import { parseAge } from './lib/cache-manager.mjs';
32
32
  import { createStyler } from './lib/ansi.mjs';
33
33
  import { apiBase } from './lib/api-utils.mjs';
34
34
  import { isLicensed, showUpgradePrompt, readLicense } from './lib/license.mjs';
35
+ import { SUMMARIZE_TIP } from './lib/upsell-tips.mjs';
35
36
  import { detectVcs } from './lib/vcs-detector.mjs';
36
37
  import { runComplianceCheck } from './lib/compliance-checker.mjs';
37
38
  import { runConsensusCheck } from './lib/consensus-checker.mjs';
@@ -1456,7 +1457,7 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
1456
1457
  // Contextual upsell: after a deep traversal with a substantial graph, nudge toward --summarize
1457
1458
  if (depth > 1 && !args.includes('--summarize') && (ticket.linked?.length ?? 0) >= 2) {
1458
1459
  const s = createStyler({ isTTY: process.stderr.isTTY });
1459
- printErrFn(` ${s.dim('○')} ${s.dim('Tip: large briefs compress further — `--summarize` condenses this to a single AI digest ($8/mo)')}\n`);
1460
+ printErrFn(` ${s.dim('○')} ${s.dim(SUMMARIZE_TIP)}\n`);
1460
1461
  }
1461
1462
  }
1462
1463
 
@@ -45,19 +45,19 @@ export function printHelp({ stream = process.stdout } = {}) {
45
45
  ` ${s.brand('ticketlens')} compliance ${s.dim('<TICKET-KEY>')} Check requirements coverage ${s.dim('[Pro/Free 3/mo]')}`,
46
46
  ` ${s.brand('ticketlens')} install-hooks ${s.dim('[--uninstall]')} Git pre-push compliance gate`,
47
47
  ` ${s.brand('ticketlens')} pr ${s.dim('<TICKET-KEY>')} Assemble a PR description from ticket context`,
48
- ` ${s.brand('ticketlens')} ledger ${s.dim('[--format=json|csv]')} Export your signed usage ledger ${s.dim('[Pro]')}`,
48
+ ` ${s.brand('ticketlens')} ledger ${s.dim('[--format=json|csv]')} Export your signed compliance ledger ${s.dim('[Pro]')}`,
49
49
  ` ${s.brand('ticketlens')} history ${s.dim('<TICKET-KEY>')} Urgency timeline for a ticket ${s.dim('[Pro]')}`,
50
50
  ` ${s.brand('ticketlens')} stats ${s.dim('[options]')} Personal response-time metrics from local history`,
51
51
  ` ${s.brand('ticketlens')} doctor ${s.dim('[--fix] [options]')} Diagnose profile/license/connectivity/cache/queue problems`,
52
- ` ${s.brand('ticketlens')} note add ${s.dim('--title=... [--ticket=KEY]')} Save a Recall note ${s.dim('[Pro]')}`,
52
+ ` ${s.brand('ticketlens')} note add ${s.dim('--title=... [--ticket=KEY] [--attach=a,b]')} Save a Recall note ${s.dim('[Pro]')}`,
53
53
  ` ${s.brand('ticketlens')} note delete ${s.dim('--id=... [--ticket=KEY]')} Remove a note from your local vault ${s.dim('[Pro]')}`,
54
54
  ` ${s.brand('ticketlens')} recall ${s.dim('<query|TICKET-KEY>')} Search your saved Recall notes ${s.dim('[Pro]')}`,
55
55
  ` ${s.brand('ticketlens')} recall sync Retry any notes stuck in the local queue ${s.dim('[Team+]')}`,
56
56
  ` ${s.brand('ticketlens')} recall settings Show effective retry-queue settings + capture strictness, fetched live ${s.dim('[Team+]')}`,
57
- ` ${s.brand('ticketlens')} mcp Start the MCP stdio server (Recall + ticket writes) ${s.dim('[Pro]')}`,
57
+ ` ${s.brand('ticketlens')} mcp Start the MCP stdio server (all CLI actions; each tool enforces its own tier)`,
58
58
  ` ${s.brand('ticketlens')} comment ${s.dim('<TICKET-KEY> --body=... [--attach=...]')} Post a comment to the tracker ${s.dim('[Pro]')}`,
59
59
  ` ${s.brand('ticketlens')} transition ${s.dim('<TICKET-KEY> [--target=... --confirm]')} Move ticket status ${s.dim('[Pro]')}`,
60
- ` ${s.brand('ticketlens')} assign ${s.dim('<TICKET-KEY> --to=me')} Assign a ticket to yourself ${s.dim('[Pro]')}`,
60
+ ` ${s.brand('ticketlens')} assign ${s.dim('<TICKET-KEY> --to=me')} Assign a ticket to yourself or (Jira) another dev ${s.dim('[Pro]')}`,
61
61
  ` ${s.brand('ticketlens')} worklog ${s.dim('<KEY=DURATION ...> [--comment=... --started=...] --confirm')} Log time on Jira tickets ${s.dim('[Pro] [Jira only]')}`,
62
62
  ` ${s.brand('ticketlens')} duplicates ${s.dim('<TICKET-KEY> [--threshold=N]')} Find likely duplicate tickets ${s.dim('[Pro]')}`,
63
63
  ` ${s.brand('ticketlens')} link ${s.dim('<SOURCE> <TARGET> [--type=... --confirm]')} Link two tickets ${s.dim('[Pro]')}`,
@@ -113,7 +113,7 @@ export function printHelp({ stream = process.stdout } = {}) {
113
113
  ` ${s.brand('--priority')}=${s.dim('LEVEL')} Filter by priority ${s.dim('(e.g. High, Blocker) [Team]')}`,
114
114
  ` ${s.brand('--export')}=${s.dim('FORMAT')} Export results to file ${s.dim('(csv|json) [Team]')}`,
115
115
  ` ${s.brand('--push')} Push snapshot to Console queue ${s.dim('[Team]')}`,
116
- ` ${s.brand('--share')} Generate a 24h share URL ${s.dim('(no login required) [Team]')}`,
116
+ ` ${s.brand('--share')} Generate a 24h share URL ${s.dim('(recipients need no login) [Team]')}`,
117
117
  ` ${s.brand('--all')} Triage all configured profiles at once ${s.dim('[Pro]')}`,
118
118
  ` ${s.brand('--save')}=${s.dim('FILE')} Save ANSI-stripped output to file ${s.dim('[Pro]')}`,
119
119
  ` ${s.brand('--digest')} POST scored results to digest endpoint ${s.dim('[Pro]')}`,
@@ -560,7 +560,7 @@ export function printNoteHelp({ stream = process.stdout } = {}) {
560
560
  const s = createStyler({ isTTY: stream.isTTY });
561
561
  const lines = [
562
562
  '',
563
- ` ${s.bold(s.brand('ticketlens'))} ${s.bold('note add')} ${s.dim('--title="..." [--ticket=KEY] [--tags=a,b]')} ${s.dim('[Pro]')}`,
563
+ ` ${s.bold(s.brand('ticketlens'))} ${s.bold('note add')} ${s.dim('--title="..." [--ticket=KEY] [--tags=a,b] [--attach=a,b]')} ${s.dim('[Pro]')}`,
564
564
  '',
565
565
  ` Save a short note about a ticket to your local Recall vault. ${s.dim('[Pro]')}`,
566
566
  ` Notes are saved at ${s.cyan('~/.ticketlens/recall/')} and matched into future ticket briefs.`,
@@ -572,6 +572,7 @@ export function printNoteHelp({ stream = process.stdout } = {}) {
572
572
  ` ${s.brand('--ticket')}=${s.dim('KEY')} Ticket this note is about ${s.dim('(optional — omit for a general note)')}`,
573
573
  ` ${s.brand('--tags')}=${s.dim('a,b')} Comma-separated tags`,
574
574
  ` ${s.brand('--include-attachments')} Seed the note with text from this ticket's cached attachments`,
575
+ ` ${s.brand('--attach')}=${s.dim('a,b')} Comma-separated local files to save with the note ${s.dim('(10 on Free, 50 on Pro+)')}`,
575
576
  ` ${s.brand('--plain')} Plain confirmation, no color (default when piped)`,
576
577
  ` ${s.brand('-h')}, ${s.brand('--help')} Show this help`,
577
578
  '',
@@ -579,7 +580,7 @@ export function printNoteHelp({ stream = process.stdout } = {}) {
579
580
  '',
580
581
  ` ${s.dim('$')} echo "Retry needs exponential backoff" | ticketlens note add --title="Retry gotcha" --ticket=PROD-123 --tags=bug`,
581
582
  '',
582
- ` ${s.bold('ticketlens note patch')} ${s.dim('--id="..." [--ticket=KEY] [--expect-mtime=MS]')} ${s.dim('[Pro]')}`,
583
+ ` ${s.bold('ticketlens note patch')} ${s.dim('--id="..." [--ticket=KEY] [--expect-mtime=MS] [--attach=a,b]')} ${s.dim('[Pro]')}`,
583
584
  '',
584
585
  ` Overwrites an existing note's body with a better draft, read from stdin.`,
585
586
  ` Internal mechanism used by the jtb skill's note quality loop inside a Claude`,
@@ -592,6 +593,7 @@ export function printNoteHelp({ stream = process.stdout } = {}) {
592
593
  ` ${s.brand('--ticket')}=${s.dim('KEY')} Ticket this note is about ${s.dim('(optional)')}`,
593
594
  ` ${s.brand('--expect-mtime')}=${s.dim('MS')} File mtime (ms) the caller last observed ${s.dim('(optional)')} — a no-op,`,
594
595
  ` not an overwrite, if the file changed since then`,
596
+ ` ${s.brand('--attach')}=${s.dim('a,b')} Append local files to the note, never replacing existing ones ${s.dim('(optional)')}`,
595
597
  '',
596
598
  ` ${s.bold('ticketlens note delete')} ${s.dim('--id="..." [--ticket=KEY] [--yes]')} ${s.dim('[Pro]')}`,
597
599
  '',
@@ -1016,6 +1018,7 @@ export function printConfigHelp({ stream = process.stdout } = {}) {
1016
1018
  ` ${s.bold(s.brand('ticketlens'))} ${s.bold('config')} ${s.dim('[--profile=NAME]')}`,
1017
1019
  ` ${s.bold(s.brand('ticketlens'))} ${s.bold('config set aiProvider')} ${s.dim('<anthropic|openai|groq>')}`,
1018
1020
  ` ${s.bold(s.brand('ticketlens'))} ${s.bold('config set recallStrictness')} ${s.dim('<loose|balanced|strict>')}`,
1021
+ ` ${s.bold(s.brand('ticketlens'))} ${s.bold('config set errorReporting')} ${s.dim('<on|off>')}`,
1019
1022
  '',
1020
1023
  ` Edit settings for an existing profile using an interactive wizard.`,
1021
1024
  ` Without ${s.cyan('--profile')}, edits the currently active profile.`,
@@ -1035,6 +1038,8 @@ export function printConfigHelp({ stream = process.stdout } = {}) {
1035
1038
  ` ${s.dim('balanced')} = today's default behavior`,
1036
1039
  ` ${s.dim('strict')} = capture more liberally, when in doubt`,
1037
1040
  '',
1041
+ ` ${s.brand('set errorReporting')} ${s.dim('<on|off>')} Opt in or out of anonymous error reports`,
1042
+ '',
1038
1043
  ` ${s.bold('OPTIONS')}`,
1039
1044
  '',
1040
1045
  ` ${s.brand('--profile')}=${s.dim('NAME')} Profile to configure`,
@@ -1047,6 +1052,7 @@ export function printConfigHelp({ stream = process.stdout } = {}) {
1047
1052
  ` ${s.dim('$')} ticketlens config set aiProvider groq`,
1048
1053
  ` ${s.dim('$')} ticketlens config set aiProvider anthropic`,
1049
1054
  ` ${s.dim('$')} ticketlens config set recallStrictness strict`,
1055
+ ` ${s.dim('$')} ticketlens config set errorReporting off`,
1050
1056
  '',
1051
1057
  ];
1052
1058
  stream.write(lines.join('\n') + '\n');
@@ -1076,7 +1082,7 @@ export function printTriageHelp({ stream = process.stdout } = {}) {
1076
1082
  ` ${s.brand('--priority')}=${s.dim('LEVEL')} Filter by priority ${s.dim('(e.g. High, Blocker) [Team]')}`,
1077
1083
  ` ${s.brand('--export')}=${s.dim('FORMAT')} Export results to file ${s.dim('(csv|json) [Team]')}`,
1078
1084
  ` ${s.brand('--push')} Push snapshot to Console queue ${s.dim('[Team]')}`,
1079
- ` ${s.brand('--share')} Generate a 24h share URL ${s.dim('(no login required) [Team]')}`,
1085
+ ` ${s.brand('--share')} Generate a 24h share URL ${s.dim('(recipients need no login) [Team]')}`,
1080
1086
  ` ${s.brand('--all')} Triage all configured profiles at once ${s.dim('[Pro]')}`,
1081
1087
  ` ${s.brand('--save')}=${s.dim('FILE')} Save ANSI-stripped output to file ${s.dim('[Pro]')}`,
1082
1088
  ` ${s.brand('--digest')} POST scored results to digest endpoint ${s.dim('[Pro]')}`,
@@ -1187,8 +1193,8 @@ export function printLedgerHelp({ stream = process.stdout } = {}) {
1187
1193
  '',
1188
1194
  ` ${s.bold(s.brand('ticketlens'))} ${s.bold('ledger')} ${s.dim('[--format=json|csv]')} ${s.dim('[Pro]')}`,
1189
1195
  '',
1190
- ` Export your local usage ledger — a signed, tamper-evident record of`,
1191
- ` billable actions (AI calls, exports, etc). Verifiable offline via an`,
1196
+ ` Export your local compliance ledger — a signed, tamper-evident record of`,
1197
+ ` every compliance check run (Pro). Verifiable offline via an`,
1192
1198
  ` HMAC-SHA256 signature over {records, exportedAt}, keyed at ledger-key.`,
1193
1199
  '',
1194
1200
  ` ${s.bold('OPTIONS')}`,
@@ -1268,7 +1274,7 @@ export function printUpdateSkillHelp({ stream = process.stdout } = {}) {
1268
1274
  ` ${s.bold(s.brand('ticketlens'))} ${s.bold('update-skill')} ${s.dim('[--dry-run] [--path=DIR] [--quiet]')}`,
1269
1275
  '',
1270
1276
  ` Copy the latest /jtb SKILL.md into every detected AI assistant command directory.`,
1271
- ` Runs automatically on ${s.dim('npm install -g ticketlens')} for existing installs.`,
1277
+ ` Runs automatically on ${s.dim('npm i -g ticketlens@beta')} for existing installs.`,
1272
1278
  '',
1273
1279
  ` ${s.bold('SUPPORTED ASSISTANTS')}`,
1274
1280
  '',
@@ -1368,7 +1374,7 @@ export function printStatsHelp({ stream = process.stdout } = {}) {
1368
1374
  ` ${s.bold(s.brand('ticketlens'))} ${s.bold('stats')} ${s.dim('[--days=N] [--format=plain|json] [--profile=NAME]')}`,
1369
1375
  '',
1370
1376
  ` Show response-time and triage-cadence metrics from your local triage history.`,
1371
- ` Reads daily snapshots captured by ${s.brand('ticketlens triage --push')} or the CLI.`,
1377
+ ` Reads daily snapshots saved automatically by every ${s.brand('ticketlens triage')} run.`,
1372
1378
  '',
1373
1379
  ` ${s.bold('OPTIONS')}`,
1374
1380
  '',
@@ -0,0 +1,3 @@
1
+ // Plan prices live in the API's config/tiers.php; never hardcode them in CLI copy.
2
+ export const SUMMARIZE_TIP =
3
+ 'Tip: large briefs compress further — `--summarize` condenses this to a single AI digest (Pro)';