syntaur 0.11.0 → 0.13.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 (77) hide show
  1. package/.claude-plugin/plugin.json +3 -1
  2. package/README.md +21 -0
  3. package/dashboard/dist/assets/{_basePickBy-DPIRyskD.js → _basePickBy-qEDLAY_J.js} +1 -1
  4. package/dashboard/dist/assets/{_baseUniq-CwicZZgc.js → _baseUniq-7tDs18Gn.js} +1 -1
  5. package/dashboard/dist/assets/{arc-CcHe1anq.js → arc-Brdw0dYW.js} +1 -1
  6. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-4lwARCsm.js → architectureDiagram-2XIMDMQ5-BiEdLT9m.js} +1 -1
  7. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-SdqET9RP.js → blockDiagram-WCTKOSBZ-HC-j_hPZ.js} +1 -1
  8. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-BAKj5SV2.js → c4Diagram-IC4MRINW-DA_rWbl7.js} +1 -1
  9. package/dashboard/dist/assets/channel-D2-caOn6.js +1 -0
  10. package/dashboard/dist/assets/{chunk-4BX2VUAB-Dn95vjsS.js → chunk-4BX2VUAB-CF3hguyj.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-55IACEB6-C6L5bDoe.js → chunk-55IACEB6-BTeCeIc_.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-FMBD7UC4---Go123J.js → chunk-FMBD7UC4-8qVvxmks.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-JSJVCQXG-C9E63Ryg.js → chunk-JSJVCQXG-DCf8n7EO.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-KX2RTZJC-Yyi0h479.js → chunk-KX2RTZJC-CcpjTmME.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-NQ4KR5QH-2Fh1XO45.js → chunk-NQ4KR5QH-BnFPKo4-.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-QZHKN3VN-B4a3DKz-.js → chunk-QZHKN3VN-IPMVLLv2.js} +1 -1
  17. package/dashboard/dist/assets/{chunk-WL4C6EOR-XtMf_tFy.js → chunk-WL4C6EOR-D1pQmHqX.js} +1 -1
  18. package/dashboard/dist/assets/classDiagram-VBA2DB6C-HFOb_XEt.js +1 -0
  19. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-HFOb_XEt.js +1 -0
  20. package/dashboard/dist/assets/clone-BrYOwgIq.js +1 -0
  21. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-8wHbmnAP.js → cose-bilkent-S5V4N54A-tvJjvBQr.js} +1 -1
  22. package/dashboard/dist/assets/{dagre-KLK3FWXG-uJZOLE_S.js → dagre-KLK3FWXG-qp1vt6LY.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-E7M64L7V-BZih6Ivf.js → diagram-E7M64L7V-VF5eizNr.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-IFDJBPK2-CtR6WhET.js → diagram-IFDJBPK2-C3pCbJ1e.js} +1 -1
  25. package/dashboard/dist/assets/{diagram-P4PSJMXO-DTGhoDSE.js → diagram-P4PSJMXO-D-taXZwH.js} +1 -1
  26. package/dashboard/dist/assets/{erDiagram-INFDFZHY-Yr93h1x4.js → erDiagram-INFDFZHY-QjY-NHVs.js} +1 -1
  27. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-CZ3QY4eZ.js → flowDiagram-PKNHOUZH-FjO8P5Tq.js} +1 -1
  28. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-CSf--huI.js → ganttDiagram-A5KZAMGK-Bsn7CyK3.js} +1 -1
  29. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-CokKKyLR.js → gitGraphDiagram-K3NZZRJ6-BZ8DjhnF.js} +1 -1
  30. package/dashboard/dist/assets/{graph-DRs4cE7X.js → graph-AdliIYqX.js} +1 -1
  31. package/dashboard/dist/assets/index-BoR_oyUZ.css +1 -0
  32. package/dashboard/dist/assets/index-RCBI4aX2.js +525 -0
  33. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-C4CTdWLD.js → infoDiagram-LFFYTUFH-BfZjLA86.js} +1 -1
  34. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-DGrfYHHk.js → ishikawaDiagram-PHBUUO56-BSgSGMp5.js} +1 -1
  35. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-CEDbuCY2.js → journeyDiagram-4ABVD52K-BlQ7qeMC.js} +1 -1
  36. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-BjgAWDsE.js → kanban-definition-K7BYSVSG-CifO0AUu.js} +1 -1
  37. package/dashboard/dist/assets/{layout-Coi0pRnm.js → layout-B9r-gpD9.js} +1 -1
  38. package/dashboard/dist/assets/{linear-B4xKytgE.js → linear-CaaIRxVZ.js} +1 -1
  39. package/dashboard/dist/assets/{mermaid.core-dkpoLY2K.js → mermaid.core-C8wCWYw-.js} +4 -4
  40. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-BgUfAXkB.js → mindmap-definition-YRQLILUH-BpX5T7iT.js} +1 -1
  41. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-BBuMJroZ.js → pieDiagram-SKSYHLDU-BNGYtWrc.js} +1 -1
  42. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-iXygRpen.js → quadrantDiagram-337W2JSQ-CR4GeM9D.js} +1 -1
  43. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-DhJGFHd3.js → requirementDiagram-Z7DCOOCP-CXrPkMLl.js} +1 -1
  44. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-CLkFLBq1.js → sankeyDiagram-WA2Y5GQK-CIzof7Dm.js} +1 -1
  45. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-DQh28Zt-.js → sequenceDiagram-2WXFIKYE-BJZLt0O2.js} +1 -1
  46. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-BZKsD9LJ.js → stateDiagram-RAJIS63D-x9gC6zy6.js} +1 -1
  47. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-CN5Dcv7u.js +1 -0
  48. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-DCJlSWVw.js → timeline-definition-YZTLITO2-D_ZSQn1K.js} +1 -1
  49. package/dashboard/dist/assets/{treemap-KZPCXAKY-DEGy-zW9.js → treemap-KZPCXAKY-LPpMEco4.js} +1 -1
  50. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-CfQAKq7b.js → vennDiagram-LZ73GAT5-CRFZQpr7.js} +1 -1
  51. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-CgD-asFJ.js → xychartDiagram-JWTSCODW-Bivutynw.js} +1 -1
  52. package/dashboard/dist/index.html +2 -2
  53. package/dist/dashboard/server.js +355 -81
  54. package/dist/dashboard/server.js.map +1 -1
  55. package/dist/db/leases-db.d.ts +178 -0
  56. package/dist/db/leases-db.js +557 -0
  57. package/dist/db/leases-db.js.map +1 -0
  58. package/dist/index.js +1219 -357
  59. package/dist/index.js.map +1 -1
  60. package/package.json +1 -1
  61. package/platforms/claude-code/.claude-plugin/plugin.json +3 -1
  62. package/platforms/claude-code/skills/claim-resource/SKILL.md +89 -0
  63. package/platforms/claude-code/skills/release-resource/SKILL.md +65 -0
  64. package/platforms/claude-code/skills/syntaur-protocol/SKILL.md +1 -0
  65. package/platforms/codex/skills/claim-resource/SKILL.md +89 -0
  66. package/platforms/codex/skills/release-resource/SKILL.md +65 -0
  67. package/platforms/codex/skills/syntaur-protocol/SKILL.md +1 -0
  68. package/skills/claim-resource/SKILL.md +89 -0
  69. package/skills/release-resource/SKILL.md +65 -0
  70. package/skills/syntaur-protocol/SKILL.md +1 -0
  71. package/dashboard/dist/assets/channel-DKdluNz1.js +0 -1
  72. package/dashboard/dist/assets/classDiagram-VBA2DB6C-93dPLc4W.js +0 -1
  73. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-93dPLc4W.js +0 -1
  74. package/dashboard/dist/assets/clone-vnWuXQgZ.js +0 -1
  75. package/dashboard/dist/assets/index-B9uCPhOL.js +0 -510
  76. package/dashboard/dist/assets/index-BKoccIZR.css +0 -1
  77. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DU5UnRV-.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "syntaur",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Project workflow CLI with dashboard, Claude Code plugin, and Codex plugin",
