Further Agent updates
This commit is contained in:
@@ -281,7 +281,12 @@ function bareAgentToolDefinitions() {
|
||||
include_stat: {
|
||||
type: 'boolean',
|
||||
description: 'If true, call stat on each entry (slower; default false)'
|
||||
}
|
||||
},
|
||||
tree: {
|
||||
type: 'boolean',
|
||||
description: 'Grok-style bounded BFS tree instead of a flat listing'
|
||||
},
|
||||
max_depth: { type: 'integer', description: 'Tree depth (default 6)' }
|
||||
},
|
||||
required: ['path']
|
||||
}
|
||||
@@ -1117,6 +1122,10 @@ function bareAgentToolDefinitions() {
|
||||
files_with_matches: {
|
||||
type: 'boolean',
|
||||
description: 'Return matching paths only'
|
||||
},
|
||||
output_mode: {
|
||||
type: 'string',
|
||||
description: 'content (default) | files_with_matches | count'
|
||||
}
|
||||
},
|
||||
required: ['pattern']
|
||||
@@ -1362,6 +1371,168 @@ function bareAgentToolDefinitions() {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'git_log',
|
||||
description:
|
||||
'git log --oneline (optional -N / path) via /bin/git. Prefer over raw run_command.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
cwd: { type: 'string' },
|
||||
max: { type: 'integer', description: 'Default 20, cap 80' },
|
||||
path: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'git_show',
|
||||
description: 'git show <rev> (optional path / --stat) via /bin/git.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
cwd: { type: 'string' },
|
||||
rev: { type: 'string', description: 'Commit-ish (default HEAD)' },
|
||||
path: { type: 'string' },
|
||||
stat: { type: 'boolean' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'git_blame',
|
||||
description: 'git blame a file via /bin/git.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
cwd: { type: 'string' },
|
||||
path: { type: 'string' }
|
||||
},
|
||||
required: ['path']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'copy_path',
|
||||
description:
|
||||
'Copy a file or directory on the VFS (parents created). Prefer this over run_command cp.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
from_path: { type: 'string' },
|
||||
to_path: { type: 'string' }
|
||||
},
|
||||
required: ['from_path', 'to_path']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'diff_files',
|
||||
description:
|
||||
'Unified diff of two UTF-8 files (no git). Prefer this when comparing two paths that are not a git hunk.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
from_path: { type: 'string' },
|
||||
to_path: { type: 'string' },
|
||||
context: { type: 'integer', description: 'Context lines (default 3)' }
|
||||
},
|
||||
required: ['from_path', 'to_path']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'find_symbol',
|
||||
description:
|
||||
'Find likely definitions of a symbol (function/class/const/def/fn) under a root. Prefer over ad-hoc grep for go-to-definition.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
root: { type: 'string' },
|
||||
glob: { type: 'string' },
|
||||
max: { type: 'integer' }
|
||||
},
|
||||
required: ['name']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'create_skill',
|
||||
description:
|
||||
'Write a Grok-style SKILL.md (YAML frontmatter + body) under ~/.agent/workspace/skills/<id>/.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Folder name (kebab-case)' },
|
||||
name: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
body: { type: 'string', description: 'Markdown instructions after frontmatter' }
|
||||
},
|
||||
required: ['id', 'description']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'remember',
|
||||
description:
|
||||
'Save a durable FACT to MEMORY.md now (Grok /remember). Same store as memory_append.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
text: { type: 'string' },
|
||||
daily: { type: 'boolean' }
|
||||
},
|
||||
required: ['text']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'rewind_session',
|
||||
description:
|
||||
'Grok /rewind: drop the last N user turns from ~/.agent/history.json (and everything after). Default 1 turn.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
steps: { type: 'integer', description: 'How many user turns to drop (default 1)' },
|
||||
user_index: { type: 'integer', description: 'Rewind to this 0-based user turn instead' },
|
||||
keep_user: { type: 'boolean', description: 'Keep the target user prompt' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'export_session',
|
||||
description:
|
||||
'Grok /export: write the current chat history as Markdown. Default ~/.agent/export.md.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
@@ -1484,6 +1655,13 @@ async function bareAgentDispatchTool(o) {
|
||||
if (toolName === 'ripgrep') {
|
||||
return bareAgentDispatchTool({ ...o, toolName: 'grep' })
|
||||
}
|
||||
if (toolName === 'remember') {
|
||||
return bareAgentDispatchTool({
|
||||
...o,
|
||||
toolName: 'memory_append',
|
||||
argsJson: JSON.stringify({ ...args, kind: 'FACT' })
|
||||
})
|
||||
}
|
||||
|
||||
const vfs = ctx.vfs
|
||||
const mutateDenyPrefixes = (function () {
|
||||
@@ -2487,6 +2665,14 @@ async function bareAgentDispatchTool(o) {
|
||||
if (!bareAgentPathAllowed(dir)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
}
|
||||
if (Boolean(args.tree) && typeof bareAgentRenderTree === 'function') {
|
||||
appendProgress('list_directory tree ' + dir)
|
||||
const tree = await bareAgentRenderTree(ctx, dir, {
|
||||
max: maxEnt,
|
||||
maxDepth: args.max_depth
|
||||
})
|
||||
return bareAgentJsonResult(tree)
|
||||
}
|
||||
if (!vfs || typeof vfs.readdir !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'readdir unavailable' })
|
||||
}
|
||||
@@ -3509,7 +3695,8 @@ async function bareAgentDispatchTool(o) {
|
||||
after: args.after,
|
||||
context: args.context,
|
||||
max_matches: args.max_matches,
|
||||
files_with_matches: Boolean(args.files_with_matches)
|
||||
files_with_matches: Boolean(args.files_with_matches),
|
||||
output_mode: typeof args.output_mode === 'string' ? args.output_mode : ''
|
||||
})
|
||||
return bareAgentJsonResult(out)
|
||||
}
|
||||
@@ -3976,6 +4163,170 @@ async function bareAgentDispatchTool(o) {
|
||||
return bareAgentJsonResult({ ok: true, query, hits })
|
||||
}
|
||||
|
||||
if (toolName === 'git_log' || toolName === 'git_show' || toolName === 'git_blame') {
|
||||
const cwd =
|
||||
(typeof args.cwd === 'string' && args.cwd.trim()) ||
|
||||
(ctx.env && typeof ctx.env === 'object'
|
||||
? String(ctx.env.PWD || ctx.env.CWD || home || '').trim()
|
||||
: '') ||
|
||||
home ||
|
||||
'/home'
|
||||
if (!execLine) return bareAgentJsonResult({ ok: false, error: 'execLine unavailable' })
|
||||
const quoted = bareAgentShellQuote(cwd)
|
||||
let cmd = 'git -C ' + quoted + ' '
|
||||
if (toolName === 'git_log') {
|
||||
const max = Math.min(80, Math.max(1, Math.floor(Number(args.max) || 20)))
|
||||
const p = typeof args.path === 'string' ? args.path.trim() : ''
|
||||
cmd += 'log --oneline -' + String(max) + (p ? ' -- ' + bareAgentShellQuote(p) : '')
|
||||
} else if (toolName === 'git_show') {
|
||||
const rev = typeof args.rev === 'string' && args.rev.trim() ? args.rev.trim() : 'HEAD'
|
||||
const p = typeof args.path === 'string' ? args.path.trim() : ''
|
||||
cmd +=
|
||||
'show ' +
|
||||
(args.stat ? '--stat ' : '') +
|
||||
bareAgentShellQuote(rev) +
|
||||
(p ? ' -- ' + bareAgentShellQuote(p) : '')
|
||||
} else {
|
||||
const p = typeof args.path === 'string' ? args.path.trim() : ''
|
||||
if (!p) return bareAgentJsonResult({ ok: false, error: 'path_required' })
|
||||
cmd += 'blame -- ' + bareAgentShellQuote(p)
|
||||
}
|
||||
appendProgress(toolName + ' ' + cwd)
|
||||
const r = await captureExec(cmd, 30000)
|
||||
return bareAgentJsonResult(
|
||||
r.ok === false ? r : { ok: true, cwd, stdout_stderr: r.stdout_stderr }
|
||||
)
|
||||
}
|
||||
|
||||
if (toolName === 'copy_path') {
|
||||
const from = typeof args.from_path === 'string' ? args.from_path.trim() : ''
|
||||
const to = typeof args.to_path === 'string' ? args.to_path.trim() : ''
|
||||
if (
|
||||
!from ||
|
||||
!to ||
|
||||
from.includes('..') ||
|
||||
to.includes('..') ||
|
||||
!bareAgentPathAllowed(from) ||
|
||||
!bareAgentPathAllowedMutate(to, mutateDenyPrefixes)
|
||||
) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
}
|
||||
if (typeof bareAgentCopyPath !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'copy_unavailable' })
|
||||
}
|
||||
appendProgress('copy_path ' + from)
|
||||
const out = await bareAgentCopyPath(ctx, from, to)
|
||||
return bareAgentJsonResult(out)
|
||||
}
|
||||
|
||||
if (toolName === 'diff_files') {
|
||||
const from = typeof args.from_path === 'string' ? args.from_path.trim() : ''
|
||||
const to = typeof args.to_path === 'string' ? args.to_path.trim() : ''
|
||||
if (!from || !to || !bareAgentPathAllowed(from) || !bareAgentPathAllowed(to)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
}
|
||||
if (typeof bareAgentUnifiedDiff !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'diff_unavailable' })
|
||||
}
|
||||
const a = await bareAgentReadTextFile(ctx, from)
|
||||
const b = await bareAgentReadTextFile(ctx, to)
|
||||
appendProgress('diff_files ' + from)
|
||||
const out = bareAgentUnifiedDiff(a, b, {
|
||||
from,
|
||||
to,
|
||||
context: args.context
|
||||
})
|
||||
return bareAgentJsonResult(out)
|
||||
}
|
||||
|
||||
if (toolName === 'find_symbol') {
|
||||
const name = typeof args.name === 'string' ? args.name.trim() : ''
|
||||
if (!name) return bareAgentJsonResult({ ok: false, error: 'name_required' })
|
||||
const root =
|
||||
(typeof args.root === 'string' && args.root.trim()) || home || '/home'
|
||||
if (!bareAgentPathAllowed(root) || typeof bareAgentFindSymbol !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'find_symbol_unavailable' })
|
||||
}
|
||||
appendProgress('find_symbol ' + name)
|
||||
const out = await bareAgentFindSymbol(ctx, root, name, {
|
||||
glob: typeof args.glob === 'string' ? args.glob : '',
|
||||
max: args.max
|
||||
})
|
||||
return bareAgentJsonResult(out)
|
||||
}
|
||||
|
||||
if (toolName === 'create_skill') {
|
||||
const id = String(args.id || '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9-]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
if (!id) return bareAgentJsonResult({ ok: false, error: 'id_required' })
|
||||
const destRoot =
|
||||
(typeof paths.workspaceSkills === 'string' && paths.workspaceSkills) ||
|
||||
(typeof paths.workspace === 'string' ? paths.workspace + '/skills' : paths.dir + '/workspace/skills')
|
||||
const dest = destRoot + '/' + id + '/SKILL.md'
|
||||
if (!bareAgentPathAllowedMutate(dest, mutateDenyPrefixes)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
}
|
||||
const md =
|
||||
typeof bareAgentSkillMarkdown === 'function'
|
||||
? bareAgentSkillMarkdown({
|
||||
name: typeof args.name === 'string' ? args.name : id,
|
||||
description: typeof args.description === 'string' ? args.description : id,
|
||||
body: typeof args.body === 'string' ? args.body : ''
|
||||
})
|
||||
: String(args.body || '')
|
||||
await bareAgentWriteTextFile(ctx, dest, md)
|
||||
appendProgress('create_skill ' + id)
|
||||
return bareAgentJsonResult({ ok: true, id, path: dest })
|
||||
}
|
||||
|
||||
if (toolName === 'rewind_session') {
|
||||
const histPath = paths.history || paths.dir + '/history.json'
|
||||
const raw = await bareAgentReadJsonFile(ctx, histPath, [])
|
||||
if (typeof bareAgentRewindHistory !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'rewind_unavailable' })
|
||||
}
|
||||
const out = bareAgentRewindHistory(Array.isArray(raw) ? raw : [], {
|
||||
steps: args.steps,
|
||||
userIndex: args.user_index,
|
||||
keep_user: args.keep_user
|
||||
})
|
||||
if (!out.ok) return bareAgentJsonResult(out)
|
||||
await bareAgentSaveHistory(ctx, histPath, out.messages)
|
||||
appendProgress('rewind_session dropped=' + String(out.dropped))
|
||||
return bareAgentJsonResult({
|
||||
ok: true,
|
||||
dropped: out.dropped,
|
||||
remaining: out.messages.length,
|
||||
target: out.target || null,
|
||||
keep_user: Boolean(out.keep_user)
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'export_session') {
|
||||
const histPath = paths.history || paths.dir + '/history.json'
|
||||
const dest =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(paths.dir ? paths.dir + '/export.md' : '/tmp/agent-export.md')
|
||||
if (!bareAgentPathAllowedMutate(dest, mutateDenyPrefixes)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
}
|
||||
const raw = await bareAgentReadJsonFile(ctx, histPath, [])
|
||||
const md =
|
||||
typeof bareAgentExportTranscript === 'function'
|
||||
? bareAgentExportTranscript(Array.isArray(raw) ? raw : [])
|
||||
: ''
|
||||
await bareAgentWriteTextFile(ctx, dest, md)
|
||||
appendProgress('export_session ' + dest)
|
||||
return bareAgentJsonResult({
|
||||
ok: true,
|
||||
path: dest,
|
||||
messages: Array.isArray(raw) ? raw.length : 0
|
||||
})
|
||||
}
|
||||
|
||||
return bareAgentJsonResult({ ok: false, error: 'unknown_tool ' + toolName })
|
||||
} catch (e) {
|
||||
const msg =
|
||||
|
||||
Reference in New Issue
Block a user