gsd-pi 2.35.0-dev.39eee32 → 2.35.0-dev.4bbf377

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.
Files changed (71) hide show
  1. package/README.md +3 -1
  2. package/dist/resources/extensions/gsd/auto-loop.js +7 -2
  3. package/dist/resources/extensions/gsd/auto-model-selection.js +15 -3
  4. package/dist/resources/extensions/gsd/commands-rate.js +31 -0
  5. package/dist/resources/extensions/gsd/commands.js +43 -1
  6. package/dist/resources/extensions/gsd/guided-flow.js +7 -1
  7. package/dist/resources/extensions/gsd/index.js +16 -32
  8. package/dist/resources/extensions/gsd/preferences.js +12 -0
  9. package/dist/resources/extensions/gsd/session-lock.js +27 -0
  10. package/dist/resources/skills/core-web-vitals/SKILL.md +1 -1
  11. package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +1 -1
  12. package/dist/resources/skills/github-workflows/SKILL.md +0 -2
  13. package/dist/resources/skills/web-quality-audit/SKILL.md +0 -2
  14. package/package.json +1 -1
  15. package/packages/pi-agent-core/dist/agent.d.ts +10 -2
  16. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  17. package/packages/pi-agent-core/dist/agent.js +19 -8
  18. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  19. package/packages/pi-agent-core/src/agent.ts +31 -10
  20. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  21. package/packages/pi-coding-agent/dist/core/agent-session.js +20 -4
  22. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  23. package/packages/pi-coding-agent/src/core/agent-session.ts +36 -12
  24. package/src/resources/extensions/gsd/auto-loop.ts +11 -1
  25. package/src/resources/extensions/gsd/auto-model-selection.ts +23 -2
  26. package/src/resources/extensions/gsd/commands-rate.ts +55 -0
  27. package/src/resources/extensions/gsd/commands.ts +43 -1
  28. package/src/resources/extensions/gsd/guided-flow.ts +7 -1
  29. package/src/resources/extensions/gsd/index.ts +20 -32
  30. package/src/resources/extensions/gsd/preferences.ts +14 -1
  31. package/src/resources/extensions/gsd/session-lock.ts +30 -0
  32. package/src/resources/skills/core-web-vitals/SKILL.md +1 -1
  33. package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +1 -1
  34. package/src/resources/skills/github-workflows/SKILL.md +0 -2
  35. package/src/resources/skills/web-quality-audit/SKILL.md +0 -2
  36. package/dist/resources/skills/swiftui/SKILL.md +0 -208
  37. package/dist/resources/skills/swiftui/references/animations.md +0 -921
  38. package/dist/resources/skills/swiftui/references/architecture.md +0 -1561
  39. package/dist/resources/skills/swiftui/references/layout-system.md +0 -1186
  40. package/dist/resources/skills/swiftui/references/navigation.md +0 -1492
  41. package/dist/resources/skills/swiftui/references/networking-async.md +0 -214
  42. package/dist/resources/skills/swiftui/references/performance.md +0 -1706
  43. package/dist/resources/skills/swiftui/references/platform-integration.md +0 -204
  44. package/dist/resources/skills/swiftui/references/state-management.md +0 -1443
  45. package/dist/resources/skills/swiftui/references/swiftdata.md +0 -297
  46. package/dist/resources/skills/swiftui/references/testing-debugging.md +0 -247
  47. package/dist/resources/skills/swiftui/references/uikit-appkit-interop.md +0 -218
  48. package/dist/resources/skills/swiftui/workflows/add-feature.md +0 -191
  49. package/dist/resources/skills/swiftui/workflows/build-new-app.md +0 -311
  50. package/dist/resources/skills/swiftui/workflows/debug-swiftui.md +0 -192
  51. package/dist/resources/skills/swiftui/workflows/optimize-performance.md +0 -197
  52. package/dist/resources/skills/swiftui/workflows/ship-app.md +0 -203
  53. package/dist/resources/skills/swiftui/workflows/write-tests.md +0 -235
  54. package/src/resources/skills/swiftui/SKILL.md +0 -208
  55. package/src/resources/skills/swiftui/references/animations.md +0 -921
  56. package/src/resources/skills/swiftui/references/architecture.md +0 -1561
  57. package/src/resources/skills/swiftui/references/layout-system.md +0 -1186
  58. package/src/resources/skills/swiftui/references/navigation.md +0 -1492
  59. package/src/resources/skills/swiftui/references/networking-async.md +0 -214
  60. package/src/resources/skills/swiftui/references/performance.md +0 -1706
  61. package/src/resources/skills/swiftui/references/platform-integration.md +0 -204
  62. package/src/resources/skills/swiftui/references/state-management.md +0 -1443
  63. package/src/resources/skills/swiftui/references/swiftdata.md +0 -297
  64. package/src/resources/skills/swiftui/references/testing-debugging.md +0 -247
  65. package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +0 -218
  66. package/src/resources/skills/swiftui/workflows/add-feature.md +0 -191
  67. package/src/resources/skills/swiftui/workflows/build-new-app.md +0 -311
  68. package/src/resources/skills/swiftui/workflows/debug-swiftui.md +0 -192
  69. package/src/resources/skills/swiftui/workflows/optimize-performance.md +0 -197
  70. package/src/resources/skills/swiftui/workflows/ship-app.md +0 -203
  71. package/src/resources/skills/swiftui/workflows/write-tests.md +0 -235
