Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
8b9efa1
docs: add interactive command terminal & markdown fixes design spec
chinkan Jul 7, 2026
19362b3
docs: address spec-reviewer recommendations
chinkan Jul 7, 2026
6f342f9
feat: interactive command terminal, verbose mode, entity-based format…
chinkan Jul 7, 2026
c0e20d2
fix: drain pipe reader JoinHandles after child.wait() to capture all …
chinkan Jul 8, 2026
8ddec81
refactor: address code quality review — remove drain timeout, dedupli…
chinkan Jul 8, 2026
21ef6b3
fix: cancel button now reliably kills command and shows toast notific…
chinkan Jul 8, 2026
e77d906
chore: add tokio-util dependency for CancellationToken
chinkan Jul 8, 2026
9c393fb
feat(agent): add cancel_token_registry and pending_injections fields
chinkan Jul 8, 2026
9a55880
feat(agent): add cancel/inject queue public methods
chinkan Jul 8, 2026
e83e11e
feat(agent): add cancellation checks, injection drain, cancel token t…
chinkan Jul 8, 2026
54794ed
fix: distinguish user-cancelled from max-iterations in process_messag…
chinkan Jul 8, 2026
aa3dae2
feat(telegram): add /stop, /btw commands and injection queue for busy…
chinkan Jul 8, 2026
02ab5aa
fix: add /stop and /btw to /start help text, cargo fmt
chinkan Jul 8, 2026
2d043c4
chore: address code review — warn on injection save failure, cargo fm…
chinkan Jul 8, 2026
3f95b9f
feat: steer messages, lightweight /btw, and markdown entities upgrade
chinkan Jul 10, 2026
1c9103f
docs: add design spec for RichBlockTable / sendRichMessage conversion…
chinkan Jul 10, 2026
a3637e3
docs: address spec review — token access, error types, struct definit…
chinkan Jul 10, 2026
44ddd8b
feat(rich): make preprocess_markdown pub(crate) for rich_sender reuse
chinkan Jul 10, 2026
239dcba
feat(rich): add rich_sender module wrapping sendRichMessage API
chinkan Jul 10, 2026
d38ec18
feat(rich): add BOT_TOKEN static and init_bot_token to telegram module
chinkan Jul 10, 2026
4d24da4
feat(rich): make send_markdown_message try sendRichMessage first with…
chinkan Jul 10, 2026
057eb28
feat(rich): update streaming final flush to try sendRichMessage
chinkan Jul 10, 2026
e69d219
test(rich): add chunking unit tests for rich_sender
chinkan Jul 10, 2026
da80474
fix: drain steer messages between tool call iterations
chinkan Jul 10, 2026
a8f1d8b
feat: upgrade /btw to context-forked side query (Claude Code pattern)
chinkan Jul 10, 2026
a464ada
feat: add LoopDetector module with exact-repetition detection
chinkan Jul 10, 2026
c6e4415
feat: add LoopDetectionConfig to agent configuration
chinkan Jul 10, 2026
21bb769
feat: add loop detection callback registry to Agent
chinkan Jul 10, 2026
af09afd
feat: integrate loop detection into main agent loop
chinkan Jul 13, 2026
9dc5847
feat: add callback query handler for loop detection inline keyboard
chinkan Jul 13, 2026
7d37a7e
feat: add loop detection with recovery nudge to subagent loop
chinkan Jul 13, 2026
f85f11e
chore: final integration build for loop detection features
chinkan Jul 13, 2026
bb3de72
fix: address code review issues — clippy, nudge orphan, UTF-8, steer …
chinkan Jul 13, 2026
65b39ec
fix: code review issues — drain_and_inject_steer return, LoopDetector…
chinkan Jul 13, 2026
5dad31b
docs: add scheduled task isolation design spec
chinkan Jul 13, 2026
42b612f
docs: fix spec per review — restore_scheduled_tasks needs same fix, r…
chinkan Jul 13, 2026
fddc657
docs: add scheduled task isolation implementation plan
chinkan Jul 13, 2026
a200d1d
docs: fix plan per review — run ID msg, error logging, 0s delay, line…
chinkan Jul 13, 2026
d015dd1
feat(scheduler): add scheduled_task_runs table for execution history
chinkan Jul 13, 2026
ca024c9
feat(scheduler): add ScheduledTaskRun struct with insert/update/get m…
chinkan Jul 13, 2026
d9cd674
fix(scheduler): isolate scheduled task conversations with dedicated p…
chinkan Jul 13, 2026
ef5edfc
feat(scheduler): add get_scheduled_task_history and rerun_scheduled_t…
chinkan Jul 13, 2026
f603d33
feat(telegram): make send_markdown_message pub for scheduled task use
chinkan Jul 13, 2026
6c21e5e
fix(scheduler): use send_markdown_message and persist run records in …
chinkan Jul 13, 2026
6a976ec
feat(notifier): add friendly_tool_name entries for new scheduled task…
chinkan Jul 13, 2026
10b9ec7
test(scheduler): add unit tests for ScheduledTaskStore run methods
chinkan Jul 13, 2026
448b494
fix: answer callback queries even when no data present
chinkan Jul 14, 2026
b3ccc8c
fix: reformat code to comply with rustfmt
openhands-agent Jul 14, 2026
70a3143
update opencode agent for better CP model
chinkan Jul 14, 2026
d9bd5ab
Installed Skills For Real Engineers
chinkan Jul 14, 2026
5e0a7e5
fix: /btw now runs truly in parallel with correct model ID
chinkan Jul 14, 2026
a5f2294
fix rust fmt issue
chinkan Jul 14, 2026
a401063
feat: extract ToolRegistry, PlatformSender, CancelRegistry, Conversat…
chinkan Jul 15, 2026
7659060
cleanup: strip tools.rs to path validators only, remove legacy shim
chinkan Jul 16, 2026
7ac7c3e
cleanup: remove duplicate tool definitions from Agent, route all_tool…
chinkan Jul 16, 2026
febc712
cleanup: remove RunningCommand, dead functions, and #[allow(dead_code…
chinkan Jul 16, 2026
709854a
cleanup: subagent tool list uses ToolRegistry instead of old inline m…
chinkan Jul 16, 2026
33da87a
fix: restore reload_skills and reload_agents to actually reload from …
chinkan Jul 16, 2026
18b5dc9
fix: restore restart_pending, soul_updated side effects, and try_new_…
chinkan Jul 16, 2026
69de6bb
fix: restore post-loop soul reflection check in process_message
chinkan Jul 16, 2026
bcc9b4f
fix: restore original tool parameter descriptions verbatim
chinkan Jul 16, 2026
df8036b
fix: convert CancelRegistry to async methods, removing blocking_lock …
chinkan Jul 16, 2026
f7821ab
fix: move path validation to skill_tools.rs, restore path traversal p…
chinkan Jul 16, 2026
594cf01
feat: add special_tool_handler and recovery_nudge to AgenticLoop
chinkan Jul 16, 2026
77bd72f
refactor: migrate run_subagent_loop to AgenticLoop with special_tool_…
chinkan Jul 16, 2026
7c3befa
cleanup: remove old execute_tool after AgenticLoop migration
chinkan Jul 16, 2026
b2e208a
cleanup: remove bot from Agent struct, pass as parameter to restore_s…
chinkan Jul 16, 2026
259ff4d
feat(loop): async special tools and model override
chinkan Jul 17, 2026
c212887
style: cargo fmt --all
chinkan Jul 17, 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
78 changes: 78 additions & 0 deletions .agents/skills/ask-matt/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
name: ask-matt
description: Ask which skill or flow fits your situation. A router over the skills in this repo.
disable-model-invocation: true
---

