@mndrk/agx 1.4.2 → 1.4.4

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/README.md CHANGED
@@ -18,8 +18,8 @@ agx -p "what does this function do?"
18
18
  # With persistent memory (auto-detected)
19
19
  agx claude -p "continue working on the todo app"
20
20
 
21
- # Auto-create task (for agents, non-interactive)
22
- agx claude --auto-task -p "Build a todo app with React"
21
+ # Autonomous mode - creates task and works until done
22
+ agx claude --autonomous -p "Build a todo app with React"
23
23
  ```
24
24
 
25
25
  ## Memory Integration
@@ -74,10 +74,9 @@ Agents control state via markers in their output:
74
74
  --interactive, -i Force interactive mode
75
75
  --mem Enable mem integration (auto-detected)
76
76
  --no-mem Disable mem integration
77
- --auto-task Auto-create task from prompt
77
+ --autonomous, -a Create task and run autonomously (starts daemon)
78
78
  --task <name> Specific task name
79
79
  --criteria <text> Success criterion (repeatable)
80
- --daemon Loop on [continue] marker
81
80
  ```
82
81
 
83
82
  ## Claude Code Plugin
@@ -100,8 +99,32 @@ agx config # Configuration menu
100
99
  agx status # Show current config
101
100
  agx skill # View LLM skill
102
101
  agx skill install # Install skill to Claude/Gemini
102
+
103
+ # Daemon management
104
+ agx daemon start # Start background daemon
105
+ agx daemon stop # Stop daemon
106
+ agx daemon status # Check if running
107
+ agx daemon logs # Show recent logs
103
108
  ```
104
109
 
110
+ ## Autonomous Mode
111
+
112
+ Start a task that runs autonomously until complete:
113
+
114
+ ```bash
115
+ agx claude --autonomous -p "Build a React todo app with auth"
116
+ # ✓ Created task: build-react-todo
117
+ # ✓ Mapped: ~/Projects/app → task/build-react-todo
118
+ # ✓ Daemon started (pid 12345)
119
+ # ✓ Autonomous mode: daemon will continue work every 15m
120
+ ```
121
+
122
+ The daemon:
123
+ - Runs in background (survives terminal close)
124
+ - Wakes every 15 minutes
125
+ - Continues work on active tasks
126
+ - Stops when task is `[done]` or `[blocked]`
127
+
105
128
  ## Loop Control
106
129
 
107
130
  The agent controls execution flow via markers:
@@ -0,0 +1,36 @@
1
+ # /agx:daemon - Manage Background Daemon
2
+
3
+ Control the agx daemon that runs autonomous tasks.
4
+
5
+ ## Usage
6
+ ```
7
+ /agx:daemon <action>
8
+ ```
9
+
10
+ ## Actions
11
+ - `start` - Start the daemon
12
+ - `stop` - Stop the daemon
13
+ - `status` - Check if running
14
+ - `logs` - Show recent logs
15
+
16
+ ## Implementation
17
+
18
+ ```bash
19
+ # Check status
20
+ agx daemon status
21
+
22
+ # Start if needed
23
+ agx daemon start
24
+
25
+ # View logs
26
+ agx daemon logs
27
+
28
+ # Stop when done
29
+ agx daemon stop
30
+ ```
31
+
32
+ The daemon:
33
+ - Runs in background (survives terminal close)
34
+ - Wakes every 15 minutes
35
+ - Continues work on active tasks
36
+ - Stops tasks when they output [done] or [blocked]
package/commands/spawn.md CHANGED
@@ -1,6 +1,6 @@
1
- # /agx:spawn - Spawn Background Agent
1
+ # /agx:spawn - Start Autonomous Task
2
2
 
3
- Spawn a background agent task with automatic wake schedule.
3
+ Spawn an autonomous agent task that runs until complete.
4
4
 
5
5
  ## Usage
6
6
  ```
@@ -15,17 +15,17 @@ Spawn a background agent task with automatic wake schedule.
15
15
  First, ensure you're in the correct project directory, then:
16
16
 
