@schoolai/shipyard 3.5.0-rc.20260504.0 → 3.5.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/dist/{auth-LS3NBD42.js → auth-SS7LV5XK.js} +4 -3
- package/dist/{chunk-GLH3V7NG.js → chunk-2J3WSIAF.js} +5 -3
- package/dist/{chunk-GLH3V7NG.js.map → chunk-2J3WSIAF.js.map} +1 -1
- package/dist/{chunk-YUG27SAR.js → chunk-2UN5AR7V.js} +2 -2
- package/dist/{chunk-ODCN6W33.js → chunk-3CAEALVL.js} +7 -5
- package/dist/{chunk-ODCN6W33.js.map → chunk-3CAEALVL.js.map} +1 -1
- package/dist/chunk-3MNPDCO5.js +1011 -0
- package/dist/chunk-3MNPDCO5.js.map +1 -0
- package/dist/{chunk-JQ7HCEFS.js → chunk-BNEE7ZPW.js} +8 -6
- package/dist/{chunk-JQ7HCEFS.js.map → chunk-BNEE7ZPW.js.map} +1 -1
- package/dist/{chunk-5LIPEC7P.js → chunk-GIFN3IPT.js} +4 -4
- package/dist/{chunk-3TB4VNFG.js → chunk-IISLTKYY.js} +2 -2
- package/dist/chunk-PI77CUEP.js +49 -0
- package/dist/chunk-PI77CUEP.js.map +1 -0
- package/dist/chunk-SNYEQHUK.js +64 -0
- package/dist/chunk-SNYEQHUK.js.map +1 -0
- package/dist/{chunk-XXTIKBCU.js → chunk-VBPHGPBR.js} +2 -2
- package/dist/{chunk-M5M6VC5F.js → chunk-VPMN47TL.js} +31 -72
- package/dist/chunk-VPMN47TL.js.map +1 -0
- package/dist/{git-repo-CNIKBYPB.js → git-repo-364VANDM.js} +5 -4
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/{logger-7XW3I4XN.js → logger-GQCSLSZH.js} +4 -3
- package/dist/{login-RHZDNC74.js → login-D6USDG5M.js} +7 -6
- package/dist/{logout-CUAAF5IK.js → logout-VUNCW5B2.js} +6 -5
- package/dist/{logout-CUAAF5IK.js.map → logout-VUNCW5B2.js.map} +1 -1
- package/dist/mcp-servers-FZV2P2ZO.js +16 -0
- package/dist/{roi-LN7MMRH7.js → roi-Y3MX5UW4.js} +4 -3
- package/dist/{roi-LN7MMRH7.js.map → roi-Y3MX5UW4.js.map} +1 -1
- package/dist/{serve-E7CHPJD4.js → serve-IVUGCBEE.js} +73 -462
- package/dist/{serve-E7CHPJD4.js.map → serve-IVUGCBEE.js.map} +1 -1
- package/dist/services/watcher-worker/worker.d.ts +49 -0
- package/dist/services/watcher-worker/worker.js +157 -0
- package/dist/services/watcher-worker/worker.js.map +1 -0
- package/dist/{skills-OMDIMU7D.js → skills-GPGRNV4R.js} +2 -2
- package/dist/start-I7ZONWK7.js +285 -0
- package/dist/start-I7ZONWK7.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-M5M6VC5F.js.map +0 -1
- package/dist/mcp-servers-3SHS2PEJ.js +0 -15
- package/dist/start-HQ42GOYF.js +0 -36
- package/dist/start-HQ42GOYF.js.map +0 -1
- /package/dist/{auth-LS3NBD42.js.map → auth-SS7LV5XK.js.map} +0 -0
- /package/dist/{chunk-YUG27SAR.js.map → chunk-2UN5AR7V.js.map} +0 -0
- /package/dist/{chunk-5LIPEC7P.js.map → chunk-GIFN3IPT.js.map} +0 -0
- /package/dist/{chunk-3TB4VNFG.js.map → chunk-IISLTKYY.js.map} +0 -0
- /package/dist/{chunk-XXTIKBCU.js.map → chunk-VBPHGPBR.js.map} +0 -0
- /package/dist/{git-repo-CNIKBYPB.js.map → git-repo-364VANDM.js.map} +0 -0
- /package/dist/{logger-7XW3I4XN.js.map → logger-GQCSLSZH.js.map} +0 -0
- /package/dist/{login-RHZDNC74.js.map → login-D6USDG5M.js.map} +0 -0
- /package/dist/{mcp-servers-3SHS2PEJ.js.map → mcp-servers-FZV2P2ZO.js.map} +0 -0
- /package/dist/{skills-OMDIMU7D.js.map → skills-GPGRNV4R.js.map} +0 -0
|
@@ -11,9 +11,10 @@ import {
|
|
|
11
11
|
isGhAvailable,
|
|
12
12
|
isGitRepo,
|
|
13
13
|
parseOwnerRepo
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-BNEE7ZPW.js";
|
|
15
|
+
import "./chunk-2UN5AR7V.js";
|
|
16
|
+
import "./chunk-PI77CUEP.js";
|
|
17
|
+
import "./chunk-VPMN47TL.js";
|
|
17
18
|
import "./chunk-KYLY4DJF.js";
|
|
18
19
|
import "./chunk-2H7UOFLK.js";
|
|
19
20
|
export {
|
|
@@ -29,4 +30,4 @@ export {
|
|
|
29
30
|
isGitRepo,
|
|
30
31
|
parseOwnerRepo
|
|
31
32
|
};
|
|
32
|
-
//# sourceMappingURL=git-repo-
|
|
33
|
+
//# sourceMappingURL=git-repo-364VANDM.js.map
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-7H34LI75.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2UN5AR7V.js";
|
|
8
8
|
import {
|
|
9
9
|
validateEnv
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-PI77CUEP.js";
|
|
11
|
+
import "./chunk-VPMN47TL.js";
|
|
11
12
|
import "./chunk-2H7UOFLK.js";
|
|
12
13
|
|
|
13
14
|
// src/index.ts
|
|
@@ -80,7 +81,7 @@ function parseCliArgs() {
|
|
|
80
81
|
}
|
|
81
82
|
async function loadAuthFromConfig(env) {
|
|
82
83
|
if (env.SHIPYARD_USER_TOKEN) return;
|
|
83
|
-
const { loadAuthToken } = await import("./auth-
|
|
84
|
+
const { loadAuthToken } = await import("./auth-SS7LV5XK.js");
|
|
84
85
|
const auth = await loadAuthToken();
|
|
85
86
|
if (auth.status === "ok") {
|
|
86
87
|
env.SHIPYARD_USER_TOKEN = auth.token;
|
|
@@ -100,23 +101,23 @@ async function loadAuthFromConfig(env) {
|
|
|
100
101
|
async function handleSubcommand() {
|
|
101
102
|
const subcommand = process.argv[2];
|
|
102
103
|
if (subcommand === "login") {
|
|
103
|
-
const { loginCommand } = await import("./login-
|
|
104
|
+
const { loginCommand } = await import("./login-D6USDG5M.js");
|
|
104
105
|
const hasCheck = process.argv.includes("--check");
|
|
105
106
|
await loginCommand({ check: hasCheck });
|
|
106
107
|
return true;
|
|
107
108
|
}
|
|
108
109
|
if (subcommand === "logout") {
|
|
109
|
-
const { logoutCommand } = await import("./logout-
|
|
110
|
+
const { logoutCommand } = await import("./logout-VUNCW5B2.js");
|
|
110
111
|
await logoutCommand();
|
|
111
112
|
return true;
|
|
112
113
|
}
|
|
113
114
|
if (subcommand === "start") {
|
|
114
|
-
const { startCommand } = await import("./start-
|
|
115
|
+
const { startCommand } = await import("./start-I7ZONWK7.js");
|
|
115
116
|
await startCommand();
|
|
116
117
|
return true;
|
|
117
118
|
}
|
|
118
119
|
if (subcommand === "roi") {
|
|
119
|
-
const { roiCommand } = await import("./roi-
|
|
120
|
+
const { roiCommand } = await import("./roi-Y3MX5UW4.js");
|
|
120
121
|
await roiCommand();
|
|
121
122
|
return true;
|
|
122
123
|
}
|
|
@@ -128,7 +129,7 @@ async function main() {
|
|
|
128
129
|
const args = parseCliArgs();
|
|
129
130
|
if (args.serve) {
|
|
130
131
|
await loadAuthFromConfig(env);
|
|
131
|
-
const { serve } = await import("./serve-
|
|
132
|
+
const { serve } = await import("./serve-IVUGCBEE.js");
|
|
132
133
|
return serve({ isDev: env.SHIPYARD_DEV });
|
|
133
134
|
}
|
|
134
135
|
logger.error("Use `shipyard start` to run the daemon. Use --help for usage.");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseArgs } from 'node:util';\nimport { type Env, validateEnv } from './shared/env.js';\nimport { logger } from './shared/logger.js';\nimport { installWasmPanicBuffer } from './shared/wasm-panic-buffer.js';\n\n/**\n * Node defaults to `Error.stackTraceLimit = 10`. Loro wasm panics produce\n * exactly 10-frame wasm chains, which means any JS frames *above* the wasm\n * boundary are silently truncated and never reach `panic-watch.log`.\n * Raising this lets the JS-stack capture (issue #2354) actually see\n * caller-side frames so we can distinguish JS-triggered re-entry from\n * pure-wasm cascade poison.\n */\nError.stackTraceLimit = 50;\n\n/**\n * Install the `console.error` ring buffer BEFORE any `loro-crdt` import\n * resolves. The vendored Loro fork has `console_error_panic_hook` already\n * installed in Rust, which writes the actual panic site (file:line) to\n * `console.error` before the wasm trap fires. Without this Node-side\n * capture, that message is lost and we are stuck guessing among the\n * multiple Rust panic classes that all surface as bare `unreachable`\n * (Invariant #14). The dynamic `await import('./services/serve.js')`\n * below is the first thing that pulls in `@loro-extended/repo` which in\n * turn loads the wasm module — so installing here is well-ordered.\n */\ninstallWasmPanicBuffer();\n\nfunction getVersion(): string {\n try {\n const thisFile = fileURLToPath(import.meta.url);\n const pkgPath = resolve(thisFile, '../../package.json');\n const pkg: { version?: string } = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\ninterface CliArgs {\n serve?: boolean;\n}\n\nfunction parseCliArgs(): CliArgs {\n const { values } = parseArgs({\n options: {\n serve: { type: 'boolean', short: 's' },\n version: { type: 'boolean', short: 'v' },\n help: { type: 'boolean', short: 'h' },\n },\n strict: true,\n });\n\n if (values.version) {\n process.stdout.write(`${getVersion()}\\n`);\n process.exit(0);\n }\n\n if (values.help) {\n logger.info(\n [\n `shipyard v${getVersion()} - Ship together with AI — locally, visibly, confidently`,\n '',\n 'Usage:',\n ' shipyard start [--code CODE] Start daemon (authenticates if needed, opens browser)',\n ' shipyard login Authenticate with Shipyard',\n ' shipyard login --check Check current auth status',\n ' shipyard logout Clear stored credentials',\n '',\n 'Options:',\n ' -v, --version Show version',\n ' -h, --help Show this help',\n '',\n 'Authentication:',\n ' Run `claude auth login` to authenticate with Anthropic (primary method).',\n ' Alternatively, set ANTHROPIC_API_KEY for CI/headless environments.',\n '',\n 'Environment:',\n ' ANTHROPIC_API_KEY API key for Claude (optional, overrides OAuth)',\n ' SHIPYARD_DEV Set to 1 for dev mode (uses ~/.shipyard-dev/)',\n ' SHIPYARD_WEB_URL Override browser URL for auto-open',\n ' LOG_LEVEL Log level: debug, info, warn, error (default: info)',\n ' SHIPYARD_SIGNALING_URL Signaling server WebSocket URL (optional)',\n ' SHIPYARD_USER_TOKEN JWT for signaling auth (optional)',\n ' SHIPYARD_USER_ID User ID for signaling path (optional, from login)',\n ' SHIPYARD_MACHINE_ID Machine identifier (default: os.hostname())',\n ' SHIPYARD_MACHINE_NAME Human-readable machine name (default: os.hostname())',\n ].join('\\n')\n );\n process.exit(0);\n }\n\n return {\n serve: values.serve,\n };\n}\n\nasync function loadAuthFromConfig(env: Env): Promise<void> {\n if (env.SHIPYARD_USER_TOKEN) return;\n\n const { loadAuthToken } = await import('./services/bootstrap/auth.js');\n const auth = await loadAuthToken();\n\n if (auth.status === 'ok') {\n env.SHIPYARD_USER_TOKEN = auth.token;\n env.SHIPYARD_USER_ID = auth.userId;\n env.SHIPYARD_USER_DISPLAY_NAME = auth.displayName;\n if (auth.signalingUrl) {\n env.SHIPYARD_SIGNALING_URL = auth.signalingUrl;\n }\n return;\n }\n\n if (auth.status === 'expired') {\n logger.warn('Auth token expired. Run `shipyard login` to re-authenticate.');\n return;\n }\n\n logger.warn('No auth token found. Run `shipyard login` to authenticate.');\n}\n\nasync function handleSubcommand(): Promise<boolean> {\n const subcommand = process.argv[2];\n\n if (subcommand === 'login') {\n const { loginCommand } = await import('./shared/commands/login.js');\n const hasCheck = process.argv.includes('--check');\n await loginCommand({ check: hasCheck });\n return true;\n }\n\n if (subcommand === 'logout') {\n const { logoutCommand } = await import('./shared/commands/logout.js');\n await logoutCommand();\n return true;\n }\n\n if (subcommand === 'start') {\n const { startCommand } = await import('./shared/commands/start.js');\n await startCommand();\n return true;\n }\n\n if (subcommand === 'roi') {\n const { roiCommand } = await import('./shared/commands/roi.js');\n await roiCommand();\n return true;\n }\n\n return false;\n}\n\nasync function main(): Promise<void> {\n if (await handleSubcommand()) return;\n\n const env = validateEnv();\n const args = parseCliArgs();\n\n if (args.serve) {\n await loadAuthFromConfig(env);\n const { serve } = await import('./services/serve.js');\n return serve({ isDev: env.SHIPYARD_DEV });\n }\n\n logger.error('Use `shipyard start` to run the daemon. Use --help for usage.');\n process.exit(1);\n}\n\nmain().catch((error: unknown) => {\n const errMsg = error instanceof Error ? error.message : String(error);\n const errStack = error instanceof Error ? error.stack : undefined;\n logger.error({ err: errMsg, stack: errStack }, 'Fatal error');\n process.exit(1);\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseArgs } from 'node:util';\nimport { type Env, validateEnv } from './shared/env.js';\nimport { logger } from './shared/logger.js';\nimport { installWasmPanicBuffer } from './shared/wasm-panic-buffer.js';\n\n/**\n * Node defaults to `Error.stackTraceLimit = 10`. Loro wasm panics produce\n * exactly 10-frame wasm chains, which means any JS frames *above* the wasm\n * boundary are silently truncated and never reach `panic-watch.log`.\n * Raising this lets the JS-stack capture (issue #2354) actually see\n * caller-side frames so we can distinguish JS-triggered re-entry from\n * pure-wasm cascade poison.\n */\nError.stackTraceLimit = 50;\n\n/**\n * Install the `console.error` ring buffer BEFORE any `loro-crdt` import\n * resolves. The vendored Loro fork has `console_error_panic_hook` already\n * installed in Rust, which writes the actual panic site (file:line) to\n * `console.error` before the wasm trap fires. Without this Node-side\n * capture, that message is lost and we are stuck guessing among the\n * multiple Rust panic classes that all surface as bare `unreachable`\n * (Invariant #14). The dynamic `await import('./services/serve.js')`\n * below is the first thing that pulls in `@loro-extended/repo` which in\n * turn loads the wasm module — so installing here is well-ordered.\n */\ninstallWasmPanicBuffer();\n\nfunction getVersion(): string {\n try {\n const thisFile = fileURLToPath(import.meta.url);\n const pkgPath = resolve(thisFile, '../../package.json');\n const pkg: { version?: string } = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\ninterface CliArgs {\n serve?: boolean;\n}\n\nfunction parseCliArgs(): CliArgs {\n const { values } = parseArgs({\n options: {\n serve: { type: 'boolean', short: 's' },\n version: { type: 'boolean', short: 'v' },\n help: { type: 'boolean', short: 'h' },\n },\n strict: true,\n });\n\n if (values.version) {\n process.stdout.write(`${getVersion()}\\n`);\n process.exit(0);\n }\n\n if (values.help) {\n logger.info(\n [\n `shipyard v${getVersion()} - Ship together with AI — locally, visibly, confidently`,\n '',\n 'Usage:',\n ' shipyard start [--code CODE] Start daemon (authenticates if needed, opens browser)',\n ' shipyard login Authenticate with Shipyard',\n ' shipyard login --check Check current auth status',\n ' shipyard logout Clear stored credentials',\n '',\n 'Options:',\n ' -v, --version Show version',\n ' -h, --help Show this help',\n '',\n 'Authentication:',\n ' Run `claude auth login` to authenticate with Anthropic (primary method).',\n ' Alternatively, set ANTHROPIC_API_KEY for CI/headless environments.',\n '',\n 'Environment:',\n ' ANTHROPIC_API_KEY API key for Claude (optional, overrides OAuth)',\n ' SHIPYARD_DEV Set to 1 for dev mode (uses ~/.shipyard-dev/)',\n ' SHIPYARD_WEB_URL Override browser URL for auto-open',\n ' LOG_LEVEL Log level: debug, info, warn, error (default: info)',\n ' SHIPYARD_SIGNALING_URL Signaling server WebSocket URL (optional)',\n ' SHIPYARD_USER_TOKEN JWT for signaling auth (optional)',\n ' SHIPYARD_USER_ID User ID for signaling path (optional, from login)',\n ' SHIPYARD_MACHINE_ID Machine identifier (default: os.hostname())',\n ' SHIPYARD_MACHINE_NAME Human-readable machine name (default: os.hostname())',\n ].join('\\n')\n );\n process.exit(0);\n }\n\n return {\n serve: values.serve,\n };\n}\n\nasync function loadAuthFromConfig(env: Env): Promise<void> {\n if (env.SHIPYARD_USER_TOKEN) return;\n\n const { loadAuthToken } = await import('./services/bootstrap/auth.js');\n const auth = await loadAuthToken();\n\n if (auth.status === 'ok') {\n env.SHIPYARD_USER_TOKEN = auth.token;\n env.SHIPYARD_USER_ID = auth.userId;\n env.SHIPYARD_USER_DISPLAY_NAME = auth.displayName;\n if (auth.signalingUrl) {\n env.SHIPYARD_SIGNALING_URL = auth.signalingUrl;\n }\n return;\n }\n\n if (auth.status === 'expired') {\n logger.warn('Auth token expired. Run `shipyard login` to re-authenticate.');\n return;\n }\n\n logger.warn('No auth token found. Run `shipyard login` to authenticate.');\n}\n\nasync function handleSubcommand(): Promise<boolean> {\n const subcommand = process.argv[2];\n\n if (subcommand === 'login') {\n const { loginCommand } = await import('./shared/commands/login.js');\n const hasCheck = process.argv.includes('--check');\n await loginCommand({ check: hasCheck });\n return true;\n }\n\n if (subcommand === 'logout') {\n const { logoutCommand } = await import('./shared/commands/logout.js');\n await logoutCommand();\n return true;\n }\n\n if (subcommand === 'start') {\n const { startCommand } = await import('./shared/commands/start.js');\n await startCommand();\n return true;\n }\n\n if (subcommand === 'roi') {\n const { roiCommand } = await import('./shared/commands/roi.js');\n await roiCommand();\n return true;\n }\n\n return false;\n}\n\nasync function main(): Promise<void> {\n if (await handleSubcommand()) return;\n\n const env = validateEnv();\n const args = parseCliArgs();\n\n if (args.serve) {\n await loadAuthFromConfig(env);\n const { serve } = await import('./services/serve.js');\n return serve({ isDev: env.SHIPYARD_DEV });\n }\n\n logger.error('Use `shipyard start` to run the daemon. Use --help for usage.');\n process.exit(1);\n}\n\nmain().catch((error: unknown) => {\n const errMsg = error instanceof Error ? error.message : String(error);\n const errStack = error instanceof Error ? error.stack : undefined;\n logger.error({ err: errMsg, stack: errStack }, 'Fatal error');\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
flushLogger,
|
|
5
5
|
getLogFilePath,
|
|
6
6
|
logger
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-2UN5AR7V.js";
|
|
8
|
+
import "./chunk-PI77CUEP.js";
|
|
9
|
+
import "./chunk-VPMN47TL.js";
|
|
9
10
|
import "./chunk-2H7UOFLK.js";
|
|
10
11
|
export {
|
|
11
12
|
createChildLogger,
|
|
@@ -13,4 +14,4 @@ export {
|
|
|
13
14
|
getLogFilePath,
|
|
14
15
|
logger
|
|
15
16
|
};
|
|
16
|
-
//# sourceMappingURL=logger-
|
|
17
|
+
//# sourceMappingURL=logger-GQCSLSZH.js.map
|
|
@@ -3,17 +3,18 @@ import {
|
|
|
3
3
|
ensureAuthenticated,
|
|
4
4
|
getSignalingUrl,
|
|
5
5
|
loginCommand
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-3CAEALVL.js";
|
|
7
7
|
import "./chunk-6TYNQ5KQ.js";
|
|
8
8
|
import "./chunk-EHQITHQX.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-IISLTKYY.js";
|
|
10
|
+
import "./chunk-2UN5AR7V.js";
|
|
11
|
+
import "./chunk-2J3WSIAF.js";
|
|
12
|
+
import "./chunk-PI77CUEP.js";
|
|
13
|
+
import "./chunk-VPMN47TL.js";
|
|
13
14
|
import "./chunk-2H7UOFLK.js";
|
|
14
15
|
export {
|
|
15
16
|
ensureAuthenticated,
|
|
16
17
|
getSignalingUrl,
|
|
17
18
|
loginCommand
|
|
18
19
|
};
|
|
19
|
-
//# sourceMappingURL=login-
|
|
20
|
+
//# sourceMappingURL=login-D6USDG5M.js.map
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
print
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-IISLTKYY.js";
|
|
5
|
+
import "./chunk-2UN5AR7V.js";
|
|
5
6
|
import {
|
|
6
7
|
deleteConfig,
|
|
7
8
|
getConfigPath
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-2J3WSIAF.js";
|
|
10
|
+
import "./chunk-PI77CUEP.js";
|
|
11
|
+
import "./chunk-VPMN47TL.js";
|
|
11
12
|
import "./chunk-2H7UOFLK.js";
|
|
12
13
|
|
|
13
14
|
// src/shared/commands/logout.ts
|
|
@@ -22,4 +23,4 @@ async function logoutCommand() {
|
|
|
22
23
|
export {
|
|
23
24
|
logoutCommand
|
|
24
25
|
};
|
|
25
|
-
//# sourceMappingURL=logout-
|
|
26
|
+
//# sourceMappingURL=logout-VUNCW5B2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/commands/logout.ts"],"sourcesContent":["import { deleteConfig, getConfigPath } from '../../services/bootstrap/auth.js';\nimport { print } from './output.js';\n\nexport async function logoutCommand(): Promise<void> {\n const deleted = await deleteConfig();\n if (deleted) {\n print(`Logged out. Token removed from ${getConfigPath()}`);\n } else {\n print('Not logged in (no config file found).');\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/shared/commands/logout.ts"],"sourcesContent":["import { deleteConfig, getConfigPath } from '../../services/bootstrap/auth.js';\nimport { print } from './output.js';\n\nexport async function logoutCommand(): Promise<void> {\n const deleted = await deleteConfig();\n if (deleted) {\n print(`Logged out. Token removed from ${getConfigPath()}`);\n } else {\n print('Not logged in (no config file found).');\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAGA,eAAsB,gBAA+B;AACnD,QAAM,UAAU,MAAM,aAAa;AACnC,MAAI,SAAS;AACX,UAAM,kCAAkC,cAAc,CAAC,EAAE;AAAA,EAC3D,OAAO;AACL,UAAM,uCAAuC;AAAA,EAC/C;AACF;","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
detectMCPServers,
|
|
4
|
+
redactArgs,
|
|
5
|
+
redactEnv
|
|
6
|
+
} from "./chunk-GIFN3IPT.js";
|
|
7
|
+
import "./chunk-2UN5AR7V.js";
|
|
8
|
+
import "./chunk-PI77CUEP.js";
|
|
9
|
+
import "./chunk-VPMN47TL.js";
|
|
10
|
+
import "./chunk-2H7UOFLK.js";
|
|
11
|
+
export {
|
|
12
|
+
detectMCPServers,
|
|
13
|
+
redactArgs,
|
|
14
|
+
redactEnv
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=mcp-servers-FZV2P2ZO.js.map
|
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
import "./chunk-EHQITHQX.js";
|
|
9
9
|
import {
|
|
10
10
|
logger
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-2UN5AR7V.js";
|
|
12
|
+
import "./chunk-PI77CUEP.js";
|
|
13
|
+
import "./chunk-VPMN47TL.js";
|
|
13
14
|
import "./chunk-2H7UOFLK.js";
|
|
14
15
|
|
|
15
16
|
// src/shared/commands/roi.ts
|
|
@@ -677,4 +678,4 @@ function renderReport(report, format) {
|
|
|
677
678
|
export {
|
|
678
679
|
roiCommand
|
|
679
680
|
};
|
|
680
|
-
//# sourceMappingURL=roi-
|
|
681
|
+
//# sourceMappingURL=roi-Y3MX5UW4.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/commands/roi.ts","../../../packages/roi-aggregator/src/joins/task-context.ts","../../../packages/roi-aggregator/src/metrics/cost-per-pr.ts","../../../packages/roi-aggregator/src/metrics/efficiency-curve.ts","../../../packages/roi-aggregator/src/metrics/funnel.ts","../../../packages/roi-aggregator/src/metrics/leverage.ts","../../../packages/roi-aggregator/src/metrics/per-engineer.ts","../../../packages/roi-aggregator/src/reports/build-report.ts","../../../packages/roi-aggregator/src/reports/csv.ts","../../../packages/roi-aggregator/src/reports/html.ts","../../../packages/roi-aggregator/src/reports/json.ts","../../../packages/roi-aggregator/src/sources/d1.ts","../../../packages/roi-aggregator/src/sources/tasks-json.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport { parseArgs } from 'node:util';\nimport {\n buildRoiReport,\n buildTaskContexts,\n defaultTasksJsonPath,\n fetchRoiEvents,\n type RawTaskRecord,\n type RoiEvent,\n readTasksJson,\n renderCsvReport,\n renderHtmlReport,\n renderJsonReport,\n} from '@shipyard/roi-aggregator';\nimport { logger } from '../logger.js';\n\nexport interface RoiCommandArgs {\n since?: string;\n until?: string;\n format: 'json' | 'html' | 'csv';\n output?: string;\n workerUrl: string;\n token: string;\n tasksJsonPath?: string;\n}\n\nfunction parseIsoToMs(raw: string | undefined): number | undefined {\n if (!raw) return undefined;\n const ms = Date.parse(raw);\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nexport async function roiCommand(argv: string[] = process.argv.slice(3)): Promise<void> {\n const { values } = parseArgs({\n args: argv,\n options: {\n since: { type: 'string' },\n until: { type: 'string' },\n format: { type: 'string', default: 'json' },\n output: { type: 'string', short: 'o' },\n 'worker-url': { type: 'string' },\n token: { type: 'string' },\n 'tasks-json': { type: 'string' },\n 'cwd-prefix': { type: 'string', multiple: true },\n help: { type: 'boolean', short: 'h' },\n },\n strict: true,\n allowPositionals: false,\n });\n\n if (values.help) {\n process.stdout.write(\n [\n 'Usage: shipyard roi [options]',\n '',\n ' --since DATE Start of window (ISO date). Default: 30 days ago.',\n ' --until DATE End of window (ISO date). Default: now.',\n ' --format FORMAT Output format: json, html, csv. Default: json.',\n ' --output FILE, -o Write to file instead of stdout.',\n ' --worker-url URL Metrics worker base URL. Env: SHIPYARD_METRICS_URL.',\n ' --token TOKEN JWT bearer token. Env: SHIPYARD_USER_TOKEN.',\n ' --tasks-json PATH Override path to local tasks.json.',\n ' --cwd-prefix PATH Only include tasks whose cwd starts with PATH. Repeatable for worktrees.',\n ' -h, --help Show this help.',\n '',\n ].join('\\n')\n );\n return;\n }\n\n const format = normalizeFormat(values.format);\n if (!format) {\n logger.error('Invalid --format. Expected one of: json, html, csv.');\n process.exit(1);\n }\n\n const workerUrl = values['worker-url'] ?? process.env.SHIPYARD_METRICS_URL;\n const token = values.token ?? process.env.SHIPYARD_USER_TOKEN;\n if (!workerUrl || !token) {\n logger.error(\n 'Missing required --worker-url / SHIPYARD_METRICS_URL and --token / SHIPYARD_USER_TOKEN.'\n );\n process.exit(1);\n }\n\n const until = parseIsoToMs(values.until) ?? Date.now();\n const since = parseIsoToMs(values.since) ?? until - 30 * 24 * 60 * 60 * 1000;\n const sinceIso = new Date(since).toISOString();\n const untilIso = new Date(until).toISOString();\n\n const [{ roiEvents }, tasks] = await Promise.all([\n fetchRoiEvents({ workerUrl, authToken: token, since, until }),\n readTasksJson(values['tasks-json'] ?? defaultTasksJsonPath()),\n ]);\n\n const cwdPrefixes = normalizeCwdPrefixes(values['cwd-prefix']);\n const { records: scopedRecords, events: scopedEvents } = applyCwdScope(\n tasks.tasks,\n roiEvents,\n cwdPrefixes\n );\n if (cwdPrefixes.length > 0) {\n logger.info(\n {\n cwdPrefixes,\n recordsKept: Object.keys(scopedRecords).length,\n recordsTotal: Object.keys(tasks.tasks).length,\n eventsKept: scopedEvents.length,\n eventsTotal: roiEvents.length,\n },\n 'roi report scoped by --cwd-prefix'\n );\n }\n\n const ctxs = buildTaskContexts(scopedEvents, scopedRecords);\n const report = buildRoiReport(ctxs, { since: sinceIso, until: untilIso });\n\n const rendered = renderReport(report, format);\n if (values.output) {\n await fs.writeFile(values.output, rendered, 'utf8');\n logger.info({ output: values.output, format }, 'roi report written');\n } else {\n process.stdout.write(rendered);\n if (!rendered.endsWith('\\n')) process.stdout.write('\\n');\n }\n}\n\nfunction normalizeCwdPrefixes(raw: string[] | undefined): string[] {\n if (!raw || raw.length === 0) return [];\n return raw.map((p) => {\n const expanded = p.startsWith('~/') || p === '~' ? p.replace(/^~/, homedir()) : p;\n return resolve(expanded);\n });\n}\n\nfunction applyCwdScope(\n records: Record<string, RawTaskRecord>,\n events: RoiEvent[],\n cwdPrefixes: string[]\n): { records: Record<string, RawTaskRecord>; events: RoiEvent[] } {\n if (cwdPrefixes.length === 0) return { records, events };\n const scoped: Record<string, RawTaskRecord> = {};\n for (const [taskId, record] of Object.entries(records)) {\n const recordCwd = record.cwd ? resolve(record.cwd) : '';\n const matches = cwdPrefixes.some(\n (prefix) => recordCwd === prefix || recordCwd.startsWith(`${prefix}/`)\n );\n if (matches) scoped[taskId] = record;\n }\n const allowed = new Set(Object.keys(scoped));\n const filteredEvents = events.filter((event) => allowed.has(event.taskId));\n return { records: scoped, events: filteredEvents };\n}\n\nfunction normalizeFormat(raw: string | undefined): 'json' | 'html' | 'csv' | null {\n if (raw === 'json' || raw === 'html' || raw === 'csv') return raw;\n return null;\n}\n\nfunction renderReport(\n report: Parameters<typeof renderJsonReport>[0],\n format: 'json' | 'html' | 'csv'\n): string {\n switch (format) {\n case 'json':\n return renderJsonReport(report);\n case 'html':\n return renderHtmlReport(report);\n case 'csv':\n return renderCsvReport(report);\n default: {\n const _exhaustive: never = format;\n throw new Error(`unhandled format: ${String(_exhaustive)}`);\n }\n }\n}\n","import {\n assertNeverEvent,\n type CommitAttributedEvent,\n type PrAttributedEvent,\n type PrMergedEvent,\n type RoiEvent,\n type TaskEndedEvent,\n type TaskStartedEvent,\n} from '../schemas/events';\nimport type { RawTaskRecord } from '../sources/tasks-json';\n\nexport interface TaskContext {\n taskId: string;\n userId: string;\n record: RawTaskRecord | null;\n started: TaskStartedEvent | null;\n ended: TaskEndedEvent | null;\n commits: CommitAttributedEvent[];\n pr: PrAttributedEvent | null;\n merged: PrMergedEvent | null;\n}\n\nfunction emptyContext(\n taskId: string,\n userId: string,\n records: Record<string, RawTaskRecord>\n): TaskContext {\n return {\n taskId,\n userId,\n record: records[taskId] ?? null,\n started: null,\n ended: null,\n commits: [],\n pr: null,\n merged: null,\n };\n}\n\nfunction applyEvent(ctx: TaskContext, event: RoiEvent): void {\n switch (event.kind) {\n case 'task_started':\n if (!ctx.started || event.timestamp < ctx.started.timestamp) ctx.started = event;\n break;\n case 'task_ended':\n if (!ctx.ended || event.timestamp > ctx.ended.timestamp) ctx.ended = event;\n break;\n case 'commit_attributed':\n ctx.commits.push(event);\n break;\n case 'pr_attributed':\n if (!ctx.pr) ctx.pr = event;\n break;\n case 'pr_merged':\n if (!ctx.merged || event.timestamp < ctx.merged.timestamp) ctx.merged = event;\n break;\n default:\n assertNeverEvent(event);\n }\n}\n\nexport function buildTaskContexts(\n events: RoiEvent[],\n records: Record<string, RawTaskRecord>\n): TaskContext[] {\n const byTask = new Map<string, TaskContext>();\n\n for (const event of events) {\n let ctx = byTask.get(event.taskId);\n if (!ctx) {\n ctx = emptyContext(event.taskId, event.userId, records);\n byTask.set(event.taskId, ctx);\n }\n applyEvent(ctx, event);\n }\n\n for (const record of Object.values(records)) {\n if (!byTask.has(record.taskId)) {\n byTask.set(record.taskId, emptyContext(record.taskId, '', records));\n }\n }\n\n return [...byTask.values()];\n}\n","import type { TaskContext } from '../joins/task-context';\n\nexport function computeCostPerMergedPr(ctxs: TaskContext[]): number {\n let totalCost = 0;\n let mergedPrs = 0;\n for (const ctx of ctxs) {\n const hasMergedPr =\n ctx.record?.status === 'completed' && (ctx.pr != null || ctx.record?.prUrl != null);\n if (!hasMergedPr) continue;\n mergedPrs += 1;\n const cost = ctx.ended?.totalCostUsd ?? ctx.record?.totalCostUsd ?? sumCommitCosts(ctx);\n totalCost += cost;\n }\n return mergedPrs === 0 ? 0 : Number((totalCost / mergedPrs).toFixed(4));\n}\n\nfunction sumCommitCosts(ctx: TaskContext): number {\n return ctx.commits.reduce((sum, c) => sum + c.costUsd, 0);\n}\n","import type { TaskContext } from '../joins/task-context';\nimport type { TimeSeriesMetric, TimeSeriesPoint } from '../schemas/report';\n\n/** ISO 8601 week number — week containing the first Thursday of the year is week 1. */\nfunction isoWeek(ms: number): string {\n const d = new Date(ms);\n const thursday = new Date(d);\n thursday.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));\n const yearStart = Date.UTC(thursday.getUTCFullYear(), 0, 1);\n const weekNo = Math.ceil(((thursday.getTime() - yearStart) / 86_400_000 + 1) / 7);\n return `${thursday.getUTCFullYear()}-W${String(weekNo).padStart(2, '0')}`;\n}\n\nexport function computeTokensPerMergedPrWeekly(ctxs: TaskContext[]): TimeSeriesMetric {\n const buckets = new Map<string, { tokens: number; prs: number }>();\n for (const ctx of ctxs) {\n const merged =\n ctx.record?.status === 'completed' && (ctx.pr != null || ctx.record?.prUrl != null);\n if (!merged) continue;\n const ts = ctx.ended?.timestamp ?? ctx.record?.updatedAt;\n if (ts == null) continue;\n const bucket = isoWeek(ts);\n const cur = buckets.get(bucket) ?? { tokens: 0, prs: 0 };\n cur.tokens += ctx.ended?.totalOutputTokens ?? ctx.record?.totalOutputTokens ?? 0;\n cur.prs += 1;\n buckets.set(bucket, cur);\n }\n const series: TimeSeriesPoint[] = [...buckets.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([week, { tokens, prs }]) => ({\n bucket: week,\n value: prs === 0 ? 0 : Math.round(tokens / prs),\n denominator: prs,\n }));\n return { metric: 'tokens_per_pr', unit: 'tokens', bucketSize: 'week', series };\n}\n\nexport function computeConcurrentTasksWeekly(ctxs: TaskContext[]): TimeSeriesMetric {\n const buckets = new Map<string, { peak: number }>();\n for (const ctx of ctxs) {\n const ts = ctx.started?.timestamp ?? ctx.record?.createdAt;\n if (ts == null) continue;\n const active = ctx.started?.activeTaskCount ?? 0;\n const bucket = isoWeek(ts);\n const cur = buckets.get(bucket) ?? { peak: 0 };\n cur.peak = Math.max(cur.peak, active);\n buckets.set(bucket, cur);\n }\n const series: TimeSeriesPoint[] = [...buckets.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([week, { peak }]) => ({ bucket: week, value: peak }));\n return { metric: 'concurrent_tasks', unit: 'count', bucketSize: 'week', series };\n}\n","import type { TaskContext } from '../joins/task-context';\nimport type { TaskOutcomeFunnel } from '../schemas/report';\n\nexport function computeFunnel(ctxs: TaskContext[]): TaskOutcomeFunnel {\n let tasksStarted = 0;\n let producedCommits = 0;\n let openedPr = 0;\n let merged = 0;\n let abandoned = 0;\n\n for (const ctx of ctxs) {\n if (ctx.started || ctx.record?.taskStartedAt != null) tasksStarted++;\n if (ctx.commits.length > 0) producedCommits++;\n if (ctx.pr || ctx.record?.prUrl) openedPr++;\n if (ctx.merged != null || ctx.record?.mergedAt != null) merged++;\n if (ctx.record?.abandonedAt != null) abandoned++;\n }\n\n return {\n tasksStarted,\n producedCommits,\n openedPr,\n merged,\n abandoned,\n revertedWithin30d: 0,\n };\n}\n","import type { TaskContext } from '../joins/task-context';\n\nexport const HUMAN_BASELINE_LEVERAGE = 1.0;\n\nexport interface LeverageSummary {\n peakByUser: Record<string, number>;\n avgPeak: number;\n humanBaseline: number;\n}\n\nexport function computePeakLeverage(ctxs: TaskContext[]): LeverageSummary {\n const peakByUser: Record<string, number> = {};\n for (const ctx of ctxs) {\n const user = ctx.userId || 'unknown';\n const count = ctx.started?.activeTaskCount ?? 0;\n peakByUser[user] = Math.max(peakByUser[user] ?? 0, count);\n }\n const values = Object.values(peakByUser);\n const avgPeak =\n values.length === 0\n ? 0\n : Number((values.reduce((a, b) => a + b, 0) / values.length).toFixed(2));\n return { peakByUser, avgPeak, humanBaseline: HUMAN_BASELINE_LEVERAGE };\n}\n","import type { TaskContext } from '../joins/task-context';\nimport type { EngineerScorecardRow } from '../schemas/report';\n\nfunction classifySignal(sampleSize: number, peakParallel: number): EngineerScorecardRow['signal'] {\n if (sampleSize < 3) return 'insufficient-data';\n if (sampleSize < 10) return 'modest';\n if (sampleSize < 20) return peakParallel >= 3 ? 'strong' : 'modest';\n if (peakParallel >= 5) return 'outlier-high';\n if (peakParallel >= 3) return 'strong';\n return 'modest';\n}\n\nfunction engineerRow(userId: string, ctxs: TaskContext[]): EngineerScorecardRow {\n const mergedPrs = ctxs.filter(\n (c) => c.record?.status === 'completed' && (c.pr || c.record?.prUrl)\n );\n const peakParallel = ctxs.reduce((max, c) => Math.max(max, c.started?.activeTaskCount ?? 0), 0);\n const sampleSize = ctxs.length;\n const costPerPr =\n mergedPrs.length === 0\n ? 0\n : Number(\n (\n mergedPrs.reduce((sum, c) => sum + (c.ended?.totalCostUsd ?? 0), 0) / mergedPrs.length\n ).toFixed(4)\n );\n return {\n userId,\n peakParallel,\n costPerPrUsd: costPerPr,\n revertRate: 0,\n sampleSize,\n signal: classifySignal(sampleSize, peakParallel),\n };\n}\n\nexport function computeEngineerScorecard(ctxs: TaskContext[]): EngineerScorecardRow[] {\n const byUser = new Map<string, TaskContext[]>();\n for (const ctx of ctxs) {\n const user = ctx.userId || 'unknown';\n const list = byUser.get(user) ?? [];\n list.push(ctx);\n byUser.set(user, list);\n }\n\n const rows: EngineerScorecardRow[] = [];\n for (const [userId, userCtxs] of byUser.entries()) {\n rows.push(engineerRow(userId, userCtxs));\n }\n return rows.sort((a, b) => b.sampleSize - a.sampleSize);\n}\n","import type { TaskContext } from '../joins/task-context';\nimport { computeCostPerMergedPr } from '../metrics/cost-per-pr';\nimport {\n computeConcurrentTasksWeekly,\n computeTokensPerMergedPrWeekly,\n} from '../metrics/efficiency-curve';\nimport { computeFunnel } from '../metrics/funnel';\nimport { computePeakLeverage } from '../metrics/leverage';\nimport { computeEngineerScorecard } from '../metrics/per-engineer';\nimport { REPORT_SCHEMA_VERSION, type RoiReport, type RoiWarning } from '../schemas/report';\n\nexport interface BuildReportOptions {\n since: string;\n until: string;\n}\n\nexport function buildRoiReport(ctxs: TaskContext[], opts: BuildReportOptions): RoiReport {\n const warnings: RoiWarning[] = [];\n if (ctxs.length === 0) {\n warnings.push({\n code: 'no_data',\n message:\n 'No ROI events found in the selected window. Ensure the daemon is running and events are being ingested.',\n context: {},\n });\n }\n\n const leverage = computePeakLeverage(ctxs);\n const funnel = computeFunnel(ctxs);\n const costPerMergedPr = computeCostPerMergedPr(ctxs);\n const byEngineer = computeEngineerScorecard(ctxs);\n const totalCost = ctxs.reduce(\n (sum, c) => sum + (c.ended?.totalCostUsd ?? c.record?.totalCostUsd ?? 0),\n 0\n );\n const totalTokens = ctxs.reduce(\n (sum, c) => sum + (c.ended?.totalOutputTokens ?? c.record?.totalOutputTokens ?? 0),\n 0\n );\n const engineersActive = new Set(\n ctxs.map((c) => c.userId || 'unknown').filter((u) => u !== 'unknown')\n ).size;\n\n for (const row of byEngineer) {\n if (row.sampleSize < 20) {\n warnings.push({\n code: 'small_sample',\n message: `Engineer ${row.userId} has only ${row.sampleSize} tasks in window. Deltas not statistically significant.`,\n context: { userId: row.userId, sampleSize: row.sampleSize },\n });\n }\n }\n\n return {\n schemaVersion: REPORT_SCHEMA_VERSION,\n generatedAt: new Date().toISOString(),\n window: { since: opts.since, until: opts.until },\n totals: {\n tasksStarted: funnel.tasksStarted,\n tasksAbandoned: funnel.abandoned,\n tasksShipped: funnel.merged,\n costUsd: Number(totalCost.toFixed(4)),\n tokens: totalTokens,\n engineersActive,\n },\n kpis: {\n costPerMergedPr,\n peakLeverageAvg: leverage.avgPeak,\n peakLeverageHumanBaseline: leverage.humanBaseline,\n revertRateShipyard: 0,\n },\n series: {\n tokensPerPrWeekly: computeTokensPerMergedPrWeekly(ctxs),\n concurrentTasksWeekly: computeConcurrentTasksWeekly(ctxs),\n },\n funnel,\n byEngineer,\n facets: [],\n warnings,\n };\n}\n","import type { RoiReport } from '../schemas/report';\n\nfunction csvEscape(value: string | number): string {\n const s = String(value);\n if (s.includes(',') || s.includes('\"') || s.includes('\\n')) {\n return `\"${s.replace(/\"/g, '\"\"')}\"`;\n }\n return s;\n}\n\nexport function renderCsvReport(report: RoiReport): string {\n const lines: string[] = [];\n lines.push('section,key,value');\n lines.push(`totals,tasksStarted,${report.totals.tasksStarted}`);\n lines.push(`totals,tasksAbandoned,${report.totals.tasksAbandoned}`);\n lines.push(`totals,tasksShipped,${report.totals.tasksShipped}`);\n lines.push(`totals,costUsd,${report.totals.costUsd}`);\n lines.push(`totals,tokens,${report.totals.tokens}`);\n lines.push(`totals,engineersActive,${report.totals.engineersActive}`);\n lines.push(`kpi,costPerMergedPr,${report.kpis.costPerMergedPr}`);\n lines.push(`kpi,peakLeverageAvg,${report.kpis.peakLeverageAvg}`);\n lines.push(`funnel,tasksStarted,${report.funnel.tasksStarted}`);\n lines.push(`funnel,producedCommits,${report.funnel.producedCommits}`);\n lines.push(`funnel,openedPr,${report.funnel.openedPr}`);\n lines.push(`funnel,merged,${report.funnel.merged}`);\n lines.push(`funnel,abandoned,${report.funnel.abandoned}`);\n\n lines.push('');\n lines.push('engineer,sampleSize,peakParallel,costPerPrUsd,revertRate,signal');\n for (const row of report.byEngineer) {\n lines.push(\n [\n csvEscape(row.userId),\n row.sampleSize,\n row.peakParallel,\n row.costPerPrUsd,\n row.revertRate,\n row.signal,\n ].join(',')\n );\n }\n\n return `${lines.join('\\n')}\\n`;\n}\n","import type { RoiReport } from '../schemas/report';\n\nexport function renderHtmlReport(report: RoiReport): string {\n const data = JSON.stringify(report).replace(/</g, '\\\\u003c');\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Shipyard ROI — ${report.window.since} → ${report.window.until}</title>\n<style>\n :root { color-scheme: light dark; --text:#e7e5e4; --muted:#a8a29e; --bg:#0c0a09; --surface:#1c1917; --border:#292524; --ok:#1d9e75; --warn:#f0a030; --danger:#e24b4a; }\n @media (prefers-color-scheme: light) { :root { --text:#1c1917; --muted:#57534e; --bg:#fafaf9; --surface:#ffffff; --border:#e7e5e4; } }\n body { font-family: system-ui, sans-serif; background: var(--bg); color: var(--text); margin: 0; padding: 32px; max-width: 1100px; margin-left: auto; margin-right: auto; line-height: 1.5; }\n h1 { font-size: 22px; font-weight: 500; margin: 0 0 4px 0; }\n .subtitle { font-size: 12px; color: var(--muted); margin-bottom: 24px; font-family: ui-monospace, monospace; }\n .kpi-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:12px; margin-bottom:20px; }\n .kpi { background: var(--surface); padding: 16px; border-radius: 12px; border: 1px solid var(--border); }\n .kpi .label { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); }\n .kpi .value { font-size: 28px; font-weight: 500; margin-top: 4px; }\n .kpi .baseline { font-size: 11px; color: var(--muted); margin-top: 4px; font-family: ui-monospace, monospace; }\n .panel { background: var(--surface); padding: 14px 16px; border-radius: 12px; border: 1px solid var(--border); margin-bottom: 16px; }\n .panel h2 { font-size: 14px; font-weight: 500; margin: 0 0 12px 0; }\n table { width: 100%; border-collapse: collapse; font-size: 13px; }\n th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); border-bottom: 1px solid var(--border); padding: 8px 10px; font-weight: 500; }\n td { padding: 8px 10px; border-bottom: 1px solid var(--border); font-variant-numeric: tabular-nums; }\n .funnel-row { display: grid; grid-template-columns: 1fr 80px; gap: 8px; align-items: center; margin-bottom: 6px; }\n .funnel-bar { height: 10px; background: color-mix(in srgb, var(--ok) 25%, transparent); border-radius: 3px; overflow: hidden; }\n .funnel-fill { height: 100%; background: var(--ok); }\n .funnel-fill.revert { background: var(--danger); }\n .funnel-label { font-size: 12px; color: var(--muted); }\n .funnel-count { font-family: ui-monospace, monospace; font-size: 12px; text-align: right; }\n .warn { background: color-mix(in srgb, var(--warn) 12%, transparent); color: var(--warn); padding: 10px 12px; border-radius: 8px; font-size: 12px; margin-bottom: 8px; }\n .chart-wrap { position: relative; height: 240px; }\n</style>\n</head>\n<body>\n <h1>Shipyard ROI — ${escapeHtml(report.window.since)} → ${escapeHtml(report.window.until)}</h1>\n <div class=\"subtitle\">Generated ${escapeHtml(report.generatedAt)} · ${report.totals.engineersActive} engineer(s) · schema v${report.schemaVersion}</div>\n\n <div class=\"kpi-grid\">\n <div class=\"kpi\"><div class=\"label\">Cost / merged PR</div><div class=\"value\">$${report.kpis.costPerMergedPr.toFixed(2)}</div></div>\n <div class=\"kpi\"><div class=\"label\">Peak leverage</div><div class=\"value\">${report.kpis.peakLeverageAvg.toFixed(1)}×</div><div class=\"baseline\">vs ${report.kpis.peakLeverageHumanBaseline.toFixed(1)}× human baseline</div></div>\n <div class=\"kpi\"><div class=\"label\">Tasks shipped</div><div class=\"value\">${report.totals.tasksShipped}</div></div>\n <div class=\"kpi\"><div class=\"label\">Token spend</div><div class=\"value\">$${report.totals.costUsd.toFixed(2)}</div></div>\n </div>\n\n <div class=\"panel\">\n <h2>Task outcome funnel</h2>\n ${renderFunnel(report)}\n </div>\n\n <div class=\"panel\">\n <h2>Tokens per merged PR (weekly)</h2>\n <div class=\"chart-wrap\"><canvas id=\"chart-tokens\"></canvas></div>\n </div>\n\n <div class=\"panel\">\n <h2>Peak concurrent tasks (weekly)</h2>\n <div class=\"chart-wrap\"><canvas id=\"chart-concurrent\"></canvas></div>\n </div>\n\n <div class=\"panel\">\n <h2>Per-engineer scorecard</h2>\n <table>\n <thead><tr><th>Engineer</th><th>Tasks</th><th>Peak ‖</th><th>$ / PR</th><th>Signal</th></tr></thead>\n <tbody>\n ${report.byEngineer\n .map(\n (row) =>\n `<tr><td>${escapeHtml(row.userId)}</td><td>${row.sampleSize}</td><td>${row.peakParallel}</td><td>$${row.costPerPrUsd.toFixed(2)}</td><td>${escapeHtml(row.signal)}</td></tr>`\n )\n .join('')}\n </tbody>\n </table>\n </div>\n\n ${\n report.warnings.length > 0\n ? `<div class=\"panel\"><h2>Warnings</h2>${report.warnings.map((w) => `<div class=\"warn\">${escapeHtml(w.message)}</div>`).join('')}</div>`\n : ''\n }\n\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js\" crossorigin=\"anonymous\"></script>\n<script>\n const report = ${data};\n const textColor = getComputedStyle(document.documentElement).getPropertyValue('--text').trim();\n const gridColor = getComputedStyle(document.documentElement).getPropertyValue('--border').trim();\n Chart.defaults.color = textColor;\n\n const tokensEl = document.getElementById('chart-tokens');\n if (tokensEl) {\n new Chart(tokensEl, {\n type: 'line',\n data: {\n labels: report.series.tokensPerPrWeekly.series.map(p => p.bucket),\n datasets: [{ label: 'tokens / PR', data: report.series.tokensPerPrWeekly.series.map(p => p.value), borderColor: '#1d9e75', tension: 0.3 }]\n },\n options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { grid: { color: gridColor } }, y: { grid: { color: gridColor } } } }\n });\n }\n\n const concurrentEl = document.getElementById('chart-concurrent');\n if (concurrentEl) {\n const buckets = report.series.concurrentTasksWeekly.series.map(p => p.bucket);\n const baselineValue = report.kpis.peakLeverageHumanBaseline;\n new Chart(concurrentEl, {\n type: 'line',\n data: {\n labels: buckets,\n datasets: [\n { label: 'peak concurrent', data: report.series.concurrentTasksWeekly.series.map(p => p.value), borderColor: '#0f6e56', backgroundColor: 'rgba(15,110,86,0.18)', fill: true, tension: 0.3 },\n { label: 'human baseline', data: buckets.map(() => baselineValue), borderColor: '#888780', borderDash: [4, 4], borderWidth: 1, pointRadius: 0, fill: false }\n ]\n },\n options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, labels: { boxWidth: 12, font: { size: 11 } } } }, scales: { x: { grid: { color: gridColor } }, y: { grid: { color: gridColor }, beginAtZero: true } } }\n });\n }\n</script>\n</body>\n</html>\n`;\n}\n\nfunction renderFunnel(report: RoiReport): string {\n const stages = [\n { label: 'Tasks started', count: report.funnel.tasksStarted },\n { label: 'Produced commits', count: report.funnel.producedCommits },\n { label: 'Opened PR', count: report.funnel.openedPr },\n { label: 'Merged', count: report.funnel.merged },\n { label: 'Abandoned', count: report.funnel.abandoned, revert: true },\n ];\n const max = Math.max(1, ...stages.map((s) => s.count));\n return stages\n .map(\n (s) =>\n `<div class=\"funnel-row\"><div><div class=\"funnel-label\">${escapeHtml(s.label)}</div><div class=\"funnel-bar\"><div class=\"funnel-fill ${s.revert ? 'revert' : ''}\" style=\"width: ${Math.round((s.count / max) * 100)}%\"></div></div></div><div class=\"funnel-count\">${s.count}</div></div>`\n )\n .join('');\n}\n\nfunction escapeHtml(value: string): string {\n return String(value)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n","import type { RoiReport } from '../schemas/report';\n\nexport function renderJsonReport(report: RoiReport): string {\n return JSON.stringify(report, null, 2);\n}\n","import { ROI_EVENT_TYPE, type RoiEvent, RoiEventSchema } from '../schemas/events';\n\nexport interface FetchEventsOptions {\n workerUrl: string;\n authToken: string;\n since?: number;\n until?: number;\n limit?: number;\n fetchImpl?: typeof fetch;\n}\n\nexport interface RawQueryEvent {\n eventType: string;\n taskId: string | null;\n userId: string;\n clientTimestamp: number;\n serverTimestamp: number;\n payload: unknown;\n}\n\nexport interface FetchedEvents {\n events: RawQueryEvent[];\n roiEvents: RoiEvent[];\n}\n\nexport async function fetchRoiEvents(opts: FetchEventsOptions): Promise<FetchedEvents> {\n const fetchImpl = opts.fetchImpl ?? fetch;\n const baseUrl = opts.workerUrl.replace(/\\/$/, '');\n const qs = new URLSearchParams({ eventType: ROI_EVENT_TYPE });\n if (opts.since != null) qs.set('since', String(opts.since));\n if (opts.until != null) qs.set('until', String(opts.until));\n if (opts.limit != null) qs.set('limit', String(opts.limit));\n\n const response = await fetchImpl(`${baseUrl}/query/events?${qs.toString()}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${opts.authToken}` },\n });\n if (!response.ok) {\n throw new Error(`query/events responded ${response.status}: ${await response.text()}`);\n }\n // eslint-disable-next-line no-restricted-syntax -- fetch response.json() is untyped at the boundary\n const body = (await response.json()) as { events: RawQueryEvent[] };\n const events = body.events ?? [];\n const roiEvents: RoiEvent[] = [];\n for (const row of events) {\n if (row.eventType !== ROI_EVENT_TYPE) continue;\n const parsed = RoiEventSchema.safeParse(row.payload);\n if (parsed.success) roiEvents.push(parsed.data);\n }\n return { events, roiEvents };\n}\n","import { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nexport interface RawTaskRecord {\n taskId: string;\n channelId: string;\n title: string;\n status: string;\n mode?: string;\n createdAt: number;\n updatedAt: number;\n taskStartedAt: number | null;\n cwd: string;\n totalCostUsd?: number;\n totalOutputTokens?: number;\n taskType?: string;\n prUrl?: string;\n abandonedAt?: number | null;\n /** ROI: canonical \"shipped\" signal — stamped by the 3-tier merge detector. */\n mergedAt?: number | null;\n}\n\nexport interface TasksJsonShape {\n schemaVersion?: number;\n tasks: Record<string, RawTaskRecord>;\n}\n\nexport function defaultTasksJsonPath(): string {\n return join(homedir(), '.shipyard', 'data', 'tasks.json');\n}\n\n/**\n * Read the per-task directory layout (`~/.shipyard/data/tasks/${id}.json`)\n * that `DirectoryRecordStore` writes after the v3.5 migration. Returns\n * `null` when the directory is missing — caller falls back to the legacy\n * single-file path.\n */\nasync function readTasksDirectory(dirPath: string): Promise<TasksJsonShape | null> {\n let entries: string[];\n try {\n entries = await fs.readdir(dirPath);\n } catch {\n return null;\n }\n const tasks: Record<string, RawTaskRecord> = {};\n for (const entry of entries) {\n if (!entry.endsWith('.json') || entry.startsWith('__')) continue;\n const taskId = entry.slice(0, -'.json'.length);\n let raw: string;\n try {\n raw = await fs.readFile(join(dirPath, entry), 'utf8');\n } catch {\n continue;\n }\n try {\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse result is validated by field access below\n const parsed = JSON.parse(raw) as RawTaskRecord;\n tasks[taskId] = parsed;\n } catch {\n process.stderr.write(\n `[roi-aggregator] warning: ${entry} contains malformed JSON — task skipped\\n`\n );\n }\n }\n return { tasks };\n}\n\n/**\n * Read the task store, preferring the per-task directory layout introduced in\n * v3.5 (`DirectoryRecordStore`) and falling back to the legacy single-file\n * `tasks.json` for unmigrated installs. Final fallback is `tasks.json.migrated`\n * (the rename produced by the directory store) so reports run during a\n * partial-rollout upgrade still see local task context.\n *\n * The `filePath` argument is interpreted as the legacy single-file path; the\n * sibling `tasks/` directory is checked first when no explicit override is\n * given. CLI users passing `--tasks-json PATH` keep single-file semantics.\n */\nexport async function readTasksJson(\n filePath: string = defaultTasksJsonPath()\n): Promise<TasksJsonShape> {\n const dirPath = join(dirname(filePath), 'tasks');\n const fromDir = await readTasksDirectory(dirPath);\n if (fromDir !== null) return fromDir;\n\n let raw: string;\n let sourcePath = filePath;\n try {\n raw = await fs.readFile(filePath, 'utf8');\n } catch {\n /**\n * Final fallback: the directory store renames `tasks.json` →\n * `tasks.json.migrated` after a successful migration. If the user is\n * mid-upgrade and the per-task directory is empty for some reason\n * (e.g. corrupt + quarantined), reading the renamed snapshot still\n * lets ROI report from the last known good state instead of returning\n * empty.\n */\n try {\n const migratedPath = `${filePath}.migrated`;\n raw = await fs.readFile(migratedPath, 'utf8');\n sourcePath = migratedPath;\n } catch {\n return { tasks: {} };\n }\n }\n try {\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse result is validated by field access below\n const parsed = JSON.parse(raw) as TasksJsonShape;\n return { schemaVersion: parsed.schemaVersion, tasks: parsed.tasks ?? {} };\n } catch {\n process.stderr.write(\n `[roi-aggregator] warning: ${sourcePath} exists but contains malformed JSON — local task context will be empty\\n`\n );\n return { tasks: {} };\n }\n}\n"],"mappings":"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","names":["fs","homedir","fs","homedir"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/commands/roi.ts","../../../packages/roi-aggregator/src/joins/task-context.ts","../../../packages/roi-aggregator/src/metrics/cost-per-pr.ts","../../../packages/roi-aggregator/src/metrics/efficiency-curve.ts","../../../packages/roi-aggregator/src/metrics/funnel.ts","../../../packages/roi-aggregator/src/metrics/leverage.ts","../../../packages/roi-aggregator/src/metrics/per-engineer.ts","../../../packages/roi-aggregator/src/reports/build-report.ts","../../../packages/roi-aggregator/src/reports/csv.ts","../../../packages/roi-aggregator/src/reports/html.ts","../../../packages/roi-aggregator/src/reports/json.ts","../../../packages/roi-aggregator/src/sources/d1.ts","../../../packages/roi-aggregator/src/sources/tasks-json.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport { parseArgs } from 'node:util';\nimport {\n buildRoiReport,\n buildTaskContexts,\n defaultTasksJsonPath,\n fetchRoiEvents,\n type RawTaskRecord,\n type RoiEvent,\n readTasksJson,\n renderCsvReport,\n renderHtmlReport,\n renderJsonReport,\n} from '@shipyard/roi-aggregator';\nimport { logger } from '../logger.js';\n\nexport interface RoiCommandArgs {\n since?: string;\n until?: string;\n format: 'json' | 'html' | 'csv';\n output?: string;\n workerUrl: string;\n token: string;\n tasksJsonPath?: string;\n}\n\nfunction parseIsoToMs(raw: string | undefined): number | undefined {\n if (!raw) return undefined;\n const ms = Date.parse(raw);\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nexport async function roiCommand(argv: string[] = process.argv.slice(3)): Promise<void> {\n const { values } = parseArgs({\n args: argv,\n options: {\n since: { type: 'string' },\n until: { type: 'string' },\n format: { type: 'string', default: 'json' },\n output: { type: 'string', short: 'o' },\n 'worker-url': { type: 'string' },\n token: { type: 'string' },\n 'tasks-json': { type: 'string' },\n 'cwd-prefix': { type: 'string', multiple: true },\n help: { type: 'boolean', short: 'h' },\n },\n strict: true,\n allowPositionals: false,\n });\n\n if (values.help) {\n process.stdout.write(\n [\n 'Usage: shipyard roi [options]',\n '',\n ' --since DATE Start of window (ISO date). Default: 30 days ago.',\n ' --until DATE End of window (ISO date). Default: now.',\n ' --format FORMAT Output format: json, html, csv. Default: json.',\n ' --output FILE, -o Write to file instead of stdout.',\n ' --worker-url URL Metrics worker base URL. Env: SHIPYARD_METRICS_URL.',\n ' --token TOKEN JWT bearer token. Env: SHIPYARD_USER_TOKEN.',\n ' --tasks-json PATH Override path to local tasks.json.',\n ' --cwd-prefix PATH Only include tasks whose cwd starts with PATH. Repeatable for worktrees.',\n ' -h, --help Show this help.',\n '',\n ].join('\\n')\n );\n return;\n }\n\n const format = normalizeFormat(values.format);\n if (!format) {\n logger.error('Invalid --format. Expected one of: json, html, csv.');\n process.exit(1);\n }\n\n const workerUrl = values['worker-url'] ?? process.env.SHIPYARD_METRICS_URL;\n const token = values.token ?? process.env.SHIPYARD_USER_TOKEN;\n if (!workerUrl || !token) {\n logger.error(\n 'Missing required --worker-url / SHIPYARD_METRICS_URL and --token / SHIPYARD_USER_TOKEN.'\n );\n process.exit(1);\n }\n\n const until = parseIsoToMs(values.until) ?? Date.now();\n const since = parseIsoToMs(values.since) ?? until - 30 * 24 * 60 * 60 * 1000;\n const sinceIso = new Date(since).toISOString();\n const untilIso = new Date(until).toISOString();\n\n const [{ roiEvents }, tasks] = await Promise.all([\n fetchRoiEvents({ workerUrl, authToken: token, since, until }),\n readTasksJson(values['tasks-json'] ?? defaultTasksJsonPath()),\n ]);\n\n const cwdPrefixes = normalizeCwdPrefixes(values['cwd-prefix']);\n const { records: scopedRecords, events: scopedEvents } = applyCwdScope(\n tasks.tasks,\n roiEvents,\n cwdPrefixes\n );\n if (cwdPrefixes.length > 0) {\n logger.info(\n {\n cwdPrefixes,\n recordsKept: Object.keys(scopedRecords).length,\n recordsTotal: Object.keys(tasks.tasks).length,\n eventsKept: scopedEvents.length,\n eventsTotal: roiEvents.length,\n },\n 'roi report scoped by --cwd-prefix'\n );\n }\n\n const ctxs = buildTaskContexts(scopedEvents, scopedRecords);\n const report = buildRoiReport(ctxs, { since: sinceIso, until: untilIso });\n\n const rendered = renderReport(report, format);\n if (values.output) {\n await fs.writeFile(values.output, rendered, 'utf8');\n logger.info({ output: values.output, format }, 'roi report written');\n } else {\n process.stdout.write(rendered);\n if (!rendered.endsWith('\\n')) process.stdout.write('\\n');\n }\n}\n\nfunction normalizeCwdPrefixes(raw: string[] | undefined): string[] {\n if (!raw || raw.length === 0) return [];\n return raw.map((p) => {\n const expanded = p.startsWith('~/') || p === '~' ? p.replace(/^~/, homedir()) : p;\n return resolve(expanded);\n });\n}\n\nfunction applyCwdScope(\n records: Record<string, RawTaskRecord>,\n events: RoiEvent[],\n cwdPrefixes: string[]\n): { records: Record<string, RawTaskRecord>; events: RoiEvent[] } {\n if (cwdPrefixes.length === 0) return { records, events };\n const scoped: Record<string, RawTaskRecord> = {};\n for (const [taskId, record] of Object.entries(records)) {\n const recordCwd = record.cwd ? resolve(record.cwd) : '';\n const matches = cwdPrefixes.some(\n (prefix) => recordCwd === prefix || recordCwd.startsWith(`${prefix}/`)\n );\n if (matches) scoped[taskId] = record;\n }\n const allowed = new Set(Object.keys(scoped));\n const filteredEvents = events.filter((event) => allowed.has(event.taskId));\n return { records: scoped, events: filteredEvents };\n}\n\nfunction normalizeFormat(raw: string | undefined): 'json' | 'html' | 'csv' | null {\n if (raw === 'json' || raw === 'html' || raw === 'csv') return raw;\n return null;\n}\n\nfunction renderReport(\n report: Parameters<typeof renderJsonReport>[0],\n format: 'json' | 'html' | 'csv'\n): string {\n switch (format) {\n case 'json':\n return renderJsonReport(report);\n case 'html':\n return renderHtmlReport(report);\n case 'csv':\n return renderCsvReport(report);\n default: {\n const _exhaustive: never = format;\n throw new Error(`unhandled format: ${String(_exhaustive)}`);\n }\n }\n}\n","import {\n assertNeverEvent,\n type CommitAttributedEvent,\n type PrAttributedEvent,\n type PrMergedEvent,\n type RoiEvent,\n type TaskEndedEvent,\n type TaskStartedEvent,\n} from '../schemas/events';\nimport type { RawTaskRecord } from '../sources/tasks-json';\n\nexport interface TaskContext {\n taskId: string;\n userId: string;\n record: RawTaskRecord | null;\n started: TaskStartedEvent | null;\n ended: TaskEndedEvent | null;\n commits: CommitAttributedEvent[];\n pr: PrAttributedEvent | null;\n merged: PrMergedEvent | null;\n}\n\nfunction emptyContext(\n taskId: string,\n userId: string,\n records: Record<string, RawTaskRecord>\n): TaskContext {\n return {\n taskId,\n userId,\n record: records[taskId] ?? null,\n started: null,\n ended: null,\n commits: [],\n pr: null,\n merged: null,\n };\n}\n\nfunction applyEvent(ctx: TaskContext, event: RoiEvent): void {\n switch (event.kind) {\n case 'task_started':\n if (!ctx.started || event.timestamp < ctx.started.timestamp) ctx.started = event;\n break;\n case 'task_ended':\n if (!ctx.ended || event.timestamp > ctx.ended.timestamp) ctx.ended = event;\n break;\n case 'commit_attributed':\n ctx.commits.push(event);\n break;\n case 'pr_attributed':\n if (!ctx.pr) ctx.pr = event;\n break;\n case 'pr_merged':\n if (!ctx.merged || event.timestamp < ctx.merged.timestamp) ctx.merged = event;\n break;\n default:\n assertNeverEvent(event);\n }\n}\n\nexport function buildTaskContexts(\n events: RoiEvent[],\n records: Record<string, RawTaskRecord>\n): TaskContext[] {\n const byTask = new Map<string, TaskContext>();\n\n for (const event of events) {\n let ctx = byTask.get(event.taskId);\n if (!ctx) {\n ctx = emptyContext(event.taskId, event.userId, records);\n byTask.set(event.taskId, ctx);\n }\n applyEvent(ctx, event);\n }\n\n for (const record of Object.values(records)) {\n if (!byTask.has(record.taskId)) {\n byTask.set(record.taskId, emptyContext(record.taskId, '', records));\n }\n }\n\n return [...byTask.values()];\n}\n","import type { TaskContext } from '../joins/task-context';\n\nexport function computeCostPerMergedPr(ctxs: TaskContext[]): number {\n let totalCost = 0;\n let mergedPrs = 0;\n for (const ctx of ctxs) {\n const hasMergedPr =\n ctx.record?.status === 'completed' && (ctx.pr != null || ctx.record?.prUrl != null);\n if (!hasMergedPr) continue;\n mergedPrs += 1;\n const cost = ctx.ended?.totalCostUsd ?? ctx.record?.totalCostUsd ?? sumCommitCosts(ctx);\n totalCost += cost;\n }\n return mergedPrs === 0 ? 0 : Number((totalCost / mergedPrs).toFixed(4));\n}\n\nfunction sumCommitCosts(ctx: TaskContext): number {\n return ctx.commits.reduce((sum, c) => sum + c.costUsd, 0);\n}\n","import type { TaskContext } from '../joins/task-context';\nimport type { TimeSeriesMetric, TimeSeriesPoint } from '../schemas/report';\n\n/** ISO 8601 week number — week containing the first Thursday of the year is week 1. */\nfunction isoWeek(ms: number): string {\n const d = new Date(ms);\n const thursday = new Date(d);\n thursday.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));\n const yearStart = Date.UTC(thursday.getUTCFullYear(), 0, 1);\n const weekNo = Math.ceil(((thursday.getTime() - yearStart) / 86_400_000 + 1) / 7);\n return `${thursday.getUTCFullYear()}-W${String(weekNo).padStart(2, '0')}`;\n}\n\nexport function computeTokensPerMergedPrWeekly(ctxs: TaskContext[]): TimeSeriesMetric {\n const buckets = new Map<string, { tokens: number; prs: number }>();\n for (const ctx of ctxs) {\n const merged =\n ctx.record?.status === 'completed' && (ctx.pr != null || ctx.record?.prUrl != null);\n if (!merged) continue;\n const ts = ctx.ended?.timestamp ?? ctx.record?.updatedAt;\n if (ts == null) continue;\n const bucket = isoWeek(ts);\n const cur = buckets.get(bucket) ?? { tokens: 0, prs: 0 };\n cur.tokens += ctx.ended?.totalOutputTokens ?? ctx.record?.totalOutputTokens ?? 0;\n cur.prs += 1;\n buckets.set(bucket, cur);\n }\n const series: TimeSeriesPoint[] = [...buckets.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([week, { tokens, prs }]) => ({\n bucket: week,\n value: prs === 0 ? 0 : Math.round(tokens / prs),\n denominator: prs,\n }));\n return { metric: 'tokens_per_pr', unit: 'tokens', bucketSize: 'week', series };\n}\n\nexport function computeConcurrentTasksWeekly(ctxs: TaskContext[]): TimeSeriesMetric {\n const buckets = new Map<string, { peak: number }>();\n for (const ctx of ctxs) {\n const ts = ctx.started?.timestamp ?? ctx.record?.createdAt;\n if (ts == null) continue;\n const active = ctx.started?.activeTaskCount ?? 0;\n const bucket = isoWeek(ts);\n const cur = buckets.get(bucket) ?? { peak: 0 };\n cur.peak = Math.max(cur.peak, active);\n buckets.set(bucket, cur);\n }\n const series: TimeSeriesPoint[] = [...buckets.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([week, { peak }]) => ({ bucket: week, value: peak }));\n return { metric: 'concurrent_tasks', unit: 'count', bucketSize: 'week', series };\n}\n","import type { TaskContext } from '../joins/task-context';\nimport type { TaskOutcomeFunnel } from '../schemas/report';\n\nexport function computeFunnel(ctxs: TaskContext[]): TaskOutcomeFunnel {\n let tasksStarted = 0;\n let producedCommits = 0;\n let openedPr = 0;\n let merged = 0;\n let abandoned = 0;\n\n for (const ctx of ctxs) {\n if (ctx.started || ctx.record?.taskStartedAt != null) tasksStarted++;\n if (ctx.commits.length > 0) producedCommits++;\n if (ctx.pr || ctx.record?.prUrl) openedPr++;\n if (ctx.merged != null || ctx.record?.mergedAt != null) merged++;\n if (ctx.record?.abandonedAt != null) abandoned++;\n }\n\n return {\n tasksStarted,\n producedCommits,\n openedPr,\n merged,\n abandoned,\n revertedWithin30d: 0,\n };\n}\n","import type { TaskContext } from '../joins/task-context';\n\nexport const HUMAN_BASELINE_LEVERAGE = 1.0;\n\nexport interface LeverageSummary {\n peakByUser: Record<string, number>;\n avgPeak: number;\n humanBaseline: number;\n}\n\nexport function computePeakLeverage(ctxs: TaskContext[]): LeverageSummary {\n const peakByUser: Record<string, number> = {};\n for (const ctx of ctxs) {\n const user = ctx.userId || 'unknown';\n const count = ctx.started?.activeTaskCount ?? 0;\n peakByUser[user] = Math.max(peakByUser[user] ?? 0, count);\n }\n const values = Object.values(peakByUser);\n const avgPeak =\n values.length === 0\n ? 0\n : Number((values.reduce((a, b) => a + b, 0) / values.length).toFixed(2));\n return { peakByUser, avgPeak, humanBaseline: HUMAN_BASELINE_LEVERAGE };\n}\n","import type { TaskContext } from '../joins/task-context';\nimport type { EngineerScorecardRow } from '../schemas/report';\n\nfunction classifySignal(sampleSize: number, peakParallel: number): EngineerScorecardRow['signal'] {\n if (sampleSize < 3) return 'insufficient-data';\n if (sampleSize < 10) return 'modest';\n if (sampleSize < 20) return peakParallel >= 3 ? 'strong' : 'modest';\n if (peakParallel >= 5) return 'outlier-high';\n if (peakParallel >= 3) return 'strong';\n return 'modest';\n}\n\nfunction engineerRow(userId: string, ctxs: TaskContext[]): EngineerScorecardRow {\n const mergedPrs = ctxs.filter(\n (c) => c.record?.status === 'completed' && (c.pr || c.record?.prUrl)\n );\n const peakParallel = ctxs.reduce((max, c) => Math.max(max, c.started?.activeTaskCount ?? 0), 0);\n const sampleSize = ctxs.length;\n const costPerPr =\n mergedPrs.length === 0\n ? 0\n : Number(\n (\n mergedPrs.reduce((sum, c) => sum + (c.ended?.totalCostUsd ?? 0), 0) / mergedPrs.length\n ).toFixed(4)\n );\n return {\n userId,\n peakParallel,\n costPerPrUsd: costPerPr,\n revertRate: 0,\n sampleSize,\n signal: classifySignal(sampleSize, peakParallel),\n };\n}\n\nexport function computeEngineerScorecard(ctxs: TaskContext[]): EngineerScorecardRow[] {\n const byUser = new Map<string, TaskContext[]>();\n for (const ctx of ctxs) {\n const user = ctx.userId || 'unknown';\n const list = byUser.get(user) ?? [];\n list.push(ctx);\n byUser.set(user, list);\n }\n\n const rows: EngineerScorecardRow[] = [];\n for (const [userId, userCtxs] of byUser.entries()) {\n rows.push(engineerRow(userId, userCtxs));\n }\n return rows.sort((a, b) => b.sampleSize - a.sampleSize);\n}\n","import type { TaskContext } from '../joins/task-context';\nimport { computeCostPerMergedPr } from '../metrics/cost-per-pr';\nimport {\n computeConcurrentTasksWeekly,\n computeTokensPerMergedPrWeekly,\n} from '../metrics/efficiency-curve';\nimport { computeFunnel } from '../metrics/funnel';\nimport { computePeakLeverage } from '../metrics/leverage';\nimport { computeEngineerScorecard } from '../metrics/per-engineer';\nimport { REPORT_SCHEMA_VERSION, type RoiReport, type RoiWarning } from '../schemas/report';\n\nexport interface BuildReportOptions {\n since: string;\n until: string;\n}\n\nexport function buildRoiReport(ctxs: TaskContext[], opts: BuildReportOptions): RoiReport {\n const warnings: RoiWarning[] = [];\n if (ctxs.length === 0) {\n warnings.push({\n code: 'no_data',\n message:\n 'No ROI events found in the selected window. Ensure the daemon is running and events are being ingested.',\n context: {},\n });\n }\n\n const leverage = computePeakLeverage(ctxs);\n const funnel = computeFunnel(ctxs);\n const costPerMergedPr = computeCostPerMergedPr(ctxs);\n const byEngineer = computeEngineerScorecard(ctxs);\n const totalCost = ctxs.reduce(\n (sum, c) => sum + (c.ended?.totalCostUsd ?? c.record?.totalCostUsd ?? 0),\n 0\n );\n const totalTokens = ctxs.reduce(\n (sum, c) => sum + (c.ended?.totalOutputTokens ?? c.record?.totalOutputTokens ?? 0),\n 0\n );\n const engineersActive = new Set(\n ctxs.map((c) => c.userId || 'unknown').filter((u) => u !== 'unknown')\n ).size;\n\n for (const row of byEngineer) {\n if (row.sampleSize < 20) {\n warnings.push({\n code: 'small_sample',\n message: `Engineer ${row.userId} has only ${row.sampleSize} tasks in window. Deltas not statistically significant.`,\n context: { userId: row.userId, sampleSize: row.sampleSize },\n });\n }\n }\n\n return {\n schemaVersion: REPORT_SCHEMA_VERSION,\n generatedAt: new Date().toISOString(),\n window: { since: opts.since, until: opts.until },\n totals: {\n tasksStarted: funnel.tasksStarted,\n tasksAbandoned: funnel.abandoned,\n tasksShipped: funnel.merged,\n costUsd: Number(totalCost.toFixed(4)),\n tokens: totalTokens,\n engineersActive,\n },\n kpis: {\n costPerMergedPr,\n peakLeverageAvg: leverage.avgPeak,\n peakLeverageHumanBaseline: leverage.humanBaseline,\n revertRateShipyard: 0,\n },\n series: {\n tokensPerPrWeekly: computeTokensPerMergedPrWeekly(ctxs),\n concurrentTasksWeekly: computeConcurrentTasksWeekly(ctxs),\n },\n funnel,\n byEngineer,\n facets: [],\n warnings,\n };\n}\n","import type { RoiReport } from '../schemas/report';\n\nfunction csvEscape(value: string | number): string {\n const s = String(value);\n if (s.includes(',') || s.includes('\"') || s.includes('\\n')) {\n return `\"${s.replace(/\"/g, '\"\"')}\"`;\n }\n return s;\n}\n\nexport function renderCsvReport(report: RoiReport): string {\n const lines: string[] = [];\n lines.push('section,key,value');\n lines.push(`totals,tasksStarted,${report.totals.tasksStarted}`);\n lines.push(`totals,tasksAbandoned,${report.totals.tasksAbandoned}`);\n lines.push(`totals,tasksShipped,${report.totals.tasksShipped}`);\n lines.push(`totals,costUsd,${report.totals.costUsd}`);\n lines.push(`totals,tokens,${report.totals.tokens}`);\n lines.push(`totals,engineersActive,${report.totals.engineersActive}`);\n lines.push(`kpi,costPerMergedPr,${report.kpis.costPerMergedPr}`);\n lines.push(`kpi,peakLeverageAvg,${report.kpis.peakLeverageAvg}`);\n lines.push(`funnel,tasksStarted,${report.funnel.tasksStarted}`);\n lines.push(`funnel,producedCommits,${report.funnel.producedCommits}`);\n lines.push(`funnel,openedPr,${report.funnel.openedPr}`);\n lines.push(`funnel,merged,${report.funnel.merged}`);\n lines.push(`funnel,abandoned,${report.funnel.abandoned}`);\n\n lines.push('');\n lines.push('engineer,sampleSize,peakParallel,costPerPrUsd,revertRate,signal');\n for (const row of report.byEngineer) {\n lines.push(\n [\n csvEscape(row.userId),\n row.sampleSize,\n row.peakParallel,\n row.costPerPrUsd,\n row.revertRate,\n row.signal,\n ].join(',')\n );\n }\n\n return `${lines.join('\\n')}\\n`;\n}\n","import type { RoiReport } from '../schemas/report';\n\nexport function renderHtmlReport(report: RoiReport): string {\n const data = JSON.stringify(report).replace(/</g, '\\\\u003c');\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Shipyard ROI — ${report.window.since} → ${report.window.until}</title>\n<style>\n :root { color-scheme: light dark; --text:#e7e5e4; --muted:#a8a29e; --bg:#0c0a09; --surface:#1c1917; --border:#292524; --ok:#1d9e75; --warn:#f0a030; --danger:#e24b4a; }\n @media (prefers-color-scheme: light) { :root { --text:#1c1917; --muted:#57534e; --bg:#fafaf9; --surface:#ffffff; --border:#e7e5e4; } }\n body { font-family: system-ui, sans-serif; background: var(--bg); color: var(--text); margin: 0; padding: 32px; max-width: 1100px; margin-left: auto; margin-right: auto; line-height: 1.5; }\n h1 { font-size: 22px; font-weight: 500; margin: 0 0 4px 0; }\n .subtitle { font-size: 12px; color: var(--muted); margin-bottom: 24px; font-family: ui-monospace, monospace; }\n .kpi-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:12px; margin-bottom:20px; }\n .kpi { background: var(--surface); padding: 16px; border-radius: 12px; border: 1px solid var(--border); }\n .kpi .label { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); }\n .kpi .value { font-size: 28px; font-weight: 500; margin-top: 4px; }\n .kpi .baseline { font-size: 11px; color: var(--muted); margin-top: 4px; font-family: ui-monospace, monospace; }\n .panel { background: var(--surface); padding: 14px 16px; border-radius: 12px; border: 1px solid var(--border); margin-bottom: 16px; }\n .panel h2 { font-size: 14px; font-weight: 500; margin: 0 0 12px 0; }\n table { width: 100%; border-collapse: collapse; font-size: 13px; }\n th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); border-bottom: 1px solid var(--border); padding: 8px 10px; font-weight: 500; }\n td { padding: 8px 10px; border-bottom: 1px solid var(--border); font-variant-numeric: tabular-nums; }\n .funnel-row { display: grid; grid-template-columns: 1fr 80px; gap: 8px; align-items: center; margin-bottom: 6px; }\n .funnel-bar { height: 10px; background: color-mix(in srgb, var(--ok) 25%, transparent); border-radius: 3px; overflow: hidden; }\n .funnel-fill { height: 100%; background: var(--ok); }\n .funnel-fill.revert { background: var(--danger); }\n .funnel-label { font-size: 12px; color: var(--muted); }\n .funnel-count { font-family: ui-monospace, monospace; font-size: 12px; text-align: right; }\n .warn { background: color-mix(in srgb, var(--warn) 12%, transparent); color: var(--warn); padding: 10px 12px; border-radius: 8px; font-size: 12px; margin-bottom: 8px; }\n .chart-wrap { position: relative; height: 240px; }\n</style>\n</head>\n<body>\n <h1>Shipyard ROI — ${escapeHtml(report.window.since)} → ${escapeHtml(report.window.until)}</h1>\n <div class=\"subtitle\">Generated ${escapeHtml(report.generatedAt)} · ${report.totals.engineersActive} engineer(s) · schema v${report.schemaVersion}</div>\n\n <div class=\"kpi-grid\">\n <div class=\"kpi\"><div class=\"label\">Cost / merged PR</div><div class=\"value\">$${report.kpis.costPerMergedPr.toFixed(2)}</div></div>\n <div class=\"kpi\"><div class=\"label\">Peak leverage</div><div class=\"value\">${report.kpis.peakLeverageAvg.toFixed(1)}×</div><div class=\"baseline\">vs ${report.kpis.peakLeverageHumanBaseline.toFixed(1)}× human baseline</div></div>\n <div class=\"kpi\"><div class=\"label\">Tasks shipped</div><div class=\"value\">${report.totals.tasksShipped}</div></div>\n <div class=\"kpi\"><div class=\"label\">Token spend</div><div class=\"value\">$${report.totals.costUsd.toFixed(2)}</div></div>\n </div>\n\n <div class=\"panel\">\n <h2>Task outcome funnel</h2>\n ${renderFunnel(report)}\n </div>\n\n <div class=\"panel\">\n <h2>Tokens per merged PR (weekly)</h2>\n <div class=\"chart-wrap\"><canvas id=\"chart-tokens\"></canvas></div>\n </div>\n\n <div class=\"panel\">\n <h2>Peak concurrent tasks (weekly)</h2>\n <div class=\"chart-wrap\"><canvas id=\"chart-concurrent\"></canvas></div>\n </div>\n\n <div class=\"panel\">\n <h2>Per-engineer scorecard</h2>\n <table>\n <thead><tr><th>Engineer</th><th>Tasks</th><th>Peak ‖</th><th>$ / PR</th><th>Signal</th></tr></thead>\n <tbody>\n ${report.byEngineer\n .map(\n (row) =>\n `<tr><td>${escapeHtml(row.userId)}</td><td>${row.sampleSize}</td><td>${row.peakParallel}</td><td>$${row.costPerPrUsd.toFixed(2)}</td><td>${escapeHtml(row.signal)}</td></tr>`\n )\n .join('')}\n </tbody>\n </table>\n </div>\n\n ${\n report.warnings.length > 0\n ? `<div class=\"panel\"><h2>Warnings</h2>${report.warnings.map((w) => `<div class=\"warn\">${escapeHtml(w.message)}</div>`).join('')}</div>`\n : ''\n }\n\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js\" crossorigin=\"anonymous\"></script>\n<script>\n const report = ${data};\n const textColor = getComputedStyle(document.documentElement).getPropertyValue('--text').trim();\n const gridColor = getComputedStyle(document.documentElement).getPropertyValue('--border').trim();\n Chart.defaults.color = textColor;\n\n const tokensEl = document.getElementById('chart-tokens');\n if (tokensEl) {\n new Chart(tokensEl, {\n type: 'line',\n data: {\n labels: report.series.tokensPerPrWeekly.series.map(p => p.bucket),\n datasets: [{ label: 'tokens / PR', data: report.series.tokensPerPrWeekly.series.map(p => p.value), borderColor: '#1d9e75', tension: 0.3 }]\n },\n options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { grid: { color: gridColor } }, y: { grid: { color: gridColor } } } }\n });\n }\n\n const concurrentEl = document.getElementById('chart-concurrent');\n if (concurrentEl) {\n const buckets = report.series.concurrentTasksWeekly.series.map(p => p.bucket);\n const baselineValue = report.kpis.peakLeverageHumanBaseline;\n new Chart(concurrentEl, {\n type: 'line',\n data: {\n labels: buckets,\n datasets: [\n { label: 'peak concurrent', data: report.series.concurrentTasksWeekly.series.map(p => p.value), borderColor: '#0f6e56', backgroundColor: 'rgba(15,110,86,0.18)', fill: true, tension: 0.3 },\n { label: 'human baseline', data: buckets.map(() => baselineValue), borderColor: '#888780', borderDash: [4, 4], borderWidth: 1, pointRadius: 0, fill: false }\n ]\n },\n options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, labels: { boxWidth: 12, font: { size: 11 } } } }, scales: { x: { grid: { color: gridColor } }, y: { grid: { color: gridColor }, beginAtZero: true } } }\n });\n }\n</script>\n</body>\n</html>\n`;\n}\n\nfunction renderFunnel(report: RoiReport): string {\n const stages = [\n { label: 'Tasks started', count: report.funnel.tasksStarted },\n { label: 'Produced commits', count: report.funnel.producedCommits },\n { label: 'Opened PR', count: report.funnel.openedPr },\n { label: 'Merged', count: report.funnel.merged },\n { label: 'Abandoned', count: report.funnel.abandoned, revert: true },\n ];\n const max = Math.max(1, ...stages.map((s) => s.count));\n return stages\n .map(\n (s) =>\n `<div class=\"funnel-row\"><div><div class=\"funnel-label\">${escapeHtml(s.label)}</div><div class=\"funnel-bar\"><div class=\"funnel-fill ${s.revert ? 'revert' : ''}\" style=\"width: ${Math.round((s.count / max) * 100)}%\"></div></div></div><div class=\"funnel-count\">${s.count}</div></div>`\n )\n .join('');\n}\n\nfunction escapeHtml(value: string): string {\n return String(value)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n","import type { RoiReport } from '../schemas/report';\n\nexport function renderJsonReport(report: RoiReport): string {\n return JSON.stringify(report, null, 2);\n}\n","import { ROI_EVENT_TYPE, type RoiEvent, RoiEventSchema } from '../schemas/events';\n\nexport interface FetchEventsOptions {\n workerUrl: string;\n authToken: string;\n since?: number;\n until?: number;\n limit?: number;\n fetchImpl?: typeof fetch;\n}\n\nexport interface RawQueryEvent {\n eventType: string;\n taskId: string | null;\n userId: string;\n clientTimestamp: number;\n serverTimestamp: number;\n payload: unknown;\n}\n\nexport interface FetchedEvents {\n events: RawQueryEvent[];\n roiEvents: RoiEvent[];\n}\n\nexport async function fetchRoiEvents(opts: FetchEventsOptions): Promise<FetchedEvents> {\n const fetchImpl = opts.fetchImpl ?? fetch;\n const baseUrl = opts.workerUrl.replace(/\\/$/, '');\n const qs = new URLSearchParams({ eventType: ROI_EVENT_TYPE });\n if (opts.since != null) qs.set('since', String(opts.since));\n if (opts.until != null) qs.set('until', String(opts.until));\n if (opts.limit != null) qs.set('limit', String(opts.limit));\n\n const response = await fetchImpl(`${baseUrl}/query/events?${qs.toString()}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${opts.authToken}` },\n });\n if (!response.ok) {\n throw new Error(`query/events responded ${response.status}: ${await response.text()}`);\n }\n // eslint-disable-next-line no-restricted-syntax -- fetch response.json() is untyped at the boundary\n const body = (await response.json()) as { events: RawQueryEvent[] };\n const events = body.events ?? [];\n const roiEvents: RoiEvent[] = [];\n for (const row of events) {\n if (row.eventType !== ROI_EVENT_TYPE) continue;\n const parsed = RoiEventSchema.safeParse(row.payload);\n if (parsed.success) roiEvents.push(parsed.data);\n }\n return { events, roiEvents };\n}\n","import { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nexport interface RawTaskRecord {\n taskId: string;\n channelId: string;\n title: string;\n status: string;\n mode?: string;\n createdAt: number;\n updatedAt: number;\n taskStartedAt: number | null;\n cwd: string;\n totalCostUsd?: number;\n totalOutputTokens?: number;\n taskType?: string;\n prUrl?: string;\n abandonedAt?: number | null;\n /** ROI: canonical \"shipped\" signal — stamped by the 3-tier merge detector. */\n mergedAt?: number | null;\n}\n\nexport interface TasksJsonShape {\n schemaVersion?: number;\n tasks: Record<string, RawTaskRecord>;\n}\n\nexport function defaultTasksJsonPath(): string {\n return join(homedir(), '.shipyard', 'data', 'tasks.json');\n}\n\n/**\n * Read the per-task directory layout (`~/.shipyard/data/tasks/${id}.json`)\n * that `DirectoryRecordStore` writes after the v3.5 migration. Returns\n * `null` when the directory is missing — caller falls back to the legacy\n * single-file path.\n */\nasync function readTasksDirectory(dirPath: string): Promise<TasksJsonShape | null> {\n let entries: string[];\n try {\n entries = await fs.readdir(dirPath);\n } catch {\n return null;\n }\n const tasks: Record<string, RawTaskRecord> = {};\n for (const entry of entries) {\n if (!entry.endsWith('.json') || entry.startsWith('__')) continue;\n const taskId = entry.slice(0, -'.json'.length);\n let raw: string;\n try {\n raw = await fs.readFile(join(dirPath, entry), 'utf8');\n } catch {\n continue;\n }\n try {\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse result is validated by field access below\n const parsed = JSON.parse(raw) as RawTaskRecord;\n tasks[taskId] = parsed;\n } catch {\n process.stderr.write(\n `[roi-aggregator] warning: ${entry} contains malformed JSON — task skipped\\n`\n );\n }\n }\n return { tasks };\n}\n\n/**\n * Read the task store, preferring the per-task directory layout introduced in\n * v3.5 (`DirectoryRecordStore`) and falling back to the legacy single-file\n * `tasks.json` for unmigrated installs. Final fallback is `tasks.json.migrated`\n * (the rename produced by the directory store) so reports run during a\n * partial-rollout upgrade still see local task context.\n *\n * The `filePath` argument is interpreted as the legacy single-file path; the\n * sibling `tasks/` directory is checked first when no explicit override is\n * given. CLI users passing `--tasks-json PATH` keep single-file semantics.\n */\nexport async function readTasksJson(\n filePath: string = defaultTasksJsonPath()\n): Promise<TasksJsonShape> {\n const dirPath = join(dirname(filePath), 'tasks');\n const fromDir = await readTasksDirectory(dirPath);\n if (fromDir !== null) return fromDir;\n\n let raw: string;\n let sourcePath = filePath;\n try {\n raw = await fs.readFile(filePath, 'utf8');\n } catch {\n /**\n * Final fallback: the directory store renames `tasks.json` →\n * `tasks.json.migrated` after a successful migration. If the user is\n * mid-upgrade and the per-task directory is empty for some reason\n * (e.g. corrupt + quarantined), reading the renamed snapshot still\n * lets ROI report from the last known good state instead of returning\n * empty.\n */\n try {\n const migratedPath = `${filePath}.migrated`;\n raw = await fs.readFile(migratedPath, 'utf8');\n sourcePath = migratedPath;\n } catch {\n return { tasks: {} };\n }\n }\n try {\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse result is validated by field access below\n const parsed = JSON.parse(raw) as TasksJsonShape;\n return { schemaVersion: parsed.schemaVersion, tasks: parsed.tasks ?? {} };\n } catch {\n process.stderr.write(\n `[roi-aggregator] warning: ${sourcePath} exists but contains malformed JSON — local task context will be empty\\n`\n );\n return { tasks: {} };\n }\n}\n"],"mappings":"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","names":["fs","homedir","fs","homedir"]}
|