# Ask Matt

You don't remember every skill, so ask.

A **flow** is a path through the skills. Most paths run along one **main flow**, and two **on-ramps** merge onto it. Everything else is standalone, or a vocabulary layer that runs underneath.

## The main flow: idea → ship

The route most work travels. You have an idea and want it built.

1. **`/grill-with-docs`** — sharpen the idea by interview. Start here when you **have a codebase**: it's stateful, retaining what it learns in `CONTEXT.md` and ADRs. (No codebase? Use `/grill-me` — see Standalone. Both run the same `/grilling` primitive; `grill-with-docs` is the one that leaves a paper trail.)
2. **Branch — can you settle every question in conversation?** If a question needs a runnable answer (state, business logic, a UI you have to see), detour through a prototype, bridged by **`/handoff`** in both directions (see Crossing sessions):
- **`/handoff`** out, then open a fresh session against that file,
- **`/prototype`** to answer the question with throwaway code,
- **`/handoff`** back what you learned, and reference it from the original idea thread.
3. **Branch — is this a multi-session build?**
- **Yes****`/to-spec`** (turn the thread into a spec), then **`/to-tickets`** to split it into tracer-bullet tickets, each declaring its **blocking edges**. On a local tracker that's one file per ticket under `.scratch/<feature>/issues/`, worked blockers-first by hand; on a real tracker the edges become native blocking links, so any ticket whose blockers are done can be grabbed — kick off **`/implement`** per ticket, **clearing context between each one**.
- **No****`/implement`** right here, in the same context window.

