Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
46cc445
chore(tasks): add blackbox port status ledger for agent enhancement plan
elkaix Jun 15, 2026
5ce53ee
feat(soul): inject bounded git snapshot into root agent prompt
elkaix Jun 15, 2026
cb43064
chore(tasks): update blackbox port ledger for phase 1-2 progress
elkaix Jun 15, 2026
3d8f042
fix(soul): hand off on proactive compaction failure
elkaix Jun 15, 2026
e0badc9
test(soul): close remaining tool fidelity gaps
elkaix Jun 15, 2026
dd40c4b
feat(recall): read bounded prior-session windows
elkaix Jun 15, 2026
7407cca
chore(tasks): close memory and skill port rows
elkaix Jun 15, 2026
ec328ed
feat(mcp): invoke connected server prompts
elkaix Jun 15, 2026
997b03d
fix(plan): require verification in plan handoff guidance
elkaix Jun 15, 2026
3d1cd72
chore(tasks): record verified observability rows
elkaix Jun 15, 2026
f7d96ae
chore(tasks): reconcile Phase 5-6 audit rows
elkaix Jun 15, 2026
07f9b71
feat(ui): show current todos in compaction block instead of a random tip
elkaix Jun 15, 2026
30da45c
feat(agent): harden MCP config, telemetry, and shell steering UX
elkaix Jun 15, 2026
8db3a1e
feat(agent): MCP lifecycle, review fixes, and transport handoff
elkaix Jun 15, 2026
1328b21
test: update snapshots for InvokeMcpPrompt, compaction config, and gi…
elkaix Jun 15, 2026
c6810a9
fix(ui): address clean-code-guard findings for agent behaviour tweaks
elkaix Jun 15, 2026
59c3102
feat(agent): close blackbox checkpoints and polish shell UX
elkaix Jun 15, 2026
48da1ab
feat(agent): add plugin discovery and MCP live refresh
elkaix Jun 15, 2026
dd3dadb
feat(agent): load plugin MCP servers
elkaix Jun 15, 2026
d346657
docs(agents): add Feature Development Standard to AGENTS.md
elkaix Jun 16, 2026
7eb27f2
feat(plugin): add marketplace installs and activation policy
elkaix Jun 16, 2026
687046e
feat(plugin): auto-detect external plugins via two-flag policy
elkaix Jun 16, 2026
5662e3f
feat(plugin): add enable/disable CLI, marketplace install, and plugin…
elkaix Jun 16, 2026
49b08de
fix(mcp): harden MCP tool registry lifecycle and address PR #155 revi…
elkaix Jun 16, 2026
1085e95
feat(plugin): resolve plugin dependencies (load demotion + install cl…
elkaix Jun 16, 2026
7b6e4a8
feat(plugin): substitute user_config values in MCP and hook artifacts
elkaix Jun 16, 2026
aba9a99
fix(plugin): address CodeRabbit review findings on PR #155
elkaix Jun 16, 2026
b06189c
fix: address remaining CodeRabbit findings on PR #155 (MCP, eval, UI)
elkaix Jun 16, 2026
ae99189
fix: resolve PR #155 CI build and CodeRabbit review findings
elkaix Jun 16, 2026
0b2ff57
docs(plip): add PLIP-10 LSP system proposal
elkaix Jun 16, 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
18 changes: 18 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ Pythinker CLI is a Python CLI agent for software engineering workflows. It suppo
interactive shell UI, ACP server mode for IDE integrations, MCP tool loading, background work,
subagents, skills, web/visualization UIs, and multi-provider LLM authentication.

## Feature Development Standard

Build every feature as production code, not a happy-path demo. **Before implementing**, answer:
what the feature does, who or what calls it, its inputs, its outputs and side effects, how it can
fail, what happens on failure, which edge cases apply, and what test proves it works. If
requirements are ambiguous, make the safest reasonable assumption and document it — only block when
the missing detail would change the implementation.

**Handle the failure and edge cases**, not just the happy path: missing / empty / invalid /
malformed input, unauthorized access, expired tokens, timeouts and network errors, partial success,
concurrent or duplicate requests, rate limits, large payloads, stale cache, missing records, retry
exhaustion, cancellation, and rollback/cleanup failure. Never silently ignore an unexpected state.

**Make errors explicit**: typed or categorized, logged with actionable context, recoverable where
possible, and safe to surface — never leaking secrets, tokens, or stack traces. No bare
`except`/catch that swallows the error. For feature work this restates the Failure truthfulness
contract and C01–C15 tripwires below; ship the matching tests and verification with the feature.

## Non-negotiable rules

- **Use `uv` for Python commands.** Prefer `make ...` targets; if running tools directly, use
Expand Down
62 changes: 62 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,46 @@ GitHub Releases page; `0.8.0` is the new starting line.

## Unreleased

- **Plugin marketplaces and activation policy.** `pythinker plugin marketplace` can add,
refresh, install, and uninstall Claude/Codex-compatible marketplace plugins. Plugins
installed for Claude Code or Codex are auto-detected (no symlink): their safe artifacts
(skills, commands, agents) activate by default, while executable artifacts (hooks, MCP
servers) stay opt-in. Config `plugins.discover_external`, `plugins.external_exec`,
`plugins.enabled`, and `plugins.disabled` — plus `pythinker plugin enable/disable <name>`
— control which installed plugins contribute artifacts. Hook and MCP commands
expand `${CLAUDE_PLUGIN_ROOT}`/`${PYTHINKER_PLUGIN_ROOT}` and
`${CLAUDE_PLUGIN_DATA}`/`${PYTHINKER_PLUGIN_DATA}`.
- **Plugin dependencies.** Plugins may declare `dependencies`; installing one pulls its
transitive dependencies from the same marketplace (cross-marketplace deps are blocked),
and a plugin whose dependencies aren't present+enabled is disabled at load instead of
half-activating.
- **Plugin options (`userConfig`).** `${user_config.KEY}` in a plugin's MCP server configs
and hook commands is filled from `[plugins.options.<plugin>]` config; an artifact that
references an unconfigured option is skipped rather than run blank. (Content substitution,
`PYTHINKER_PLUGIN_OPTION_*` hook env vars, and keychain-backed sensitive storage are not
yet implemented.)
- **MCP tool lists refresh automatically when servers change.** Connected MCP
sessions stay open for `tools/list_changed` (and resources/prompts) notifications;
inventory is re-published without a manual `/mcp refresh`.
- **Shell live token readouts track output throughput.** The spinner and background
status line show session-wide output tokens produced during the current turn or
background stretch instead of the context-size snapshot.
- **MCP servers can be managed without a full reload.** `/mcp disconnect`, `/mcp reconnect`, and
`/mcp refresh` (or `retry`) update the live toolset for one server; disconnect unregisters its
tools and marks the server failed until reconnect.
- **Recall search matches session ids and plan slugs.** Prior-session keyword search now indexes
`session_id` and `plan_slug` in addition to titles so agents can find plan-linked sessions by slug.
- **Wire and ACP surfaces now get max-steps handoff summaries.** When a turn hits the step ceiling,
wire clients receive a streamed handoff event plus a `handoff` field on the `max_steps_reached`
result; ACP sessions emit the same summary text before returning `max_turn_requests`.
- **MCP CLI commands resolve normalized server names.** `mcp remove`, `mcp auth`, `mcp test`, and
`reset-auth` accept display names with spaces or slashes and map them to stored config keys; config
load applies the same normalization as add.
- **Compaction failure circuit breaker respects thresholds above one.** A proactive compaction
failure below `max_compaction_failures` no longer aborts the turn; the handoff fires only after
the configured number of consecutive failures.
- **AI eval gate schema is self-contained under `tests_ai/`.** Shared eval-case types live in
`tests_ai/eval_schema.py` so isolated `tests_ai` runs do not import from `tests_e2e`.
- **Softer TUI chrome in the dark theme.** Panel borders (welcome banner, menus) and the input-area
rules now render in a mid grey (`#8a8d91`) instead of near-white, for a less glaring look.
- **Stop-time memory extraction can now be enabled explicitly.** Added an opt-in
Expand All @@ -25,6 +65,28 @@ GitHub Releases page; `0.8.0` is the new starting line.
`ToolSearch` plus root-session `EnterWorktree` and `ExitWorktree` tools so agents can find
currently available capabilities by keyword and isolate a session's operational working directory
in a git worktree without deleting user work on exit.
- **Root sessions now get a bounded git snapshot in the prompt.** When `git_status_injection` is
enabled (default), the agent receives branch, dirty-file summary, and recent commits as an
explicitly stale point-in-time reminder; disable via config or set `git_status_injection = false`.
- **Context compaction and MCP tool registration now fail more predictably.** Proactive compaction
failures hand back with an explicit `compaction_failed` stop instead of bubbling an unstructured
loop error, and MCP duplicate tool-name resolution now follows configured server order instead of
connection completion order. Tool hooks also retain the original model input even if a tool
mutates a nested argument object during execution.
- **Recall can now read bounded transcript windows.** `Recall(mode="read")` accepts
`message_offset` and `max_messages` so agents can inspect a precise, sanitized slice of a prior
workspace session without pulling the whole transcript into context.
- **MCP prompt templates can now be invoked from connected servers.** `InvokeMcpPrompt` renders a
server-published prompt with structured arguments and wraps the returned messages as untrusted
input for the model.
- **Telemetry, MCP config, and shell UX hardening.** Tool spans and metrics sanitize MCP/plugin
names; `mcp.json` load paths inject docker `--rm` and normalize server keys with collision
errors; shell suggestions accept via Alt+S into the prompt; markdown agent frontmatter maps
`max_turns`/`disallowed_tools`; `ReadMediaFile` enforces per-kind byte/pixel caps; written plans
without a Verification section get a soft warning; AI eval budgets can gate `tests_ai` reports.
- **Plan-mode exit guidance now requires verification.** The `ExitPlanMode` tool now tells agents
that written plans must include a Verification section with the smallest command, test, or check
for each meaningful change.
- **Agent-loop observability now emits explicit Wire events for key runtime state.** Added
`TodoListUpdated`, `SubagentToolFallback`, `AgentListDelta`, `ToolUseSkipped`, and
`ContextOverflowRecovered` events, with todo updates, subagent launch fallbacks, same-step tool
Expand Down
109 changes: 109 additions & 0 deletions docs/en/customization/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,112 @@ Plugins and MCP servers are complementary extension mechanisms:
- **MCP**: Suitable for services that need to run continuously, complex tool orchestration, or cross-process communication
- **Plugins**: Suitable for simple script wrappers, project-specific tools, or rapid prototyping
:::

## Marketplace plugins (Claude/Codex compatible)

In addition to the script-tool plugins above, Pythinker can install and activate
**artifact plugins** from *marketplaces* — the same plugin format used by Claude
Code (`.claude-plugin/plugin.json`) and Codex. An artifact plugin contributes
skills, subagents, slash commands, hooks, and MCP servers to a session.

### Marketplaces

A marketplace is a catalog (`marketplace.json`) listing plugins and their
sources. Manage marketplaces with `pythinker plugin marketplace`:

```bash
# Add a marketplace (GitHub owner/repo, git/URL, or a local path)
pythinker plugin marketplace add anthropics/claude-plugins-official
pythinker plugin marketplace add /path/to/local/marketplace --name local

# List, refresh, or remove
pythinker plugin marketplace list
pythinker plugin marketplace refresh claude-plugins-official
pythinker plugin marketplace remove local
```

### Installing marketplace plugins

```bash
# Install a plugin from a configured marketplace
pythinker plugin marketplace install ponytail@claude-plugins-official
# (equivalent positional form)
pythinker plugin marketplace install ponytail claude-plugins-official

pythinker plugin marketplace installed # list installed marketplace plugins
pythinker plugin marketplace uninstall ponytail@claude-plugins-official
```

Plugins install into `~/.pythinker/plugins/cache/<marketplace>/<plugin>/<version>/`.
If the same plugin or marketplace is already present in a Claude Code
(`~/.claude/plugins`) or Codex (`~/.codex/plugins`) install, Pythinker
**symlinks to it instead of copying** — no redundant downloads.

### Activation policy

Pythinker **auto-detects** plugins installed for Claude Code (`~/.claude/plugins`)
and Codex (`~/.codex/plugins`) — no symlink or manual copy needed. Their *safe*
artifacts (skills, commands, agents) activate automatically because they are
model-invoked, never auto-run; their *executable* artifacts (hooks, MCP servers)
auto-execute, so they stay opt-in. Detection reads the plugins in place (no
copy/symlink) and de-duplicates by name, so there is no redundancy.

Tune this via `[plugins]` in `~/.pythinker/config.toml`:

```toml
[plugins]
# Auto-detect Claude/Codex plugins' skills, commands, and agents. On by default.
# Set false to ignore external plugins entirely.
discover_external = true
# Also run external plugins' hooks and MCP servers (they auto-execute). Opt-in.
external_exec = false
# Empty enables all discovered plugins; a non-empty list enables only those named
# (by "name" or "name@marketplace").
enabled = []
# Plugins to turn off by name. Excluded even when `enabled` would allow them —
# this is how `pythinker plugin disable <name>` works under the all-on default.
disabled = []
```

Toggle plugins without editing the file or uninstalling them:

```bash
pythinker plugin disable ponytail # adds to [plugins].disabled
pythinker plugin enable ponytail # removes it again
```

Plugin-contributed hook and MCP commands may reference the plugin's own
directories via `${CLAUDE_PLUGIN_ROOT}` / `${PYTHINKER_PLUGIN_ROOT}` (the
versioned install dir) and `${CLAUDE_PLUGIN_DATA}` / `${PYTHINKER_PLUGIN_DATA}`
(a persistent per-plugin data dir); both are expanded on load.

### Plugin dependencies

A plugin may declare `dependencies` in its `plugin.json` (`"name"` or
`"name@marketplace"`). Installing a plugin from a marketplace also installs its
transitive dependencies from the same marketplace; cross-marketplace
dependencies are blocked (install them from their own marketplace first). At load
time, a plugin whose dependencies are not present and enabled is disabled, so it
never half-activates.

### Plugin options (`userConfig`)

A plugin may declare `userConfig` options and reference them as
`${user_config.KEY}` in its MCP server configs and hook commands. Provide values
per plugin in config:

```toml
[plugins.options.my-plugin]
api_base = "https://example.test"
```

An MCP server or hook that references an option with no configured value is
skipped (it never runs with a blank), and the value is filled in on load.

::: info Not yet ported
`${user_config.KEY}` substitution in **skill/agent/command content**, the
`PYTHINKER_PLUGIN_OPTION_*` **hook environment variables**, keychain-backed
storage for `sensitive` options, and an interactive enable-time prompt are not
implemented yet — they require changes outside the plugin subsystem. Today,
option values (including any marked `sensitive`) are read from config.
:::
64 changes: 64 additions & 0 deletions docs/en/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,48 @@ GitHub Releases page; `0.8.0` is the new starting line.

## Unreleased

- **Plugin marketplaces and activation policy.** `pythinker plugin marketplace` can add,
refresh, install, and uninstall Claude/Codex-compatible marketplace plugins. Plugins
installed for Claude Code or Codex are auto-detected (no symlink): their safe artifacts
(skills, commands, agents) activate by default, while executable artifacts (hooks, MCP
servers) stay opt-in. Config `plugins.discover_external`, `plugins.external_exec`,
`plugins.enabled`, and `plugins.disabled` — plus `pythinker plugin enable/disable <name>`
— control which installed plugins contribute artifacts. Hook and MCP commands
expand `${CLAUDE_PLUGIN_ROOT}`/`${PYTHINKER_PLUGIN_ROOT}` and
`${CLAUDE_PLUGIN_DATA}`/`${PYTHINKER_PLUGIN_DATA}`.
- **Plugin dependencies.** Plugins may declare `dependencies`; installing one pulls its
transitive dependencies from the same marketplace (cross-marketplace deps are blocked),
and a plugin whose dependencies aren't present+enabled is disabled at load instead of
half-activating.
- **Plugin options (`userConfig`).** `${user_config.KEY}` in a plugin's MCP server configs
and hook commands is filled from `[plugins.options.<plugin>]` config; an artifact that
references an unconfigured option is skipped rather than run blank. (Content substitution,
`PYTHINKER_PLUGIN_OPTION_*` hook env vars, and keychain-backed sensitive storage are not
yet implemented.)
- **MCP tool lists refresh automatically when servers change.** Connected MCP
sessions stay open for `tools/list_changed` (and resources/prompts) notifications;
inventory is re-published without a manual `/mcp refresh`.
- **Shell live token readouts track output throughput.** The spinner and background
status line show session-wide output tokens produced during the current turn or
background stretch instead of the context-size snapshot.
- **MCP servers can be managed without a full reload.** `/mcp disconnect`, `/mcp reconnect`, and
`/mcp refresh` (or `retry`) update the live toolset for one server; disconnect unregisters its
tools and marks the server failed until reconnect.
- **Recall search matches session ids and plan slugs.** Prior-session keyword search now indexes
`session_id` and `plan_slug` in addition to titles so agents can find plan-linked sessions by slug.
- **Wire and ACP surfaces now get max-steps handoff summaries.** When a turn hits the step ceiling,
wire clients receive a streamed handoff event plus a `handoff` field on the `max_steps_reached`
result; ACP sessions emit the same summary text before returning `max_turn_requests`.
- **MCP CLI commands resolve normalized server names.** `mcp remove`, `mcp auth`, `mcp test`, and
`reset-auth` accept display names with spaces or slashes and map them to stored config keys; config
load applies the same normalization as add.
- **Compaction failure circuit breaker respects thresholds above one.** A proactive compaction
failure below `max_compaction_failures` no longer aborts the turn; the handoff fires only after
the configured number of consecutive failures.
- **AI eval gate schema is self-contained under `tests_ai/`.** Shared eval-case types live in
`tests_ai/eval_schema.py` so isolated `tests_ai` runs do not import from `tests_e2e`.
- **Softer TUI chrome in the dark theme.** Panel borders (welcome banner, menus) and the input-area
rules now render in a mid grey (`#8a8d91`) instead of near-white, for a less glaring look.
- **Stop-time memory extraction can now be enabled explicitly.** Added an opt-in
`memory.harvest_on_stop` setting that stages safe assistant decisions, blockers, evidence, and
next steps into the existing scratchpad recall flow at turn end without writing directly to
Expand All @@ -25,6 +67,28 @@ GitHub Releases page; `0.8.0` is the new starting line.
`ToolSearch` plus root-session `EnterWorktree` and `ExitWorktree` tools so agents can find
currently available capabilities by keyword and isolate a session's operational working directory
in a git worktree without deleting user work on exit.
- **Root sessions now get a bounded git snapshot in the prompt.** When `git_status_injection` is
enabled (default), the agent receives branch, dirty-file summary, and recent commits as an
explicitly stale point-in-time reminder; disable via config or set `git_status_injection = false`.
- **Context compaction and MCP tool registration now fail more predictably.** Proactive compaction
failures hand back with an explicit `compaction_failed` stop instead of bubbling an unstructured
loop error, and MCP duplicate tool-name resolution now follows configured server order instead of
connection completion order. Tool hooks also retain the original model input even if a tool
mutates a nested argument object during execution.
- **Recall can now read bounded transcript windows.** `Recall(mode="read")` accepts
`message_offset` and `max_messages` so agents can inspect a precise, sanitized slice of a prior
workspace session without pulling the whole transcript into context.
- **MCP prompt templates can now be invoked from connected servers.** `InvokeMcpPrompt` renders a
server-published prompt with structured arguments and wraps the returned messages as untrusted
input for the model.
- **Telemetry, MCP config, and shell UX hardening.** Tool spans and metrics sanitize MCP/plugin
names; `mcp.json` load paths inject docker `--rm` and normalize server keys with collision
errors; shell suggestions accept via Alt+S into the prompt; markdown agent frontmatter maps
`max_turns`/`disallowed_tools`; `ReadMediaFile` enforces per-kind byte/pixel caps; written plans
without a Verification section get a soft warning; AI eval budgets can gate `tests_ai` reports.
- **Plan-mode exit guidance now requires verification.** The `ExitPlanMode` tool now tells agents
that written plans must include a Verification section with the smallest command, test, or check
for each meaningful change.
- **Agent-loop observability now emits explicit Wire events for key runtime state.** Added
`TodoListUpdated`, `SubagentToolFallback`, `AgentListDelta`, `ToolUseSkipped`, and
`ContextOverflowRecovered` events, with todo updates, subagent launch fallbacks, same-step tool
Expand Down
Loading
Loading