@prmichaelsen/remember-mcp 3.18.1 → 3.19.0

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.
Files changed (35) hide show
  1. package/AGENT.md +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +35 -1
  4. package/agent/commands/acp.clarification-address.md +417 -0
  5. package/agent/commands/acp.clarification-create.md +5 -0
  6. package/agent/commands/acp.clarifications-research.md +326 -0
  7. package/agent/commands/acp.handoff.md +270 -0
  8. package/agent/design/local.api-token-oauth-access.md +286 -0
  9. package/agent/milestones/milestone-21-api-token-oauth-access.md +53 -0
  10. package/agent/progress.yaml +73 -2
  11. package/agent/scripts/acp.install.sh +50 -27
  12. package/agent/scripts/acp.package-validate.sh +55 -27
  13. package/agent/tasks/milestone-21-api-token-oauth-access/task-515-add-auth-scheme-config.md +56 -0
  14. package/agent/tasks/milestone-21-api-token-oauth-access/task-516-implement-oauth-token-exchange.md +56 -0
  15. package/agent/tasks/milestone-21-api-token-oauth-access/task-517-implement-local-config-resolution.md +59 -0
  16. package/agent/tasks/milestone-21-api-token-oauth-access/task-518-wire-oauth-into-server-factory.md +56 -0
  17. package/agent/tasks/milestone-21-api-token-oauth-access/task-519-tests-and-documentation.md +54 -0
  18. package/agent/tasks/unassigned/task-514-remember-mcp-consume-core-space.md +71 -0
  19. package/dist/auth/config-resolver.d.ts +28 -0
  20. package/dist/auth/config-resolver.spec.d.ts +2 -0
  21. package/dist/auth/oauth-bootstrap.d.ts +22 -0
  22. package/dist/auth/oauth-bootstrap.spec.d.ts +2 -0
  23. package/dist/auth/oauth-exchange.d.ts +32 -0
  24. package/dist/auth/oauth-exchange.spec.d.ts +2 -0
  25. package/dist/config.d.ts +24 -0
  26. package/dist/server.js +1454 -78
  27. package/package.json +1 -1
  28. package/src/auth/config-resolver.spec.ts +101 -0
  29. package/src/auth/config-resolver.ts +127 -0
  30. package/src/auth/oauth-bootstrap.spec.ts +72 -0
  31. package/src/auth/oauth-bootstrap.ts +45 -0
  32. package/src/auth/oauth-exchange.spec.ts +126 -0
  33. package/src/auth/oauth-exchange.ts +128 -0
  34. package/src/config.ts +46 -0
  35. package/src/server.ts +17 -3
