syntaur 0.3.0 → 0.4.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.
- package/README.md +26 -3
- package/dist/dashboard/server.js +497 -315
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +1240 -550
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
- package/platforms/claude-code/agents/syntaur-expert.md +35 -20
- package/platforms/claude-code/commands/complete-assignment/complete-assignment.md +20 -0
- package/platforms/claude-code/commands/create-assignment/create-assignment.md +20 -0
- package/platforms/claude-code/commands/create-project/create-project.md +20 -0
- package/platforms/claude-code/commands/grab-assignment/grab-assignment.md +20 -0
- package/platforms/claude-code/commands/plan-assignment/plan-assignment.md +20 -0
- package/platforms/claude-code/commands/track-session/track-session.md +43 -18
- package/platforms/claude-code/hooks/hooks.json +11 -0
- package/platforms/claude-code/hooks/session-cleanup.sh +13 -23
- package/platforms/claude-code/hooks/session-start.sh +80 -0
- package/platforms/codex/.codex-plugin/plugin.json +1 -1
- package/platforms/codex/agents/syntaur-operator.md +6 -4
- package/platforms/codex/scripts/resolve-session.sh +49 -0
- package/statusline/statusline.sh +133 -0
- package/vendor/syntaur-skills/LICENSE +21 -0
- package/vendor/syntaur-skills/README.md +43 -0
- package/vendor/syntaur-skills/skills/complete-assignment/SKILL.md +146 -0
- package/vendor/syntaur-skills/skills/create-assignment/SKILL.md +72 -0
- package/vendor/syntaur-skills/skills/create-project/SKILL.md +56 -0
- package/vendor/syntaur-skills/skills/grab-assignment/SKILL.md +158 -0
- package/vendor/syntaur-skills/skills/plan-assignment/SKILL.md +137 -0
- package/vendor/syntaur-skills/skills/syntaur-protocol/SKILL.md +119 -0
- package/vendor/syntaur-skills/skills/syntaur-protocol/references/file-ownership.md +67 -0
- package/vendor/syntaur-skills/skills/syntaur-protocol/references/protocol-summary.md +82 -0
- package/platforms/claude-code/skills/complete-assignment/SKILL.md +0 -155
- package/platforms/claude-code/skills/create-assignment/SKILL.md +0 -67
- package/platforms/claude-code/skills/grab-assignment/SKILL.md +0 -187
- package/platforms/claude-code/skills/plan-assignment/SKILL.md +0 -148
- package/platforms/claude-code/skills/syntaur-protocol/SKILL.md +0 -86
- package/platforms/codex/skills/complete-assignment/SKILL.md +0 -64
- package/platforms/codex/skills/create-assignment/SKILL.md +0 -49
- package/platforms/codex/skills/grab-assignment/SKILL.md +0 -71
- package/platforms/codex/skills/plan-assignment/SKILL.md +0 -57
- package/platforms/codex/skills/syntaur-protocol/SKILL.md +0 -102
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "syntaur",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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": {
|
|
@@ -29,7 +29,11 @@
|
|
|
29
29
|
".agents",
|
|
30
30
|
"platforms",
|
|
31
31
|
"examples",
|
|
32
|
-
"dashboard/dist"
|
|
32
|
+
"dashboard/dist",
|
|
33
|
+
"statusline",
|
|
34
|
+
"vendor/syntaur-skills/skills/**",
|
|
35
|
+
"vendor/syntaur-skills/LICENSE",
|
|
36
|
+
"vendor/syntaur-skills/README.md"
|
|
33
37
|
],
|
|
34
38
|
"scripts": {
|
|
35
39
|
"build": "tsup",
|
|
@@ -40,7 +44,9 @@
|
|
|
40
44
|
"typecheck": "tsc --noEmit",
|
|
41
45
|
"test": "vitest run",
|
|
42
46
|
"test:watch": "vitest",
|
|
47
|
+
"prepack": "node scripts/verify-vendored-skills.mjs",
|
|
43
48
|
"prepublishOnly": "npm run build && npm ci --prefix dashboard && npm run build --prefix dashboard",
|
|
49
|
+
"postinstall": "node scripts/postinstall-submodules.mjs",
|
|
44
50
|
"try": "node scripts/try.mjs",
|
|
45
51
|
"untry": "npm unlink -g syntaur && npm install -g syntaur@latest && echo '\\n✓ global syntaur restored to latest published version'"
|
|
46
52
|
},
|
|
@@ -12,10 +12,12 @@ When answering questions, read the actual source files rather than relying solel
|
|
|
12
12
|
|
|
13
13
|
## Key Source Files
|
|
14
14
|
|
|
15
|
-
- **Protocol summary:** `${CLAUDE_PLUGIN_ROOT}/references/protocol-summary.md`
|
|
15
|
+
- **Protocol summary:** `${CLAUDE_PLUGIN_ROOT}/references/protocol-summary.md` (or `~/.claude/skills/syntaur-protocol/references/protocol-summary.md` for the installed skill version)
|
|
16
16
|
- **File ownership:** `${CLAUDE_PLUGIN_ROOT}/references/file-ownership.md`
|
|
17
17
|
- **Plugin manifest:** `${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json`
|
|
18
|
-
- **
|
|
18
|
+
- **Protocol skills (installed by `syntaur install-plugin`):** `~/.claude/skills/{syntaur-protocol,grab-assignment,plan-assignment,complete-assignment,create-assignment,create-project}/`
|
|
19
|
+
- **Protocol skills source (vendored via submodule):** `<syntaur-repo>/vendor/syntaur-skills/skills/` — standalone repo at https://github.com/prong-horn/syntaur-skills
|
|
20
|
+
- **Slash commands (ship in plugin):** `${CLAUDE_PLUGIN_ROOT}/commands/` — thin wrappers that invoke the corresponding installed skill
|
|
19
21
|
- **Hooks:** `${CLAUDE_PLUGIN_ROOT}/hooks/`
|
|
20
22
|
|
|
21
23
|
For the live CLI surface, run `syntaur --help` in the user environment.
|
|
@@ -191,7 +193,7 @@ Only the assigned agent may write to its own assignment folder.
|
|
|
191
193
|
### Session Tracking
|
|
192
194
|
| Command | Description |
|
|
193
195
|
|---------|-------------|
|
|
194
|
-
| `syntaur track-session --project M --assignment A --agent N
|
|
196
|
+
| `syntaur track-session --project M --assignment A --agent N --session-id <real-id> --transcript-path <path>` | Register agent session. `--session-id` is required and must be the agent runtime's real id (Claude: `~/.claude/sessions/<pid>.json` or SessionStart hook payload; Codex: `payload.id` from `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`). Do not synthesize. |
|
|
195
197
|
|
|
196
198
|
All commands support `--dir <path>` to override the default `~/.syntaur/projects/` directory.
|
|
197
199
|
|
|
@@ -204,27 +206,38 @@ The Syntaur Claude Code plugin is installed by `syntaur install-plugin`, which r
|
|
|
204
206
|
```
|
|
205
207
|
plugin/
|
|
206
208
|
.claude-plugin/
|
|
207
|
-
plugin.json
|
|
209
|
+
plugin.json # Plugin metadata
|
|
208
210
|
agents/
|
|
209
|
-
syntaur-expert.md
|
|
210
|
-
skills/
|
|
211
|
-
syntaur-protocol/SKILL.md # Core protocol rules (background)
|
|
212
|
-
grab-assignment/SKILL.md # Claim a pending assignment
|
|
213
|
-
create-project/SKILL.md # Create new project
|
|
214
|
-
create-assignment/SKILL.md # Create new assignment
|
|
215
|
-
plan-assignment/SKILL.md # Write implementation plan
|
|
216
|
-
complete-assignment/SKILL.md # Handoff and complete
|
|
211
|
+
syntaur-expert.md # This agent
|
|
217
212
|
commands/
|
|
218
|
-
|
|
213
|
+
grab-assignment/grab-assignment.md # Slash wrapper for grab-assignment skill
|
|
214
|
+
plan-assignment/plan-assignment.md # Slash wrapper for plan-assignment skill
|
|
215
|
+
complete-assignment/complete-assignment.md # Slash wrapper for complete-assignment skill
|
|
216
|
+
create-assignment/create-assignment.md # Slash wrapper for create-assignment skill
|
|
217
|
+
create-project/create-project.md # Slash wrapper for create-project skill
|
|
218
|
+
track-session/track-session.md # Claude-specific session registration
|
|
219
|
+
doctor-syntaur/... # Diagnose install
|
|
220
|
+
track-server/... # Register a running server
|
|
219
221
|
hooks/
|
|
220
|
-
hooks.json
|
|
221
|
-
session-
|
|
222
|
-
|
|
222
|
+
hooks.json # Hook definitions
|
|
223
|
+
session-start.sh # Merge real session_id + transcript_path into existing .syntaur/context.json
|
|
224
|
+
session-cleanup.sh # Mark sessions stopped on exit
|
|
225
|
+
enforce-boundaries.sh # Write boundary enforcement
|
|
223
226
|
references/
|
|
224
|
-
protocol-summary.md
|
|
225
|
-
file-ownership.md
|
|
227
|
+
protocol-summary.md # One-page protocol quick reference
|
|
228
|
+
file-ownership.md # Write boundary rules
|
|
229
|
+
|
|
230
|
+
~/.claude/skills/ # Installed by `syntaur install-plugin` (vendored from syntaur-skills repo)
|
|
231
|
+
syntaur-protocol/SKILL.md # Auto-activates on Syntaur file contexts
|
|
232
|
+
grab-assignment/SKILL.md
|
|
233
|
+
plan-assignment/SKILL.md
|
|
234
|
+
complete-assignment/SKILL.md
|
|
235
|
+
create-assignment/SKILL.md
|
|
236
|
+
create-project/SKILL.md
|
|
226
237
|
```
|
|
227
238
|
|
|
239
|
+
Slash commands (`/grab-assignment` etc.) are thin wrappers that delegate to the installed skills. This lets the same protocol skills work in Claude Code (via slash command + auto-activation) and Codex (via auto-activation only).
|
|
240
|
+
|
|
228
241
|
### Skills Summary
|
|
229
242
|
|
|
230
243
|
| Skill | Trigger | Purpose |
|
|
@@ -241,6 +254,7 @@ plugin/
|
|
|
241
254
|
| Hook | Event | Behavior |
|
|
242
255
|
|------|-------|----------|
|
|
243
256
|
| PostToolUse: ExitPlanMode | User exits plan mode | Prompts to write the plan to the next unused `plan-v<N>.md` (or `plan.md` if none exists) and append a linked todo in the `## Todos` section of `assignment.md` |
|
|
257
|
+
| SessionStart | Claude Code session starts | Runs session-start.sh to merge the real `session_id` + `transcript_path` into an EXISTING `.syntaur/context.json`. Does nothing if context.json is absent (no active assignment). |
|
|
244
258
|
| SessionEnd | Claude Code session exits | Runs session-cleanup.sh to mark session as stopped |
|
|
245
259
|
| PreToolUse: enforce-boundaries | Edit/Write/MultiEdit | Validates target path is within assignment boundaries |
|
|
246
260
|
|
|
@@ -370,7 +384,7 @@ syntaur dashboard
|
|
|
370
384
|
|
|
371
385
|
## Context File (.syntaur/context.json)
|
|
372
386
|
|
|
373
|
-
Created by `/grab-assignment` in the current working directory.
|
|
387
|
+
Created by `/grab-assignment` in the current working directory. The SessionStart hook merges `sessionId` / `transcriptPath` into this file on each Claude Code session start — it never creates the file, only enriches an existing one. Contents:
|
|
374
388
|
```json
|
|
375
389
|
{
|
|
376
390
|
"projectSlug": "my-first-project",
|
|
@@ -381,7 +395,8 @@ Created by `/grab-assignment` in the current working directory. Contains:
|
|
|
381
395
|
"title": "Design the schema",
|
|
382
396
|
"branch": "feature/design-the-schema",
|
|
383
397
|
"grabbedAt": "2026-03-18T14:30:00Z",
|
|
384
|
-
"sessionId": "
|
|
398
|
+
"sessionId": "<real-claude-session-id>",
|
|
399
|
+
"transcriptPath": "/Users/you/.claude/projects/<encoded-cwd>/<session-id>.jsonl"
|
|
385
400
|
}
|
|
386
401
|
```
|
|
387
402
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: complete-assignment
|
|
3
|
+
description: Append a progress entry + handoff and transition the current Syntaur assignment to review or completed
|
|
4
|
+
arguments:
|
|
5
|
+
- name: args
|
|
6
|
+
description: "Optional — see the complete-assignment skill for supported flags"
|
|
7
|
+
required: false
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# /complete-assignment
|
|
11
|
+
|
|
12
|
+
Thin wrapper that invokes the `complete-assignment` skill. The skill lives in `~/.claude/skills/complete-assignment/` (installed by `syntaur setup` / `syntaur install-plugin`) and contains the full protocol — verifying acceptance criteria and todos, appending a progress.md entry, writing a handoff.md section, and calling `syntaur review` or `syntaur complete`.
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
Invoke the `complete-assignment` skill via the Skill tool, passing the user's arguments. The skill handles everything else.
|
|
17
|
+
|
|
18
|
+
Arguments: $ARGUMENTS
|
|
19
|
+
|
|
20
|
+
If the skill is not installed, tell the user to run `syntaur install-plugin` (or `syntaur setup` if they haven't set up yet).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-assignment
|
|
3
|
+
description: Create a new Syntaur assignment (project-nested or standalone one-off)
|
|
4
|
+
arguments:
|
|
5
|
+
- name: args
|
|
6
|
+
description: "Title and flags. See the create-assignment skill for supported forms (e.g. --project <slug>, --one-off, --type <type>)."
|
|
7
|
+
required: false
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# /create-assignment
|
|
11
|
+
|
|
12
|
+
Thin wrapper that invokes the `create-assignment` skill. The skill lives in `~/.claude/skills/create-assignment/` (installed by `syntaur setup` / `syntaur install-plugin`) and contains the full protocol — picking project-nested or standalone, validating the type, scaffolding assignment.md / progress.md / comments.md.
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
Invoke the `create-assignment` skill via the Skill tool, passing the user's arguments. The skill handles everything else.
|
|
17
|
+
|
|
18
|
+
Arguments: $ARGUMENTS
|
|
19
|
+
|
|
20
|
+
If the skill is not installed, tell the user to run `syntaur install-plugin` (or `syntaur setup` if they haven't set up yet).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-project
|
|
3
|
+
description: Create a new Syntaur project with full scaffolding
|
|
4
|
+
arguments:
|
|
5
|
+
- name: args
|
|
6
|
+
description: "Title and optional flags (--slug, --dir, --workspace). See the create-project skill for full usage."
|
|
7
|
+
required: false
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# /create-project
|
|
11
|
+
|
|
12
|
+
Thin wrapper that invokes the `create-project` skill. The skill lives in `~/.claude/skills/create-project/` (installed by `syntaur setup` / `syntaur install-plugin`) and contains the full protocol — calling `syntaur create-project`, reading the generated project.md, and guiding next steps.
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
Invoke the `create-project` skill via the Skill tool, passing the user's arguments. The skill handles everything else.
|
|
17
|
+
|
|
18
|
+
Arguments: $ARGUMENTS
|
|
19
|
+
|
|
20
|
+
If the skill is not installed, tell the user to run `syntaur install-plugin` (or `syntaur setup` if they haven't set up yet).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: grab-assignment
|
|
3
|
+
description: Claim a Syntaur assignment and load it into the current working context
|
|
4
|
+
arguments:
|
|
5
|
+
- name: args
|
|
6
|
+
description: "Project slug and optional assignment slug, or --id <uuid> for standalone. See the grab-assignment skill for full forms."
|
|
7
|
+
required: false
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# /grab-assignment
|
|
11
|
+
|
|
12
|
+
Thin wrapper that invokes the `grab-assignment` skill. The skill lives in `~/.claude/skills/grab-assignment/` (installed by `syntaur setup` / `syntaur install-plugin`) and contains the full protocol — discovering pending assignments, merging `.syntaur/context.json`, registering the agent session, reading the assignment.
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
Invoke the `grab-assignment` skill via the Skill tool, passing the user's arguments. The skill handles everything else.
|
|
17
|
+
|
|
18
|
+
Arguments: $ARGUMENTS
|
|
19
|
+
|
|
20
|
+
If the skill is not installed, tell the user to run `syntaur install-plugin` (or `syntaur setup` if they haven't set up yet).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan-assignment
|
|
3
|
+
description: Create a detailed implementation plan for the current Syntaur assignment
|
|
4
|
+
arguments:
|
|
5
|
+
- name: args
|
|
6
|
+
description: "Optional — see the plan-assignment skill for supported flags"
|
|
7
|
+
required: false
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# /plan-assignment
|
|
11
|
+
|
|
12
|
+
Thin wrapper that invokes the `plan-assignment` skill. The skill lives in `~/.claude/skills/plan-assignment/` (installed by `syntaur setup` / `syntaur install-plugin`) and contains the full protocol — picking the next `plan-v<N>.md`, writing it, appending a `## Todos` entry, marking any prior plan todo superseded, and recording key decisions in `decision-record.md`.
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
Invoke the `plan-assignment` skill via the Skill tool, passing the user's arguments. The skill handles everything else.
|
|
17
|
+
|
|
18
|
+
Arguments: $ARGUMENTS
|
|
19
|
+
|
|
20
|
+
If the skill is not installed, tell the user to run `syntaur install-plugin` (or `syntaur setup` if they haven't set up yet).
|
|
@@ -11,6 +11,8 @@ arguments:
|
|
|
11
11
|
|
|
12
12
|
Register the current Claude Code session as an agent session in the Syntaur dashboard. Works standalone or linked to a project/assignment.
|
|
13
13
|
|
|
14
|
+
Only real Claude Code session IDs are accepted — no synthesis. The real id is written to `.syntaur/context.json` by the SessionStart hook, with `~/.claude/sessions/<pid>.json` as the fallback source.
|
|
15
|
+
|
|
14
16
|
## Usage
|
|
15
17
|
|
|
16
18
|
- `/track-session` — register a standalone session
|
|
@@ -29,37 +31,60 @@ Extract optional flags from the argument string:
|
|
|
29
31
|
- `--project <slug>` — project to link to
|
|
30
32
|
- `--assignment <slug>` — assignment to link to
|
|
31
33
|
|
|
32
|
-
### Step 2:
|
|
34
|
+
### Step 2: Source the real session id + transcript path
|
|
35
|
+
|
|
36
|
+
In priority order:
|
|
37
|
+
|
|
38
|
+
1. Read `.syntaur/context.json` if present. If it contains `sessionId`, use it. Also pick up `transcriptPath` if present.
|
|
39
|
+
2. Otherwise, read the most-recently-modified file under `~/.claude/sessions/*.json` whose `cwd` matches `$(pwd)` and use its `sessionId` field. The transcript path is conventionally `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl`; include it if the file exists, otherwise omit.
|
|
40
|
+
3. If neither source yields an id, abort with: "Could not resolve a real Claude Code session id. Restart the Claude session so the SessionStart hook can populate `.syntaur/context.json`, or run `/rename <slug>` then try again."
|
|
41
|
+
|
|
42
|
+
DO NOT generate a UUID. `syntaur track-session` rejects missing session IDs.
|
|
33
43
|
|
|
34
|
-
Run the
|
|
44
|
+
### Step 3: Run the CLI command
|
|
45
|
+
|
|
46
|
+
Run the track-session CLI via Bash (use `dangerouslyDisableSandbox: true` since it writes to `~/.syntaur/`):
|
|
35
47
|
|
|
36
48
|
```bash
|
|
37
|
-
syntaur track-session
|
|
49
|
+
syntaur track-session \
|
|
50
|
+
--agent claude \
|
|
51
|
+
--session-id "$SESSION_ID" \
|
|
52
|
+
--transcript-path "$TRANSCRIPT_PATH" \
|
|
53
|
+
--path "$(pwd)" \
|
|
54
|
+
[--description "<text>"] \
|
|
55
|
+
[--project <slug>] \
|
|
56
|
+
[--assignment <slug>]
|
|
38
57
|
```
|
|
39
58
|
|
|
40
|
-
|
|
59
|
+
Omit `--transcript-path` entirely (don't pass an empty string) if no transcript path could be resolved.
|
|
41
60
|
|
|
42
|
-
The CLI
|
|
61
|
+
The CLI prints one of:
|
|
43
62
|
- `Registered standalone agent session <sessionId>.`
|
|
44
63
|
- `Registered agent session <sessionId> for <assignment> in <project>.`
|
|
45
64
|
|
|
46
|
-
|
|
65
|
+
Registration is idempotent — re-running the command with the same session id safely upserts project/assignment/description onto the existing row.
|
|
47
66
|
|
|
48
|
-
### Step 4:
|
|
67
|
+
### Step 4: Merge context.json
|
|
49
68
|
|
|
50
|
-
|
|
69
|
+
Ensure `.syntaur/context.json` has the session fields (so SessionEnd and future `/track-session` runs find them). Merge, don't overwrite:
|
|
51
70
|
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
```bash
|
|
72
|
+
mkdir -p .syntaur
|
|
73
|
+
if [ -f .syntaur/context.json ]; then
|
|
74
|
+
jq --arg sid "$SESSION_ID" --arg tp "$TRANSCRIPT_PATH" \
|
|
75
|
+
'. + {sessionId: $sid} + (if ($tp | length) > 0 then {transcriptPath: $tp} else {} end)' \
|
|
76
|
+
.syntaur/context.json > .syntaur/context.json.tmp \
|
|
77
|
+
&& mv .syntaur/context.json.tmp .syntaur/context.json
|
|
78
|
+
else
|
|
79
|
+
jq -n --arg sid "$SESSION_ID" --arg tp "$TRANSCRIPT_PATH" \
|
|
80
|
+
'{sessionId: $sid} + (if ($tp | length) > 0 then {transcriptPath: $tp} else {} end)' \
|
|
81
|
+
> .syntaur/context.json
|
|
82
|
+
fi
|
|
83
|
+
```
|
|
59
84
|
|
|
60
85
|
### Step 5: Confirm
|
|
61
86
|
|
|
62
87
|
Tell the user:
|
|
63
|
-
- The session was registered (include the short session
|
|
64
|
-
- It will be auto-stopped when this conversation ends
|
|
65
|
-
- If linked to a project, mention which project/assignment
|
|
88
|
+
- The session was registered (include the short session id).
|
|
89
|
+
- It will be auto-stopped when this conversation ends via the SessionEnd hook.
|
|
90
|
+
- If linked to a project, mention which project/assignment.
|
|
@@ -34,39 +34,29 @@ SESSION_ID=$(jq -r '.sessionId // empty' "$CONTEXT_FILE" 2>/dev/null)
|
|
|
34
34
|
MISSION_SLUG=$(jq -r '.projectSlug // empty' "$CONTEXT_FILE" 2>/dev/null)
|
|
35
35
|
ASSIGNMENT_SLUG=$(jq -r '.assignmentSlug // empty' "$CONTEXT_FILE" 2>/dev/null)
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# --- Step 5: If no session was registered, try to auto-register (requires project+assignment) ---
|
|
37
|
+
# Fall back to the SessionEnd stdin payload if context.json didn't have the id.
|
|
38
|
+
# Claude Code passes session_id on stdin for SessionEnd.
|
|
40
39
|
if [ -z "$SESSION_ID" ]; then
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
exit 0
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
# Generate a session ID for the log entry
|
|
47
|
-
SESSION_ID=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "ses-$(date +%s)")
|
|
48
|
-
# Lowercase the UUID (uuidgen on macOS outputs uppercase)
|
|
49
|
-
SESSION_ID=$(echo "$SESSION_ID" | tr '[:upper:]' '[:lower:]')
|
|
40
|
+
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
|
|
41
|
+
fi
|
|
50
42
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
-d "{\"projectSlug\": \"${MISSION_SLUG}\", \"assignmentSlug\": \"${ASSIGNMENT_SLUG}\", \"agent\": \"claude\", \"sessionId\": \"${SESSION_ID}\", \"path\": \"${CWD}\"}" \
|
|
54
|
-
2>/dev/null) || true
|
|
43
|
+
# No real session id available — exit quietly. We never synthesize one.
|
|
44
|
+
[ -z "$SESSION_ID" ] && exit 0
|
|
55
45
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
46
|
+
# --- Dashboard endpoint resolution (mirror session-start.sh exactly so start
|
|
47
|
+
# and end hooks always target the same host:port) ---
|
|
48
|
+
PORT="${SYNTAUR_DASHBOARD_PORT:-}"
|
|
49
|
+
if [ -z "$PORT" ]; then
|
|
50
|
+
PORT=$(cat "$HOME/.syntaur/dashboard-port" 2>/dev/null || echo "4800")
|
|
61
51
|
fi
|
|
62
52
|
|
|
63
|
-
# --- Step
|
|
53
|
+
# --- Step 5: Mark session as stopped via dashboard API ---
|
|
64
54
|
BODY="{\"status\": \"stopped\"}"
|
|
65
55
|
if [ -n "$MISSION_SLUG" ]; then
|
|
66
56
|
BODY="{\"status\": \"stopped\", \"projectSlug\": \"${MISSION_SLUG}\"}"
|
|
67
57
|
fi
|
|
68
58
|
|
|
69
|
-
curl -sf -X PATCH "http://
|
|
59
|
+
curl -sf --max-time 3 -X PATCH "http://127.0.0.1:${PORT}/api/agent-sessions/${SESSION_ID}/status" \
|
|
70
60
|
-H "Content-Type: application/json" \
|
|
71
61
|
-d "$BODY" \
|
|
72
62
|
-o /dev/null 2>/dev/null || true
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Syntaur SessionStart Hook
|
|
3
|
+
# (1) Merges the real Claude Code session_id + transcript_path into an
|
|
4
|
+
# EXISTING .syntaur/context.json. Never creates context.json — that would
|
|
5
|
+
# break grab-assignment's "context.json implies active assignment" semantic.
|
|
6
|
+
# (2) Pre-registers a minimal row in the dashboard sessions table so
|
|
7
|
+
# SessionEnd's PATCH /status always has a row to target. Best-effort —
|
|
8
|
+
# silently ignores dashboard-unreachable.
|
|
9
|
+
#
|
|
10
|
+
# Reads JSON from stdin per Claude Code SessionStart contract:
|
|
11
|
+
# { "session_id": "...", "transcript_path": "...", "cwd": "...", ... }
|
|
12
|
+
#
|
|
13
|
+
# Always exits 0.
|
|
14
|
+
|
|
15
|
+
set -o pipefail 2>/dev/null || true
|
|
16
|
+
|
|
17
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
18
|
+
|
|
19
|
+
INPUT=$(cat)
|
|
20
|
+
[ -z "$INPUT" ] && exit 0
|
|
21
|
+
|
|
22
|
+
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
|
|
23
|
+
TRANSCRIPT_PATH=$(printf '%s' "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
24
|
+
CWD=$(printf '%s' "$INPUT" | jq -r '.cwd // empty' 2>/dev/null)
|
|
25
|
+
|
|
26
|
+
[ -z "$SESSION_ID" ] && exit 0
|
|
27
|
+
[ -z "$CWD" ] && exit 0
|
|
28
|
+
|
|
29
|
+
CONTEXT_FILE="$CWD/.syntaur/context.json"
|
|
30
|
+
|
|
31
|
+
# REQUIRED invariant: only operate on an EXISTING context file. If the current
|
|
32
|
+
# cwd has no active Syntaur assignment, leave the filesystem untouched.
|
|
33
|
+
[ ! -f "$CONTEXT_FILE" ] && exit 0
|
|
34
|
+
|
|
35
|
+
# --- (1) Merge session fields into context.json.
|
|
36
|
+
# Always replace both sessionId and transcriptPath together. If the incoming
|
|
37
|
+
# transcript_path is empty, explicitly null the stored transcriptPath so a new
|
|
38
|
+
# session never inherits a stale transcript path from the prior session.
|
|
39
|
+
TMP="${CONTEXT_FILE}.tmp.$$"
|
|
40
|
+
jq \
|
|
41
|
+
--arg sid "$SESSION_ID" \
|
|
42
|
+
--arg tp "$TRANSCRIPT_PATH" \
|
|
43
|
+
'. + {sessionId: $sid, transcriptPath: (if ($tp | length) > 0 then $tp else null end)}' \
|
|
44
|
+
"$CONTEXT_FILE" > "$TMP" 2>/dev/null \
|
|
45
|
+
&& mv "$TMP" "$CONTEXT_FILE" 2>/dev/null \
|
|
46
|
+
|| rm -f "$TMP"
|
|
47
|
+
|
|
48
|
+
# --- (2) Best-effort pre-registration in the dashboard.
|
|
49
|
+
# Read project/assignment context if present so the pre-registered row is
|
|
50
|
+
# already linked. Upsert semantics on the server mean this is idempotent with
|
|
51
|
+
# later /track-session or grab-assignment calls.
|
|
52
|
+
MISSION_SLUG=$(jq -r '.projectSlug // empty' "$CONTEXT_FILE" 2>/dev/null)
|
|
53
|
+
ASSIGNMENT_SLUG=$(jq -r '.assignmentSlug // empty' "$CONTEXT_FILE" 2>/dev/null)
|
|
54
|
+
|
|
55
|
+
PORT="${SYNTAUR_DASHBOARD_PORT:-}"
|
|
56
|
+
if [ -z "$PORT" ]; then
|
|
57
|
+
PORT=$(cat "$HOME/.syntaur/dashboard-port" 2>/dev/null || echo "4800")
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
BODY=$(jq -cn \
|
|
61
|
+
--arg sid "$SESSION_ID" \
|
|
62
|
+
--arg tp "$TRANSCRIPT_PATH" \
|
|
63
|
+
--arg proj "$MISSION_SLUG" \
|
|
64
|
+
--arg assn "$ASSIGNMENT_SLUG" \
|
|
65
|
+
--arg path "$CWD" \
|
|
66
|
+
'{ agent: "claude", sessionId: $sid, path: $path }
|
|
67
|
+
+ (if ($tp | length) > 0 then {transcriptPath: $tp} else {} end)
|
|
68
|
+
+ (if ($proj | length) > 0 then {projectSlug: $proj} else {} end)
|
|
69
|
+
+ (if ($assn | length) > 0 then {assignmentSlug: $assn} else {} end)' 2>/dev/null)
|
|
70
|
+
|
|
71
|
+
if [ -n "$BODY" ]; then
|
|
72
|
+
# --max-time bounds the hook's wall-clock cost if the dashboard socket
|
|
73
|
+
# accepts but then hangs. The hook itself is registered with timeout: 5.
|
|
74
|
+
curl -sf --max-time 3 -X POST "http://127.0.0.1:${PORT}/api/agent-sessions" \
|
|
75
|
+
-H "Content-Type: application/json" \
|
|
76
|
+
-d "$BODY" \
|
|
77
|
+
-o /dev/null 2>/dev/null || true
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
exit 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "syntaur",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Run Syntaur project and assignment workflows from Codex, including claiming work, planning, completing handoffs, session tracking, and write-boundary enforcement.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Brennen"
|
|
@@ -93,7 +93,7 @@ Use these commands directly when needed:
|
|
|
93
93
|
- `syntaur comment <assignment-slug-or-uuid> "body" --type question|note|feedback [--reply-to <id>] [--project <slug>]` — append to `comments.md`
|
|
94
94
|
- `syntaur request <target-slug-or-uuid> "text" [--from <source>] [--project <slug>]` — append to target's `## Todos`, annotated `(from: <source>)`
|
|
95
95
|
- `syntaur uninstall [--all] [--yes]`
|
|
96
|
-
- `syntaur track-session --project <project-slug> --assignment <assignment-slug> --agent codex --session-id <id> --path <cwd>`
|
|
96
|
+
- `syntaur track-session --project <project-slug> --assignment <assignment-slug> --agent codex --session-id <real-id> --transcript-path <rollout-path> --path <cwd>` (both `--session-id` and `--transcript-path` must come from the matching Codex rollout file — never synthesize)
|
|
97
97
|
- `syntaur setup-adapter codex --project <project-slug> --assignment <assignment-slug>`
|
|
98
98
|
|
|
99
99
|
## Standard Workflows
|
|
@@ -103,9 +103,11 @@ Use these commands directly when needed:
|
|
|
103
103
|
1. Discover the project and pending assignments.
|
|
104
104
|
2. Run `syntaur assign ... --agent codex`.
|
|
105
105
|
3. Run `syntaur start ...`.
|
|
106
|
-
4. Create `.syntaur/context.json` in the working directory.
|
|
107
|
-
5.
|
|
108
|
-
6.
|
|
106
|
+
4. Create (or merge into) `.syntaur/context.json` in the working directory. If a prior context file exists, preserve its fields.
|
|
107
|
+
5. Resolve the real Codex session id and rollout path: `bash ./scripts/resolve-session.sh "$(pwd)"` (relative to the plugin root). Parse `session_id=<id>` and `transcript_path=<abs path>`. If the helper exits non-zero, there is no matching Codex rollout in this cwd — start the Codex session first, then retry. Never `uuidgen`.
|
|
108
|
+
6. Merge `sessionId` + `transcriptPath` into `.syntaur/context.json`.
|
|
109
|
+
7. Register the session: `syntaur track-session --project <slug> --assignment <slug> --agent codex --session-id <id> --transcript-path <path> --path "$(pwd)"`.
|
|
110
|
+
8. If needed, run `syntaur setup-adapter codex --project <slug> --assignment <slug>`.
|
|
109
111
|
|
|
110
112
|
### Plan an assignment
|
|
111
113
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Syntaur Codex resolve-session helper
|
|
3
|
+
# Finds the most-recent Codex rollout file whose session_meta.payload.cwd
|
|
4
|
+
# matches $1 (default $PWD) and emits two lines to stdout:
|
|
5
|
+
# session_id=<id>
|
|
6
|
+
# transcript_path=<absolute path>
|
|
7
|
+
# Exits non-zero with nothing on stdout if no match.
|
|
8
|
+
#
|
|
9
|
+
# Override the search root via CODEX_SESSIONS_DIR (default: $HOME/.codex/sessions).
|
|
10
|
+
# Known limitation: if multiple concurrent Codex sessions share the same cwd,
|
|
11
|
+
# this picks the newest-by-mtime. Users can bypass by passing --session-id and
|
|
12
|
+
# --transcript-path explicitly to `syntaur track-session`.
|
|
13
|
+
|
|
14
|
+
set -o pipefail 2>/dev/null || true
|
|
15
|
+
|
|
16
|
+
command -v jq >/dev/null 2>&1 || { exit 1; }
|
|
17
|
+
|
|
18
|
+
TARGET_CWD="${1:-$PWD}"
|
|
19
|
+
SESSIONS_ROOT="${CODEX_SESSIONS_DIR:-$HOME/.codex/sessions}"
|
|
20
|
+
|
|
21
|
+
shopt -s nullglob 2>/dev/null || true
|
|
22
|
+
|
|
23
|
+
# Expand the glob explicitly via bash. If no files match, `files` stays empty
|
|
24
|
+
# and we exit without invoking ls — guards against `ls -1t` falling back to
|
|
25
|
+
# listing the current directory when the glob strips to zero operands.
|
|
26
|
+
files=("$SESSIONS_ROOT"/*/*/*/rollout-*.jsonl)
|
|
27
|
+
[ "${#files[@]}" -eq 0 ] && exit 1
|
|
28
|
+
|
|
29
|
+
MATCHED_FILE=""
|
|
30
|
+
MATCHED_ID=""
|
|
31
|
+
|
|
32
|
+
while IFS= read -r f; do
|
|
33
|
+
[ -z "$f" ] && continue
|
|
34
|
+
FIRST=$(head -n 1 "$f" 2>/dev/null)
|
|
35
|
+
[ -z "$FIRST" ] && continue
|
|
36
|
+
SESSION_CWD=$(printf '%s' "$FIRST" | jq -r 'select(.type=="session_meta") | .payload.cwd // empty' 2>/dev/null)
|
|
37
|
+
SESSION_ID=$(printf '%s' "$FIRST" | jq -r 'select(.type=="session_meta") | .payload.id // empty' 2>/dev/null)
|
|
38
|
+
if [ "$SESSION_CWD" = "$TARGET_CWD" ] && [ -n "$SESSION_ID" ]; then
|
|
39
|
+
MATCHED_FILE="$f"
|
|
40
|
+
MATCHED_ID="$SESSION_ID"
|
|
41
|
+
break
|
|
42
|
+
fi
|
|
43
|
+
done < <(ls -1t "${files[@]}" 2>/dev/null)
|
|
44
|
+
|
|
45
|
+
[ -z "$MATCHED_FILE" ] && exit 1
|
|
46
|
+
|
|
47
|
+
printf 'session_id=%s\n' "$MATCHED_ID"
|
|
48
|
+
printf 'transcript_path=%s\n' "$MATCHED_FILE"
|
|
49
|
+
exit 0
|