package/README.md CHANGED
@@ -455,7 +455,9 @@ auto_report: true
455
455
 
456
456
  ### Agent Instructions
457
457
 
458
- Create an `agent-instructions.md` file in your project root to inject persistent per-project behavioral guidance into every agent session. This file is loaded automatically and provides project-specific context the LLM should always have coding standards, architectural decisions, domain terminology, or workflow preferences.
458
+ Place an `AGENTS.md` file in any directory to provide persistent behavioral guidance for that scope. Pi core loads `AGENTS.md` automatically (with `CLAUDE.md` as a fallback) at both user and project levels. Use these files for coding standards, architectural decisions, domain terminology, or workflow preferences.
459
+
460
+ > **Note:** The legacy `agent-instructions.md` format (`~/.gsd/agent-instructions.md` and `.gsd/agent-instructions.md`) is deprecated and no longer loaded. Migrate any existing instructions to `AGENTS.md` or `CLAUDE.md`.
459
461
 
460
462
  ### Debug Mode
461
463
 
@@ -632,6 +632,11 @@ export async function autoLoop(ctx, pi, s, deps) {
632
632
  unitType,
633
633
  unitId,
634
634
  });
635
+ // Detect retry and capture previous tier for escalation
636
+ const isRetry = !!(s.currentUnit &&
637
+ s.currentUnit.type === unitType &&
638
+ s.currentUnit.id === unitId);
639
+ const previousTier = s.currentUnitRouting?.tier;
635
640
  // Closeout previous unit
636
641
  if (s.currentUnit) {
637
642
  await deps.closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, deps.buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
@@ -737,8 +742,8 @@ export async function autoLoop(ctx, pi, s, deps) {
737
742
  const msg = reorderErr instanceof Error ? reorderErr.message : String(reorderErr);
738
743
  process.stderr.write(`[gsd] prompt reorder failed (non-fatal): ${msg}\n`);
739
744
  }
740
- // Select and apply model
741
- const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel);
745
+ // Select and apply model (with tier escalation on retry)
746
+ const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel, { isRetry, previousTier });
742
747
  s.currentUnitRouting =
743
748
  modelResult.routing;
