Commit Graph
1120 Commits
Author SHA1 Message Date
reacechalmers 2944e02a09 fix(clippy): clear remaining -D warnings failures in rusty-claude-cli
`cargo clippy --workspace --all-targets -- -D warnings` is this repo's own
documented verification command, and it could not run to completion. Earlier
compile failures were masking a further 48 lints; with those cleared the
workspace now passes with zero findings.

Real fixes, not suppressions:
  - status_json_value took 8 positional parameters, four of them a tail of
    `Option<&_>` that most callers passed as None — trivially transposable.
    Grouped into `StatusJsonExtras` with a Default impl; all 7 call sites now
    name only the fields they actually set.
  - extract_help_metadata's 8-tuple return is now a documented `HelpMetadata`
    alias.
  - the auto-compact loop indexed preserve_schedule by its loop counter;
    it now iterates the array directly.
  - a sandbox status chain had two identical arms; merged the condition.
  - a filter_map whose every arm returned Some is now a map.
  - 33 unused stdout/stderr bindings in the output-format contract tests are
    prefixed with `_`, and 9 `map_or(false, ..)` are now `is_some_and(..)`.

NOTE: several `--bin claw` tests (parse_args defaults) read the *real*
`~/.claw/settings.json` rather than an isolated config home, so they fail on
any machine whose user config sets `permissions.defaultMode`. With that file
moved aside the binary's suite is 207/207. That isolation gap is pre-existing
and left for a separate change.

Verified: clippy workspace/all-targets/-D warnings reports 0; cargo test
--workspace passes with the user config neutralised; scripts/fmt.sh clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9McEEH32ZtyM62GSXVSyJ
(cherry picked from commit 03c8a8fd7ba6e405a8217d00d3766c1c6367e527)
2026-10-12 22:58:19 +00:00
reacechalmers cd01ffc5f3 fix(clippy): box ApiError::Api payload to clear result_large_err
`ApiError` is the `Err` half of nearly every `Result` in the api crate, and
its `Api` variant carried ~139 bytes inline (8 fields, four of them
`Option<String>`). That tripped clippy::result_large_err on 35 signatures
and, with -D warnings, made the whole crate unbuildable under the repo's
own documented verification command.

Move the payload into `ApiErrorDetails` behind a `Box`:
  - `ApiError::Api(Box<ApiErrorDetails>)`, with `ApiError::api(..)` to
    construct and `api_details()` to borrow
  - `ApiErrorDetails` re-exported from the crate root
  - adding a field here can no longer re-inflate every signature

This also simplifies `enrich_bearer_auth_error`, which previously
destructured and rebuilt all eight fields at five separate return points
just to append a hint; it now mutates the boxed payload in place.

Also clears the remaining pre-existing lints that blocked -D warnings:
unused test bindings, a redundant `use serde_json`, and an explicit
`.into_iter()` in claw-rag-service.