5
5
  "homepage": "https://github.com/prong-horn/syntaur#readme",
6
6
  "repository": {
@@ -25,6 +25,8 @@
25
25
  "./skills/add-memory",
26
26
  "./skills/list-assignments",
27
27
  "./skills/log-progress",
28
- "./skills/set-workspace"
28
+ "./skills/set-workspace",
29
+ "./skills/claim-resource",
30
+ "./skills/release-resource"
29
31
  ]
30
32
  }
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: claim-resource
3
+ description: Claim an idle member of a Syntaur leases inventory (dev env, test DB, API key, named lock, etc.) for the current assignment session. Triggers on "/claim-resource", "claim a dev env", "lease an environment", "grab a test DB", "claim a slot", or when the user wants to coordinate access to a shared finite resource.
4
+ license: MIT
5
+ ---
6
+
7
+ # Claim Resource
8
+
9
+ Claim an idle member of a Syntaur leases inventory and persist the lease into `.syntaur/context.json`. The claimed member's connection metadata (URL, ssh string, port, etc.) is printed back so the agent can use it immediately.
10
+
11
+ The claim is **fail-fast** — if no member is idle, the command exits non-zero. If a long-running blocking claim is wanted, the agent retries externally with backoff.
12
+
13
+ ## Usage
14
+
15
+ User arguments: `$ARGUMENTS`
16
+
17
+ - `<inventory_slug>` (required) — which inventory to claim from
18
+ - `--ttl <duration>` (optional) — lease TTL, e.g. `15m`, `2h`. Defaults to the inventory's `default_ttl`.
19
+ - `--for <tag>` (optional) — free-form requester tag (defaults to the active assignment slug if omitted)
20
+
21
+ Examples:
22
+ - `/claim-resource dev-envs`
23
+ - `/claim-resource dev-envs --ttl 1h`
24
+ - `/claim-resource prod-migration-lock --ttl 5m --for "migration-2026-05-12"`
25
+
26
+ ## Workflow
27
+
28
+ ### Step 1: Parse arguments
29
+
30
+ Extract `<inventory_slug>` (required, first positional), plus optional `--ttl <duration>` and `--for <tag>`.
31
+
32
+ ### Step 2: Pre-check assignment context
33
+
34
+ Read `.syntaur/context.json` from the current working directory. If the file is missing, OR has neither `sessionId` nor any of `projectSlug`/`assignmentSlug`/`assignmentDir`, abort with:
35
+
36
+ > "No active Syntaur context in this workspace. Grab an assignment first (`/grab-assignment <project> <slug>`) or restart the session so the SessionStart hook can populate `.syntaur/context.json`."
37
+
38
+ This guard exists because writing a leases-only context file would trip `syntaur doctor`'s workspace check.
39
+
40
+ If `--for` was not provided, derive its value from the context: prefer `assignmentSlug` if present, otherwise `sessionId`.
41
+
42
+ ### Step 3: Run the claim CLI
43
+
44
+ ```bash
45
+ syntaur lease claim "$INVENTORY_SLUG" --json \
46
+ ${TTL:+--ttl "$TTL"} \
47
+ --for "$TAG"
48
+ ```
49
+
50
+ (Use `dangerouslyDisableSandbox: true` for the Bash call — the CLI writes to `~/.syntaur/syntaur.db`.)
51
+
52
+ Possible non-zero exits and how to surface them to the user:
53
+
54
+ | stderr fragment | meaning | how to respond |
55
+ |---|---|---|
56
+ | `no idle members in '<slug>'` | `NoIdleMemberError` — pool exhausted | Tell user; suggest waiting / retrying later or asking a human to add capacity. |
57
+ | `contention timeout on '<slug>'; retry` | `LeaseContentionError` — busy timeout | Retry once after ~1s. If it fails again, surface the error. |
58
+ | `inventory '<slug>' not found` | typo or inventory doesn't exist | Tell user; list with `syntaur lease list`. |
59
+
60
+ ### Step 4: Parse the claim result
61
+
62
+ Parse the stdout JSON: `{ lease_id, inventory_slug, member_id, member_gen, granted_at, expires_at, metadata }`.
63
+
64
+ ### Step 5: Persist the lease in `.syntaur/context.json`
65
+
66
+ Append the lease record to the `leases: []` array in `.syntaur/context.json` (creating the array if absent). Atomic write via `jq` + rename:
67
+
68
+ ```bash
69
+ mkdir -p .syntaur
70
+ jq --arg lid "$LEASE_ID" \
71
+ --arg slug "$INVENTORY_SLUG" \
72
+ --arg mid "$MEMBER_ID" \
73
+ --arg exp "$EXPIRES_AT" \
74
+ --arg claimed "$GRANTED_AT" \
75
+ --argjson meta "$METADATA_JSON" \
76
+ '.leases = ((.leases // []) | map(select(.lease_id != $lid)) +
77
+ [{ lease_id: $lid, inventory_slug: $slug, member_id: $mid,
78
+ expires_at: $exp, metadata: $meta, claimed_at: $claimed }])' \
79
+ .syntaur/context.json > .syntaur/context.json.tmp \
80
+ && mv .syntaur/context.json.tmp .syntaur/context.json
81
+ ```
82
+
83
+ ### Step 6: Report back
84
+
85
+ Tell the user:
86
+ - Which member was claimed (`<inventory>/<member_id>`) and the lease id.
87
+ - When the lease expires (`expires_at`).
88
+ - The connection metadata, if any (URL, port, credentials reference, etc.) — print as a code block so the agent (or human) can use it.
89
+ - Reminder: leases do NOT auto-release on session end or assignment completion in v1. Call `/release-resource <lease_id>` when finished, or let the TTL expire and `gc` reclaim.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: release-resource
3
+ description: Release a Syntaur resource lease previously claimed via /claim-resource, freeing the member for the next claimant. Triggers on "/release-resource", "release this dev env", "drop the lease", "release my slot", "release all leases", or when the user is done with a claimed resource.
4
+ license: MIT
5
+ ---
6
+
7
+ # Release Resource
8
+
9
+ Release one or more active resource leases, removing them from `.syntaur/context.json` and freeing their members for the next claimant.
10
+
11
+ ## Usage
12
+
13
+ User arguments: `$ARGUMENTS`
14
+
15
+ - `<lease_id>` — release a specific lease by id
16
+ - `--all` — release every active lease recorded in `.syntaur/context.json`
17
+
18
+ Examples:
19
+ - `/release-resource cf3bd0a6-ba38-4a8c-a882-bdba9cabad80`
20
+ - `/release-resource --all`
21
+
22
+ ## Workflow
23
+
24
+ ### Step 1: Parse arguments
25
+
26
+ If `--all` is passed, build the list of `lease_id`s from `.syntaur/context.json`'s `leases: []` array. Otherwise the first positional argument is the single `lease_id` to release.
27
+
28
+ If there is no `lease_id` and `--all` was not passed, abort with: "Specify `<lease_id>` or use `--all`. Active leases: $(jq -r '.leases[]?.lease_id' .syntaur/context.json 2>/dev/null || echo 'none')."
29
+
30
+ ### Step 2: Release each lease
31
+
32
+ For each `lease_id`:
33
+
34
+ ```bash
35
+ syntaur lease release "$LEASE_ID"
36
+ ```
37
+
38
+ (Use `dangerouslyDisableSandbox: true` since the CLI writes to `~/.syntaur/syntaur.db`.)
39
+
40
+ Outcomes:
41
+
42
+ | Result | Meaning | What to do |
43
+ |---|---|---|
44
+ | Exit 0 with `Released lease <id>.` | Normal release. | Continue. |
45
+ | Exit 1 with `lease ... is no longer active` | Stale lease (already expired/revoked). The slot has already been freed. | Still remove from `.syntaur/context.json` — the row is dead either way. Note the staleness in the final summary. |
46
+ | Exit 1 with `contention timeout; retry` | `LeaseContentionError`. | Retry once after ~1s; if it fails again, surface to the user. |
47
+
48
+ ### Step 3: Remove the entry from `.syntaur/context.json`
49
+
50
+ Whether the CLI succeeded OR returned a stale-lease error, drop the matching record from `.syntaur/context.json` (stale leases are dead — keeping them in context is misleading):
51
+
52
+ ```bash
53
+ jq --arg lid "$LEASE_ID" \
54
+ '.leases = ((.leases // []) | map(select(.lease_id != $lid)))' \
55
+ .syntaur/context.json > .syntaur/context.json.tmp \
56
+ && mv .syntaur/context.json.tmp .syntaur/context.json
57
+ ```
58
+
59
+ ### Step 4: Report back
60
+
61
+ For a single release: `Released <lease_id> (<inventory>/<member_id>).` or `Lease <id> was already stale; removed from context.`
62
+
63
+ For `--all`: print one line per lease, then a summary count: `Released N lease(s), M already stale.`
64
+
65
+ If no leases were recorded in context, say so explicitly.
@@ -60,6 +60,7 @@ If `.syntaur/context.json` exists in the current working directory, read it befo
60
60
  - `workspaceRoot` — absolute path to the code workspace