Either way, **`/implement`** builds each issue by driving **`/tdd`** internally — one red-green slice at a time — then closes out by running **`/code-review`**, a two-axis review (Standards + Spec) of the diff, before committing. Reach for **`/tdd`** on its own when you just want to build a concrete behaviour test-first without a full spec, and **`/code-review`** on its own whenever you want to review a branch or PR against a fixed point.

### Context hygiene

Keep steps 1–3 in **one unbroken context window** — don't compact or clear until after `/to-tickets` — so the grilling, spec, and tickets all build on the same thinking. Each `/implement` then starts fresh, working from the ticket.

The limit on this is the **[smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone)**: the window (~120k tokens on state-of-the-art models) within which the model still reasons sharply. If a session approaches it before `/to-tickets`, don't push on degraded — `/handoff` and continue in a fresh thread.

## On-ramps

A starting situation that generates work, then merges onto the main flow.

- **Bugs and requests piling up****`/triage`**. It moves issues through triage roles and produces agent-ready issues, which **`/implement`** later picks up.

Triage is only for issues **you didn't create** — bug reports, incoming feature requests, anything that arrives raw. Tickets that `/to-tickets` produced are already agent-ready, so **don't triage them**.

- **Something's broken****`/diagnosing-bugs`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression that crept in between two known-good states. It refuses to theorise until it has a **tight feedback loop** — one command that already goes red on *this* bug — then fixes with a regression test. Its post-mortem hands off to **`/improve-codebase-architecture`** when the real finding is that there's no good seam to lock the bug down.

- **A huge, foggy effort — a greenfield project or a huge feature build, too big for one session****`/wayfinder`**, the most cognitively demanding flow here. When the way from here to the destination isn't visible yet, it charts a **shared map** of **decision tickets** on the issue tracker and resolves them one at a time — producing **decisions, not deliverables** — until the fog is pushed back and the way is clear. Where **`/grill-with-docs`** sharpens an idea you can hold in one session, wayfinder is for the idea you can't — and it's slower and denser, so save it for exactly that, never a well-scoped feature.

When the map clears, **it hands off, it doesn't build**: merge onto the main flow at **`/to-spec`**, which collapses the map's linked decisions into a buildable plan, then `/to-tickets` and `/implement` as usual. Looping the map straight into `/implement` skips that collapse and throws the linked detail away — go straight to `/implement` only when the effort turned out genuinely small.

## Codebase health

Not feature work — upkeep.

- **`/improve-codebase-architecture`** — run whenever you have a spare moment to keep the codebase good for agents to operate in. It surfaces **deepening opportunities**; picking one _generates an idea_ you can take into the main flow at `/grill-with-docs`. It's the survey that finds the candidates; **`/codebase-design`** (below) is the bench you design the chosen one on.

## Vocabulary underneath

Two model-invoked references that run *beneath* the other skills — each the single source of truth for its vocabulary. Reach for them directly when the **words**, not the process, are the problem; or let the skills above pull them in.

- **`/domain-modeling`** — sharpen the project's *domain* language: challenge a fuzzy term, resolve an overloaded word ("account" doing three jobs), record a hard-to-reverse decision as an ADR. It's the active discipline `/grill-with-docs` drives to keep `CONTEXT.md` a clean glossary.
- **`/codebase-design`** — the deep-module vocabulary (module, interface, depth, seam, adapter, leverage, locality) for designing a module's *shape*: a lot of behaviour behind a small interface at a clean seam. `/tdd` and `/improve-codebase-architecture` both speak it.

## Crossing sessions

- **`/handoff`** — when a thread is full or you need to branch off (e.g. into a `/prototype` session), this compacts the conversation into a markdown file. You don't continue in place — you **open a new session and reference that file** to carry the context across. It's the bridge between context windows, in either direction. Use it when you want a **fresh session** but need the **current conversation preserved**.
- **`/compact`** (built-in) — stay in the **same conversation**, letting the earlier turns be summarized. Use it at **intentional breaks between phases**, when you don't mind losing the verbatim history. Don't compact mid-phase — the agent can lose its way. `/handoff` forks; `/compact` continues.

## Standalone

Off the main flow entirely.

- **`/grill-me`** — the same relentless interview as `/grill-with-docs`, but for when you have **no codebase**. Stateless: it saves nothing locally, builds no `CONTEXT.md`. Reach for it to sharpen any plan or design that doesn't live in a repo.
- **`/prototype`** — a small, throwaway program that answers one design question: does this state model feel right, or what should this UI look like. Throwaway from day one — keep the answer, delete the code. It's the detour in step 2 of the main flow, but reach for it any time a design question is hard to settle on paper.
- **`/research`** — delegate reading legwork to a **background agent**: it investigates a question against **primary sources**, then leaves a cited Markdown file in the repo. Keep working while it reads. The file it produces is something to take *into* the main flow at `/grill-with-docs` — research feeds the thinking, it doesn't replace it.
- **`/teach`** — learn a concept over multiple sessions, using the current directory as a stateful workspace.
- **`/writing-great-skills`** — reference for writing and editing skills well.

## Precondition

