mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 16:04:03 +00:00
feat: add bash-style array slicing for $@ in prompt templates
Implements support for ${@:N} and ${@:N:L} syntax to slice argument arrays
in prompt templates, following bash conventions.
Syntax:
- ${@:N} - All arguments from Nth position onwards (1-indexed)
- ${@:N:L} - L arguments starting from Nth position
Features:
- Bash-style slicing familiar to shell users
- 1-indexed for consistency with $1, $2, etc.
- Processes before simple $@ to avoid conflicts
- No recursive substitution of patterns in arguments
- Comprehensive edge case handling
Examples:
- ${@:2} with ["a", "b", "c"] -> "b c"
- ${@:2:1} with ["a", "b", "c"] -> "b"
- ${@:99} with ["a", "b"] -> "" (empty, out of range)
Test coverage: 24 new tests, all passing (73 total)
Closes #769
This commit is contained in:
parent
2836d97735
commit
f869cc4ae5
2 changed files with 124 additions and 1 deletions
|
|
@ -181,6 +181,111 @@ describe("substituteArgs", () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// substituteArgs - Array Slicing (Bash-Style)
|
||||
// ============================================================================
|
||||
|
||||
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing literal ${@:N} syntax
|
||||
describe("substituteArgs - array slicing", () => {
|
||||
test("should slice from index (${@:N})", () => {
|
||||
expect(substituteArgs("${@:2}", ["a", "b", "c", "d"])).toBe("b c d");
|
||||
expect(substituteArgs("${@:1}", ["a", "b", "c"])).toBe("a b c");
|
||||
expect(substituteArgs("${@:3}", ["a", "b", "c", "d"])).toBe("c d");
|
||||
});
|
||||
|
||||
test("should slice with length (${@:N:L})", () => {
|
||||
expect(substituteArgs("${@:2:2}", ["a", "b", "c", "d"])).toBe("b c");
|
||||
expect(substituteArgs("${@:1:1}", ["a", "b", "c"])).toBe("a");
|
||||
expect(substituteArgs("${@:3:1}", ["a", "b", "c", "d"])).toBe("c");
|
||||
expect(substituteArgs("${@:2:3}", ["a", "b", "c", "d", "e"])).toBe("b c d");
|
||||
});
|
||||
|
||||
test("should handle out of range slices", () => {
|
||||
expect(substituteArgs("${@:99}", ["a", "b"])).toBe("");
|
||||
expect(substituteArgs("${@:5}", ["a", "b"])).toBe("");
|
||||
expect(substituteArgs("${@:10:5}", ["a", "b"])).toBe("");
|
||||
});
|
||||
|
||||
test("should handle zero-length slices", () => {
|
||||
expect(substituteArgs("${@:2:0}", ["a", "b", "c"])).toBe("");
|
||||
expect(substituteArgs("${@:1:0}", ["a", "b"])).toBe("");
|
||||
});
|
||||
|
||||
test("should handle length exceeding array", () => {
|
||||
expect(substituteArgs("${@:2:99}", ["a", "b", "c"])).toBe("b c");
|
||||
expect(substituteArgs("${@:1:10}", ["a", "b"])).toBe("a b");
|
||||
});
|
||||
|
||||
test("should process slice before simple $@", () => {
|
||||
expect(substituteArgs("${@:2} vs $@", ["a", "b", "c"])).toBe("b c vs a b c");
|
||||
expect(substituteArgs("First: ${@:1:1}, All: $@", ["x", "y", "z"])).toBe("First: x, All: x y z");
|
||||
});
|
||||
|
||||
test("should not recursively substitute slice patterns in args", () => {
|
||||
expect(substituteArgs("${@:1}", ["${@:2}", "test"])).toBe("${@:2} test");
|
||||
expect(substituteArgs("${@:2}", ["a", "${@:3}", "c"])).toBe("${@:3} c");
|
||||
});
|
||||
|
||||
test("should handle mixed usage with positional args", () => {
|
||||
expect(substituteArgs("$1: ${@:2}", ["cmd", "arg1", "arg2"])).toBe("cmd: arg1 arg2");
|
||||
expect(substituteArgs("$1 $2 ${@:3}", ["a", "b", "c", "d"])).toBe("a b c d");
|
||||
});
|
||||
|
||||
test("should treat ${@:0} as all args", () => {
|
||||
expect(substituteArgs("${@:0}", ["a", "b", "c"])).toBe("a b c");
|
||||
});
|
||||
|
||||
test("should handle empty args array", () => {
|
||||
expect(substituteArgs("${@:2}", [])).toBe("");
|
||||
expect(substituteArgs("${@:1}", [])).toBe("");
|
||||
});
|
||||
|
||||
test("should handle single arg array", () => {
|
||||
expect(substituteArgs("${@:1}", ["only"])).toBe("only");
|
||||
expect(substituteArgs("${@:2}", ["only"])).toBe("");
|
||||
});
|
||||
|
||||
test("should handle slice in middle of text", () => {
|
||||
expect(substituteArgs("Process ${@:2} with $1", ["tool", "file1", "file2"])).toBe(
|
||||
"Process file1 file2 with tool",
|
||||
);
|
||||
});
|
||||
|
||||
test("should handle multiple slices in one template", () => {
|
||||
expect(substituteArgs("${@:1:1} and ${@:2}", ["a", "b", "c"])).toBe("a and b c");
|
||||
expect(substituteArgs("${@:1:2} vs ${@:3:2}", ["a", "b", "c", "d", "e"])).toBe("a b vs c d");
|
||||
});
|
||||
|
||||
test("should handle quoted arguments in slices", () => {
|
||||
expect(substituteArgs("${@:2}", ["cmd", "first arg", "second arg"])).toBe("first arg second arg");
|
||||
});
|
||||
|
||||
test("should handle special characters in sliced args", () => {
|
||||
expect(substituteArgs("${@:2}", ["cmd", "$100", "@user", "#tag"])).toBe("$100 @user #tag");
|
||||
});
|
||||
|
||||
test("should handle unicode in sliced args", () => {
|
||||
expect(substituteArgs("${@:1}", ["日本語", "🎉", "café"])).toBe("日本語 🎉 café");
|
||||
});
|
||||
|
||||
test("should combine positional, slice, and wildcard placeholders", () => {
|
||||
const template = "Run $1 on ${@:2:2}, then process $@";
|
||||
const args = ["eslint", "file1.ts", "file2.ts", "file3.ts"];
|
||||
expect(substituteArgs(template, args)).toBe(
|
||||
"Run eslint on file1.ts file2.ts, then process eslint file1.ts file2.ts file3.ts",
|
||||
);
|
||||
});
|
||||
|
||||
test("should handle slice with no spacing", () => {
|
||||
expect(substituteArgs("prefix${@:2}suffix", ["a", "b", "c"])).toBe("prefixb csuffix");
|
||||
});
|
||||
|
||||
test("should handle large slice lengths gracefully", () => {
|
||||
const args = Array.from({ length: 10 }, (_, i) => `arg${i + 1}`);
|
||||
expect(substituteArgs("${@:5:100}", args)).toBe("arg5 arg6 arg7 arg8 arg9 arg10");
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// parseCommandArgs
|
||||
// ============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue