mirror of
https://github.com/instructkr/claude-code.git
synced 2026-05-30 01:16:43 +00:00
Compare commits
1 Commits
ab44985916
...
fix/permis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e9b21f415 |
@@ -2674,44 +2674,10 @@ fn render_mcp_report_for(
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(args) if args.split_whitespace().next() == Some("list") && args.contains(' ') => {
|
|
||||||
// `mcp list <filter>` — list does not accept arguments; treat as unsupported action.
|
|
||||||
Ok(render_mcp_unsupported_action_text(
|
|
||||||
args,
|
|
||||||
"list accepts no filter argument; use `claw mcp list`",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Some(args) if matches!(args.split_whitespace().next(), Some("info" | "describe")) => {
|
|
||||||
Ok(render_mcp_unsupported_action_text(
|
|
||||||
args,
|
|
||||||
"use `claw mcp show <server>` to inspect a server",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Some(args) => Ok(render_mcp_usage(Some(args))),
|
Some(args) => Ok(render_mcp_usage(Some(args))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_mcp_unsupported_action_text(action: &str, hint: &str) -> String {
|
|
||||||
format!(
|
|
||||||
"MCP\n Error unsupported action '{action}'\n Hint {hint}\n Usage /mcp [list|show <server>|help]"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_mcp_unsupported_action_json(action: &str, hint: &str) -> Value {
|
|
||||||
json!({
|
|
||||||
"kind": "mcp",
|
|
||||||
"action": "error",
|
|
||||||
"ok": false,
|
|
||||||
"error_kind": "unsupported_action",
|
|
||||||
"requested_action": action,
|
|
||||||
"hint": hint,
|
|
||||||
"usage": {
|
|
||||||
"slash_command": "/mcp [list|show <server>|help]",
|
|
||||||
"direct_cli": "claw mcp [list|show <server>|help]",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_mcp_report_json_for(
|
fn render_mcp_report_json_for(
|
||||||
loader: &ConfigLoader,
|
loader: &ConfigLoader,
|
||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
@@ -2792,18 +2758,6 @@ fn render_mcp_report_json_for(
|
|||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(args) if args.split_whitespace().next() == Some("list") && args.contains(' ') => {
|
|
||||||
Ok(render_mcp_unsupported_action_json(
|
|
||||||
args,
|
|
||||||
"list accepts no filter argument; use `claw mcp list`",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Some(args) if matches!(args.split_whitespace().next(), Some("info" | "describe")) => {
|
|
||||||
Ok(render_mcp_unsupported_action_json(
|
|
||||||
args,
|
|
||||||
"use `claw mcp show <server>` to inspect a server",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Some(args) => Ok(render_mcp_usage_json(Some(args))),
|
Some(args) => Ok(render_mcp_usage_json(Some(args))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4791,38 +4745,6 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mcp_unsupported_actions_return_typed_error_not_generic_help() {
|
|
||||||
// `mcp info <name>` and `mcp list <filter>` must return typed errors, not raw help.
|
|
||||||
// Regression for #504: these previously fell through to render_mcp_usage with
|
|
||||||
// unexpected=arg, giving no machine-readable error_kind.
|
|
||||||
use crate::handle_mcp_slash_command_json;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
let cwd = PathBuf::from("/tmp");
|
|
||||||
|
|
||||||
let info_json = handle_mcp_slash_command_json(Some("info nonexistent"), &cwd)
|
|
||||||
.expect("info nonexistent should not error at IO level");
|
|
||||||
assert_eq!(info_json["kind"], "mcp");
|
|
||||||
assert_eq!(info_json["ok"], false);
|
|
||||||
assert_eq!(info_json["error_kind"], "unsupported_action");
|
|
||||||
assert!(info_json["hint"]
|
|
||||||
.as_str()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.contains("show"));
|
|
||||||
|
|
||||||
let list_filter_json = handle_mcp_slash_command_json(Some("list nonexistent"), &cwd)
|
|
||||||
.expect("list nonexistent should not error at IO level");
|
|
||||||
assert_eq!(list_filter_json["kind"], "mcp");
|
|
||||||
assert_eq!(list_filter_json["ok"], false);
|
|
||||||
assert_eq!(list_filter_json["error_kind"], "unsupported_action");
|
|
||||||
|
|
||||||
let describe_json = handle_mcp_slash_command_json(Some("describe myserver"), &cwd)
|
|
||||||
.expect("describe myserver should not error at IO level");
|
|
||||||
assert_eq!(describe_json["kind"], "mcp");
|
|
||||||
assert_eq!(describe_json["ok"], false);
|
|
||||||
assert_eq!(describe_json["error_kind"], "unsupported_action");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rejects_invalid_mcp_arguments() {
|
fn rejects_invalid_mcp_arguments() {
|
||||||
let show_error = parse_error_message("/mcp show alpha beta");
|
let show_error = parse_error_message("/mcp show alpha beta");
|
||||||
|
|||||||
@@ -877,17 +877,13 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
|||||||
// `missing Anthropic credentials` even though the command is purely
|
// `missing Anthropic credentials` even though the command is purely
|
||||||
// local introspection. Mirror `agents`/`mcp`/`skills`: action is the
|
// local introspection. Mirror `agents`/`mcp`/`skills`: action is the
|
||||||
// first positional arg, target is the second.
|
// first positional arg, target is the second.
|
||||||
// `plugin` (singular) and `marketplace` are aliases for `plugins`.
|
"plugins" => {
|
||||||
// All three must route to the same local handler so that no form
|
|
||||||
// falls through to the LLM/prompt path.
|
|
||||||
"plugins" | "plugin" | "marketplace" => {
|
|
||||||
let tail = &rest[1..];
|
let tail = &rest[1..];
|
||||||
let action = tail.first().cloned();
|
let action = tail.first().cloned();
|
||||||
let target = tail.get(1).cloned();
|
let target = tail.get(1).cloned();
|
||||||
if tail.len() > 2 {
|
if tail.len() > 2 {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"unexpected extra arguments after `claw {} {}`: {}",
|
"unexpected extra arguments after `claw plugins {}`: {}",
|
||||||
rest[0],
|
|
||||||
tail[..2].join(" "),
|
tail[..2].join(" "),
|
||||||
tail[2..].join(" ")
|
tail[2..].join(" ")
|
||||||
));
|
));
|
||||||
@@ -5107,17 +5103,10 @@ impl LiveCli {
|
|||||||
let cwd = env::current_dir()?;
|
let cwd = env::current_dir()?;
|
||||||
match output_format {
|
match output_format {
|
||||||
CliOutputFormat::Text => println!("{}", handle_mcp_slash_command(args, &cwd)?),
|
CliOutputFormat::Text => println!("{}", handle_mcp_slash_command(args, &cwd)?),
|
||||||
CliOutputFormat::Json => {
|
CliOutputFormat::Json => println!(
|
||||||
let value = handle_mcp_slash_command_json(args, &cwd)?;
|
"{}",
|
||||||
// Propagate ok:false → non-zero exit so automation callers
|
serde_json::to_string_pretty(&handle_mcp_slash_command_json(args, &cwd)?)?
|
||||||
// can rely on exit code instead of inspecting the envelope.
|
),
|
||||||
// (#68: mcp error envelopes previously always exited 0.)
|
|
||||||
let is_error = value.get("ok").and_then(|v| v.as_bool()) == Some(false);
|
|
||||||
println!("{}", serde_json::to_string_pretty(&value)?);
|
|
||||||
if is_error {
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# dogfood-build.sh — Build claw from current checkout and verify provenance.
|
|
||||||
#
|
|
||||||
# Injects GIT_SHA at build time so version JSON is non-null.
|
|
||||||
# Suppresses Cargo compile noise on stderr.
|
|
||||||
# Prints the verified binary path on success. Use as:
|
|
||||||
#
|
|
||||||
# CLAW=$(bash scripts/dogfood-build.sh)
|
|
||||||
#
|
|
||||||
# Then dogfood with config isolation (avoids real user config bleeding in):
|
|
||||||
#
|
|
||||||
# CLAW_CONFIG_HOME=$(mktemp -d) $CLAW plugins list --output-format json
|
|
||||||
#
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
||||||
RUST_DIR="$REPO_ROOT/rust"
|
|
||||||
BINARY="$RUST_DIR/target/debug/claw"
|
|
||||||
EXPECTED_SHA="$(git -C "$REPO_ROOT" rev-parse --short HEAD)"
|
|
||||||
|
|
||||||
echo "▶ Building claw from $REPO_ROOT" >&2
|
|
||||||
echo " Commit: $(git -C "$REPO_ROOT" log --oneline -1)" >&2
|
|
||||||
|
|
||||||
# Inject GIT_SHA so version JSON returns a non-null sha.
|
|
||||||
# Redirect cargo stderr to /dev/null to suppress compile noise;
|
|
||||||
# on build failure cargo exits non-zero and set -e aborts.
|
|
||||||
if ! GIT_SHA="$EXPECTED_SHA" cargo build \
|
|
||||||
--manifest-path "$RUST_DIR/Cargo.toml" \
|
|
||||||
-p rusty-claude-cli -q 2>/dev/null; then
|
|
||||||
# Re-run with visible output so the user sees the error
|
|
||||||
echo "✗ Build failed — rerunning with output:" >&2
|
|
||||||
GIT_SHA="$EXPECTED_SHA" cargo build \
|
|
||||||
--manifest-path "$RUST_DIR/Cargo.toml" \
|
|
||||||
-p rusty-claude-cli 2>&1 | sed 's/^/ /' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -x "$BINARY" ]]; then
|
|
||||||
echo "✗ Binary not found at $BINARY" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
BINARY_SHA=$("$BINARY" version --output-format json 2>/dev/null \
|
|
||||||
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('git_sha') or 'null')" 2>/dev/null \
|
|
||||||
|| echo "null")
|
|
||||||
|
|
||||||
if [[ "$BINARY_SHA" == "null" || -z "$BINARY_SHA" ]]; then
|
|
||||||
echo "✗ Provenance check failed: binary reports git_sha: null" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$BINARY_SHA" != "$EXPECTED_SHA" ]]; then
|
|
||||||
echo "✗ Provenance mismatch: binary=$BINARY_SHA, HEAD=$EXPECTED_SHA" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✓ Binary verified: $BINARY_SHA == HEAD" >&2
|
|
||||||
echo "" >&2
|
|
||||||
echo " export CLAW=$BINARY" >&2
|
|
||||||
echo "" >&2
|
|
||||||
echo " Dogfood with isolated config (no real user config on stderr):" >&2
|
|
||||||
echo " CLAW_ISOLATED=\$(mktemp -d)" >&2
|
|
||||||
echo " CLAW_CONFIG_HOME=\$CLAW_ISOLATED \$CLAW plugins list --output-format json" >&2
|
|
||||||
echo " rm -rf \$CLAW_ISOLATED" >&2
|
|
||||||
echo "" >&2
|
|
||||||
echo " cargo run overhead: ~1s/invocation vs 7ms for pre-built binary." >&2
|
|
||||||
echo " Prefer pre-built binary (\$CLAW) for dogfood loops." >&2
|
|
||||||
echo "$BINARY"
|
|
||||||
Reference in New Issue
Block a user