Skip to content

Commit 23292a4

Browse files
Shahinyanmclaude
andcommitted
feat(v0.9.0)!: remove cli backend entirely + clean docs
BREAKING: --backend=cli is gone. The v0.8.0 deprecation alias is deleted along with the ClaudeCliClassifier implementation, the --classifier-command flag, and the TJ_CLASSIFIER_CLI env-var write path. Uninstall still strips the legacy env key (back-compat). Why now: claude -p no longer rides the Pro/Max subscription — running it bills tokens separately. Keeping the alias one release was enough notice; carrying a deprecated free-fallback that isn't free is a worse contract than just removing it. Removed: - crates/tj-core/src/classifier/cli.rs (ClaudeCliClassifier + tests) - crates/tj-core/tests/classifier_eval.rs + fixtures dir - InstallHooks::classifier_command flag - TJ_CLASSIFIER_CLI write on install (strip-on-uninstall kept) - Two install_hooks tests that exercised the removed flag Public API: - tj_core::classifier::cli module — gone - --backend=cli value — now errors out - --classifier-command flag — gone Docs: - README: rewritten around the hybrid model (heuristic + API), no more `claude -p` / subscription references. Env-var table trimmed to ANTHROPIC_API_KEY + TJ_CLASSIFIER_MODEL. - SKILL.md: drop the Pro/Max subscription claim. - PreCompact description updated to mention transcript catch-up. Tests: 64 cli integration + 158 core lib pass (2 pre-existing migrate_project /tmp canonicalization failures unrelated). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ef39514 commit 23292a4

15 files changed

Lines changed: 105 additions & 716 deletions

File tree

CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.9.0] - 2026-05-17
11+
12+
**Breaking — `cli` backend removed.** v0.8.0 left it as a deprecated
13+
alias for `hybrid`; v0.9.0 deletes the implementation. With it goes
14+
the `--classifier-command` flag, the `TJ_CLASSIFIER_CLI` env var
15+
(only the back-compat strip on uninstall stays), and the
16+
`ClaudeCliClassifier` struct.
17+
18+
If you upgraded to v0.8.0 you saw a one-line deprecation warning on
19+
every hook; that's the whole migration story. On v0.9.0 the value
20+
`--backend=cli` errors with `unknown backend: cli (expected hybrid,
21+
api, or heuristic)`.
22+
23+
### Removed
24+
- `tj_core::classifier::cli::ClaudeCliClassifier` and the entire
25+
`tj_core::classifier::cli` module.
26+
- `crates/tj-core/tests/classifier_eval.rs` and its fixtures — the
27+
eval harness depended on `ClaudeCliClassifier`.
28+
- `task-journal install-hooks --classifier-command <CMD>` flag.
29+
- `TJ_CLASSIFIER_CLI` env var write on install. The variable is
30+
still **stripped** on `--uninstall` to clean up settings.json from
31+
pre-0.9 installs (back-compat).
32+
- Default value handling for `--backend=cli`. It now hits the
33+
generic `unknown backend` error path.
34+
35+
### Documentation
36+
- README rewritten around the hybrid model — heuristic stage
37+
characterized, API stage as the optional fallback, no `claude -p`
38+
references anywhere.
39+
- Plugin skill description (`SKILL.md`) drops the Pro/Max
40+
subscription claim that was no longer true.
41+
- Configuration table trimmed to the two env vars that still matter:
42+
`ANTHROPIC_API_KEY` and `TJ_CLASSIFIER_MODEL`.
43+
44+
### Migration
45+
- Re-run `task-journal install-hooks --scope user` to refresh
46+
`~/.claude/settings.json` without the legacy `--backend=cli` flag
47+
and without `TJ_CLASSIFIER_CLI` in `env`.
48+
- If you want the API stage (recommended for full coverage), set
49+
`ANTHROPIC_API_KEY` in your shell or in the same `settings.json`
50+
`env` block.
51+
1052
## [0.8.0] - 2026-05-17
1153

1254
**Breaking — classifier backend reshaped.** Anthropic changed `claude -p`

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ members = [
77
]
88

99
[workspace.package]
10-
version = "0.8.0"
10+
version = "0.9.0"
1111
edition = "2021"
1212
rust-version = "1.88"
1313
license = "MIT"

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ That's it. Restart Claude Code, start working, and the journal fills itself.
7575

7676
## How it works
7777