744
749
  // Start unit supervision
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { resolveModelWithFallbacksForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
7
7
  import { classifyUnitComplexity, tierLabel } from "./complexity-classifier.js";
8
- import { resolveModelForComplexity } from "./model-router.js";
8
+ import { resolveModelForComplexity, escalateTier } from "./model-router.js";
9
9
  import { getLedger, getProjectTotals } from "./metrics.js";
10
10
  import { unitPhaseLabel } from "./auto-dashboard.js";
11
11
  /**
@@ -15,7 +15,7 @@ import { unitPhaseLabel } from "./auto-dashboard.js";
15
15
  *
16
16
  * Returns routing metadata for metrics tracking.
17
17
  */
18
- export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, prefs, verbose, autoModeStartModel) {
18
+ export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, prefs, verbose, autoModeStartModel, retryContext) {
19
19
  const modelConfig = resolveModelWithFallbacksForUnit(unitType);
20
20
  let routing = null;
21
21
  if (modelConfig) {
@@ -37,8 +37,20 @@ export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, p
37
37
  const isHook = unitType.startsWith("hook/");
38
38
  const shouldClassify = !isHook || routingConfig.hooks !== false;
39
39
  if (shouldClassify) {
40
- const classification = classifyUnitComplexity(unitType, unitId, basePath, budgetPct);
40
+ let classification = classifyUnitComplexity(unitType, unitId, basePath, budgetPct);
41
41
  const availableModelIds = availableModels.map(m => m.id);
42
+ // Escalate tier on retry when escalate_on_failure is enabled (default: true)
43
+ if (retryContext?.isRetry &&
44
+ retryContext.previousTier &&
45
+ routingConfig.escalate_on_failure !== false) {
46
+ const escalated = escalateTier(retryContext.previousTier);
47
+ if (escalated) {
48
+ classification = { ...classification, tier: escalated, reason: "escalated after failure" };
49
+ if (verbose) {
50
+ ctx.ui.notify(`Tier escalation: ${retryContext.previousTier} → ${escalated} (retry after failure)`, "info");
51
+ }
52
+ }
53
+ }
42
54
  const routingResult = resolveModelForComplexity(classification, modelConfig, routingConfig, availableModelIds);
43
55
  if (routingResult.wasDowngraded) {
44
56
  effectiveModelConfig = {
@@ -0,0 +1,31 @@
1
+ /**
2
+ * /gsd rate — Submit feedback on the last unit's model tier assignment.
3
+ * Feeds into the adaptive routing history so future dispatches improve.
4
+ */
5
+ import { loadLedgerFromDisk } from "./metrics.js";
6
+ import { recordFeedback, initRoutingHistory } from "./routing-history.js";
7
+ const VALID_RATINGS = new Set(["over", "under", "ok"]);
8
+ export async function handleRate(args, ctx, basePath) {
9
+ const rating = args.trim().toLowerCase();
10
+ if (!rating || !VALID_RATINGS.has(rating)) {
11
+ ctx.ui.notify("Usage: /gsd rate <over|ok|under>\n" +
12
+ " over — model was overpowered for that task (encourage cheaper)\n" +
13
+ " ok — model was appropriate\n" +
14
+ " under — model was too weak (encourage stronger)", "info");
15
+ return;
16
+ }
17
+ const ledger = loadLedgerFromDisk(basePath);
18
+ if (!ledger || ledger.units.length === 0) {
19
+ ctx.ui.notify("No completed units found — nothing to rate.", "warning");
20
+ return;
21
+ }
22
+ const lastUnit = ledger.units[ledger.units.length - 1];
23
+ const tier = lastUnit.tier;
24
+ if (!tier) {
25
+ ctx.ui.notify("Last unit has no tier data (dynamic routing was not active). Rating skipped.", "warning");
26
+ return;
27
+ }
28
+ initRoutingHistory(basePath);
29
+ recordFeedback(lastUnit.type, lastUnit.id, tier, rating);
30
+ ctx.ui.notify(`Recorded "${rating}" for ${lastUnit.type}/${lastUnit.id} at tier ${tier}.`, "info");
31
+ }
@@ -36,6 +36,7 @@ import { computeProgressScore, formatProgressLine } from "./progress-score.js";
36
36
  import { runEnvironmentChecks } from "./doctor-environment.js";
37
37
  import { handleLogs } from "./commands-logs.js";
38
38
  import { handleStart, handleTemplates, getTemplateCompletions } from "./commands-workflow-templates.js";
39
+ import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.js";
39
40
  /** Resolve the effective project root, accounting for worktree paths. */
40
41
  export function projectRoot() {
41
42
  const cwd = process.cwd();
@@ -54,6 +55,38 @@ export function projectRoot() {
54
55
  }
55
56
  return root;
56
57
  }
58
+ /**
59
+ * Check if another process holds the auto-mode session lock.
60
+ * Returns the lock data if a remote session is alive, null otherwise.
61
+ */
62
+ function getRemoteAutoSession(basePath) {
63
+ const lockData = readSessionLockData(basePath);
64
+ if (!lockData)
65
+ return null;
66
+ if (lockData.pid === process.pid)
67
+ return null;
68
+ if (!isSessionLockProcessAlive(lockData))
69
+ return null;
70
+ return { pid: lockData.pid };
71
+ }
72
+ /**
73
+ * Show a steering menu when auto-mode is running in another process.
74
+ * Returns true if a remote session was detected (caller should return early).
75
+ */
76
+ function notifyRemoteAutoActive(ctx, basePath) {
77
+ const remote = getRemoteAutoSession(basePath);
78
+ if (!remote)
79
+ return false;
80
+ ctx.ui.notify(`Auto-mode is running in another process (PID ${remote.pid}).\n` +
81
+ `Use these commands to interact with it:\n` +
82
+ ` /gsd status — check progress\n` +
83
+ ` /gsd discuss — discuss architecture decisions\n` +
84
+ ` /gsd queue — queue the next milestone\n` +
85
+ ` /gsd steer — apply an override to active work\n` +
86
+ ` /gsd capture — fire-and-forget thought\n` +
87
+ ` /gsd stop — stop auto-mode`, "warning");
88
+ return true;
89
+ }
57
90
  export function registerGSDCommand(pi) {
58
91
  pi.registerCommand("gsd", {
59
92
  description: "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|dispatch|history|undo|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|update",
@@ -74,6 +107,7 @@ export function registerGSDCommand(pi) {
74
107
  { cmd: "triage", desc: "Manually trigger triage of pending captures" },
75
108
  { cmd: "dispatch", desc: "Dispatch a specific phase directly" },
76
109
  { cmd: "history", desc: "View execution history" },
110
+ { cmd: "rate", desc: "Rate last unit's model tier (over/ok/under) — improves adaptive routing" },
77
111
  { cmd: "undo", desc: "Revert last completed unit" },
78
112
  { cmd: "skip", desc: "Prevent a unit from auto-mode dispatch" },
79
113
  { cmd: "export", desc: "Export milestone/slice results" },
@@ -459,6 +493,8 @@ export async function handleGSDCommand(args, ctx, pi) {
459
493
  await handleDryRun(ctx, projectRoot());
460
494
  return;
461
495
  }
496
+ if (notifyRemoteAutoActive(ctx, projectRoot()))
497
+ return;
462
498
  const verboseMode = trimmed.includes("--verbose");
463
499
  const debugMode = trimmed.includes("--debug");
464
500
  if (debugMode)
@@ -513,6 +549,11 @@ export async function handleGSDCommand(args, ctx, pi) {
513
549
  await handleUndo(trimmed.replace(/^undo\s*/, "").trim(), ctx, pi, projectRoot());
514
550
  return;
515
551
  }
552
+ if (trimmed === "rate" || trimmed.startsWith("rate ")) {
553
+ const { handleRate } = await import("./commands-rate.js");
554
+ await handleRate(trimmed.replace(/^rate\s*/, "").trim(), ctx, projectRoot());
555
+ return;
556
+ }
516
557
  if (trimmed.startsWith("skip ")) {
517
558
  await handleSkip(trimmed.replace(/^skip\s*/, "").trim(), ctx, projectRoot());
518
559
  return;
@@ -809,7 +850,8 @@ Examples:
809
850
  return;
810
851
  }
811
852
  if (trimmed === "") {
812
- // Bare /gsd defaults to step mode
853
+ if (notifyRemoteAutoActive(ctx, projectRoot()))
854
+ return;
813
855
  await startAuto(ctx, pi, projectRoot(), false, { step: true });
814
856
  return;
815
857
  }
@@ -17,6 +17,7 @@ import { resolveExpectedArtifactPath } from "./auto.js";
17
17
  import { gsdRoot, milestonesDir, resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveGsdRootFile, relGsdRootFile, relMilestoneFile, relSliceFile, } from "./paths.js";
18
18
  import { join } from "node:path";
19
19
  import { readFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from "node:fs";
20
+ import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.js";
20
21
  import { nativeIsRepo, nativeInit } from "./native-git-bridge.js";
21
22
  import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
22
23
  import { loadEffectiveGSDPreferences } from "./preferences.js";
@@ -426,7 +427,12 @@ export async function showDiscuss(ctx, pi, basePath) {
426
427
  // If all pending slices are discussed, notify and exit instead of looping
427
428
  const allDiscussed = pendingSlices.every(s => discussedMap.get(s.id));
428
429
  if (allDiscussed) {
429
- ctx.ui.notify(`All ${pendingSlices.length} slices discussed. Run /gsd to start planning.`, "info");
430
+ const lockData = readSessionLockData(basePath);
431
+ const remoteAutoRunning = lockData && lockData.pid !== process.pid && isSessionLockProcessAlive(lockData);
432
+ const nextStep = remoteAutoRunning
433
+ ? "Auto-mode is already running — use /gsd status to check progress."
434
+ : "Run /gsd to start planning.";
435
+ ctx.ui.notify(`All ${pendingSlices.length} slices discussed. ${nextStep}`, "info");
430
436
  return;
431
437
  }
432
438
  // Find the first undiscussed slice to recommend
@@ -46,33 +46,21 @@ import { pauseAutoForProviderError, classifyProviderError } from "./provider-err
46
46
  import { toPosixPath } from "../shared/mod.js";
47
47
  import { isParallelActive, shutdownParallel } from "./parallel-orchestrator.js";
48
48
  import { DEFAULT_BASH_TIMEOUT_SECS } from "./constants.js";
49
- // ── Agent Instructions ────────────────────────────────────────────────────
50
- // Lightweight "always follow" files injected into every GSD agent session.
51
- // Global: ~/.gsd/agent-instructions.md Project: .gsd/agent-instructions.md
52
- // Both are loaded and concatenated (global first, project appends).
53
- function loadAgentInstructions() {
54
- const parts = [];
55
- const globalPath = join(homedir(), ".gsd", "agent-instructions.md");
56
- if (existsSync(globalPath)) {
57
- try {
58
- const content = readFileSync(globalPath, "utf-8").trim();
59
- if (content)
60
- parts.push(content);
61
- }
62
- catch { /* non-fatal — skip unreadable file */ }
63
- }
64
- const projectPath = join(process.cwd(), ".gsd", "agent-instructions.md");
65
- if (existsSync(projectPath)) {
66
- try {
67
- const content = readFileSync(projectPath, "utf-8").trim();
68
- if (content)
69
- parts.push(content);
49
+ // ── Agent Instructions (DEPRECATED) ──────────────────────────────────────
50
+ // agent-instructions.md is deprecated. Use AGENTS.md or CLAUDE.md instead.
51
+ // Pi core natively supports AGENTS.md (with CLAUDE.md fallback) per directory.
52
+ function warnDeprecatedAgentInstructions() {
53
+ const paths = [
54
+ join(homedir(), ".gsd", "agent-instructions.md"),
55
+ join(process.cwd(), ".gsd", "agent-instructions.md"),
56
+ ];
57
+ for (const p of paths) {
58
+ if (existsSync(p)) {
59
+ console.warn(`[GSD] DEPRECATED: ${p} is no longer loaded. ` +
60
+ `Migrate your instructions to AGENTS.md (or CLAUDE.md) in the same directory. ` +
61
+ `See https://github.com/gsd-build/GSD-2/issues/1492`);
70
62
  }
71
- catch { /* non-fatal — skip unreadable file */ }
72
63
  }
73
- if (parts.length === 0)
74
- return null;
75
- return parts.join("\n\n");
76
64
  }
77
65
  // ── Depth verification state ──────────────────────────────────────────────
78
66
  let depthVerificationDone = false;
@@ -589,12 +577,8 @@ export default function (pi) {
589
577
  newSkillsBlock = formatSkillsXml(newSkills);
590
578
  }
591
579
  }
592
- // Load agent instructions (global + project)
593
- let agentInstructionsBlock = "";
594
- const agentInstructions = loadAgentInstructions();
595
- if (agentInstructions) {
596
- agentInstructionsBlock = `\n\n## Agent Instructions\n\nThe following instructions were provided by the user and must be followed in every session:\n\n${agentInstructions}`;
597
- }
580
+ // Warn if deprecated agent-instructions.md files are still present
581
+ warnDeprecatedAgentInstructions();
598
582
  const injection = await buildGuidedExecuteContextInjection(event.prompt, process.cwd());
599
583
  // Worktree context — override the static CWD in the system prompt
600
584
  let worktreeBlock = "";
@@ -637,7 +621,7 @@ export default function (pi) {
637
621
  "Write every .gsd artifact in the worktree path above, never in the main project tree.",
638
622
  ].join("\n");
639
623
  }
640
- const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${agentInstructionsBlock}${knowledgeBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}`;
624
+ const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}`;
641
625
  stopContextTimer({
642
626
  systemPromptSize: fullSystem.length,
643
627
  injectionSize: injection?.length ?? 0,
@@ -15,6 +15,7 @@ import { join } from "node:path";
15
15
  import { gsdRoot } from "./paths.js";
16
16
  import { parse as parseYaml } from "yaml";
17
17
  import { normalizeStringArray } from "../shared/mod.js";
18
+ import { resolveProfileDefaults as _resolveProfileDefaults } from "./preferences-models.js";
18
19
  import { MODE_DEFAULTS, } from "./preferences-types.js";
19
20
  import { validatePreferences } from "./preferences-validation.js";
20
21
  import { formatSkillRef } from "./preferences-skills.js";
@@ -79,6 +80,17 @@ export function loadEffectiveGSDPreferences() {
79
80
  ...(mergedWarnings.length > 0 ? { warnings: mergedWarnings } : {}),
80
81
  };
81
82
  }
83
+ // Apply token-profile defaults as the lowest-priority layer so that
84
+ // `token_profile: budget` sets models and phase-skips automatically.
85
+ // Explicit user preferences always override profile defaults.
86
+ const profile = result.preferences.token_profile;
87
+ if (profile) {
88
+ const profileDefaults = _resolveProfileDefaults(profile);
89
+ result = {
90
+ ...result,
91
+ preferences: mergePreferences(profileDefaults, result.preferences),
92
+ };
93
+ }
82
94
  // Apply mode defaults as the lowest-priority layer
83
95
  if (result.preferences.mode) {
84
96
  result = {
@@ -231,6 +231,14 @@ export function acquireSessionLock(basePath) {
231
231
  stale: 1_800_000, // 30 minutes — match primary lock settings
232
232
  update: 10_000,
233
233
  onCompromised: () => {
234
+ // Same false-positive suppression as the primary lock (#1512).
235
+ // Without this, the retry path fires _lockCompromised unconditionally
236
+ // on benign mtime drift (laptop sleep, heavy LLM event loop stalls).
237
+ const elapsed = Date.now() - _lockAcquiredAt;
238
+ if (elapsed < 1_800_000) {
239
+ process.stderr.write(`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — event loop stall, continuing.\n`);
240
+ return;
241
+ }
234
242
  _lockCompromised = true;
235
243
  _releaseFunction = null;
236
244
  },
@@ -315,6 +323,25 @@ export function updateSessionLock(basePath, unitType, unitId, completedUnits, se
315
323
  export function validateSessionLock(basePath) {
316
324
  // Lock was compromised by proper-lockfile (mtime drift from sleep, stall, etc.)
317
325
  if (_lockCompromised) {
326
+ // Recovery gate (#1512): Before declaring the lock lost, check if the lock
327
+ // file still contains our PID. If it does, no other process took over — the
328
+ // onCompromised fired from benign mtime drift (laptop sleep, event loop stall
329
+ // beyond the stale window). Attempt re-acquisition instead of giving up.
330
+ const lp = lockPath(basePath);
331
+ const existing = readExistingLockData(lp);
332
+ if (existing && existing.pid === process.pid) {
333
+ // Lock file still ours — try to re-acquire the OS lock
334
+ try {
335
+ const result = acquireSessionLock(basePath);
336
+ if (result.acquired) {
337
+ process.stderr.write(`[gsd] Lock recovered after onCompromised — lock file PID matched, re-acquired.\n`);
338
+ return true;
339
+ }
340
+ }
341
+ catch {
342
+ // Re-acquisition failed — fall through to return false
343
+ }
344
+ }
318
345
  return false;
319
346
  }
320
347
  // If we have an OS-level lock, we're still the owner
@@ -438,4 +438,4 @@ startTransition(() => setExpensiveState(newValue));
438
438
  - [web.dev LCP](https://web.dev/articles/lcp)
439
439
  - [web.dev INP](https://web.dev/articles/inp)
440
440
  - [web.dev CLS](https://web.dev/articles/cls)
441
- - [Performance skill](../performance/SKILL.md)
441
+ - [Code Optimizer skill](../code-optimizer/SKILL.md)
@@ -42,7 +42,7 @@ The file must `export default function(pi: ExtensionAPI) { ... }`.
42
42
 
43
43
  ## Step 4: Check for Common Mistakes
44
44
 
45
- Read `references/key-rules-gotchas.md` and verify each rule against the extension code.
45
+ Read `../references/key-rules-gotchas.md` and verify each rule against the extension code.
46
46
 
47
47
  ## Step 5: Add Debugging
48
48
 
@@ -88,5 +88,3 @@ EVIDENCE: [output from ci_monitor.cjs]
88
88
  ## References
89
89
 
90
90
  - `references/gh/SKILL.md` — gh CLI reference
91
- - `scripts/ci_monitor.cjs` — CI monitoring tool
92
- - `scripts/ci_monitor.md` — Tool usage documentation
@@ -163,8 +163,6 @@ When performing an audit, structure findings as:
163
163
  ## References
164
164
 
165
165
  For detailed guidelines on specific areas:
166
- - [Performance Optimization](../performance/SKILL.md)
167
166
  - [Core Web Vitals](../core-web-vitals/SKILL.md)
168
167
  - [Accessibility](../accessibility/SKILL.md)
169
- - [SEO](../seo/SKILL.md)
170
168
  - [Best Practices](../best-practices/SKILL.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.35.0-dev.39eee32",
3
+ "version": "2.35.0-dev.4bbf377",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -140,15 +140,23 @@ export declare class Agent {
140
140
  * Queue a steering message to interrupt the agent mid-run.
141
141
  * Delivered after current tool execution, skips remaining tools.
142
142
  */
143
- steer(m: AgentMessage): void;
143
+ steer(m: AgentMessage, origin?: "user" | "system"): void;
144
144
  /**
145
145
  * Queue a follow-up message to be processed after the agent finishes.
146
146
  * Delivered only when agent has no more tool calls or steering messages.
147
147
  */
148
- followUp(m: AgentMessage): void;
148
+ followUp(m: AgentMessage, origin?: "user" | "system"): void;
149
149
  clearSteeringQueue(): void;
150
150
  clearFollowUpQueue(): void;
151
151
  clearAllQueues(): void;
152
+ /**
153
+ * Drain user-origin messages from queues, leaving system messages in place.
154
+ * Used during abort to preserve messages the user explicitly typed.
155
+ */
156
+ drainUserMessages(): {
157
+ steering: AgentMessage[];
158
+ followUp: AgentMessage[];
159
+ };
152
160
  hasQueuedMessages(): boolean;
153
161
  private dequeueSteeringMessages;
154
162
  private dequeueFollowUpMessages;
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
@@ -145,15 +145,15 @@ export class Agent {
145
145
  * Queue a steering message to interrupt the agent mid-run.
146
146
  * Delivered after current tool execution, skips remaining tools.
147
147
  */
148
- steer(m) {
149
- this.steeringQueue.push(m);
148
+ steer(m, origin = "system") {
149
+ this.steeringQueue.push({ message: m, origin });
150
150
  }
151
151
  /**
152
152
  * Queue a follow-up message to be processed after the agent finishes.
153
153
  * Delivered only when agent has no more tool calls or steering messages.
154
154
  */
155
- followUp(m) {
156
- this.followUpQueue.push(m);
155
+ followUp(m, origin = "system") {
156
+ this.followUpQueue.push({ message: m, origin });
157
157
  }
158
158
  clearSteeringQueue() {
159
159
  this.steeringQueue = [];
@@ -165,6 +165,17 @@ export class Agent {
165
165
  this.steeringQueue = [];
166
166
  this.followUpQueue = [];
167
167
  }
168
+ /**
169
+ * Drain user-origin messages from queues, leaving system messages in place.
170
+ * Used during abort to preserve messages the user explicitly typed.
171
+ */
172
+ drainUserMessages() {
173
+ const userSteering = this.steeringQueue.filter((e) => e.origin === "user").map((e) => e.message);
174
+ const userFollowUp = this.followUpQueue.filter((e) => e.origin === "user").map((e) => e.message);
175
+ this.steeringQueue = this.steeringQueue.filter((e) => e.origin !== "user");
176
+ this.followUpQueue = this.followUpQueue.filter((e) => e.origin !== "user");
177
+ return { steering: userSteering, followUp: userFollowUp };
178
+ }
168
179
  hasQueuedMessages() {
169
180
  return this.steeringQueue.length > 0 || this.followUpQueue.length > 0;
170
181
  }
@@ -173,11 +184,11 @@ export class Agent {
173
184
  if (this.steeringQueue.length > 0) {
174
185
  const first = this.steeringQueue[0];
175
186
  this.steeringQueue = this.steeringQueue.slice(1);
176
- return [first];
187
+ return [first.message];
177
188
  }
178
189
  return [];
179
190
  }
180
- const steering = this.steeringQueue.slice();
191
+ const steering = this.steeringQueue.map((e) => e.message);
181
192
  this.steeringQueue = [];
182
193
  return steering;
183
194
  }
@@ -186,11 +197,11 @@ export class Agent {
186
197
  if (this.followUpQueue.length > 0) {
187
198
  const first = this.followUpQueue[0];
188
199
  this.followUpQueue = this.followUpQueue.slice(1);
189
- return [first];
200
+ return [first.message];
190
201
  }
191
202
  return [];
192
203
  }
193
- const followUp = this.followUpQueue.slice();
204
+ const followUp = this.followUpQueue.map((e) => e.message);
194
205
  this.followUpQueue = [];
195
206
  return followUp;
196
207
  }