@mndrk/agx 1.3.0 → 1.4.1
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/.claude/settings.local.json +2 -1
- package/.claude-plugin/plugin.json +7 -0
- package/README.md +94 -183
- package/commands/continue.md +16 -0
- package/commands/spawn.md +31 -0
- package/index.js +524 -55
- package/package.json +8 -3
- package/skills/agx/SKILL.md +123 -0
package/README.md
CHANGED
|
@@ -1,242 +1,153 @@
|
|
|
1
1
|
# agx
|
|
2
2
|
|
|
3
|
-
Unified AI Agent
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
### Via npm (recommended)
|
|
3
|
+
Unified AI Agent CLI with persistent memory. Wraps Claude, Gemini, and Ollama with automatic state management via [mem](https://github.com/ramarlina/memx).
|
|
8
4
|
|
|
9
5
|
```bash
|
|
10
6
|
npm install -g @mndrk/agx
|
|
11
7
|
```
|
|
12
8
|
|
|
13
|
-
### From source
|
|
14
|
-
|
|
15
|
-
Clone the repository and link locally:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
git clone https://github.com/ramarlina/agx.git
|
|
19
|
-
cd agx
|
|
20
|
-
npm link
|
|
21
|
-
```
|
|
22
|
-
|
|
23
9
|
## Quick Start
|
|
24
10
|
|
|
25
|
-
When you run `agx` for the first time, it will automatically start the setup wizard:
|
|
26
|
-
|
|
27
11
|
```bash
|
|
28
|
-
|
|
29
|
-
|
|
12
|
+
# Simple prompt
|
|
13
|
+
agx claude -p "explain this code"
|
|
30
14
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
2. Guide you through installing any missing providers
|
|
34
|
-
3. Help you authenticate with your chosen providers
|
|
35
|
-
4. Set your default provider
|
|
15
|
+
# Use default provider
|
|
16
|
+
agx -p "what does this function do?"
|
|
36
17
|
|
|
37
|
-
|
|
18
|
+
# With persistent memory (auto-detected)
|
|
19
|
+
agx claude -p "continue working on the todo app"
|
|
38
20
|
|
|
39
|
-
|
|
40
|
-
agx --
|
|
21
|
+
# Auto-create task (for agents, non-interactive)
|
|
22
|
+
agx claude --auto-task -p "Build a todo app with React"
|
|
41
23
|
```
|
|
42
24
|
|
|
43
|
-
##
|
|
44
|
-
|
|
45
|
-
### With Default Provider
|
|
25
|
+
## Memory Integration
|
|
46
26
|
|
|
47
|
-
|
|
27
|
+
agx integrates with [mem](https://github.com/ramarlina/memx) for persistent state across sessions:
|
|
48
28
|
|
|
49
29
|
```bash
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
# If ~/.mem has a task mapped to cwd, context is auto-loaded
|
|
31
|
+
cd ~/Projects/my-app
|
|
32
|
+
agx claude -p "continue" # Knows where it left off
|
|
53
33
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
34
|
+
# Create task with explicit criteria
|
|
35
|
+
agx claude --task todo-app \
|
|
36
|
+
--criteria "CRUD working" \
|
|
37
|
+
--criteria "Tests passing" \
|
|
38
|
+
--criteria "Deployed to Vercel" \
|
|
39
|
+
-p "Build a todo app"
|
|
58
40
|
```
|
|
59
41
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
| Provider | Aliases | Backend |
|
|
63
|
-
|----------|---------|---------|
|
|
64
|
-
| `gemini` | `gem`, `g` | Google Gemini CLI |
|
|
65
|
-
| `claude` | `cl`, `c` | Anthropic Claude CLI |
|
|
66
|
-
| `ollama` | `ol`, `o` | Local Ollama via Claude interface |
|
|
42
|
+
## Output Markers
|
|
67
43
|
|
|
68
|
-
|
|
44
|
+
Agents control state via markers in their output:
|
|
69
45
|
|
|
70
|
-
| Option | Short | Description |
|
|
71
|
-
|--------|-------|-------------|
|
|
72
|
-
| `--prompt <text>` | `-p` | The prompt to send |
|
|
73
|
-
| `--model <name>` | `-m` | Model name to use |
|
|
74
|
-
| `--yolo` | `-y` | Skip permission prompts |
|
|
75
|
-
| `--print` | | Non-interactive mode (output and exit) |
|
|
76
|
-
| `--interactive` | `-i` | Force interactive mode |
|
|
77
|
-
| `--sandbox` | `-s` | Enable sandbox (gemini only) |
|
|
78
|
-
| `--debug` | `-d` | Enable debug output |
|
|
79
|
-
| `--mcp <config>` | | MCP config file (claude/ollama only) |
|
|
80
|
-
|
|
81
|
-
### Raw Passthrough
|
|
82
|
-
|
|
83
|
-
Use `--` to pass arguments directly to the underlying CLI:
|
|
84
|
-
```bash
|
|
85
|
-
agx claude -- --resume
|
|
86
46
|
```
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
47
|
+
[checkpoint: Hero section complete] # Save progress
|
|
48
|
+
[learn: Tailwind is fast] # Record learning
|
|
49
|
+
[next: Add auth system] # Set next step
|
|
50
|
+
[criteria: 2] # Mark criterion #2 done
|
|
51
|
+
[approve: Deploy to production?] # Halt for approval
|
|
52
|
+
[blocked: Need API key from client] # Mark stuck
|
|
53
|
+
[pause] # Stop, resume later
|
|
54
|
+
[continue] # Keep going (daemon)
|
|
55
|
+
[done] # Task complete
|
|
56
|
+
[split: auth "Handle authentication"] # Create subtask
|
|
96
57
|
```
|
|
97
58
|
|
|
98
|
-
|
|
99
|
-
- Detect installed providers (claude, gemini, ollama)
|
|
100
|
-
- Guide you through installation and authentication
|
|
101
|
-
- Let you set or change your default provider
|
|
59
|
+
## Providers
|
|
102
60
|
|
|
103
|
-
|
|
61
|
+
| Provider | Aliases | Description |
|
|
62
|
+
|----------|---------|-------------|
|
|
63
|
+
| claude | c, cl | Anthropic Claude Code |
|
|
64
|
+
| gemini | g, gem | Google Gemini CLI |
|
|
65
|
+
| ollama | o, ol | Local Ollama models |
|
|
104
66
|
|
|
105
|
-
|
|
67
|
+
## Options
|
|
106
68
|
|
|
107
|
-
```bash
|
|
108
|
-
agx config
|
|
109
69
|
```
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
70
|
+
--prompt, -p <text> Prompt to send
|
|
71
|
+
--model, -m <name> Model name
|
|
72
|
+
--yolo, -y Skip permission prompts
|
|
73
|
+
--print Non-interactive output
|
|
74
|
+
--interactive, -i Force interactive mode
|
|
75
|
+
--mem Enable mem integration (auto-detected)
|
|
76
|
+
--no-mem Disable mem integration
|
|
77
|
+
--auto-task Auto-create task from prompt
|
|
78
|
+
--task <name> Specific task name
|
|
79
|
+
--criteria <text> Success criterion (repeatable)
|
|
80
|
+
--daemon Loop on [continue] marker
|
|
119
81
|
```
|
|
120
82
|
|
|
121
|
-
|
|
83
|
+
## Claude Code Plugin
|
|
122
84
|
|
|
123
|
-
|
|
85
|
+
Install as a Claude Code plugin:
|
|
124
86
|
|
|
125
87
|
```bash
|
|
126
|
-
|
|
127
|
-
agx login gemini # Login to Gemini
|
|
88
|
+
claude plugin install github:ramarlina/agx
|
|
128
89
|
```
|
|
129
90
|
|
|
130
|
-
|
|
91
|
+
This adds:
|
|
92
|
+
- **Skill**: Claude learns how to spawn background agents
|
|
93
|
+
- **Commands**: `/agx:spawn <goal>`, `/agx:continue`
|
|
131
94
|
|
|
132
|
-
|
|
95
|
+
## Commands
|
|
133
96
|
|
|
134
97
|
```bash
|
|
135
|
-
agx
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
98
|
+
agx init # Setup wizard
|
|
99
|
+
agx config # Configuration menu
|
|
100
|
+
agx status # Show current config
|
|
101
|
+
agx skill # View LLM skill
|
|
102
|
+
agx skill install # Install skill to Claude/Gemini
|
|
139
103
|
```
|
|
140
|
-
agx Configuration Status
|
|
141
|
-
------------------------
|
|
142
|
-
Default Provider: claude
|
|
143
|
-
|
|
144
|
-
Installed Providers:
|
|
145
|
-
✓ claude (authenticated)
|
|
146
|
-
✓ gemini (authenticated)
|
|
147
|
-
✓ ollama (running)
|
|
148
104
|
|
|
149
|
-
|
|
150
|
-
```
|
|
105
|
+
## Loop Control
|
|
151
106
|
|
|
152
|
-
|
|
107
|
+
The agent controls execution flow via markers:
|
|
153
108
|
|
|
154
|
-
|
|
109
|
+
- `[done]` → Task complete, exit
|
|
110
|
+
- `[pause]` → Save state, exit (resume later with same command)
|
|
111
|
+
- `[blocked: reason]` → Mark stuck, notify human, exit
|
|
112
|
+
- `[continue]` → Keep going (daemon mode loops)
|
|
113
|
+
- `[approve: question]` → Halt until human approves
|
|
155
114
|
|
|
156
|
-
|
|
115
|
+
## Task Splitting
|
|
157
116
|
|
|
158
|
-
|
|
117
|
+
Break large tasks into subtasks:
|
|
159
118
|
|
|
160
|
-
```bash
|
|
161
|
-
agx skill
|
|
162
119
|
```
|
|
120
|
+
Agent output:
|
|
121
|
+
This is too big. Breaking it down.
|
|
163
122
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
agx skill install
|
|
123
|
+
[split: setup "Project scaffolding"]
|
|
124
|
+
[split: auth "Authentication system"]
|
|
125
|
+
[split: crud "CRUD operations"]
|
|
126
|
+
[next: Start with setup subtask]
|
|
127
|
+
[pause]
|
|
172
128
|
```
|
|
173
129
|
|
|
174
|
-
|
|
175
|
-
- AI agents can call other AI providers through agx
|
|
176
|
-
- Cross-provider collaboration (e.g., Claude can ask Gemini for help)
|
|
177
|
-
- Consistent command patterns across all providers
|
|
178
|
-
|
|
179
|
-
## LLM-Predictable Command Patterns
|
|
130
|
+
agx creates subtask branches in ~/.mem linked to the parent.
|
|
180
131
|
|
|
181
|
-
|
|
132
|
+
## Example: Full Workflow
|
|
182
133
|
|
|
183
134
|
```bash
|
|
184
|
-
#
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
# Pattern: agx <provider> --prompt "<prompt>"
|
|
188
|
-
agx claude --prompt "explain this code"
|
|
189
|
-
agx gemini --prompt "summarize the file"
|
|
190
|
-
agx ollama --prompt "write a function"
|
|
191
|
-
|
|
192
|
-
# Pattern: agx <provider> --model <model> --prompt "<prompt>"
|
|
193
|
-
agx claude --model claude-sonnet-4-20250514 --prompt "fix the bug"
|
|
194
|
-
agx gemini --model gemini-2.0-flash --prompt "optimize this"
|
|
195
|
-
agx ollama --model qwen3:8b --prompt "refactor"
|
|
196
|
-
|
|
197
|
-
# Pattern: agx <provider> --yolo --prompt "<prompt>"
|
|
198
|
-
agx claude --yolo --prompt "run the tests"
|
|
135
|
+
# Day 1: Start project
|
|
136
|
+
mkdir ~/Projects/my-app && cd ~/Projects/my-app
|
|
137
|
+
agx claude --auto-task -p "Build a React todo app with auth"
|
|
199
138
|
|
|
200
|
-
#
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
```
|
|
207
|
-
agx [provider] [--model <name>] [--yolo] [--print] --prompt "<prompt>"
|
|
208
|
-
```
|
|
139
|
+
# Agent works, outputs markers
|
|
140
|
+
# [checkpoint: Scaffolded with Vite]
|
|
141
|
+
# [learn: Vite is faster than CRA]
|
|
142
|
+
# [next: Add todo list component]
|
|
143
|
+
# [pause]
|
|
209
144
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
4. Use full provider names (`claude`, `gemini`, `ollama`) for clarity
|
|
215
|
-
5. Provider is optional if a default is configured
|
|
216
|
-
|
|
217
|
-
## Configuration File
|
|
218
|
-
|
|
219
|
-
agx stores its configuration in `~/.agx/config.json`:
|
|
220
|
-
|
|
221
|
-
```json
|
|
222
|
-
{
|
|
223
|
-
"defaultProvider": "claude",
|
|
224
|
-
"providers": {
|
|
225
|
-
"claude": {
|
|
226
|
-
"installed": true,
|
|
227
|
-
"authenticated": true
|
|
228
|
-
},
|
|
229
|
-
"gemini": {
|
|
230
|
-
"installed": true,
|
|
231
|
-
"authenticated": true
|
|
232
|
-
},
|
|
233
|
-
"ollama": {
|
|
234
|
-
"installed": true
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
145
|
+
# Day 2: Continue
|
|
146
|
+
cd ~/Projects/my-app
|
|
147
|
+
agx claude -p "continue"
|
|
148
|
+
# Context auto-loaded, agent picks up where it left off
|
|
238
149
|
```
|
|
239
150
|
|
|
240
|
-
##
|
|
151
|
+
## License
|
|
241
152
|
|
|
242
|
-
|
|
153
|
+
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# /agx:continue - Continue Current Task
|
|
2
|
+
|
|
3
|
+
Continue working on the current task from the last checkpoint.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
```
|
|
7
|
+
/agx:continue
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Implementation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
agx claude -p "continue"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This loads the mem context and continues from where the task left off.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# /agx:spawn - Spawn Background Agent
|
|
2
|
+
|
|
3
|
+
Spawn a background agent task with automatic wake schedule.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
```
|
|
7
|
+
/agx:spawn <goal>
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Arguments
|
|
11
|
+
- `$ARGUMENTS` - The goal/task description for the agent
|
|
12
|
+
|
|
13
|
+
## Implementation
|
|
14
|
+
|
|
15
|
+
First, ensure you're in the correct project directory, then:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
agx claude --auto-task -p "$ARGUMENTS"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This will:
|
|
22
|
+
1. Create a mem task branch
|
|
23
|
+
2. Set wake schedule (every 15m)
|
|
24
|
+
3. Start the agent working
|
|
25
|
+
|
|
26
|
+
After spawning, install the wake cron:
|
|
27
|
+
```bash
|
|
28
|
+
(crontab -l 2>/dev/null; mem cron export) | crontab -
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Report the task name and confirm wake schedule is set.
|
package/index.js
CHANGED
|
@@ -12,18 +12,54 @@ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
|
12
12
|
// agx skill - instructions for LLMs on how to use agx
|
|
13
13
|
const AGX_SKILL = `---
|
|
14
14
|
name: agx
|
|
15
|
-
description:
|
|
15
|
+
description: Run AI agents with persistent memory. Wraps Claude, Gemini, Ollama with automatic state management.
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
# agx -
|
|
18
|
+
# agx - AI Agent CLI with Persistent Memory
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Run AI agents with automatic state persistence via mem integration.
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Basic Usage
|
|
23
23
|
|
|
24
24
|
\`\`\`bash
|
|
25
|
-
agx
|
|
26
|
-
agx
|
|
25
|
+
agx claude -p "explain this code" # simple prompt
|
|
26
|
+
agx -p "what does this do?" # use default provider
|
|
27
|
+
agx c --yolo -p "fix the bug" # skip confirmations
|
|
28
|
+
\`\`\`
|
|
29
|
+
|
|
30
|
+
## Memory Integration
|
|
31
|
+
|
|
32
|
+
agx auto-detects \`~/.mem\` and loads context:
|
|
33
|
+
|
|
34
|
+
\`\`\`bash
|
|
35
|
+
# Auto-load context if task exists for cwd
|
|
36
|
+
agx claude -p "continue working"
|
|
37
|
+
|
|
38
|
+
# Auto-create task (non-interactive, for agents)
|
|
39
|
+
agx claude --auto-task -p "Build todo app"
|
|
40
|
+
|
|
41
|
+
# Explicit task with criteria
|
|
42
|
+
agx claude --task todo-app \\
|
|
43
|
+
--criteria "CRUD working" \\
|
|
44
|
+
--criteria "Deployed" \\
|
|
45
|
+
-p "Build a todo app"
|
|
46
|
+
\`\`\`
|
|
47
|
+
|
|
48
|
+
## Output Markers
|
|
49
|
+
|
|
50
|
+
Use these in agent output to save state:
|
|
51
|
+
|
|
52
|
+
\`\`\`
|
|
53
|
+
[checkpoint: message] Save progress point
|
|
54
|
+
[learn: insight] Record learning
|
|
55
|
+
[next: step] Set next step
|
|
56
|
+
[criteria: N] Mark criterion #N complete
|
|
57
|
+
[approve: question] Halt for human approval
|
|
58
|
+
[blocked: reason] Mark blocked, stop
|
|
59
|
+
[pause] Save and stop (resume later)
|
|
60
|
+
[continue] Keep going (daemon mode)
|
|
61
|
+
[done] Mark task complete
|
|
62
|
+
[split: name "goal"] Create subtask
|
|
27
63
|
\`\`\`
|
|
28
64
|
|
|
29
65
|
## Providers
|
|
@@ -34,41 +70,50 @@ agx [options] --prompt "<prompt>" # uses default provider
|
|
|
34
70
|
| gemini | g, gem | Google Gemini CLI |
|
|
35
71
|
| ollama | o, ol | Local Ollama models |
|
|
36
72
|
|
|
37
|
-
##
|
|
73
|
+
## Options
|
|
38
74
|
|
|
39
|
-
| Option |
|
|
40
|
-
|
|
41
|
-
| --prompt
|
|
42
|
-
| --model
|
|
43
|
-
| --yolo
|
|
44
|
-
| --
|
|
45
|
-
| --
|
|
75
|
+
| Option | Description |
|
|
76
|
+
|--------|-------------|
|
|
77
|
+
| --prompt, -p | The prompt to send |
|
|
78
|
+
| --model, -m | Model name |
|
|
79
|
+
| --yolo, -y | Skip permission prompts |
|
|
80
|
+
| --mem | Enable mem (auto-detected) |
|
|
81
|
+
| --no-mem | Disable mem |
|
|
82
|
+
| --auto-task | Auto-create task from prompt |
|
|
83
|
+
| --task NAME | Specific task name |
|
|
84
|
+
| --criteria "..." | Success criterion (repeatable) |
|
|
85
|
+
| --daemon | Loop on [continue] marker |
|
|
86
|
+
| --until-done | Keep running until [done] |
|
|
46
87
|
|
|
47
|
-
##
|
|
88
|
+
## Continuous Work Loop
|
|
48
89
|
|
|
49
|
-
|
|
50
|
-
# Simple prompt
|
|
51
|
-
agx claude --prompt "explain this code"
|
|
90
|
+
New tasks auto-set \`wake: every 15m\`. The loop:
|
|
52
91
|
|
|
53
|
-
|
|
54
|
-
|
|
92
|
+
\`\`\`
|
|
93
|
+
WAKE → load context → review → work → save → SLEEP
|
|
94
|
+
↓
|
|
95
|
+
repeat until [done]
|
|
96
|
+
\`\`\`
|
|
55
97
|
|
|
56
|
-
|
|
57
|
-
|
|
98
|
+
Agent keeps working by default. Only output stopping markers when needed:
|
|
99
|
+
- \`[done]\` → task complete, stop loop
|
|
100
|
+
- \`[blocked: reason]\` → need human help, pause loop
|
|
101
|
+
- \`[approve: question]\` → need approval, wait
|
|
58
102
|
|
|
59
|
-
|
|
60
|
-
agx ollama --model qwen3:8b --prompt "write a function"
|
|
103
|
+
Install wake: \`mem cron export >> crontab\`
|
|
61
104
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
105
|
+
## Task Splitting
|
|
106
|
+
|
|
107
|
+
Break large tasks into subtasks:
|
|
65
108
|
|
|
66
|
-
|
|
109
|
+
\`\`\`
|
|
110
|
+
[split: setup "Project scaffolding"]
|
|
111
|
+
[split: auth "Authentication system"]
|
|
112
|
+
[split: crud "CRUD operations"]
|
|
113
|
+
[next: Start with setup]
|
|
114
|
+
\`\`\`
|
|
67
115
|
|
|
68
|
-
|
|
69
|
-
- Running prompts across different AI providers
|
|
70
|
-
- Scripting AI interactions
|
|
71
|
-
- When you need --yolo mode to skip confirmations
|
|
116
|
+
Creates subtask branches in ~/.mem, linked to parent.
|
|
72
117
|
`;
|
|
73
118
|
|
|
74
119
|
// ANSI colors
|
|
@@ -94,6 +139,201 @@ function commandExists(cmd) {
|
|
|
94
139
|
}
|
|
95
140
|
}
|
|
96
141
|
|
|
142
|
+
// ==================== MEM INTEGRATION ====================
|
|
143
|
+
|
|
144
|
+
// Find .mem directory (walk up from cwd, or check ~/.mem)
|
|
145
|
+
function findMemDir(startDir = process.cwd()) {
|
|
146
|
+
// First check local .mem
|
|
147
|
+
let dir = startDir;
|
|
148
|
+
while (dir !== path.dirname(dir)) {
|
|
149
|
+
const memDir = path.join(dir, '.mem');
|
|
150
|
+
if (fs.existsSync(memDir) && fs.existsSync(path.join(memDir, '.git'))) {
|
|
151
|
+
return memDir;
|
|
152
|
+
}
|
|
153
|
+
dir = path.dirname(dir);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Then check ~/.mem with index
|
|
157
|
+
const globalMem = path.join(process.env.HOME || process.env.USERPROFILE, '.mem');
|
|
158
|
+
if (fs.existsSync(globalMem)) {
|
|
159
|
+
const indexFile = path.join(globalMem, 'index.json');
|
|
160
|
+
if (fs.existsSync(indexFile)) {
|
|
161
|
+
try {
|
|
162
|
+
const index = JSON.parse(fs.readFileSync(indexFile, 'utf8'));
|
|
163
|
+
if (index[startDir]) {
|
|
164
|
+
return globalMem;
|
|
165
|
+
}
|
|
166
|
+
} catch {}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Load mem context
|
|
174
|
+
function loadMemContext(memDir) {
|
|
175
|
+
try {
|
|
176
|
+
const result = execSync('mem context', {
|
|
177
|
+
cwd: path.dirname(memDir),
|
|
178
|
+
encoding: 'utf8',
|
|
179
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
180
|
+
});
|
|
181
|
+
return result.trim();
|
|
182
|
+
} catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Parse output markers
|
|
188
|
+
function parseMemMarkers(output) {
|
|
189
|
+
const markers = [];
|
|
190
|
+
const patterns = [
|
|
191
|
+
{ type: 'checkpoint', regex: /\[checkpoint:\s*([^\]]+)\]/gi },
|
|
192
|
+
{ type: 'learn', regex: /\[learn:\s*([^\]]+)\]/gi },
|
|
193
|
+
{ type: 'next', regex: /\[next:\s*([^\]]+)\]/gi },
|
|
194
|
+
{ type: 'stuck', regex: /\[stuck:\s*([^\]]+)\]/gi },
|
|
195
|
+
{ type: 'blocked', regex: /\[blocked:\s*([^\]]+)\]/gi },
|
|
196
|
+
{ type: 'done', regex: /\[done\]/gi },
|
|
197
|
+
{ type: 'pause', regex: /\[pause(?::\s*([^\]]*))?\]/gi },
|
|
198
|
+
{ type: 'continue', regex: /\[continue\]/gi },
|
|
199
|
+
{ type: 'approve', regex: /\[approve:\s*([^\]]+)\]/gi },
|
|
200
|
+
{ type: 'criteria', regex: /\[criteria:\s*(\d+)\]/gi },
|
|
201
|
+
{ type: 'split', regex: /\[split:\s*([^\s\]]+)(?:\s+"([^"]+)")?\]/gi },
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
for (const { type, regex } of patterns) {
|
|
205
|
+
let match;
|
|
206
|
+
while ((match = regex.exec(output)) !== null) {
|
|
207
|
+
if (type === 'split') {
|
|
208
|
+
markers.push({ type, name: match[1], goal: match[2] || match[1] });
|
|
209
|
+
} else {
|
|
210
|
+
markers.push({ type, value: match[1] || true });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return markers;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Apply mem markers - returns control signals
|
|
219
|
+
function applyMemMarkers(markers, memDir) {
|
|
220
|
+
const workDir = path.dirname(memDir);
|
|
221
|
+
const result = {
|
|
222
|
+
approvals: [],
|
|
223
|
+
shouldContinue: false,
|
|
224
|
+
shouldPause: false,
|
|
225
|
+
isDone: false,
|
|
226
|
+
isBlocked: false,
|
|
227
|
+
splits: []
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
for (const marker of markers) {
|
|
231
|
+
try {
|
|
232
|
+
switch (marker.type) {
|
|
233
|
+
case 'checkpoint':
|
|
234
|
+
execSync(`mem checkpoint "${marker.value.replace(/"/g, '\\"')}"`, {
|
|
235
|
+
cwd: workDir, stdio: 'ignore'
|
|
236
|
+
});
|
|
237
|
+
console.log(`${c.green}✓${c.reset} ${c.dim}Checkpoint:${c.reset} ${marker.value}`);
|
|
238
|
+
break;
|
|
239
|
+
case 'learn':
|
|
240
|
+
execSync(`mem learn "${marker.value.replace(/"/g, '\\"')}"`, {
|
|
241
|
+
cwd: workDir, stdio: 'ignore'
|
|
242
|
+
});
|
|
243
|
+
console.log(`${c.green}✓${c.reset} ${c.dim}Learned:${c.reset} ${marker.value}`);
|
|
244
|
+
break;
|
|
245
|
+
case 'next':
|
|
246
|
+
execSync(`mem next "${marker.value.replace(/"/g, '\\"')}"`, {
|
|
247
|
+
cwd: workDir, stdio: 'ignore'
|
|
248
|
+
});
|
|
249
|
+
console.log(`${c.green}✓${c.reset} ${c.dim}Next:${c.reset} ${marker.value}`);
|
|
250
|
+
break;
|
|
251
|
+
case 'stuck':
|
|
252
|
+
case 'blocked':
|
|
253
|
+
execSync(`mem stuck "${marker.value.replace(/"/g, '\\"')}"`, {
|
|
254
|
+
cwd: workDir, stdio: 'ignore'
|
|
255
|
+
});
|
|
256
|
+
console.log(`${c.yellow}⚠${c.reset} ${c.dim}Blocked:${c.reset} ${marker.value}`);
|
|
257
|
+
result.isBlocked = true;
|
|
258
|
+
break;
|
|
259
|
+
case 'done':
|
|
260
|
+
console.log(`${c.green}✓${c.reset} ${c.dim}Task marked done${c.reset}`);
|
|
261
|
+
result.isDone = true;
|
|
262
|
+
break;
|
|
263
|
+
case 'pause':
|
|
264
|
+
console.log(`${c.cyan}⏸${c.reset} ${c.dim}Pausing${marker.value ? ': ' + marker.value : ''}${c.reset}`);
|
|
265
|
+
result.shouldPause = true;
|
|
266
|
+
break;
|
|
267
|
+
case 'continue':
|
|
268
|
+
console.log(`${c.cyan}▶${c.reset} ${c.dim}Continuing...${c.reset}`);
|
|
269
|
+
result.shouldContinue = true;
|
|
270
|
+
break;
|
|
271
|
+
case 'approve':
|
|
272
|
+
result.approvals.push(marker.value);
|
|
273
|
+
break;
|
|
274
|
+
case 'criteria':
|
|
275
|
+
execSync(`mem criteria ${marker.value}`, {
|
|
276
|
+
cwd: workDir, stdio: 'ignore'
|
|
277
|
+
});
|
|
278
|
+
console.log(`${c.green}✓${c.reset} ${c.dim}Criteria #${marker.value} complete${c.reset}`);
|
|
279
|
+
break;
|
|
280
|
+
case 'split':
|
|
281
|
+
result.splits.push({ name: marker.name, goal: marker.goal });
|
|
282
|
+
console.log(`${c.cyan}⑂${c.reset} ${c.dim}Split:${c.reset} ${marker.name} - ${marker.goal}`);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
} catch (err) {
|
|
286
|
+
console.error(`${c.red}mem error:${c.reset} ${err.message}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Create subtasks from split markers
|
|
294
|
+
function createSubtasks(splits, memDir) {
|
|
295
|
+
const workDir = path.dirname(memDir);
|
|
296
|
+
|
|
297
|
+
for (const split of splits) {
|
|
298
|
+
try {
|
|
299
|
+
// Create subtask as new branch
|
|
300
|
+
execSync(`mem init ${split.name} "${split.goal.replace(/"/g, '\\"')}"`, {
|
|
301
|
+
cwd: workDir,
|
|
302
|
+
stdio: 'ignore'
|
|
303
|
+
});
|
|
304
|
+
console.log(`${c.green}✓${c.reset} Created subtask: ${c.bold}${split.name}${c.reset}`);
|
|
305
|
+
} catch (err) {
|
|
306
|
+
console.error(`${c.red}Failed to create subtask ${split.name}:${c.reset} ${err.message}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Handle approval prompts
|
|
312
|
+
async function handleApprovals(approvals) {
|
|
313
|
+
if (approvals.length === 0) return true;
|
|
314
|
+
|
|
315
|
+
const rl = readline.createInterface({
|
|
316
|
+
input: process.stdin,
|
|
317
|
+
output: process.stdout
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
for (const approval of approvals) {
|
|
321
|
+
const answer = await new Promise(resolve => {
|
|
322
|
+
rl.question(`\n${c.yellow}⚠ APPROVAL REQUIRED:${c.reset} ${approval}\n${c.dim}Continue? [y/N]:${c.reset} `, resolve);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
|
|
326
|
+
console.log(`${c.red}✗${c.reset} Rejected. Workflow halted.`);
|
|
327
|
+
rl.close();
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
console.log(`${c.green}✓${c.reset} Approved.`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
rl.close();
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
|
|
97
337
|
// Load config
|
|
98
338
|
function loadConfig() {
|
|
99
339
|
try {
|
|
@@ -862,7 +1102,15 @@ const options = {
|
|
|
862
1102
|
interactive: false,
|
|
863
1103
|
sandbox: false,
|
|
864
1104
|
debug: false,
|
|
865
|
-
mcp: null
|
|
1105
|
+
mcp: null,
|
|
1106
|
+
mem: false,
|
|
1107
|
+
memDir: null,
|
|
1108
|
+
autoTask: false,
|
|
1109
|
+
taskName: null,
|
|
1110
|
+
criteria: [],
|
|
1111
|
+
daemon: false,
|
|
1112
|
+
untilDone: false,
|
|
1113
|
+
wakeInterval: null
|
|
866
1114
|
};
|
|
867
1115
|
|
|
868
1116
|
// Collect positional args (legacy support, but --prompt is preferred)
|
|
@@ -912,6 +1160,42 @@ for (let i = 0; i < processedArgs.length; i++) {
|
|
|
912
1160
|
i++;
|
|
913
1161
|
}
|
|
914
1162
|
break;
|
|
1163
|
+
case '--mem':
|
|
1164
|
+
options.mem = true;
|
|
1165
|
+
break;
|
|
1166
|
+
case '--no-mem':
|
|
1167
|
+
options.mem = false;
|
|
1168
|
+
break;
|
|
1169
|
+
case '--auto-task':
|
|
1170
|
+
options.autoTask = true;
|
|
1171
|
+
options.mem = true;
|
|
1172
|
+
break;
|
|
1173
|
+
case '--task':
|
|
1174
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
1175
|
+
options.taskName = nextArg;
|
|
1176
|
+
options.mem = true;
|
|
1177
|
+
i++;
|
|
1178
|
+
}
|
|
1179
|
+
break;
|
|
1180
|
+
case '--criteria':
|
|
1181
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
1182
|
+
options.criteria.push(nextArg);
|
|
1183
|
+
i++;
|
|
1184
|
+
}
|
|
1185
|
+
break;
|
|
1186
|
+
case '--daemon':
|
|
1187
|
+
options.daemon = true;
|
|
1188
|
+
break;
|
|
1189
|
+
case '--until-done':
|
|
1190
|
+
options.untilDone = true;
|
|
1191
|
+
options.daemon = true;
|
|
1192
|
+
break;
|
|
1193
|
+
case '--wake':
|
|
1194
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
1195
|
+
options.wakeInterval = nextArg;
|
|
1196
|
+
i++;
|
|
1197
|
+
}
|
|
1198
|
+
break;
|
|
915
1199
|
default:
|
|
916
1200
|
if (arg.startsWith('-')) {
|
|
917
1201
|
// Unknown flag - pass through
|
|
@@ -986,29 +1270,214 @@ if (provider === 'gemini') {
|
|
|
986
1270
|
// Append raw args at the end
|
|
987
1271
|
translatedArgs.push(...rawArgs);
|
|
988
1272
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1273
|
+
// ==================== MEM INTEGRATION ====================
|
|
1274
|
+
|
|
1275
|
+
// Auto-detect mem if .mem exists (unless --no-mem)
|
|
1276
|
+
let memDir = options.mem !== false ? findMemDir() : null;
|
|
1277
|
+
if (memDir && options.mem !== false) {
|
|
1278
|
+
options.mem = true;
|
|
1279
|
+
options.memDir = memDir;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// Auto-create task if --auto-task or --task specified but no mem found
|
|
1283
|
+
if ((options.autoTask || options.taskName) && !options.memDir && finalPrompt) {
|
|
1284
|
+
const taskName = options.taskName || finalPrompt
|
|
1285
|
+
.toLowerCase()
|
|
1286
|
+
.replace(/[^a-z0-9\s]/g, '')
|
|
1287
|
+
.split(/\s+/)
|
|
1288
|
+
.slice(0, 3)
|
|
1289
|
+
.join('-');
|
|
1290
|
+
|
|
1291
|
+
console.log(`${c.dim}[mem] Creating task: ${taskName}${c.reset}`);
|
|
1292
|
+
|
|
1293
|
+
try {
|
|
1294
|
+
// Build criteria args
|
|
1295
|
+
const criteriaArgs = options.criteria.length
|
|
1296
|
+
? options.criteria.map(c => `--criteria "${c}"`).join(' ')
|
|
1297
|
+
: '';
|
|
1298
|
+
|
|
1299
|
+
// Create task non-interactively
|
|
1300
|
+
const centralMem = path.join(process.env.HOME || process.env.USERPROFILE, '.mem');
|
|
1301
|
+
|
|
1302
|
+
// Ensure central mem exists
|
|
1303
|
+
if (!fs.existsSync(centralMem)) {
|
|
1304
|
+
fs.mkdirSync(centralMem, { recursive: true });
|
|
1305
|
+
execSync('git init', { cwd: centralMem, stdio: 'ignore' });
|
|
1306
|
+
fs.writeFileSync(path.join(centralMem, 'playbook.md'), '# Playbook\n\nGlobal learnings that transfer across tasks.\n');
|
|
1307
|
+
execSync('git add -A && git commit -m "init: memory repo"', { cwd: centralMem, stdio: 'ignore', shell: true });
|
|
1007
1308
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1309
|
+
|
|
1310
|
+
// Create task branch
|
|
1311
|
+
const branch = `task/${taskName}`;
|
|
1312
|
+
try {
|
|
1313
|
+
execSync(`git checkout main`, { cwd: centralMem, stdio: 'ignore' });
|
|
1314
|
+
} catch {}
|
|
1315
|
+
execSync(`git checkout -b ${branch}`, { cwd: centralMem, stdio: 'ignore' });
|
|
1316
|
+
|
|
1317
|
+
// Create task files
|
|
1318
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1319
|
+
const criteriaText = options.criteria.length
|
|
1320
|
+
? options.criteria.map(c => `- [ ] ${c}`).join('\n')
|
|
1321
|
+
: '- [ ] Define success criteria';
|
|
1322
|
+
|
|
1323
|
+
fs.writeFileSync(path.join(centralMem, 'goal.md'),
|
|
1324
|
+
`---\ntask: ${taskName}\ncreated: ${today}\n---\n\n# Goal\n\n${finalPrompt}\n\n## Definition of Done\n\n${criteriaText}\n\n## Progress: 0%`);
|
|
1325
|
+
fs.writeFileSync(path.join(centralMem, 'state.md'),
|
|
1326
|
+
`---\nstatus: active\n---\n\n# State\n\n## Next Step\n\nBegin work\n\n## Checkpoints\n\n- [ ] Started`);
|
|
1327
|
+
fs.writeFileSync(path.join(centralMem, 'memory.md'), '# Learnings\n\n');
|
|
1328
|
+
|
|
1329
|
+
execSync('git add -A && git commit -m "init: ' + taskName + '"', { cwd: centralMem, stdio: 'ignore', shell: true });
|
|
1330
|
+
|
|
1331
|
+
// Update index
|
|
1332
|
+
const indexFile = path.join(centralMem, 'index.json');
|
|
1333
|
+
let index = {};
|
|
1334
|
+
try { index = JSON.parse(fs.readFileSync(indexFile, 'utf8')); } catch {}
|
|
1335
|
+
index[process.cwd()] = branch;
|
|
1336
|
+
fs.writeFileSync(indexFile, JSON.stringify(index, null, 2));
|
|
1337
|
+
|
|
1338
|
+
options.memDir = centralMem;
|
|
1339
|
+
console.log(`${c.green}✓${c.reset} Created task: ${c.bold}${taskName}${c.reset}`);
|
|
1340
|
+
console.log(`${c.green}✓${c.reset} Mapped: ${c.dim}${process.cwd()} → ${branch}${c.reset}`);
|
|
1341
|
+
|
|
1342
|
+
// Auto-set wake schedule for new tasks with proper command
|
|
1343
|
+
// Detect agx path dynamically for cron (no PATH in cron env)
|
|
1344
|
+
try {
|
|
1345
|
+
const projectDir = process.cwd();
|
|
1346
|
+
let agxPath = 'agx';
|
|
1347
|
+
try {
|
|
1348
|
+
agxPath = execSync('which agx', { encoding: 'utf8' }).trim();
|
|
1349
|
+
} catch {}
|
|
1350
|
+
const wakeCmd = `cd ${projectDir} && ${agxPath} claude -y -p "continue"`;
|
|
1351
|
+
execSync(`mem wake "every 15m" --run "${wakeCmd}"`, { cwd: process.cwd(), stdio: 'ignore' });
|
|
1352
|
+
console.log(`${c.green}✓${c.reset} Wake: ${c.dim}every 15m (until done)${c.reset}`);
|
|
1353
|
+
console.log(`${c.dim} Run: agx claude -y -p "continue"${c.reset}\n`);
|
|
1354
|
+
} catch {}
|
|
1355
|
+
|
|
1356
|
+
} catch (err) {
|
|
1357
|
+
console.error(`${c.yellow}Warning: Could not create task:${c.reset} ${err.message}`);
|
|
1010
1358
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// Prepend mem context to prompt if mem is enabled
|
|
1362
|
+
if (options.mem && options.memDir && finalPrompt) {
|
|
1363
|
+
const context = loadMemContext(options.memDir);
|
|
1364
|
+
if (context) {
|
|
1365
|
+
console.log(`${c.dim}[mem] Loaded context from ${options.memDir}${c.reset}\n`);
|
|
1366
|
+
|
|
1367
|
+
// Prepend context to prompt with full marker documentation
|
|
1368
|
+
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.`;
|
|
1369
|
+
|
|
1370
|
+
// Replace the prompt in translatedArgs
|
|
1371
|
+
const promptIndex = translatedArgs.indexOf(finalPrompt);
|
|
1372
|
+
if (promptIndex !== -1) {
|
|
1373
|
+
translatedArgs[promptIndex] = augmentedPrompt;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// Run with mem output capture or normal mode
|
|
1379
|
+
if (options.mem && options.memDir) {
|
|
1380
|
+
// Capture output for marker parsing
|
|
1381
|
+
const child = spawn(command, translatedArgs, {
|
|
1382
|
+
env,
|
|
1383
|
+
stdio: ['inherit', 'pipe', 'inherit'],
|
|
1384
|
+
shell: false
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
let output = '';
|
|
1388
|
+
|
|
1389
|
+
child.stdout.on('data', (data) => {
|
|
1390
|
+
const text = data.toString();
|
|
1391
|
+
output += text;
|
|
1392
|
+
process.stdout.write(text);
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
child.on('exit', async (code) => {
|
|
1396
|
+
// Parse and apply mem markers
|
|
1397
|
+
const markers = parseMemMarkers(output);
|
|
1398
|
+
|
|
1399
|
+
if (markers.length > 0) {
|
|
1400
|
+
console.log(`\n${c.dim}[mem] Processing markers...${c.reset}`);
|
|
1401
|
+
const result = applyMemMarkers(markers, options.memDir);
|
|
1402
|
+
|
|
1403
|
+
// Create subtasks if any
|
|
1404
|
+
if (result.splits.length > 0) {
|
|
1405
|
+
createSubtasks(result.splits, options.memDir);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// Handle approvals
|
|
1409
|
+
if (result.approvals.length > 0) {
|
|
1410
|
+
const approved = await handleApprovals(result.approvals);
|
|
1411
|
+
if (!approved) {
|
|
1412
|
+
console.log(`${c.yellow}Workflow halted. Resume later.${c.reset}`);
|
|
1413
|
+
process.exit(1);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// Handle loop control - default is CONTINUE (wake again in 15m)
|
|
1418
|
+
if (result.isDone) {
|
|
1419
|
+
console.log(`\n${c.green}✓ Task complete!${c.reset}`);
|
|
1420
|
+
// Clear wake schedule on done
|
|
1421
|
+
try {
|
|
1422
|
+
execSync('mem wake clear', { cwd: process.cwd(), stdio: 'ignore' });
|
|
1423
|
+
console.log(`${c.dim}Wake schedule cleared.${c.reset}`);
|
|
1424
|
+
} catch {}
|
|
1425
|
+
process.exit(0);
|
|
1426
|
+
} else if (result.isBlocked) {
|
|
1427
|
+
console.log(`\n${c.yellow}⚠ Task blocked. Human intervention needed.${c.reset}`);
|
|
1428
|
+
console.log(`${c.dim}Wake continues - run 'mem stuck clear' when unblocked.${c.reset}`);
|
|
1429
|
+
process.exit(1);
|
|
1430
|
+
} else if (options.untilDone) {
|
|
1431
|
+
// Local loop mode: wait and run again
|
|
1432
|
+
console.log(`\n${c.cyan}▶ Continuing in 10s...${c.reset}`);
|
|
1433
|
+
setTimeout(() => {
|
|
1434
|
+
const { spawnSync } = require('child_process');
|
|
1435
|
+
spawnSync(process.argv[0], process.argv.slice(1), { stdio: 'inherit' });
|
|
1436
|
+
}, 10000);
|
|
1437
|
+
return;
|
|
1438
|
+
} else {
|
|
1439
|
+
// Default: save and exit, wake will resume in 15m
|
|
1440
|
+
console.log(`\n${c.dim}Progress saved. Will continue on next wake (~15m).${c.reset}`);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
process.exit(code || 0);
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
child.on('error', (err) => {
|
|
1448
|
+
if (err.code === 'ENOENT') {
|
|
1449
|
+
console.error(`${c.red}Error:${c.reset} "${command}" command not found.`);
|
|
1450
|
+
} else {
|
|
1451
|
+
console.error(`${c.red}Failed to start ${command}:${c.reset}`, err.message);
|
|
1452
|
+
}
|
|
1453
|
+
process.exit(1);
|
|
1454
|
+
});
|
|
1455
|
+
} else {
|
|
1456
|
+
// Normal mode - stdio inherit
|
|
1457
|
+
const child = spawn(command, translatedArgs, {
|
|
1458
|
+
env,
|
|
1459
|
+
stdio: 'inherit',
|
|
1460
|
+
shell: false
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
child.on('exit', (code) => {
|
|
1464
|
+
process.exit(code || 0);
|
|
1465
|
+
});
|
|
1466
|
+
|
|
1467
|
+
child.on('error', (err) => {
|
|
1468
|
+
if (err.code === 'ENOENT') {
|
|
1469
|
+
console.error(`${c.red}Error:${c.reset} "${command}" command not found.`);
|
|
1470
|
+
console.error(`\n${c.dim}Install it first:${c.reset}`);
|
|
1471
|
+
if (command === 'claude') {
|
|
1472
|
+
console.error(` npm install -g @anthropic-ai/claude-code`);
|
|
1473
|
+
} else if (command === 'gemini') {
|
|
1474
|
+
console.error(` npm install -g @anthropic-ai/gemini-cli`);
|
|
1475
|
+
}
|
|
1476
|
+
} else {
|
|
1477
|
+
console.error(`${c.red}Failed to start ${command}:${c.reset}`, err.message);
|
|
1478
|
+
}
|
|
1479
|
+
process.exit(1);
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1013
1482
|
|
|
1014
1483
|
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mndrk/agx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Unified AI Agent Wrapper for Gemini, Claude, and Ollama",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -16,12 +16,17 @@
|
|
|
16
16
|
"gemini",
|
|
17
17
|
"claude",
|
|
18
18
|
"ollama",
|
|
19
|
-
"llm"
|
|
19
|
+
"llm",
|
|
20
|
+
"memory",
|
|
21
|
+
"persistent"
|
|
20
22
|
],
|
|
21
|
-
"author": "
|
|
23
|
+
"author": "Mendrika Ramarlina",
|
|
22
24
|
"license": "MIT",
|
|
23
25
|
"repository": {
|
|
24
26
|
"type": "git",
|
|
25
27
|
"url": "https://github.com/ramarlina/agx"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@mndrk/memx": "^0.3.0"
|
|
26
31
|
}
|
|
27
32
|
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# agx - Unified AI Agent Wrapper
|
|
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.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- **Spawning background agents** for long-running tasks
|
|
8
|
+
- **Auto-task creation** with wake schedules
|
|
9
|
+
- **Running different AI providers** with unified interface
|
|
10
|
+
- **Autonomous work loops** that continue until done
|
|
11
|
+
|
|
12
|
+
## Basic Usage
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
agx claude -p "prompt" # Run Claude Code
|
|
16
|
+
agx gemini -p "prompt" # Run Gemini
|
|
17
|
+
agx ollama -p "prompt" # Run Ollama (local)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Auto-Task Mode (Recommended)
|
|
21
|
+
|
|
22
|
+
Create a task with automatic wake schedule:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd ~/Projects/my-app
|
|
26
|
+
agx claude --auto-task -p "Build a React todo app with auth"
|
|
27
|
+
# ✓ Created task: build-react-todo
|
|
28
|
+
# ✓ Mapped: ~/Projects/my-app → task/build-react-todo
|
|
29
|
+
# ✓ Wake: every 15m (until done)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This:
|
|
33
|
+
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]
|
|
37
|
+
|
|
38
|
+
## Wake Loop
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
WAKE (cron) → Load context → Agent works → Save state → SLEEP
|
|
42
|
+
↓
|
|
43
|
+
repeat until [done]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Install the wake schedule:
|
|
47
|
+
```bash
|
|
48
|
+
mem cron export # View entry
|
|
49
|
+
(crontab -l; mem cron export) | crontab - # Install
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Output Markers
|
|
53
|
+
|
|
54
|
+
Use these in agent output to control the loop:
|
|
55
|
+
|
|
56
|
+
### Progress (parsed automatically)
|
|
57
|
+
```
|
|
58
|
+
[checkpoint: message] # Save progress point
|
|
59
|
+
[learn: insight] # Record learning
|
|
60
|
+
[next: step] # Set next step
|
|
61
|
+
[criteria: N] # Mark criterion #N complete
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Stopping Markers
|
|
65
|
+
```
|
|
66
|
+
[done] # Task complete, clear wake, stop
|
|
67
|
+
[blocked: reason] # Need human help, pause loop
|
|
68
|
+
[approve: question] # Need approval, wait
|
|
69
|
+
[pause] # Stop, resume on next wake
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Loop Control
|
|
73
|
+
```
|
|
74
|
+
[continue] # Keep going (--daemon mode loops locally)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Default behavior:** Keep working. Only output stopping markers when needed.
|
|
78
|
+
|
|
79
|
+
## Provider Aliases
|
|
80
|
+
|
|
81
|
+
| Provider | Aliases | Description |
|
|
82
|
+
|----------|---------|-------------|
|
|
83
|
+
| claude | c, cl | Anthropic Claude Code |
|
|
84
|
+
| gemini | g, gem | Google Gemini |
|
|
85
|
+
| ollama | o, oll | Local Ollama |
|
|
86
|
+
|
|
87
|
+
## Common Flags
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
--auto-task # Auto-create task from prompt
|
|
91
|
+
--task NAME # Specific task name
|
|
92
|
+
--criteria "..." # Success criterion (repeatable)
|
|
93
|
+
--daemon # Loop on [continue] marker
|
|
94
|
+
--until-done # Keep running until [done]
|
|
95
|
+
-y # Skip confirmations
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Examples
|
|
99
|
+
|
|
100
|
+
### Quick one-shot
|
|
101
|
+
```bash
|
|
102
|
+
agx claude -p "Explain this error" -y
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Background task with wake
|
|
106
|
+
```bash
|
|
107
|
+
cd ~/Projects/api
|
|
108
|
+
agx claude --auto-task -p "Add user authentication with JWT"
|
|
109
|
+
# Agent works, saves progress, wakes every 15m to continue
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Check on a running task
|
|
113
|
+
```bash
|
|
114
|
+
mem status # Quick summary
|
|
115
|
+
mem progress # % complete
|
|
116
|
+
mem context # Full state dump
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Manual continue
|
|
120
|
+
```bash
|
|
121
|
+
cd ~/Projects/api
|
|
122
|
+
agx claude -p "continue" # Resume from last checkpoint
|
|
123
|
+
```
|