Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ GitHub Releases page; `0.8.0` is the new starting line.
- **The in-app updater recovers from Homebrew's untrusted-tap refusal.** Homebrew 5.0 (`HOMEBREW_REQUIRE_TAP_TRUST`) refuses to load formulas from third-party taps until `brew trust <tap>` is run once, which made the in-app `brew upgrade` fail with only a generic "run manually" hint. The updater now detects both the hard `Refusing to load … from untrusted tap` refusal and the soft `Skipping … not trusted` warning, offers to run `brew trust pythoughts-labs/pythinker` and retry the upgrade once on an interactive terminal, and otherwise prints the exact remediation. It also catches the silent no-op where an untrusted tap is skipped during `brew update` and `brew upgrade` exits 0 without advancing the version.
- **Session exports redact secrets surfaced by tool output.** A tool result (e.g. `grep`/`cat` over a `.env`) could write a secret value into an exported transcript in plaintext. `/export` now redacts the value of secret-named keys (`password`, `token`, `api_key`, `secret`, …) to `[REDACTED]` in both the markdown and YAML formats, while leaving non-secret keys such as `token_count`, usernames, and ports intact.
- **The welcome logo's antenna blinks a fixed number of times on launch, then settles.** Replaces the terminal's indefinite slow-blink with a bounded boot animation — the antenna ball blinks seven times after the banner prints and then holds steady. It is skipped under reduced motion, on non-interactive output, and when the terminal is too short to keep the antenna row on screen.
- **Inline `/command` references get acted on, not just explained away.** When a message mentions a slash command mid-sentence (e.g. "your `/goal` today is to `/plan` and build the page"), the command doesn't auto-run — but the agent no longer leads its reply by reporting it as failed. The per-turn reminder and the system prompt now steer the agent to act on the intent: call the real `EnterPlanMode` tool for `/plan` (clarified as a genuine, callable tool so models stop doubting it exists), pursue the described objective for `/goal`, load `/skill:<name>` via `ReadSkill`, and apply equivalent guidance for other commands — only surfacing how to invoke the literal command when genuinely needed.

## 0.41.0 (2026-06-11)

Expand Down
2 changes: 1 addition & 1 deletion src/pythinker_code/agents/default/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ For research or multimedia tasks (images, video, PDFs, docs, spreadsheets, prese

**Skills (`ReadSkill`).** Load a skill's exact instructions before applying its workflow — mandatory for `review-pr`, `diagnose-ci-failures`, `fix-errors`, `implement-specs`, `spec-driven-implementation`, `check-impl-against-spec`, `resolve-merge-conflicts`, and `create-pr`. Read skill details only when needed, to conserve context. Catalog and scope precedence in §12.

**Inline `/command` references.** Slash commands execute only as their own message starting with `/`. A `/command` or `/skill:<name>` mentioned mid-message did not run: treat the reference as part of the request — load a referenced skill via `ReadSkill`, apply referenced guidance yourself, or tell the user to invoke it as a standalone message. Never silently drop such a reference.
**Inline `/command` references.** Slash commands execute only as their own message starting with `/`. A `/command` or `/skill:<name>` mentioned mid-message did not auto-run, but it still expresses intent — act on it rather than leading your reply by reporting it as failed. For `/plan`, call `EnterPlanMode` (a real tool in your toolset, not just prose); for `/goal`, pursue the described objective until it is verifiably done; for `/skill:<name>`, load it via `ReadSkill` and apply it; for other guidance commands, apply the equivalent guidance yourself. Mention invoking the real command only when genuinely needed. Never silently drop such a reference.

**MCP.** Connected MCP servers expose their capabilities as ordinary tools already in your toolset (descriptions name the server). To *use* one, invoke its tools directly — never pip-install the server, import it as a module, or search the repo for its config. If a named server has no tools present, it is not connected (loading, failed, or unauthorized), not missing: point the user to `/mcp` for status, and to `pythinker mcp auth <server_name>` for an unauthorized OAuth server.

Expand Down
14 changes: 9 additions & 5 deletions src/pythinker_code/soul/dynamic_injections/inline_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@

_REMINDER_TEMPLATE = (
"The user's message references slash commands inline: {refs}. Inline references do "
"NOT execute — a slash command runs only as its own message starting with '/'. Do "
"not silently ignore them; treat each reference as part of the request: for "
"NOT execute as commands — a slash command runs only as its own message starting "
"with '/'. Do not silently ignore them, and do not lead your reply by telling the "
"user they failed to run. Instead act on the intent each reference expresses: for "
"/plan, call the EnterPlanMode tool to enter plan mode before implementing (it is "
"an available tool, not just prose); for /goal, treat the objective the user "
"described as the goal to pursue across this work until it is verifiably done; for "
"/skill:<name>, load that skill with ReadSkill and apply its instructions; for "
"guidance-injecting commands (e.g. /best-practices), apply the closest equivalent "
"guidance yourself and tell the user how to run the real command; otherwise tell "
"the user the command did not run and how to invoke it."
"other guidance-injecting commands (e.g. /best-practices), apply the closest "
"equivalent guidance yourself. Only mention how to invoke the real slash command "
"if doing so is genuinely needed to satisfy the request."
)


Expand Down
17 changes: 17 additions & 0 deletions tests/core/test_inline_command_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def _mock_soul(is_subagent: bool = False) -> MagicMock:
_cmd("best-practices", aliases=["bp"]),
_cmd("clear"),
_cmd("goal"),
_cmd("plan"),
]
return soul

Expand All @@ -42,6 +43,22 @@ async def test_injects_for_inline_known_command() -> None:
assert "NOT execute" in result[0].content


async def test_reminder_is_actionable_not_dismissive() -> None:
"""The reminder must steer the agent to act on intent, not to lead by telling
the user the command 'did not run'."""
provider = InlineCommandReminderProvider()
history = [_user("your /goal today is to /plan and build the landing page")]
result = await provider.get_injections(history, _mock_soul())
assert len(result) == 1
content = result[0].content
# Names the real EnterPlanMode tool so the agent stops doubting it exists.
assert "EnterPlanMode" in content
# Pursues the described objective for /goal rather than just explaining syntax.
assert "goal to pursue" in content
# Does not instruct the agent to report the commands as failed up front.
assert "did not run" not in content


async def test_injects_for_alias() -> None:
provider = InlineCommandReminderProvider()
history = [_user("apply /bp while you build it")]
Expand Down
Loading