17
17
  ```bash
18
- agx claude --auto-task -p "$ARGUMENTS"
18
+ agx claude --autonomous -p "$ARGUMENTS"
19
19
  ```
20
20
 
21
21
  This will:
22
22
  1. Create a mem task branch
23
- 2. Set wake schedule (every 15m)
24
- 3. Start the agent working
23
+ 2. Start the agx daemon (if not running)
24
+ 3. Begin working on the task
25
+ 4. Daemon wakes every 15m to continue until [done]
25
26
 
26
- After spawning, install the wake cron:
27
+ Check status with:
27
28
  ```bash
28
- (crontab -l 2>/dev/null; mem cron export) | crontab -
29
+ agx daemon status
30
+ mem status
29
31
  ```
30
-
31
- Report the task name and confirm wake schedule is set.
package/index.js CHANGED
@@ -36,7 +36,7 @@ agx auto-detects \`~/.mem\` and loads context:
36
36
  agx claude -p "continue working"
37
37
 
38
38
  # Auto-create task (non-interactive, for agents)
39
- agx claude --auto-task -p "Build todo app"
39
+ agx claude --autonomous -p "Build todo app"
40
40
 
41
41
  # Explicit task with criteria
42
42
  agx claude --task todo-app \\
@@ -79,7 +79,7 @@ Use these in agent output to save state:
79
79
  | --yolo, -y | Skip permission prompts |
80
80
  | --mem | Enable mem (auto-detected) |
81
81
  | --no-mem | Disable mem |
82
- | --auto-task | Auto-create task from prompt |
82
+ | --autonomous, -a | Create task and run autonomously until done |
83
83
  | --task NAME | Specific task name |
84
84
  | --criteria "..." | Success criterion (repeatable) |
85
85
  | --daemon | Loop on [continue] marker |
@@ -148,7 +148,7 @@ function findMemDir(startDir = process.cwd()) {
148
148
  while (dir !== path.dirname(dir)) {
149
149
  const memDir = path.join(dir, '.mem');
150
150
  if (fs.existsSync(memDir) && fs.existsSync(path.join(memDir, '.git'))) {
151
- return memDir;
151
+ return { memDir, taskBranch: null, projectDir: dir, isLocal: true };
152
152
  }
153
153
  dir = path.dirname(dir);
154
154
  }
@@ -162,14 +162,14 @@ function findMemDir(startDir = process.cwd()) {
162
162
  const index = JSON.parse(fs.readFileSync(indexFile, 'utf8'));
163
163
  // Exact match
164
164
  if (index[startDir]) {
165
- return globalMem;
165
+ return { memDir: globalMem, taskBranch: index[startDir], projectDir: startDir, isLocal: false };
166
166
  }
167
167
  // Check parent directories (for monorepo/subdirectory usage)
168
168
  let checkDir = startDir;
169
169
  while (checkDir !== path.dirname(checkDir)) {
170
170
  checkDir = path.dirname(checkDir);
171
171
  if (index[checkDir]) {
172
- return globalMem;
172
+ return { memDir: globalMem, taskBranch: index[checkDir], projectDir: checkDir, isLocal: false };
173
173
  }
174
174
  }
175
175
  } catch {}
@@ -179,11 +179,22 @@ function findMemDir(startDir = process.cwd()) {
179
179
  return null;
180
180
  }
181
181
 
182
- // Load mem context
183
- function loadMemContext(memDir) {
182
+ // Load mem context for a specific task
183
+ function loadMemContext(memInfo) {
184
184
  try {
185
+ // If we know the task branch, switch to it first to ensure correct context
186
+ if (memInfo.taskBranch && !memInfo.isLocal) {
187
+ try {
188
+ execSync(`git checkout ${memInfo.taskBranch}`, {
189
+ cwd: memInfo.memDir,
190
+ stdio: ['pipe', 'pipe', 'pipe']
191
+ });
192
+ } catch {}
193
+ }
194
+
195
+ // Run mem context from the project directory
185
196
  const result = execSync('mem context', {
186
- cwd: path.dirname(memDir),
197
+ cwd: memInfo.projectDir,
187
198
  encoding: 'utf8',
188
199
  stdio: ['pipe', 'pipe', 'pipe']
189
200
  });
@@ -225,8 +236,9 @@ function parseMemMarkers(output) {
225
236
  }
226
237
 
227
238
  // Apply mem markers - returns control signals
228
- function applyMemMarkers(markers, memDir) {
229
- const workDir = path.dirname(memDir);
239
+ function applyMemMarkers(markers, memInfo) {
240
+ // Use project directory for commands (not parent of ~/.mem)
241
+ const workDir = memInfo.projectDir || path.dirname(memInfo.memDir || memInfo);
230
242
  const result = {
231
243
  approvals: [],
232
244
  shouldContinue: false,
@@ -300,8 +312,8 @@ function applyMemMarkers(markers, memDir) {
300
312
  }
301
313
 
302
314
  // Create subtasks from split markers
303
- function createSubtasks(splits, memDir) {
304
- const workDir = path.dirname(memDir);
315
+ function createSubtasks(splits, memInfo) {
316
+ const workDir = memInfo.projectDir || path.dirname(memInfo.memDir || memInfo);
305
317
 
306
318
  for (const split of splits) {
307
319
  try {
@@ -317,6 +329,200 @@ function createSubtasks(splits, memDir) {
317
329
  }
318
330
  }
319
331
 
332
+ // ==================== DAEMON ====================
333
+
334
+ const DAEMON_PID_FILE = path.join(process.env.HOME || process.env.USERPROFILE, '.agx', 'daemon.pid');
335
+ const DAEMON_LOG_FILE = path.join(process.env.HOME || process.env.USERPROFILE, '.agx', 'daemon.log');
336
+ const DAEMON_STATE_FILE = path.join(process.env.HOME || process.env.USERPROFILE, '.agx', 'daemon-state.json');
337
+
338
+ function isDaemonRunning() {
339
+ try {
340
+ if (!fs.existsSync(DAEMON_PID_FILE)) return false;
341
+ const pid = parseInt(fs.readFileSync(DAEMON_PID_FILE, 'utf8').trim());
342
+ process.kill(pid, 0); // Check if process exists
343
+ return pid;
344
+ } catch {
345
+ return false;
346
+ }
347
+ }
348
+
349
+ function startDaemon() {
350
+ const existingPid = isDaemonRunning();
351
+ if (existingPid) {
352
+ console.log(`${c.dim}Daemon already running (pid ${existingPid})${c.reset}`);
353
+ return existingPid;
354
+ }
355
+
356
+ // Ensure .agx directory exists
357
+ const agxDir = path.dirname(DAEMON_PID_FILE);
358
+ if (!fs.existsSync(agxDir)) {
359
+ fs.mkdirSync(agxDir, { recursive: true });
360
+ }
361
+
362
+ // Spawn daemon process
363
+ const agxPath = process.argv[1]; // Current script path
364
+ const daemon = spawn(process.execPath, [agxPath, 'daemon', '--run'], {
365
+ detached: true,
366
+ stdio: ['ignore',
367
+ fs.openSync(DAEMON_LOG_FILE, 'a'),
368
+ fs.openSync(DAEMON_LOG_FILE, 'a')
369
+ ],
370
+ env: { ...process.env, AGX_DAEMON: '1' }
371
+ });
372
+
373
+ daemon.unref();
374
+ fs.writeFileSync(DAEMON_PID_FILE, String(daemon.pid));
375
+
376
+ console.log(`${c.green}✓${c.reset} Daemon started (pid ${daemon.pid})`);
377
+ console.log(`${c.dim} Logs: ${DAEMON_LOG_FILE}${c.reset}`);
378
+
379
+ return daemon.pid;
380
+ }
381
+
382
+ function stopDaemon() {
383
+ const pid = isDaemonRunning();
384
+ if (!pid) {
385
+ console.log(`${c.yellow}Daemon not running${c.reset}`);
386
+ return false;
387
+ }
388
+
389
+ try {
390
+ process.kill(pid, 'SIGTERM');
391
+ fs.unlinkSync(DAEMON_PID_FILE);
392
+ console.log(`${c.green}✓${c.reset} Daemon stopped (pid ${pid})`);
393
+ return true;
394
+ } catch (err) {
395
+ console.error(`${c.red}Failed to stop daemon:${c.reset} ${err.message}`);
396
+ return false;
397
+ }
398
+ }
399
+
400
+ // Parse wake pattern to milliseconds (e.g., "every 15m" → 900000)
401
+ function parseWakeInterval(pattern) {
402
+ if (!pattern) return null;
403
+
404
+ const match = pattern.match(/every\s+(\d+)\s*(m|min|h|hr|hour)/i);
405
+ if (match) {
406
+ const value = parseInt(match[1]);
407
+ const unit = match[2].toLowerCase();
408
+ if (unit.startsWith('h')) return value * 60 * 60 * 1000;
409
+ return value * 60 * 1000; // minutes
410
+ }
411
+ return null;
412
+ }
413
+
414
+ async function runDaemon() {
415
+ console.log(`[${new Date().toISOString()}] Daemon starting...`);
416
+
417
+ const DEFAULT_WAKE_INTERVAL = 15 * 60 * 1000; // 15 minutes fallback
418
+ const TICK_INTERVAL = 60 * 1000; // Check every 1 minute
419
+ const memDir = path.join(process.env.HOME || process.env.USERPROFILE, '.mem');
420
+
421
+ // Load/save daemon state (last run times per task)
422
+ function loadState() {
423
+ try {
424
+ if (fs.existsSync(DAEMON_STATE_FILE)) {
425
+ return JSON.parse(fs.readFileSync(DAEMON_STATE_FILE, 'utf8'));
426
+ }
427
+ } catch {}
428
+ return { lastRun: {} };
429
+ }
430
+
431
+ function saveState(state) {
432
+ const dir = path.dirname(DAEMON_STATE_FILE);
433
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
434
+ fs.writeFileSync(DAEMON_STATE_FILE, JSON.stringify(state, null, 2));
435
+ }
436
+
437
+ // Get wake interval for a specific task
438
+ function getTaskWakeInterval(projectDir) {
439
+ try {
440
+ const wake = execSync('mem wake', { cwd: projectDir, encoding: 'utf8' });
441
+ const wakeMatch = wake.match(/Wake:\s*(.+)/);
442
+ if (wakeMatch) {
443
+ const interval = parseWakeInterval(wakeMatch[1]);
444
+ if (interval) return interval;
445
+ }
446
+ } catch {}
447
+ return DEFAULT_WAKE_INTERVAL;
448
+ }
449
+
450
+ const tick = async () => {
451
+ const now = Date.now();
452
+ const state = loadState();
453
+
454
+ // Get task list from mem index
455
+ const indexFile = path.join(memDir, 'index.json');
456
+ if (!fs.existsSync(indexFile)) return;
457
+
458
+ const index = JSON.parse(fs.readFileSync(indexFile, 'utf8'));
459
+ let ranAny = false;
460
+
461
+ for (const [projectDir, taskBranch] of Object.entries(index)) {
462
+ if (!fs.existsSync(projectDir)) continue;
463
+
464
+ try {
465
+ // Check if task is due based on its wake interval
466
+ const wakeInterval = getTaskWakeInterval(projectDir);
467
+ const lastRun = state.lastRun[taskBranch] || 0;
468
+ const elapsed = now - lastRun;
469
+
470
+ if (elapsed < wakeInterval) {
471
+ continue; // Not due yet
472
+ }
473
+
474
+ // Use mem to switch and check status
475
+ execSync(`mem switch ${taskBranch.replace('task/', '')}`, { cwd: projectDir, stdio: 'ignore' });
476
+ const status = execSync('mem status', { cwd: projectDir, encoding: 'utf8' });
477
+
478
+ // Check if task is active (not done/blocked)
479
+ if (status.includes('status: done') || status.includes('status: blocked')) {
480
+ continue; // Skip inactive tasks
481
+ }
482
+
483
+ console.log(`[${new Date().toISOString()}] Running task: ${taskBranch} (due after ${Math.round(wakeInterval/60000)}m)`);
484
+ ranAny = true;
485
+
486
+ // Update last run time before executing (in case it takes a while)
487
+ state.lastRun[taskBranch] = now;
488
+ saveState(state);
489
+
490
+ // Run agx continue
491
+ try {
492
+ execSync(`agx claude -y -p "continue"`, {
493
+ cwd: projectDir,
494
+ stdio: 'inherit',
495
+ timeout: 10 * 60 * 1000 // 10 min timeout
496
+ });
497
+ } catch (err) {
498
+ console.log(`[${new Date().toISOString()}] Task ${taskBranch} error: ${err.message}`);
499
+ }
500
+
501
+ } catch (err) {
502
+ console.log(`[${new Date().toISOString()}] Error checking ${taskBranch}: ${err.message}`);
503
+ }
504
+ }
505
+
506
+ if (!ranAny) {
507
+ // Only log periodically to avoid spam
508
+ const lastLog = state.lastLog || 0;
509
+ if (now - lastLog > 5 * 60 * 1000) { // Every 5 min
510
+ console.log(`[${new Date().toISOString()}] Daemon tick - no tasks due`);
511
+ state.lastLog = now;
512
+ saveState(state);
513
+ }
514
+ }
515
+ };
516
+
517
+ // Initial tick
518
+ await tick();
519
+
520
+ // Check every minute, but only run tasks when their interval is due
521
+ setInterval(tick, TICK_INTERVAL);
522
+
523
+ console.log(`[${new Date().toISOString()}] Daemon running, checking every ${TICK_INTERVAL / 1000}s`);
524
+ }
525
+
320
526
  // Handle approval prompts
321
527
  async function handleApprovals(approvals) {
322
528
  if (approvals.length === 0) return true;
@@ -915,6 +1121,48 @@ async function checkOnboarding() {
915
1121
  return true;
916
1122
  }
917
1123
 
1124
+ // Daemon commands
1125
+ if (cmd === 'daemon') {
1126
+ const subcmd = args[1];
1127
+ if (subcmd === 'start') {
1128
+ startDaemon();
1129
+ process.exit(0);
1130
+ } else if (subcmd === 'stop') {
1131
+ stopDaemon();
1132
+ process.exit(0);
1133
+ } else if (subcmd === 'status') {
1134
+ const pid = isDaemonRunning();
1135
+ if (pid) {
1136
+ console.log(`${c.green}Daemon running${c.reset} (pid ${pid})`);
1137
+ console.log(`${c.dim}Logs: ${DAEMON_LOG_FILE}${c.reset}`);
1138
+ } else {
1139
+ console.log(`${c.yellow}Daemon not running${c.reset}`);
1140
+ }
1141
+ process.exit(0);
1142
+ } else if (subcmd === 'logs') {
1143
+ if (fs.existsSync(DAEMON_LOG_FILE)) {
1144
+ const logs = fs.readFileSync(DAEMON_LOG_FILE, 'utf8');
1145
+ console.log(logs.split('\n').slice(-50).join('\n'));
1146
+ } else {
1147
+ console.log(`${c.dim}No logs yet${c.reset}`);
1148
+ }
1149
+ process.exit(0);
1150
+ } else if (subcmd === '--run') {
1151
+ // Internal: actually run the daemon loop
1152
+ await runDaemon();
1153
+ return true; // Never exits
1154
+ } else {
1155
+ console.log(`${c.bold}agx daemon${c.reset} - Background task runner\n`);
1156
+ console.log(`Commands:`);
1157
+ console.log(` agx daemon start Start the daemon`);
1158
+ console.log(` agx daemon stop Stop the daemon`);
1159
+ console.log(` agx daemon status Check if running`);
1160
+ console.log(` agx daemon logs Show recent logs`);
1161
+ process.exit(0);
1162
+ }
1163
+ return true;
1164
+ }
1165
+
918
1166
  // Login command
919
1167
  if (cmd === 'login') {
920
1168
  const provider = args[1];
@@ -1114,7 +1362,7 @@ const options = {
1114
1362
  mcp: null,
1115
1363
  mem: false,
1116
1364
  memDir: null,
1117
- autoTask: false,
1365
+ autonomous: false,
1118
1366
  taskName: null,
1119
1367
  criteria: [],
1120
1368
  daemon: false,
@@ -1175,9 +1423,12 @@ for (let i = 0; i < processedArgs.length; i++) {
1175
1423
  case '--no-mem':
1176
1424
  options.mem = false;
1177
1425
  break;
1178
- case '--auto-task':
1179
- options.autoTask = true;
1426
+ case '--autonomous':
1427
+ case '--auto':
1428
+ case '-a':
1429
+ options.autonomous = true;
1180
1430
  options.mem = true;
1431
+ options.yolo = true; // Autonomous = unattended, skip prompts
1181
1432
  break;
1182
1433
  case '--task':
1183
1434
  if (nextArg && !nextArg.startsWith('-')) {
@@ -1282,14 +1533,15 @@ translatedArgs.push(...rawArgs);
1282
1533
  // ==================== MEM INTEGRATION ====================
1283
1534
 
1284
1535
  // Auto-detect mem if .mem exists (unless --no-mem)
1285
- let memDir = options.mem !== false ? findMemDir() : null;
1286
- if (memDir && options.mem !== false) {
1536
+ let memInfo = options.mem !== false ? findMemDir() : null;
1537
+ if (memInfo && options.mem !== false) {
1287
1538
  options.mem = true;
1288
- options.memDir = memDir;
1539
+ options.memInfo = memInfo;
1540
+ options.memDir = memInfo.memDir; // For backwards compat
1289
1541
  }
1290
1542
 
1291
- // Auto-create task if --auto-task or --task specified but no mem found
1292
- if ((options.autoTask || options.taskName) && !options.memDir && finalPrompt) {
1543
+ // Auto-create task if --autonomous or --task specified but no mem found
1544
+ if ((options.autonomous || options.taskName) && !options.memInfo && finalPrompt) {
1293
1545
  const taskName = options.taskName || finalPrompt
1294
1546
  .toLowerCase()
1295
1547
  .replace(/[^a-z0-9\s]/g, '')
@@ -1345,33 +1597,32 @@ if ((options.autoTask || options.taskName) && !options.memDir && finalPrompt) {
1345
1597
  fs.writeFileSync(indexFile, JSON.stringify(index, null, 2));
1346
1598
 
1347
1599
  options.memDir = centralMem;
1600
+ options.memInfo = { memDir: centralMem, taskBranch: branch, projectDir: process.cwd(), isLocal: false };
1348
1601
  console.log(`${c.green}✓${c.reset} Created task: ${c.bold}${taskName}${c.reset}`);
1349
1602
  console.log(`${c.green}✓${c.reset} Mapped: ${c.dim}${process.cwd()} → ${branch}${c.reset}`);
1350
1603
 
1351
- // Auto-set wake schedule for new tasks with proper command
1352
- // Detect agx path dynamically for cron (no PATH in cron env)
1604
+ // Auto-set wake schedule
1353
1605
  try {
1354
- const projectDir = process.cwd();
1355
- let agxPath = 'agx';
1356
- try {
1357
- agxPath = execSync('which agx', { encoding: 'utf8' }).trim();
1358
- } catch {}
1359
- const wakeCmd = `cd ${projectDir} && ${agxPath} claude -y -p "continue"`;
1360
- execSync(`mem wake "every 15m" --run "${wakeCmd}"`, { cwd: process.cwd(), stdio: 'ignore' });
1361
- console.log(`${c.green}✓${c.reset} Wake: ${c.dim}every 15m (until done)${c.reset}`);
1362
- console.log(`${c.dim} Run: agx claude -y -p "continue"${c.reset}\n`);
1606
+ execSync(`mem wake "every 15m"`, { cwd: process.cwd(), stdio: 'ignore' });
1363
1607
  } catch {}
1364
1608
 
1609
+ // Start daemon for autonomous mode
1610
+ if (options.autonomous) {
1611
+ startDaemon();
1612
+ console.log(`${c.green}✓${c.reset} Autonomous mode: daemon will continue work every 15m\n`);
1613
+ }
1614
+
1365
1615
  } catch (err) {
1366
1616
  console.error(`${c.yellow}Warning: Could not create task:${c.reset} ${err.message}`);
1367
1617
  }
1368
1618
  }
1369
1619
 
1370
1620
  // Prepend mem context to prompt if mem is enabled
1371
- if (options.mem && options.memDir && finalPrompt) {
1372
- const context = loadMemContext(options.memDir);
1621
+ if (options.mem && options.memInfo && finalPrompt) {
1622
+ const context = loadMemContext(options.memInfo);
1373
1623
  if (context) {
1374
- console.log(`${c.dim}[mem] Loaded context from ${options.memDir}${c.reset}\n`);
1624
+ const taskInfo = options.memInfo.taskBranch || 'local';
1625
+ console.log(`${c.dim}[mem] Loaded context: ${taskInfo} (${options.memInfo.projectDir})${c.reset}\n`);
1375
1626
 
1376
1627
  // Prepend context to prompt with full marker documentation
1377
1628
  const augmentedPrompt = `## Current Context (from mem)\n\n${context}\n\n## Task\n\n${finalPrompt}\n\n## Instructions\n\nYou are continuing work on this task. Review the context above, then continue where you left off.\n\n## Output Markers\n\nUse these markers to save state (will be parsed automatically):\n\n- [checkpoint: message] - save progress point\n- [learn: insight] - record a learning \n- [next: step] - set what to work on next\n- [criteria: N] - mark criterion #N complete\n- [split: name "goal"] - break into subtask\n\nStopping markers (only use when needed):\n- [done] - task complete (all criteria met)\n- [blocked: reason] - need human help, cannot proceed\n- [approve: question] - need human approval before continuing\n\nThe default is to keep working. You will wake again in 15 minutes to continue.`;
