@aiaiai-pt/martha-cli 0.4.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/martha-cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Terminal-first client for the Martha AI platform",
5
5
  "homepage": "https://docs.martha.nomadriver.co",
6
6
  "repository": {
@@ -15,7 +15,7 @@ Martha has a few distinct primitives that get confused in everyday talk. This is
15
15
  |---|---|---|
16
16
  | **Tenant** | Opaque data isolation boundary (`tenant_id` string). All queries filter by this. Set from the JWT, never from request body. | Every entity is tenant-scoped. You don't pass it explicitly to the CLI — it comes from your token. |
17
17
  | **Client** | A chat-API consumer (web app, SMS sender, voice line). Has `keycloak_client_id`, `system_prompts`, allowlists. **Not a tenant.** | Use when you're configuring how a frontend or messaging channel talks to Martha. |
18
- | **Agent** | An `AgentDefinition` row: prompt + LLM config + loop config + tool grants. Cloud or external. | Cloud agents are run by Martha (Temporal). External agents are remote harnesses that authenticate and execute Martha tasks. |
18
+ | **Agent** | An `AgentDefinition` row: `system_prompt` + LLM config + loop config + tool grants. Cloud or external. | Cloud agents are run by Martha (Temporal). External agents are remote harnesses that authenticate and execute Martha tasks. |
19
19
  | **Team** | A named group of agents with a routing strategy (`round_robin`, `manual`, `external`). | Use to spread work across many similar agents (e.g. 5 ork instances doing code review). |
20
20
  | **Task** | A unit of work with goal, priority, lifecycle (`open`/`claimed`/`running`/`completed`/`failed`/`cancelled`/`stale`/`poisoned`). Optionally linked to a tracker issue. | Use to queue async work for agents. Humans or agents can create them. |
21
21
  | **Function** | An HTTP endpoint or platform Python callable that an agent can invoke as a tool. Stored as `FunctionDefinition`. | Define once, grant to many agents. |
@@ -35,7 +35,7 @@ Everything below operates on these primitives. When in doubt, run `martha status
35
35
  | Flag | Purpose |
36
36
  |---|---|
37
37
  | `--json` | Machine-readable JSON output. **Always set this when piping to `jq` or parsing.** Without it, output is human-formatted and may include color codes. |
38
- | `--profile <name>` | Use a named profile from `~/.martha/profiles/`. Default profile is `default`. |
38
+ | `--profile <name>` | Use a named profile from `~/.martha/config.yaml`. |
39
39
  | `--api-url <url>` | Override `MARTHA_API_URL`. Useful for hitting staging/prod from the same shell. |
40
40
  | `--verbose` | DEBUG-level logging on stderr. Shows HTTP requests, retry attempts, token expiry decisions. |
41
41
  | `--quiet` | Suppress informational output. Errors still print. |
@@ -47,11 +47,28 @@ Everything below operates on these primitives. When in doubt, run `martha status
47
47
 
48
48
  ## Authentication
49
49
 
50
+ Install does not pre-provision a global profile. Configure one explicitly before
51
+ running commands:
52
+
53
+ ```bash
54
+ # Local development stack
55
+ martha init --no-interactive --preset local
56
+
57
+ # Customer, staging, or private-cloud tenant
58
+ martha init --no-interactive --name acme \
59
+ --api-url https://martha.acme.example \
60
+ --keycloak-url https://auth.acme.example \
61
+ --keycloak-realm acme
62
+ ```
63
+
64
+ Use `martha init --preset cloud` only when you intentionally want the hosted
65
+ Martha cloud profile (`martha.nomadriver.co` + `keycloak.frank.nomadriver.co`).
66
+
50
67
  The CLI resolves credentials in this priority order. The first non-empty wins:
51
68
 
52
69
  1. `MARTHA_TOKEN` — raw JWT, bypasses all login flows. Highest priority.
53
70
  2. `MARTHA_CLIENT_ID` + `MARTHA_CLIENT_SECRET` — OAuth2 client credentials, auto-refreshes.
54
- 3. Profile-stored token from prior `martha auth login` (cached at `~/.martha/profiles/<name>.json`).
71
+ 3. Profile-stored token from prior `martha auth login` (cached under `~/.martha/` for the active profile).
55
72
  4. Browser-based OIDC (interactive only, won't fire in non-TTY).
56
73
 
57
74
  ```bash
@@ -130,9 +147,22 @@ agent_type: cloud
130
147
  auth_method: service_account # Slice 3B: provisions Keycloak SA on create
131
148
  system_prompt: "You write friendly morning briefings."
132
149
  llm_config: { provider: anthropic, model: claude-sonnet-4-5-20250929 }
133
- loop_config: { enabled: true, max_iterations: 5 }
150
+ loop_config: { max_iterations: 5 }
134
151
  ```
135
152
 
153
+ Agent field notes:
154
+
155
+ - `system_prompt` is the durable instruction for the agent.
156
+ - Grant an agent to a chat client with
157
+ `martha clients grant <client> agent <agent>` to make it callable as a tool
158
+ from that client.
159
+ - `runs_as_chat` is an advanced top-level chat-takeover override, not normal
160
+ provisioning. Use it only when exactly one chat client should replace its own
161
+ `system_prompt`, `llm_config`, and tools with one agent.
162
+ - `loop_config.enabled` is legacy compatibility. New YAML should not use it.
163
+ - Workflow `llm` nodes use `config.prompt` for a one-step task prompt; that is
164
+ separate from an agent `system_prompt`.
165
+
136
166
  `--dry-run` prints what would change without writing. `--yes` skips the confirmation when applying to a non-empty tenant.
137
167
 
138
168
  ### Export (back up or migrate)
@@ -241,11 +271,11 @@ Default `execute_on` mapping: `llm` → `local`; `function` / `wait` / `transfor
241
271
  ```bash
242
272
  martha agents list [--inactive] [--limit 50]
243
273
  martha agents get <name> # Includes auth_method, status, llm_config, granted functions/workflows
244
- martha agents create --name <n> --model <m> --prompt "system prompt" \
274
+ martha agents create --name <n> --model <m> --system-prompt "system prompt" \
245
275
  [--description "..."] [--temperature 0.7] [--max-tokens 4096] \
246
276
  [--type cloud|external] [--auth service-account|api-key] \
247
277
  [--tags code-review,python] [--local-tools filesystem,git]
248
- martha agents update <name> [--model <m>] [--prompt "..."] [--description "..."]
278
+ martha agents update <name> -f agent.yaml
249
279
  martha agents delete <name> [--hard] [--yes]
250
280
  martha agents versions <name>
251
281
  martha agents rollback <name> <version>
@@ -272,11 +302,13 @@ martha agents generate-key <agent>
272
302
  **Function grants:**
273
303
 
274
304
  ```bash
275
- martha agents add-function <agent> <function> [--set key=value] # config_overrides
276
- martha agents remove-function <agent> <function>
305
+ martha agents add-function <agent> <function> [--set key=value] [--collection <slug-or-id>]
306
+ martha agents remove-function <agent> <function> [--collection <slug-or-id>]
277
307
  martha agents functions <agent> # List granted functions
278
308
  ```
279
309
 
310
+ `--collection` scopes the grant to one collection per the #372 PR2 cascade. Omit it to grant at the root (tenant-wide); pass it on `remove-function` to revoke just that scope. Server returns 400 if the function is collection-agnostic (e.g. `recall`).
311
+
280
312
  ---
281
313
 
282
314
  ## Tasks: queue + executor lifecycle
@@ -482,38 +514,51 @@ martha tasks poll --json
482
514
 
483
515
  A **Connection** is a stored credential record for an integration. Auth values live in HashiCorp Vault keyed by `(scope, scope_id, service_name)`. The DB only has metadata. Tracker connections include adapter-specific config (team_id, repository, project_id) stored as a flat dict in Vault — `resolve_connection_config()` is the single merge point for tracker adapter calls.
484
516
 
485
- The CLI doesn't yet have a top-level `connections` subcommand — manage them via the admin UI at `/settings` (Trackers tab) or via the API:
486
-
487
517
  ```bash
488
- # List connections (uses your token's tenant_id scope)
489
- curl -s -H "Authorization: Bearer $(martha auth token)" \
490
- "$MARTHA_API_URL/api/admin/connections" | jq
518
+ martha connections list [--integration <name>] [--scope tenant|client|system]
519
+ martha connections create --integration <name> --name <name> --auth-type <type> \
520
+ --credential-value <value|@file|-> [--config '<json>'] \
521
+ [--scope tenant|client|system] [--scope-ref <ref>] [--not-default]
522
+ martha connections update <connection-id> [--credential-value <value|@file|->] \
523
+ [--config '<json>'] [--name <name>] [--default|--not-default]
524
+ martha connections test <connection-id>
525
+ martha connections delete <connection-id> --yes
526
+ ```
491
527
 
492
- # List available tracker adapters + their config_schema
493
- curl -s -H "Authorization: Bearer $(martha auth token)" \
494
- "$MARTHA_API_URL/api/admin/trackers" | jq
528
+ **Rotating a service-account key:** `martha connections update <id> --credential-value @new-sa.json`
529
+ replaces the SA key in Vault in place (same connection id, sources stay attached)
530
+ and drops the cached SA token so the new key takes effect immediately.
495
531
 
496
- # Create a Linear connection (full config dict in JSON, stored as one Vault entry)
497
- curl -s -X POST -H "Authorization: Bearer $(martha auth token)" \
498
- -H "Content-Type: application/json" \
499
- -d '{
500
- "integration_name": "linear",
501
- "name": "production",
502
- "auth_type": "api_key",
503
- "credential_value": "{\"api_key\":\"lin_api_xxx\",\"team_id\":\"uuid-here\"}",
504
- "is_default": true
505
- }' \
506
- "$MARTHA_API_URL/api/admin/connections"
532
+ `--credential-value` accepts a literal, `@path` (read a file), or `-` (read stdin).
533
+ `--config` is a non-secret JSON object stored in Postgres (never Vault).
534
+ **OAuth2 connections cannot be created from the CLI** — they need an interactive
535
+ browser consent flow; use the admin UI under Integrations → Connections.
536
+
537
+ ```bash
538
+ # Google Drive via service account (#407) — reads enterprise Shared Drives
539
+ # without CASA. credential_value is the SA JSON key; config carries the
540
+ # optional impersonation subject (domain-wide delegation) + scopes.
541
+ martha connections create \
542
+ --integration google_drive --name "SOMENGIL Drive" \
543
+ --auth-type service_account \
544
+ --credential-value @/path/to/sa-key.json \
545
+ --config '{"subject":"owner@corp.com","scopes":["https://www.googleapis.com/auth/drive.readonly"]}'
546
+ # Then add the SA's client_email as a member (Viewer) of the Shared Drive.
507
547
 
508
- # Test a connection (calls adapter.test_connection() with merged config)
509
- curl -s -X POST -H "Authorization: Bearer $(martha auth token)" \
510
- "$MARTHA_API_URL/api/admin/connections/<connection-id>/test" | jq
548
+ # Linear connection (full config dict in JSON, stored as one Vault entry)
549
+ martha connections create --integration linear --name production \
550
+ --auth-type api_key \
551
+ --credential-value '{"api_key":"lin_api_xxx","team_id":"uuid-here"}'
511
552
 
512
- # Delete (also removes from Vault and deprovisions tracker triggers if applicable)
513
- curl -s -X DELETE -H "Authorization: Bearer $(martha auth token)" \
514
- "$MARTHA_API_URL/api/admin/connections/<connection-id>"
553
+ # Test a connection (calls the adapter's health check with merged config)
554
+ martha connections test <connection-id>
515
555
  ```
516
556
 
557
+ `martha connections create` mirrors `POST /api/admin/connections`. For
558
+ service_account it validates the key shape (`type`, `client_email`) before
559
+ submit. List available tracker adapters + their `config_schema` via
560
+ `curl -s -H "Authorization: Bearer $(martha auth token)" "$MARTHA_API_URL/api/admin/trackers" | jq`.
561
+
517
562
  **Tracker connection auto-provisioning:** When you create a connection where `integration_name` matches a tracker (`linear`/`github`/`gitlab`), the backend automatically provisions:
518
563
  1. A `webhook_definition` (returns one-time `webhook_url` + `webhook_secret` in the create response)
519
564
  2. Three trigger definitions (managed by `tracker:{type}`):
@@ -579,9 +624,10 @@ Document collections are tenant-scoped containers that ingest files (PDF, DOCX,
579
624
 
580
625
  ```bash
581
626
  # Collections
582
- martha documents collections [--inactive] [--limit 50]
627
+ martha documents collections [--inactive] [--limit 50] [--tree] # --tree renders the parent/child hierarchy as ASCII (#372)
583
628
  martha documents collection <id> # Stats, ingestion status, total size
584
- martha documents create-collection --name "My Docs" [--description "..."]
629
+ martha documents create-collection --name "My Docs" [--description "..."] [--parent <slug-or-id>]
630
+ martha documents move-collection <id> --parent <slug-or-id> | --root # Atomic move; server rejects cycles
585
631
 
586
632
  # Document lifecycle
587
633
  martha documents upload <collection-id> <file> [--wait] [--follow] # --follow streams ingestion progress
@@ -608,6 +654,24 @@ A failed enrich step does NOT fail the document — it falls back to keyword-onl
608
654
 
609
655
  ---
610
656
 
657
+ ## Document Sync (durable Drive/folder sync)
658
+
659
+ Durable sync sources mirror an external provider (Google Drive today) into Martha collections. Folder structure changes in Drive — move, rename, delete — propagate to the collection tree inbound (#372 PR5).
660
+
661
+ ```bash
662
+ # Backfill / repair the collection tree from a Drive source's folder hierarchy.
663
+ # Stamps drive_folder_id on existing collections matching (parent, name) and
664
+ # creates sub-collections for unmapped Drive folders. Idempotent.
665
+ martha document-sync reconcile-tree --source <source-id> [--dry-run]
666
+ martha document-sync reconcile-tree --all [--dry-run] # every google_drive source
667
+ ```
668
+
669
+ `reconcile-tree` is the one-shot backfill that maps a source connected before folder-sync existed (or repairs drift). It runs server-side against the source's live OAuth token, so the source must be connected and validatable. `--dry-run` prints the would-link / would-create / skipped counts without writing. Re-running is safe — already-linked folders are skipped.
670
+
671
+ Cross-source moves are rejected: a collection can't move between sync sources. Reorganize the folder in Drive directly and inbound sync mirrors it.
672
+
673
+ ---
674
+
611
675
  ## Approvals (human-in-the-loop)
612
676
 
613
677
  Approvals are pause-points inside workflows. The `approval_gate` workflow node creates an `ApprovalCase`; downstream nodes wait until a human resolves it.
@@ -653,11 +717,13 @@ martha clients update <name-or-id> [--name <n>] [--system-prompts '...']
653
717
  martha clients delete <name-or-id> [--force] [--yes] # --force drops sessions
654
718
 
655
719
  # Access grants — what definitions this client's sessions can use
656
- martha clients grant <client> function|workflow|agent <def-name> [--config key=value]
657
- martha clients revoke <client> function|workflow|agent <def-name>
720
+ martha clients grant <client> function|workflow|agent <def-name> [--config key=value] [--collection <slug-or-id>]
721
+ martha clients revoke <client> function|workflow|agent <def-name> [--collection <slug-or-id>]
658
722
  martha clients access <name-or-id> # All grants for this client
659
723
  ```
660
724
 
725
+ `--collection` is only valid on `function` grants (#372 PR2). The same UUID, slug, or name accepted by `documents move-collection` works here. Granting a scope on a collection-agnostic function (like `recall`) returns 400.
726
+
661
727
  ---
662
728
 
663
729
  ## Sessions + Chat
@@ -799,7 +865,7 @@ martha teams add-member refactoring-team ork-1
799
865
  # Agent side
800
866
  export MARTHA_CLIENT_ID=<from above>
801
867
  export MARTHA_CLIENT_SECRET=<from above>
802
- export MARTHA_API_URL=https://martha.nomadriver.co
868
+ export MARTHA_API_URL=https://martha.acme.example
803
869
  martha auth login --service-account
804
870
  martha tasks poll --json # Should return open team-scoped tasks
805
871
  ```
@@ -859,6 +925,35 @@ martha integrations specs --json | jq -r '.[] | select(.name == "linear") | .id'
859
925
  done
860
926
  ```
861
927
 
928
+ ### Collection hierarchy + scoped grants (#372)
929
+
930
+ ```bash
931
+ # See the tree
932
+ martha documents collections --tree
933
+
934
+ # Build a hierarchy
935
+ martha documents create-collection --name "Reports"
936
+ martha documents create-collection --name "2026 Reports" --parent reports
937
+ martha documents create-collection --name "Q1 2026" --parent 2026-reports
938
+
939
+ # Reorganize
940
+ martha documents move-collection q1-2026 --parent reports # promote a level
941
+ martha documents move-collection 2026-reports --root # back to top-level
942
+
943
+ # Scoped grants. Agent only sees docs in /Reports and descendants.
944
+ martha agents add-function planner list_docs --collection reports
945
+ martha agents add-function planner read_doc --collection reports
946
+
947
+ # Most-specific ancestor wins: an additional grant on a child
948
+ # collection overrides config_overrides for that subtree only.
949
+ martha agents add-function planner list_docs --collection q1-2026 --set max_results=50
950
+
951
+ # Revoke just one scope; the parent root grant (if any) survives.
952
+ martha agents remove-function planner list_docs --collection q1-2026
953
+ ```
954
+
955
+ `--collection` accepts UUID, slug, or name — same identifier the agent uses. Resolution mirrors the backend.
956
+
862
957
  ---
863
958
 
864
959
  ## Troubleshooting