61
61
  - `sessionId` — real agent-runtime session id (never a synthesized UUID)
62
62
  - `transcriptPath` — absolute path to the agent's rollout/transcript file, if known
63
+ - `leases` — array of active resource-lease records (managed by `/claim-resource` and `/release-resource`). Entry shape: `{ lease_id, inventory_slug, member_id, expires_at, metadata, claimed_at }`. Leases are NOT auto-released on session end or assignment completion in v1 — call `/release-resource` explicitly (or let the TTL expire).
63
64
 
64
65
  ## Required Reading Order
65
66
 
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: claim-resource
3
+ description: Claim an idle member of a Syntaur leases inventory (dev env, test DB, API key, named lock, etc.) for the current assignment session. Triggers on "/claim-resource", "claim a dev env", "lease an environment", "grab a test DB", "claim a slot", or when the user wants to coordinate access to a shared finite resource.
4
+ license: MIT
5
+ ---
6
+
7
+ # Claim Resource
8
+
9
+ Claim an idle member of a Syntaur leases inventory and persist the lease into `.syntaur/context.json`. The claimed member's connection metadata (URL, ssh string, port, etc.) is printed back so the agent can use it immediately.
10
+
11
+ The claim is **fail-fast** — if no member is idle, the command exits non-zero. If a long-running blocking claim is wanted, the agent retries externally with backoff.
12
+
13
+ ## Usage
14
+
15
+ User arguments: `$ARGUMENTS`
16
+
17
+ - `<inventory_slug>` (required) — which inventory to claim from
18
+ - `--ttl <duration>` (optional) — lease TTL, e.g. `15m`, `2h`. Defaults to the inventory's `default_ttl`.
19
+ - `--for <tag>` (optional) — free-form requester tag (defaults to the active assignment slug if omitted)
20
+
21
+ Examples:
22
+ - `/claim-resource dev-envs`
23
+ - `/claim-resource dev-envs --ttl 1h`
24
+ - `/claim-resource prod-migration-lock --ttl 5m --for "migration-2026-05-12"`
25
+
26
+ ## Workflow
27
+
28
+ ### Step 1: Parse arguments
29
+
30
+ Extract `<inventory_slug>` (required, first positional), plus optional `--ttl <duration>` and `--for <tag>`.
31
+
32
+ ### Step 2: Pre-check assignment context
33
+
34
+ Read `.syntaur/context.json` from the current working directory. If the file is missing, OR has neither `sessionId` nor any of `projectSlug`/`assignmentSlug`/`assignmentDir`, abort with:
35
+
36
+ > "No active Syntaur context in this workspace. Grab an assignment first (`/grab-assignment <project> <slug>`) or restart the session so the SessionStart hook can populate `.syntaur/context.json`."
37
+
38
+ This guard exists because writing a leases-only context file would trip `syntaur doctor`'s workspace check.
39
+
40
+ If `--for` was not provided, derive its value from the context: prefer `assignmentSlug` if present, otherwise `sessionId`.
41
+
42
+ ### Step 3: Run the claim CLI
43
+
44
+ ```bash
45
+ syntaur lease claim "$INVENTORY_SLUG" --json \
46
+ ${TTL:+--ttl "$TTL"} \
47
+ --for "$TAG"
48
+ ```
49
+
50
+ (Use `dangerouslyDisableSandbox: true` for the Bash call — the CLI writes to `~/.syntaur/syntaur.db`.)
51
+
52
+ Possible non-zero exits and how to surface them to the user:
53
+
54
+ | stderr fragment | meaning | how to respond |
55
+ |---|---|---|
56
+ | `no idle members in '<slug>'` | `NoIdleMemberError` — pool exhausted | Tell user; suggest waiting / retrying later or asking a human to add capacity. |
57
+ | `contention timeout on '<slug>'; retry` | `LeaseContentionError` — busy timeout | Retry once after ~1s. If it fails again, surface the error. |
58
+ | `inventory '<slug>' not found` | typo or inventory doesn't exist | Tell user; list with `syntaur lease list`. |
59
+
60
+ ### Step 4: Parse the claim result
61
+
62
+ Parse the stdout JSON: `{ lease_id, inventory_slug, member_id, member_gen, granted_at, expires_at, metadata }`.
63
+
64
+ ### Step 5: Persist the lease in `.syntaur/context.json`
65
+
66
+ Append the lease record to the `leases: []` array in `.syntaur/context.json` (creating the array if absent). Atomic write via `jq` + rename:
67
+
68
+ ```bash
69
+ mkdir -p .syntaur
70
+ jq --arg lid "$LEASE_ID" \
71
+ --arg slug "$INVENTORY_SLUG" \
72
+ --arg mid "$MEMBER_ID" \
73
+ --arg exp "$EXPIRES_AT" \
74
+ --arg claimed "$GRANTED_AT" \
75
+ --argjson meta "$METADATA_JSON" \
76
+ '.leases = ((.leases // []) | map(select(.lease_id != $lid)) +
77
+ [{ lease_id: $lid, inventory_slug: $slug, member_id: $mid,
78
+ expires_at: $exp, metadata: $meta, claimed_at: $claimed }])' \
79
+ .syntaur/context.json > .syntaur/context.json.tmp \
80
+ && mv .syntaur/context.json.tmp .syntaur/context.json
81
+ ```
82
+
83
+ ### Step 6: Report back
84
+
85
+ Tell the user:
86
+ - Which member was claimed (`<inventory>/<member_id>`) and the lease id.
87
+ - When the lease expires (`expires_at`).
88
+ - The connection metadata, if any (URL, port, credentials reference, etc.) — print as a code block so the agent (or human) can use it.
89
+ - Reminder: leases do NOT auto-release on session end or assignment completion in v1. Call `/release-resource <lease_id>` when finished, or let the TTL expire and `gc` reclaim.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: release-resource
3
+ description: Release a Syntaur resource lease previously claimed via /claim-resource, freeing the member for the next claimant. Triggers on "/release-resource", "release this dev env", "drop the lease", "release my slot", "release all leases", or when the user is done with a claimed resource.
4
+ license: MIT
5
+ ---
6
+
7
+ # Release Resource
8
+
9
+ Release one or more active resource leases, removing them from `.syntaur/context.json` and freeing their members for the next claimant.
10
+
11
+ ## Usage
12
+
13
+ User arguments: `$ARGUMENTS`
14
+
15
+ - `<lease_id>` — release a specific lease by id
16
+ - `--all` — release every active lease recorded in `.syntaur/context.json`
17
+
18
+ Examples:
19
+ - `/release-resource cf3bd0a6-ba38-4a8c-a882-bdba9cabad80`
20
+ - `/release-resource --all`
21
+
22
+ ## Workflow
23
+
24
+ ### Step 1: Parse arguments
25
+
26
+ If `--all` is passed, build the list of `lease_id`s from `.syntaur/context.json`'s `leases: []` array. Otherwise the first positional argument is the single `lease_id` to release.
27
+
28
+ If there is no `lease_id` and `--all` was not passed, abort with: "Specify `<lease_id>` or use `--all`. Active leases: $(jq -r '.leases[]?.lease_id' .syntaur/context.json 2>/dev/null || echo 'none')."
29
+
30
+ ### Step 2: Release each lease
31
+
32
+ For each `lease_id`:
33
+
34
+ ```bash
35
+ syntaur lease release "$LEASE_ID"
36
+ ```
37
+
38
+ (Use `dangerouslyDisableSandbox: true` since the CLI writes to `~/.syntaur/syntaur.db`.)
39
+
40
+ Outcomes:
41
+
42
+ | Result | Meaning | What to do |
43
+ |---|---|---|
44
+ | Exit 0 with `Released lease <id>.` | Normal release. | Continue. |
45
+ | Exit 1 with `lease ... is no longer active` | Stale lease (already expired/revoked). The slot has already been freed. | Still remove from `.syntaur/context.json` — the row is dead either way. Note the staleness in the final summary. |
46
+ | Exit 1 with `contention timeout; retry` | `LeaseContentionError`. | Retry once after ~1s; if it fails again, surface to the user. |
47
+
48
+ ### Step 3: Remove the entry from `.syntaur/context.json`
49
+
50
+ Whether the CLI succeeded OR returned a stale-lease error, drop the matching record from `.syntaur/context.json` (stale leases are dead — keeping them in context is misleading):
51
+
52
+ ```bash
53
+ jq --arg lid "$LEASE_ID" \
54
+ '.leases = ((.leases // []) | map(select(.lease_id != $lid)))' \
55
+ .syntaur/context.json > .syntaur/context.json.tmp \
56
+ && mv .syntaur/context.json.tmp .syntaur/context.json
57
+ ```
58
+
59
+ ### Step 4: Report back
60
+
61
+ For a single release: `Released <lease_id> (<inventory>/<member_id>).` or `Lease <id> was already stale; removed from context.`
62
+
63
+ For `--all`: print one line per lease, then a summary count: `Released N lease(s), M already stale.`
64
+
65
+ If no leases were recorded in context, say so explicitly.
@@ -60,6 +60,7 @@ If `.syntaur/context.json` exists in the current working directory, read it befo
60
60
  - `workspaceRoot` — absolute path to the code workspace
