@brainervirus/workit-opencode 0.8.12 → 0.9.2
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/plugin.js +400 -162
- package/package.json +2 -2
package/dist/plugin.js
CHANGED
|
@@ -14,7 +14,7 @@ var __export = (target, all) => {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// packages/workit-opencode/src/plugin.ts
|
|
17
|
-
import { existsSync as
|
|
17
|
+
import { existsSync as existsSync22, readdirSync as readdirSync13 } from "node:fs";
|
|
18
18
|
import path39 from "node:path";
|
|
19
19
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
20
20
|
|
|
@@ -455,7 +455,7 @@ var shouldInjectConfigGuard = (currentText) => !currentText.includes(CONFIG_GUAR
|
|
|
455
455
|
var shouldInjectIssueRail = (currentText) => !currentText.includes(ISSUE_RAIL_TEXT);
|
|
456
456
|
|
|
457
457
|
// packages/workit-core/src/core/detector.ts
|
|
458
|
-
import { existsSync as
|
|
458
|
+
import { existsSync as existsSync17, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "node:fs";
|
|
459
459
|
import path23 from "node:path";
|
|
460
460
|
|
|
461
461
|
// packages/workit-core/src/core/init.ts
|
|
@@ -2115,8 +2115,21 @@ import path13 from "node:path";
|
|
|
2115
2115
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2116
2116
|
|
|
2117
2117
|
// packages/workit-core/src/core/branch.ts
|
|
2118
|
-
import {
|
|
2118
|
+
import {
|
|
2119
|
+
copyFileSync as copyFileSync3,
|
|
2120
|
+
cpSync as cpSync3,
|
|
2121
|
+
existsSync as existsSync9,
|
|
2122
|
+
mkdirSync as mkdirSync5,
|
|
2123
|
+
readFileSync as readFileSync10,
|
|
2124
|
+
readdirSync as readdirSync5,
|
|
2125
|
+
renameSync as renameSync2,
|
|
2126
|
+
rmSync as rmSync2,
|
|
2127
|
+
statSync as statSync4,
|
|
2128
|
+
writeFileSync as writeFileSync8
|
|
2129
|
+
} from "node:fs";
|
|
2119
2130
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
2131
|
+
import { createHash } from "node:crypto";
|
|
2132
|
+
import { tmpdir } from "node:os";
|
|
2120
2133
|
import path12 from "node:path";
|
|
2121
2134
|
|
|
2122
2135
|
// packages/workit-core/src/core/git.ts
|
|
@@ -2200,6 +2213,7 @@ var baseBranch = (cwd) => {
|
|
|
2200
2213
|
};
|
|
2201
2214
|
var DECLARE_RE = /^\s*\*+Branch:\*+\s*`?([^`\s|]+)`?\s*$/gim;
|
|
2202
2215
|
var USE_CURRENT_RE = /^\s*\*+Branch:\*+\s*use-current\s*$/im;
|
|
2216
|
+
var toPosix = (p) => p.split(path12.sep).join("/");
|
|
2203
2217
|
var readSafe2 = (p) => {
|
|
2204
2218
|
try {
|
|
2205
2219
|
return readFileSync10(p, "utf8");
|
|
@@ -2335,10 +2349,7 @@ var docsBranch = ({
|
|
|
2335
2349
|
}
|
|
2336
2350
|
return { error: `cannot resolve docs branch from HEAD ${JSON.stringify(current)}` };
|
|
2337
2351
|
};
|
|
2338
|
-
var
|
|
2339
|
-
const git = gitContext(cwd);
|
|
2340
|
-
if (!git.branch || git.branch === "unknown")
|
|
2341
|
-
return { ok: false, error: "not in a git repository" };
|
|
2352
|
+
var originBaseReady = (cwd, base) => {
|
|
2342
2353
|
const run2 = (args) => execFileSync2("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
2343
2354
|
try {
|
|
2344
2355
|
try {
|
|
@@ -2346,20 +2357,28 @@ var ensureBaseBranch = (cwd, base) => {
|
|
|
2346
2357
|
} catch {
|
|
2347
2358
|
run2(["fetch", "origin", "--prune"]);
|
|
2348
2359
|
}
|
|
2349
|
-
let hasOriginBase = true;
|
|
2350
2360
|
try {
|
|
2351
2361
|
execFileSync2("git", ["show-ref", "--verify", "--quiet", `refs/remotes/origin/${base}`], {
|
|
2352
2362
|
cwd,
|
|
2353
2363
|
stdio: "pipe"
|
|
2354
2364
|
});
|
|
2355
2365
|
} catch {
|
|
2356
|
-
hasOriginBase = false;
|
|
2357
|
-
}
|
|
2358
|
-
if (!hasOriginBase)
|
|
2359
2366
|
return {
|
|
2360
2367
|
ok: false,
|
|
2361
2368
|
error: `origin/${base} missing — push ${base} before creating feature/* or bugfix/* branches`
|
|
2362
2369
|
};
|
|
2370
|
+
}
|
|
2371
|
+
return { ok: true };
|
|
2372
|
+
} catch (error) {
|
|
2373
|
+
return {
|
|
2374
|
+
ok: false,
|
|
2375
|
+
error: error instanceof Error ? error.message : "ensure-base-branch failed"
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
var fastForwardBase = (cwd, base) => {
|
|
2380
|
+
const run2 = (args) => execFileSync2("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
2381
|
+
try {
|
|
2363
2382
|
let hasLocalBase = true;
|
|
2364
2383
|
try {
|
|
2365
2384
|
execFileSync2("git", ["show-ref", "--verify", "--quiet", `refs/heads/${base}`], {
|
|
@@ -2385,12 +2404,95 @@ var ensureBaseBranch = (cwd, base) => {
|
|
|
2385
2404
|
};
|
|
2386
2405
|
}
|
|
2387
2406
|
};
|
|
2407
|
+
var ensureBaseBranch = (cwd, base) => {
|
|
2408
|
+
const git = gitContext(cwd);
|
|
2409
|
+
if (!git.branch || git.branch === "unknown")
|
|
2410
|
+
return { ok: false, error: "not in a git repository" };
|
|
2411
|
+
const ready = originBaseReady(cwd, base);
|
|
2412
|
+
if (!ready.ok)
|
|
2413
|
+
return ready;
|
|
2414
|
+
return fastForwardBase(cwd, base);
|
|
2415
|
+
};
|
|
2416
|
+
var purgeStaleFlowGuardRoots = (now) => {
|
|
2417
|
+
const cutoff = now - 24 * 3600000;
|
|
2418
|
+
let entries;
|
|
2419
|
+
try {
|
|
2420
|
+
entries = readdirSync5(tmpdir());
|
|
2421
|
+
} catch {
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
for (const entry of entries) {
|
|
2425
|
+
if (!entry.startsWith("workit-flow-guard-"))
|
|
2426
|
+
continue;
|
|
2427
|
+
try {
|
|
2428
|
+
if (statSync4(path12.join(tmpdir(), entry)).mtimeMs < cutoff)
|
|
2429
|
+
rmSync2(path12.join(tmpdir(), entry), { recursive: true, force: true });
|
|
2430
|
+
} catch {}
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2433
|
+
var snapshotFlowState = (cwd) => {
|
|
2434
|
+
purgeStaleFlowGuardRoots(Date.now());
|
|
2435
|
+
const root2 = path12.join(tmpdir(), `workit-flow-guard-${createHash("sha256").update(path12.resolve(cwd)).digest("hex").slice(0, 16)}.${process.pid}.${process.hrtime.bigint()}`);
|
|
2436
|
+
mkdirSync5(root2, { recursive: true });
|
|
2437
|
+
const docsDir = path12.join(path12.resolve(cwd), "docs");
|
|
2438
|
+
let slugs = [];
|
|
2439
|
+
try {
|
|
2440
|
+
slugs = readdirSync5(docsDir);
|
|
2441
|
+
} catch {
|
|
2442
|
+
return root2;
|
|
2443
|
+
}
|
|
2444
|
+
for (const slug of slugs) {
|
|
2445
|
+
for (const rel of [["sdd", "flow.json"], ["spec.md"], ["plan.md"]]) {
|
|
2446
|
+
const src = path12.join(docsDir, slug, ...rel);
|
|
2447
|
+
try {
|
|
2448
|
+
if (!statSync4(src).isFile())
|
|
2449
|
+
continue;
|
|
2450
|
+
} catch {
|
|
2451
|
+
continue;
|
|
2452
|
+
}
|
|
2453
|
+
const dest = path12.join(root2, "docs", slug, ...rel);
|
|
2454
|
+
mkdirSync5(path12.dirname(dest), { recursive: true });
|
|
2455
|
+
cpSync3(src, dest);
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
return root2;
|
|
2459
|
+
};
|
|
2460
|
+
var restoreFlowSnapshot = (snapDir, cwd) => {
|
|
2461
|
+
const walk = (dir, rel) => readdirSync5(dir, { withFileTypes: true }).flatMap((entry) => entry.isDirectory() ? walk(path12.join(dir, entry.name), path12.join(rel, entry.name)) : [path12.join(rel, entry.name)]);
|
|
2462
|
+
const restored = [];
|
|
2463
|
+
try {
|
|
2464
|
+
const workspace = path12.resolve(cwd);
|
|
2465
|
+
for (const rel of walk(snapDir, "")) {
|
|
2466
|
+
const dest = path12.join(workspace, rel);
|
|
2467
|
+
if (existsSync9(dest))
|
|
2468
|
+
continue;
|
|
2469
|
+
mkdirSync5(path12.dirname(dest), { recursive: true });
|
|
2470
|
+
const tmpDest = `${dest}.tmp-${process.pid}`;
|
|
2471
|
+
try {
|
|
2472
|
+
copyFileSync3(path12.join(snapDir, rel), tmpDest);
|
|
2473
|
+
renameSync2(tmpDest, dest);
|
|
2474
|
+
} catch (error) {
|
|
2475
|
+
rmSync2(tmpDest, { force: true });
|
|
2476
|
+
throw error;
|
|
2477
|
+
}
|
|
2478
|
+
restored.push(toPosix(rel));
|
|
2479
|
+
}
|
|
2480
|
+
rmSync2(snapDir, { recursive: true, force: true });
|
|
2481
|
+
return { restored };
|
|
2482
|
+
} catch (error) {
|
|
2483
|
+
return {
|
|
2484
|
+
restored,
|
|
2485
|
+
warning: `flow state snapshot restore failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
};
|
|
2388
2489
|
var branchSetup = ({
|
|
2389
2490
|
action,
|
|
2390
2491
|
sdd_dir,
|
|
2391
2492
|
target_branch,
|
|
2392
2493
|
stash,
|
|
2393
|
-
workspace_root
|
|
2494
|
+
workspace_root,
|
|
2495
|
+
log
|
|
2394
2496
|
}) => {
|
|
2395
2497
|
const cwd = path12.resolve(workspace_root);
|
|
2396
2498
|
const exec = (args) => execFileSync2("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
@@ -2409,20 +2511,74 @@ var branchSetup = ({
|
|
|
2409
2511
|
};
|
|
2410
2512
|
const writeManifest = (data) => writeFileSync8(manifestPath, JSON.stringify(data, null, 2) + `
|
|
2411
2513
|
`, "utf8");
|
|
2514
|
+
let snapDir = null;
|
|
2515
|
+
const journal = (message) => log?.(`flow-guard: ${message}`);
|
|
2516
|
+
const snapshotRelPaths = () => {
|
|
2517
|
+
const dir = snapDir;
|
|
2518
|
+
if (!dir)
|
|
2519
|
+
return [];
|
|
2520
|
+
try {
|
|
2521
|
+
const walk = (from, rel) => readdirSync5(from, { withFileTypes: true }).flatMap((entry) => entry.isDirectory() ? walk(path12.join(from, entry.name), path12.join(rel, entry.name)) : [path12.join(rel, entry.name)]);
|
|
2522
|
+
return walk(dir, "").map(toPosix);
|
|
2523
|
+
} catch {
|
|
2524
|
+
return [];
|
|
2525
|
+
}
|
|
2526
|
+
};
|
|
2527
|
+
const journalSnapshot = () => {
|
|
2528
|
+
const dir = snapDir;
|
|
2529
|
+
if (!dir || !log)
|
|
2530
|
+
return;
|
|
2531
|
+
const rels = snapshotRelPaths();
|
|
2532
|
+
journal(`snapshot: ${rels.length} file(s)`);
|
|
2533
|
+
for (const rel of rels) {
|
|
2534
|
+
let shortHash = "";
|
|
2535
|
+
try {
|
|
2536
|
+
shortHash = createHash("sha256").update(readFileSync10(path12.join(dir, rel))).digest("hex").slice(0, 8);
|
|
2537
|
+
} catch {}
|
|
2538
|
+
journal(`snapshot: ${rel} sha=${shortHash}`);
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2541
|
+
const journalPresence = (phase) => {
|
|
2542
|
+
for (const rel of snapshotRelPaths()) {
|
|
2543
|
+
journal(`${phase}: ${rel} ${existsSync9(path12.join(cwd, rel)) ? "present" : "MISSING"}`);
|
|
2544
|
+
}
|
|
2545
|
+
};
|
|
2546
|
+
const restoreWithWarning = (dir) => {
|
|
2547
|
+
if (!dir)
|
|
2548
|
+
return { restored: [], warnings: [] };
|
|
2549
|
+
const total = log ? snapshotRelPaths().length : 0;
|
|
2550
|
+
const { restored, warning } = restoreFlowSnapshot(dir, cwd);
|
|
2551
|
+
if (log)
|
|
2552
|
+
journal(`restore: restored=${restored.length} skipped=${total - restored.length}${warning ? ` warning: ${warning}` : ""}`);
|
|
2553
|
+
else if (warning)
|
|
2554
|
+
journal(`restore warning: ${warning}`);
|
|
2555
|
+
return { restored, warnings: warning ? [warning] : [] };
|
|
2556
|
+
};
|
|
2412
2557
|
if (action === "reapply_stash") {
|
|
2413
|
-
const
|
|
2414
|
-
const ref =
|
|
2558
|
+
const manifest = readManifest();
|
|
2559
|
+
const ref = manifest.stash_ref;
|
|
2415
2560
|
if (!ref)
|
|
2416
2561
|
return { error: "no stash_ref in manifest" };
|
|
2562
|
+
snapDir = snapshotFlowState(cwd);
|
|
2563
|
+
journalSnapshot();
|
|
2564
|
+
journal(`pre-pop: ${String(ref)}`);
|
|
2417
2565
|
try {
|
|
2418
2566
|
exec(["stash", "pop", String(ref)]);
|
|
2419
2567
|
} catch (error) {
|
|
2420
|
-
|
|
2568
|
+
journal("pop: failed");
|
|
2569
|
+
const {
|
|
2570
|
+
warnings: [warning]
|
|
2571
|
+
} = restoreWithWarning(snapDir);
|
|
2572
|
+
return {
|
|
2573
|
+
error: `${error instanceof Error ? error.message : "stash pop failed"}${warning ? `; ${warning}` : ""}`
|
|
2574
|
+
};
|
|
2421
2575
|
}
|
|
2422
|
-
|
|
2423
|
-
delete
|
|
2424
|
-
|
|
2425
|
-
|
|
2576
|
+
journal("pop: ok");
|
|
2577
|
+
delete manifest.stash_ref;
|
|
2578
|
+
delete manifest.stash_created_at;
|
|
2579
|
+
writeManifest(manifest);
|
|
2580
|
+
const { warnings: warnings2 } = restoreWithWarning(snapDir);
|
|
2581
|
+
return { action: "reapply_stash", ok: true, ...warnings2.length > 0 ? { warnings: warnings2 } : {} };
|
|
2426
2582
|
}
|
|
2427
2583
|
const target = target_branch ?? "";
|
|
2428
2584
|
if (!target)
|
|
@@ -2431,61 +2587,141 @@ var branchSetup = ({
|
|
|
2431
2587
|
return { error: `protected branch ${target}` };
|
|
2432
2588
|
if (!allowedBranch(cwd, target))
|
|
2433
2589
|
return { error: `target branch ${target} is not allowed by the branch policy` };
|
|
2590
|
+
let base;
|
|
2591
|
+
let targetExists = true;
|
|
2592
|
+
try {
|
|
2593
|
+
exec(["rev-parse", "--verify", "--quiet", `refs/heads/${target}`]);
|
|
2594
|
+
} catch {
|
|
2595
|
+
targetExists = false;
|
|
2596
|
+
}
|
|
2597
|
+
if (!targetExists) {
|
|
2598
|
+
const baseResolved = baseBranch(cwd);
|
|
2599
|
+
if ("error" in baseResolved)
|
|
2600
|
+
return { error: baseResolved.error };
|
|
2601
|
+
base = baseResolved.base;
|
|
2602
|
+
}
|
|
2603
|
+
journal(`entry: current=${current} target=${target} base=${base ?? "-"}`);
|
|
2434
2604
|
let stash_ref;
|
|
2605
|
+
const failAfterStash = (message) => {
|
|
2606
|
+
let suffix = "";
|
|
2607
|
+
if (stash_ref) {
|
|
2608
|
+
journal(`pre-pop: ${stash_ref}`);
|
|
2609
|
+
try {
|
|
2610
|
+
exec(["stash", "pop", stash_ref]);
|
|
2611
|
+
stash_ref = undefined;
|
|
2612
|
+
journal("pop: ok");
|
|
2613
|
+
} catch {
|
|
2614
|
+
journal("pop: failed");
|
|
2615
|
+
suffix = " (changes preserved in stash)";
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
const {
|
|
2619
|
+
warnings: [warning]
|
|
2620
|
+
} = restoreWithWarning(snapDir);
|
|
2621
|
+
return { error: `${message}${suffix}${warning ? `; ${warning}` : ""}` };
|
|
2622
|
+
};
|
|
2435
2623
|
if (current !== target) {
|
|
2436
2624
|
const dirty = Boolean(gitContext(cwd).status_short.trim());
|
|
2625
|
+
if (dirty && stash !== "yes") {
|
|
2626
|
+
return {
|
|
2627
|
+
error: "dirty working tree — ask with native question, then call workit_branch_setup with stash=yes"
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
let validatedBase;
|
|
2631
|
+
if (!targetExists && base !== undefined) {
|
|
2632
|
+
const ready = originBaseReady(cwd, base);
|
|
2633
|
+
if (!ready.ok)
|
|
2634
|
+
return { error: ready.error ?? "ensure-base-branch failed" };
|
|
2635
|
+
validatedBase = base;
|
|
2636
|
+
}
|
|
2437
2637
|
if (dirty) {
|
|
2438
|
-
if (stash !== "yes") {
|
|
2439
|
-
return {
|
|
2440
|
-
error: "dirty working tree — ask with native question, then call workit_branch_setup with stash=yes"
|
|
2441
|
-
};
|
|
2442
|
-
}
|
|
2443
2638
|
try {
|
|
2639
|
+
snapDir = snapshotFlowState(cwd);
|
|
2640
|
+
journalSnapshot();
|
|
2444
2641
|
exec(["stash", "push", "-u", "-m", `workit: pre-checkout ${target}`, "--", ":!docs/*/sdd"]);
|
|
2445
2642
|
} catch (error) {
|
|
2446
2643
|
return { error: error instanceof Error ? error.message : "stash push failed" };
|
|
2447
2644
|
}
|
|
2448
2645
|
stash_ref = "stash@{0}";
|
|
2646
|
+
journal(`stash push: ${stash_ref}`);
|
|
2449
2647
|
}
|
|
2450
2648
|
try {
|
|
2451
2649
|
exec(["checkout", target]);
|
|
2650
|
+
journalPresence("post-checkout");
|
|
2452
2651
|
} catch (error) {
|
|
2453
2652
|
const message = error instanceof Error ? error.message : "checkout failed";
|
|
2454
2653
|
if (/worktree/i.test(message)) {
|
|
2455
|
-
return {
|
|
2456
|
-
error: `branch ${target} is locked by an existing git worktree — remove it first (we do not use worktrees)`
|
|
2457
|
-
};
|
|
2654
|
+
return failAfterStash(`branch ${target} is locked by an existing git worktree — remove it first (we do not use worktrees)`);
|
|
2458
2655
|
}
|
|
2459
2656
|
try {
|
|
2460
|
-
|
|
2461
|
-
if (
|
|
2462
|
-
|
|
2463
|
-
|
|
2657
|
+
let effectiveBase = base;
|
|
2658
|
+
if (effectiveBase === undefined) {
|
|
2659
|
+
const lateResolved = baseBranch(cwd);
|
|
2660
|
+
if ("error" in lateResolved)
|
|
2661
|
+
return failAfterStash(lateResolved.error);
|
|
2662
|
+
effectiveBase = lateResolved.base;
|
|
2663
|
+
}
|
|
2664
|
+
const baseResult = validatedBase !== undefined ? fastForwardBase(cwd, effectiveBase) : ensureBaseBranch(cwd, effectiveBase);
|
|
2464
2665
|
if (!baseResult.ok)
|
|
2465
|
-
return
|
|
2666
|
+
return failAfterStash(baseResult.error ?? "ensure-base-branch failed");
|
|
2466
2667
|
exec(["checkout", "-b", target]);
|
|
2668
|
+
journalPresence("post-create");
|
|
2467
2669
|
} catch (createError) {
|
|
2468
|
-
return
|
|
2469
|
-
error: createError instanceof Error ? createError.message : "branch create failed"
|
|
2470
|
-
};
|
|
2670
|
+
return failAfterStash(createError instanceof Error ? createError.message : "branch create failed");
|
|
2471
2671
|
}
|
|
2472
2672
|
}
|
|
2473
2673
|
}
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2674
|
+
try {
|
|
2675
|
+
const manifest = readManifest();
|
|
2676
|
+
manifest.branch = target;
|
|
2677
|
+
manifest.previous_branch = current;
|
|
2678
|
+
if (stash_ref) {
|
|
2679
|
+
manifest.stash_ref = stash_ref;
|
|
2680
|
+
manifest.stash_created_at = new Date().toISOString();
|
|
2681
|
+
}
|
|
2682
|
+
writeManifest(manifest);
|
|
2683
|
+
} catch (error) {
|
|
2684
|
+
const result = failAfterStash(error instanceof Error ? `manifest update failed: ${error.message}` : "manifest update failed");
|
|
2685
|
+
if (stash_ref === undefined && gitContext(cwd).branch !== current) {
|
|
2686
|
+
try {
|
|
2687
|
+
exec(["checkout", current]);
|
|
2688
|
+
} catch {}
|
|
2689
|
+
}
|
|
2690
|
+
return result;
|
|
2691
|
+
}
|
|
2692
|
+
const { restored: restoredRels, warnings } = restoreWithWarning(snapDir);
|
|
2693
|
+
if (stash_ref && snapDir && warnings.length === 0) {
|
|
2694
|
+
const covered = new Set(restoredRels.map(toPosix));
|
|
2695
|
+
try {
|
|
2696
|
+
const stashed = exec([
|
|
2697
|
+
"stash",
|
|
2698
|
+
"show",
|
|
2699
|
+
"--include-untracked",
|
|
2700
|
+
"--name-only",
|
|
2701
|
+
String(stash_ref)
|
|
2702
|
+
]).split(`
|
|
2703
|
+
`).map((line) => toPosix(line.trim())).filter(Boolean);
|
|
2704
|
+
if (stashed.length > 0 && stashed.every((p) => covered.has(p))) {
|
|
2705
|
+
exec(["stash", "drop", String(stash_ref)]);
|
|
2706
|
+
stash_ref = undefined;
|
|
2707
|
+
journal("dropped redundant stash (fully covered by snapshot restore)");
|
|
2708
|
+
const manifest = readManifest();
|
|
2709
|
+
delete manifest.stash_ref;
|
|
2710
|
+
delete manifest.stash_created_at;
|
|
2711
|
+
writeManifest(manifest);
|
|
2712
|
+
} else {
|
|
2713
|
+
journal(`kept stash ref ${stash_ref} (not fully covered by actual restores)`);
|
|
2714
|
+
}
|
|
2715
|
+
} catch {}
|
|
2480
2716
|
}
|
|
2481
|
-
writeManifest(manifest);
|
|
2482
2717
|
return {
|
|
2483
2718
|
action: "setup",
|
|
2484
2719
|
ok: true,
|
|
2485
2720
|
branch: target,
|
|
2486
2721
|
previous_branch: current,
|
|
2487
2722
|
stash_ref: stash_ref ?? null,
|
|
2488
|
-
manifest: manifestPath
|
|
2723
|
+
manifest: manifestPath,
|
|
2724
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
2489
2725
|
};
|
|
2490
2726
|
};
|
|
2491
2727
|
|
|
@@ -2792,13 +3028,13 @@ import fs3 from "node:fs";
|
|
|
2792
3028
|
import path15 from "node:path";
|
|
2793
3029
|
|
|
2794
3030
|
// packages/workit-core/src/core/templates.ts
|
|
2795
|
-
import { existsSync as
|
|
3031
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "node:fs";
|
|
2796
3032
|
import path14 from "node:path";
|
|
2797
3033
|
var repoRoot2 = assetRoot();
|
|
2798
3034
|
var templatePath = (name) => path14.join(configDir(), "templates", `${name}.md`);
|
|
2799
3035
|
var readTemplate = (name) => {
|
|
2800
3036
|
const cfg = templatePath(name);
|
|
2801
|
-
if (
|
|
3037
|
+
if (existsSync10(cfg))
|
|
2802
3038
|
return { source: "config", content: readFileSync11(cfg, "utf8") };
|
|
2803
3039
|
return {
|
|
2804
3040
|
source: "repo",
|
|
@@ -2816,9 +3052,9 @@ var writeTemplate = (name, content, confirmed) => {
|
|
|
2816
3052
|
var listTemplates = () => ["issue-update", "greeting", "headers"].map((name) => {
|
|
2817
3053
|
const cfg = templatePath(name);
|
|
2818
3054
|
const repoFile = path14.join(repoRoot2, "templates", `${name}.md`);
|
|
2819
|
-
if (
|
|
3055
|
+
if (existsSync10(cfg))
|
|
2820
3056
|
return { name, source: "config", path: cfg };
|
|
2821
|
-
if (
|
|
3057
|
+
if (existsSync10(repoFile))
|
|
2822
3058
|
return { name, source: "repo", path: repoFile };
|
|
2823
3059
|
return { name, source: "missing", path: cfg };
|
|
2824
3060
|
});
|
|
@@ -3868,29 +4104,29 @@ function configGuardError(missing) {
|
|
|
3868
4104
|
// packages/workit-core/src/core/flow-state.ts
|
|
3869
4105
|
import {
|
|
3870
4106
|
closeSync,
|
|
3871
|
-
existsSync as
|
|
4107
|
+
existsSync as existsSync16,
|
|
3872
4108
|
fstatSync,
|
|
3873
4109
|
fsyncSync,
|
|
3874
4110
|
mkdirSync as mkdirSync9,
|
|
3875
4111
|
openSync,
|
|
3876
|
-
readdirSync as
|
|
4112
|
+
readdirSync as readdirSync7,
|
|
3877
4113
|
readFileSync as readFileSync16,
|
|
3878
|
-
renameSync as
|
|
3879
|
-
rmSync as
|
|
3880
|
-
statSync as
|
|
4114
|
+
renameSync as renameSync3,
|
|
4115
|
+
rmSync as rmSync3,
|
|
4116
|
+
statSync as statSync8,
|
|
3881
4117
|
unlinkSync as unlinkSync2,
|
|
3882
4118
|
writeFileSync as writeFileSync11
|
|
3883
4119
|
} from "node:fs";
|
|
3884
|
-
import { createHash } from "node:crypto";
|
|
4120
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3885
4121
|
import path22 from "node:path";
|
|
3886
4122
|
|
|
3887
4123
|
// packages/workit-core/src/core/docs-validate.ts
|
|
3888
|
-
import { existsSync as
|
|
4124
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "node:fs";
|
|
3889
4125
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
3890
4126
|
import path18 from "node:path";
|
|
3891
4127
|
|
|
3892
4128
|
// packages/workit-core/src/core/docs-layout.ts
|
|
3893
|
-
import { existsSync as
|
|
4129
|
+
import { existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync7, realpathSync as realpathSync2, statSync as statSync5 } from "node:fs";
|
|
3894
4130
|
import path17 from "node:path";
|
|
3895
4131
|
var SLUG_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
3896
4132
|
var LEGACY_SLUG = "superpowers";
|
|
@@ -3898,7 +4134,7 @@ var posix = (p) => p.split(path17.sep).join("/");
|
|
|
3898
4134
|
var canonicalize = (base, candidate) => {
|
|
3899
4135
|
const abs = path17.resolve(base, candidate);
|
|
3900
4136
|
let ancestor = abs;
|
|
3901
|
-
while (!
|
|
4137
|
+
while (!existsSync11(ancestor))
|
|
3902
4138
|
ancestor = path17.dirname(ancestor);
|
|
3903
4139
|
let real;
|
|
3904
4140
|
try {
|
|
@@ -3929,8 +4165,8 @@ var buildLayout = (workspace, slug) => {
|
|
|
3929
4165
|
};
|
|
3930
4166
|
};
|
|
3931
4167
|
var probeLegacyDocs = (workspace) => ({
|
|
3932
|
-
legacy_sdd:
|
|
3933
|
-
superpowers_dir:
|
|
4168
|
+
legacy_sdd: existsSync11(path17.join(workspace, ".superpowers", "sdd")),
|
|
4169
|
+
superpowers_dir: existsSync11(path17.join(workspace, "docs", LEGACY_SLUG))
|
|
3934
4170
|
});
|
|
3935
4171
|
var resolveCanonicalLayout = (input) => {
|
|
3936
4172
|
const { workspace_root, slug, spec_path, plan_path } = input;
|
|
@@ -4025,11 +4261,11 @@ var prepareDocsLayout = (input) => {
|
|
|
4025
4261
|
const { layout } = resolved;
|
|
4026
4262
|
const created = [];
|
|
4027
4263
|
const ensure = (dir) => {
|
|
4028
|
-
if (!
|
|
4264
|
+
if (!existsSync11(dir)) {
|
|
4029
4265
|
mkdirSync7(dir, { recursive: true });
|
|
4030
4266
|
const rel = posix(path17.relative(layout.workspace, dir));
|
|
4031
4267
|
created.push(rel || ".");
|
|
4032
|
-
} else if (!
|
|
4268
|
+
} else if (!statSync5(dir).isDirectory()) {
|
|
4033
4269
|
throw new Error(`path exists but is not a directory: ${posix(path17.relative(layout.workspace, dir))}`);
|
|
4034
4270
|
}
|
|
4035
4271
|
};
|
|
@@ -4353,7 +4589,7 @@ var qualitySpec = (text) => {
|
|
|
4353
4589
|
};
|
|
4354
4590
|
var sddIgnored = (cwd, slug) => {
|
|
4355
4591
|
const sddDir = path18.join(cwd, "docs", slug, "sdd");
|
|
4356
|
-
if (!
|
|
4592
|
+
if (!existsSync12(sddDir))
|
|
4357
4593
|
return true;
|
|
4358
4594
|
try {
|
|
4359
4595
|
execFileSync3("git", ["-C", cwd, "rev-parse", "--is-inside-work-tree"], { stdio: "pipe" });
|
|
@@ -4371,10 +4607,10 @@ var sddIgnored = (cwd, slug) => {
|
|
|
4371
4607
|
// packages/workit-core/src/core/sdd.ts
|
|
4372
4608
|
import {
|
|
4373
4609
|
appendFileSync,
|
|
4374
|
-
existsSync as
|
|
4610
|
+
existsSync as existsSync13,
|
|
4375
4611
|
mkdirSync as mkdirSync8,
|
|
4376
4612
|
readFileSync as readFileSync13,
|
|
4377
|
-
statSync as
|
|
4613
|
+
statSync as statSync6,
|
|
4378
4614
|
writeFileSync as writeFileSync10
|
|
4379
4615
|
} from "node:fs";
|
|
4380
4616
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
@@ -4399,7 +4635,7 @@ function ledgerCompletion(root2, slug) {
|
|
|
4399
4635
|
let started = false;
|
|
4400
4636
|
const completed = [];
|
|
4401
4637
|
const absProgress = path19.join(root2, "docs", slug, "sdd", "progress.md");
|
|
4402
|
-
if (
|
|
4638
|
+
if (existsSync13(absProgress)) {
|
|
4403
4639
|
try {
|
|
4404
4640
|
for (const line of readFileSync13(absProgress, "utf8").split(`
|
|
4405
4641
|
`)) {
|
|
@@ -4415,7 +4651,7 @@ function ledgerCompletion(root2, slug) {
|
|
|
4415
4651
|
const required = [];
|
|
4416
4652
|
try {
|
|
4417
4653
|
const absPlan = path19.join(root2, "docs", slug, "plan.md");
|
|
4418
|
-
if (
|
|
4654
|
+
if (existsSync13(absPlan)) {
|
|
4419
4655
|
for (const task of parseTasksFromPlan(readFileSync13(absPlan, "utf8")))
|
|
4420
4656
|
required.push(task.id);
|
|
4421
4657
|
}
|
|
@@ -4448,7 +4684,7 @@ function sddContext({
|
|
|
4448
4684
|
let progress_lines = [];
|
|
4449
4685
|
let completed_task_ids = [];
|
|
4450
4686
|
const absProgress = path19.join(resolved.layout.sdd, "progress.md");
|
|
4451
|
-
if (
|
|
4687
|
+
if (existsSync13(absProgress)) {
|
|
4452
4688
|
progress_lines = readFileSync13(absProgress, "utf8").split(`
|
|
4453
4689
|
`).map((ln) => ln.trim()).filter(Boolean);
|
|
4454
4690
|
const pat = /^Task\s+(\d+):\s+complete\b/i;
|
|
@@ -4456,7 +4692,7 @@ function sddContext({
|
|
|
4456
4692
|
}
|
|
4457
4693
|
let manifest = {};
|
|
4458
4694
|
const absManifest = path19.join(resolved.layout.sdd, "manifest.json");
|
|
4459
|
-
if (
|
|
4695
|
+
if (existsSync13(absManifest)) {
|
|
4460
4696
|
try {
|
|
4461
4697
|
manifest = JSON.parse(readFileSync13(absManifest, "utf8"));
|
|
4462
4698
|
} catch {
|
|
@@ -4464,7 +4700,7 @@ function sddContext({
|
|
|
4464
4700
|
}
|
|
4465
4701
|
}
|
|
4466
4702
|
const legacy_path = path19.join(cwd, ".superpowers/sdd");
|
|
4467
|
-
const legacy_exists =
|
|
4703
|
+
const legacy_exists = existsSync13(legacy_path);
|
|
4468
4704
|
let todos = [];
|
|
4469
4705
|
let task_count = 0;
|
|
4470
4706
|
if (plan_path) {
|
|
@@ -4495,7 +4731,7 @@ function sddContext({
|
|
|
4495
4731
|
progress_lines,
|
|
4496
4732
|
completed_task_ids,
|
|
4497
4733
|
manifest,
|
|
4498
|
-
created:
|
|
4734
|
+
created: existsSync13(path19.join(cwd, sdd_dir)),
|
|
4499
4735
|
forbidden_legacy_path: ".superpowers/sdd",
|
|
4500
4736
|
legacy_sdd_exists: legacy_exists,
|
|
4501
4737
|
warning: legacy_exists ? "Ignore .superpowers/sdd — use sdd_dir from this tool only" : undefined,
|
|
@@ -4579,7 +4815,7 @@ function sddAppendProgress({
|
|
|
4579
4815
|
if (!match || match[1].toLowerCase() === match[2].toLowerCase()) {
|
|
4580
4816
|
return { error: "invalid progress line format" };
|
|
4581
4817
|
}
|
|
4582
|
-
if (
|
|
4818
|
+
if (existsSync13(path_) && statSync6(path_).isDirectory()) {
|
|
4583
4819
|
return { error: `progress path is a directory: ${progress_path}` };
|
|
4584
4820
|
}
|
|
4585
4821
|
mkdirSync8(path19.dirname(path_), { recursive: true });
|
|
@@ -4627,7 +4863,7 @@ function sddAppendAdvisory({
|
|
|
4627
4863
|
if (!contained.ok)
|
|
4628
4864
|
return { error: contained.error, code: "advisory_path_invalid" };
|
|
4629
4865
|
const abs = contained.path;
|
|
4630
|
-
if (
|
|
4866
|
+
if (existsSync13(abs) && statSync6(abs).isDirectory()) {
|
|
4631
4867
|
return {
|
|
4632
4868
|
error: `advisory target is a directory: ${advisories_path}`,
|
|
4633
4869
|
code: "advisory_target_invalid"
|
|
@@ -4643,12 +4879,12 @@ function sddAppendAdvisory({
|
|
|
4643
4879
|
|
|
4644
4880
|
// packages/workit-core/src/core/verify-project.ts
|
|
4645
4881
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
4646
|
-
import { existsSync as
|
|
4882
|
+
import { existsSync as existsSync15, readFileSync as readFileSync15, statSync as statSync7 } from "node:fs";
|
|
4647
4883
|
import path21 from "node:path";
|
|
4648
4884
|
|
|
4649
4885
|
// packages/workit-core/src/core/repo-context.ts
|
|
4650
4886
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
4651
|
-
import { existsSync as
|
|
4887
|
+
import { existsSync as existsSync14, readFileSync as readFileSync14, readdirSync as readdirSync6 } from "node:fs";
|
|
4652
4888
|
import path20 from "node:path";
|
|
4653
4889
|
var runGit = (cwd, args) => {
|
|
4654
4890
|
const result = spawnSync4("git", args, { cwd, encoding: "utf8" });
|
|
@@ -4738,7 +4974,7 @@ var printSection = (title) => `
|
|
|
4738
4974
|
`;
|
|
4739
4975
|
var probeCaseInsensitive = (dir, wanted) => {
|
|
4740
4976
|
try {
|
|
4741
|
-
for (const entry of
|
|
4977
|
+
for (const entry of readdirSync6(dir)) {
|
|
4742
4978
|
if (entry.toLowerCase() === wanted.toLowerCase())
|
|
4743
4979
|
return entry;
|
|
4744
4980
|
}
|
|
@@ -4790,7 +5026,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
4790
5026
|
};
|
|
4791
5027
|
var packageScripts = (cwd, keys) => {
|
|
4792
5028
|
const pkgPath = path20.join(cwd, "package.json");
|
|
4793
|
-
if (!
|
|
5029
|
+
if (!existsSync14(pkgPath))
|
|
4794
5030
|
return [];
|
|
4795
5031
|
try {
|
|
4796
5032
|
const pkg = JSON.parse(readFileSync14(pkgPath, "utf8"));
|
|
@@ -4807,7 +5043,7 @@ var documentationFiles = (cwd) => {
|
|
|
4807
5043
|
return;
|
|
4808
5044
|
let entries;
|
|
4809
5045
|
try {
|
|
4810
|
-
entries =
|
|
5046
|
+
entries = readdirSync6(dir, { withFileTypes: true });
|
|
4811
5047
|
} catch {
|
|
4812
5048
|
return;
|
|
4813
5049
|
}
|
|
@@ -4825,7 +5061,7 @@ var documentationFiles = (cwd) => {
|
|
|
4825
5061
|
return matches.sort().slice(0, 200);
|
|
4826
5062
|
};
|
|
4827
5063
|
var readTrimmed = (file, maxLines) => {
|
|
4828
|
-
if (!
|
|
5064
|
+
if (!existsSync14(file))
|
|
4829
5065
|
return "";
|
|
4830
5066
|
const lines = readFileSync14(file, "utf8").split(`
|
|
4831
5067
|
`);
|
|
@@ -4899,7 +5135,7 @@ ${status.stderr}` : "");
|
|
|
4899
5135
|
stdout += `
|
|
4900
5136
|
`;
|
|
4901
5137
|
stdout += printSection("Recent Validation Signals");
|
|
4902
|
-
if (
|
|
5138
|
+
if (existsSync14(path20.join(cwd, "package.json"))) {
|
|
4903
5139
|
stdout += `package.json detected. Common scripts:
|
|
4904
5140
|
`;
|
|
4905
5141
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -4908,7 +5144,7 @@ ${status.stderr}` : "");
|
|
|
4908
5144
|
`) + `
|
|
4909
5145
|
`;
|
|
4910
5146
|
}
|
|
4911
|
-
if (
|
|
5147
|
+
if (existsSync14(path20.join(cwd, "Cargo.toml")) || existsSync14(path20.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
4912
5148
|
stdout += `Rust project detected.
|
|
4913
5149
|
`;
|
|
4914
5150
|
}
|
|
@@ -4956,7 +5192,7 @@ function changelogContext(root2, range) {
|
|
|
4956
5192
|
`;
|
|
4957
5193
|
stdout += printSection("Existing CHANGELOG.md");
|
|
4958
5194
|
const changelogPath = path20.join(cwd, "CHANGELOG.md");
|
|
4959
|
-
if (
|
|
5195
|
+
if (existsSync14(changelogPath)) {
|
|
4960
5196
|
stdout += readTrimmed(changelogPath, 260) + `
|
|
4961
5197
|
`;
|
|
4962
5198
|
} else {
|
|
@@ -4994,7 +5230,7 @@ function docsRefreshContext(root2, range) {
|
|
|
4994
5230
|
`;
|
|
4995
5231
|
stdout += printSection("README Preview");
|
|
4996
5232
|
const readme = path20.join(cwd, "README.md");
|
|
4997
|
-
if (
|
|
5233
|
+
if (existsSync14(readme)) {
|
|
4998
5234
|
stdout += readTrimmed(readme, 220) + `
|
|
4999
5235
|
`;
|
|
5000
5236
|
} else {
|
|
@@ -5003,7 +5239,7 @@ function docsRefreshContext(root2, range) {
|
|
|
5003
5239
|
}
|
|
5004
5240
|
stdout += printSection("Package Scripts");
|
|
5005
5241
|
const pkgPath = path20.join(cwd, "package.json");
|
|
5006
|
-
if (
|
|
5242
|
+
if (existsSync14(pkgPath)) {
|
|
5007
5243
|
try {
|
|
5008
5244
|
const pkg = JSON.parse(readFileSync14(pkgPath, "utf8"));
|
|
5009
5245
|
stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
|
|
@@ -5050,7 +5286,7 @@ function releaseNotesContext(root2, rangeOrTag) {
|
|
|
5050
5286
|
`;
|
|
5051
5287
|
stdout += printSection("Existing Release Files");
|
|
5052
5288
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
5053
|
-
if (
|
|
5289
|
+
if (existsSync14(path20.join(cwd, rel)))
|
|
5054
5290
|
stdout += rel + `
|
|
5055
5291
|
`;
|
|
5056
5292
|
}
|
|
@@ -5067,9 +5303,9 @@ var commandOnPath2 = (name) => {
|
|
|
5067
5303
|
for (const candidateName of names) {
|
|
5068
5304
|
const candidate = path21.join(dir, candidateName);
|
|
5069
5305
|
try {
|
|
5070
|
-
|
|
5306
|
+
statSync7(candidate);
|
|
5071
5307
|
if (process.platform !== "win32") {
|
|
5072
|
-
const mode =
|
|
5308
|
+
const mode = statSync7(candidate).mode & 73;
|
|
5073
5309
|
if (mode === 0)
|
|
5074
5310
|
continue;
|
|
5075
5311
|
}
|
|
@@ -5081,7 +5317,7 @@ var commandOnPath2 = (name) => {
|
|
|
5081
5317
|
};
|
|
5082
5318
|
var hasScript = (cwd, name) => {
|
|
5083
5319
|
const pkgPath = path21.join(cwd, "package.json");
|
|
5084
|
-
if (!
|
|
5320
|
+
if (!existsSync15(pkgPath))
|
|
5085
5321
|
return false;
|
|
5086
5322
|
try {
|
|
5087
5323
|
const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
|
|
@@ -5091,10 +5327,10 @@ var hasScript = (cwd, name) => {
|
|
|
5091
5327
|
}
|
|
5092
5328
|
};
|
|
5093
5329
|
var packageRunner = (cwd) => {
|
|
5094
|
-
if (
|
|
5330
|
+
if (existsSync15(path21.join(cwd, "pnpm-lock.yaml")))
|
|
5095
5331
|
return "pnpm";
|
|
5096
5332
|
const pkgPath = path21.join(cwd, "package.json");
|
|
5097
|
-
if (
|
|
5333
|
+
if (existsSync15(pkgPath)) {
|
|
5098
5334
|
try {
|
|
5099
5335
|
const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
|
|
5100
5336
|
if (typeof pkg.packageManager === "string" && pkg.packageManager.startsWith("pnpm")) {
|
|
@@ -5102,7 +5338,7 @@ var packageRunner = (cwd) => {
|
|
|
5102
5338
|
}
|
|
5103
5339
|
} catch {}
|
|
5104
5340
|
}
|
|
5105
|
-
if (
|
|
5341
|
+
if (existsSync15(path21.join(cwd, "yarn.lock")))
|
|
5106
5342
|
return "yarn";
|
|
5107
5343
|
return "npm";
|
|
5108
5344
|
};
|
|
@@ -5163,7 +5399,7 @@ root: ${cwd}
|
|
|
5163
5399
|
lines.push(`dry_run: ${String(dryRun)}
|
|
5164
5400
|
`);
|
|
5165
5401
|
const pkgPath = path21.join(cwd, "package.json");
|
|
5166
|
-
if (
|
|
5402
|
+
if (existsSync15(pkgPath)) {
|
|
5167
5403
|
const runner = packageRunner(cwd);
|
|
5168
5404
|
for (const script of ["lint", "format:check", "test", "build"]) {
|
|
5169
5405
|
if (hasScript(cwd, script)) {
|
|
@@ -5178,7 +5414,7 @@ root: ${cwd}
|
|
|
5178
5414
|
} else {
|
|
5179
5415
|
skipCheck("javascript", "package.json not found");
|
|
5180
5416
|
}
|
|
5181
|
-
const cargoManifest =
|
|
5417
|
+
const cargoManifest = existsSync15(path21.join(cwd, "Cargo.toml")) ? "Cargo.toml" : existsSync15(path21.join(cwd, "src-tauri/Cargo.toml")) ? "src-tauri/Cargo.toml" : "";
|
|
5182
5418
|
if (cargoManifest) {
|
|
5183
5419
|
runCheck("cargo fmt", "cargo", ["fmt", "--manifest-path", cargoManifest, "--", "--check"]);
|
|
5184
5420
|
runCheck("cargo clippy", "cargo", [
|
|
@@ -5194,7 +5430,7 @@ root: ${cwd}
|
|
|
5194
5430
|
} else {
|
|
5195
5431
|
skipCheck("rust", "Cargo.toml not found");
|
|
5196
5432
|
}
|
|
5197
|
-
if (
|
|
5433
|
+
if (existsSync15(path21.join(cwd, "pyproject.toml")) || existsSync15(path21.join(cwd, "pytest.ini")) || existsSync15(path21.join(cwd, "tests"))) {
|
|
5198
5434
|
if (commandOnPath2("pytest"))
|
|
5199
5435
|
runCheck("pytest", "pytest", []);
|
|
5200
5436
|
else
|
|
@@ -5216,7 +5452,7 @@ root: ${cwd}
|
|
|
5216
5452
|
lines.push(`status: skipped (dry run)
|
|
5217
5453
|
`);
|
|
5218
5454
|
skipped += 1;
|
|
5219
|
-
} else if (
|
|
5455
|
+
} else if (existsSync15(changelogPath)) {
|
|
5220
5456
|
if (/## \[Unreleased\]/i.test(readFileSync15(changelogPath, "utf8"))) {
|
|
5221
5457
|
lines.push(`status: pass
|
|
5222
5458
|
`);
|
|
@@ -5330,7 +5566,7 @@ var emptyState = (slug) => ({
|
|
|
5330
5566
|
});
|
|
5331
5567
|
var readFlowState = (root2, slug) => {
|
|
5332
5568
|
const file = flowPath(root2, slug);
|
|
5333
|
-
if (!
|
|
5569
|
+
if (!existsSync16(file))
|
|
5334
5570
|
return emptyState(slug);
|
|
5335
5571
|
try {
|
|
5336
5572
|
return normalizeState(JSON.parse(readFileSync16(file, "utf8")), slug);
|
|
@@ -5472,7 +5708,7 @@ var validateState = (parsed, slug) => {
|
|
|
5472
5708
|
var readFlowStrict = (root2, slug) => {
|
|
5473
5709
|
const file = flowPath(root2, slug);
|
|
5474
5710
|
const rel = path22.posix.join("docs", slug, "sdd", "flow.json");
|
|
5475
|
-
if (!
|
|
5711
|
+
if (!existsSync16(file)) {
|
|
5476
5712
|
return err2("flow_not_activated", `flow not activated for ${slug} — run workit_flow_status first`);
|
|
5477
5713
|
}
|
|
5478
5714
|
let text;
|
|
@@ -5509,15 +5745,15 @@ var writeFlowFileAtomic = (file, state) => {
|
|
|
5509
5745
|
fsyncSync(fd);
|
|
5510
5746
|
closeSync(fd);
|
|
5511
5747
|
fd = null;
|
|
5512
|
-
|
|
5748
|
+
renameSync3(tmp, file);
|
|
5513
5749
|
} finally {
|
|
5514
5750
|
try {
|
|
5515
5751
|
if (fd !== null)
|
|
5516
5752
|
closeSync(fd);
|
|
5517
5753
|
} catch {}
|
|
5518
5754
|
try {
|
|
5519
|
-
if (
|
|
5520
|
-
|
|
5755
|
+
if (existsSync16(tmp))
|
|
5756
|
+
rmSync3(tmp, { force: true });
|
|
5521
5757
|
} catch {}
|
|
5522
5758
|
}
|
|
5523
5759
|
};
|
|
@@ -5525,14 +5761,14 @@ var MAX_WRITE_ATTEMPTS = 5;
|
|
|
5525
5761
|
var STALE_LOCK_MS = 1000;
|
|
5526
5762
|
var lockMtimeMs = (lock) => {
|
|
5527
5763
|
try {
|
|
5528
|
-
return
|
|
5764
|
+
return statSync8(lock).mtimeMs;
|
|
5529
5765
|
} catch {
|
|
5530
5766
|
return null;
|
|
5531
5767
|
}
|
|
5532
5768
|
};
|
|
5533
5769
|
var lockOwnedBy = (fd, lock) => {
|
|
5534
5770
|
try {
|
|
5535
|
-
return fstatSync(fd).ino ===
|
|
5771
|
+
return fstatSync(fd).ino === statSync8(lock).ino;
|
|
5536
5772
|
} catch {
|
|
5537
5773
|
return false;
|
|
5538
5774
|
}
|
|
@@ -5548,7 +5784,7 @@ var writeFlowStateIfCurrent = (root2, expected, next) => {
|
|
|
5548
5784
|
const tmp = uniqueTempPath(file);
|
|
5549
5785
|
let fd = null;
|
|
5550
5786
|
try {
|
|
5551
|
-
const currentText =
|
|
5787
|
+
const currentText = existsSync16(file) ? readFileSync16(file, "utf8") : null;
|
|
5552
5788
|
if (currentText !== expectedText)
|
|
5553
5789
|
return { ok: false, conflict: true };
|
|
5554
5790
|
mkdirSync9(path22.dirname(file), { recursive: true });
|
|
@@ -5557,10 +5793,10 @@ var writeFlowStateIfCurrent = (root2, expected, next) => {
|
|
|
5557
5793
|
fsyncSync(fd);
|
|
5558
5794
|
closeSync(fd);
|
|
5559
5795
|
fd = null;
|
|
5560
|
-
const reRead =
|
|
5796
|
+
const reRead = existsSync16(file) ? readFileSync16(file, "utf8") : null;
|
|
5561
5797
|
if (reRead !== expectedText)
|
|
5562
5798
|
return { ok: false, conflict: true };
|
|
5563
|
-
|
|
5799
|
+
renameSync3(tmp, file);
|
|
5564
5800
|
return { ok: true };
|
|
5565
5801
|
} catch (error) {
|
|
5566
5802
|
return { ok: false, io_error: error instanceof Error ? error.message : String(error) };
|
|
@@ -5570,8 +5806,8 @@ var writeFlowStateIfCurrent = (root2, expected, next) => {
|
|
|
5570
5806
|
closeSync(fd);
|
|
5571
5807
|
} catch {}
|
|
5572
5808
|
try {
|
|
5573
|
-
if (
|
|
5574
|
-
|
|
5809
|
+
if (existsSync16(tmp))
|
|
5810
|
+
rmSync3(tmp, { force: true });
|
|
5575
5811
|
} catch {}
|
|
5576
5812
|
}
|
|
5577
5813
|
};
|
|
@@ -5592,7 +5828,7 @@ var readCanonicalDigest = (root2, rel) => {
|
|
|
5592
5828
|
} catch {
|
|
5593
5829
|
return { ok: false, code: "document_unreadable" };
|
|
5594
5830
|
}
|
|
5595
|
-
return { ok: true, text, digest:
|
|
5831
|
+
return { ok: true, text, digest: createHash2("sha256").update(bytes).digest("hex") };
|
|
5596
5832
|
};
|
|
5597
5833
|
var resetForSpecDrift = (state) => ({
|
|
5598
5834
|
...state,
|
|
@@ -5668,11 +5904,11 @@ var normalizeCompatibility = (root2, slug, parsed, state) => {
|
|
|
5668
5904
|
};
|
|
5669
5905
|
var withFlowLock = (file, fn) => {
|
|
5670
5906
|
const lock = `${file}.lock`;
|
|
5671
|
-
if (!
|
|
5907
|
+
if (!existsSync16(path22.dirname(file)))
|
|
5672
5908
|
return { locked: true, value: fn() };
|
|
5673
5909
|
try {
|
|
5674
|
-
if (
|
|
5675
|
-
|
|
5910
|
+
if (existsSync16(`${lock}.stale`))
|
|
5911
|
+
rmSync3(`${lock}.stale`, { force: true });
|
|
5676
5912
|
} catch {}
|
|
5677
5913
|
const wait = new Int32Array(new SharedArrayBuffer(4));
|
|
5678
5914
|
let fd = null;
|
|
@@ -5691,7 +5927,7 @@ var withFlowLock = (file, fn) => {
|
|
|
5691
5927
|
const mtime = lockMtimeMs(lock);
|
|
5692
5928
|
if (mtime !== null && Date.now() - mtime > STALE_LOCK_MS) {
|
|
5693
5929
|
try {
|
|
5694
|
-
|
|
5930
|
+
renameSync3(lock, `${lock}.stale`);
|
|
5695
5931
|
unlinkSync2(`${lock}.stale`);
|
|
5696
5932
|
} catch {}
|
|
5697
5933
|
try {
|
|
@@ -5727,7 +5963,7 @@ var withFlowLock = (file, fn) => {
|
|
|
5727
5963
|
} finally {
|
|
5728
5964
|
try {
|
|
5729
5965
|
if (fd !== null && lockOwnedBy(fd, lock))
|
|
5730
|
-
|
|
5966
|
+
rmSync3(lock, { force: true });
|
|
5731
5967
|
} catch {}
|
|
5732
5968
|
try {
|
|
5733
5969
|
if (fd !== null)
|
|
@@ -6044,7 +6280,7 @@ var prepareFlowState = (root2, slug, opts = {}, ctx) => {
|
|
|
6044
6280
|
const planPath = path22.posix.join("docs", slug, "plan.md");
|
|
6045
6281
|
const file = flowPath(root2, slug);
|
|
6046
6282
|
const locked = withFlowLock(file, () => {
|
|
6047
|
-
if (!
|
|
6283
|
+
if (!existsSync16(file)) {
|
|
6048
6284
|
writeFlowFileAtomic(file, {
|
|
6049
6285
|
slug,
|
|
6050
6286
|
activated: true,
|
|
@@ -6085,7 +6321,7 @@ var transitionSpec = (root2, slug, specPath, evidence, ctx) => {
|
|
|
6085
6321
|
return err2("path_invalid", doc.error);
|
|
6086
6322
|
const relPath = path22.posix.join("docs", slug, "spec.md");
|
|
6087
6323
|
return readModifyWrite(root2, slug, (state) => {
|
|
6088
|
-
if (!
|
|
6324
|
+
if (!existsSync16(doc.path))
|
|
6089
6325
|
return err2("spec_missing", `spec not found: ${specPath}`);
|
|
6090
6326
|
if (state.spec.status === "draft" || state.spec.status === "self_reviewed") {
|
|
6091
6327
|
const digest = readCanonicalDigest(root2, relPath);
|
|
@@ -6130,7 +6366,7 @@ var transitionPlan = (root2, slug, planPath, evidence, ctx) => {
|
|
|
6130
6366
|
return err2("path_invalid", doc.error);
|
|
6131
6367
|
const relPath = path22.posix.join("docs", slug, "plan.md");
|
|
6132
6368
|
return readModifyWrite(root2, slug, (state) => {
|
|
6133
|
-
if (!
|
|
6369
|
+
if (!existsSync16(doc.path))
|
|
6134
6370
|
return err2("plan_missing", `plan not found: ${planPath}`);
|
|
6135
6371
|
if (state.spec.status !== "approved") {
|
|
6136
6372
|
return err2("spec_not_approved", "spec must be approved before the plan can be approved");
|
|
@@ -6796,7 +7032,7 @@ var subagentDrivenInterception = (input) => {
|
|
|
6796
7032
|
var findActiveSubagentDrivenContexts = (root2) => {
|
|
6797
7033
|
let entries = [];
|
|
6798
7034
|
try {
|
|
6799
|
-
entries =
|
|
7035
|
+
entries = readdirSync7(path22.join(root2, "docs"), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
6800
7036
|
} catch {
|
|
6801
7037
|
return [];
|
|
6802
7038
|
}
|
|
@@ -6910,7 +7146,7 @@ var detectBacktickDocRefs = (text) => {
|
|
|
6910
7146
|
};
|
|
6911
7147
|
var scanActiveSubagentDrivenPlans = (root2) => {
|
|
6912
7148
|
const docsDir = path23.join(root2, "docs");
|
|
6913
|
-
if (!
|
|
7149
|
+
if (!existsSync17(docsDir))
|
|
6914
7150
|
return { slugs: [], read_errors: [] };
|
|
6915
7151
|
const slugs = [];
|
|
6916
7152
|
const readErrors = [];
|
|
@@ -6931,9 +7167,9 @@ var scanActiveSubagentDrivenPlans = (root2) => {
|
|
|
6931
7167
|
if (exec.status === "active" && exec.mode === "subagent-driven")
|
|
6932
7168
|
slugs.push(slug);
|
|
6933
7169
|
};
|
|
6934
|
-
for (const slug of
|
|
7170
|
+
for (const slug of readdirSync8(docsDir)) {
|
|
6935
7171
|
const file = path23.join(docsDir, slug, "sdd", "flow.json");
|
|
6936
|
-
if (!
|
|
7172
|
+
if (!existsSync17(file))
|
|
6937
7173
|
continue;
|
|
6938
7174
|
let raw;
|
|
6939
7175
|
try {
|
|
@@ -19276,7 +19512,7 @@ function tool(input) {
|
|
|
19276
19512
|
tool.schema = exports_external;
|
|
19277
19513
|
// packages/workit-core/src/core.ts
|
|
19278
19514
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
19279
|
-
import { existsSync as
|
|
19515
|
+
import { existsSync as existsSync18, realpathSync as realpathSync3 } from "node:fs";
|
|
19280
19516
|
import path24 from "node:path";
|
|
19281
19517
|
var ok = (data) => ({ ok: true, data, error: null });
|
|
19282
19518
|
var fail = (error45, data = null) => ({
|
|
@@ -19316,7 +19552,7 @@ function resolveInside(root2, candidate) {
|
|
|
19316
19552
|
throw new Error("path must stay inside repository root");
|
|
19317
19553
|
}
|
|
19318
19554
|
let ancestor = target;
|
|
19319
|
-
while (!
|
|
19555
|
+
while (!existsSync18(ancestor))
|
|
19320
19556
|
ancestor = path24.dirname(ancestor);
|
|
19321
19557
|
const canonicalAncestor = realpathSync3(ancestor);
|
|
19322
19558
|
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path24.sep)) {
|
|
@@ -19926,12 +20162,14 @@ function createRepoTools(runtime = defaultRuntime) {
|
|
|
19926
20162
|
} catch (error45) {
|
|
19927
20163
|
return output(fail(error45 instanceof Error ? error45.message : "invalid SDD path"));
|
|
19928
20164
|
}
|
|
20165
|
+
const diagnostic = getDiagnosticLogger();
|
|
19929
20166
|
const result = branchSetup({
|
|
19930
20167
|
action,
|
|
19931
20168
|
sdd_dir: resolvedSdd,
|
|
19932
20169
|
target_branch,
|
|
19933
20170
|
stash,
|
|
19934
|
-
workspace_root: context2.directory
|
|
20171
|
+
workspace_root: context2.directory,
|
|
20172
|
+
log: diagnostic ? (message) => diagnostic.info(message) : undefined
|
|
19935
20173
|
});
|
|
19936
20174
|
return output(legacyScriptResult({
|
|
19937
20175
|
stdout: JSON.stringify(result),
|
|
@@ -20562,12 +20800,12 @@ import path32 from "node:path";
|
|
|
20562
20800
|
import path31 from "node:path";
|
|
20563
20801
|
|
|
20564
20802
|
// packages/workit-core/src/core/handoff-context.ts
|
|
20565
|
-
import { existsSync as
|
|
20803
|
+
import { existsSync as existsSync19, readFileSync as readFileSync20, readdirSync as readdirSync9, statSync as statSync9 } from "node:fs";
|
|
20566
20804
|
import path30 from "node:path";
|
|
20567
20805
|
var DOC_RE = /docs\/([A-Za-z0-9][A-Za-z0-9._-]*)\/(spec|plan)\.md/g;
|
|
20568
20806
|
var listMd = (dir) => {
|
|
20569
20807
|
try {
|
|
20570
|
-
return
|
|
20808
|
+
return readdirSync9(dir).filter((f) => f.endsWith(".md")).sort();
|
|
20571
20809
|
} catch {
|
|
20572
20810
|
return [];
|
|
20573
20811
|
}
|
|
@@ -20583,7 +20821,7 @@ var resolveFromMessagePaths = (root2, message) => {
|
|
|
20583
20821
|
const slug = slugs[0];
|
|
20584
20822
|
const plan = `docs/${slug}/plan.md`;
|
|
20585
20823
|
const spec = `docs/${slug}/spec.md`;
|
|
20586
|
-
if (!
|
|
20824
|
+
if (!existsSync19(path30.join(root2, plan)) || !existsSync19(path30.join(root2, spec))) {
|
|
20587
20825
|
return { error: `docs/${slug}/ must contain both plan.md and spec.md` };
|
|
20588
20826
|
}
|
|
20589
20827
|
return { spec, plan, source: "message_paths" };
|
|
@@ -20593,7 +20831,7 @@ var resolveActivePair = (root2) => {
|
|
|
20593
20831
|
let best = null;
|
|
20594
20832
|
let entries = [];
|
|
20595
20833
|
try {
|
|
20596
|
-
entries =
|
|
20834
|
+
entries = readdirSync9(docsDir);
|
|
20597
20835
|
} catch {
|
|
20598
20836
|
return { error: "no pair" };
|
|
20599
20837
|
}
|
|
@@ -20602,9 +20840,9 @@ var resolveActivePair = (root2) => {
|
|
|
20602
20840
|
continue;
|
|
20603
20841
|
const plan = path30.join("docs", slug, "plan.md");
|
|
20604
20842
|
const spec = path30.join("docs", slug, "spec.md");
|
|
20605
|
-
if (!
|
|
20843
|
+
if (!existsSync19(path30.join(root2, plan)) || !existsSync19(path30.join(root2, spec)))
|
|
20606
20844
|
continue;
|
|
20607
|
-
const score = Math.max(
|
|
20845
|
+
const score = Math.max(statSync9(path30.join(root2, spec)).mtimeMs, statSync9(path30.join(root2, plan)).mtimeMs);
|
|
20608
20846
|
if (best === null || score > best.score || score === best.score && slug < best.spec.split("/")[1]) {
|
|
20609
20847
|
best = { score, spec, plan, source: "active_pair" };
|
|
20610
20848
|
}
|
|
@@ -20623,7 +20861,7 @@ var resolveWorkflowPaths = (root2, message) => {
|
|
|
20623
20861
|
if (!("error" in active))
|
|
20624
20862
|
return active;
|
|
20625
20863
|
const docsDir = path30.join(root2, "docs");
|
|
20626
|
-
if (!
|
|
20864
|
+
if (!existsSync19(docsDir) || listMd(docsDir).length === 0) {
|
|
20627
20865
|
return { error: "no docs/<slug>/ features found under docs/" };
|
|
20628
20866
|
}
|
|
20629
20867
|
return {
|
|
@@ -20793,7 +21031,7 @@ function createHandoffTools(client, state) {
|
|
|
20793
21031
|
import path34 from "node:path";
|
|
20794
21032
|
|
|
20795
21033
|
// packages/workit-core/src/core/youtrack-tools.ts
|
|
20796
|
-
import { readFileSync as readFileSync21, statSync as
|
|
21034
|
+
import { readFileSync as readFileSync21, statSync as statSync10 } from "node:fs";
|
|
20797
21035
|
import os3 from "node:os";
|
|
20798
21036
|
import path33 from "node:path";
|
|
20799
21037
|
var ISSUE_RE2 = /^[A-Z]+-\d+$/;
|
|
@@ -20813,7 +21051,7 @@ function readCredentials(env = process.env, home = os3.homedir()) {
|
|
|
20813
21051
|
const config2 = parsed;
|
|
20814
21052
|
const tokenFile = config2.tokenFile ?? "youtrack.token";
|
|
20815
21053
|
const tokenPath = path33.resolve(path33.dirname(resolvedConfig), tokenFile.replace(/^~(?=\/)/, home));
|
|
20816
|
-
if (process.platform !== "win32" && (
|
|
21054
|
+
if (process.platform !== "win32" && (statSync10(tokenPath).mode & 511) !== 384)
|
|
20817
21055
|
throw new Error("youtrack.token mode must be 0600");
|
|
20818
21056
|
const token = readFileSync21(tokenPath, "utf8").trim();
|
|
20819
21057
|
if (!token)
|
|
@@ -21271,7 +21509,7 @@ function createPresentTools() {
|
|
|
21271
21509
|
}
|
|
21272
21510
|
|
|
21273
21511
|
// packages/workit-core/src/core/docs-repo.ts
|
|
21274
|
-
import { existsSync as
|
|
21512
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync10, readFileSync as readFileSync22, writeFileSync as writeFileSync12, readdirSync as readdirSync10 } from "node:fs";
|
|
21275
21513
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
21276
21514
|
import path35 from "node:path";
|
|
21277
21515
|
var configPath2 = () => process.env.WORKFLOW_DOCS_REPO_CONFIG ?? path35.join(configDir(), "docs-repo.json");
|
|
@@ -21291,7 +21529,7 @@ var writeDocsRepoConfig = (docsPath) => {
|
|
|
21291
21529
|
};
|
|
21292
21530
|
var docsRepoPath = () => readDocsRepoConfig()?.path ?? null;
|
|
21293
21531
|
var validateDocsRepo = (docsPath) => {
|
|
21294
|
-
if (!
|
|
21532
|
+
if (!existsSync20(docsPath))
|
|
21295
21533
|
return { ok: false, error: `docs repo path does not exist: ${docsPath}` };
|
|
21296
21534
|
try {
|
|
21297
21535
|
execFileSync5("git", ["-C", docsPath, "rev-parse", "--is-inside-work-tree"], { stdio: "pipe" });
|
|
@@ -21299,7 +21537,7 @@ var validateDocsRepo = (docsPath) => {
|
|
|
21299
21537
|
return { ok: false, error: `docs repo is not a git repository: ${docsPath}` };
|
|
21300
21538
|
}
|
|
21301
21539
|
const featuresDir = path35.join(docsPath, "features");
|
|
21302
|
-
if (!
|
|
21540
|
+
if (!existsSync20(featuresDir))
|
|
21303
21541
|
mkdirSync10(featuresDir, { recursive: true });
|
|
21304
21542
|
return { ok: true };
|
|
21305
21543
|
};
|
|
@@ -21316,19 +21554,19 @@ var listSpecs = (workspaceRoot) => {
|
|
|
21316
21554
|
const repoPath = docsRepoPath();
|
|
21317
21555
|
const specs = [];
|
|
21318
21556
|
const docsDir = path35.join(workspaceRoot, "docs");
|
|
21319
|
-
if (
|
|
21320
|
-
for (const slug of
|
|
21557
|
+
if (existsSync20(docsDir)) {
|
|
21558
|
+
for (const slug of readdirSync10(docsDir)) {
|
|
21321
21559
|
if (slug.startsWith("."))
|
|
21322
21560
|
continue;
|
|
21323
21561
|
const spec = path35.posix.join("docs", slug, "spec.md");
|
|
21324
|
-
if (!
|
|
21562
|
+
if (!existsSync20(path35.join(workspaceRoot, spec)))
|
|
21325
21563
|
continue;
|
|
21326
21564
|
let promoted = false;
|
|
21327
21565
|
let target = null;
|
|
21328
21566
|
if (repoPath) {
|
|
21329
21567
|
const featuresDir = path35.join(repoPath, "features");
|
|
21330
|
-
if (
|
|
21331
|
-
const match =
|
|
21568
|
+
if (existsSync20(featuresDir)) {
|
|
21569
|
+
const match = readdirSync10(featuresDir).find((d) => new RegExp(`^20\\d{2}-\\d{2}-${slug}$`).test(d));
|
|
21332
21570
|
if (match) {
|
|
21333
21571
|
promoted = true;
|
|
21334
21572
|
target = path35.join(repoPath, "features", match);
|
|
@@ -21402,7 +21640,7 @@ var promoteSpec = (workspaceRoot, slug, opts) => {
|
|
|
21402
21640
|
}
|
|
21403
21641
|
if (!opts.force) {
|
|
21404
21642
|
const sddDir = path35.join(workspaceRootCanonical, "docs", slug, "sdd");
|
|
21405
|
-
if (
|
|
21643
|
+
if (existsSync20(sddDir)) {
|
|
21406
21644
|
try {
|
|
21407
21645
|
execFileSync5("git", [
|
|
21408
21646
|
"-C",
|
|
@@ -21420,7 +21658,7 @@ var promoteSpec = (workspaceRoot, slug, opts) => {
|
|
|
21420
21658
|
}
|
|
21421
21659
|
const prefix = monthPrefix();
|
|
21422
21660
|
const featuresDir = path35.join(repoPath, "features");
|
|
21423
|
-
const existing =
|
|
21661
|
+
const existing = existsSync20(featuresDir) ? readdirSync10(featuresDir).find((d) => new RegExp(`^20\\d{2}-\\d{2}-${slug}$`).test(d)) : undefined;
|
|
21424
21662
|
const targetDir = path35.join(featuresDir, existing ?? `${prefix}-${slug}`);
|
|
21425
21663
|
mkdirSync10(targetDir, { recursive: true });
|
|
21426
21664
|
const files = ["spec.md"];
|
|
@@ -21479,15 +21717,15 @@ ${row}
|
|
|
21479
21717
|
// packages/workit-core/src/core/docs-migration.ts
|
|
21480
21718
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
21481
21719
|
import {
|
|
21482
|
-
existsSync as
|
|
21720
|
+
existsSync as existsSync21,
|
|
21483
21721
|
lstatSync as lstatSync2,
|
|
21484
21722
|
mkdirSync as mkdirSync11,
|
|
21485
|
-
readdirSync as
|
|
21723
|
+
readdirSync as readdirSync11,
|
|
21486
21724
|
readFileSync as readFileSync23,
|
|
21487
21725
|
realpathSync as realpathSync5,
|
|
21488
|
-
renameSync as
|
|
21489
|
-
rmSync as
|
|
21490
|
-
statSync as
|
|
21726
|
+
renameSync as renameSync4,
|
|
21727
|
+
rmSync as rmSync4,
|
|
21728
|
+
statSync as statSync11,
|
|
21491
21729
|
writeFileSync as writeFileSync13
|
|
21492
21730
|
} from "node:fs";
|
|
21493
21731
|
import path36 from "node:path";
|
|
@@ -21536,14 +21774,14 @@ var explicitTargetSlug = (planText) => {
|
|
|
21536
21774
|
var detectLegacyDocs = (workspace_root) => {
|
|
21537
21775
|
const root2 = legacyRoot(workspace_root);
|
|
21538
21776
|
const raw = [];
|
|
21539
|
-
if (
|
|
21540
|
-
for (const entry of
|
|
21777
|
+
if (existsSync21(root2)) {
|
|
21778
|
+
for (const entry of readdirSync11(root2, { withFileTypes: true })) {
|
|
21541
21779
|
const abs = path36.join(root2, entry.name);
|
|
21542
21780
|
const rel = posix3(path36.relative(workspace_root, abs));
|
|
21543
21781
|
if (entry.isDirectory()) {
|
|
21544
|
-
const hasSpec =
|
|
21545
|
-
const hasPlan =
|
|
21546
|
-
if (!hasSpec && !hasPlan && !
|
|
21782
|
+
const hasSpec = existsSync21(path36.join(abs, "spec.md"));
|
|
21783
|
+
const hasPlan = existsSync21(path36.join(abs, "plan.md"));
|
|
21784
|
+
if (!hasSpec && !hasPlan && !existsSync21(path36.join(abs, "sdd")))
|
|
21547
21785
|
continue;
|
|
21548
21786
|
const explicit = explicitTargetSlug(hasPlan ? readFileSafe(path36.join(abs, "plan.md")) : null);
|
|
21549
21787
|
raw.push({
|
|
@@ -21551,7 +21789,7 @@ var detectLegacyDocs = (workspace_root) => {
|
|
|
21551
21789
|
legacy_dir: rel,
|
|
21552
21790
|
spec: hasSpec ? posix3(path36.join(rel, "spec.md")) : null,
|
|
21553
21791
|
plan: hasPlan ? posix3(path36.join(rel, "plan.md")) : null,
|
|
21554
|
-
sdd:
|
|
21792
|
+
sdd: existsSync21(path36.join(abs, "sdd")),
|
|
21555
21793
|
explicit: explicit !== null
|
|
21556
21794
|
});
|
|
21557
21795
|
} else if (entry.name === "spec.md" || entry.name === "plan.md") {
|
|
@@ -21679,7 +21917,7 @@ var planTree = (workspace, legacyAbs, legacyRel, destRelRoot, destAbsRoot, legac
|
|
|
21679
21917
|
if (visits.has(real))
|
|
21680
21918
|
return { ok: true };
|
|
21681
21919
|
visits.add(real);
|
|
21682
|
-
for (const entry of
|
|
21920
|
+
for (const entry of readdirSync11(real, { withFileTypes: true })) {
|
|
21683
21921
|
const fromAbs = path36.join(real, entry.name);
|
|
21684
21922
|
const fromRel = posix3(path36.join(legacyRel, entry.name));
|
|
21685
21923
|
const toRel = posix3(path36.join(destRelRoot, entry.name));
|
|
@@ -21699,7 +21937,7 @@ var planTree = (workspace, legacyAbs, legacyRel, destRelRoot, destAbsRoot, legac
|
|
|
21699
21937
|
if (target !== workspaceReal && !target.startsWith(workspaceReal + path36.sep)) {
|
|
21700
21938
|
return { ok: false, error: `symlink escape refused: ${fromRel}` };
|
|
21701
21939
|
}
|
|
21702
|
-
if (
|
|
21940
|
+
if (statSync11(target).isDirectory()) {
|
|
21703
21941
|
const sub = planTree(workspace, fromAbs, fromRel, toRel, path36.join(destAbsRoot, entry.name), legacyName, slug, plan, visits);
|
|
21704
21942
|
if (!sub.ok)
|
|
21705
21943
|
return sub;
|
|
@@ -21806,7 +22044,7 @@ var migrateLegacyDocs = (input) => {
|
|
|
21806
22044
|
}
|
|
21807
22045
|
const collisions = [];
|
|
21808
22046
|
for (const item of plan) {
|
|
21809
|
-
if (!
|
|
22047
|
+
if (!existsSync21(item.destAbs))
|
|
21810
22048
|
continue;
|
|
21811
22049
|
let identical = false;
|
|
21812
22050
|
try {
|
|
@@ -21847,10 +22085,10 @@ var migrateLegacyDocs = (input) => {
|
|
|
21847
22085
|
try {
|
|
21848
22086
|
mkdirSync11(path36.dirname(item.destAbs), { recursive: true });
|
|
21849
22087
|
writeFileSync13(tmp, item.bytes);
|
|
21850
|
-
|
|
22088
|
+
renameSync4(tmp, item.destAbs);
|
|
21851
22089
|
} catch (error45) {
|
|
21852
22090
|
try {
|
|
21853
|
-
|
|
22091
|
+
rmSync4(tmp, { force: true });
|
|
21854
22092
|
} catch {}
|
|
21855
22093
|
return abort(`legacy migration retry required: failed to copy ${item.fromRel}: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
21856
22094
|
}
|
|
@@ -22050,7 +22288,7 @@ SDD: ${value.sdd}` : null;
|
|
|
22050
22288
|
}
|
|
22051
22289
|
|
|
22052
22290
|
// packages/workit-core/src/core/logger.ts
|
|
22053
|
-
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync12, readdirSync as
|
|
22291
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync12, readdirSync as readdirSync12, unlinkSync as unlinkSync3 } from "node:fs";
|
|
22054
22292
|
import os4 from "node:os";
|
|
22055
22293
|
import path37 from "node:path";
|
|
22056
22294
|
var REDACTED = "[REDACTED]";
|
|
@@ -22185,7 +22423,7 @@ var dailyFileName = (date5) => {
|
|
|
22185
22423
|
};
|
|
22186
22424
|
var pruneOldFiles = (dir) => {
|
|
22187
22425
|
try {
|
|
22188
|
-
const files =
|
|
22426
|
+
const files = readdirSync12(dir).filter((name) => DAY_FILE.test(name)).sort().reverse();
|
|
22189
22427
|
for (const file2 of files.slice(RETAINED_DAYS)) {
|
|
22190
22428
|
try {
|
|
22191
22429
|
unlinkSync3(path37.join(dir, file2));
|
|
@@ -22348,16 +22586,16 @@ var worktreeDenials = {
|
|
|
22348
22586
|
};
|
|
22349
22587
|
var hasMarkedDestination = (root3) => {
|
|
22350
22588
|
const docsDir = path39.join(root3, "docs");
|
|
22351
|
-
if (!
|
|
22589
|
+
if (!existsSync22(docsDir))
|
|
22352
22590
|
return false;
|
|
22353
22591
|
let slugs;
|
|
22354
22592
|
try {
|
|
22355
|
-
slugs =
|
|
22593
|
+
slugs = readdirSync13(docsDir);
|
|
22356
22594
|
} catch {
|
|
22357
22595
|
return false;
|
|
22358
22596
|
}
|
|
22359
22597
|
for (const slug of slugs) {
|
|
22360
|
-
if (!
|
|
22598
|
+
if (!existsSync22(path39.join(docsDir, slug, "sdd", "flow.json")))
|
|
22361
22599
|
continue;
|
|
22362
22600
|
const effective = readEffectiveFlowState(root3, slug);
|
|
22363
22601
|
if (effective.ok && effective.state.handoff_destination)
|
|
@@ -22482,7 +22720,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
22482
22720
|
const skillPath = path39.join(root2, "skills");
|
|
22483
22721
|
const vendoredSkillsPath = path39.join(root2, "vendor", "superpowers", "skills");
|
|
22484
22722
|
for (const p of [skillPath, vendoredSkillsPath]) {
|
|
22485
|
-
if (
|
|
22723
|
+
if (existsSync22(p) && !mutable.skills.paths.includes(p)) {
|
|
22486
22724
|
mutable.skills.paths.push(p);
|
|
22487
22725
|
}
|
|
22488
22726
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit OpenCode plugin (thin wrapper over @brainervirus/workit-core)",
|
|
6
6
|
"keywords": [
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"typecheck": "tsc --noEmit"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@brainervirus/workit-core": "^0.
|
|
37
|
+
"@brainervirus/workit-core": "^0.9.2"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@opencode-ai/plugin": "1.17.7"
|