@@ -0,0 +1,286 @@
1
+ # API Token & OAuth Access
2
+
3
+ **Concept**: Enable CLI and local access to remember data via API tokens exchanged through an OAuth flow
4
+ **Created**: 2026-03-14
5
+ **Status**: Design Specification
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ Users currently access remember data through two paths: the agentbase.me web UI (Firebase Auth) and deployed MCP servers (JWT via mcp-auth). Neither supports local CLI access — there's no way to run remember-mcp locally in Claude Code or call remember-rest-service from a terminal without manually managing service credentials.
12
+
13
+ This design introduces API tokens as a first-class credential type. Users generate tokens on agentbase.me, configure them locally, and use them to authenticate via a standard OAuth token exchange. The entire downstream stack remains JWT-based.
14
+
15
+ ---
16
+
17
+ ## Problem Statement
18
+
19
+ - **No CLI access path**: Users cannot connect Claude Code (local) to their cloud remember data without fragile manual secret management and raw curl commands.
20
+ - **Platform coupling**: The only auth path is through agentbase.me's Firebase Auth, which requires a browser and doesn't support headless/CLI workflows.
21
+ - **Multi-tenant inflexibility**: Self-hosted platforms can't use the same auth infrastructure because credentials are hardcoded to agentbase.me.
22
+
23
+ ---
24
+
25
+ ## Solution
26
+
27
+ Introduce a three-layer auth architecture:
28
+
29
+ 1. **API Token** (credential) — opaque, prefixed, short-lived (1 hour), stored as SHA-256 hash in Firestore
30
+ 2. **OAuth Token Exchange** (flow) — API token is exchanged for a JWT via `REMEMBER_OAUTH_ENDPOINT`
31
+ 3. **JWT** (access) — existing downstream code, unchanged
32
+
33
+ ```
34
+ User generates token on agentbase.me
35
+ → stored hashed in Firestore, raw shown once
36
+ → user saves to .remember/config or REMEMBER_API_TOKEN env var
37
+
38
+ remember-mcp starts with REMEMBER_AUTH_SCHEME=oauth
39
+ → reads REMEMBER_API_TOKEN + REMEMBER_OAUTH_ENDPOINT
40
+ → exchanges API token for JWT via OAuth endpoint
41
+ → proceeds with JWT as normal (userId extracted from claims)
42
+
43
+ All downstream code unchanged — same request.userId, same tool handlers
44
+ ```
45
+
46
+ ### Alternative Considered: Direct Token Validation in Auth Guard
47
+
48
+ Extending the remember-rest-service auth guard to validate API tokens directly (Firestore lookup per request). Rejected because:
49
+ - Mixes two auth strategies in one guard
50
+ - Doesn't work with mcp-auth's existing OAuth support
51
+ - Couples remember-rest-service to a specific token storage backend
52
+ - The OAuth exchange approach is standard and enables self-hosted platforms
53
+
54
+ ---
55
+
56
+ ## Implementation
57
+
58
+ ### Token Format
59
+
60
+ Prefixed opaque tokens with secret/public key distinction:
61
+
62
+ ```
63
+ ab_live-sk_<32 bytes hex> # secret key (server-side, CLI)
64
+ ab_live-pk_<32 bytes hex> # public key (client-side, future)
65
+ ```
66
+
67
+ - Opaque: leaked token reveals nothing (no embedded userId)
68
+ - Prefix enables secret scanning (GitHub, GitGuardian)
69
+ - 32 bytes of entropy (256 bits)
70
+
71
+ ### Firestore Schema
72
+
73
+ Collection: `api_tokens` (top-level, keyed by SHA-256 hash of raw token)
74
+
75
+ ```typescript
76
+ interface ApiToken {
77
+ user_id: string;
78
+ name: string; // human-readable label, e.g. "MacBook Pro"
79
+ scopes: string[]; // ["*"] for full access, or specific scopes
80
+ created_at: Timestamp;
81
+ expires_at: Timestamp; // created_at + 1 hour
82
+ last_used_at: Timestamp;
83
+ disabled: boolean; // emergency kill switch
84
+ }
85
+ ```
86
+
87
+ Lookup: `hash(raw_token)` → document → `user_id`
88
+
89
+ ### Auth Scheme Configuration
90
+
91
+ remember-mcp supports two auth schemes via `REMEMBER_AUTH_SCHEME`:
92
+
93
+ | Env Var | Value | Behavior |
94
+ |---|---|---|
95
+ | `REMEMBER_AUTH_SCHEME` | `service` (default) | Current behavior — JWT via mcp-auth, deployed behind remember-mcp-server |
96
+ | `REMEMBER_AUTH_SCHEME` | `oauth` | Local mode — exchanges API token for JWT via OAuth endpoint |
97
+ | `REMEMBER_OAUTH_ENDPOINT` | URL | OAuth token exchange endpoint (required when scheme=oauth) |
98
+ | `REMEMBER_API_TOKEN` | token | API token for OAuth exchange (or read from `.remember/config`) |
99
+
100
+ ### Local Configuration Resolution
101
+
102
+ Token and endpoint are resolved in order (first wins):
103
+
104
+ 1. `./.remember/config` (project-level)
105
+ 2. `~/.remember/config` (global)
106
+ 3. `REMEMBER_API_TOKEN` / `REMEMBER_OAUTH_ENDPOINT` env vars (override)
107
+
108
+ Config file format:
109
+
110
+ ```yaml
111
+ # .remember/config
112
+ oauth_endpoint: https://agentbase.me/api/oauth/token
113
+ api_token: ab_live-sk_...
114
+ ```
115
+
116
+ This enables per-project multi-tenant support (different projects → different platforms).
117
+
118
+ ### OAuth Token Exchange
119
+
120
+ The OAuth endpoint accepts an API token and returns a JWT:
121
+
122
+ ```
123
+ POST {REMEMBER_OAUTH_ENDPOINT}
124
+ Content-Type: application/json
125
+
126
+ {
127
+ "grant_type": "api_token",
128
+ "api_token": "ab_live-sk_..."
129
+ }
130
+
131
+ → 200 OK
132
+ {
133
+ "access_token": "<jwt>",
134
+ "token_type": "Bearer",
135
+ "expires_in": 3600
136
+ }
137
+ ```
138
+
139
+ The JWT contains the same claims as browser-issued tokens (`sub`, `iss`, `aud`), so downstream code is unaware of the auth method.
140
+
141
+ ### Auth Method Observability
142
+
143
+ The auth guard sets `request.authMethod` alongside `request.userId`:
144
+
145
+ ```typescript
146
+ request.userId = payload.sub;
147
+ request.authMethod = 'jwt' | 'api_token';
148
+ ```
149
+
150
+ Downstream tool handlers ignore `authMethod`. Rate-limiting and logging can use it.
151
+
152
+ ### Rate Limiting
153
+
154
+ | Tier | Rate | Purpose |
155
+ |---|---|---|
156
+ | Token hard limit | Higher than JWT default | API tokens get more headroom for programmatic access |
157
+ | Token emergency limit | Burst threshold | Auto-disable token on anomalous burst activity |
158
+
159
+ Rate limits are per-token (not per-user), enabling independent limits per credential.
160
+
161
+ ### agentbase.me Endpoints
162
+
163
+ | Endpoint | Method | Description |
164
+ |---|---|---|
165
+ | `/api/tokens` | POST | Create token (returns raw token once) |
166
+ | `/api/tokens` | GET | List tokens (metadata only, no raw values) |
167
+ | `/api/tokens/:id` | DELETE | Revoke token |
168
+ | `/api/oauth/token` | POST | OAuth token exchange (API token → JWT) |
169
+
170
+ ### CLI Login Flow
171
+
172
+ ```
173
+ $ agentbase login
174
+ Opening browser for authentication...
175
+ → Browser opens agentbase.me/cli-auth
176
+ → User authenticates via Firebase Auth
177
+ → agentbase.me generates API token
178
+ → Token sent back to CLI via localhost callback
179
+ Authenticated! Token saved to ~/.remember/config
180
+ ```
181
+
182
+ ### agentbase.me UI
183
+
184
+ Settings page: "API Tokens"
185
+ - Create token (name field, shown-once raw value)
186
+ - List tokens (name, created date, last used, revoke button)
187
+ - Token scopes display (v1: always "Full access")
188
+
189
+ ---
190
+
191
+ ## Benefits
192
+
193
+ - **CLI access**: Claude Code and terminal workflows can access cloud remember data
194
+ - **Platform agnostic**: `REMEMBER_OAUTH_ENDPOINT` can point to any compatible platform, not just agentbase.me
195
+ - **Zero downstream changes**: All existing tool handlers, services, and auth context work unchanged
196
+ - **Security**: Opaque tokens, SHA-256 hashing, 1-hour expiry, emergency disable
197
+ - **Multi-tenant**: Per-project `.remember/config` supports different accounts/platforms
198
+
199
+ ---
200
+
201
+ ## Trade-offs
202
+
203
+ - **Firestore read per exchange**: Each OAuth token exchange requires a Firestore lookup by token hash. Mitigated by 1-hour JWT TTL (one lookup per hour, not per request).
204
+ - **Token management complexity**: Users must manage tokens (generate, store, revoke). Mitigated by `agentbase login` CLI flow.
205
+ - **Cross-project coordination**: Changes span 4-5 codebases. Mitigated by breaking into per-project milestones.
206
+
207
+ ---
208
+
209
+ ## Dependencies
210
+
211
+ - **agentbase.me**: Token CRUD API, OAuth endpoint, CLI auth page, settings UI
212
+ - **remember-core**: Firestore paths for `api_tokens` collection, optional token validation helpers
213
+ - **remember-rest-service**: Auth guard extension for `authMethod` observability
214
+ - **remember-mcp**: `REMEMBER_AUTH_SCHEME` config, OAuth token exchange at startup
215
+ - **agentbase-mcp-server** (new): Dedicated codebase wrapping remember-mcp with agentbase.me OAuth
216
+
217
+ ---
218
+
219
+ ## Testing Strategy
220
+
221
+ - **Unit**: Token generation (format, entropy), hashing, config resolution order, auth scheme switching
222
+ - **Integration**: Full OAuth flow (create token → exchange → JWT → tool call), token expiry, revocation
223
+ - **Security**: Verify raw tokens never stored, hash collisions, prefix scanning, disabled token rejection
224
+ - **E2E**: `agentbase login` flow, Claude Code local session with cloud data
225
+
226
+ ---
227
+
228
+ ## Migration Path
229
+
230
+ 1. **agentbase.me**: Add `/api/tokens` CRUD + `/api/oauth/token` exchange endpoint + settings UI
231
+ 2. **remember-core**: Add `api_tokens` Firestore paths, optional validation helpers
232
+ 3. **remember-mcp**: Add `REMEMBER_AUTH_SCHEME=oauth` mode with token exchange
233
+ 4. **remember-rest-service**: Add `request.authMethod` to auth guard
234
+ 5. **CLI**: Build `agentbase login` command (or npm package)
235
+
236
+ No breaking changes — `REMEMBER_AUTH_SCHEME` defaults to `service` (current behavior).
237
+
238
+ ---
239
+
240
+ ## Key Design Decisions
241
+
242
+ ### Token Format & Security
243
+
244
+ | Decision | Choice | Rationale |
245
+ |---|---|---|
246
+ | Token opacity | Opaque (random bytes) | Leaked token reveals nothing; userId requires API lookup which is rate-limitable and revocable |
247
+ | Token prefix | `ab_live-sk_` / `ab_live-pk_` | Enables secret scanning tools (GitHub, GitGuardian) |
248
+ | Storage | SHA-256 hash only | Raw token never persisted; shown once at creation |
249
+ | Expiry | 1 hour | Matches Google OAuth convention; limits blast radius of leaked tokens |
250
+ | Scopes | Supported but v1 = full access | Schema includes `scopes` array; v1 always `["*"]` |
251
+
252
+ ### Architecture
253
+
254
+ | Decision | Choice | Rationale |
255
+ |---|---|---|
256
+ | Auth integration | OAuth token exchange (not direct guard validation) | Clean separation; works with mcp-auth; enables self-hosted platforms |
257
+ | Auth scheme env var | `REMEMBER_AUTH_SCHEME=service\|oauth` | remember-mcp stays platform-agnostic; no agentbase.me coupling in core code |
258
+ | Config naming | `REMEMBER_*` (not `AGENTBASE_*`) | Core code must be platform-agnostic; any OAuth endpoint should work |
259
+ | Config resolution | `./.remember/config` > `~/.remember/config` > env var | Per-project multi-tenancy; similar to `.npmrc` precedence |
260
+
261
+ ### Observability
262
+
263
+ | Decision | Choice | Rationale |
264
+ |---|---|---|
265
+ | `request.authMethod` | `'jwt' \| 'api_token'` alongside `request.userId` | Zero downstream changes; rate-limiting and logging can differentiate |
266
+ | Rate limiting | Per-token with hard + emergency limits | Protects against abuse per credential; emergency auto-disable on burst |
267
+
268
+ ---
269
+
270
+ ## Future Considerations
271
+
272
+ - **Multiple tokens per user**: Schema supports array; v1 limited to single active token
273
+ - **Scoped tokens**: Read-only, per-tool, per-space scoping
274
+ - **Fully local mode**: Local Weaviate + Firestore emulator for offline/air-gapped use
275
+ - **Token rotation**: Automatic refresh before expiry
276
+ - **CLI package**: `@prmichaelsen/agentbase-cli` npm package with `agentbase login`, `agentbase status`, etc.
277
+ - **`remember_login` MCP tool**: In-session authentication for remember-mcp
278
+
279
+ ---
280
+
281
+ **Status**: Design Specification
282
+ **Recommendation**: Break into per-project milestones and begin with agentbase.me token CRUD + OAuth endpoint
283
+ **Related Documents**:
284
+ - [Clarification 5: API Token & CLI Access](../clarifications/clarification-5-api-token-cli-access.md)
285
+ - [REST API Architecture](../../remember-rest-service/agent/design/local.rest-api-architecture.md)
286
+ - [Group ACL Integration](local.group-acl-integration.md)
@@ -0,0 +1,53 @@
1
+ # Milestone 21: API Token & OAuth Access
2
+
3
+ **Status**: not_started
4
+ **Started**: —
5
+ **Estimated Weeks**: 1
6
+ **Tasks**: 5
7
+
8
+ ---
9
+
10
+ ## Goal
11
+
12
+ Enable remember-mcp to run locally (e.g. in Claude Code) by supporting an OAuth auth scheme where an API token is exchanged for a JWT via a configurable endpoint. This is the remember-mcp slice of the cross-project API Token feature.
13
+
14
+ ## Scope
15
+
16
+ This milestone covers **remember-mcp changes only**. Other projects (agentbase.me, remember-core, remember-rest-service) have their own milestones for the remaining pieces.
17
+
18
+ Changes:
19
+ - `REMEMBER_AUTH_SCHEME` config (`service` | `oauth`)
20
+ - OAuth token exchange client
21
+ - Local config file resolution (`.remember/config`)
22
+ - Wiring into server-factory startup
23
+ - Tests and documentation
24
+
25
+ ## Deliverables
26
+
27
+ - [ ] Config module supports `REMEMBER_AUTH_SCHEME`, `REMEMBER_OAUTH_ENDPOINT`, `REMEMBER_API_TOKEN`
28
+ - [ ] OAuth token exchange client exchanges API token for JWT
29
+ - [ ] `.remember/config` resolution (project > global > env var)
30
+ - [ ] server-factory uses OAuth flow when `REMEMBER_AUTH_SCHEME=oauth`
31
+ - [ ] Unit tests for config, exchange client, config resolution
32
+ - [ ] CHANGELOG and README updated
33
+
34
+ ## Success Criteria
35
+
36
+ - `REMEMBER_AUTH_SCHEME=service` behaves exactly as today (no regression)
37
+ - `REMEMBER_AUTH_SCHEME=oauth` exchanges token and starts server with resolved userId
38
+ - Config resolution order: `./.remember/config` > `~/.remember/config` > env vars
39
+ - Build passes, all existing tests pass, new tests cover oauth flow
40
+
41
+ ## Dependencies
42
+
43
+ - Design: [local.api-token-oauth-access.md](../design/local.api-token-oauth-access.md)
44
+ - Clarification: [clarification-5-api-token-cli-access.md](../clarifications/clarification-5-api-token-cli-access.md)
45
+ - External: agentbase.me must have `/api/oauth/token` endpoint for end-to-end testing (can be mocked for unit tests)
46
+
47
+ ## Tasks
48
+
49
+ - [Task 515: Add auth scheme config](../tasks/milestone-21-api-token-oauth-access/task-515-add-auth-scheme-config.md)
50
+ - [Task 516: Implement OAuth token exchange client](../tasks/milestone-21-api-token-oauth-access/task-516-implement-oauth-token-exchange.md)
51
+ - [Task 517: Implement local config resolution](../tasks/milestone-21-api-token-oauth-access/task-517-implement-local-config-resolution.md)
52
+ - [Task 518: Wire OAuth into server-factory](../tasks/milestone-21-api-token-oauth-access/task-518-wire-oauth-into-server-factory.md)
53
+ - [Task 519: Tests and documentation](../tasks/milestone-21-api-token-oauth-access/task-519-tests-and-documentation.md)
@@ -2,10 +2,10 @@
2
2
 