61
61
  - `sessionId` — real agent-runtime session id (never a synthesized UUID)
62
62
  - `transcriptPath` — absolute path to the agent's rollout/transcript file, if known
63
+ - `leases` — array of active resource-lease records (managed by `/claim-resource` and `/release-resource`). Entry shape: `{ lease_id, inventory_slug, member_id, expires_at, metadata, claimed_at }`. Leases are NOT auto-released on session end or assignment completion in v1 — call `/release-resource` explicitly (or let the TTL expire).
63
64
 
64
65
  ## Required Reading Order
65
66
 
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: claim-resource
3
+ description: Claim an idle member of a Syntaur leases inventory (dev env, test DB, API key, named lock, etc.) for the current assignment session. Triggers on "/claim-resource", "claim a dev env", "lease an environment", "grab a test DB", "claim a slot", or when the user wants to coordinate access to a shared finite resource.
4
+ license: MIT
5
+ ---
6
+
7
+ # Claim Resource
8
+
9
+ Claim an idle member of a Syntaur leases inventory and persist the lease into `.syntaur/context.json`. The claimed member's connection metadata (URL, ssh string, port, etc.) is printed back so the agent can use it immediately.
10
+
11
+ The claim is **fail-fast** — if no member is idle, the command exits non-zero. If a long-running blocking claim is wanted, the agent retries externally with backoff.
12
+
13
+ ## Usage
14
+
15
+ User arguments: `$ARGUMENTS`
16
+
17
+ - `<inventory_slug>` (required) — which inventory to claim from
18
+ - `--ttl <duration>` (optional) — lease TTL, e.g. `15m`, `2h`. Defaults to the inventory's `default_ttl`.
19
+ - `--for <tag>` (optional) — free-form requester tag (defaults to the active assignment slug if omitted)
20
+
21
+ Examples:
22
+ - `/claim-resource dev-envs`
23
+ - `/claim-resource dev-envs --ttl 1h`
24
+ - `/claim-resource prod-migration-lock --ttl 5m --for "migration-2026-05-12"`
25
+
26
+ ## Workflow
27
+
28
+ ### Step 1: Parse arguments
29
+
30
+ Extract `<inventory_slug>` (required, first positional), plus optional `--ttl <duration>` and `--for <tag>`.
31
+
32
+ ### Step 2: Pre-check assignment context
33
+
34
+ Read `.syntaur/context.json` from the current working directory. If the file is missing, OR has neither `sessionId` nor any of `projectSlug`/`assignmentSlug`/`assignmentDir`, abort with:
35
+
36
+ > "No active Syntaur context in this workspace. Grab an assignment first (`/grab-assignment <project> <slug>`) or restart the session so the SessionStart hook can populate `.syntaur/context.json`."
37
+
38
+ This guard exists because writing a leases-only context file would trip `syntaur doctor`'s workspace check.
39
+
40
+ If `--for` was not provided, derive its value from the context: prefer `assignmentSlug` if present, otherwise `sessionId`.
41
+
42
+ ### Step 3: Run the claim CLI
43
+
44
+ ```bash
45
+ syntaur lease claim "$INVENTORY_SLUG" --json \
46
+ ${TTL:+--ttl "$TTL"} \
47
+ --for "$TAG"
48
+ ```
49
+
50
+ (Use `dangerouslyDisableSandbox: true` for the Bash call — the CLI writes to `~/.syntaur/syntaur.db`.)
51
+
52
+ Possible non-zero exits and how to surface them to the user:
53
+
54
+ | stderr fragment | meaning | how to respond |
55
+ |---|---|---|
56
+ | `no idle members in '<slug>'` | `NoIdleMemberError` — pool exhausted | Tell user; suggest waiting / retrying later or asking a human to add capacity. |
57
+ | `contention timeout on '<slug>'; retry` | `LeaseContentionError` — busy timeout | Retry once after ~1s. If it fails again, surface the error. |
58
+ | `inventory '<slug>' not found` | typo or inventory doesn't exist | Tell user; list with `syntaur lease list`. |
59
+
60
+ ### Step 4: Parse the claim result
61
+
62
+ Parse the stdout JSON: `{ lease_id, inventory_slug, member_id, member_gen, granted_at, expires_at, metadata }`.
63
+
64
+ ### Step 5: Persist the lease in `.syntaur/context.json`
65
+
66
+ Append the lease record to the `leases: []` array in `.syntaur/context.json` (creating the array if absent). Atomic write via `jq` + rename:
67
+
68
+ ```bash
69
+ mkdir -p .syntaur
70
+ jq --arg lid "$LEASE_ID" \
71
+ --arg slug "$INVENTORY_SLUG" \
72
+ --arg mid "$MEMBER_ID" \
73
+ --arg exp "$EXPIRES_AT" \
74
+ --arg claimed "$GRANTED_AT" \
75
+ --argjson meta "$METADATA_JSON" \
76
+ '.leases = ((.leases // []) | map(select(.lease_id != $lid)) +
77
+ [{ lease_id: $lid, inventory_slug: $slug, member_id: $mid,
78
+ expires_at: $exp, metadata: $meta, claimed_at: $claimed }])' \
79
+ .syntaur/context.json > .syntaur/context.json.tmp \
80
+ && mv .syntaur/context.json.tmp .syntaur/context.json
81
+ ```
82
+
83
+ ### Step 6: Report back
84
+
85
+ Tell the user:
86
+ - Which member was claimed (`<inventory>/<member_id>`) and the lease id.
87
+ - When the lease expires (`expires_at`).
88
+ - The connection metadata, if any (URL, port, credentials reference, etc.) — print as a code block so the agent (or human) can use it.
89
+ - Reminder: leases do NOT auto-release on session end or assignment completion in v1. Call `/release-resource <lease_id>` when finished, or let the TTL expire and `gc` reclaim.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: release-resource
3
+ description: Release a Syntaur resource lease previously claimed via /claim-resource, freeing the member for the next claimant. Triggers on "/release-resource", "release this dev env", "drop the lease", "release my slot", "release all leases", or when the user is done with a claimed resource.
4
+ license: MIT
5
+ ---
6
+
7
+ # Release Resource
8
+
9
+ Release one or more active resource leases, removing them from `.syntaur/context.json` and freeing their members for the next claimant.
10
+
11
+ ## Usage
12
+
13
+ User arguments: `$ARGUMENTS`
14
+
15
+ - `<lease_id>` — release a specific lease by id
16
+ - `--all` — release every active lease recorded in `.syntaur/context.json`
17
+
18
+ Examples:
19
+ - `/release-resource cf3bd0a6-ba38-4a8c-a882-bdba9cabad80`
20
+ - `/release-resource --all`
21
+
22
+ ## Workflow
23
+
24
+ ### Step 1: Parse arguments
25
+
26
+ If `--all` is passed, build the list of `lease_id`s from `.syntaur/context.json`'s `leases: []` array. Otherwise the first positional argument is the single `lease_id` to release.
27
+
28
+ If there is no `lease_id` and `--all` was not passed, abort with: "Specify `<lease_id>` or use `--all`. Active leases: $(jq -r '.leases[]?.lease_id' .syntaur/context.json 2>/dev/null || echo 'none')."
29
+
30
+ ### Step 2: Release each lease
31
+
32
+ For each `lease_id`:
33
+
34
+ ```bash
35
+ syntaur lease release "$LEASE_ID"
36
+ ```
37
+
38
+ (Use `dangerouslyDisableSandbox: true` since the CLI writes to `~/.syntaur/syntaur.db`.)
39
+
40
+ Outcomes:
41
+
42
+ | Result | Meaning | What to do |
43
+ |---|---|---|
44
+ | Exit 0 with `Released lease <id>.` | Normal release. | Continue. |
45
+ | Exit 1 with `lease ... is no longer active` | Stale lease (already expired/revoked). The slot has already been freed. | Still remove from `.syntaur/context.json` — the row is dead either way. Note the staleness in the final summary. |
46
+ | Exit 1 with `contention timeout; retry` | `LeaseContentionError`. | Retry once after ~1s; if it fails again, surface to the user. |
47
+
48
+ ### Step 3: Remove the entry from `.syntaur/context.json`
49
+
50
+ Whether the CLI succeeded OR returned a stale-lease error, drop the matching record from `.syntaur/context.json` (stale leases are dead — keeping them in context is misleading):
51
+
52
+ ```bash
53
+ jq --arg lid "$LEASE_ID" \
54
+ '.leases = ((.leases // []) | map(select(.lease_id != $lid)))' \
55
+ .syntaur/context.json > .syntaur/context.json.tmp \
56
+ && mv .syntaur/context.json.tmp .syntaur/context.json
57
+ ```
58
+
59
+ ### Step 4: Report back
60
+
61
+ For a single release: `Released <lease_id> (<inventory>/<member_id>).` or `Lease <id> was already stale; removed from context.`
62
+
63
+ For `--all`: print one line per lease, then a summary count: `Released N lease(s), M already stale.`
64
+
65
+ If no leases were recorded in context, say so explicitly.
@@ -60,6 +60,7 @@ If `.syntaur/context.json` exists in the current working directory, read it befo
60
60
  - `workspaceRoot` — absolute path to the code workspace
