Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b8e5d12
Add PLAN.md for SDK-typed reasoning effort selection across providers
rafaeelricco Apr 21, 2026
2690590
Update AI SDK dependencies to pinned versions
rafaeelricco Apr 22, 2026
ed30f26
Remove PLAN.md implementation specification document
rafaeelricco Apr 22, 2026
5e39388
Add reasoning effort configuration for LLM providers
rafaeelricco Apr 23, 2026
63a436c
Add optional git metadata lookup helpers and simplify push rendering
rafaeelricco Apr 23, 2026
9b89a8e
Refactor `execBin` to return `Result`-based `ExecResult`
rafaeelricco Apr 23, 2026
150f74a
Remove immutable snapshots guidance from conventions
rafaeelricco Apr 24, 2026
dab95ec
Simplify effort value declarations in config
rafaeelricco Apr 24, 2026
1c8123f
Increase Prettier `printWidth` to 154 and reformat codebase
rafaeelricco Apr 24, 2026
37966b7
Rename `withAuthMethod` to `resolveAuthMethod`
rafaeelricco Apr 25, 2026
aa871cb
Remove obsolete TODO comment in auth resolver
rafaeelricco Apr 25, 2026
21e29c5
Simplify response parser to a provider-agnostic text shape
rafaeelricco Apr 25, 2026
d574fe6
Simplify Anthropic effort handling with always-on adaptive thinking
rafaeelricco Apr 25, 2026
e451d75
Refactor Anthropic client to use native SDK types and raise token limit
rafaeelricco Apr 25, 2026
0f9a3c6
Simplify LLM effort handling and remove fallback retries
rafaeelricco Apr 25, 2026
29ce9c2
Add doctor timing output and improve base branch resolution
rafaeelricco Apr 25, 2026
9d89ded
Remove hardcoded LLM output token limits
rafaeelricco Apr 25, 2026
1c13110
Add `MVar` and `Queue` concurrency primitives
rafaeelricco Apr 25, 2026
783feb5
Update Gemini to use streaming responses
rafaeelricco Apr 25, 2026
13f9b58
Use streaming OpenAI responses for all auth methods
rafaeelricco Apr 25, 2026
579e936
Switch Anthropic client to streaming API using `finalMessage`
rafaeelricco Apr 25, 2026
5e7cc38
Reformat arrow functions and trailing commas in MVar and Queue
rafaeelricco Apr 25, 2026
ee1024a
Expand documentation for `MVar`, `Queue`, and `MQueue`
rafaeelricco Apr 25, 2026
0660129
Refactor commit metadata parsing with NUL-separated fields and decoders
rafaeelricco Apr 25, 2026
a1e2309
Refactor `classifyFailure` to use named regex helpers and cache stder…
rafaeelricco Apr 25, 2026
6ea0793
Change default Anthropic and Gemini effort to medium
rafaeelricco Apr 25, 2026
386e74b
Refactor UI pickers to use `Future.create` instead of wrapping Promises
rafaeelricco Apr 25, 2026
b27d5c8
Move `resolveAuthMethod` to config module and refactor token updates
rafaeelricco Apr 25, 2026
742054f
Expand comments explaining lazy Ink/React imports in pickers
rafaeelricco Apr 25, 2026
a5eddc3
Lowercase effort slider labels
rafaeelricco Apr 25, 2026
1155dbe
Show Google OAuth completion notice
rafaeelricco Apr 25, 2026
f0ab8a3
Add shared fuzzy search for model selection
rafaeelricco Apr 25, 2026
2aaac34
Update package version to 0.2.5
rafaeelricco Apr 25, 2026
945d23e
Inline Anthropic max token limit
rafaeelricco Apr 25, 2026
7eec983
Add commit effort command for updating reasoning effort
rafaeelricco Apr 25, 2026
bd4514c
Use package version for CLI version output
rafaeelricco Apr 25, 2026
9c660dc
Let pending writer trigger complete MVar handoff
rafaeelricco Apr 25, 2026
86d467f
Preserve current effort when cancelling effort picker
rafaeelricco Apr 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"printWidth": 120,
"printWidth": 154,
"proseWrap": "preserve",
"semi": true,
"singleQuote": false,
Expand Down
94 changes: 64 additions & 30 deletions CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,10 @@ Favour static types, explicit data flow, immutability, pure functions, compositi
s.variant({ type: "error", message: s.string, code: s.optional(s.string) })
]);
```
- Embed **immutable snapshots** in event payloads when referencing mutable external data (e.g. a property's price at the time of booking).
```ts
const schema_UserActions = s.discriminatedUnion([
s.variant({ type: "view_property", property: schema_PropertySnapshot }),
s.variant({ type: "start_booking", property: schema_PropertySnapshot, draftId: DraftId.schema }),
s.variant({ type: "confirm_booking", draftId: DraftId.schema })
]);
```
- Bundle related state into **union-driven state machines**. Don't use loose boolean flags (`isStreaming`, `isError`, `isLoading`) spread across stores.

```ts
type Stream<E, R> =
| { type: "not_started" }
| { type: "streaming"; results: R[] }
| { type: "done"; results: R[] }
| { type: "error"; error: E };
type Stream<E, R> = { type: "not_started" } | { type: "streaming"; results: R[] } | { type: "done"; results: R[] } | { type: "error"; error: E };