Verified: cargo test --workspace passes, 0 failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9McEEH32ZtyM62GSXVSyJ
(cherry picked from commit e9210bd75eb76e5d88b2ae797f537102bcabd41d)
2026-10-12 22:58:19 +00:00
reacechalmers 4361e98998 fix: page large read_file results instead of returning whole files
An unbounded read_file returned the entire file, which on a small-context
backend overflows the model's window. The backend then rejects the whole
request (llama-server: "request (N tokens) exceeds the available context
size"), which kills the turn — so reading everything at once is precisely
what makes the session stop. Observed live: one 100 KB source file is
~25k tokens against a 32k window with a ~6k-token prefix, and a session
read the same file three times and died on six 400s.

read_file now returns a page and says so:
  - absent `limit` yields DEFAULT_READ_LINES (2000) rather than EOF
  - MAX_READ_CHARS (64 KB) bounds the payload even when `limit` is
    explicit, so minified or generated files can't defeat the line cap
  - `truncated` and `nextOffset` tell the caller it has a partial view
    and exactly where to resume; the tool description explains both

MAX_READ_SIZE (10 MB) is untouched: it is a process guard against slurping
huge or binary blobs, not a context guard, and at ~2.5M tokens it never
fires on the files that actually overflow a window.

Also clears pre-existing clippy failures in trident.rs that blocked
-D warnings on every edit to this crate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9McEEH32ZtyM62GSXVSyJ
(cherry picked from commit c796d7e5eedc66b0cb37bb6823ec772f8ceef9cf)
2026-10-12 22:58:19 +00:00
990cc33c19 test(runtime): expand json module unit tests [formatting fix] (#3308)
* test(runtime): expand json module unit tests

The hand-rolled JsonValue parser/renderer in runtime had only two
tests covering a self-contained JSON parser, renderer, accessor set,
and error type. Add focused unit tests covering primitive rendering,
sorted object keys, escape handling (quotes, backslashes, control
chars), signed-integer and whitespace-tolerant parsing, nested
structures, render->parse round-tripping, malformed-input rejection,
i64 overflow rejection, accessor variant mismatches, and JsonError
Display.

* style: format json.rs tests to match rustfmt standards

---------

Co-authored-by: 黄云龙 <76432572+nankingjing@users.noreply.github.com>
Co-authored-by: gaebal-gajae <clawdbot@users.noreply.github.com>
2026-10-13 07:22:31 +09:00
黄云龙 8d2c7b78b2 refactor(cli): let the emitter report the output tail it leaves behind (#3304)
refactor(cli): let the emitter report the output tail it leaves behind

Two places believed they owned terminal output: consume_stream streams
assistant deltas live while emit_output = true, and run_turn decided
whether to emit a separating newline before spinner.finish by guessing
from final_assistant_text(&summary).

That guess agrees with reality only because every *other* writer happens to
terminate its output with a newline — tool results go through
stream_markdown (which appends one), thinking summaries are written with a
trailing newline, and tool-call headers use writeln! — so the assistant
text path is the only one that can leave a partial line. That is a property
of the current renderers rather than of the streaming protocol, and it is
not enforced anywhere.

TerminalTail is state the emitter records into as it writes: both live
writers (AnthropicRuntimeClient::consume_stream and the tool-result blocks
in CliToolExecutor::execute) now push bytes through a TailTrackingWriter
that reports the last byte written, and the caller that ends a turn asks
where the emitter stopped instead of inferring it.

The heuristic also left the failure arm unguarded. A turn that streamed text
and then errored called spinner.fail on a partial line, which the line
clear then erased; that arm now carries the same guard.

Also gate the animated spinner.tick frame on is_terminal(). That frame is
pure cursor control (save, rewrite line, restore) and emits no newline, so
down a pipe it leaves an unterminated banner that the first streamed delta
then continues on the same physical line.

—
*[repo owner's gaebal-gajae (clawdbot) 🦞]*
2026-10-13 07:18:12 +09:00
Bellman 972ba85c3b Merge pull request #3268 from nankingjing/docs-model-aliases
docs(rust/README): document xAI and Kimi model aliases
2026-10-13 07:16:43 +09:00
Bellman 171a07b68c fix(cli): preserve trailing block separators in streamed markdown (#3312)
Merged via automated lane resolution
2026-10-13 07:15:19 +09:00
黄云龙 8d81c61b7a test(runtime): cover JSON escapes end to end and pin the i64 boundary pair (#3305)
Reviewed and verified: 4 new tests added, all passing. 116 lines added to test JSON escape sequences and i64 boundaries. No production code changes.

—
*[repo owner's gaebal-gajae (clawdbot) 🦞]*
2026-10-13 07:14:47 +09:00
AbdullahFageehandOz 05a2d71cb5 fix: support groq compound defaults (#3281)
Route groq-prefixed models through the OpenAI-compatible client,
apply Groq token-limit metadata, and skip local tool injection for
compound models.

Add regression coverage for routing, token limits, and compound
local-tool behavior.

Co-authored-by: Oz <oz-agent@warp.dev>
2026-10-13 07:14:14 +09:00
Bellman 8aeef8d2e2 Merge pull request #3271 from Aneesh-382005/fix/local-model-validation-bypass
fix: generalize local-model validation bypass beyond Ollama tags
2026-10-13 07:13:39 +09:00
Ming LiuandClaude Opus 4.6 43d68dba75 feat: add persistent auto-memory system (#3241)
feat: add persistent auto-memory system

Implement a file-based persistent memory system that allows the AI to retain knowledge about users, feedback, projects, and references across sessions.

## Implementation Details
- MemoryStore data layer with FNV-1a workspace fingerprinting
- MemoryRead/MemoryWrite tools with security hardening (path traversal, symlink, absolute path, and size limit protections)
- System prompt injection of MEMORY.md index with prompt injection mitigation (fenced code block + trust-lowering notice)
- autoMemoryEnabled config option (defaults to true)
- Doctor health check for auto-memory state
- /memory command enhancement showing persistent memory info

## Verification
✓ 1023 lib tests pass
✓ Output format contract tests pass
✓ Release build succeeds
✓ Security hardening verified

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-13 07:13:36 +09:00
黄云龙 86428abce6 refactor(runtime): make the permission privilege ladder an explicit rank (#3303)
* fix(runtime): prompt permission mode never invokes the prompter

PermissionMode derives Ord from declaration order, placing Prompt (and Allow)
above the real privilege ladder (ReadOnly < WorkspaceWrite < DangerFullAccess).
In PermissionPolicy::authorize_with_context the ladder check
'current_mode >= required_mode' therefore evaluates true for every tool when the
active mode is Prompt (3 >= {0,1,2}), returning Allow and making the subsequent
'if current_mode == PermissionMode::Prompt' branch that routes to prompt_or_deny
unreachable.

Effect: a session configured with the 'prompt' permission mode silently
auto-allows every tool and never calls the prompter (reached via
ConversationRuntime::run_turn with an interactive prompter), defeating the mode's
entire purpose. The sibling PermissionEnforcer already special-cases Prompt
before its own 'active_mode >= required_mode' check, confirming Prompt is not
meant to be part of the ordered ladder.

Fix: exclude Prompt from the ordered-ladder comparison so Prompt mode falls
through to the interactive prompt path. Allow remains handled by its explicit
equality check. The same comparison appears twice in authorize_with_context —
once in the shared tail and once inside the 'Some(PermissionOverride::Allow)'
arm, which is reached when a hook emits permissionDecision "allow" — so both
copies carry the guard; guarding only the tail still left a hook able to
auto-allow every tool in Prompt mode without ever consulting the prompter.

Adds regression tests covering Prompt mode with a prompter, without a prompter,
and with a hook allow override.

* refactor(runtime): make the permission privilege ladder an explicit rank

`PermissionMode` derived `PartialOrd`/`Ord`, so the ladder deciding whether a
tool runs without asking was declaration order. A variant inserted in the middle
of the enum re-ranked it silently, and the difference between "this tool may
run" and "this tool may run after a prompt" was a property of where an `enum`
line happened to sit. That is how `Prompt` came to outrank `DangerFullAccess`,
satisfy `current_mode >= required_mode` for every tool, and skip the prompter.

Replace the derive with `privilege_rank`, an exhaustive `match` naming every
variant, so inserting one is a build failure rather than a silent re-ranking.
`Ord` is deliberately not derived, so a stray `>=` on two modes no longer
compiles; the two comparisons outside this module now say which rank they read
(`PermissionEnforcer::check_with_required_mode` and the `claw doctor` permission
check). Both keep their previous answers, since `privilege_rank` reproduces the
old declaration order exactly, including `Prompt` and `Allow` above the tiers.

`Prompt` and `Allow` are modes of operation, not privilege tiers, so they are
never ranked *against* a tool requirement. The new `covers` predicate states
that by name rather than by number: `Allow` always covers, `Prompt` never does
(it asks), and only the three tiers compare ranks.

That also collapses the duplicated ladder in `authorize_with_context`. The
`Some(PermissionOverride::Allow)` arm carried a second, identical copy of the
ask-rule check and the ladder; it now falls through to the single copy below,
so a hook `allow` is an override rather than a bypass and the two copies can no
longer drift apart.

Behaviour is unchanged for every (mode, requirement, rule, override)
combination. The tests pin the `Prompt` routes — plain and hook-`allow` — across
all three tiers, plus the rank ordering itself.
2026-10-13 07:04:44 +09:00
gaebal-gajae 794254597c Fix untrusted project hooks in read-only mode 2026-10-12 22:02:05 +00:00
Bellman cc58238e43 Merge pull request #3273 from nankingjing/fix-duplicate-assistant-text-3258
fix(cli): stop reprinting streamed assistant text after 'Done' in non-compact text mode
2026-10-12 05:40:00 +09:00
Bellman 16ce599ef0 Merge pull request #3286 from SulimanAbdulrazzaq/fix/3278-windows-workspace-path
fix(runtime): normalize Windows workspace paths
2026-10-12 05:38:29 +09:00
gaebal-gajae 811403286b test(cli): assert streamed response is emitted once 2026-10-11 20:35:09 +00:00
nankingjing 072594dfee test(api): lock the README model-alias table to the resolver
The `## Model Aliases` table and `MODEL_REGISTRY` had drifted: the table
listed only the three Anthropic aliases while the resolver had grown the
xAI and Kimi entries, so an alias could resolve without being documented.

Assert the two agree in both directions — every registry alias appears in
the table with the canonical id and auth env var it actually resolves to,
and the table introduces no alias the resolver does not know. Each row is
also checked case-insensitively, since the README claims `OPUS` behaves
the same as `opus`.

Also pin that a provider-prefixed form such as `anthropic/opus` is not an
alias: it is passed through as-is rather than expanded. The README now
says so, which is what made the distinction worth a test rather than a
comment.
2026-09-24 12:19:39 +08:00
Suliman Abdulrazzaq dc346498fb fix(runtime): accept other Windows spellings of a workspace path
validate_workspace_boundary compared paths lexically, so a location inside
the workspace spelled with different letter case (a working directory typed
as c:\users\... for C:\Users\...) or through the \\.\ device namespace was
reported as escaping the workspace.

When the lexical comparison fails, compare the path's canonical form before
reporting an escape. A path whose canonical form is outside the workspace
stays rejected.
2026-09-18 12:03:14 +03:00
Suliman Abdulrazzaq dbf5389729 test(runtime): gate the Windows extended-path boundary test to Windows
accepts_equivalent_extended_path_at_workspace_boundary builds its paths
with backslash separators. Backslashes only separate path components on
Windows, so on Linux each path is a single component, the boundary check
rejects the path, and cargo test fails.

Run the test only on Windows, and call validate_workspace_boundary through
super:: so non-Windows test builds do not warn about an unused import.
2026-09-17 13:15:05 +00:00
YeonGyu-Kim 08106b0c37 docs: add hierarchical AGENTS.md knowledge base
Root knowledge base plus complexity-scored subdirectory files for the
rust/ workspace, its five highest-mass crates (runtime, rusty-claude-cli,
api, tools, commands, plugins), and the src/ Python porting workspace.

Generated via init-deep: 13 parallel explore agents, LSP/ast-grep code
map, centrality-scored placement. Snapshot in .omo/init-deep.json (local).
2026-08-16 15:18:33 +09:00
Suliman Abdulrazzaq 9e7fd601a5 fix(runtime): normalize Windows workspace paths
Keep extended-length and regular Windows paths comparable when enforcing workspace boundaries, including first-run roots that cannot yet be canonicalized. Add regression coverage for DOS and UNC path forms.

Signed-off-by: Suliman Abdulrazzaq <suliman9000a@gmail.com>
2026-08-09 15:14:37 +03:00
code-yeongyuandlinkst 525035b51b fix(sandbox): probe the full launcher shape, not just the mapping
The startup probe validated only the mapping flags against the trivial
program `true`, but the real launcher always adds
--mount --ipc --pid --uts --fork. On environments where the user
namespace is created but mount propagation inside it is restricted
(e.g. AppArmor-restricted CI runners), the fallback mapping passed the
probe and the sandbox activated, yet every sandboxed command died with
"cannot change root filesystem propagation: Permission denied",
silently returning empty tool output and breaking the mock parity
suite.

The candidates now define the complete static launcher shape (mapping
flags + namespace flags), so probe success implies launch success; the
launcher reuses the candidate instead of re-appending the namespace
flags, keeping probe and launch as one source of truth. The
order-guarding test asserts the namespace flags are present in every
candidate.

Co-authored-by: linkst <2024023709@m.scnu.edu.cn>
2026-08-06 19:41:27 +09:00
linkst 9cbe6d9a8c docs(sandbox): document newuidmap/newgidmap dependency for --map-auto fallback
The fallback candidate relies on the setuid newuidmap/newgidmap helpers
(uidmap package) plus a subuid/subgid range for the current user. Note in
the candidate docs that the startup probe rejects the candidate when those
are missing, so the plain --map-root-user form is used instead.
2026-08-01 15:21:56 +08:00
linkst 277fdda894 fix(sandbox): fall back to --map-auto when root-user mapping is restricted
Plain `unshare --user --map-root-user` fails on kernels and containers
that block unprivileged writes to /proc/self/uid_map (e.g. GitHub Actions,
restricted AppArmor profiles). On those systems util-linux delegates to the
setuid newuidmap/newgidmap helpers when --map-auto is also present.

Add the combined form as a fallback candidate and build the launcher args
from the probed mapping, so systems without newuidmap/newgidmap or a
/etc/subuid range keep using the plain form.
2026-07-31 17:09:00 +08:00
linkst 1c8338ea66 refactor(sandbox): extract unshare mapping probe into cached helper
No behavior change. Move the inline probe out of
unshare_user_namespace_works into a reusable unshare_probe helper and a
cached working_unshare_mapping() that picks the first working candidate
from UNSHARE_MAPPING_CANDIDATES, so the launcher and the capability probe
share one code path.
2026-07-31 17:08:51 +08:00
nankingjing 971d350e53 fix(cli): preserve retried streamed response tail 2026-07-14 01:18:47 +00:00
黄云龙 1a66da4eb3 fix(cli): stop reprinting streamed assistant text after 'Done' 2026-07-11 17:45:50 +08:00
Aneesh 5eeb474da4 fix: generalize local-model validation bypass beyond Ollama tags 2026-07-10 19:10:01 -04:00
YeonGyu-Kim d229a9b022 Merge pull request #3227 from TheArchitectit/worktree-wizard-entry-points
feat: wizard entry points — /setup command, claw setup subcommand (rebased)
2026-06-08 15:06:11 +09:00
YeonGyu-Kim 05f0201ec7 fix: preserve runtime config validation compatibility 2026-06-08 15:01:48 +09:00
YeonGyu-Kim 222faabd7f fix(cli): hint Ollama for Qwen tags 2026-06-08 10:08:38 +09:00
YeonGyu-Kim 7503c1c031 fix(providers): parse Ollama reasoning fields 2026-06-08 10:08:32 +09:00
YeonGyu-Kim a1da1ca8e6 test(cli): serialize env-sensitive model alias checks 2026-06-08 01:37:28 +09:00
YeonGyu-Kim 27acfe1014 test(runtime): isolate session and git metadata checks 2026-06-08 01:23:32 +09:00
YeonGyu-Kim c1646613d1 fix(providers): preserve OpenAI-compatible reasoning history 2026-06-08 01:23:13 +09:00
Ajinkya-Ghuge 0755ddff3c fix(providers): strip provider prefix from model names for openai_compat endpoints 2026-06-06 22:29:59 +05:30
bellman be8112f5f5 feat: add native Ollama provider support via OLLAMA_HOST env var
- OLLAMA_HOST takes priority over OPENAI_BASE_URL for local Ollama instances
- No API key required; placeholder token used for Authorization header
- Model names like 'qwen3:8b' bypass strict provider/model syntax validation
- detect_provider_kind() checks OLLAMA_HOST first in routing cascade
- ProviderClient dispatch uses from_ollama_env() when OLLAMA_HOST is set
- Updated USAGE.md and docs with OLLAMA_HOST as preferred env var
- Added OLLAMA_CONFIG constant and from_ollama_env() to openai_compat
- Added test_ollama_host_bypasses_model_validation unit test
- Supersedes PR #3213 (which had a duplicate if-let bug in mod.rs)
2026-06-05 12:12:56 +09:00
TheArchitectit d58197cca4 fix: update slash command count and add /setup assertion in test
- Update slash_command_specs().len() assertion from 139 to 140.
  The /setup command added by this PR increased the spec count by 1
  but the test's expected count was not updated, causing CI failure.

- Add assert!(help.contains("/setup")) to the
  renders_help_from_shared_specs test so the new command is
  verified in the help output.

  Fixes CI Build ❌ and Test ❌ on #3218.
2026-06-04 22:06:32 -05:00
Your NameandClaude Opus 4.8 3845040b9d feat: wizard entry points -- /setup command, claw setup subcommand, and RuntimeProviderConfig
The setup wizard was merged in PR #3017 but was orphaned -- it was not
declared as a module in main.rs, making it unreachable. Additionally,
the setup_wizard.rs imports RuntimeProviderConfig which did not exist
on upstream/main. This commit makes the wizard accessible and adds the
necessary RuntimeProviderConfig type.

Changes:
- Add RuntimeProviderConfig struct to runtime/src/config.rs with
  kind(), api_key(), base_url(), model() accessors.
- Add parse_optional_provider_config() to parse the provider object
  from merged settings JSON.
- Add provider() method to RuntimeConfig and RuntimeFeatureConfig.
- Export RuntimeProviderConfig, save_user_provider_settings,
  clear_user_provider_settings, and default_config_home from runtime
  crate public API (runtime/src/lib.rs).
- Add mod setup_wizard to rusty-claude-cli/src/main.rs.
- Add claw setup CLI subcommand.
- Add /setup slash command.
- Add Setup variant to SlashCommand enum.
- Add Setup to LocalHelpTopic enum.
- Add setup to diagnostic subcommand matching.
- Add subagentModel to TOP_LEVEL_FIELDS in config_validate.rs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:52:48 -05:00
bellmanandGajae Code 503d515f38 style: cargo fmt after merged PRs (#3164, #3209, #3214, #3216)
Fix formatting inconsistencies introduced by merged external PRs.

Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
2026-06-05 10:52:37 +09:00
Bellman 114c2da9fc Merge pull request #3216 from TheArchitectit/worktree-session-resume-fixes
fix: session resume — scan all workspaces, skip current empty session
2026-06-05 10:38:19 +09:00
Bellman b90f18f75a Merge pull request #3214 from TheArchitectit/worktree-api-timeout-retry-v2
feat: API timeout config, Retry-After header, configurable retry, and 400 transient retry
2026-06-05 10:33:35 +09:00
Bellman 5b15197117 Merge pull request #3209 from Sam0urr/harden-permission-enforcer
Harden permission enforcement against sandbox bypasses
2026-06-05 10:32:50 +09:00
Bellman ef0d0c30a4 Merge pull request #3164 from petterreinholdtsen/llama.cpp-errors
fix: recover from llama.cpp context overflow and reqwest SSE decode failures
2026-06-05 10:25:54 +09:00
bellman 7305e5509a fix: update skills help test assertions for --project flag (#95 CI fix)
Updated the agents_and_skills_usage_support_help_and_unexpected_args
test to match the new skills help text that includes [--project].

Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
2026-06-05 10:23:36 +09:00
bellman 2f0b5b36eb fix: wrap concurrent ENOENT as domain-specific session error (#112)
Session save_to_path now wraps ENOENT errors from rotate and atomic
write with a clear "possible concurrent modification" message instead
of surfacing raw OS errno. Helps operators debugging race conditions
when multiple claw invocations touch the same session file.

Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
2026-06-05 10:19:43 +09:00
bellman c848eeb768 fix: status JSON reports all workspace panes not just the first (#326)
SessionLifecycleSummary now collects all matching tmux panes into an
all_panes field and includes them in the JSON output. Previously the
status command returned on the first non-idle pane, losing all other
active panes in the same workspace/session.

Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
2026-06-05 10:15:23 +09:00
bellman d4aad7103e fix: add actionable auth hint to 401/403 API errors (#28)
401 and 403 errors now include a hint explaining which env vars to
check for each provider (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.)
and suggesting claw doctor for credential verification.

Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
2026-06-05 10:10:24 +09:00
bellman b60cbebb3a feat: skills install --project flag for project-level scope (#95)
claw skills install --project <path> now installs to .claw/skills/
in the current project instead of the user-level registry. Skills
installed at project level are already discovered by the existing
registry system. Both text and JSON handlers updated.

Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
2026-06-05 10:05:36 +09:00
bellman 7c4bcd92b6 fix: expand ${VAR} and ~/ in MCP config fields (#92)
MCP server config now expands ${VAR} environment variable references
and ~/ home directory prefix in command, args, and url fields. Previously
these values were passed verbatim to execve/URL-parse, causing silent
"No such file or directory" failures for standard config patterns.

Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
2026-06-05 10:00:45 +09:00