@11agents/cli 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@11agents/cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "11agents local runtime and telemetry CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  import { spawn } from 'node:child_process'
2
2
  import { createHash } from 'node:crypto'
3
- import { readFileSync } from 'node:fs'
3
+ import { appendFileSync, readFileSync } from 'node:fs'
4
4
  import { appendFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
5
5
  import os from 'node:os'
6
6
  import { dirname, resolve } from 'node:path'
@@ -20,7 +20,7 @@ function sleep(ms) {
20
20
  return new Promise(resolve => setTimeout(resolve, ms))
21
21
  }
22
22
 
23
- export function runProcess(command, args, { input = '', cwd = process.cwd(), env = process.env } = {}) {
23
+ export function runProcess(command, args, { input = '', cwd = process.cwd(), env = process.env, onStdoutChunk } = {}) {
24
24
  return new Promise(resolve => {
25
25
  let settled = false
26
26
  const child = spawn(command, args, { cwd, env, stdio: ['pipe', 'pipe', 'pipe'] })
@@ -31,7 +31,10 @@ export function runProcess(command, args, { input = '', cwd = process.cwd(), env
31
31
  settled = true
32
32
  resolve(result)
33
33
  }
34
- child.stdout.on('data', chunk => { stdout += chunk })
34
+ child.stdout.on('data', chunk => {
35
+ stdout += chunk
36
+ if (onStdoutChunk) onStdoutChunk(chunk)
37
+ })
35
38
  child.stderr.on('data', chunk => { stderr += chunk })
36
39
  child.on('error', error => {
37
40
  settle({ code: 127, stdout, stderr: error.message })
@@ -769,7 +772,15 @@ async function materializeSkillsIfChanged({ task, workdir, flags, deps }) {
769
772
  const statePath = path.join(skillsDir, 'skills-state.json')
770
773
  const nextHash = stableHash(skills)
771
774
  const current = await readJsonFile(statePath, {})
772
- if (current.hash === nextHash) return { changed: false, count: skills.length }
775
+ if (current.hash === nextHash) {
776
+ const allExist = (await Promise.all(
777
+ skills.map(skill =>
778
+ readFile(path.join(skillsDir, slugify(skill.name, 'skill'), 'SKILL.md'), 'utf8')
779
+ .then(() => true).catch(() => false)
780
+ )
781
+ )).every(Boolean)
782
+ if (allExist) return { changed: false, count: skills.length }
783
+ }
773
784
 
774
785
  await mkdir(skillsDir, { recursive: true })
775
786
  for (const skill of skills) {
@@ -883,10 +894,16 @@ function agentEnvironment(task) {
883
894
 
884
895
  async function prepareRuntimeTask(task, flags, deps, config) {
885
896
  const workdir = flag(flags, 'codex-workdir') || projectDirForTask(task, flags, deps)
886
- const tmpDir = path.join(workdir, 'tmp', sanitizeTaskId(task.id))
897
+ const agentDir = path.join(workdir, 'agents', slugify(agentNameForTask(task), 'agent'))
898
+ const agentSkillsDir = path.join(agentDir, 'skills')
899
+ const agentMemoryDir = path.join(agentDir, 'memory')
900
+ const agentResultsDir = path.join(agentDir, 'results')
901
+ const tmpDir = path.join(agentDir, 'tmp', sanitizeTaskId(task.id))
887
902
  const runDir = path.join(workdir, 'runs', sanitizeTaskId(task.id))
888
903
  await mkdir(tmpDir, { recursive: true })
889
904
  await mkdir(runDir, { recursive: true })
905
+ await mkdir(agentResultsDir, { recursive: true })
906
+ await mkdir(agentMemoryDir, { recursive: true })
890
907
 
891
908
  const database = await syncDatabaseIfNeeded({ task, workdir, config, flags, deps })
892
909
  const skills = await materializeSkillsIfChanged({ task, workdir, flags, deps })
@@ -898,6 +915,10 @@ async function prepareRuntimeTask(task, flags, deps, config) {
898
915
  ...process.env,
899
916
  ...agentEnvironment(task),
900
917
  ELEVENAGENTS_PROJECT_DIR: workdir,
918
+ ELEVENAGENTS_AGENT_DIR: agentDir,
919
+ ELEVENAGENTS_AGENT_SKILLS_DIR: agentSkillsDir,
920
+ ELEVENAGENTS_AGENT_MEMORY_DIR: agentMemoryDir,
921
+ ELEVENAGENTS_AGENT_RESULTS_DIR: agentResultsDir,
901
922
  ELEVENAGENTS_TASK_TMP: tmpDir,
902
923
  ELEVENAGENTS_TASK_ID: String(task.id || ''),
903
924
  ...(projectToken ? { GTM_SWARM_TOKEN: projectToken } : {}),
@@ -905,6 +926,10 @@ async function prepareRuntimeTask(task, flags, deps, config) {
905
926
 
906
927
  return {
907
928
  workdir,
929
+ agent_dir: agentDir,
930
+ agent_skills_dir: agentSkillsDir,
931
+ agent_memory_dir: agentMemoryDir,
932
+ agent_results_dir: agentResultsDir,
908
933
  tmp_dir: tmpDir,
909
934
  run_dir: runDir,
910
935
  project_slug: projectSlugForTask(task, flags),
@@ -923,13 +948,23 @@ function buildCodexPrompt(task) {
923
948
  'Execution workspace:',
924
949
  compactJson({
925
950
  workdir: task.execution_context?.workdir,
951
+ agent_skills_dir: task.execution_context?.agent_skills_dir,
952
+ agent_memory_dir: task.execution_context?.agent_memory_dir,
953
+ agent_results_dir: task.execution_context?.agent_results_dir,
926
954
  tmp_dir: task.execution_context?.tmp_dir,
927
955
  project_slug: task.execution_context?.project_slug,
928
- knowledge_base: './knowledge_base/',
929
- rule: 'Treat the project directory as read-only project context except ./knowledge_base/ for durable project knowledge updates and ./tmp/<taskId>/ for temporary scratch files.',
930
- cleanup: 'Temporary files under ./tmp/<taskId>/ are removed by the CLI after the task finishes.',
956
+ knowledge_base: task.execution_context?.workdir ? path.join(task.execution_context.workdir, 'knowledge_base') : './knowledge_base',
931
957
  }),
932
958
  '',
959
+ 'Directory rules — follow strictly:',
960
+ '1. agent_skills_dir: BEFORE starting work, list files here to see which skills are installed.',
961
+ ' Each skill is a subdirectory with a SKILL.md. If a skill you need is present, read its SKILL.md and follow it.',
962
+ '2. agent_results_dir: Write all final deliverables and task outputs here (generated content, reports, files).',
963
+ '3. agent_memory_dir: After completing work, record key decisions, facts, and learned context here.',
964
+ ' Append to index.qmd or create new .qmd files. This memory persists across future task runs.',
965
+ '4. tmp_dir: Scratch/working files only. This directory is deleted by the CLI after the task finishes — do NOT put deliverables here.',
966
+ '5. workdir: Treat as read-only project context except for the four agent directories and knowledge_base above.',
967
+ '',
933
968
  'Task context:',
934
969
  compactJson({
935
970
  queue_event: task.queue_event,
@@ -1012,7 +1047,10 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
1012
1047
  })
1013
1048
  deps.log(JSON.stringify({ running: 'codex exec', command: commandLine, workdir }, null, 2))
1014
1049
  const taskEnv = task.execution_context?.env || process.env
1015
- let result = await deps.runProcess(codexBin, args, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv })
1050
+ const stdoutLogPath = runDir ? path.join(runDir, 'stdout.log') : ''
1051
+ if (stdoutLogPath) await writeRunFile(runDir, 'stdout.log', '')
1052
+ const onStdoutChunk = stdoutLogPath ? (chunk) => { try { appendFileSync(stdoutLogPath, chunk) } catch {} } : undefined
1053
+ let result = await deps.runProcess(codexBin, args, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv, onStdoutChunk })
1016
1054
 
1017
1055
  // When --codex-sandbox is set but bwrap is unavailable in the container (missing CAP_NET_ADMIN),
1018
1056
  // fall back to --yolo exec which runs commands directly without bwrap.
@@ -1023,7 +1061,8 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
1023
1061
  const fallbackLine = [codexBin, ...fallbackArgs].map(v => JSON.stringify(String(v))).join(' ')
1024
1062
  deps.log(JSON.stringify({ warning: 'bwrap unavailable in this environment, retrying without sandbox', fallback_command: fallbackLine }))
1025
1063
  await updateRunMeta(runDir, { bwrap_fallback: true, fallback_command_line: fallbackLine })
1026
- result = await deps.runProcess(codexBin, fallbackArgs, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv })
1064
+ if (stdoutLogPath) await writeRunFile(runDir, 'stdout.log', '')
1065
+ result = await deps.runProcess(codexBin, fallbackArgs, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv, onStdoutChunk })
1027
1066
  }
1028
1067
  }
1029
1068
  const rawStdout = String(result.stdout || '')
@@ -1106,10 +1145,14 @@ async function runClaude({ task, prompt, flags = {}, deps }) {
1106
1145
 
1107
1146
  deps.log(JSON.stringify({ running: 'claude --print', command: commandLine, workdir }, null, 2))
1108
1147
 
1148
+ const stdoutLogPath = runDir ? path.join(runDir, 'stdout.log') : ''
1149
+ if (stdoutLogPath) await writeRunFile(runDir, 'stdout.log', '')
1150
+ const onStdoutChunk = stdoutLogPath ? (chunk) => { try { appendFileSync(stdoutLogPath, chunk) } catch {} } : undefined
1109
1151
  const result = await deps.runProcess(claudeBin, args, {
1110
1152
  input: prompt,
1111
1153
  cwd: workdir,
1112
1154
  env: task.execution_context?.env || process.env,
1155
+ onStdoutChunk,
1113
1156
  })
1114
1157
 
1115
1158
  const rawStdout = String(result.stdout || '')