type VoiceConnection =
| { type: "disconnected" }
Expand Down Expand Up @@ -232,28 +220,74 @@ Use `Future<E, T>` instead of `Promise` for lazy, cancelable async.
- `TreeSet`: `.insert()`, `.remove()`, `.union()` mutate in place. Use `TreeSet.from()` to clone first.
- Use `.has()` for O(log n) membership. Don't use `.values().includes()` — that's O(n).

### MVar & BoundedBuffer — Async Coordination
### MVar — Async Coordination

- Use `MVar<T>` for async synchronization. `put(v)` blocks if full, `take()` blocks if empty. Resolves in FIFO order.
- Use `BoundedBuffer<T>` for backpressure queues with max capacity. `enqueue(v)` blocks if full, `dequeue()` blocks if empty.
Use `MVar<T>` to coordinate concurrent operations through a single mutable cell. Operations are FIFO — fair across waiters, no starvation.

```ts
const textBuffer = new BoundedBuffer<string>(100);
const endSignal = MVar.newEmpty<null>();
- Construct with `MVar.new(v)` (full) or `MVar.newEmpty()` (empty).
- `put`/`take`/`read`/`modify` block; `tryPut`/`tryTake`/`tryRead` don't.
- `tryPut` returns `boolean`; `tryTake`/`tryRead` return `Maybe<A>` — pattern match with `instanceof Just` / `instanceof Nothing`.
- Use `modify` (not external locks) to atomically transform shared state — the original value is restored if the callback rejects.
- Don't busy-loop on `tryTake` — use `take` to block.
- Don't use boolean flags or unbounded arrays for "done" state — use `MVar` to block until populated.

model.onToken((token) => {
textBuffer.enqueue(token);
});
model.onDone(() => {
endSignal.put(null);
});
**Completion/error latch with `MVar<Maybe<Error>>`** — surface either "closed cleanly" or "closed with error" from a callback-based lifecycle.

for await (const text of iterable) {
await ttsService.synthesize(text);
}
```
```ts
const done: MVar<Maybe<Error>> = MVar.newEmpty();
ws.on("close", () => {
done.tryPut(Nothing());
});
ws.on("error", (err) => {
done.tryPut(Just(err));
});

const result = await done.take();
if (result instanceof Just) throw result.value;
```

- Don't use unbounded arrays for streaming — memory leak risk. Don't use boolean flags for "done" state — use `MVar` to block until populated.
**End-of-stream latch with `MVar<null>`** — convert a callback-based "done" signal into a value awaitable from an `AsyncIterable`.

```ts
const end = MVar.newEmpty<null>();
const finished = end.take();

const iterable: AsyncIterable<string> = {
[Symbol.asyncIterator]() {
return {
next: () => Promise.race([buffer.dequeue().then((value) => ({ done: false, value })), finished.then(() => ({ done: true, value: undefined }))])
};
}
};

model.onDone(() => {
end.put(null);
});
```

**Atomic state via `modify`** — guard mutable shared state with automatic rollback on rejection.

```ts
const counter = MVar.new(0);
const previous = await counter.modify(async (n) => [n + 1, n]);
```

