gsd-pi 2.78.1-dev.b0759e59b → 2.78.1-dev.e9d88a536
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/headless-recover.d.ts +23 -0
- package/dist/headless-recover.js +93 -0
- package/dist/headless.js +9 -0
- package/dist/help-text.js +1 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/tools/intent.js +8 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +4 -56
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -27
- package/dist/resources/extensions/gsd/auto-start.js +1 -8
- package/dist/resources/extensions/gsd/auto-worktree.js +59 -176
- package/dist/resources/extensions/gsd/auto.js +24 -6
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +9 -77
- package/dist/resources/extensions/gsd/commands-codebase.js +2 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +5 -5
- package/dist/resources/extensions/gsd/commands-logs.js +2 -2
- package/dist/resources/extensions/gsd/commands-scan.js +2 -2
- package/dist/resources/extensions/gsd/commands-ship.js +2 -2
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +5 -5
- package/dist/resources/extensions/gsd/db-writer.js +16 -85
- package/dist/resources/extensions/gsd/dispatch-guard.js +6 -10
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +2 -2
- package/dist/resources/extensions/gsd/gsd-db.js +74 -8
- package/dist/resources/extensions/gsd/guided-flow.js +31 -8
- package/dist/resources/extensions/gsd/markdown-renderer.js +14 -51
- package/dist/resources/extensions/gsd/parallel-merge.js +14 -13
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +5 -2
- package/dist/resources/extensions/gsd/paths.js +35 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
- package/dist/resources/extensions/gsd/queue-order.js +6 -1
- package/dist/resources/extensions/gsd/rethink.js +2 -2
- package/dist/resources/extensions/gsd/state.js +91 -372
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -5
- package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -12
- package/dist/resources/extensions/gsd/tools/complete-task.js +19 -31
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -5
- package/dist/resources/extensions/gsd/workflow-manifest.js +2 -1
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -21
- package/dist/resources/extensions/gsd/workflow-reconcile.js +3 -3
- package/dist/resources/extensions/gsd/worktree-command.js +4 -3
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- package/dist/web/standalone/.next/server/chunks/6336.js +1 -0
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +6 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +56 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/parse-workflow-args.test.ts +80 -0
- package/packages/mcp-server/src/workflow-tools.ts +61 -2
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/browser-tools/tools/intent.ts +13 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +4 -60
- package/src/resources/extensions/gsd/auto-post-unit.ts +7 -26
- package/src/resources/extensions/gsd/auto-start.ts +1 -8
- package/src/resources/extensions/gsd/auto-worktree.ts +61 -204
- package/src/resources/extensions/gsd/auto.ts +23 -6
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +9 -84
- package/src/resources/extensions/gsd/commands-codebase.ts +2 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +5 -5
- package/src/resources/extensions/gsd/commands-logs.ts +2 -2
- package/src/resources/extensions/gsd/commands-scan.ts +2 -2
- package/src/resources/extensions/gsd/commands-ship.ts +2 -2
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +5 -5
- package/src/resources/extensions/gsd/db-writer.ts +16 -83
- package/src/resources/extensions/gsd/dispatch-guard.ts +6 -11
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +2 -2
- package/src/resources/extensions/gsd/gsd-db.ts +85 -8
- package/src/resources/extensions/gsd/guided-flow.ts +35 -8
- package/src/resources/extensions/gsd/markdown-renderer.ts +13 -64
- package/src/resources/extensions/gsd/parallel-merge.ts +14 -13
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +5 -2
- package/src/resources/extensions/gsd/paths.ts +55 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
- package/src/resources/extensions/gsd/queue-order.ts +6 -1
- package/src/resources/extensions/gsd/rethink.ts +2 -2
- package/src/resources/extensions/gsd/state.ts +91 -389
- package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +21 -34
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +6 -7
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +12 -27
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +18 -5
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +6 -5
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +10 -38
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +136 -56
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +119 -61
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +6 -20
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +14 -15
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +11 -16
- package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +15 -36
- package/src/resources/extensions/gsd/tests/handler-worktree-write-isolation.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +15 -15
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +15 -5
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +14 -8
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +25 -16
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +184 -0
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +4 -0
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/slice-disk-reconcile.test.ts +10 -56
- package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +15 -16
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +23 -27
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +13 -14
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +10 -33
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +12 -7
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +65 -71
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +26 -151
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -5
- package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -14
- package/src/resources/extensions/gsd/tools/complete-task.ts +19 -34
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +7 -5
- package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -18
- package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
- package/src/resources/extensions/gsd/worktree-command.ts +4 -3
- package/dist/web/standalone/.next/server/chunks/8527.js +0 -1
- /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_ssgManifest.js +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
3
|
|
|
4
4
|
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
5
5
|
import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@gsd/pi-coding-agent";
|
|
6
6
|
|
|
7
7
|
import { DEFAULT_BASH_TIMEOUT_SECS } from "../constants.js";
|
|
8
8
|
import { setLogBasePath, logWarning } from "../workflow-logger.js";
|
|
9
|
+
import { resolveGsdPathContract } from "../paths.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Resolve the correct DB path for the current working directory.
|
|
@@ -14,75 +15,16 @@ import { setLogBasePath, logWarning } from "../workflow-logger.js";
|
|
|
14
15
|
* returns `<basePath>/.gsd/gsd.db`.
|
|
15
16
|
*/
|
|
16
17
|
export function resolveProjectRootDbPath(basePath: string): string {
|
|
17
|
-
|
|
18
|
-
// A worktree path looks like: /project/root/.gsd/worktrees/M001/...
|
|
19
|
-
// We need to resolve back to /project/root/.gsd/gsd.db
|
|
20
|
-
const marker = `${sep}.gsd${sep}worktrees${sep}`;
|
|
21
|
-
const idx = basePath.indexOf(marker);
|
|
22
|
-
if (idx !== -1) {
|
|
23
|
-
const projectRoot = basePath.slice(0, idx);
|
|
24
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Also handle forward-slash paths on all platforms
|
|
28
|
-
const fwdMarker = "/.gsd/worktrees/";
|
|
29
|
-
const fwdIdx = basePath.indexOf(fwdMarker);
|
|
30
|
-
if (fwdIdx !== -1) {
|
|
31
|
-
const projectRoot = basePath.slice(0, fwdIdx);
|
|
32
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// External-state layout: ~/.gsd/projects/<hash>/worktrees/<MID>/...
|
|
36
|
-
// Resolve to ~/.gsd/projects/<hash>/gsd.db (the canonical project DB) (#2952).
|
|
37
|
-
// Must be checked before the generic symlink-resolved handler: both match
|
|
38
|
-
// /.gsd/projects/<hash>/worktrees/ but require different resolution targets.
|
|
39
|
-
const extRe = /[/\\]\.gsd[/\\]projects[/\\][a-f0-9]+[/\\]worktrees(?:[/\\]|$)/;
|
|
40
|
-
const extMatch = extRe.exec(basePath);
|
|
41
|
-
if (extMatch) {
|
|
42
|
-
const matchStr = extMatch[0];
|
|
43
|
-
// Find the "/worktrees" portion within the match and slice up to it
|
|
44
|
-
const wtIdx = matchStr.search(/[/\\]worktrees(?:[/\\]|$)/);
|
|
45
|
-
const projectStateRoot = basePath.slice(0, extMatch.index + wtIdx);
|
|
46
|
-
return join(projectStateRoot, "gsd.db");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/M001/...
|
|
50
|
-
// The project root is everything before /.gsd/projects/ (#2517)
|
|
51
|
-
const symlinkMarker = `${sep}.gsd${sep}projects${sep}`;
|
|
52
|
-
const symlinkIdx = basePath.indexOf(symlinkMarker);
|
|
53
|
-
if (symlinkIdx !== -1) {
|
|
54
|
-
const afterProjects = basePath.slice(symlinkIdx + symlinkMarker.length);
|
|
55
|
-
// Expect: <hash>/worktrees/...
|
|
56
|
-
const worktreeSeg = `${sep}worktrees${sep}`;
|
|
57
|
-
if (afterProjects.includes(worktreeSeg)) {
|
|
58
|
-
const projectRoot = basePath.slice(0, symlinkIdx);
|
|
59
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Forward-slash variant for symlink-resolved layout
|
|
64
|
-
const fwdSymlinkMarker = "/.gsd/projects/";
|
|
65
|
-
const fwdSymlinkIdx = basePath.indexOf(fwdSymlinkMarker);
|
|
66
|
-
if (fwdSymlinkIdx !== -1) {
|
|
67
|
-
const afterProjects = basePath.slice(fwdSymlinkIdx + fwdSymlinkMarker.length);
|
|
68
|
-
if (afterProjects.includes("/worktrees/")) {
|
|
69
|
-
const projectRoot = basePath.slice(0, fwdSymlinkIdx);
|
|
70
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return join(basePath, ".gsd", "gsd.db");
|
|
18
|
+
return resolveGsdPathContract(basePath).projectDb;
|
|
76
19
|
}
|
|
77
20
|
|
|
78
21
|
export async function ensureDbOpen(basePath: string = process.cwd()): Promise<boolean> {
|
|
79
22
|
try {
|
|
80
23
|
const db = await import("../gsd-db.js");
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const projectRoot = join(dbPath, "..", "..");
|
|
24
|
+
const contract = resolveGsdPathContract(basePath);
|
|
25
|
+
const dbPath = contract.projectDb;
|
|
26
|
+
const gsdDir = contract.projectGsd;
|
|
27
|
+
const projectRoot = dirname(dirname(dbPath));
|
|
86
28
|
|
|
87
29
|
// Open existing DB file (may be at project root for worktrees)
|
|
88
30
|
if (existsSync(dbPath)) {
|
|
@@ -91,26 +33,9 @@ export async function ensureDbOpen(basePath: string = process.cwd()): Promise<bo
|
|
|
91
33
|
return opened;
|
|
92
34
|
}
|
|
93
35
|
|
|
94
|
-
// No DB file — create
|
|
36
|
+
// No DB file — create an empty authoritative DB. Markdown migration is
|
|
37
|
+
// explicit-only; runtime startup must not import projections into state.
|
|
95
38
|
if (existsSync(gsdDir)) {
|
|
96
|
-
const hasDecisions = existsSync(join(gsdDir, "DECISIONS.md"));
|
|
97
|
-
const hasRequirements = existsSync(join(gsdDir, "REQUIREMENTS.md"));
|
|
98
|
-
const hasMilestones = existsSync(join(gsdDir, "milestones"));
|
|
99
|
-
if (hasDecisions || hasRequirements || hasMilestones) {
|
|
100
|
-
const opened = db.openDatabase(dbPath);
|
|
101
|
-
if (opened) {
|
|
102
|
-
setLogBasePath(projectRoot);
|
|
103
|
-
try {
|
|
104
|
-
const { migrateFromMarkdown } = await import("../md-importer.js");
|
|
105
|
-
migrateFromMarkdown(basePath);
|
|
106
|
-
} catch (err) {
|
|
107
|
-
logWarning("bootstrap", `ensureDbOpen auto-migration failed: ${(err as Error).message}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return opened;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// .gsd/ exists but has no Markdown content (fresh project) — create empty DB
|
|
114
39
|
const opened = db.openDatabase(dbPath);
|
|
115
40
|
if (opened) setLogBasePath(projectRoot);
|
|
116
41
|
return opened;
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./codebase-generator.js";
|
|
17
17
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
18
18
|
import type { CodebaseMapOptions } from "./codebase-generator.js";
|
|
19
|
-
import {
|
|
19
|
+
import { currentDirectoryRoot } from "./commands/context.js";
|
|
20
20
|
|
|
21
21
|
const USAGE =
|
|
22
22
|
"Usage: /gsd codebase [generate|update|stats]\n\n" +
|
|
@@ -37,7 +37,7 @@ export async function handleCodebase(
|
|
|
37
37
|
ctx: ExtensionCommandContext,
|
|
38
38
|
_pi: ExtensionAPI,
|
|
39
39
|
): Promise<void> {
|
|
40
|
-
const basePath =
|
|
40
|
+
const basePath = currentDirectoryRoot();
|
|
41
41
|
const parts = args.trim().split(/\s+/);
|
|
42
42
|
const sub = parts[0] ?? "";
|
|
43
43
|
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from "./doctor.js";
|
|
25
25
|
import { isAutoActive, checkRemoteAutoSession } from "./auto.js";
|
|
26
26
|
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
27
|
-
import { projectRoot } from "./commands/context.js";
|
|
27
|
+
import { currentDirectoryRoot, projectRoot } from "./commands/context.js";
|
|
28
28
|
import { loadPrompt } from "./prompt-loader.js";
|
|
29
29
|
|
|
30
30
|
const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/gsd-pi/latest";
|
|
@@ -203,7 +203,7 @@ export async function handleCapture(args: string, ctx: ExtensionCommandContext):
|
|
|
203
203
|
return;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
const basePath =
|
|
206
|
+
const basePath = currentDirectoryRoot();
|
|
207
207
|
|
|
208
208
|
// Ensure .gsd/ exists — capture should work even without a milestone
|
|
209
209
|
const gsdDir = gsdRoot(basePath);
|
|
@@ -270,7 +270,7 @@ export async function handleTriage(ctx: ExtensionCommandContext, pi: ExtensionAP
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
export async function handleSteer(change: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
|
|
273
|
-
const basePath =
|
|
273
|
+
const basePath = currentDirectoryRoot();
|
|
274
274
|
const state = await deriveState(basePath);
|
|
275
275
|
const mid = state.activeMilestone?.id ?? "none";
|
|
276
276
|
const sid = state.activeSlice?.id ?? "none";
|
|
@@ -343,7 +343,7 @@ export async function handleKnowledge(args: string, ctx: ExtensionCommandContext
|
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
const type = typeArg as "rule" | "pattern" | "lesson";
|
|
346
|
-
const basePath =
|
|
346
|
+
const basePath = currentDirectoryRoot();
|
|
347
347
|
const state = await deriveState(basePath);
|
|
348
348
|
const scope = state.activeMilestone?.id
|
|
349
349
|
? `${state.activeMilestone.id}${state.activeSlice ? `/${state.activeSlice.id}` : ""}`
|
|
@@ -372,7 +372,7 @@ Examples:
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
const [hookName, unitType, unitId] = parts;
|
|
375
|
-
const basePath =
|
|
375
|
+
const basePath = currentDirectoryRoot();
|
|
376
376
|
|
|
377
377
|
// Import the hook trigger function
|
|
378
378
|
const { triggerHookManually, formatHookStatus, getHookStatus } = await import("./post-unit-hooks.js");
|
|
@@ -15,7 +15,7 @@ import { existsSync, readdirSync, readFileSync, statSync, unlinkSync } from "nod
|
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { gsdRoot } from "./paths.js";
|
|
17
17
|
import { loadJsonFileOrNull } from "./json-persistence.js";
|
|
18
|
-
import {
|
|
18
|
+
import { currentDirectoryRoot } from "./commands/context.js";
|
|
19
19
|
|
|
20
20
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
21
21
|
|
|
@@ -244,7 +244,7 @@ function summarizeDebugLog(filePath: string): {
|
|
|
244
244
|
// ─── Main Handler ───────────────────────────────────────────────────────────
|
|
245
245
|
|
|
246
246
|
export async function handleLogs(args: string, ctx: ExtensionCommandContext): Promise<void> {
|
|
247
|
-
const basePath =
|
|
247
|
+
const basePath = currentDirectoryRoot();
|
|
248
248
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
249
249
|
const subCmd = parts[0] ?? "";
|
|
250
250
|
|
|
@@ -20,7 +20,7 @@ import { existsSync, mkdirSync } from "node:fs";
|
|
|
20
20
|
import { join, relative } from "node:path";
|
|
21
21
|
|
|
22
22
|
import { loadPrompt } from "./prompt-loader.js";
|
|
23
|
-
import {
|
|
23
|
+
import { currentDirectoryRoot } from "./commands/context.js";
|
|
24
24
|
|
|
25
25
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
26
26
|
|
|
@@ -87,7 +87,7 @@ export async function handleScan(
|
|
|
87
87
|
ctx: ExtensionCommandContext,
|
|
88
88
|
pi: ExtensionAPI,
|
|
89
89
|
): Promise<void> {
|
|
90
|
-
const basePath =
|
|
90
|
+
const basePath = currentDirectoryRoot();
|
|
91
91
|
const { focus } = parseScanArgs(args);
|
|
92
92
|
const outputDir = join(basePath, ".gsd", "codebase");
|
|
93
93
|
const outputPaths = buildScanOutputPaths(focus, basePath);
|
|
@@ -17,7 +17,7 @@ import { getLedger, getProjectTotals, aggregateByModel, formatCost, formatTokenC
|
|
|
17
17
|
import { nativeGetCurrentBranch, nativeDetectMainBranch } from "./native-git-bridge.js";
|
|
18
18
|
import { formatDuration } from "../shared/format-utils.js";
|
|
19
19
|
import { parseEvalReviewFrontmatter, type Verdict } from "./eval-review-schema.js";
|
|
20
|
-
import {
|
|
20
|
+
import { currentDirectoryRoot } from "./commands/context.js";
|
|
21
21
|
|
|
22
22
|
function git(basePath: string, args: readonly string[]): string {
|
|
23
23
|
return execFileSync("git", args, { cwd: basePath, encoding: "utf-8" }).trim();
|
|
@@ -223,7 +223,7 @@ export async function handleShip(
|
|
|
223
223
|
ctx: ExtensionCommandContext,
|
|
224
224
|
_pi: ExtensionAPI,
|
|
225
225
|
): Promise<void> {
|
|
226
|
-
const basePath =
|
|
226
|
+
const basePath = currentDirectoryRoot();
|
|
227
227
|
const dryRun = args.includes("--dry-run");
|
|
228
228
|
const draft = args.includes("--draft");
|
|
229
229
|
const force = args.includes("--force");
|
|
@@ -23,7 +23,7 @@ import { createGitService, runGit } from "./git-service.js";
|
|
|
23
23
|
import { isAutoActive, isAutoPaused } from "./auto.js";
|
|
24
24
|
import { getErrorMessage } from "./error-utils.js";
|
|
25
25
|
import { resolvePlugin, type WorkflowPlugin } from "./workflow-plugins.js";
|
|
26
|
-
import {
|
|
26
|
+
import { currentDirectoryRoot } from "./commands/context.js";
|
|
27
27
|
|
|
28
28
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
29
29
|
|
|
@@ -197,7 +197,7 @@ export async function handleStart(
|
|
|
197
197
|
// ─── Resume detection ───────────────────────────────────────────────────
|
|
198
198
|
// /gsd start --resume or /gsd start resume → resume in-progress workflow
|
|
199
199
|
if (trimmed === "--resume" || trimmed === "resume") {
|
|
200
|
-
const basePath =
|
|
200
|
+
const basePath = currentDirectoryRoot();
|
|
201
201
|
const inProgress = findInProgressWorkflows(basePath);
|
|
202
202
|
if (inProgress.length === 0) {
|
|
203
203
|
ctx.ui.notify("No in-progress workflows found.", "info");
|
|
@@ -248,7 +248,7 @@ export async function handleStart(
|
|
|
248
248
|
|
|
249
249
|
// Show in-progress workflows when /gsd start is called with no args
|
|
250
250
|
if (!trimmed) {
|
|
251
|
-
const basePath =
|
|
251
|
+
const basePath = currentDirectoryRoot();
|
|
252
252
|
const inProgress = findInProgressWorkflows(basePath);
|
|
253
253
|
if (inProgress.length > 0) {
|
|
254
254
|
const wf = inProgress[0];
|
|
@@ -347,7 +347,7 @@ export async function handleStart(
|
|
|
347
347
|
|
|
348
348
|
const templateId = match.id;
|
|
349
349
|
const template = match.template;
|
|
350
|
-
const basePath =
|
|
350
|
+
const basePath = currentDirectoryRoot();
|
|
351
351
|
const date = new Date().toISOString().split("T")[0];
|
|
352
352
|
|
|
353
353
|
// Load the workflow template content — prefer a project/global plugin
|
|
@@ -582,7 +582,7 @@ export function dispatchMarkdownPhasePlugin(
|
|
|
582
582
|
}
|
|
583
583
|
|
|
584
584
|
const templateId = plugin.name;
|
|
585
|
-
const basePath =
|
|
585
|
+
const basePath = currentDirectoryRoot();
|
|
586
586
|
const date = new Date().toISOString().split("T")[0];
|
|
587
587
|
let workflowContent: string;
|
|
588
588
|
try {
|
|
@@ -318,23 +318,6 @@ export async function saveRequirementToDb(
|
|
|
318
318
|
LIMIT 1`,
|
|
319
319
|
)
|
|
320
320
|
.get({ ':description': fields.description });
|
|
321
|
-
const previousRow: Requirement | null = existingRow
|
|
322
|
-
? {
|
|
323
|
-
id: existingRow['id'] as string,
|
|
324
|
-
class: existingRow['class'] as string,
|
|
325
|
-
status: existingRow['status'] as string,
|
|
326
|
-
description: existingRow['description'] as string,
|
|
327
|
-
why: existingRow['why'] as string,
|
|
328
|
-
source: existingRow['source'] as string,
|
|
329
|
-
primary_owner: existingRow['primary_owner'] as string,
|
|
330
|
-
supporting_slices: existingRow['supporting_slices'] as string,
|
|
331
|
-
validation: existingRow['validation'] as string,
|
|
332
|
-
notes: existingRow['notes'] as string,
|
|
333
|
-
full_content: existingRow['full_content'] as string,
|
|
334
|
-
superseded_by: (existingRow['superseded_by'] as string) ?? null,
|
|
335
|
-
}
|
|
336
|
-
: null;
|
|
337
|
-
|
|
338
321
|
const row = adapter
|
|
339
322
|
.prepare('SELECT MAX(CAST(SUBSTR(id, 2) AS INTEGER)) as max_num FROM requirements')
|
|
340
323
|
.get();
|
|
@@ -361,9 +344,9 @@ export async function saveRequirementToDb(
|
|
|
361
344
|
};
|
|
362
345
|
|
|
363
346
|
db.upsertRequirement(requirement);
|
|
364
|
-
return { id: nextId
|
|
347
|
+
return { id: nextId };
|
|
365
348
|
});
|
|
366
|
-
const { id
|
|
349
|
+
const { id } = txResult;
|
|
367
350
|
|
|
368
351
|
// Fetch all requirements for full file regeneration
|
|
369
352
|
const adapter = db._getAdapter();
|
|
@@ -392,17 +375,7 @@ export async function saveRequirementToDb(
|
|
|
392
375
|
try {
|
|
393
376
|
await saveFile(filePath, md);
|
|
394
377
|
} catch (diskErr) {
|
|
395
|
-
|
|
396
|
-
try {
|
|
397
|
-
if (isNew) {
|
|
398
|
-
db.deleteRequirementById(id);
|
|
399
|
-
} else if (previousRow) {
|
|
400
|
-
db.upsertRequirement(previousRow);
|
|
401
|
-
}
|
|
402
|
-
} catch (rollbackErr) {
|
|
403
|
-
logError('manifest', 'SPLIT BRAIN: disk write failed AND DB rollback failed — DB has orphaned row', { fn: 'saveRequirementToDb', id, error: String((rollbackErr as Error).message) });
|
|
404
|
-
}
|
|
405
|
-
throw diskErr;
|
|
378
|
+
logWarning('projection', 'REQUIREMENTS.md projection write failed; DB requirement remains committed', { fn: 'saveRequirementToDb', id, error: String((diskErr as Error).message) });
|
|
406
379
|
}
|
|
407
380
|
invalidateStateCache();
|
|
408
381
|
clearPathCache();
|
|
@@ -538,13 +511,7 @@ export async function saveDecisionToDb(
|
|
|
538
511
|
try {
|
|
539
512
|
await saveFile(filePath, md);
|
|
540
513
|
} catch (diskErr) {
|
|
541
|
-
|
|
542
|
-
try {
|
|
543
|
-
db.deleteDecisionById(id);
|
|
544
|
-
} catch (rollbackErr) {
|
|
545
|
-
logError('manifest', 'SPLIT BRAIN: disk write failed AND DB rollback failed — DB has orphaned row', { fn: 'saveDecisionToDb', id, error: String((rollbackErr as Error).message) });
|
|
546
|
-
}
|
|
547
|
-
throw diskErr;
|
|
514
|
+
logWarning('projection', 'DECISIONS.md projection write failed; DB decision remains committed', { fn: 'saveDecisionToDb', id, error: String((diskErr as Error).message) });
|
|
548
515
|
}
|
|
549
516
|
// #2661: When a decision defers a slice, update the slice status in the DB
|
|
550
517
|
// so the dispatcher skips it. Without this, STATE.md and DECISIONS.md are
|
|
@@ -667,34 +634,7 @@ export async function updateRequirementInDb(
|
|
|
667
634
|
try {
|
|
668
635
|
const db = await import('./gsd-db.js');
|
|
669
636
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
// If requirement doesn't exist in DB, seed the entire requirements table
|
|
673
|
-
// from REQUIREMENTS.md first (#3346). This handles the standard workflow
|
|
674
|
-
// where requirements are authored in markdown during discussion but never
|
|
675
|
-
// imported into the database — making gsd_requirement_update always fail
|
|
676
|
-
// with "not_found" at milestone completion.
|
|
677
|
-
if (!existing) {
|
|
678
|
-
const reqFilePath = resolveGsdRootFile(basePath, 'REQUIREMENTS');
|
|
679
|
-
try {
|
|
680
|
-
const content = readFileSync(reqFilePath, 'utf-8');
|
|
681
|
-
const { parseRequirementsSections } = await import('./md-importer.js');
|
|
682
|
-
const parsed = parseRequirementsSections(content);
|
|
683
|
-
if (parsed.length > 0) {
|
|
684
|
-
logWarning('manifest', `Seeding ${parsed.length} requirements from REQUIREMENTS.md into DB (first update triggers import)`, { fn: 'updateRequirementInDb' });
|
|
685
|
-
for (const req of parsed) {
|
|
686
|
-
// Only seed if not already in DB (avoid overwriting concurrent inserts)
|
|
687
|
-
if (!db.getRequirementById(req.id)) {
|
|
688
|
-
db.upsertRequirement(req);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
// Re-check after seeding
|
|
692
|
-
existing = db.getRequirementById(id);
|
|
693
|
-
}
|
|
694
|
-
} catch {
|
|
695
|
-
// REQUIREMENTS.md missing or unparseable — fall through to skeleton
|
|
696
|
-
}
|
|
697
|
-
}
|
|
637
|
+
const existing = db.getRequirementById(id);
|
|
698
638
|
|
|
699
639
|
const base: Requirement = existing ?? {
|
|
700
640
|
id,
|
|
@@ -750,11 +690,7 @@ export async function updateRequirementInDb(
|
|
|
750
690
|
try {
|
|
751
691
|
await saveFile(filePath, md);
|
|
752
692
|
} catch (diskErr) {
|
|
753
|
-
|
|
754
|
-
if (existing) {
|
|
755
|
-
db.upsertRequirement(existing);
|
|
756
|
-
}
|
|
757
|
-
throw diskErr;
|
|
693
|
+
logWarning('projection', 'REQUIREMENTS.md projection write failed; DB requirement update remains committed', { fn: 'updateRequirementInDb', id, error: String((diskErr as Error).message) });
|
|
758
694
|
}
|
|
759
695
|
// Invalidate file-read caches so deriveState() sees the updated markdown.
|
|
760
696
|
// Do NOT clear the artifacts table — we just wrote to it intentionally.
|
|
@@ -805,20 +741,19 @@ export async function saveArtifactToDb(
|
|
|
805
741
|
contentToPersist = generateRequirementsMd(activeRequirements);
|
|
806
742
|
}
|
|
807
743
|
|
|
808
|
-
// Shrinkage guard: if the file already exists and the new
|
|
809
|
-
// significantly smaller (<50%), preserve the richer file on
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
// canonical
|
|
813
|
-
//
|
|
814
|
-
|
|
744
|
+
// Shrinkage guard: if the projection file already exists and the new
|
|
745
|
+
// content is significantly smaller (<50%), preserve the richer file on
|
|
746
|
+
// disk, but keep the DB row authoritative with the caller-provided content.
|
|
747
|
+
// The disk file is a stale projection until the next explicit render.
|
|
748
|
+
// Root canonical artifacts are exempt because their content is rendered
|
|
749
|
+
// from canonical DB state, and cleanup/consolidation is often intentionally
|
|
750
|
+
// much smaller than a malformed accumulated file.
|
|
815
751
|
let skipDiskWrite = false;
|
|
816
752
|
if (!isRootCanonicalArtifact(opts) && existsSync(fullPath)) {
|
|
817
753
|
const existingSize = statSync(fullPath).size;
|
|
818
754
|
const newSize = Buffer.byteLength(contentToPersist, 'utf-8');
|
|
819
755
|
if (existingSize > 0 && newSize < existingSize * 0.5) {
|
|
820
|
-
logWarning('
|
|
821
|
-
dbContent = readFileSync(fullPath, 'utf-8');
|
|
756
|
+
logWarning('projection', `new content (${newSize}B) is <50% of existing projection (${existingSize}B), preserving disk file while DB remains authoritative`, { fn: 'saveArtifactToDb', path: opts.path });
|
|
822
757
|
skipDiskWrite = true;
|
|
823
758
|
}
|
|
824
759
|
}
|
|
@@ -829,7 +764,7 @@ export async function saveArtifactToDb(
|
|
|
829
764
|
milestone_id: opts.milestone_id ?? null,
|
|
830
765
|
slice_id: opts.slice_id ?? null,
|
|
831
766
|
task_id: opts.task_id ?? null,
|
|
832
|
-
full_content:
|
|
767
|
+
full_content: contentToPersist,
|
|
833
768
|
});
|
|
834
769
|
|
|
835
770
|
// Write the file to disk (only if we're not preserving a richer existing file)
|
|
@@ -837,9 +772,7 @@ export async function saveArtifactToDb(
|
|
|
837
772
|
try {
|
|
838
773
|
await saveFile(fullPath, contentToPersist);
|
|
839
774
|
} catch (diskErr) {
|
|
840
|
-
|
|
841
|
-
db.deleteArtifactByPath(opts.path);
|
|
842
|
-
throw diskErr;
|
|
775
|
+
logWarning('projection', 'artifact projection write failed; DB artifact remains committed', { fn: 'saveArtifactToDb', path: opts.path, error: String((diskErr as Error).message) });
|
|
843
776
|
}
|
|
844
777
|
}
|
|
845
778
|
// Invalidate file-read caches so deriveState() sees the updated markdown.
|
|
@@ -54,21 +54,16 @@ export function getPriorSliceCompletionBlocker(
|
|
|
54
54
|
// completion, which is wrong when the SUMMARY is a failure-path report
|
|
55
55
|
// (verification FAILED, blocker placeholder, etc.). Resolve as follows:
|
|
56
56
|
// 1. When DB is available and status is closed → skip (authoritative).
|
|
57
|
-
// 2. When
|
|
58
|
-
//
|
|
59
|
-
// the guard can still block dependents of an active milestone.
|
|
60
|
-
// 3. Otherwise (SUMMARY without failure markers) → skip. Preserves
|
|
61
|
-
// the #1716 contract where a completed milestone with unchecked
|
|
62
|
-
// remediation slices is still treated as done.
|
|
63
|
-
const summaryPath = resolveMilestoneFile(base, mid, "SUMMARY");
|
|
57
|
+
// 2. When DB is unavailable, legacy SUMMARY.md fallback may skip.
|
|
58
|
+
// DB-backed projects must not treat SUMMARY.md as authoritative.
|
|
64
59
|
if (isDbAvailable()) {
|
|
65
60
|
const milestoneRow = getMilestone(mid);
|
|
66
61
|
if (milestoneRow && isClosedStatus(milestoneRow.status)) continue;
|
|
67
|
-
}
|
|
68
|
-
|
|
62
|
+
} else {
|
|
63
|
+
const summaryPath = resolveMilestoneFile(base, mid, "SUMMARY");
|
|
69
64
|
let summaryContent: string | null = null;
|
|
70
|
-
try { summaryContent = readFileSync(summaryPath, "utf-8"); } catch { /* ignore */ }
|
|
71
|
-
if (
|
|
65
|
+
try { summaryContent = summaryPath ? readFileSync(summaryPath, "utf-8") : null; } catch { /* ignore */ }
|
|
66
|
+
if (summaryContent && classifyMilestoneSummaryContent(summaryContent) !== "failure") {
|
|
72
67
|
continue;
|
|
73
68
|
}
|
|
74
69
|
}
|
|
@@ -3,7 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import type { DoctorIssue } from "./doctor-types.js";
|
|
5
5
|
import { isDbAvailable, _getAdapter } from "./gsd-db.js";
|
|
6
|
-
import { resolveMilestoneFile } from "./paths.js";
|
|
6
|
+
import { resolveGsdPathContract, resolveMilestoneFile } from "./paths.js";
|
|
7
7
|
import { deriveState } from "./state.js";
|
|
8
8
|
import { readEvents } from "./workflow-events.js";
|
|
9
9
|
import { renderAllProjections } from "./workflow-projections.js";
|
|
@@ -13,7 +13,7 @@ export async function checkEngineHealth(
|
|
|
13
13
|
issues: DoctorIssue[],
|
|
14
14
|
fixesApplied: string[],
|
|
15
15
|
): Promise<void> {
|
|
16
|
-
const dbPath =
|
|
16
|
+
const dbPath = resolveGsdPathContract(basePath).projectDb;
|
|
17
17
|
|
|
18
18
|
if (!isDbAvailable() && existsSync(dbPath)) {
|
|
19
19
|
issues.push({
|