61
61
  - `sessionId` — real agent-runtime session id (never a synthesized UUID)
62
62
  - `transcriptPath` — absolute path to the agent's rollout/transcript file, if known
63
+ - `leases` — array of active resource-lease records (managed by `/claim-resource` and `/release-resource`). Entry shape: `{ lease_id, inventory_slug, member_id, expires_at, metadata, claimed_at }`. Leases are NOT auto-released on session end or assignment completion in v1 — call `/release-resource` explicitly (or let the TTL expire).
63
64
 
64
65
  ## Required Reading Order
65
66
 
@@ -1 +0,0 @@
1
- import{aq as o,ar as n}from"./mermaid.core-dkpoLY2K.js";const t=(r,a)=>o.lang.round(n.parse(r)[a]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-WL4C6EOR-XtMf_tFy.js";import{_ as i}from"./mermaid.core-dkpoLY2K.js";import"./chunk-FMBD7UC4---Go123J.js";import"./chunk-JSJVCQXG-C9E63Ryg.js";import"./chunk-55IACEB6-C6L5bDoe.js";import"./chunk-KX2RTZJC-Yyi0h479.js";import"./index-B9uCPhOL.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-WL4C6EOR-XtMf_tFy.js";import{_ as i}from"./mermaid.core-dkpoLY2K.js";import"./chunk-FMBD7UC4---Go123J.js";import"./chunk-JSJVCQXG-C9E63Ryg.js";import"./chunk-55IACEB6-C6L5bDoe.js";import"./chunk-KX2RTZJC-Yyi0h479.js";import"./index-B9uCPhOL.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{b as r}from"./_baseUniq-CwicZZgc.js";var e=4;function a(o){return r(o,e)}export{a as c};