### Queue & MQueue — Functional Queues

- Use `Queue<T>` for immutable persistent queues — `enqueue` returns a new `Queue`; safe to share across async boundaries without copying.
- Use `MQueue<T>` for transient producer-consumer queues where mutation is local (e.g. waiter queues inside coordination primitives).
- `Queue.dequeue()` returns `Maybe<[T, Queue<T>]>`; `MQueue.dequeue()` returns `Maybe<T>` — both must be pattern-matched.
- Both have amortised O(1) enqueue/dequeue via two-list (front/back) representation.
- Don't reach for native `Array.shift()` for FIFO — it's O(n) and mutates.

```ts
let q = Queue.fromArray([1, 2, 3]);
const r = q.dequeue();
if (r instanceof Just) {
const [head, rest] = r.value;
q = rest; // thread the new queue
}
```

---

Expand Down
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# commit-tools

[![Version](https://img.shields.io/badge/version-0.2.0-blue.svg)](#)
[![Version](https://img.shields.io/badge/version-0.2.5-blue.svg)](#)

Writing good commit messages _can_ have a high cognitive cost, especially when you make dozens of commits a day. That energy should be directed toward solving hard problems and shipping features, not summarizing them.

Expand Down Expand Up @@ -113,6 +113,12 @@ After setup, you can switch AI models from your configured provider at any time:
commit model
```

This flow also lets you adjust the reasoning effort for the chosen model. If the model is already the one you want and you only need to change the effort level, run:

```bash
commit effort
```

### 3. Generate a Commit

Stage your changes, then run:
Expand Down Expand Up @@ -144,16 +150,17 @@ To see all available commands at any time, run:
commit --help
```

| Command | Description |
| ------------------------ | ---------------------------------------- |
| `commit` | Generate a commit message (default) |
| `commit generate` | Generate a commit message |
| `commit setup` | Configure authentication and conventions |
| `commit login` | Alias for setup — re-authenticate |
| `commit doctor` | Check installation and environment |
| `commit model` | Select a different AI model |
| `commit --version`, `-v` | Show version |
| `commit --help`, `-h` | Show help |
| Command | Description |
| ------------------------ | ------------------------------------------------- |
| `commit` | Generate a commit message (default) |
| `commit generate` | Generate a commit message |
| `commit setup` | Configure authentication and conventions |
| `commit login` | Alias for setup — re-authenticate |
| `commit doctor` | Check installation and environment |
| `commit model` | Select a different AI model |
| `commit effort` | Adjust the reasoning effort for the current model |
| `commit --version`, `-v` | Show version |
| `commit --help`, `-h` | Show help |

## Providers

Expand Down
3 changes: 3 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Commit } from "@/cli/commit";
import { Setup } from "@/cli/setup";
import { Doctor } from "@/cli/doctor";
import { ModelCommand } from "@/cli/model";
import { EffortCommand } from "@/cli/effort";
import { parseArgs, showHelp, showVersion } from "@/cli/parser";
import { Future } from "@/libs/future";

Expand All @@ -26,6 +27,8 @@ const main = () => {
return Doctor.create().run();
case "model":
return ModelCommand.create().chain((m) => m.run());
case "effort":
return EffortCommand.create().chain((e) => e.run());
case "version":
showVersion();
return Future.resolve(undefined);
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rafaeelricco/commit-tools",
"version": "0.2.4",
"version": "0.2.5",
"type": "module",
"bin": {
"commit": "./dist/index.js"
Expand Down Expand Up @@ -45,17 +45,17 @@
"typescript": "^5"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.87.0",
"@anthropic-ai/sdk": "0.90.0",
"@clack/prompts": "^1.0.1",
"@google/generative-ai": "^0.24.1",
"@google/genai": "1.50.1",
"chalk": "^5.6.2",
"cli-table3": "^0.6.5",
"fluture": "^14.0.0",
"google-auth-library": "^10.5.0",
"ink": "^6.8.0",
"luxon": "^3.7.2",
"open": "^11.0.0",
"openai": "^6.25.0",
"openai": "6.34.0",
"picocolors": "^1.1.1",
"react": "^19.2.4"
}
Expand Down
Loading