3
3
  project:
4
4
  name: remember-mcp
5
- version: 3.15.4
5
+ version: 3.19.0
6
6
  started: 2026-02-11
7
7
  status: in_progress
8
- current_milestone: M20
8
+ current_milestone: M21
9
9
  last_updated: 2026-03-07
10
10
 
11
11
  milestones:
@@ -381,6 +381,26 @@ milestones:
381
381
  See: agent/milestones/milestone-20-unified-internal-memory-tools.md
382
382
  Design: agent/design/local.unified-internal-memory-tools.md
383
383
 
384
+ - id: M21
385
+ name: API Token & OAuth Access
386
+ status: not_started
387
+ progress: 0%
388
+ estimated_weeks: 1
389
+ tasks_completed: 5
390
+ tasks_total: 5
391
+ completed: 2026-03-14
392
+ status: completed
393
+ progress: 100%
394
+ notes: |
395
+ 🎉 MILESTONE COMPLETE! Local CLI access via OAuth token exchange.
396
+ ✅ Task 515: Auth scheme config (discriminated union, no Zod)
397
+ ✅ Task 516: OAuth token exchange client (native fetch, JWT decode)
398
+ ✅ Task 517: .remember/config resolution (project > global > env)
399
+ ✅ Task 518: Wire OAuth into server.ts startup
400
+ ✅ Task 519: 20 new tests (450 total), CHANGELOG, README, v3.19.0
401
+ See: agent/milestones/milestone-21-api-token-oauth-access.md
402
+ Design: agent/design/local.api-token-oauth-access.md
403
+
384
404
  tasks:
385
405
  milestone_1:
386
406
  - id: task-1
@@ -2408,3 +2428,54 @@ task_20_completion:
2408
2428
  ✅ Ghost source isolation verified (scope tags filter per ghost type)
2409
2429
  ✅ Agent exclusion from default search verified (task-216)
2410
2430
  ✅ 436 total tests passing
2431
+
2432
+ milestone_21:
2433
+ - id: task-515
2434
+ name: Add Auth Scheme Config
2435
+ status: completed
2436
+ completed_date: 2026-03-14
2437
+ file: agent/tasks/milestone-21-api-token-oauth-access/task-515-add-auth-scheme-config.md
2438
+ estimated_hours: 2
2439
+ dependencies: []
2440
+ notes: |
2441
+ 📋 Add REMEMBER_AUTH_SCHEME, REMEMBER_OAUTH_ENDPOINT, REMEMBER_API_TOKEN to config module
2442
+
2443
+ - id: task-516
2444
+ name: Implement OAuth Token Exchange Client
2445
+ status: completed
2446
+ completed_date: 2026-03-14
2447
+ file: agent/tasks/milestone-21-api-token-oauth-access/task-516-implement-oauth-token-exchange.md
2448
+ estimated_hours: 3
2449
+ dependencies: [task-515]
2450
+ notes: |
2451
+ 📋 Exchange API token for JWT via OAuth endpoint, extract userId from JWT
2452
+
2453
+ - id: task-517
2454
+ name: Implement Local Config Resolution
2455
+ status: completed
2456
+ completed_date: 2026-03-14
2457
+ file: agent/tasks/milestone-21-api-token-oauth-access/task-517-implement-local-config-resolution.md
2458
+ estimated_hours: 2-3
2459
+ dependencies: [task-515]
2460
+ notes: |
2461
+ 📋 .remember/config resolution: project > global > env vars
2462
+
2463
+ - id: task-518
2464
+ name: Wire OAuth into Server Factory
2465
+ status: completed
2466
+ completed_date: 2026-03-14
2467
+ file: agent/tasks/milestone-21-api-token-oauth-access/task-518-wire-oauth-into-server-factory.md
2468
+ estimated_hours: 2-3
2469
+ dependencies: [task-515, task-516, task-517]
2470
+ notes: |
2471
+ 📋 Bootstrap OAuth flow at startup when REMEMBER_AUTH_SCHEME=oauth
2472
+
2473
+ - id: task-519
2474
+ name: Tests and Documentation
2475
+ status: completed
2476
+ completed_date: 2026-03-14
2477
+ file: agent/tasks/milestone-21-api-token-oauth-access/task-519-tests-and-documentation.md
2478
+ estimated_hours: 2-3
2479
+ dependencies: [task-515, task-516, task-517, task-518]
2480
+ notes: |
2481
+ 📋 Unit tests for all auth modules, CHANGELOG, README, version bump
@@ -112,6 +112,11 @@ if [ -d "$TEMP_DIR/agent/index" ]; then
112
112
  find "$TEMP_DIR/agent/index" -maxdepth 1 -name "*.template.yaml" -exec cp {} "$TARGET_DIR/agent/index/" \;
