gsd-pi 2.78.1-dev.b6a389b66 → 2.78.1-dev.d8826a445
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/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/phases.js +7 -2
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +3 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +185 -40
- package/dist/resources/extensions/gsd/auto.js +62 -1
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -16
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -55
- package/dist/resources/extensions/gsd/db-writer.js +96 -16
- package/dist/resources/extensions/gsd/delegation-policy.js +155 -0
- package/dist/resources/extensions/gsd/gsd-db.js +194 -0
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +117 -25
- package/dist/resources/extensions/gsd/metrics.js +287 -1
- package/dist/resources/extensions/gsd/paths.js +79 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
- package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
- package/dist/resources/extensions/gsd/templates/project.md +10 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
- package/dist/resources/extensions/gsd/workspace.js +59 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +15 -2
- package/dist/resources/extensions/gsd/write-intercept.js +3 -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 +10 -10
- 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/required-server-files.json +1 -1
- 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/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 +10 -10
- 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/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/README.md +2 -11
- package/packages/mcp-server/dist/remote-questions.d.ts +27 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
- package/packages/mcp-server/dist/remote-questions.js +28 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts +28 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +94 -4
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/mcp-server.test.ts +226 -0
- package/packages/mcp-server/src/remote-questions.test.ts +103 -0
- package/packages/mcp-server/src/remote-questions.ts +35 -0
- package/packages/mcp-server/src/server.ts +129 -6
- package/packages/mcp-server/src/workflow-tools.ts +1 -1
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/phases.ts +8 -2
- package/src/resources/extensions/gsd/auto/session.ts +4 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +10 -2
- package/src/resources/extensions/gsd/auto-post-unit.ts +8 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +225 -47
- package/src/resources/extensions/gsd/auto.ts +79 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -17
- package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +103 -0
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -55
- package/src/resources/extensions/gsd/db-writer.ts +113 -17
- package/src/resources/extensions/gsd/delegation-policy.ts +197 -0
- package/src/resources/extensions/gsd/gsd-db.ts +184 -0
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +154 -25
- package/src/resources/extensions/gsd/metrics.ts +321 -1
- package/src/resources/extensions/gsd/paths.ts +67 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
- package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
- package/src/resources/extensions/gsd/templates/project.md +10 -0
- package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +14 -14
- package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +331 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/db-writer-path-containment.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/db-writer-root-artifact.test.ts +221 -0
- package/src/resources/extensions/gsd/tests/db-writer-scope.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/delegation-policy.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/dispatch-backgroundable-annotation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +3 -23
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +193 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +246 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +218 -0
- package/src/resources/extensions/gsd/tests/gsd-db-failed-open-restore.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/gsd-db-workspace-scope.test.ts +226 -0
- package/src/resources/extensions/gsd/tests/gsd-root-canonical.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/gsd-root-home-guard.test.ts +68 -5
- package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +371 -0
- package/src/resources/extensions/gsd/tests/metrics-atomic-merge.test.ts +222 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-hardening.test.ts +400 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-not-acquired.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-retry-sleep.test.ts +287 -0
- package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +149 -0
- package/src/resources/extensions/gsd/tests/metrics-scope.test.ts +378 -0
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +329 -0
- package/src/resources/extensions/gsd/tests/path-cache-decoupled.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/path-normalization-unified.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/paths-cache.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +150 -7
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +74 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +28 -16
- package/src/resources/extensions/gsd/tests/resume-missing-worktree-warning.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +453 -0
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +162 -0
- package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +239 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/workspace.test.ts +190 -0
- package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +35 -35
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -52
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +1 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
- package/src/resources/extensions/gsd/workspace.ts +95 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +16 -2
- package/src/resources/extensions/gsd/write-intercept.ts +3 -3
- /package/dist/web/standalone/.next/static/{HahrZrc_Xn4wumj0O1Ydp → AT5qi39nKXkdmQIOIoh0f}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{HahrZrc_Xn4wumj0O1Ydp → AT5qi39nKXkdmQIOIoh0f}/_ssgManifest.js +0 -0
|
@@ -1156,6 +1156,193 @@ let _exitHandlerRegistered = false;
|
|
|
1156
1156
|
let _dbOpenAttempted = false;
|
|
1157
1157
|
let _lastDbError = null;
|
|
1158
1158
|
let _lastDbPhase = null;
|
|
1159
|
+
/**
|
|
1160
|
+
* Identity key of the workspace whose connection is currently active
|
|
1161
|
+
* (currentDb). Set by openDatabaseByWorkspace(); null when the active
|
|
1162
|
+
* connection was opened via the legacy openDatabase(path) path.
|
|
1163
|
+
*/
|
|
1164
|
+
let _currentIdentityKey = null;
|
|
1165
|
+
/**
|
|
1166
|
+
* Workspace-scoped connection cache.
|
|
1167
|
+
* Key: GsdWorkspace.identityKey (realpath-normalized project root).
|
|
1168
|
+
* Value: the DB path and open adapter for that workspace.
|
|
1169
|
+
*
|
|
1170
|
+
* Sibling worktrees of the same project share the same identityKey (set by
|
|
1171
|
+
* createWorkspace) and therefore reuse the same cached connection, preserving
|
|
1172
|
+
* shared-WAL semantics. Different projects get distinct cache entries.
|
|
1173
|
+
*
|
|
1174
|
+
* NOTE: Only one connection is "active" at a time (currentDb/currentPath).
|
|
1175
|
+
* The cache allows fast re-activation of a previously opened connection when
|
|
1176
|
+
* callers switch between known workspaces via openDatabaseByWorkspace().
|
|
1177
|
+
*/
|
|
1178
|
+
const _dbCache = new Map();
|
|
1179
|
+
/** Test helper: expose the internal cache for inspection. Not for production use. */
|
|
1180
|
+
export function _getDbCache() {
|
|
1181
|
+
return _dbCache;
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Close and evict every entry in the workspace connection cache, then call
|
|
1185
|
+
* closeDatabase() to close the active connection.
|
|
1186
|
+
*
|
|
1187
|
+
* Use this for test teardown or process-shutdown paths where every open
|
|
1188
|
+
* connection must be flushed. Normal callers should use closeDatabase() or
|
|
1189
|
+
* closeDatabaseByWorkspace() instead.
|
|
1190
|
+
*/
|
|
1191
|
+
export function closeAllDatabases() {
|
|
1192
|
+
// Close all non-active cached connections first.
|
|
1193
|
+
for (const [key, entry] of _dbCache) {
|
|
1194
|
+
if (entry.db === currentDb)
|
|
1195
|
+
continue; // handled by closeDatabase() below
|
|
1196
|
+
_dbCache.delete(key);
|
|
1197
|
+
try {
|
|
1198
|
+
entry.db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
1199
|
+
}
|
|
1200
|
+
catch { /* best-effort */ }
|
|
1201
|
+
try {
|
|
1202
|
+
entry.db.exec("PRAGMA incremental_vacuum(64)");
|
|
1203
|
+
}
|
|
1204
|
+
catch { /* best-effort */ }
|
|
1205
|
+
try {
|
|
1206
|
+
entry.db.close();
|
|
1207
|
+
}
|
|
1208
|
+
catch { /* best-effort */ }
|
|
1209
|
+
}
|
|
1210
|
+
closeDatabase();
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Open (or reuse) the database connection scoped to the given workspace.
|
|
1214
|
+
*
|
|
1215
|
+
* Uses workspace.identityKey as the cache key, so sibling worktrees of the
|
|
1216
|
+
* same project resolve to the same connection. On a cache hit the existing
|
|
1217
|
+
* adapter is reactivated as the current connection without re-opening the
|
|
1218
|
+
* file. On a cache miss, delegates to openDatabase() for the full
|
|
1219
|
+
* open + schema-init + migration flow, then caches the result.
|
|
1220
|
+
*
|
|
1221
|
+
* When switching to a different workspace, the previously active connection
|
|
1222
|
+
* is preserved in the cache (not closed), so callers can switch back to it
|
|
1223
|
+
* cheaply via a subsequent openDatabaseByWorkspace() call.
|
|
1224
|
+
*
|
|
1225
|
+
* @param workspace A GsdWorkspace created by createWorkspace().
|
|
1226
|
+
* @returns true if the connection is open and ready, false otherwise.
|
|
1227
|
+
*/
|
|
1228
|
+
export function openDatabaseByWorkspace(workspace) {
|
|
1229
|
+
const key = workspace.identityKey;
|
|
1230
|
+
const dbPath = workspace.contract.projectDb;
|
|
1231
|
+
const cached = _dbCache.get(key);
|
|
1232
|
+
if (cached) {
|
|
1233
|
+
// Reactivate the cached connection as the current singleton.
|
|
1234
|
+
currentDb = cached.db;
|
|
1235
|
+
currentPath = cached.dbPath;
|
|
1236
|
+
currentPid = process.pid;
|
|
1237
|
+
_dbOpenAttempted = true;
|
|
1238
|
+
_currentIdentityKey = key;
|
|
1239
|
+
return true;
|
|
1240
|
+
}
|
|
1241
|
+
// Cache miss — need to open a new connection.
|
|
1242
|
+
//
|
|
1243
|
+
// If there is a currently active workspace connection, stash it in the
|
|
1244
|
+
// cache under its identity key before calling openDatabase(), because
|
|
1245
|
+
// openDatabase() will call closeDatabase() when the path changes (which
|
|
1246
|
+
// would destroy the existing adapter). By nulling out currentDb first,
|
|
1247
|
+
// we prevent openDatabase() from closing the live adapter.
|
|
1248
|
+
let oldDb = null;
|
|
1249
|
+
let oldPath = null;
|
|
1250
|
+
let oldPid = 0;
|
|
1251
|
+
let oldKey = null;
|
|
1252
|
+
if (currentDb !== null && _currentIdentityKey !== null) {
|
|
1253
|
+
// Snapshot the old globals so we can restore them on failure.
|
|
1254
|
+
oldDb = currentDb;
|
|
1255
|
+
oldPath = currentPath;
|
|
1256
|
+
oldPid = currentPid;
|
|
1257
|
+
oldKey = _currentIdentityKey;
|
|
1258
|
+
// Save the current connection so it stays alive in the cache.
|
|
1259
|
+
_dbCache.set(_currentIdentityKey, {
|
|
1260
|
+
dbPath: currentPath,
|
|
1261
|
+
db: currentDb,
|
|
1262
|
+
});
|
|
1263
|
+
// Detach from globals so openDatabase() opens fresh without closing it.
|
|
1264
|
+
currentDb = null;
|
|
1265
|
+
currentPath = null;
|
|
1266
|
+
currentPid = 0;
|
|
1267
|
+
_currentIdentityKey = null;
|
|
1268
|
+
}
|
|
1269
|
+
// Run the full open/schema/migration flow for the new workspace.
|
|
1270
|
+
// openDatabase() can throw on corrupt DB or permission error — catch so we
|
|
1271
|
+
// can restore the previous connection rather than leaving globals null.
|
|
1272
|
+
let opened;
|
|
1273
|
+
try {
|
|
1274
|
+
opened = openDatabase(dbPath);
|
|
1275
|
+
}
|
|
1276
|
+
catch (err) {
|
|
1277
|
+
// Failed to open the new DB. Restore the previous workspace connection so
|
|
1278
|
+
// the caller's workspace remains active (it is still safe in _dbCache).
|
|
1279
|
+
if (oldDb !== null) {
|
|
1280
|
+
currentDb = oldDb;
|
|
1281
|
+
currentPath = oldPath;
|
|
1282
|
+
currentPid = oldPid;
|
|
1283
|
+
_currentIdentityKey = oldKey;
|
|
1284
|
+
}
|
|
1285
|
+
throw err;
|
|
1286
|
+
}
|
|
1287
|
+
if (opened && currentDb) {
|
|
1288
|
+
_dbCache.set(key, { dbPath, db: currentDb });
|
|
1289
|
+
_currentIdentityKey = key;
|
|
1290
|
+
}
|
|
1291
|
+
else if (!opened && oldDb !== null) {
|
|
1292
|
+
// Restore the previous connection so the caller's workspace remains active.
|
|
1293
|
+
// The failed attempt left no live adapter, so the globals stayed null.
|
|
1294
|
+
currentDb = oldDb;
|
|
1295
|
+
currentPath = oldPath;
|
|
1296
|
+
currentPid = oldPid;
|
|
1297
|
+
_currentIdentityKey = oldKey;
|
|
1298
|
+
}
|
|
1299
|
+
return opened;
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Open (or reuse) the database connection scoped to the workspace in a
|
|
1303
|
+
* MilestoneScope. Thin delegation to openDatabaseByWorkspace().
|
|
1304
|
+
*/
|
|
1305
|
+
export function openDatabaseByScope(scope) {
|
|
1306
|
+
return openDatabaseByWorkspace(scope.workspace);
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Close the database connection for the given workspace and remove it from
|
|
1310
|
+
* the cache. If the workspace's connection is currently active (currentDb),
|
|
1311
|
+
* performs a full closeDatabase() including WAL checkpoint. Otherwise only
|
|
1312
|
+
* removes the cache entry (the adapter was already replaced by a later open).
|
|
1313
|
+
*/
|
|
1314
|
+
export function closeDatabaseByWorkspace(workspace) {
|
|
1315
|
+
const key = workspace.identityKey;
|
|
1316
|
+
const cached = _dbCache.get(key);
|
|
1317
|
+
if (!cached)
|
|
1318
|
+
return;
|
|
1319
|
+
_dbCache.delete(key);
|
|
1320
|
+
if (currentDb === cached.db) {
|
|
1321
|
+
// This workspace's connection is the active one — full close.
|
|
1322
|
+
closeDatabase();
|
|
1323
|
+
}
|
|
1324
|
+
else {
|
|
1325
|
+
// Connection was displaced by a later open; close the adapter directly.
|
|
1326
|
+
try {
|
|
1327
|
+
cached.db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
1328
|
+
}
|
|
1329
|
+
catch (e) {
|
|
1330
|
+
logWarning("db", `WAL checkpoint (byWorkspace) failed: ${e.message}`);
|
|
1331
|
+
}
|
|
1332
|
+
try {
|
|
1333
|
+
cached.db.exec("PRAGMA incremental_vacuum(64)");
|
|
1334
|
+
}
|
|
1335
|
+
catch (e) {
|
|
1336
|
+
logWarning("db", `incremental vacuum (byWorkspace) failed: ${e.message}`);
|
|
1337
|
+
}
|
|
1338
|
+
try {
|
|
1339
|
+
cached.db.close();
|
|
1340
|
+
}
|
|
1341
|
+
catch (e) {
|
|
1342
|
+
logWarning("db", `database close (byWorkspace) failed: ${e.message}`);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1159
1346
|
export function getDbProvider() {
|
|
1160
1347
|
loadProvider();
|
|
1161
1348
|
return providerName;
|
|
@@ -1300,6 +1487,13 @@ export function closeDatabase() {
|
|
|
1300
1487
|
catch (e) {
|
|
1301
1488
|
logWarning("db", `database close failed: ${e.message}`);
|
|
1302
1489
|
}
|
|
1490
|
+
// If this connection was workspace-tracked, evict it from the cache so
|
|
1491
|
+
// subsequent openDatabaseByWorkspace() calls re-open rather than reactivate
|
|
1492
|
+
// a closed adapter.
|
|
1493
|
+
if (_currentIdentityKey !== null) {
|
|
1494
|
+
_dbCache.delete(_currentIdentityKey);
|
|
1495
|
+
_currentIdentityKey = null;
|
|
1496
|
+
}
|
|
1303
1497
|
currentDb = null;
|
|
1304
1498
|
currentPath = null;
|
|
1305
1499
|
currentPid = 0;
|
|
@@ -150,7 +150,7 @@ export async function showQueueAdd(ctx, pi, basePath, state) {
|
|
|
150
150
|
].join(" ");
|
|
151
151
|
// ── Dispatch the queue prompt ───────────────────────────────────────
|
|
152
152
|
// Activate the queue phase so the write-gate applies to CONTEXT.md writes
|
|
153
|
-
setQueuePhaseActive(true);
|
|
153
|
+
setQueuePhaseActive(true, basePath);
|
|
154
154
|
const queueInlinedTemplates = inlineTemplate("context", "Context");
|
|
155
155
|
const prompt = loadPrompt("queue", {
|
|
156
156
|
preamble,
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { showNextAction } from "../shared/tui.js";
|
|
9
9
|
import { loadFile, saveFile } from "./files.js";
|
|
10
|
-
import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js";
|
|
10
|
+
import { isDbAvailable, getMilestone, getMilestoneSlices } from "./gsd-db.js";
|
|
11
11
|
import { parseRoadmapSlices } from "./roadmap-slices.js";
|
|
12
12
|
import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
|
|
13
13
|
import { buildCompleteSlicePrompt, buildDiscussMilestonePrompt, buildExecuteTaskPrompt, buildPlanMilestonePrompt, buildPlanSlicePrompt, buildSkillActivationBlock, } from "./auto-prompts.js";
|
|
14
|
-
import { deriveState } from "./state.js";
|
|
14
|
+
import { deriveState, isGhostMilestone } from "./state.js";
|
|
15
15
|
import { invalidateAllCaches } from "./cache.js";
|
|
16
16
|
import { startAutoDetached } from "./auto.js";
|
|
17
17
|
import { clearLock } from "./crash-recovery.js";
|
|
@@ -19,7 +19,7 @@ import { assessInterruptedSession, formatInterruptedSessionRunningMessage, forma
|
|
|
19
19
|
import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
|
|
20
20
|
import { resolveExpectedArtifactPath } from "./auto.js";
|
|
21
21
|
import { gsdHome } from "./gsd-home.js";
|
|
22
|
-
import { gsdRoot, milestonesDir, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveGsdRootFile, relGsdRootFile, relMilestoneFile, relSliceFile, } from "./paths.js";
|
|
22
|
+
import { gsdRoot, milestonesDir, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveGsdRootFile, relGsdRootFile, relMilestoneFile, relSliceFile, clearPathCache, } from "./paths.js";
|
|
23
23
|
import { join } from "node:path";
|
|
24
24
|
import { readFileSync, existsSync, mkdirSync, readdirSync, rmSync, unlinkSync } from "node:fs";
|
|
25
25
|
import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.js";
|
|
@@ -43,10 +43,39 @@ import { DISCUSS_TOOLS_ALLOWLIST } from "./constants.js";
|
|
|
43
43
|
import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForGuidedUnit, supportsStructuredQuestions, } from "./workflow-mcp.js";
|
|
44
44
|
import { runPreparation, formatCodebaseBrief, formatPriorContextBrief, } from "./preparation.js";
|
|
45
45
|
import { verifyExpectedArtifact } from "./auto-recovery.js";
|
|
46
|
+
import { createWorkspace, scopeMilestone } from "./workspace.js";
|
|
46
47
|
// ─── Re-exports (preserve public API for existing importers) ────────────────
|
|
47
48
|
export { MILESTONE_ID_RE, generateMilestoneSuffix, nextMilestoneId, extractMilestoneSeq, parseMilestoneId, milestoneIdSort, maxMilestoneNum, findMilestoneIds, reserveMilestoneId, claimReservedId, getReservedMilestoneIds, clearReservedMilestoneIds, } from "./milestone-ids.js";
|
|
48
49
|
export { showQueue, handleQueueReorder, showQueueAdd, buildExistingMilestonesContext, } from "./guided-flow-queue.js";
|
|
49
50
|
import { logWarning } from "./workflow-logger.js";
|
|
51
|
+
// ─── Scope-based validator wrappers ──────────────────────────────────────────
|
|
52
|
+
// These thin wrappers accept a MilestoneScope so callers that already hold a
|
|
53
|
+
// pinned scope never have to re-derive (basePath, milestoneId) separately.
|
|
54
|
+
// The underlying implementations in auto-recovery.ts / auto-artifact-paths.ts /
|
|
55
|
+
// state.ts are unchanged — only the call surface in guided-flow.ts is migrated.
|
|
56
|
+
/**
|
|
57
|
+
* Scope-based overload of verifyExpectedArtifact.
|
|
58
|
+
* Uses scope.workspace.projectRoot as the authoritative base path, making
|
|
59
|
+
* the check immune to cwd-drift and worktree-path divergence.
|
|
60
|
+
*/
|
|
61
|
+
export function verifyExpectedArtifactForScope(scope, unitType, unitId) {
|
|
62
|
+
return verifyExpectedArtifact(unitType, unitId, scope.workspace.projectRoot);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Scope-based overload of resolveExpectedArtifactPath.
|
|
66
|
+
* Returns the canonical absolute path (or null) using the scope's projectRoot.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveExpectedArtifactPathForScope(scope, unitType, unitId) {
|
|
69
|
+
return resolveExpectedArtifactPath(unitType, unitId, scope.workspace.projectRoot);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Scope-based overload of isGhostMilestone.
|
|
73
|
+
* Binds basePath and milestoneId from the scope, ensuring path resolution
|
|
74
|
+
* uses the canonical project root regardless of the cwd at call time.
|
|
75
|
+
*/
|
|
76
|
+
export function isGhostMilestoneByScope(scope) {
|
|
77
|
+
return isGhostMilestone(scope.workspace.projectRoot, scope.milestoneId);
|
|
78
|
+
}
|
|
50
79
|
function needsPlanV2Gate(state) {
|
|
51
80
|
return state.phase === "executing"
|
|
52
81
|
|| state.phase === "summarizing"
|
|
@@ -77,6 +106,10 @@ function buildDocsCommitInstruction(_message) {
|
|
|
77
106
|
// #4573: cap for how many times we nudge the LLM after a premature ready
|
|
78
107
|
// phrase before giving up and asking the user to re-run /gsd.
|
|
79
108
|
const MAX_READY_REJECTS = 2;
|
|
109
|
+
// H1 (#5012): cap for Gate 1b plan-blocked recovery hints. After this many
|
|
110
|
+
// consecutive recovery attempts the loop is stopped and the user is directed
|
|
111
|
+
// to investigate manually.
|
|
112
|
+
const MAX_PLAN_BLOCKED_RECOVERIES = 3;
|
|
80
113
|
// #4573: matches the canonical ready phrase the discuss prompt asks the LLM
|
|
81
114
|
// to emit. Accepts any M-prefixed milestone ID (three digits + optional
|
|
82
115
|
// suffix) with optional trailing punctuation.
|
|
@@ -110,9 +143,9 @@ This stage is running inside the foreground \`/gsd new-project --deep\` intervie
|
|
|
110
143
|
/**
|
|
111
144
|
* Backward-compat bridge: returns a mutable reference to the entry matching
|
|
112
145
|
* basePath, or the sole entry when only one session exists.
|
|
113
|
-
*
|
|
146
|
+
* Exported for testing — internal use only in production code.
|
|
114
147
|
*/
|
|
115
|
-
function _getPendingAutoStart(basePath) {
|
|
148
|
+
export function _getPendingAutoStart(basePath) {
|
|
116
149
|
if (basePath)
|
|
117
150
|
return pendingAutoStartMap.get(basePath) ?? null;
|
|
118
151
|
if (pendingAutoStartMap.size === 1)
|
|
@@ -157,7 +190,9 @@ function clearEmptyLegacyDeepSetupPseudoMilestones(basePath, entries) {
|
|
|
157
190
|
* Exported for testing (#2985).
|
|
158
191
|
*/
|
|
159
192
|
export function setPendingAutoStart(basePath, entry) {
|
|
160
|
-
|
|
193
|
+
const ws = createWorkspace(entry.basePath);
|
|
194
|
+
const scope = scopeMilestone(ws, entry.milestoneId);
|
|
195
|
+
pendingAutoStartMap.set(basePath, { createdAt: Date.now(), planBlockedRecoveryCount: 0, ...entry, scope });
|
|
161
196
|
}
|
|
162
197
|
/**
|
|
163
198
|
* Clear pending auto-start state.
|
|
@@ -249,6 +284,10 @@ export async function checkDeepProjectSetupAfterTurn(_event, ctx, basePath) {
|
|
|
249
284
|
if (!entry)
|
|
250
285
|
return false;
|
|
251
286
|
if (entry.currentUnitType && entry.currentUnitId) {
|
|
287
|
+
// TODO(C-future): PendingDeepProjectSetupEntry does not carry a MilestoneScope
|
|
288
|
+
// because deep-project-setup units span non-milestone unit types (discuss-project,
|
|
289
|
+
// discuss-requirements, etc.). Migrate to verifyExpectedArtifactForScope once
|
|
290
|
+
// PendingDeepProjectSetupEntry is extended with a scope field.
|
|
252
291
|
const artifactReady = verifyExpectedArtifact(entry.currentUnitType, entry.currentUnitId, entry.basePath);
|
|
253
292
|
if (!artifactReady) {
|
|
254
293
|
return false;
|
|
@@ -320,17 +359,61 @@ export function checkAutoStartAfterDiscuss() {
|
|
|
320
359
|
const { ctx, pi, basePath, milestoneId, step } = entry;
|
|
321
360
|
// Gate 1: Primary milestone must have CONTEXT.md or ROADMAP.md
|
|
322
361
|
// The "discuss" path creates CONTEXT.md; the "plan" path creates ROADMAP.md.
|
|
323
|
-
|
|
324
|
-
const
|
|
362
|
+
// Use pinned scope (immune to cwd-drift) for existence checks.
|
|
363
|
+
const contextFilePath = entry.scope.contextFile();
|
|
364
|
+
const roadmapFilePath = entry.scope.roadmapFile();
|
|
365
|
+
const contextFile = existsSync(contextFilePath) ? contextFilePath : null;
|
|
366
|
+
const roadmapFile = existsSync(roadmapFilePath) ? roadmapFilePath : null;
|
|
325
367
|
if (!contextFile && !roadmapFile)
|
|
326
368
|
return false; // neither artifact yet — keep waiting
|
|
369
|
+
// Gate 1b: Discriminate plan-blocked from discuss-incomplete when the DB row is queued.
|
|
370
|
+
// If the DB is available and the row is still "queued" but CONTEXT.md already exists on
|
|
371
|
+
// disk, the discuss phase completed but gsd_plan_milestone was hard-blocked by the
|
|
372
|
+
// depth-verification gate. Emit a recovery hint so the next agent turn can retry
|
|
373
|
+
// gsd_plan_milestone, then return false (keep blocking auto-start).
|
|
374
|
+
// If CONTEXT.md does not exist (discuss-incomplete), Gate 1 already blocked above.
|
|
375
|
+
if (isDbAvailable()) {
|
|
376
|
+
const dbRow = getMilestone(milestoneId);
|
|
377
|
+
if (dbRow?.status === "queued" && contextFile) {
|
|
378
|
+
if (entry.planBlockedRecoveryCount >= MAX_PLAN_BLOCKED_RECOVERIES) {
|
|
379
|
+
// H1: recovery loop cap reached — stop triggering new turns, escalate to user.
|
|
380
|
+
logWarning("guided", `Gate 1b: milestone ${milestoneId} plan-blocked recovery limit reached ` +
|
|
381
|
+
`(${entry.planBlockedRecoveryCount}/${MAX_PLAN_BLOCKED_RECOVERIES}); escalating to user`);
|
|
382
|
+
ctx.ui.notify(`Milestone ${milestoneId} plan_milestone has been blocked ${entry.planBlockedRecoveryCount} times. ` +
|
|
383
|
+
`Re-run /gsd to reset the recovery counter, or run /gsd-debug to diagnose without resetting.`, "error");
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
logWarning("guided", `Gate 1b: milestone ${milestoneId} queued with CONTEXT.md present — ` +
|
|
387
|
+
`plan_milestone was blocked; emitting recovery hint ` +
|
|
388
|
+
`(attempt ${entry.planBlockedRecoveryCount + 1}/${MAX_PLAN_BLOCKED_RECOVERIES})`);
|
|
389
|
+
ctx.ui.notify(`Milestone ${milestoneId}: context file exists but milestone is still queued. ` +
|
|
390
|
+
`Retrying gsd_plan_milestone to complete the blocked planning step.`, "warning");
|
|
391
|
+
try {
|
|
392
|
+
pi.sendMessage({
|
|
393
|
+
customType: "gsd-plan-milestone-blocked-recovery",
|
|
394
|
+
content: `Milestone ${milestoneId} has ${contextFile} on disk but its DB row is still ` +
|
|
395
|
+
`"queued". The gsd_plan_milestone tool was previously blocked by the ` +
|
|
396
|
+
`depth-verification gate. Call gsd_plan_milestone now to complete the ` +
|
|
397
|
+
`planning phase.`,
|
|
398
|
+
display: false,
|
|
399
|
+
}, { triggerTurn: true });
|
|
400
|
+
// Increment only after a successful dispatch so transient sendMessage
|
|
401
|
+
// failures do not consume recovery budget.
|
|
402
|
+
entry.planBlockedRecoveryCount += 1;
|
|
403
|
+
}
|
|
404
|
+
catch (e) {
|
|
405
|
+
logWarning("guided", `Gate 1b recovery sendMessage failed: ${e.message}`);
|
|
406
|
+
}
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
327
410
|
// Gate 2: STATE.md must exist — written as the last step in the discuss
|
|
328
411
|
// output phase. This prevents auto-start from firing during Phase 3
|
|
329
412
|
// (sequential readiness gates for remaining milestones) in multi-milestone
|
|
330
413
|
// discussions, where M001-CONTEXT.md exists but M002/M003 haven't been
|
|
331
414
|
// processed yet.
|
|
332
|
-
const
|
|
333
|
-
if (!
|
|
415
|
+
const stateFilePath = entry.scope.stateFile();
|
|
416
|
+
if (!existsSync(stateFilePath))
|
|
334
417
|
return false; // discussion not finalized yet
|
|
335
418
|
// Gate 3: Multi-milestone completeness warning
|
|
336
419
|
// Parse PROJECT.md for milestone sequence, warn if any are missing context.
|
|
@@ -362,7 +445,7 @@ export function checkAutoStartAfterDiscuss() {
|
|
|
362
445
|
// The LLM writes DISCUSSION-MANIFEST.json after each Phase 3 gate decision.
|
|
363
446
|
// When it exists, validate it before auto-starting. Project history alone is
|
|
364
447
|
// not a reliable signal for the current discussion mode.
|
|
365
|
-
const manifestPath = join(
|
|
448
|
+
const manifestPath = join(entry.scope.workspace.contract.projectGsd, "DISCUSSION-MANIFEST.json");
|
|
366
449
|
if (existsSync(manifestPath)) {
|
|
367
450
|
try {
|
|
368
451
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
@@ -486,6 +569,12 @@ export function maybeHandleReadyPhraseWithoutFiles(event) {
|
|
|
486
569
|
const text = extractAssistantText(lastMsg);
|
|
487
570
|
if (!READY_PHRASE_RE.test(text))
|
|
488
571
|
return false;
|
|
572
|
+
// Bust paths.ts cached dir listings before checking for fresh writes. The
|
|
573
|
+
// LLM's Write tool calls do not invalidate paths.ts caches, so a stale
|
|
574
|
+
// listing taken before the milestone dir or its CONTEXT/ROADMAP files
|
|
575
|
+
// existed would falsely report the artifacts as missing and trigger the
|
|
576
|
+
// 3-strike "ready without files" abort even though the writes succeeded.
|
|
577
|
+
clearPathCache();
|
|
489
578
|
// Gate: artifacts must still be missing — if they exist, the happy path
|
|
490
579
|
// already fired and we have nothing to do.
|
|
491
580
|
const contextFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT");
|
|
@@ -890,7 +979,7 @@ export async function showHeadlessMilestoneCreation(ctx, pi, basePath, seedConte
|
|
|
890
979
|
// Build and dispatch the headless discuss prompt
|
|
891
980
|
const prompt = buildHeadlessDiscussPrompt(nextId, seedContext, basePath);
|
|
892
981
|
// Set pending auto start (auto-mode triggers on "Milestone X ready." via checkAutoStartAfterDiscuss)
|
|
893
|
-
|
|
982
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId });
|
|
894
983
|
// Dispatch as discuss-milestone. The LLM writes PROJECT.md, REQUIREMENTS.md,
|
|
895
984
|
// and CONTEXT.md, then calls gsd_plan_milestone — this is semantically the
|
|
896
985
|
// discuss path, just non-interactive. Using "plan-milestone" here caused
|
|
@@ -1056,13 +1145,13 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1056
1145
|
const seed = draftContent
|
|
1057
1146
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
1058
1147
|
: basePrompt;
|
|
1059
|
-
|
|
1148
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: mid, step: false });
|
|
1060
1149
|
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
1061
1150
|
}
|
|
1062
1151
|
else if (choice === "discuss_fresh") {
|
|
1063
1152
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1064
1153
|
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
1065
|
-
|
|
1154
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: mid, step: false });
|
|
1066
1155
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1067
1156
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1068
1157
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
@@ -1075,7 +1164,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1075
1164
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1076
1165
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1077
1166
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1078
|
-
|
|
1167
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false });
|
|
1079
1168
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1080
1169
|
}
|
|
1081
1170
|
return;
|
|
@@ -1329,6 +1418,9 @@ function selfHealRuntimeRecords(basePath, ctx) {
|
|
|
1329
1418
|
for (const record of records) {
|
|
1330
1419
|
const { unitType, unitId, phase } = record;
|
|
1331
1420
|
// Clear records whose expected artifact already exists (completed but not cleaned up)
|
|
1421
|
+
// TODO(C-future): selfHealRuntimeRecords iterates across all unit types (not just milestone
|
|
1422
|
+
// units), so it cannot be converted to resolveExpectedArtifactPathForScope without
|
|
1423
|
+
// first establishing a per-record scope. Migrate once unit runtime records carry scope info.
|
|
1332
1424
|
const artifactPath = resolveExpectedArtifactPath(unitType, unitId, basePath);
|
|
1333
1425
|
if (artifactPath && existsSync(artifactPath)) {
|
|
1334
1426
|
clearUnitRuntimeRecord(basePath, unitType, unitId);
|
|
@@ -1431,7 +1523,7 @@ async function handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneT
|
|
|
1431
1523
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1432
1524
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1433
1525
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1434
|
-
|
|
1526
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1435
1527
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1436
1528
|
return true;
|
|
1437
1529
|
}
|
|
@@ -1636,7 +1728,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1636
1728
|
const isFirst = milestoneIds.length === 0;
|
|
1637
1729
|
if (isFirst) {
|
|
1638
1730
|
// First ever — skip wizard, just ask directly
|
|
1639
|
-
|
|
1731
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1640
1732
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1641
1733
|
}
|
|
1642
1734
|
else {
|
|
@@ -1654,7 +1746,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1654
1746
|
notYetMessage: "Run /gsd when ready.",
|
|
1655
1747
|
});
|
|
1656
1748
|
if (choice === "new_milestone") {
|
|
1657
|
-
|
|
1749
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1658
1750
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1659
1751
|
}
|
|
1660
1752
|
}
|
|
@@ -1663,7 +1755,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1663
1755
|
const milestoneId = state.activeMilestone.id;
|
|
1664
1756
|
const milestoneTitle = state.activeMilestone.title;
|
|
1665
1757
|
if (planV2GateDecision === "recover-missing-context") {
|
|
1666
|
-
|
|
1758
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1667
1759
|
await dispatchWorkflow(pi, await buildDiscussMilestonePrompt(milestoneId, milestoneTitle, basePath, getStructuredQuestionsAvailability(pi, ctx)), "gsd-discuss", ctx, "discuss-milestone");
|
|
1668
1760
|
return;
|
|
1669
1761
|
}
|
|
@@ -1691,7 +1783,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1691
1783
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1692
1784
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1693
1785
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1694
|
-
|
|
1786
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1695
1787
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1696
1788
|
}
|
|
1697
1789
|
else if (choice === "status") {
|
|
@@ -1738,13 +1830,13 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1738
1830
|
const seed = draftContent
|
|
1739
1831
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
1740
1832
|
: basePrompt;
|
|
1741
|
-
|
|
1833
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1742
1834
|
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
1743
1835
|
}
|
|
1744
1836
|
else if (choice === "discuss_fresh") {
|
|
1745
1837
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1746
1838
|
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
1747
|
-
|
|
1839
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1748
1840
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1749
1841
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1750
1842
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
@@ -1755,7 +1847,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1755
1847
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1756
1848
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1757
1849
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1758
|
-
|
|
1850
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1759
1851
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1760
1852
|
}
|
|
1761
1853
|
return;
|
|
@@ -1811,7 +1903,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1811
1903
|
notYetMessage: "Run /gsd when ready.",
|
|
1812
1904
|
});
|
|
1813
1905
|
if (choice === "plan") {
|
|
1814
|
-
|
|
1906
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1815
1907
|
await dispatchWorkflow(pi, await buildPlanMilestonePrompt(milestoneId, milestoneTitle, basePath), "gsd-run", ctx, "plan-milestone");
|
|
1816
1908
|
}
|
|
1817
1909
|
else if (choice === "discuss") {
|
|
@@ -1827,7 +1919,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1827
1919
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1828
1920
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1829
1921
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1830
|
-
|
|
1922
|
+
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1831
1923
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1832
1924
|
}
|
|
1833
1925
|
else if (choice === "discard_milestone") {
|