78-
- **Auto-capture via Claude Code hooks.** Every prompt, tool call, and Claude reply is classified by a small `claude -p` invocation and appended as a typed event (`finding` / `decision` / `evidence` / `rejection` / …). Hook returns in <100 ms — classification happens in the background, never blocks your session.
78+
- **Auto-capture via Claude Code hooks.** Every prompt, tool call, and Claude reply runs through a two-stage classifier and lands as a typed event (`finding` / `decision` / `evidence` / `rejection` / …). Stage 1 is a fast in-process heuristic — pattern-matches obvious phrasing in EN+RU for zero cost. Stage 2 falls back to the Anthropic API (`ANTHROPIC_API_KEY`) only when the heuristic is uncertain. Hook returns in <100 ms — both stages run in a detached background worker, never blocking your session.
7979
- **Artifact extraction.** Each event scans its text for commit hashes, PR URLs, file paths, issue IDs, and branch names. Aggregated artifacts are how Task Journal links related tasks: when you start a new task touching the same issue or file, the prior task is surfaced automatically.
8080
- **Resume packs.** `task_pack` (MCP tool or CLI) renders a task into a compact Markdown briefing — Goal, Outcome, decisions, rejections, evidence, artifacts — that fits in a fresh agent's context window without dumping the raw event log.
81-
- **Auto-capture boundaries.** Beyond per-event capture, two extra hooks mark *reasoning boundaries* automatically: `PreCompact` drops a marker decision when Claude Code is about to compact, and a `/rewind`-prefixed prompt appends a single correction event so pack readers see where the user rolled back. No mass-rejection of prior events — the boundary is a sentinel, not a rewrite.
81+
- **Auto-capture boundaries.** Beyond per-event capture, two extra hooks mark *reasoning boundaries* automatically. On `PreCompact`, Task Journal reads the transcript JSONL tail (entries newer than the active task's last event) and enqueues anything the synchronous hooks missed before the compact — then drops a marker decision so the post-compact agent sees a clear cut. A `/rewind`-prefixed prompt appends a single correction event so pack readers see where the user rolled back. No mass-rejection of prior events — the boundary is a sentinel, not a rewrite.
8282

83-
Source of truth is an append-only JSONL log per project. SQLite holds derived state and is fully rebuildable. Nothing is sent off-machine except the classifier prompt to your own `claude -p` (subscription) or the Anthropic API.
83+
Source of truth is an append-only JSONL log per project. SQLite holds derived state and is fully rebuildable. Nothing is sent off-machine except the classifier prompt to the Anthropic API — and only when the local heuristic is uncertain. With no `ANTHROPIC_API_KEY` set, Task Journal still works: the heuristic handles the obvious cases, and anything it can't classify sits in the local pending queue for later retry.
8484

8585
### Statusline integration
8686

@@ -183,9 +183,8 @@ The MCP server exposes five tools to Claude Code (and any MCP client):
183183

184184
| Env var | Effect | Default |
185185
|---------|--------|---------|
186-
| `TJ_CLASSIFIER_CLI` | Classifier command. When set to bare `claude`, Task Journal automatically injects `--strict-mcp-config --mcp-config '{"mcpServers":{}}'` so the inner classifier session does not load every plugin in your settings (keeps classifier startup fast). | `claude` |
187-
| `TJ_CLASSIFIER_MODEL` | Model alias for the classifier (`claude -p` or HTTP API). | `haiku` (CLI) / `claude-haiku-4-5-20251001` (API) |
188-
| `ANTHROPIC_API_KEY` | Required for `--backend=api` (HTTP classifier). | _unset_ |
186+
| `ANTHROPIC_API_KEY` | Powers the API stage of `--backend=hybrid` (default) and is required for `--backend=api`. Without it, only the offline heuristic runs and ambiguous chunks land in the local pending queue. | _unset_ |
187+
| `TJ_CLASSIFIER_MODEL` | Override the Anthropic model used by the API stage. | `claude-haiku-4-5-20251001` |
189188
| `TJ_AUTO_OPEN_TASKS` | Set to `0` / `false` to disable auto-opening a task from `UserPromptSubmit` when no open task exists. | `1` |
190189

191190
## Event types

crates/tj-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ name = "task-journal"
1616
path = "src/main.rs"
1717

1818
[dependencies]
19-
tj-core = { package = "task-journal-core", version = "0.8.0", path = "../tj-core" }
19+
tj-core = { package = "task-journal-core", version = "0.9.0", path = "../tj-core" }
2020
anyhow = { workspace = true }
2121
clap = { workspace = true }
2222
tracing = { workspace = true }

crates/tj-cli/src/main.rs

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -718,11 +718,6 @@ enum Commands {
718718
/// Remove our hook entries instead of installing.
719719
#[arg(long)]
720720
uninstall: bool,
721-
/// Override classifier command. Writes env.TJ_CLASSIFIER_CLI into settings.json
722-
/// so wrappers (aimux, litellm, etc.) work without manual env setup.
723-
/// Default: classifier uses `claude -p`.
724-
#[arg(long)]
725-
classifier_command: Option<String>,
726721
/// After installing hooks, retro-import existing Claude Code session
727722
/// history for the current project. Equivalent to running
728723
/// `task-journal backfill` afterwards. Onboarding shortcut.
@@ -1271,7 +1266,6 @@ fn main() -> Result<()> {
12711266
Commands::InstallHooks {
12721267
scope,
12731268
uninstall,
1274-
classifier_command,
12751269
backfill,
12761270
} => {
12771271
let settings_path = match scope.as_str() {
@@ -1388,23 +1382,6 @@ fn main() -> Result<()> {
13881382
"PreCompact": [{ "matcher": "", "hooks": [{ "type": "command", "command": cmd }] }],
13891383
});
13901384
hooks_obj.insert("hooks".into(), entries);
1391-
1392-
// Optional: set env.TJ_CLASSIFIER_CLI for users running classifier
1393-
// through a wrapper (aimux, litellm, etc.). Claude Code reads this
1394-
// env block and propagates the var to hook subprocesses, so users
1395-
// don't need to mess with bashrc.
1396-
if let Some(cmd) = classifier_command {
1397-
let env = hooks_obj
1398-
.entry("env".to_string())
1399-
.or_insert_with(|| serde_json::json!({}));
1400-
let env_obj = env
1401-
.as_object_mut()
1402-
.ok_or_else(|| anyhow::anyhow!("settings.env is not a JSON object"))?;
1403-
env_obj.insert(
1404-
"TJ_CLASSIFIER_CLI".to_string(),
1405-
serde_json::Value::String(cmd),
1406-
);
1407-
}
14081385
}
14091386
std::fs::write(&settings_path, serde_json::to_string_pretty(&current)?)?;
14101387
println!("{}", settings_path.display());
@@ -1806,18 +1783,6 @@ fn main() -> Result<()> {
18061783
"hybrid" | "" => Box::new(
18071784
tj_core::classifier::hybrid::HybridClassifier::from_env(),
18081785
),
1809-
// Legacy alias — keeps existing settings.json + plugin
1810-
// hooks installed by 0.7.x working without manual edits.
1811-
// Same dispatch as hybrid; the warning lands on stderr
1812-
// which Claude Code captures into hook logs.
1813-
"cli" => {
1814-
eprintln!(
1815-
"warn: --backend=cli is deprecated (claude -p no longer rides Pro/Max). Routing to --backend=hybrid."
1816-
);
1817-
Box::new(
1818-
tj_core::classifier::hybrid::HybridClassifier::from_env(),
1819-
)
1820-
}
18211786
"api" => {
18221787
Box::new(tj_core::classifier::http::AnthropicClassifier::from_env()?)
18231788
}
@@ -3127,9 +3092,31 @@ fn process_pending_entry(
31273092

31283093
use tj_core::classifier::Classifier;
31293094
let classifier: Box<dyn Classifier> = match backend {
3130-
"cli" => Box::new(tj_core::classifier::cli::ClaudeCliClassifier::default()),
3095+
"hybrid" | "" => {
3096+
Box::new(tj_core::classifier::hybrid::HybridClassifier::from_env())
3097+
}
31313098
"api" => Box::new(tj_core::classifier::http::AnthropicClassifier::from_env()?),
3132-
other => anyhow::bail!("unknown backend: {other}"),
3099+
"heuristic" => {
3100+
use tj_core::classifier::heuristic::try_heuristic;
3101+
use tj_core::classifier::{ClassifyInput, ClassifyOutput};
3102+
struct HeuristicOnly;
3103+
impl Classifier for HeuristicOnly {
3104+
fn classify(
3105+
&self,
3106+
input: &ClassifyInput,
3107+
) -> anyhow::Result<ClassifyOutput> {
3108+
try_heuristic(input).ok_or_else(|| {
3109+
anyhow::anyhow!(
3110+
"heuristic uncertain (heuristic-only mode has no LLM fallback)"
3111+
)
3112+
})
3113+
}
3114+
}
3115+
Box::new(HeuristicOnly)
3116+
}
3117+
other => anyhow::bail!(
3118+
"unknown backend: {other} (expected `hybrid`, `api`, or `heuristic`)"
3119+
),
31333120
};
31343121
let input = tj_core::classifier::ClassifyInput {
31353122
text: text.clone(),

0 commit comments

Comments
 (0)