113
113
  fi
114
114
 
115
+ # Copy bundled index files (e.g. acp.core.yaml)
116
+ if [ -d "$TEMP_DIR/agent/index" ]; then
117
+ find "$TEMP_DIR/agent/index" -maxdepth 1 -name "*.yaml" ! -name "*.template.yaml" ! -name "local.*" -exec cp {} "$TARGET_DIR/agent/index/" \;
118
+ fi
119
+
115
120
  # Copy command template
116
121
  cp "$TEMP_DIR/agent/commands/command.template.md" "$TARGET_DIR/agent/commands/"
117
122
 
@@ -182,28 +187,30 @@ if [ -d "$TEMP_DIR/agent/scripts" ]; then
182
187
  yaml_parse "$TEMP_DIR/package.yaml"
183
188
  fi
184
189
 
185
- # Find all NON-experimental commands in package (or all if no experimental filtering)
190
+ # Find all NON-experimental commands and collect their scripts
186
191
  PACKAGE_COMMANDS=()
187
- while IFS= read -r cmd; do
188
- if [ -n "$cmd" ] && [ "$cmd" != "null" ]; then
189
- # Check if command is experimental
190
- is_exp=$(yaml_query ".contents.commands[] | select(.name == \"$cmd\") | .experimental?" 2>/dev/null || echo "false")
191
- if [ "$is_exp" != "true" ]; then
192
- PACKAGE_COMMANDS+=("$cmd")
193
- fi
194
- fi
195
- done < <(yaml_query ".contents.commands[].name" 2>/dev/null || echo "")
196
-
197
- # Collect required scripts from non-experimental commands
198
192
  REQUIRED_SCRIPTS=()