@@ -1407,11 +1658,11 @@ if (options.mem && options.memDir) {
1407
1658
 
1408
1659
  if (markers.length > 0) {
1409
1660
  console.log(`\n${c.dim}[mem] Processing markers...${c.reset}`);
1410
- const result = applyMemMarkers(markers, options.memDir);
1661
+ const result = applyMemMarkers(markers, options.memInfo || { memDir: options.memDir, projectDir: process.cwd() });
1411
1662
 
1412
1663
  // Create subtasks if any
1413
1664
  if (result.splits.length > 0) {
1414
- createSubtasks(result.splits, options.memDir);
1665
+ createSubtasks(result.splits, options.memInfo || { memDir: options.memDir, projectDir: process.cwd() });
1415
1666
  }
1416
1667
 
1417
1668
  // Handle approvals
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mndrk/agx",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "Unified AI Agent Wrapper for Gemini, Claude, and Ollama",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1,13 +1,13 @@
1
1
  # agx - Unified AI Agent Wrapper
2
2
 
3
- Use `agx` to run AI agents (Claude Code, Gemini, Ollama) with persistent memory integration. Spawn background tasks that wake automatically and continue working.
3
+ Use `agx` to run AI agents (Claude Code, Gemini, Ollama) with persistent memory integration. Spawn autonomous tasks that work in the background until complete.
4
4
 
5
5
  ## When to Use
6
6
 
7
- - **Spawning background agents** for long-running tasks
8
- - **Auto-task creation** with wake schedules
7
+ - **Autonomous agents** for long-running tasks
8
+ - **Background work** that continues without supervision
9
9
  - **Running different AI providers** with unified interface
10
- - **Autonomous work loops** that continue until done
10
+ - **Persistent memory** across sessions
11
11
 
12
12
  ## Basic Usage
13
13
 
@@ -17,41 +17,43 @@ agx gemini -p "prompt" # Run Gemini
17
17
  agx ollama -p "prompt" # Run Ollama (local)
18
18
  ```
19
19
 
20
- ## Auto-Task Mode (Recommended)
20
+ ## Autonomous Mode (Recommended)
21
21
 
22
- Create a task with automatic wake schedule:
22
+ Start a task that runs autonomously until complete:
23
23
 
24
24
  ```bash
25
25
  cd ~/Projects/my-app
26
- agx claude --auto-task -p "Build a React todo app with auth"
26
+ agx claude --autonomous -p "Build a React todo app with auth"
27
27
  # ✓ Created task: build-react-todo
28
28
  # ✓ Mapped: ~/Projects/my-app → task/build-react-todo
29
- # ✓ Wake: every 15m (until done)
29
+ # ✓ Daemon started (pid 12345)
30
+ # ✓ Autonomous mode: daemon will continue work every 15m
30
31
  ```
31
32
 
32
33
  This:
33
34
  1. Creates a mem task branch
34
- 2. Sets wake schedule (every 15m)
35
- 3. Installs cron to continue automatically
36
- 4. Agent works until [done] or [blocked]
35
+ 2. Starts the agx daemon (if not running)
36
+ 3. Daemon wakes every 15m to continue work
37
+ 4. Runs until agent outputs [done] or [blocked]
37
38
 
38
- ## Wake Loop
39
+ ## Daemon Management
39
40
 
40
- ```
41
- WAKE (cron) → Load context → Agent works → Save state → SLEEP
42
-
43
- repeat until [done]
44
- ```
45
-
46
- Install the wake schedule:
47
41
  ```bash
48
- mem cron export # View entry
49
- (crontab -l; mem cron export) | crontab - # Install
42
+ agx daemon start # Start background daemon
43
+ agx daemon stop # Stop daemon
44
+ agx daemon status # Check if running
45
+ agx daemon logs # Show recent logs
50
46
  ```
51
47
 
48
+ The daemon:
49
+ - Runs in background (survives terminal close)
50
+ - Wakes every 15 minutes
51
+ - Continues work on active tasks
52
+ - Stops when task is [done] or [blocked]
53
+
52
54
  ## Output Markers
53
55
 
54
- Use these in agent output to control the loop:
56
+ Use these in agent output to control state:
55
57
 
56
58
  ### Progress (parsed automatically)
57
59
  ```
@@ -63,17 +65,12 @@ Use these in agent output to control the loop:
63
65
 
64
66
  ### Stopping Markers
65
67
  ```
66
- [done] # Task complete, clear wake, stop
67
- [blocked: reason] # Need human help, pause loop
68
+ [done] # Task complete, stop
69
+ [blocked: reason] # Need human help, pause
68
70
  [approve: question] # Need approval, wait
69
71
  [pause] # Stop, resume on next wake
70
72
  ```
71
73
 
72
- ### Loop Control
73
- ```
74
- [continue] # Keep going (--daemon mode loops locally)
75
- ```
76
-
77
74
  **Default behavior:** Keep working. Only output stopping markers when needed.
78
75
 
79
76
  ## Provider Aliases
@@ -87,11 +84,9 @@ Use these in agent output to control the loop:
87
84
  ## Common Flags
88
85
 
89
86
  ```bash
90
- --auto-task # Auto-create task from prompt
87
+ --autonomous, -a # Create task and run autonomously (starts daemon)
91
88
  --task NAME # Specific task name
92
89
  --criteria "..." # Success criterion (repeatable)
93
- --daemon # Loop on [continue] marker
94
- --until-done # Keep running until [done]
95
90
  -y # Skip confirmations
96
91
  ```
97
92
 
@@ -102,18 +97,18 @@ Use these in agent output to control the loop:
102
97
  agx claude -p "Explain this error" -y
103
98
  ```
104
99
 
105
- ### Background task with wake
100
+ ### Autonomous task
106
101
  ```bash
107
102
  cd ~/Projects/api
108
- agx claude --auto-task -p "Add user authentication with JWT"
109
- # Agent works, saves progress, wakes every 15m to continue
103
+ agx claude --autonomous -p "Add user authentication with JWT"
104
+ # Daemon continues work every 15m until done
110
105
  ```
111
106
 
112
107
  ### Check on a running task
113
108
  ```bash
114
- mem status # Quick summary
109
+ agx daemon status # Check daemon
110
+ mem status # Task summary
115
111
  mem progress # % complete
116
- mem context # Full state dump
117
112
  ```
118
113
 
119
114
  ### Manual continue