syntaur 0.14.0 → 0.16.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/dashboard/dist/assets/{_basePickBy-qEDLAY_J.js → _basePickBy-B1S22H85.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-7tDs18Gn.js → _baseUniq-CJIRCCsh.js} +1 -1
- package/dashboard/dist/assets/{arc-Brdw0dYW.js → arc-CdlNYTcd.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-BiEdLT9m.js → architectureDiagram-2XIMDMQ5-KVI0HWOy.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-HC-j_hPZ.js → blockDiagram-WCTKOSBZ-ClnG7gaq.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-DA_rWbl7.js → c4Diagram-IC4MRINW-BD9lkttL.js} +1 -1
- package/dashboard/dist/assets/channel-Cm_FzKJq.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-CF3hguyj.js → chunk-4BX2VUAB-BM3DXZMG.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-BTeCeIc_.js → chunk-55IACEB6-DUIbFJ3r.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-8qVvxmks.js → chunk-FMBD7UC4-B_cxCRkA.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-DCf8n7EO.js → chunk-JSJVCQXG-2mLf1WoM.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-CcpjTmME.js → chunk-KX2RTZJC-BMz3ND2R.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-BnFPKo4-.js → chunk-NQ4KR5QH-DiCxpw3L.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-IPMVLLv2.js → chunk-QZHKN3VN-BcaWPzd5.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-D1pQmHqX.js → chunk-WL4C6EOR-BbsfhS6J.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-Bdan2Dp5.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-Bdan2Dp5.js +1 -0
- package/dashboard/dist/assets/clone-9PoPEM_s.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-tvJjvBQr.js → cose-bilkent-S5V4N54A-iuQzyyrA.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-qp1vt6LY.js → dagre-KLK3FWXG-C0TB1zg5.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-VF5eizNr.js → diagram-E7M64L7V-Ck8tvxP2.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-C3pCbJ1e.js → diagram-IFDJBPK2-rQKIi-sc.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-D-taXZwH.js → diagram-P4PSJMXO-DS1RGSgx.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-QjY-NHVs.js → erDiagram-INFDFZHY-BqWCybWW.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-FjO8P5Tq.js → flowDiagram-PKNHOUZH-BCoQtyQ5.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-Bsn7CyK3.js → ganttDiagram-A5KZAMGK-CD1BmicP.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-BZ8DjhnF.js → gitGraphDiagram-K3NZZRJ6-tGDYOZuO.js} +1 -1
- package/dashboard/dist/assets/{graph-AdliIYqX.js → graph-BT0s-094.js} +1 -1
- package/dashboard/dist/assets/index-BdSkANCC.js +510 -0
- package/dashboard/dist/assets/index-D2Yoi6DJ.css +1 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-BfZjLA86.js → infoDiagram-LFFYTUFH-E4dVb8BD.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BSgSGMp5.js → ishikawaDiagram-PHBUUO56-CubuM_ev.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-BlQ7qeMC.js → journeyDiagram-4ABVD52K-DfyJbgMF.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-CifO0AUu.js → kanban-definition-K7BYSVSG-BXamajyZ.js} +1 -1
- package/dashboard/dist/assets/{layout-B9r-gpD9.js → layout-6b_SlWD_.js} +1 -1
- package/dashboard/dist/assets/{linear-CaaIRxVZ.js → linear-BE0O-66A.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-C8wCWYw-.js → mermaid.core-DM3qWr07.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-BpX5T7iT.js → mindmap-definition-YRQLILUH-puOtERrG.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-BNGYtWrc.js → pieDiagram-SKSYHLDU-cE6l8UBX.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-CR4GeM9D.js → quadrantDiagram-337W2JSQ-CgDAyZQP.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-CXrPkMLl.js → requirementDiagram-Z7DCOOCP-CJrai_8U.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-CIzof7Dm.js → sankeyDiagram-WA2Y5GQK-Bf72l7UW.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-BJZLt0O2.js → sequenceDiagram-2WXFIKYE-0D8RN7ds.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-x9gC6zy6.js → stateDiagram-RAJIS63D-B8LucUIt.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-D0dc7wxQ.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-D_ZSQn1K.js → timeline-definition-YZTLITO2-DL3Hq8ii.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-LPpMEco4.js → treemap-KZPCXAKY-wF1X4_uI.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-CRFZQpr7.js → vennDiagram-LZ73GAT5-DDAjoGTZ.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-Bivutynw.js → xychartDiagram-JWTSCODW-T-YmzRW8.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +7664 -5884
- package/dist/dashboard/server.js.map +1 -1
- package/dist/db/leases-db.js.map +1 -1
- package/dist/index.js +6438 -2094
- package/dist/index.js.map +1 -1
- package/dist/launch/index.d.ts +318 -0
- package/dist/launch/index.js +3122 -0
- package/dist/launch/index.js.map +1 -0
- package/package.json +15 -2
- package/platforms/claude-code/commands/track-session/track-session.md +2 -1
- package/platforms/claude-code/hooks/session-start.sh +14 -1
- package/platforms/claude-code/references/protocol-summary.md +1 -1
- package/platforms/claude-code/skills/capture-artifacts/SKILL.md +2 -1
- package/platforms/claude-code/skills/complete-assignment/SKILL.md +2 -0
- package/platforms/claude-code/skills/syntaur-protocol/references/protocol-summary.md +1 -1
- package/platforms/claude-code/skills/track-session/SKILL.md +2 -1
- package/platforms/codex/agents/syntaur-operator.md +2 -2
- package/platforms/codex/references/protocol-summary.md +1 -1
- package/platforms/codex/skills/capture-artifacts/SKILL.md +2 -1
- package/platforms/codex/skills/complete-assignment/SKILL.md +2 -0
- package/platforms/codex/skills/syntaur-protocol/references/protocol-summary.md +1 -1
- package/platforms/codex/skills/track-session/SKILL.md +2 -1
- package/scripts/install-macos-url-handler.mjs +540 -0
- package/skills/capture-artifacts/SKILL.md +2 -1
- package/skills/complete-assignment/SKILL.md +2 -0
- package/skills/syntaur-protocol/references/protocol-summary.md +1 -1
- package/skills/track-session/SKILL.md +2 -1
- package/dashboard/dist/assets/channel-D2-caOn6.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-HFOb_XEt.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-HFOb_XEt.js +0 -1
- package/dashboard/dist/assets/clone-BrYOwgIq.js +0 -1
- package/dashboard/dist/assets/index-BoR_oyUZ.css +0 -1
- package/dashboard/dist/assets/index-RCBI4aX2.js +0 -525
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-CN5Dcv7u.js +0 -1
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Register `syntaur://` as a URL scheme handler on macOS.
|
|
4
|
+
*
|
|
5
|
+
* Two call sites:
|
|
6
|
+
* - Postinstall hook (npm runs `node scripts/install-macos-url-handler.mjs`):
|
|
7
|
+
* uses the bottom-of-file `main()` wrapper which calls
|
|
8
|
+
* `registerMacosUrlHandler({ throwOnFailure: false })` and swallows
|
|
9
|
+
* everything so `npm install` never fails on URL-scheme registration.
|
|
10
|
+
* - `syntaur install-url-handler` subcommand: dynamic-imports this module
|
|
11
|
+
* and calls `registerMacosUrlHandler({ throwOnFailure: true })` so any
|
|
12
|
+
* `osacompile`/`codesign`/`lsregister` failure surfaces loudly.
|
|
13
|
+
*
|
|
14
|
+
* Implementation: builds an AppleScript applet (`.app` bundle) that
|
|
15
|
+
* LaunchServices routes incoming `syntaur://...` URLs to via the standard
|
|
16
|
+
* `on open location` Apple Event handler, then runs `lsregister -f` to
|
|
17
|
+
* register it. See the renderAppleScript docstring for the routing flow.
|
|
18
|
+
*
|
|
19
|
+
* Why an AppleScript applet, not a bare Bash executable: macOS URL scheme
|
|
20
|
+
* handlers receive URLs as `kAEGetURL` Apple Events, not as `argv[1]`. A plain
|
|
21
|
+
* Bash exec inside an `.app` bundle never sees the URL — `$1` is empty. The
|
|
22
|
+
* AppleScript runtime is the simplest way to handle the Apple Event without
|
|
23
|
+
* shipping a compiled Swift/ObjC helper; `osacompile` produces a real `.app`
|
|
24
|
+
* with the right entry point.
|
|
25
|
+
*
|
|
26
|
+
* The AppleScript handler shells out to the installed `syntaur` CLI using the
|
|
27
|
+
* absolute path to `node` (= `process.execPath` at install time) and the
|
|
28
|
+
* absolute path to the CLI entry (`bin/syntaur.js`), so LaunchServices's
|
|
29
|
+
* stripped PATH doesn't matter.
|
|
30
|
+
*
|
|
31
|
+
* Concurrency: both call sites can fire simultaneously (e.g. user has the
|
|
32
|
+
* subcommand open in one terminal while `npm install -g syntaur` runs in
|
|
33
|
+
* another). The bundle path is shared, so we acquire an exclusive lockfile
|
|
34
|
+
* at `~/.syntaur/install-url-handler.lock` via `fs.openSync(path, 'wx')`
|
|
35
|
+
* before touching the bundle directory and release it in `finally`.
|
|
36
|
+
*
|
|
37
|
+
* No-op on non-darwin platforms (returns `{ bundlePath: '' }` under
|
|
38
|
+
* `throwOnFailure: false`; throws under `true`).
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import {
|
|
42
|
+
existsSync,
|
|
43
|
+
mkdirSync,
|
|
44
|
+
rmSync,
|
|
45
|
+
writeFileSync,
|
|
46
|
+
realpathSync,
|
|
47
|
+
openSync,
|
|
48
|
+
closeSync,
|
|
49
|
+
unlinkSync,
|
|
50
|
+
} from 'node:fs';
|
|
51
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
52
|
+
import { dirname, resolve, join } from 'node:path';
|
|
53
|
+
import { homedir, tmpdir } from 'node:os';
|
|
54
|
+
import { spawnSync } from 'node:child_process';
|
|
55
|
+
|
|
56
|
+
const BUNDLE_ID = 'app.syntaur.url-handler';
|
|
57
|
+
const BUNDLE_NAME = 'Syntaur URL Handler';
|
|
58
|
+
const URL_SCHEME = 'syntaur';
|
|
59
|
+
const LSREGISTER = '/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister';
|
|
60
|
+
const OSACOMPILE = '/usr/bin/osacompile';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Mirror of `src/utils/paths.ts:syntaurRoot()`. Replicated here because the
|
|
64
|
+
* postinstall .mjs cannot reach into compiled TS — at postinstall time
|
|
65
|
+
* `dist/` may not exist yet on a fresh clone.
|
|
66
|
+
*/
|
|
67
|
+
function syntaurRootMjs() {
|
|
68
|
+
const override = process.env.SYNTAUR_HOME;
|
|
69
|
+
if (override && override.length > 0) {
|
|
70
|
+
return override;
|
|
71
|
+
}
|
|
72
|
+
return join(homedir(), '.syntaur');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Register the syntaur:// URL handler on macOS.
|
|
77
|
+
*
|
|
78
|
+
* @param {{ throwOnFailure: boolean }} options
|
|
79
|
+
* throwOnFailure: when true (subcommand path), any non-zero exit from
|
|
80
|
+
* osacompile/codesign/lsregister throws and a non-darwin platform throws.
|
|
81
|
+
* When false (postinstall path), failures degrade to console.warn and the
|
|
82
|
+
* function returns with bundlePath: ''.
|
|
83
|
+
* @returns {Promise<{ bundlePath: string }>}
|
|
84
|
+
*/
|
|
85
|
+
export async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
86
|
+
const { throwOnFailure } = options;
|
|
87
|
+
|
|
88
|
+
if (process.platform !== 'darwin') {
|
|
89
|
+
if (throwOnFailure) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
'macOS-only: syntaur:// URL handler registration is only supported on darwin.',
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return { bundlePath: '' };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Acquire the cross-process lock before touching the shared bundle path.
|
|
98
|
+
// `wx` flag = O_CREAT | O_EXCL — open fails with EEXIST if the file is
|
|
99
|
+
// already there, which is exactly the "another registration in progress"
|
|
100
|
+
// signal we want. The stale-lock case (process crashed mid-registration)
|
|
101
|
+
// is left to manual cleanup; surfacing the lock path in the error message
|
|
102
|
+
// makes that obvious to the user.
|
|
103
|
+
const stateRoot = syntaurRootMjs();
|
|
104
|
+
mkdirSync(stateRoot, { recursive: true });
|
|
105
|
+
const lockPath = resolve(stateRoot, 'install-url-handler.lock');
|
|
106
|
+
|
|
107
|
+
let lockFd;
|
|
108
|
+
try {
|
|
109
|
+
lockFd = openSync(lockPath, 'wx');
|
|
110
|
+
} catch (err) {
|
|
111
|
+
if (err && err.code === 'EEXIST') {
|
|
112
|
+
const msg = `Another syntaur:// handler registration is in progress (lock at ${lockPath}). Wait for it to finish or remove the stale lock.`;
|
|
113
|
+
if (throwOnFailure) {
|
|
114
|
+
throw new Error(msg);
|
|
115
|
+
}
|
|
116
|
+
console.warn(`syntaur: skipping macOS URL-handler registration (${msg})`);
|
|
117
|
+
return { bundlePath: '' };
|
|
118
|
+
}
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
124
|
+
const cliBin = realpathSync(resolve(pkgRoot, 'bin/syntaur.js'));
|
|
125
|
+
const nodeBin = process.execPath;
|
|
126
|
+
const bundleParent = join(
|
|
127
|
+
homedir(),
|
|
128
|
+
'Library',
|
|
129
|
+
'Application Support',
|
|
130
|
+
'Syntaur',
|
|
131
|
+
);
|
|
132
|
+
const bundlePath = join(bundleParent, 'syntaur-url.app');
|
|
133
|
+
|
|
134
|
+
mkdirSync(bundleParent, { recursive: true });
|
|
135
|
+
|
|
136
|
+
// Overwrite the bundle in place for idempotency. osacompile -o overwrites
|
|
137
|
+
// an existing target only if we remove it first.
|
|
138
|
+
if (existsSync(bundlePath)) {
|
|
139
|
+
rmSync(bundlePath, { recursive: true, force: true });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Detect which AppleScript-driven terminals are installed. osacompile
|
|
143
|
+
// resolves application terminology at compile time, so a `tell application
|
|
144
|
+
// "Ghostty"` block would fail to compile if Ghostty isn't installed. We
|
|
145
|
+
// only embed tell-blocks for apps we find on disk; unknown/missing
|
|
146
|
+
// terminals fall back to a shell-out path which goes through the CLI's
|
|
147
|
+
// executeLaunchPlan (and may need its own TCC grant on the parent shell
|
|
148
|
+
// process).
|
|
149
|
+
const installedTerminals = detectInstalledTerminals();
|
|
150
|
+
|
|
151
|
+
// 1. Write the AppleScript source to a temp file.
|
|
152
|
+
const scriptPath = join(
|
|
153
|
+
tmpdir(),
|
|
154
|
+
`syntaur-url-handler-${process.pid}.applescript`,
|
|
155
|
+
);
|
|
156
|
+
writeFileSync(
|
|
157
|
+
scriptPath,
|
|
158
|
+
renderAppleScript({ nodeBin, cliBin, installedTerminals }),
|
|
159
|
+
'utf-8',
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// 2. Compile it into an .app bundle. osacompile produces a real macOS app
|
|
163
|
+
// with the AppleScript runtime as its main executable — that runtime
|
|
164
|
+
// knows how to dispatch the kAEGetURL Apple Event to our handler.
|
|
165
|
+
// osacompile failure is always fatal: registration is impossible
|
|
166
|
+
// without the bundle, so we throw regardless of `throwOnFailure`.
|
|
167
|
+
const compile = spawnSync(
|
|
168
|
+
OSACOMPILE,
|
|
169
|
+
['-o', bundlePath, scriptPath],
|
|
170
|
+
{ stdio: 'pipe', encoding: 'utf-8' },
|
|
171
|
+
);
|
|
172
|
+
if (compile.status !== 0) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`osacompile exited with code ${compile.status}: ${compile.stderr || compile.stdout}`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 3. Patch the Info.plist that osacompile generated to add the URL scheme
|
|
179
|
+
// declaration, the bundle id, and a name. osacompile's default plist
|
|
180
|
+
// has no CFBundleURLTypes — without these LaunchServices won't route
|
|
181
|
+
// syntaur:// URLs to the bundle.
|
|
182
|
+
const infoPlistPath = join(bundlePath, 'Contents', 'Info.plist');
|
|
183
|
+
if (!existsSync(infoPlistPath)) {
|
|
184
|
+
throw new Error(`osacompile did not produce ${infoPlistPath}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Use PlistBuddy to surgically add/replace the keys we care about. Each
|
|
188
|
+
// step tries Add first (for missing keys) and falls back to Set (for keys
|
|
189
|
+
// osacompile already wrote). The :CFBundleURLTypes array we always Add
|
|
190
|
+
// fresh because osacompile never writes one.
|
|
191
|
+
//
|
|
192
|
+
// The Delete entries strip osacompile's kitchen-sink default
|
|
193
|
+
// NSXxxxUsageDescription keys — its applet template declares purpose
|
|
194
|
+
// strings for HomeKit/Photos/Camera/AppleMusic/Reminders/Siri/etc. so
|
|
195
|
+
// that ANY applet *could* request those permissions. We only need
|
|
196
|
+
// NSAppleEventsUsageDescription (for `tell application ...`); the rest
|
|
197
|
+
// would surface as scary "this app wants access to ..." entries in
|
|
198
|
+
// System Settings → Privacy & Security and serve no purpose for a URL
|
|
199
|
+
// handler. The Deletes are guarded by `null` fallbacks so missing keys
|
|
200
|
+
// are not fatal.
|
|
201
|
+
runPlistBuddy(infoPlistPath, [
|
|
202
|
+
[`Add :CFBundleIdentifier string ${BUNDLE_ID}`, `Set :CFBundleIdentifier ${BUNDLE_ID}`],
|
|
203
|
+
[`Add :CFBundleName string ${BUNDLE_NAME}`, `Set :CFBundleName ${BUNDLE_NAME}`],
|
|
204
|
+
['Add :LSUIElement bool true', 'Set :LSUIElement true'],
|
|
205
|
+
// Replace the generic Apple Events string with one that names what
|
|
206
|
+
// this app actually does, so the prompt the user sees is meaningful.
|
|
207
|
+
// PlistBuddy Set on a string with an apostrophe/space mix is unreliable;
|
|
208
|
+
// Delete-then-Add is the safe pattern.
|
|
209
|
+
['Delete :NSAppleEventsUsageDescription', null],
|
|
210
|
+
[
|
|
211
|
+
'Add :NSAppleEventsUsageDescription string Syntaur is opening your configured terminal at the assignment worktree.',
|
|
212
|
+
null,
|
|
213
|
+
],
|
|
214
|
+
['Delete :NSHomeKitUsageDescription', null],
|
|
215
|
+
['Delete :NSAppleMusicUsageDescription', null],
|
|
216
|
+
['Delete :NSCalendarsUsageDescription', null],
|
|
217
|
+
['Delete :NSSiriUsageDescription', null],
|
|
218
|
+
['Delete :NSCameraUsageDescription', null],
|
|
219
|
+
['Delete :NSMicrophoneUsageDescription', null],
|
|
220
|
+
['Delete :NSContactsUsageDescription', null],
|
|
221
|
+
['Delete :NSPhotoLibraryUsageDescription', null],
|
|
222
|
+
['Delete :NSRemindersUsageDescription', null],
|
|
223
|
+
['Delete :NSSystemAdministrationUsageDescription', null],
|
|
224
|
+
['Delete :CFBundleURLTypes', null],
|
|
225
|
+
['Add :CFBundleURLTypes array', null],
|
|
226
|
+
['Add :CFBundleURLTypes:0 dict', null],
|
|
227
|
+
['Add :CFBundleURLTypes:0:CFBundleURLName string Syntaur', null],
|
|
228
|
+
['Add :CFBundleURLTypes:0:CFBundleURLSchemes array', null],
|
|
229
|
+
[`Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string ${URL_SCHEME}`, null],
|
|
230
|
+
]);
|
|
231
|
+
|
|
232
|
+
// 4. Re-sign the bundle ad-hoc. osacompile produces an ad-hoc signed
|
|
233
|
+
// bundle, but PlistBuddy edits invalidate that signature ("Info.plist
|
|
234
|
+
// not bound to signature"). Modern macOS refuses to prompt for
|
|
235
|
+
// Automation/TCC permissions on unsigned bundles and denies Apple
|
|
236
|
+
// events outright with error -1743 "Not authorized". Re-signing with
|
|
237
|
+
// `--force --deep --sign -` produces a fresh ad-hoc signature that
|
|
238
|
+
// includes the patched Info.plist, which is enough for TCC.
|
|
239
|
+
const sign = spawnSync(
|
|
240
|
+
'/usr/bin/codesign',
|
|
241
|
+
['--force', '--deep', '--sign', '-', bundlePath],
|
|
242
|
+
{ stdio: 'pipe', encoding: 'utf-8' },
|
|
243
|
+
);
|
|
244
|
+
if (sign.status !== 0) {
|
|
245
|
+
const msg = `codesign returned ${sign.status} while re-signing ${bundlePath}: ${sign.stderr || sign.stdout || '(no output)'}`;
|
|
246
|
+
if (throwOnFailure) {
|
|
247
|
+
throw new Error(msg);
|
|
248
|
+
}
|
|
249
|
+
console.warn(`syntaur: ${msg} — macOS may deny Automation permission.`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 5. Register with LaunchServices so the OS routes syntaur:// URLs here.
|
|
253
|
+
// Switched stdio to 'pipe' (was 'ignore') so we can include lsregister's
|
|
254
|
+
// own diagnostic in failure messages — postinstall used to drop it.
|
|
255
|
+
const ls = spawnSync(LSREGISTER, ['-f', bundlePath], {
|
|
256
|
+
stdio: 'pipe',
|
|
257
|
+
encoding: 'utf-8',
|
|
258
|
+
});
|
|
259
|
+
if (ls.status !== 0) {
|
|
260
|
+
const msg = `lsregister returned ${ls.status} while registering ${bundlePath}: ${ls.stderr || ls.stdout || '(no output)'}`;
|
|
261
|
+
if (throwOnFailure) {
|
|
262
|
+
throw new Error(msg);
|
|
263
|
+
}
|
|
264
|
+
console.warn(
|
|
265
|
+
`syntaur: ${msg} — \`open syntaur://...\` may not route through the CLI handler until you run \`${LSREGISTER} -f ${bundlePath}\` manually.`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 6. Clean up the temp source file.
|
|
270
|
+
try { rmSync(scriptPath); } catch { /* not fatal */ }
|
|
271
|
+
|
|
272
|
+
return { bundlePath };
|
|
273
|
+
} finally {
|
|
274
|
+
// Release the lock — both close and unlink wrapped so cleanup never throws.
|
|
275
|
+
try { closeSync(lockFd); } catch { /* not fatal */ }
|
|
276
|
+
try { unlinkSync(lockPath); } catch { /* not fatal */ }
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Postinstall thin wrapper — preserves byte-identical behavior from before
|
|
282
|
+
* the export refactor: catches everything, prints a warning, exits 0.
|
|
283
|
+
*/
|
|
284
|
+
async function main() {
|
|
285
|
+
try {
|
|
286
|
+
await registerMacosUrlHandler({ throwOnFailure: false });
|
|
287
|
+
} catch (err) {
|
|
288
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
289
|
+
console.warn(`syntaur: skipping macOS URL-handler registration (${msg})`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Render the AppleScript source for the URL handler applet.
|
|
295
|
+
*
|
|
296
|
+
* The `on open location` handler is the documented entry point for URL scheme
|
|
297
|
+
* apps. It fires for each `kAEGetURL` Apple Event — i.e. each time the OS or
|
|
298
|
+
* another app calls `open syntaur://...`.
|
|
299
|
+
*
|
|
300
|
+
* The handler shells out to the installed `syntaur url <url>` CLI via the
|
|
301
|
+
* baked-in absolute paths to `node` and the CLI entry point. CLI stdout +
|
|
302
|
+
* stderr are appended to `~/Library/Logs/Syntaur/url-handler.log` (created on
|
|
303
|
+
* first run) so deep-link failures are diagnosable. AppleScript-level errors
|
|
304
|
+
* (e.g. CLI exited non-zero) fall through to a second `printf` that appends a
|
|
305
|
+
* summary line to the same log.
|
|
306
|
+
*/
|
|
307
|
+
/**
|
|
308
|
+
* Detect which AppleScript-driven terminals are installed on this Mac.
|
|
309
|
+
* osacompile resolves application terminology at compile time, so we can
|
|
310
|
+
* only emit `tell application "X"` blocks for apps that exist on disk.
|
|
311
|
+
*
|
|
312
|
+
* Returns a set of terminal ids that match the syntaur `TerminalChoice` enum.
|
|
313
|
+
* Terminal.app is always assumed present (it ships with macOS).
|
|
314
|
+
*/
|
|
315
|
+
function detectInstalledTerminals() {
|
|
316
|
+
const installed = new Set(['terminal-app']);
|
|
317
|
+
const bundleIds = {
|
|
318
|
+
iterm: 'com.googlecode.iterm2',
|
|
319
|
+
ghostty: 'com.mitchellh.ghostty',
|
|
320
|
+
};
|
|
321
|
+
for (const [id, bundleId] of Object.entries(bundleIds)) {
|
|
322
|
+
const r = spawnSync(
|
|
323
|
+
'mdfind',
|
|
324
|
+
[`kMDItemCFBundleIdentifier == '${bundleId}'`],
|
|
325
|
+
{ encoding: 'utf-8' },
|
|
326
|
+
);
|
|
327
|
+
if (r.status === 0 && r.stdout.trim().length > 0) {
|
|
328
|
+
installed.add(id);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return installed;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Build the AppleScript `if/else if` chain that dispatches based on the
|
|
336
|
+
* terminal id the CLI returned. We only emit `tell application "X"` blocks
|
|
337
|
+
* for terminals confirmed installed (`installedTerminals`); everything else
|
|
338
|
+
* falls through to a shell-out that re-invokes the CLI without --print-plan
|
|
339
|
+
* so `executeLaunchPlan` does the spawn.
|
|
340
|
+
*
|
|
341
|
+
* Returns an array of AppleScript source lines (no trailing newline).
|
|
342
|
+
*/
|
|
343
|
+
function buildTerminalDispatch(installedTerminals) {
|
|
344
|
+
const branches = [];
|
|
345
|
+
if (installedTerminals.has('terminal-app')) {
|
|
346
|
+
branches.push({
|
|
347
|
+
id: 'terminal-app',
|
|
348
|
+
block: [
|
|
349
|
+
'\t\t\ttell application "Terminal"',
|
|
350
|
+
'\t\t\t\tactivate',
|
|
351
|
+
'\t\t\t\tdo script shellCmd',
|
|
352
|
+
'\t\t\tend tell',
|
|
353
|
+
],
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
if (installedTerminals.has('iterm')) {
|
|
357
|
+
branches.push({
|
|
358
|
+
id: 'iterm',
|
|
359
|
+
block: [
|
|
360
|
+
'\t\t\ttell application "iTerm"',
|
|
361
|
+
'\t\t\t\tactivate',
|
|
362
|
+
'\t\t\t\tset newWindow to (create window with default profile)',
|
|
363
|
+
'\t\t\t\ttell current session of newWindow to write text shellCmd',
|
|
364
|
+
'\t\t\tend tell',
|
|
365
|
+
],
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
if (installedTerminals.has('ghostty')) {
|
|
369
|
+
branches.push({
|
|
370
|
+
id: 'ghostty',
|
|
371
|
+
block: [
|
|
372
|
+
'\t\t\ttell application "Ghostty"',
|
|
373
|
+
'\t\t\t\tactivate',
|
|
374
|
+
'\t\t\t\tset newWin to (new window)',
|
|
375
|
+
'\t\t\t\tdelay 0.2',
|
|
376
|
+
'\t\t\t\tset t to terminal 1 of selected tab of newWin',
|
|
377
|
+
'\t\t\t\tinput text shellCmd to t',
|
|
378
|
+
'\t\t\t\tsend key "enter" to t',
|
|
379
|
+
'\t\t\tend tell',
|
|
380
|
+
],
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const lines = [];
|
|
385
|
+
branches.forEach((b, i) => {
|
|
386
|
+
const keyword = i === 0 ? 'if' : 'else if';
|
|
387
|
+
lines.push(`\t\t${keyword} theTerminal is "${b.id}" then`);
|
|
388
|
+
lines.push(...b.block);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Fallback: any terminal we couldn't emit a tell-block for (because the
|
|
392
|
+
// app isn't installed) OR any CLI-driven terminal (alacritty/kitty/warp)
|
|
393
|
+
// falls through to a shell-out that runs the CLI's executeLaunchPlan.
|
|
394
|
+
if (branches.length > 0) {
|
|
395
|
+
lines.push('\t\telse');
|
|
396
|
+
}
|
|
397
|
+
lines.push(
|
|
398
|
+
`\t\t\tdo shell script quoted form of nodeBin & " " & quoted form of cliBin & " url " & quoted form of theURL & " >> " & quoted form of logFile & " 2>&1"`,
|
|
399
|
+
);
|
|
400
|
+
if (branches.length > 0) {
|
|
401
|
+
lines.push('\t\tend if');
|
|
402
|
+
}
|
|
403
|
+
return lines;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function renderAppleScript({ nodeBin, cliBin, installedTerminals }) {
|
|
407
|
+
// The applet must run the `tell application "Terminal" to do script ...`
|
|
408
|
+
// (and equivalent for iTerm/Ghostty) *itself* — not via `osascript` spawned
|
|
409
|
+
// from a do-shell-script chain. macOS TCC will not reliably attribute
|
|
410
|
+
// Apple Events sent from `applet -> do shell script -> node -> osascript`
|
|
411
|
+
// back to the applet's bundle identity; the request is denied with -1743
|
|
412
|
+
// "Not authorized" and the user never sees a permission prompt.
|
|
413
|
+
//
|
|
414
|
+
// Architecture:
|
|
415
|
+
// 1. Applet shells to `syntaur url --print-plan <url>`. The CLI returns
|
|
416
|
+
// a two-line plan: terminal id on line 1, the cd-and-run shell command
|
|
417
|
+
// on line 2. No Apple Events involved on this hop.
|
|
418
|
+
// 2. Applet parses the two lines and dispatches to a per-terminal
|
|
419
|
+
// `tell application <name> to ...` block *inside the applet's own
|
|
420
|
+
// AppleScript scope*. That makes the applet the responsible process
|
|
421
|
+
// for the Apple Event, so macOS prompts the user once and grants
|
|
422
|
+
// Automation permission to the applet.
|
|
423
|
+
//
|
|
424
|
+
// Every shell-command interpolation uses `quoted form of` so paths with
|
|
425
|
+
// spaces or shell metacharacters are safe.
|
|
426
|
+
const node = appleScriptString(nodeBin);
|
|
427
|
+
const cli = appleScriptString(cliBin);
|
|
428
|
+
|
|
429
|
+
return [
|
|
430
|
+
'on open location theURL',
|
|
431
|
+
`\tset nodeBin to ${node}`,
|
|
432
|
+
`\tset cliBin to ${cli}`,
|
|
433
|
+
'\tset libPath to POSIX path of (path to library folder from user domain)',
|
|
434
|
+
'\tset logDir to libPath & "Logs/Syntaur"',
|
|
435
|
+
'\tset logFile to logDir & "/url-handler.log"',
|
|
436
|
+
'\ttry',
|
|
437
|
+
'\t\tdo shell script "/bin/mkdir -p " & quoted form of logDir',
|
|
438
|
+
'\tend try',
|
|
439
|
+
'',
|
|
440
|
+
'\t-- Step 1: ask the CLI for the launch plan (two lines: terminal, shellCmd).',
|
|
441
|
+
'\tset planOutput to ""',
|
|
442
|
+
'\ttry',
|
|
443
|
+
`\t\tset planOutput to (do shell script quoted form of nodeBin & " " & quoted form of cliBin & " url --print-plan " & quoted form of theURL & " 2>> " & quoted form of logFile)`,
|
|
444
|
+
'\ton error errMsg number errNum',
|
|
445
|
+
'\t\ttry',
|
|
446
|
+
`\t\t\tdo shell script "/usr/bin/printf '%s\\\\n' " & quoted form of ("syntaur:// plan resolution failed for " & theURL & ": " & errMsg & " (" & errNum & ")") & " >> " & quoted form of logFile`,
|
|
447
|
+
'\t\tend try',
|
|
448
|
+
'\t\treturn',
|
|
449
|
+
'\tend try',
|
|
450
|
+
'',
|
|
451
|
+
'\t-- Step 2: parse the two-line plan. `paragraphs of` splits on CR/LF.',
|
|
452
|
+
'\tset planLines to paragraphs of planOutput',
|
|
453
|
+
'\tif (count of planLines) < 2 then',
|
|
454
|
+
'\t\ttry',
|
|
455
|
+
`\t\t\tdo shell script "/usr/bin/printf '%s\\\\n' " & quoted form of ("syntaur:// plan output malformed (expected 2 lines, got " & (count of planLines) & "): " & planOutput) & " >> " & quoted form of logFile`,
|
|
456
|
+
'\t\tend try',
|
|
457
|
+
'\t\treturn',
|
|
458
|
+
'\tend if',
|
|
459
|
+
'\tset theTerminal to item 1 of planLines',
|
|
460
|
+
'\tset shellCmd to item 2 of planLines',
|
|
461
|
+
'',
|
|
462
|
+
'\t-- Step 3: dispatch to a per-terminal block. The `tell application`',
|
|
463
|
+
"\t-- below runs in the applet's own AppleScript scope, so macOS TCC",
|
|
464
|
+
'\t-- attributes the resulting Apple Event to the applet (signed bundle',
|
|
465
|
+
'\t-- with CFBundleIdentifier app.syntaur.url-handler).',
|
|
466
|
+
'\ttry',
|
|
467
|
+
...buildTerminalDispatch(installedTerminals),
|
|
468
|
+
'\ton error errMsg number errNum',
|
|
469
|
+
'\t\ttry',
|
|
470
|
+
`\t\t\tdo shell script "/usr/bin/printf '%s\\\\n' " & quoted form of ("syntaur:// launch failed for " & theURL & " (terminal=" & theTerminal & "): " & errMsg & " (" & errNum & ")") & " >> " & quoted form of logFile`,
|
|
471
|
+
'\t\tend try',
|
|
472
|
+
'\tend try',
|
|
473
|
+
'end open location',
|
|
474
|
+
'',
|
|
475
|
+
'on run',
|
|
476
|
+
'\t-- No-op when the app is double-clicked without a URL. Users should',
|
|
477
|
+
'\t-- not launch this app directly; it exists only as a URL-scheme target.',
|
|
478
|
+
'end run',
|
|
479
|
+
'',
|
|
480
|
+
].join('\n');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Quote a string for embedding inside an AppleScript double-quoted literal.
|
|
485
|
+
* AppleScript interprets backslash and double-quote inside "..." strings;
|
|
486
|
+
* everything else passes through.
|
|
487
|
+
*/
|
|
488
|
+
function appleScriptString(value) {
|
|
489
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Run a sequence of PlistBuddy commands against the given plist. Each step is
|
|
494
|
+
* either a single command string (run as-is, error fatal) or a
|
|
495
|
+
* `[primary, fallback]` pair (run primary; if it fails, try fallback; only
|
|
496
|
+
* fatal if both fail). PlistBuddy uses different commands for Add vs Set
|
|
497
|
+
* depending on whether the key already exists.
|
|
498
|
+
*/
|
|
499
|
+
function runPlistBuddy(plistPath, steps) {
|
|
500
|
+
for (const step of steps) {
|
|
501
|
+
if (Array.isArray(step)) {
|
|
502
|
+
const [primary, fallback] = step;
|
|
503
|
+
const a = spawnSync('/usr/libexec/PlistBuddy', ['-c', primary, plistPath], {
|
|
504
|
+
stdio: 'pipe',
|
|
505
|
+
encoding: 'utf-8',
|
|
506
|
+
});
|
|
507
|
+
if (a.status === 0) continue;
|
|
508
|
+
if (fallback === null) continue; // Add-only commands are allowed to fail silently.
|
|
509
|
+
const b = spawnSync('/usr/libexec/PlistBuddy', ['-c', fallback, plistPath], {
|
|
510
|
+
stdio: 'pipe',
|
|
511
|
+
encoding: 'utf-8',
|
|
512
|
+
});
|
|
513
|
+
if (b.status !== 0) {
|
|
514
|
+
throw new Error(
|
|
515
|
+
`PlistBuddy failed: ${primary} OR ${fallback}: ${b.stderr || b.stdout}`,
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
const r = spawnSync('/usr/libexec/PlistBuddy', ['-c', step, plistPath], {
|
|
520
|
+
stdio: 'pipe',
|
|
521
|
+
encoding: 'utf-8',
|
|
522
|
+
});
|
|
523
|
+
if (r.status !== 0) {
|
|
524
|
+
throw new Error(
|
|
525
|
+
`PlistBuddy failed: ${step}: ${r.stderr || r.stdout}`,
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Only run main() when this file is executed directly (e.g. via the npm
|
|
533
|
+
// postinstall hook), NOT when it's dynamic-imported by the TS subcommand.
|
|
534
|
+
// Without this guard, `await import(...)` from the subcommand would trigger
|
|
535
|
+
// a second silent registration on import-time before the throw-mode call.
|
|
536
|
+
// pathToFileURL handles cross-platform path-to-URL normalization (matters on
|
|
537
|
+
// Windows where argv[1] uses backslashes).
|
|
538
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
539
|
+
main();
|
|
540
|
+
}
|
|
@@ -46,7 +46,7 @@ Linkage to a specific criterion is **optional**. Untagged artifacts are fine —
|
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
# File-based (screenshot, video, asciinema, http transcript)
|
|
49
|
-
syntaur capture --kind <type> --file <path> [--criterion <index>] [--note <text>] \
|
|
49
|
+
syntaur capture --kind <type> --file <path> [--criterion <index>] [--note <text>] [--transcribe] \
|
|
50
50
|
[--project <slug> <assignment-slug>]
|
|
51
51
|
|
|
52
52
|
# Text-only (no --file)
|
|
@@ -59,6 +59,7 @@ Rules:
|
|
|
59
59
|
- `--kind=http` accepts either `--file` (a transcript) or `--note` (an inline summary). At least one is required.
|
|
60
60
|
- All other kinds (`screenshot`, `video`, `asciinema`) require `--file`. The CLI rejects nonexistent / non-file paths.
|
|
61
61
|
- `--criterion <index>` is optional — pass the **0-based** index into the `## Acceptance Criteria` checklist when you want to anchor the artifact to a specific criterion.
|
|
62
|
+
- `--transcribe` is video-only and writes a sibling `<id>.transcript.md` (requires `ELEVENLABS_API_KEY` + `ffmpeg`). `proof build` renders the transcript next to the player; clicking a phrase seeks the video.
|
|
62
63
|
- If you have a `.syntaur/context.json` in the cwd, the positional target argument is unnecessary; otherwise pass `--project <slug> <assignment-slug>` or a bare assignment UUID.
|
|
63
64
|
|
|
64
65
|
The CLI copies the file (if any) under `<assignmentDir>/proof/<criterion|untagged>/<id>.<ext>` and inserts a row in `~/.syntaur/syntaur.db`. Output prints the artifact id and absolute path.
|
|
@@ -129,6 +129,8 @@ If the command fails, report the error. Common failures:
|
|
|
129
129
|
- Assignment is not in `in_progress` status
|
|
130
130
|
- Project not found
|
|
131
131
|
|
|
132
|
+
**Side effect:** Source todos that were promoted into this assignment via `syntaur todo promote --new-assignment` (or the dashboard `Promote to assignment` flow) automatically flip from `in_progress` to `completed` when this transition runs. If you later `reopen` the assignment, those auto-completed todos flip back to `in_progress`. Todos that the user manually marked `completed` after the auto-complete fired are left untouched on reopen.
|
|
133
|
+
|
|
132
134
|
## Step 8: Clean Up Context
|
|
133
135
|
|
|
134
136
|
Delete the context file:
|
|
@@ -85,6 +85,6 @@ Protocol version: **2.0**
|
|
|
85
85
|
|
|
86
86
|
Agents can attach typed evidence to an assignment so a human reviewer can verify the work in seconds without re-running it.
|
|
87
87
|
|
|
88
|
-
- `syntaur capture --kind <screenshot|video|asciinema|http|text> [--file <path>] [--criterion <index>] [--note <text>] [target]` — record an artifact for the active assignment. Criterion linkage is optional (0-based index into the `## Acceptance Criteria` checklist). For `--kind=text`, supply `--note` and omit `--file`. For `--kind=http`, supply `--file` (transcript) or `--note` (inline summary).
|
|
88
|
+
- `syntaur capture --kind <screenshot|video|asciinema|http|text> [--file <path>] [--criterion <index>] [--note <text>] [--transcribe] [target]` — record an artifact for the active assignment. Criterion linkage is optional (0-based index into the `## Acceptance Criteria` checklist). For `--kind=text`, supply `--note` and omit `--file`. For `--kind=http`, supply `--file` (transcript) or `--note` (inline summary). `--transcribe` is video-only and writes a sibling `<id>.transcript.md` (requires `ELEVENLABS_API_KEY` + `ffmpeg`).
|
|
89
89
|
- `syntaur proof build [target]` — render `proof.html` and `proof.md` at the assignment dir, walking the `## Acceptance Criteria` list and embedding tagged artifacts inline beneath each criterion. Untagged or stale (out-of-range) captures land in a final "Other artifacts" section. Atomic overwrite — safe to re-run.
|
|
90
90
|
- Artifacts are stored under `<assignmentDir>/proof/<criterion|untagged>/<id>.<ext>`. The DB row lives in `~/.syntaur/syntaur.db` alongside session tracking. No completion gate — proof is purely opt-in for v1.
|
|
@@ -47,12 +47,13 @@ syntaur track-session \
|
|
|
47
47
|
--session-id "$SESSION_ID" \
|
|
48
48
|
--transcript-path "$TRANSCRIPT_PATH" \
|
|
49
49
|
--path "$(pwd)" \
|
|
50
|
+
--pid "$(ps -o ppid= -p $$ | tr -d ' ')" \
|
|
50
51
|
[--description "<text>"] \
|
|
51
52
|
[--project <slug>] \
|
|
52
53
|
[--assignment <slug>]
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
Omit `--transcript-path` entirely (don't pass an empty string) if no transcript path could be resolved.
|
|
56
|
+
Omit `--transcript-path` entirely (don't pass an empty string) if no transcript path could be resolved. The `--pid` value is the shell PID that owns the Claude process — the dashboard uses it to disable Resume while this session may still be writing the transcript, forcing users to Fork instead. If `ps` is unavailable, omit `--pid` too.
|
|
56
57
|
|
|
57
58
|
The CLI prints one of:
|
|
58
59
|
- `Registered standalone agent session <sessionId>.`
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{aq as o,ar as n}from"./mermaid.core-C8wCWYw-.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-D1pQmHqX.js";import{_ as i}from"./mermaid.core-C8wCWYw-.js";import"./chunk-FMBD7UC4-8qVvxmks.js";import"./chunk-JSJVCQXG-DCf8n7EO.js";import"./chunk-55IACEB6-BTeCeIc_.js";import"./chunk-KX2RTZJC-CcpjTmME.js";import"./index-RCBI4aX2.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-D1pQmHqX.js";import{_ as i}from"./mermaid.core-C8wCWYw-.js";import"./chunk-FMBD7UC4-8qVvxmks.js";import"./chunk-JSJVCQXG-DCf8n7EO.js";import"./chunk-55IACEB6-BTeCeIc_.js";import"./chunk-KX2RTZJC-CcpjTmME.js";import"./index-RCBI4aX2.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-7tDs18Gn.js";var e=4;function a(o){return r(o,e)}export{a as c};
|