199
- for cmd in "${PACKAGE_COMMANDS[@]}"; do
200
- # Read scripts array from package.yaml for this command
201
- cmd_scripts=$(yaml_query ".contents.commands[] | select(.name == \"$cmd\") | .scripts[]?" 2>/dev/null || echo "")
202
-
203
- # Add each script to required list (with deduplication)
204
- while IFS= read -r script; do
205
- if [ -n "$script" ] && [ "$script" != "null" ]; then
206
- # Check if already in list
193
+ cmd_index=0
194
+ while true; do
195
+ cmd_name=$(yaml_query ".contents.commands[$cmd_index].name" 2>/dev/null || echo "")
196
+ if [ -z "$cmd_name" ] || [ "$cmd_name" = "null" ]; then
197
+ break
198
+ fi
199
+
200
+ # Check if command is experimental
201
+ is_exp=$(yaml_query ".contents.commands[$cmd_index].experimental" 2>/dev/null || echo "false")
202
+ if [ "$is_exp" != "true" ]; then
203
+ PACKAGE_COMMANDS+=("$cmd_name")
204
+
205
+ # Collect scripts for this command
206
+ script_index=0
207
+ while true; do
208
+ script=$(yaml_query ".contents.commands[$cmd_index].scripts[$script_index]" 2>/dev/null || echo "")
209
+ if [ -z "$script" ] || [ "$script" = "null" ]; then
210
+ break
211
+ fi
212
+
213
+ # Add to required scripts (with deduplication)
207
214
  already_added=false