**`/setup-matt-pocock-skills`** — run before your first engineering flow to configure the issue tracker, triage labels, and doc layout the other skills assume. Custom issue trackers also work.
5 changes: 5 additions & 0 deletions .agents/skills/ask-matt/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
interface:
display_name: "Ask Matt"
short_description: "Find the right skill or workflow"
policy:
allow_implicit_invocation: false
89 changes: 89 additions & 0 deletions .agents/skills/code-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
name: code-review
description: Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes — Standards (does the code follow this repo's documented coding standards?) and Spec (does the code match what the originating issue/PRD asked for?). Runs both reviews in parallel sub-agents and reports them side by side. Use when the user wants to review a branch, a PR, work-in-progress changes, or asks to "review since X".
---

Two-axis review of the diff between `HEAD` and a fixed point the user supplies:

- **Standards** — does the code conform to this repo's documented coding standards?
- **Spec** — does the code faithfully implement the originating issue / PRD / spec?

Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings.

The issue tracker should have been provided to you — run `/setup-matt-pocock-skills` if `docs/agents/issue-tracker.md` is missing.

## Process

### 1. Pin the fixed point

Whatever the user said is the fixed point — a commit SHA, branch name, tag, `main`, `HEAD~5`, etc. If they didn't specify one, ask for it.

Capture the diff command once: `git diff <fixed-point>...HEAD` (three-dot, so the comparison is against the merge-base). Also note the list of commits via `git log <fixed-point>..HEAD --oneline`.

Before going further, confirm the fixed point resolves (`git rev-parse <fixed-point>`) and the diff is non-empty. A bad ref or empty diff should fail here — not inside two parallel sub-agents.

### 2. Identify the spec source

Look for the originating spec, in this order:

1. Issue references in the commit messages (`#123`, `Closes #45`, GitLab `!67`, etc.) — fetch via the workflow in `docs/agents/issue-tracker.md`.
2. A path the user passed as an argument.
3. A PRD/spec file under `docs/`, `specs/`, or `.scratch/` matching the branch name or feature.
4. If nothing is found, ask the user where the spec is. If they say there isn't one, the **Spec** sub-agent will skip and report "no spec available".

### 3. Identify the standards sources

Anything in the repo that documents how code should be written, such as `CODING_STANDARDS.md` or `CONTRIBUTING.md`.

On top of whatever the repo documents, the Standards axis always carries the **smell baseline** below — a fixed set of Fowler code smells (_Refactoring_, ch.3) that applies even when a repo documents nothing. Two rules bind it:

- **The repo overrides.** A documented repo standard always wins; where it endorses something the baseline would flag, suppress the smell.
- **Always a judgement call.** Each smell is a labelled heuristic ("possible Feature Envy"), never a hard violation — and, like any standard here, skip anything tooling already enforces.

Each smell reads *what it is**how to fix*; match it against the diff:

- **Mysterious Name** — a function, variable, or type whose name doesn't reveal what it does or holds. → rename it; if no honest name comes, the design's murky.
- **Duplicated Code** — the same logic shape appears in more than one hunk or file in the change. → extract the shared shape, call it from both.
- **Feature Envy** — a method that reaches into another object's data more than its own. → move the method onto the data it envies.
- **Data Clumps** — the same few fields or params keep travelling together (a type wanting to be born). → bundle them into one type, pass that.
- **Primitive Obsession** — a primitive or string standing in for a domain concept that deserves its own type. → give the concept its own small type.
- **Repeated Switches** — the same `switch`/`if`-cascade on the same type recurs across the change. → replace with polymorphism, or one map both sites share.
- **Shotgun Surgery** — one logical change forces scattered edits across many files in the diff. → gather what changes together into one module.
- **Divergent Change** — one file or module is edited for several unrelated reasons. → split so each module changes for one reason.
- **Speculative Generality** — abstraction, parameters, or hooks added for needs the spec doesn't have. → delete it; inline back until a real need shows.
- **Message Chains** — long `a.b().c().d()` navigation the caller shouldn't depend on. → hide the walk behind one method on the first object.
- **Middle Man** — a class or function that mostly just delegates onward. → cut it, call the real target direct.
- **Refused Bequest** — a subclass or implementer that ignores or overrides most of what it inherits. → drop the inheritance, use composition.

### 4. Spawn both sub-agents in parallel

Send a single message with two `Agent` tool calls. Use the `general-purpose` subagent for both.

**Standards sub-agent prompt** — include:

- The full diff command and commit list.
- The list of standards-source files you found in step 3, **plus the smell baseline from step 3** pasted in full — the sub-agent has no other access to it.
- The brief: "Report — per file/hunk where relevant — (a) every place the diff violates a documented standard: cite the standard (file + the rule); and (b) any baseline smell you spot: name it and quote the hunk. Distinguish hard violations from judgement calls — documented-standard breaches can be hard, but baseline smells are always judgement calls, and a documented repo standard overrides the baseline. Skip anything tooling enforces. Under 400 words."

**Spec sub-agent prompt** — include:

- The diff command and commit list.
- The path or fetched contents of the spec.
- The brief: "Report: (a) requirements the spec asked for that are missing or partial; (b) behaviour in the diff that wasn't asked for (scope creep); (c) requirements that look implemented but where the implementation looks wrong. Quote the spec line for each finding. Under 400 words."

If the spec is missing, skip the Spec sub-agent and note this in the final report.

### 5. Aggregate

Present the two reports under `## Standards` and `## Spec` headings, verbatim or lightly cleaned. Do **not** merge or rerank findings — the two axes are deliberately separate (see _Why two axes_).

End with a one-line summary: total findings per axis, and the worst issue _within each axis_ (if any). Don't pick a single winner across axes — that's the reranking the separation exists to prevent.

## Why two axes

A change can pass one axis and fail the other:

- Code that follows every standard but implements the wrong thing → **Standards pass, Spec fail.**
- Code that does exactly what the issue asked but breaks the project's conventions → **Spec pass, Standards fail.**

Reporting them separately stops one axis from masking the other.
3 changes: 3 additions & 0 deletions .agents/skills/code-review/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
interface:
display_name: "Code Review"
short_description: "Review a diff on standards and spec"
37 changes: 37 additions & 0 deletions .agents/skills/codebase-design/DEEPENING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Deepening

How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [SKILL.md](SKILL.md)**module**, **interface**, **seam**, **adapter**.

## Dependency categories

When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.

### 1. In-process

Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed.

### 2. Local-substitutable

Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.

### 3. Remote but owned (Ports & Adapters)

Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.

Recommendation shape: *"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."*

### 4. True external (Mock)

Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.

## Seam discipline

- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.

## Testing strategy: replace, don't layer

- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them.
- Write new tests at the deepened module's interface. The **interface is the test surface**.
- Tests assert on observable outcomes through the interface, not internal state.
- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.
Loading
Loading