vibeman 0.0.11 → 0.0.13
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/api.js +53044 -34965
- package/dist/commit.txt +1 -1
- package/dist/index.js +166 -53
- package/dist/prisma/migrations/20260221100333_add_health_metrics/migration.sql +9 -0
- package/dist/prisma/schema.prisma +6 -0
- package/dist/prisma.config.ts +5 -1
- package/dist/scripts/init-test-repo.mjs +0 -8
- package/dist/scripts/lib/test-fixtures.mjs +67 -16
- package/dist/scripts/seed-test-fixtures.mjs +2 -2
- package/dist/ui/assets/{index-D4lRQ9OU.js → index-AacKgcwz.js} +1 -1
- package/dist/ui/assets/{index-Ck0eDlqj.js → index-B-SoOQ3F.js} +1 -1
- package/dist/ui/assets/{index-Buod5MG9.js → index-BCRvuZjU.js} +1 -1
- package/dist/ui/assets/{index-BtgmEMNX.js → index-BK62XAzY.js} +1 -1
- package/dist/ui/assets/{index-BKcn2ir8.js → index-B_lr0R8Z.js} +1 -1
- package/dist/ui/assets/{index-Q46jjFaN.js → index-BnaWildZ.js} +1 -1
- package/dist/ui/assets/{index-BFpKdhc4.js → index-BrOS5hPg.js} +1 -1
- package/dist/ui/assets/{index-DCwTMEKA.js → index-Btdv65e_.js} +1 -1
- package/dist/ui/assets/{index-CoX8THvk.js → index-BvHGNf4Y.js} +1 -1
- package/dist/ui/assets/{index-CfiNAuWd.js → index-C-AZWfRD.js} +1 -1
- package/dist/ui/assets/index-C8FDQRxy.js +1095 -0
- package/dist/ui/assets/{index-xr3-NPcF.js → index-CBQN5qXY.js} +1 -1
- package/dist/ui/assets/{index-BxFJT2l4.js → index-CWcSlUCR.js} +1 -1
- package/dist/ui/assets/{index-D_p2Z3lg.js → index-CXV7fZxu.js} +1 -1
- package/dist/ui/assets/{index-CJ0FVxY4.js → index-CuieGJbi.js} +1 -1
- package/dist/ui/assets/{index-DlPVzvxz.js → index-D1F9nIOz.js} +1 -1
- package/dist/ui/assets/{index-CmzQ8vUy.js → index-D3gXfrX-.js} +1 -1
- package/dist/ui/assets/{index-BnA5v3sz.js → index-DWT6BjYW.js} +1 -1
- package/dist/ui/assets/index-D_uhpUwJ.css +1 -0
- package/dist/ui/assets/{index-CCzed9cx.js → index-Dhu8BZu7.js} +1 -1
- package/dist/ui/assets/{index-XVbgp8h-.js → index-DkFfIHJr.js} +1 -1
- package/dist/ui/assets/{index-5kvaa7VH.js → index-cS068OSU.js} +1 -1
- package/dist/ui/assets/{index-CaM8gf-6.js → index-o8f2ilDp.js} +1 -1
- package/dist/ui/assets/{index-B2YlQpV0.js → index-q7X3WW0-.js} +1 -1
- package/dist/ui/index.html +2 -2
- package/package.json +7 -7
- package/dist/apps/api/resources/templates/task.md +0 -48
- package/dist/ui/assets/index-B-lQSV62.css +0 -1
- package/dist/ui/assets/index-BmCH7Zkp.js +0 -1020
package/dist/commit.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
cf5eb36
|
package/dist/index.js
CHANGED
|
@@ -1965,6 +1965,100 @@ function isNodeVersionCompatible(currentNodeVersion, requiredRange) {
|
|
|
1965
1965
|
}
|
|
1966
1966
|
}
|
|
1967
1967
|
|
|
1968
|
+
// ../shared/runtime/port-utils.ts
|
|
1969
|
+
import { createServer as createNetServer } from "node:net";
|
|
1970
|
+
var MIN_PORT = 1;
|
|
1971
|
+
var MAX_PORT = 65535;
|
|
1972
|
+
async function findAvailablePort(startPort) {
|
|
1973
|
+
assertValidPort(startPort);
|
|
1974
|
+
for (let port = startPort;port <= MAX_PORT; port += 1) {
|
|
1975
|
+
const available = await isPortAvailable(port);
|
|
1976
|
+
if (available) {
|
|
1977
|
+
return port;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
throw new Error(`No available port found from ${startPort} to ${MAX_PORT}.`);
|
|
1981
|
+
}
|
|
1982
|
+
function assertValidPort(port) {
|
|
1983
|
+
if (!Number.isInteger(port) || port < MIN_PORT || port > MAX_PORT) {
|
|
1984
|
+
throw new RangeError(`Port must be an integer in ${MIN_PORT}..${MAX_PORT}. Received: ${port}`);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
function isPortAvailable(port) {
|
|
1988
|
+
return new Promise((resolve2, reject) => {
|
|
1989
|
+
const probe = createNetServer();
|
|
1990
|
+
probe.unref();
|
|
1991
|
+
const onError = (error) => {
|
|
1992
|
+
cleanup();
|
|
1993
|
+
if (isAddressInUseError(error)) {
|
|
1994
|
+
resolve2(false);
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
reject(error);
|
|
1998
|
+
};
|
|
1999
|
+
const onListening = () => {
|
|
2000
|
+
cleanup();
|
|
2001
|
+
probe.close((closeError) => {
|
|
2002
|
+
if (closeError) {
|
|
2003
|
+
reject(closeError);
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
resolve2(true);
|
|
2007
|
+
});
|
|
2008
|
+
};
|
|
2009
|
+
const cleanup = () => {
|
|
2010
|
+
probe.off("error", onError);
|
|
2011
|
+
probe.off("listening", onListening);
|
|
2012
|
+
};
|
|
2013
|
+
probe.once("error", onError);
|
|
2014
|
+
probe.once("listening", onListening);
|
|
2015
|
+
probe.listen(port);
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
function isAddressInUseError(error) {
|
|
2019
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "EADDRINUSE";
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
// ../shared/runtime/vibeman-env.ts
|
|
2023
|
+
var VIBEMAN_ENV_KEYS = {
|
|
2024
|
+
ROOT: "VIBEMAN_ROOT",
|
|
2025
|
+
PORT: "VIBEMAN_PORT",
|
|
2026
|
+
DATABASE_URL: "VIBEMAN_DATABASE_URL",
|
|
2027
|
+
VERBOSE: "VIBEMAN_VERBOSE",
|
|
2028
|
+
LOG_FORMAT: "VIBEMAN_LOG_FORMAT",
|
|
2029
|
+
UI_DIST: "VIBEMAN_UI_DIST",
|
|
2030
|
+
CODEX_PATH: "VIBEMAN_CODEX_PATH",
|
|
2031
|
+
GEMINI_PATH: "VIBEMAN_GEMINI_PATH",
|
|
2032
|
+
CLAUDE_CODE_PATH: "VIBEMAN_CLAUDE_CODE_PATH",
|
|
2033
|
+
TRACE: "VIBEMAN_TRACE",
|
|
2034
|
+
DIST_ROOT: "VIBEMAN_DIST_ROOT",
|
|
2035
|
+
DB_AUTORECOVER: "VIBEMAN_DB_AUTORECOVER"
|
|
2036
|
+
};
|
|
2037
|
+
function readVibemanEnv(env, key) {
|
|
2038
|
+
const value = env[key];
|
|
2039
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
2040
|
+
return value.trim();
|
|
2041
|
+
}
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
// src/dist-layout.ts
|
|
2046
|
+
var DIST_LAYOUT = {
|
|
2047
|
+
cliEntry: "index.js",
|
|
2048
|
+
apiEntry: "api.js",
|
|
2049
|
+
commitFile: "commit.txt",
|
|
2050
|
+
prismaConfig: "prisma.config.ts",
|
|
2051
|
+
uiRoot: "ui",
|
|
2052
|
+
uiIndexHtml: "ui/index.html",
|
|
2053
|
+
prismaRoot: "prisma",
|
|
2054
|
+
prismaSchema: "prisma/schema.prisma",
|
|
2055
|
+
resourcesRoot: "resources",
|
|
2056
|
+
scriptsRoot: "scripts",
|
|
2057
|
+
fixtureInitScript: "scripts/init-test-repo.mjs",
|
|
2058
|
+
fixtureSeedScript: "scripts/seed-test-fixtures.mjs",
|
|
2059
|
+
fixtureLibraryScript: "scripts/lib/test-fixtures.mjs"
|
|
2060
|
+
};
|
|
2061
|
+
|
|
1968
2062
|
// src/index.ts
|
|
1969
2063
|
enforceNodeRuntimeCompatibility(import.meta.url);
|
|
1970
2064
|
var args = process.argv.slice(2);
|
|
@@ -1990,11 +2084,12 @@ console.error(`Unknown command: ${command}`);
|
|
|
1990
2084
|
printHelp();
|
|
1991
2085
|
process.exit(1);
|
|
1992
2086
|
function parseStartArgs(argv) {
|
|
1993
|
-
const envPort = Number(process.env.PORT);
|
|
2087
|
+
const envPort = Number(readVibemanEnv(process.env, VIBEMAN_ENV_KEYS.PORT));
|
|
1994
2088
|
const output = {
|
|
1995
2089
|
path: ".",
|
|
1996
2090
|
port: Number.isFinite(envPort) ? envPort : 6969,
|
|
1997
|
-
open: true
|
|
2091
|
+
open: true,
|
|
2092
|
+
trace: false
|
|
1998
2093
|
};
|
|
1999
2094
|
for (let i = 0;i < argv.length; i += 1) {
|
|
2000
2095
|
const arg = argv[i];
|
|
@@ -2010,6 +2105,10 @@ function parseStartArgs(argv) {
|
|
|
2010
2105
|
output.open = false;
|
|
2011
2106
|
continue;
|
|
2012
2107
|
}
|
|
2108
|
+
if (arg === "--trace") {
|
|
2109
|
+
output.trace = true;
|
|
2110
|
+
continue;
|
|
2111
|
+
}
|
|
2013
2112
|
if (!arg.startsWith("-") && output.path === ".") {
|
|
2014
2113
|
output.path = arg;
|
|
2015
2114
|
}
|
|
@@ -2018,12 +2117,16 @@ function parseStartArgs(argv) {
|
|
|
2018
2117
|
}
|
|
2019
2118
|
async function startApps(options) {
|
|
2020
2119
|
const moduleDir = dirname2(fileURLToPath2(import.meta.url));
|
|
2021
|
-
const distRoot =
|
|
2022
|
-
const distApi =
|
|
2023
|
-
const distUiRoot =
|
|
2120
|
+
const distRoot = resolvePackagedDistRoot(moduleDir);
|
|
2121
|
+
const distApi = resolve2(distRoot, DIST_LAYOUT.apiEntry);
|
|
2122
|
+
const distUiRoot = resolve2(distRoot, DIST_LAYOUT.uiRoot);
|
|
2024
2123
|
const nodePath = resolveNodePath();
|
|
2025
2124
|
const targetRoot = resolve2(process.cwd(), options.path);
|
|
2026
|
-
|
|
2125
|
+
const resolvedPort = await findAvailablePort(options.port);
|
|
2126
|
+
if (resolvedPort !== options.port) {
|
|
2127
|
+
console.warn(`[WARN] [startup] Requested port ${options.port} is in use. Using ${resolvedPort} instead.`);
|
|
2128
|
+
}
|
|
2129
|
+
if (!existsSync2(distApi) || !existsSync2(resolve2(distRoot, DIST_LAYOUT.uiIndexHtml))) {
|
|
2027
2130
|
console.error("Missing dist runtime files. Run the build first.");
|
|
2028
2131
|
process.exit(1);
|
|
2029
2132
|
}
|
|
@@ -2031,22 +2134,28 @@ async function startApps(options) {
|
|
|
2031
2134
|
cwd: process.cwd(),
|
|
2032
2135
|
env: {
|
|
2033
2136
|
...process.env,
|
|
2034
|
-
PORT: String(
|
|
2035
|
-
|
|
2036
|
-
|
|
2137
|
+
[VIBEMAN_ENV_KEYS.PORT]: String(resolvedPort),
|
|
2138
|
+
[VIBEMAN_ENV_KEYS.ROOT]: targetRoot,
|
|
2139
|
+
[VIBEMAN_ENV_KEYS.DIST_ROOT]: distRoot,
|
|
2140
|
+
[VIBEMAN_ENV_KEYS.UI_DIST]: distUiRoot,
|
|
2141
|
+
...options.trace ? { [VIBEMAN_ENV_KEYS.VERBOSE]: "trace", [VIBEMAN_ENV_KEYS.TRACE]: "true" } : {}
|
|
2037
2142
|
},
|
|
2038
2143
|
stdio: "inherit"
|
|
2039
2144
|
});
|
|
2040
|
-
const url = new URL(`http://localhost:${
|
|
2145
|
+
const url = new URL(`http://localhost:${resolvedPort}/`);
|
|
2041
2146
|
if (options.open) {
|
|
2042
|
-
const
|
|
2147
|
+
const startupSpinner = createStartupSpinner(url.toString(), resolvedPort);
|
|
2148
|
+
const ready = await waitForApiReady(resolvedPort);
|
|
2149
|
+
startupSpinner.stop(ready ? "ready" : "timeout");
|
|
2043
2150
|
if (!ready) {
|
|
2044
2151
|
console.warn("API not ready yet; opening the browser anyway.");
|
|
2045
2152
|
}
|
|
2046
2153
|
await openBrowser(url.toString());
|
|
2047
2154
|
}
|
|
2048
|
-
|
|
2049
|
-
|
|
2155
|
+
const readyLine = process.stdout.isTTY ? `
|
|
2156
|
+
\x1B[32mReady\x1B[0m \x1B[96m${url.toString()}\x1B[0m (Ctrl+C to stop)` : `
|
|
2157
|
+
Ready ${url.toString()} (Ctrl+C to stop)`;
|
|
2158
|
+
console.log(readyLine);
|
|
2050
2159
|
const stop = () => {
|
|
2051
2160
|
apiProcess.kill();
|
|
2052
2161
|
};
|
|
@@ -2080,16 +2189,12 @@ async function runFixtureScript(scriptName, forwardedArgs) {
|
|
|
2080
2189
|
function resolveFixtureScriptPath(moduleDir, currentDir, scriptName) {
|
|
2081
2190
|
const workspaceRoot = findFixtureWorkspaceRoot(moduleDir, currentDir);
|
|
2082
2191
|
if (workspaceRoot) {
|
|
2083
|
-
const scriptPath = resolve2(workspaceRoot,
|
|
2192
|
+
const scriptPath = resolve2(workspaceRoot, DIST_LAYOUT.scriptsRoot, scriptName);
|
|
2084
2193
|
if (existsSync2(scriptPath)) {
|
|
2085
2194
|
return scriptPath;
|
|
2086
2195
|
}
|
|
2087
2196
|
}
|
|
2088
|
-
const
|
|
2089
|
-
if (!packagedRoot) {
|
|
2090
|
-
return "";
|
|
2091
|
-
}
|
|
2092
|
-
const packagedScript = resolve2(packagedRoot, "scripts", scriptName);
|
|
2197
|
+
const packagedScript = resolve2(resolvePackagedDistRoot(moduleDir), DIST_LAYOUT.scriptsRoot, scriptName);
|
|
2093
2198
|
return existsSync2(packagedScript) ? packagedScript : "";
|
|
2094
2199
|
}
|
|
2095
2200
|
function findFixtureWorkspaceRoot(moduleDir, currentDir) {
|
|
@@ -2104,7 +2209,7 @@ function findFixtureWorkspaceRoot(moduleDir, currentDir) {
|
|
|
2104
2209
|
function ascendForFixtureWorkspace(seed) {
|
|
2105
2210
|
let dir = resolve2(seed);
|
|
2106
2211
|
while (true) {
|
|
2107
|
-
const fixtureScript = resolve2(dir,
|
|
2212
|
+
const fixtureScript = resolve2(dir, DIST_LAYOUT.fixtureInitScript);
|
|
2108
2213
|
const packageJson = resolve2(dir, "package.json");
|
|
2109
2214
|
if (existsSync2(fixtureScript) && existsSync2(packageJson)) {
|
|
2110
2215
|
return dir;
|
|
@@ -2115,21 +2220,6 @@ function ascendForFixtureWorkspace(seed) {
|
|
|
2115
2220
|
dir = parent;
|
|
2116
2221
|
}
|
|
2117
2222
|
}
|
|
2118
|
-
function findPackagedFixtureRoot(moduleDir) {
|
|
2119
|
-
const candidates = [
|
|
2120
|
-
moduleDir,
|
|
2121
|
-
resolve2(moduleDir, ".."),
|
|
2122
|
-
resolve2(moduleDir, "../dist"),
|
|
2123
|
-
resolve2(moduleDir, "../../dist")
|
|
2124
|
-
];
|
|
2125
|
-
for (const dir of candidates) {
|
|
2126
|
-
const fixtureScript = resolve2(dir, "scripts", "init-test-repo.mjs");
|
|
2127
|
-
if (existsSync2(fixtureScript)) {
|
|
2128
|
-
return dir;
|
|
2129
|
-
}
|
|
2130
|
-
}
|
|
2131
|
-
return "";
|
|
2132
|
-
}
|
|
2133
2223
|
async function openBrowser(target) {
|
|
2134
2224
|
const platform = process.platform;
|
|
2135
2225
|
if (platform === "darwin") {
|
|
@@ -2146,13 +2236,13 @@ function printHelp() {
|
|
|
2146
2236
|
console.log(`vibeman CLI
|
|
2147
2237
|
|
|
2148
2238
|
Usage:
|
|
2149
|
-
vibeman start [path] [--port <port>] [--no-open]
|
|
2239
|
+
vibeman start [path] [--port <port>] [--no-open] [--trace]
|
|
2150
2240
|
vibeman init-test-repo [path] [--force] [--skip-db-seed]
|
|
2151
2241
|
vibeman --version
|
|
2152
2242
|
|
|
2153
2243
|
Examples:
|
|
2154
2244
|
vibeman start .
|
|
2155
|
-
vibeman start ./notes --port 7010
|
|
2245
|
+
vibeman start ./notes --port 7010 --trace
|
|
2156
2246
|
vibeman init-test-repo ./tmp/test-repo
|
|
2157
2247
|
`);
|
|
2158
2248
|
}
|
|
@@ -2185,7 +2275,7 @@ function readGitCommit() {
|
|
|
2185
2275
|
function readPackagedCommit() {
|
|
2186
2276
|
try {
|
|
2187
2277
|
const moduleDir = dirname2(fileURLToPath2(import.meta.url));
|
|
2188
|
-
const commitPath = resolve2(moduleDir,
|
|
2278
|
+
const commitPath = resolve2(moduleDir, DIST_LAYOUT.commitFile);
|
|
2189
2279
|
if (!existsSync2(commitPath))
|
|
2190
2280
|
return "";
|
|
2191
2281
|
return readFileSync2(commitPath, "utf-8").trim();
|
|
@@ -2198,6 +2288,42 @@ function waitForExit(child) {
|
|
|
2198
2288
|
child.once("exit", (code) => resolvePromise(code));
|
|
2199
2289
|
});
|
|
2200
2290
|
}
|
|
2291
|
+
function createStartupSpinner(url, port) {
|
|
2292
|
+
if (!process.stdout.isTTY) {
|
|
2293
|
+
console.log(`[startup] Loading Vibeman... waiting for API on port ${port}`);
|
|
2294
|
+
return {
|
|
2295
|
+
stop: (result) => {
|
|
2296
|
+
if (result === "ready") {
|
|
2297
|
+
console.log(`[startup] Vibeman API is ready: ${url}`);
|
|
2298
|
+
return;
|
|
2299
|
+
}
|
|
2300
|
+
console.log(`[startup] Still loading after timeout; opening browser: ${url}`);
|
|
2301
|
+
}
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
const frames = ["|", "/", "-", "\\"];
|
|
2305
|
+
let frameIndex = 0;
|
|
2306
|
+
const startMs = Date.now();
|
|
2307
|
+
process.stdout.write(`\r${frames[frameIndex]} Loading Vibeman... waiting for API on port ${port} `);
|
|
2308
|
+
const timer = setInterval(() => {
|
|
2309
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
2310
|
+
const elapsed = ((Date.now() - startMs) / 1000).toFixed(1);
|
|
2311
|
+
process.stdout.write(`\r${frames[frameIndex]} Loading Vibeman... waiting for API on port ${port} (${elapsed}s) `);
|
|
2312
|
+
}, 120);
|
|
2313
|
+
return {
|
|
2314
|
+
stop: (result) => {
|
|
2315
|
+
clearInterval(timer);
|
|
2316
|
+
const elapsed = ((Date.now() - startMs) / 1000).toFixed(1);
|
|
2317
|
+
if (result === "ready") {
|
|
2318
|
+
process.stdout.write(`\r✓ Vibeman API ready in ${elapsed}s. Opening ${url}
|
|
2319
|
+
`);
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
2322
|
+
process.stdout.write(`\r! Vibeman startup is taking longer than expected (${elapsed}s). Opening ${url}
|
|
2323
|
+
`);
|
|
2324
|
+
}
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2201
2327
|
async function waitForApiReady(port, timeoutMs = 30000, intervalMs = 250) {
|
|
2202
2328
|
const startedAt = Date.now();
|
|
2203
2329
|
while (Date.now() - startedAt < timeoutMs) {
|
|
@@ -2233,21 +2359,8 @@ function delay(ms) {
|
|
|
2233
2359
|
setTimeout(resolvePromise, ms);
|
|
2234
2360
|
});
|
|
2235
2361
|
}
|
|
2236
|
-
function
|
|
2237
|
-
|
|
2238
|
-
const candidates = [
|
|
2239
|
-
selfDir,
|
|
2240
|
-
resolve2(execDir, ".."),
|
|
2241
|
-
resolve2(execDir, "../.."),
|
|
2242
|
-
resolve2(selfDir, ".."),
|
|
2243
|
-
resolve2(selfDir, "../..")
|
|
2244
|
-
];
|
|
2245
|
-
for (const candidate of candidates) {
|
|
2246
|
-
if (existsSync2(resolve2(candidate, "api.js")) && existsSync2(resolve2(candidate, "ui", "index.html"))) {
|
|
2247
|
-
return candidate;
|
|
2248
|
-
}
|
|
2249
|
-
}
|
|
2250
|
-
return "";
|
|
2362
|
+
function resolvePackagedDistRoot(moduleDir) {
|
|
2363
|
+
return moduleDir.endsWith("/dist") || moduleDir.endsWith("\\dist") ? moduleDir : resolve2(moduleDir, "..", "dist");
|
|
2251
2364
|
}
|
|
2252
2365
|
function resolveNodePath() {
|
|
2253
2366
|
const execBase = basename(process.execPath);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- AlterTable
|
|
2
|
+
ALTER TABLE "WorkflowRun" ADD COLUMN "completedAt" DATETIME;
|
|
3
|
+
ALTER TABLE "WorkflowRun" ADD COLUMN "durationMs" INTEGER;
|
|
4
|
+
ALTER TABLE "WorkflowRun" ADD COLUMN "startedAt" DATETIME;
|
|
5
|
+
|
|
6
|
+
-- AlterTable
|
|
7
|
+
ALTER TABLE "WorkflowRunStepResult" ADD COLUMN "completedAt" DATETIME;
|
|
8
|
+
ALTER TABLE "WorkflowRunStepResult" ADD COLUMN "durationMs" INTEGER;
|
|
9
|
+
ALTER TABLE "WorkflowRunStepResult" ADD COLUMN "startedAt" DATETIME;
|
|
@@ -67,6 +67,9 @@ model WorkflowRun {
|
|
|
67
67
|
runPayload Json?
|
|
68
68
|
initialRunPayload Json?
|
|
69
69
|
lastError String?
|
|
70
|
+
startedAt DateTime?
|
|
71
|
+
completedAt DateTime?
|
|
72
|
+
durationMs Int?
|
|
70
73
|
deletedAt DateTime?
|
|
71
74
|
createdAt DateTime @default(now())
|
|
72
75
|
updatedAt DateTime @updatedAt
|
|
@@ -95,6 +98,9 @@ model WorkflowRunStepResult {
|
|
|
95
98
|
messages Json?
|
|
96
99
|
graphState Json?
|
|
97
100
|
tokenUsage Json?
|
|
101
|
+
startedAt DateTime?
|
|
102
|
+
completedAt DateTime?
|
|
103
|
+
durationMs Int?
|
|
98
104
|
createdAt DateTime @default(now())
|
|
99
105
|
updatedAt DateTime @updatedAt
|
|
100
106
|
|
package/dist/prisma.config.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
1
3
|
import { defineConfig } from 'prisma/config';
|
|
2
4
|
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
3
7
|
export default defineConfig({
|
|
4
8
|
schema: 'prisma/schema.prisma',
|
|
5
9
|
datasource: {
|
|
6
|
-
url: process.env.DATABASE_URL ?? '
|
|
10
|
+
url: process.env.DATABASE_URL ?? `file:${resolve(__dirname, 'prisma/dev.db')}`,
|
|
7
11
|
},
|
|
8
12
|
});
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
TEST_FIXTURE_WORKFLOWS,
|
|
12
12
|
buildTaskMarkdown,
|
|
13
13
|
getRepoRoot,
|
|
14
|
-
readTaskTemplate,
|
|
15
14
|
taskFilename,
|
|
16
15
|
taskRelativePath,
|
|
17
16
|
} from './lib/test-fixtures.mjs';
|
|
@@ -104,12 +103,6 @@ async function ensureDirectoryIsEmpty(targetRoot, force) {
|
|
|
104
103
|
}
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
async function writeTaskTemplate(targetRoot) {
|
|
108
|
-
const templatePath = resolve(targetRoot, '.vibeman/templates/task.md');
|
|
109
|
-
await mkdir(resolve(targetRoot, '.vibeman/templates'), { recursive: true });
|
|
110
|
-
await writeFile(templatePath, readTaskTemplate(), 'utf8');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
106
|
async function writeFixtureTaskFiles(targetRoot) {
|
|
114
107
|
const tasksDir = resolve(targetRoot, '.vibeman/tasks');
|
|
115
108
|
await mkdir(tasksDir, { recursive: true });
|
|
@@ -206,7 +199,6 @@ async function main() {
|
|
|
206
199
|
const targetRoot = resolve(process.cwd(), args.path || '.');
|
|
207
200
|
|
|
208
201
|
await ensureDirectoryIsEmpty(targetRoot, args.force);
|
|
209
|
-
await writeTaskTemplate(targetRoot);
|
|
210
202
|
await writeFixtureTaskFiles(targetRoot);
|
|
211
203
|
await upsertGitignore(targetRoot);
|
|
212
204
|
await ensureGitRepo(targetRoot);
|
|
@@ -1,9 +1,66 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
1
|
import { dirname, resolve } from 'node:path';
|
|
3
2
|
import { fileURLToPath } from 'node:url';
|
|
4
3
|
|
|
5
4
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
6
5
|
const repoRoot = resolve(moduleDir, '..', '..');
|
|
6
|
+
const TASK_TEMPLATE = `---
|
|
7
|
+
# For human readability + AI parsing.
|
|
8
|
+
# For type/status/priority: values must match the options exactly and be lowercase.
|
|
9
|
+
id: [type]-[short-id]
|
|
10
|
+
title: [Short, descriptive title]
|
|
11
|
+
type: feature # feature, bug, chore, refactor, test, doc, other
|
|
12
|
+
status: backlog # backlog, in-progress, review, done
|
|
13
|
+
tags: [tag1, tag2]
|
|
14
|
+
priority: medium # low, medium, high
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Goal
|
|
18
|
+
|
|
19
|
+
[1-2 sentences: why this task exists and the end-user outcome. Do NOT list features here — save those for Requirements.]
|
|
20
|
+
|
|
21
|
+
## Context
|
|
22
|
+
|
|
23
|
+
[Current state: how things work today, relevant code locations, links. State facts only — no actions or constraints.]
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
[Each item = a verifiable condition that must be true when the task is done. Write as testable outcomes, not implementation actions. This is the single source of "what success looks like" — do not restate these in other sections.]
|
|
28
|
+
|
|
29
|
+
- [ ] Outcome #1
|
|
30
|
+
- [ ] Outcome #2
|
|
31
|
+
|
|
32
|
+
## Implementation Notes
|
|
33
|
+
|
|
34
|
+
[Reference-only info for the implementer. No action verbs — use Recommended Steps for that.
|
|
35
|
+
|
|
36
|
+
- target files or directories
|
|
37
|
+
- patterns or conventions to follow
|
|
38
|
+
- commands to run for validation
|
|
39
|
+
- out-of-scope items or things not to change]
|
|
40
|
+
|
|
41
|
+
## Recommended Steps
|
|
42
|
+
|
|
43
|
+
[The only place that describes HOW to do the work. Organize by implementation phase, not by requirement. Each step should advance multiple requirements at once when possible.]
|
|
44
|
+
|
|
45
|
+
- Step 1: [Phase name]
|
|
46
|
+
- [ ] Action item
|
|
47
|
+
- [ ] Action item
|
|
48
|
+
- Step 2: [Phase name]
|
|
49
|
+
- [ ] Action item
|
|
50
|
+
|
|
51
|
+
## Open Questions
|
|
52
|
+
|
|
53
|
+
[Unresolved decisions or ambiguities. Use the structure below so responses are explicit and easy to fill in.]
|
|
54
|
+
|
|
55
|
+
- Question: [Open question #1?]
|
|
56
|
+
- Answer: [Pending]
|
|
57
|
+
- Question: [Open question #2?]
|
|
58
|
+
- Answer: [Pending]
|
|
59
|
+
|
|
60
|
+
## Implementation Summary
|
|
61
|
+
|
|
62
|
+
[Auto-generated after completion: what changed + tests run.]
|
|
63
|
+
`;
|
|
7
64
|
|
|
8
65
|
const DEFAULT_CODEX_MODEL = 'gpt-5.3-codex';
|
|
9
66
|
const LOW_COST_GEMINI_MODEL = 'gemini-3-flash-preview';
|
|
@@ -812,7 +869,7 @@ export function taskRelativePath(taskId) {
|
|
|
812
869
|
}
|
|
813
870
|
|
|
814
871
|
export function readTaskTemplate() {
|
|
815
|
-
return
|
|
872
|
+
return TASK_TEMPLATE;
|
|
816
873
|
}
|
|
817
874
|
|
|
818
875
|
function replaceOnce(input, from, to) {
|
|
@@ -867,31 +924,25 @@ export function buildTaskMarkdown(taskDef, nowIso = new Date().toISOString()) {
|
|
|
867
924
|
`created_at: '${nowIso}'\nupdated_at: '${nowIso}'\n---\n\n## Goal`,
|
|
868
925
|
);
|
|
869
926
|
|
|
927
|
+
task = replaceOnce(task, /\[1-2 sentences:[^\]]+\]/, taskDef.goal);
|
|
870
928
|
task = replaceOnce(
|
|
871
929
|
task,
|
|
872
|
-
|
|
873
|
-
taskDef.goal,
|
|
874
|
-
);
|
|
875
|
-
task = replaceOnce(
|
|
876
|
-
task,
|
|
877
|
-
'[Key background, links, or constraints that matter to implementation.]',
|
|
930
|
+
/\[Current state:[^\]]+\]/,
|
|
878
931
|
`${taskDef.context}\n\nTarget workflow: \`${taskDef.workflowId}\`.`,
|
|
879
932
|
);
|
|
880
933
|
|
|
881
|
-
task = replaceOnce(task, '- [ ]
|
|
882
|
-
task = replaceOnce(task, '- [ ]
|
|
934
|
+
task = replaceOnce(task, '- [ ] Outcome #1', `- [ ] ${requirementA}`);
|
|
935
|
+
task = replaceOnce(task, '- [ ] Outcome #2', `- [ ] ${requirementB}`);
|
|
883
936
|
|
|
884
937
|
task = replaceOnce(
|
|
885
938
|
task,
|
|
886
|
-
|
|
939
|
+
/\[Reference-only info for the implementer\.[\s\S]+?not to change\]/,
|
|
887
940
|
notes,
|
|
888
941
|
);
|
|
889
942
|
|
|
890
|
-
task = replaceOnce(task, '- [ ]
|
|
891
|
-
task = replaceOnce(task, '- [ ]
|
|
892
|
-
|
|
893
|
-
task = replaceOnce(task, '- [ ] Verifiable outcome #1', `- [ ] ${acceptanceA}`);
|
|
894
|
-
task = replaceOnce(task, '- [ ] Verifiable outcome #2', `- [ ] ${acceptanceB}`);
|
|
943
|
+
task = replaceOnce(task, '- [ ] Action item', `- [ ] ${stepA}`);
|
|
944
|
+
task = replaceOnce(task, '- [ ] Action item', `- [ ] ${stepB}`);
|
|
945
|
+
task = `${task.trim()}\n\n## Acceptance Criteria\n\n- [ ] ${acceptanceA}\n- [ ] ${acceptanceB}\n`;
|
|
895
946
|
|
|
896
947
|
return task;
|
|
897
948
|
}
|
|
@@ -3,7 +3,7 @@ import { spawn } from 'node:child_process';
|
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { mkdir } from 'node:fs/promises';
|
|
5
5
|
import { dirname, resolve } from 'node:path';
|
|
6
|
-
import {
|
|
6
|
+
import { PrismaLibSql } from '@prisma/adapter-libsql';
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
PRIMARY_TEST_WORKFLOW_ID,
|
|
@@ -214,7 +214,7 @@ async function main() {
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
const PrismaClient = await resolvePrismaClient();
|
|
217
|
-
const adapter = new
|
|
217
|
+
const adapter = new PrismaLibSql({ url: databaseUrl });
|
|
218
218
|
const prisma = new PrismaClient({ adapter });
|
|
219
219
|
|
|
220
220
|
try {
|