208
215
  for existing in "${REQUIRED_SCRIPTS[@]}"; do
209
216
  if [ "$existing" = "$script" ]; then
@@ -211,12 +218,16 @@ if [ -d "$TEMP_DIR/agent/scripts" ]; then
211
218
  break
212
219
  fi
213
220
  done
214
-
221
+
215
222
  if [ "$already_added" = false ]; then
216
223
  REQUIRED_SCRIPTS+=("$script")
217
224
  fi
218
- fi
219
- done <<< "$cmd_scripts"
225
+
226
+ script_index=$((script_index + 1))
227
+ done
228
+ fi
229
+
230
+ cmd_index=$((cmd_index + 1))
220
231
  done
221
232
 
222
233
  # Install required scripts (excluding common.sh and yaml-parser.sh already copied)
@@ -224,17 +235,29 @@ if [ -d "$TEMP_DIR/agent/scripts" ]; then
224
235
  if [ "$script" = "acp.common.sh" ] || [ "$script" = "acp.yaml-parser.sh" ]; then
225
236
  continue # Already copied
226
237
  fi
227
-
238
+
228
239
  if [ -f "$TEMP_DIR/agent/scripts/$script" ]; then
229
- # Check if script is experimental
230
- is_exp=$(yaml_query ".contents.scripts[] | select(.name == \"$script\") | .experimental?" 2>/dev/null || echo "false")
240
+ # Check if script is experimental by finding it in contents.scripts
241
+ is_exp="false"
242
+ script_check_index=0
243
+ while true; do
244
+ s_name=$(yaml_query ".contents.scripts[$script_check_index].name" 2>/dev/null || echo "")
245
+ if [ -z "$s_name" ] || [ "$s_name" = "null" ]; then
246
+ break
247
+ fi
248
+ if [ "$s_name" = "$script" ]; then
249
+ is_exp=$(yaml_query ".contents.scripts[$script_check_index].experimental" 2>/dev/null || echo "false")
250
+ break
251
+ fi
252
+ script_check_index=$((script_check_index + 1))
253
+ done
231
254
  if [ "$is_exp" != "true" ]; then
232
255
  cp "$TEMP_DIR/agent/scripts/$script" "$TARGET_DIR/agent/scripts/"
233
256
  chmod +x "$TARGET_DIR/agent/scripts/$script"
234
257
  fi
235
258
  fi
236
259
  done
237
-
260
+
238
261
  echo "${GREEN}✓${NC} Installed ${#REQUIRED_SCRIPTS[@]} required script(s)"
239
262
  else
240
263
  # Direct install mode (no package.yaml) - copy all scripts