84488fe
chore: add prepublishOnly script
Click to expand commit body
Automatically build before npm publish
Amolith created
84488fe
chore: add prepublishOnly script
Automatically build before npm publish
Amolith created
dc9c4e6
chore: prepare for npm publish
- Remove 'private': true to allow publishing - Add description, license (MIT), and files whitelist
Amolith created
9f87c66
fix: use published pi-agent-core package from NPM
- Switch from local path dependency to ^0.52.8 from NPM - Fix TypeScript strict mode errors with index signature access
Amolith created
423d319
feat: add version flag
Add -v/--version flag support to CLI. Also fix dist/ entry point in package.json.
Amolith created
30613bb
chore: add dist to gitignore
Amolith created
5c91d1d
Fix NaN cost display and show request count in usage summary
When model cost config is zero (local/free models), the cost math produced NaN. Now cost is only shown when it's a valid positive number. Usage summary now includes the number of LLM requests (assistant messages) for better observability: 'usage: N tokens across M requests'. Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
feefce9
Upgrade to pi-agent-core 0.52.8 and adopt proper API key resolution
Replace the old pi-agent (0.9.0, ProviderTransport-based) with
pi-agent-core (0.52.8 from pi-mono) which takes getApiKey directly on
AgentOptions — no transport abstraction.
Upgrade pi-ai from 0.6.x to 0.52.8 (latest npm), which fixes the
sanitizeSurrogates crash on null content from reasoning models.
API key resolution now follows pi-coding-agent conventions:
- Custom models use a new 'api_key' config field (not headers)
- resolveConfigValue() supports three formats: bare env var names,
explicit $VAR/${VAR} references, and !shell-command execution
- resolveHeaders() applies the same resolution to custom HTTP headers
- Built-in providers fall back to pi-ai's getEnvApiKey()
All tool imports updated: AgentTool moved from pi-ai to pi-agent-core.
Runner error handling preserved: checks agent.state.error, stopReason,
and empty responses.
Config schema gains optional 'api_key' field on CustomModelSchema.
README documents value resolution formats.
Tests: 112 total (22 new) covering resolveConfigValue, expandEnvVars,
resolveHeaders, and buildGetApiKey with literal keys, env vars, $VAR
references, shell commands, and provider isolation.
Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
a4711fe
Document git tool trust boundary for cloned repos
Filesystem tools enforce workspace containment via ensureWorkspacePath(). Git tools pass refs and paths directly to simple-git, which is scoped to the workspace directory. This is by design: the user explicitly chooses which repository to clone, so its git objects are trusted content. - Add trust boundary explanation to AGENTS.md § Workspace Sandboxing - Add inline comments to all 6 git tool files referencing the docs Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
6e5e8da
Make partialObject recursively deep-partial
partialObject() wrapped each property in Type.Optional() but did not recurse into nested TObject properties. Adding a nested object to any config section would require the full object in partial config files rather than allowing a subset. Fix: when a property has Kind === 'Object' with a properties field, recursively apply partialObject before wrapping in Optional. Type.Record (also type 'object' but uses patternProperties) is correctly excluded via the Kind check. Tests: 8 new cases using synthetic schemas to verify deep-partial behavior — empty objects at every nesting level, partial inner fields, type rejection inside nested objects, Type.Record passthrough, and 3-level deep nesting. Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
f461815
Guard against symlink escape in workspace sandboxing
ensureWorkspacePath() previously used path.resolve() which normalizes .. segments but does not follow symlinks. A symlink inside the workspace pointing outside (e.g. <workspace>/escape -> /etc) would pass the textual prefix check and let tools operate beyond the sandbox boundary. Fix: - Add safeRealpath() that calls fs.realpathSync() and walks up to the nearest existing ancestor on ENOENT (needed for write targets that don't exist yet) - ensureWorkspacePath() now resolves both the workspace and target via realpath before checking containment - The cheap textual check runs first as a fast path; the realpath check catches symlink-based escapes Tests: 9 new cases covering symlink dirs/files pointing outside, symlinks within workspace (allowed), nested escapes, non-existent write targets through escaped parents, and writeWorkspaceFile integration. Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
cdfe1f4
chore: ignore node_modules
Amolith created
9cb8e33
fix: review round 2 — credential error types, test gaps, git_refs truncation
- Use ConfigError (not ToolInputError) for missing Kagi/Tabstack credentials in web command — these are config/env errors - Fix web_search test that silently passed when no error was thrown - Truncate git_refs output consistent with other git tools - Add expandHomePath unit tests - Add "test" script to package.json - Update AGENTS.md to reflect test suite existence - Minor import formatting cleanup in cli/index.ts Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
5695021
fix: review cleanup — dedup containment check, remove dead imports, fix -u edge case
- Deduplicate ensureContained() in content.ts: import ensureWorkspacePath from path-utils.ts instead of maintaining an identical private copy - Remove unused DEFAULT_MAX_BYTES/DEFAULT_MAX_LINES imports from git blame, diff, and show tools (truncateHead uses them internally) - Fix -u without value silently setting options["u"] = true: now leaves uri unset so command handlers can validate properly - Extract expandHomePath() utility for system_prompt_path tilde expansion, replacing fragile inline regex that missed bare ~ and empty HOME - Remove unnecessary parseArgs re-export from cli/index.ts (tests import directly from parse-args.ts) - Add test for -u at end of args Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
2a407a8
fix: split-on-first-delimiter bugs, -u flag guard, web_search errors, git_log validation
- #5: modelString.split(':') now splits on first colon only, preserving
segments after second colon (e.g. 'openrouter:google/gemini:free')
- #6: --key=value parsing splits on first '=' only, preserving values
containing '=' (e.g. '--header=Authorization=Bearer token')
- #7: -u short flag guards against swallowing next arg when it starts
with '-' (e.g. '-u --verbose' no longer sets uri='--verbose')
- #10: loadConfig already rethrows ConfigError directly (verified with
test); TOML parse errors wrapped as ConfigError with original message
- #11: web_search errors from kagi API now wrapped as FetchError with
query context, consistent with web_fetch error handling
- #12: git_log author/since/until validation rewritten: check
'!== undefined' then validate, so empty strings are now caught
(previously slipped through due to falsy short-circuit)
Also: extract parseArgs to src/cli/parse-args.ts for testability,
remove unused ToolInputError import from runner.ts.
Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
d570fc4
fix: truncate git tool output and enforce default log limit (#8, #9)
- git_show, git_diff, git_blame: apply truncateHead() consistent with filesystem tools (DEFAULT_MAX_LINES=2000, DEFAULT_MAX_BYTES=50KB), appending a [truncated] notice when output is clipped. - git_log: apply default limit of 20 when n is omitted, matching the schema description. - Add test/git-tools.test.ts covering both truncation and default-limit behavior. Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
514753c
fix(config): validate parsed TOML against ConfigSchema at runtime
Export ConfigSchema and PartialConfigSchema from schema.ts. Use TypeBox Value.Check/Value.Errors in loader.ts to validate parsed TOML before merging with defaults, and validate the merged result against the full schema. Invalid config now throws ConfigError with path, expected type, and actual value for each violation. Closes review issue #3. Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
4819fd7
fix: wrap all post-workspace code in try/finally to prevent leak on early throws
Move the try/finally cleanup block to start immediately after createWorkspace() in both web.ts and repo.ts so that early failures (credential validation, custom prompt read, checkout) trigger cleanup. Remove redundant manual workspace.cleanup() calls in catch blocks (clone error in repo.ts, fetch error in web.ts) since the outer finally now handles all paths, preventing double-cleanup. Fixes #2 Co-authored-by: Shelley <shelley@exe.dev>
Amolith and Shelley created
589bce1
fix(tools): enforce workspace path containment
Address review issues #1 and #4: Issue #1 - Path traversal in tools: - Add ensureWorkspacePath() call after path resolution in read, grep, ls, and find tools. Previously, paths were resolved but never checked against workspace boundaries, allowing traversal via ../ or absolute paths. - Remove tilde (~) expansion from expandPath(). In a workspace-sandboxed context, expanding ~ to the user's home directory bypasses containment. Tildes are now treated as literal path characters. - Move ensureWorkspacePath() from index.ts to path-utils.ts to avoid circular imports, re-export from index.ts for backward compatibility. Issue #4 - writeWorkspaceFile lacks traversal protection: - Add ensureContained() validation in writeWorkspaceFile() that checks the resolved file path stays within the workspace boundary before writing. Also: - Fix tsconfig.json rootDir from 'src' to '.' so test/ files are included in type checking (the include array already listed 'test'). - Add comprehensive test suite (28 tests) covering workspace containment for all affected modules.
Amolith created
1aa893a
chore: initial commit
Amolith created