Skip to content

feat: interactive update checker + REPL/telemetry/cloud polish#58

Merged
guima-why merged 9 commits into
mainfrom
fix_issue_260529
Jun 1, 2026
Merged

feat: interactive update checker + REPL/telemetry/cloud polish#58
guima-why merged 9 commits into
mainfrom
fix_issue_260529

Conversation

@guima-why

@guima-why guima-why commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Bundles the new interactive update checker with a batch of REPL UX polish,
telemetry/cloud fixes, and a banner version line.

Interactive update checker (new)

  • New iac_code.services.update_checker: cross-process state at
    ~/.iac-code/update-state.yml with atomic write + fcntl advisory lock,
    2h success-throttle, PEP 440 version compare, source priority
    (official PyPI > configured pip).
  • Startup prompt with three actions: update_now (runs upgrade then exits),
    skip_until_next (suppresses until a newer version appears),
    skip (default).
  • Background re-check thread kicks off after the banner renders, so the next
    startup has fresh state.
  • render_update_prompt_header / render_update_notice reuse the banner's
    Group/Panel style.
  • Full i18n coverage across zh / es / fr / de / ja / pt.

REPL UX

  • New $ trigger lists/invokes skills only (the / trigger still mixes
    built-ins and skills); typing $<built-in> errors clearly pointing at the
    / equivalent.
  • Streaming tool header shows the path field as soon as it closes
    (● Read(src/foo.py)) instead of waiting for full JSON parse — new
    extract_partial_string_fields() helper + opt-in
    Tool.streaming_preview_fields() hook (Read/Write/Edit opt in).
  • Spinner verbs no longer say "Thinking" while tools are running.
  • Welcome banner shows iac-code v<version> in the dim metadata block.

Telemetry & cloud integrations

  • Telemetry reporting is disabled when __release_date__ is empty
    (unpackaged local source), gating both ARMS exporters and any
    user-configured OTLP endpoint.
  • Alibaba Cloud OpenAPI clients carry a
    iac-code/<version>+<release_date|dev> (<os>; <arch>; Python/<py>)
    User-Agent, plumbed centrally in tools/cloud/aliyun/user_agent.py and
    wired through every Config branch in aliyun_api and ros_client.

Skill discovery fix

  • Reordered precedence so bundled skills cannot be shadowed by project-local
    or user-global skills of the same name; project-local scan now walks from
    git root toward cwd (skills/ then .iac-code/skills/ at each level).

Test plan

  • make lint clean
  • make test (full suite) green
  • Manual: launch REPL, confirm iac-code v0.3.0 line in welcome banner
  • Manual: seed ~/.iac-code/update-state.yml with a pending entry,
    confirm interactive prompt appears with skip as default
  • Manual: choose update_now, verify pip runs and process exits 0
  • Manual: choose skip_until_next, restart, confirm prompt is suppressed
    until a newer version appears
  • Manual: /help works; $help errors with hint; $<unknown> errors
  • Manual: trigger a Read tool call, confirm ● Read(<path>) appears
    during streaming (before the full JSON arrives)
  • Manual: confirm spinner does not say "Thinking" during tool execution
  • Manual: hit any Aliyun OpenAPI tool, confirm
    User-Agent: iac-code/... reaches the server-side log

🤖 Generated with Claude Code

guima-why and others added 8 commits June 1, 2026 15:32
An empty __release_date__ indicates an unpackaged local source build,
which should not pollute production observability. Treat it the same as
DISABLE_TELEMETRY=1, gating both the default ARMS exporters and any
user-configured OTLP endpoint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Identify and triage iac-code traffic server-side. Format:
iac-code/<version>+<release_date|dev> (<os>; <arch>; Python/<py_ver>).
Centralized in user_agent.build_user_agent() and wired into every
Config branch in aliyun_api and ros_client; local source builds carry
"+dev" so production logs can filter them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The / trigger continues to surface both built-in commands and skills.
The new $ trigger lists/invokes skills only; typing $<built-in> shows a
clear error pointing at the / equivalent so users aren't left guessing.

- token_extractor: recognize $ as a trigger character at token start
- skill_provider: new SuggestionProvider that filters matches to
  PromptCommand instances (skills only)
- registry: is_command()/parse() accept the $ prefix
- repl: wire SkillProvider into the aggregator; route $<local> and
  $<unknown> to a clear, translated error
- i18n: translate the two new strings in zh, es, fr, de, ja, pt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a tool's input is still being streamed (tool_input={}, partial_input
accumulating JSON fragments), the renderer used to show '● Read' with no
detail until the full JSON was parsed at content_block_stop. Now it shows
e.g. '● Read(src/foo.py)' as soon as the path field's closing quote has
streamed, by extracting opt-in string fields from partial_input.

- utils/json_utils: new extract_partial_string_fields() helper. The regex
  matches only fully-closed "key": "value" pairs, so truncated values
  never leak. JSON escapes decoded via json.loads.
- tools/base: add opt-in Tool.streaming_preview_fields() hook (default []).
- tools/read_file, write_file, edit_file: override to return ["path"].
- ui/renderer: _render_tool_header derives an effective_input from
  partial_input when tool_input is empty and the tool opted in. Once the
  real ToolUseEndEvent arrives, the real dict takes over.
- tools/write_file, edit_file: schema description on 'path' nudges the
  model to emit this field FIRST in the JSON arguments, so the streaming
  preview kicks in early instead of waiting for the large content/
  old_string field to finish streaming.

Tests: 12 new unit tests (9 for the helper, 3 for the renderer fallback).
Out of scope: full JSON parse failure, history/transcript region,
sub-agent child branch, and bash/grep/glob tools.
The spinner runs while the agent is actively executing tools, so
"Thinking..." misrepresents what's happening. Keep "Processing" and
"Working", which describe action.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@Prodesire

Copy link
Copy Markdown
Member

Please update the description of skills in the documentation.

Bundles four follow-up fixes on top of the original update checker
and $-trigger commits:

- i18n: rebase took --theirs on .po files for the update-checker
  commit, dropping the 10 Windows-compat / A2A translations that
  PR #28 had added on main. Backfill them per-locale from origin/main.

- skills: discovery._find_git_root spawned
  subprocess.run(["git", "rev-parse", "--show-toplevel"], timeout=2.0)
  inside discover_all_skills, which is reached inline from ACP/A2A
  async handlers via create_agent_runtime — exactly the path that an
  earlier commit had to rewrite for project_paths._read_git_head
  because git-for-windows leaves grandchild processes holding the
  captured stdout/stderr pipes; after timeout fires, subprocess.run's
  second communicate() blocks forever and freezes the event loop.
  Extract find_git_worktree_root() in project_paths (pure filesystem
  walk, handles linked-worktree/submodule .git files) and have both
  _read_git_head and discovery use it. Regression tests assert no
  subprocess on either path.

- docs(cli): document the new $ trigger alongside / in commands.md
  and skills.md. zh-Hans translated; ja/pt/de/fr/es commands.md
  translated; skills.md in those 5 locales was already English so
  just synced.

- review: three regressions surfaced by code review on the update
  checker / streaming preview work:
  * edit_file streaming preview briefly rendered Create operations
    as "Update" until the full input arrived — return a neutral
    "Edit" when old_string is absent (translated per locale).
  * _handle_startup_update's SystemExit(0) on Update-now bypassed
    run()'s finally graceful_shutdown(); flush telemetry explicitly
    before exiting.
  * Select prompt blocked forever on non-TTY stdin (CI shells,
    container wrappers); short-circuit at the top via
    sys.stdin.isatty(); existing tests get an autouse fixture that
    forces isatty=True since pytest captures stdin by default.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@guima-why guima-why merged commit e9fc8eb into main Jun 1, 2026
14 checks passed
@guima-why guima-why deleted the fix_issue_260529 branch June 1, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants