From 3af8339be819031452512ed9e727ef80110312d4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 21 Jul 2026 10:37:11 +0000 Subject: [PATCH] feat(agent): generate parseCommandArgs and substituteArgs via fluessig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swap the hand-written `#[napi]` prompt-template helpers (agent harness/prompt-templates.ts: `parseCommandArgs`, `substituteArgs`) to the fluessig-generated path: author the two ops in schema/api.json, add the two `core_impl` delegations to core_impl.rs, regenerate src/generated.rs, and delete the hand-written exports from src/agent.rs. This is the first swap in the `agent` package. Both ops are proven primitive shapes: `parseCommandArgs(argsString: string) -> Array` (string -> list) and `substituteArgs(content: string, args: Array) -> string`, where `args` is the first `Vec` PARAM in the schema — fluessig lowers the `list` param to `args: Vec` with no gap. The `core_impl` methods reach the SAME `pidgin_agent::harness::prompt_templates` functions the hand-written exports called (`parse_command_args` and `substitute_args`, the latter borrowing the args as `&[&str]`), so JS-visible behavior is byte-for-byte unchanged. Verified: each swapped symbol's generated `.d.ts` JSDoc+signature block, its `.js` `module.exports` line, and its nativeBinding destructure entry are byte-identical to a pre-swap build of the base (only file position moves into the generated cluster). cargo build/fmt/clippy clean; codespell 0; straitjacket 0 errors; the prompt-templates oracle slice (agent/test/harness/prompt-templates.test.ts, 5 tests) passes 5/5 against the locally-built generated addon. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_017xL2W7dkhPsTSRuXQNCDeY --- crates/pidgin-napi/schema/api.json | 46 +++++++++++++++++++++++++++++ crates/pidgin-napi/src/agent.rs | 15 ---------- crates/pidgin-napi/src/core_impl.rs | 9 ++++++ crates/pidgin-napi/src/generated.rs | 16 ++++++++++ 4 files changed, 71 insertions(+), 15 deletions(-) diff --git a/crates/pidgin-napi/schema/api.json b/crates/pidgin-napi/schema/api.json index 2c3927f2..8e8a5745 100644 --- a/crates/pidgin-napi/schema/api.json +++ b/crates/pidgin-napi/schema/api.json @@ -634,6 +634,52 @@ "name": "formatMissingSessionCwdPrompt" } } + }, + { + "name": "parseCommandArgs", + "doc": "`parseCommandArgs` (harness/prompt-templates.ts): split an argument string\nusing simple shell-style single and double quotes.", + "shape": "unary", + "infallible": true, + "readonly": true, + "params": [ + { + "name": "argsString", + "type": "string" + } + ], + "returns": { + "list": "string" + }, + "bindings": { + "node": { + "name": "parseCommandArgs" + } + } + }, + { + "name": "substituteArgs", + "doc": "`substituteArgs` (harness/prompt-templates.ts): substitute prompt-template\nplaceholders (`$1`, `$@`, `$ARGUMENTS`, `${@:N}`, `${@:N:L}`) with args.", + "shape": "unary", + "infallible": true, + "readonly": true, + "params": [ + { + "name": "content", + "type": "string" + }, + { + "name": "args", + "type": { + "list": "string" + } + } + ], + "returns": "string", + "bindings": { + "node": { + "name": "substituteArgs" + } + } } ] }, diff --git a/crates/pidgin-napi/src/agent.rs b/crates/pidgin-napi/src/agent.rs index b61b7ef8..d3b02beb 100644 --- a/crates/pidgin-napi/src/agent.rs +++ b/crates/pidgin-napi/src/agent.rs @@ -98,21 +98,6 @@ pub fn format_skill_invocation( )) } -/// `parseCommandArgs` (harness/prompt-templates.ts): split an argument string -/// using simple shell-style single and double quotes. -#[napi(js_name = "parseCommandArgs")] -pub fn parse_command_args(args_string: String) -> Vec { - pidgin_agent::harness::prompt_templates::parse_command_args(&args_string) -} - -/// `substituteArgs` (harness/prompt-templates.ts): substitute prompt-template -/// placeholders (`$1`, `$@`, `$ARGUMENTS`, `${@:N}`, `${@:N:L}`) with args. -#[napi(js_name = "substituteArgs")] -pub fn substitute_args(content: String, args: Vec) -> String { - let refs: Vec<&str> = args.iter().map(String::as_str).collect(); - pidgin_agent::harness::prompt_templates::substitute_args(&content, &refs) -} - /// `formatPromptTemplateInvocation` (harness/prompt-templates.ts): substitute /// positional arguments into a template's content. pi's `PromptTemplate` /// crosses as a JSON object; the argument list crosses as a string array. diff --git a/crates/pidgin-napi/src/core_impl.rs b/crates/pidgin-napi/src/core_impl.rs index 6bc56650..1c12e3a0 100644 --- a/crates/pidgin-napi/src/core_impl.rs +++ b/crates/pidgin-napi/src/core_impl.rs @@ -187,6 +187,15 @@ impl crate::generated::PidginCore for PidginImpl { fn format_missing_session_cwd_prompt(issue: crate::generated::SessionCwdIssueJs) -> String { pidgin_coding::core::session_cwd::format_missing_session_cwd_prompt(&issue.into()) } + + fn parse_command_args(args_string: String) -> Vec { + pidgin_agent::harness::prompt_templates::parse_command_args(&args_string) + } + + fn substitute_args(content: String, args: Vec) -> String { + let refs: Vec<&str> = args.iter().map(String::as_str).collect(); + pidgin_agent::harness::prompt_templates::substitute_args(&content, &refs) + } } // --- coding-agent session-cwd seam (core/session-cwd.ts) -------------------- diff --git a/crates/pidgin-napi/src/generated.rs b/crates/pidgin-napi/src/generated.rs index 330430c0..9bb9444c 100644 --- a/crates/pidgin-napi/src/generated.rs +++ b/crates/pidgin-napi/src/generated.rs @@ -89,6 +89,8 @@ pub trait PidginCore: Sized + Send + Sync + 'static { ) -> Option; fn format_missing_session_cwd_error(issue: SessionCwdIssueJs) -> String; fn format_missing_session_cwd_prompt(issue: SessionCwdIssueJs) -> String; + fn parse_command_args(args_string: String) -> Vec; + fn substitute_args(content: String, args: Vec) -> String; } /// The `KeybindingsManagerCore` contract — implement over the engine in `crate::core_impl`. @@ -322,6 +324,20 @@ pub fn format_missing_session_cwd_prompt(issue: SessionCwdIssueJs) -> String { ::format_missing_session_cwd_prompt(issue) } +/// `parseCommandArgs` (harness/prompt-templates.ts): split an argument string +/// using simple shell-style single and double quotes. +#[napi(js_name = "parseCommandArgs")] +pub fn parse_command_args(args_string: String) -> Vec { + ::parse_command_args(args_string) +} + +/// `substituteArgs` (harness/prompt-templates.ts): substitute prompt-template +/// placeholders (`$1`, `$@`, `$ARGUMENTS`, `${@:N}`, `${@:N:L}`) with args. +#[napi(js_name = "substituteArgs")] +pub fn substitute_args(content: String, args: Vec) -> String { + ::substitute_args(content, args) +} + /// The Rust-backed keybindings core, exposed to JavaScript as /// `KeybindingsManagerCore`. #[napi]