gsd-pi 2.38.0-dev.bc2e21e → 2.38.0-dev.d533afb

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 (99) hide show
  1. package/dist/resource-loader.js +34 -1
  2. package/dist/resources/extensions/github-sync/cli.js +284 -0
  3. package/dist/resources/extensions/github-sync/index.js +73 -0
  4. package/dist/resources/extensions/github-sync/mapping.js +67 -0
  5. package/dist/resources/extensions/github-sync/sync.js +424 -0
  6. package/dist/resources/extensions/github-sync/templates.js +118 -0
  7. package/dist/resources/extensions/github-sync/types.js +7 -0
  8. package/dist/resources/extensions/gsd/auto/session.js +3 -23
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
  10. package/dist/resources/extensions/gsd/auto-loop.js +292 -263
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +28 -3
  12. package/dist/resources/extensions/gsd/auto-prompts.js +23 -43
  13. package/dist/resources/extensions/gsd/auto-start.js +7 -1
  14. package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
  15. package/dist/resources/extensions/gsd/auto.js +143 -80
  16. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  17. package/dist/resources/extensions/gsd/commands.js +2 -1
  18. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  19. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  20. package/dist/resources/extensions/gsd/doctor-providers.js +27 -11
  21. package/dist/resources/extensions/gsd/doctor.js +20 -1
  22. package/dist/resources/extensions/gsd/exit-command.js +2 -1
  23. package/dist/resources/extensions/gsd/files.js +4 -0
  24. package/dist/resources/extensions/gsd/git-service.js +15 -12
  25. package/dist/resources/extensions/gsd/guided-flow.js +82 -32
  26. package/dist/resources/extensions/gsd/index.js +22 -19
  27. package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
  28. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  29. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  30. package/dist/resources/extensions/gsd/preferences-validation.js +58 -10
  31. package/dist/resources/extensions/gsd/preferences.js +4 -2
  32. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -0
  33. package/dist/resources/extensions/gsd/repo-identity.js +19 -3
  34. package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
  35. package/dist/resources/extensions/mcp-client/index.js +14 -1
  36. package/package.json +1 -1
  37. package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
  38. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
  39. package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
  40. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  41. package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
  42. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  43. package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
  44. package/src/resources/extensions/github-sync/cli.ts +364 -0
  45. package/src/resources/extensions/github-sync/index.ts +93 -0
  46. package/src/resources/extensions/github-sync/mapping.ts +81 -0
  47. package/src/resources/extensions/github-sync/sync.ts +556 -0
  48. package/src/resources/extensions/github-sync/templates.ts +183 -0
  49. package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
  50. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
  51. package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
  52. package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
  53. package/src/resources/extensions/github-sync/types.ts +47 -0
  54. package/src/resources/extensions/gsd/auto/session.ts +3 -25
  55. package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
  56. package/src/resources/extensions/gsd/auto-loop.ts +382 -360
  57. package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
  58. package/src/resources/extensions/gsd/auto-prompts.ts +25 -45
  59. package/src/resources/extensions/gsd/auto-start.ts +11 -1
  60. package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
  61. package/src/resources/extensions/gsd/auto.ts +139 -86
  62. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  63. package/src/resources/extensions/gsd/commands.ts +2 -2
  64. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  65. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  66. package/src/resources/extensions/gsd/doctor-providers.ts +26 -9
  67. package/src/resources/extensions/gsd/doctor.ts +22 -1
  68. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  69. package/src/resources/extensions/gsd/files.ts +3 -1
  70. package/src/resources/extensions/gsd/git-service.ts +20 -10
  71. package/src/resources/extensions/gsd/guided-flow.ts +110 -38
  72. package/src/resources/extensions/gsd/index.ts +21 -16
  73. package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
  74. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  75. package/src/resources/extensions/gsd/preferences-types.ts +4 -4
  76. package/src/resources/extensions/gsd/preferences-validation.ts +50 -10
  77. package/src/resources/extensions/gsd/preferences.ts +3 -2
  78. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -0
  79. package/src/resources/extensions/gsd/repo-identity.ts +20 -3
  80. package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
  81. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  82. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +122 -68
  83. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
  84. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  85. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  86. package/src/resources/extensions/gsd/types.ts +0 -1
  87. package/src/resources/extensions/mcp-client/index.ts +17 -1
  88. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  89. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  90. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  91. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  92. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  93. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  94. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  95. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  96. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  97. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  98. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  99. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -16,11 +16,11 @@ import type {
16
16
  InlineLevel,
17
17
  PhaseSkipPreferences,
18
18
  ParallelConfig,
19
- CompressionStrategy,
20
19
  ContextSelectionMode,
21
20
  ReactiveExecutionConfig,
22
21
  } from "./types.js";
23
22
  import type { DynamicRoutingConfig } from "./model-router.js";
23
+ import type { GitHubSyncConfig } from "../github-sync/types.js";
24
24
 
25
25
  // ─── Workflow Modes ──────────────────────────────────────────────────────────
26
26
 
@@ -84,10 +84,10 @@ export const KNOWN_PREFERENCE_KEYS = new Set<string>([
84
84
  "verification_auto_fix",
85
85
  "verification_max_retries",
86
86
  "search_provider",
87
- "compression_strategy",
88
87
  "context_selection",
89
88
  "widget_mode",
90
89
  "reactive_execution",
90
+ "github",
91
91
  ]);
92
92
 
93
93
  /** Canonical list of all dispatch unit types. */
@@ -211,14 +211,14 @@ export interface GSDPreferences {
211
211
  verification_max_retries?: number;
212
212
  /** Search provider preference. "brave"/"tavily"/"ollama" force that backend and disable native Anthropic search. "native" forces native only. "auto" = current default behavior. */
213
213
  search_provider?: "brave" | "tavily" | "ollama" | "native" | "auto";
214
- /** Compression strategy for context that exceeds budget. "truncate" (default) drops sections, "compress" applies heuristic compression first. */
215
- compression_strategy?: CompressionStrategy;
216
214
  /** Context selection mode for file inlining. "full" inlines entire files, "smart" uses semantic chunking. Default derived from token profile. */
217
215
  context_selection?: ContextSelectionMode;
218
216
  /** Default widget display mode for auto-mode dashboard. "full" | "small" | "min" | "off". Default: "full". */
219
217
  widget_mode?: "full" | "small" | "min" | "off";
220
218
  /** Reactive (graph-derived parallel) task execution within slices. Disabled by default. */
221
219
  reactive_execution?: ReactiveExecutionConfig;
220
+ /** GitHub sync configuration. Opt-in: syncs GSD events to GitHub Issues, Milestones, and PRs. */
221
+ github?: GitHubSyncConfig;
222
222
  }
223
223
 
224
224
  export interface LoadedGSDPreferences {
@@ -686,16 +686,6 @@ export function validatePreferences(preferences: GSDPreferences): {
686
686
  }
687
687
  }
688
688
 
689
- // ─── Compression Strategy ───────────────────────────────────────────
690
- if (preferences.compression_strategy !== undefined) {
691
- const validStrategies = new Set(["truncate", "compress"]);
692
- if (typeof preferences.compression_strategy === "string" && validStrategies.has(preferences.compression_strategy)) {
693
- validated.compression_strategy = preferences.compression_strategy as GSDPreferences["compression_strategy"];
694
- } else {
695
- errors.push(`compression_strategy must be one of: truncate, compress`);
696
- }
697
- }
698
-
699
689
  // ─── Context Selection ──────────────────────────────────────────────
700
690
  if (preferences.context_selection !== undefined) {
701
691
  const validModes = new Set(["full", "smart"]);
@@ -706,5 +696,55 @@ export function validatePreferences(preferences: GSDPreferences): {
706
696
  }
707
697
  }
708
698
 
699
+ // ─── GitHub Sync ────────────────────────────────────────────────────────
700
+ if (preferences.github !== undefined) {
701
+ if (typeof preferences.github === "object" && preferences.github !== null) {
702
+ const gh = preferences.github as unknown as Record<string, unknown>;
703
+ const validGh: Record<string, unknown> = {};
704
+
705
+ if (gh.enabled !== undefined) {
706
+ if (typeof gh.enabled === "boolean") validGh.enabled = gh.enabled;
707
+ else errors.push("github.enabled must be a boolean");
708
+ }
709
+ if (gh.repo !== undefined) {
710
+ if (typeof gh.repo === "string" && gh.repo.includes("/")) validGh.repo = gh.repo;
711
+ else errors.push('github.repo must be a string in "owner/repo" format');
712
+ }
713
+ if (gh.project !== undefined) {
714
+ const p = typeof gh.project === "number" ? gh.project : Number(gh.project);
715
+ if (Number.isFinite(p) && p > 0) validGh.project = Math.floor(p);
716
+ else errors.push("github.project must be a positive number");
717
+ }
718
+ if (gh.labels !== undefined) {
719
+ if (Array.isArray(gh.labels) && gh.labels.every((l: unknown) => typeof l === "string")) {
720
+ validGh.labels = gh.labels;
721
+ } else {
722
+ errors.push("github.labels must be an array of strings");
723
+ }
724
+ }
725
+ if (gh.auto_link_commits !== undefined) {
726
+ if (typeof gh.auto_link_commits === "boolean") validGh.auto_link_commits = gh.auto_link_commits;
727
+ else errors.push("github.auto_link_commits must be a boolean");
728
+ }
729
+ if (gh.slice_prs !== undefined) {
730
+ if (typeof gh.slice_prs === "boolean") validGh.slice_prs = gh.slice_prs;
731
+ else errors.push("github.slice_prs must be a boolean");
732
+ }
733
+
734
+ const knownGhKeys = new Set(["enabled", "repo", "project", "labels", "auto_link_commits", "slice_prs"]);
735
+ for (const key of Object.keys(gh)) {
736
+ if (!knownGhKeys.has(key)) {
737
+ warnings.push(`unknown github key "${key}" — ignored`);
738
+ }
739
+ }
740
+
741
+ if (Object.keys(validGh).length > 0) {
742
+ validated.github = validGh as unknown as import("../github-sync/types.js").GitHubSyncConfig;
743
+ }
744
+ } else {
745
+ errors.push("github must be an object");
746
+ }
747
+ }
748
+
709
749
  return { preferences: validated, errors, warnings };
710
750
  }
@@ -77,7 +77,6 @@ export {
77
77
  resolveProfileDefaults,
78
78
  resolveEffectiveProfile,
79
79
  resolveInlineLevel,
80
- resolveCompressionStrategy,
81
80
  resolveContextSelection,
82
81
  resolveSearchProviderFromPreferences,
83
82
  } from "./preferences-models.js";
@@ -269,10 +268,12 @@ function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPr
269
268
  verification_auto_fix: override.verification_auto_fix ?? base.verification_auto_fix,
270
269
  verification_max_retries: override.verification_max_retries ?? base.verification_max_retries,
271
270
  search_provider: override.search_provider ?? base.search_provider,
272
- compression_strategy: override.compression_strategy ?? base.compression_strategy,
273
271
  context_selection: override.context_selection ?? base.context_selection,
274
272
  auto_visualize: override.auto_visualize ?? base.auto_visualize,
275
273
  auto_report: override.auto_report ?? base.auto_report,
274
+ github: (base.github || override.github)
275
+ ? { ...(base.github ?? {}), ...(override.github ?? {}) } as import("../github-sync/types.js").GitHubSyncConfig
276
+ : undefined,
276
277
  };
277
278
  }
278
279
 
@@ -25,6 +25,8 @@ You are the UAT runner. Execute every check defined in `{{uatPath}}` as deeply a
25
25
  ### Automation rules by mode
26
26
 
27
27
  - `artifact-driven` — verify with shell commands, scripts, file reads, and artifact structure checks.
28
+ - `browser-executable` — use browser tools to navigate to the target URL and verify expected behavior. Capture screenshots as evidence. Record pass/fail with specific assertions.
29
+ - `runtime-executable` — execute the specified command or script. Capture stdout/stderr as evidence. Record pass/fail based on exit code and output.
28
30
  - `live-runtime` — exercise the real runtime path. Start or connect to the app/service if needed, use browser/runtime/network checks, and verify observable behavior.
29
31
  - `mixed` — run all automatable artifact-driven and live-runtime checks. Separate any remaining human-only checks explicitly.
30
32
  - `human-experience` — automate setup, preconditions, screenshots, logs, and objective checks, but do **not** invent subjective PASS results. Mark taste-based, experiential, or purely human-judgment checks as `NEEDS-HUMAN` and use an overall verdict of `PARTIAL` unless every required check was objective and passed.
@@ -92,14 +92,31 @@ function resolveGitRoot(basePath: string): string {
92
92
  }
93
93
  }
94
94
 
95
+ /**
96
+ * Validate a GSD_PROJECT_ID value.
97
+ *
98
+ * Must contain only alphanumeric characters, hyphens, and underscores.
99
+ * Call this once at startup so the user gets immediate feedback on bad values.
100
+ */
101
+ export function validateProjectId(id: string): boolean {
102
+ return /^[a-zA-Z0-9_-]+$/.test(id);
103
+ }
104
+
95
105
  /**
96
106
  * Compute a stable identity for a repository.
97
107
  *
98
- * SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated to 12 hex chars.
99
- * Deterministic: same repo always produces the same hash regardless of
100
- * which worktree the caller is inside.
108
+ * If `GSD_PROJECT_ID` is set, returns it directly (validation is expected
109
+ * to have already happened at startup via `validateProjectId`).
110
+ *
111
+ * Otherwise returns SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated
112
+ * to 12 hex chars. Deterministic: same repo always produces the same hash
113
+ * regardless of which worktree the caller is inside.
101
114
  */
102
115
  export function repoIdentity(basePath: string): string {
116
+ const projectId = process.env.GSD_PROJECT_ID;
117
+ if (projectId) {
118
+ return projectId;
119
+ }
103
120
  const remoteUrl = getRemoteUrl(basePath);
104
121
  const root = resolveGitRoot(basePath);
105
122
  const input = `${remoteUrl}\n${root}`;
@@ -39,6 +39,35 @@ export function markSliceDoneInRoadmap(basePath: string, mid: string, sid: strin
39
39
  return true;
40
40
  }
41
41
 
42
+ /**
43
+ * Mark a slice as not done ([ ]) in the milestone roadmap.
44
+ * Idempotent — no-op if already unchecked or if the slice isn't found.
45
+ *
46
+ * @returns true if the roadmap was modified, false if no change was needed
47
+ */
48
+ export function markSliceUndoneInRoadmap(basePath: string, mid: string, sid: string): boolean {
49
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
50
+ if (!roadmapFile) return false;
51
+
52
+ let content: string;
53
+ try {
54
+ content = readFileSync(roadmapFile, "utf-8");
55
+ } catch {
56
+ return false;
57
+ }
58
+
59
+ const updated = content.replace(
60
+ new RegExp(`^(\\s*-\\s+)\\[x\\]\\s+\\*\\*${sid}:`, "m"),
61
+ `$1[ ] **${sid}:`,
62
+ );
63
+
64
+ if (updated === content) return false;
65
+
66
+ atomicWriteSync(roadmapFile, updated);
67
+ clearParseCache();
68
+ return true;
69
+ }
70
+
42
71
  /**
43
72
  * Mark a task as done ([x]) in the slice plan.
44
73
  * Idempotent — no-op if already checked or if the task isn't found.
@@ -1,9 +1,9 @@
1
1
  /**
2
- * agent-end-retry.test.ts — Regression checks for the post-#1419 agent_end model.
2
+ * agent-end-retry.test.ts — Regression checks for the agent_end model.
3
3
  *
4
- * The old recursive handleAgentEnd retry path is gone. The loop now keeps
5
- * pendingResolve + pendingAgentEndQueue on AutoSession, and handleAgentEnd is
6
- * only a thin compatibility wrapper around resolveAgentEnd().
4
+ * The per-unit one-shot resolve function lives at module level in auto-loop.ts
5
+ * (_currentResolve). handleAgentEnd is a thin compatibility wrapper around
6
+ * resolveAgentEnd().
7
7
  */
8
8
 
9
9
  import test from "node:test";
@@ -14,40 +14,43 @@ import { fileURLToPath } from "node:url";
14
14
 
15
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
16
16
  const AUTO_TS_PATH = join(__dirname, "..", "auto.ts");
17
+ const AUTO_LOOP_TS_PATH = join(__dirname, "..", "auto-loop.ts");
17
18
  const SESSION_TS_PATH = join(__dirname, "..", "auto", "session.ts");
18
19
 
19
20
  function getAutoTsSource(): string {
20
21
  return readFileSync(AUTO_TS_PATH, "utf-8");
21
22
  }
22
23
 
24
+ function getAutoLoopTsSource(): string {
25
+ return readFileSync(AUTO_LOOP_TS_PATH, "utf-8");
26
+ }
27
+
23
28
  function getSessionTsSource(): string {
24
29
  return readFileSync(SESSION_TS_PATH, "utf-8");
25
30
  }
26
31
 
27
- test("AutoSession declares pending agent_end queue state", () => {
28
- const source = getSessionTsSource();
32
+ test("auto-loop.ts declares _currentResolve for per-unit one-shot promises", () => {
33
+ const source = getAutoLoopTsSource();
29
34
  assert.ok(
30
- source.includes("pendingResolve"),
31
- "AutoSession must declare pendingResolve for the in-flight unit promise",
35
+ source.includes("_currentResolve"),
36
+ "auto-loop.ts must declare _currentResolve for the per-unit resolve function",
32
37
  );
33
38
  assert.ok(
34
- source.includes("pendingAgentEndQueue"),
35
- "AutoSession must declare pendingAgentEndQueue for between-iteration agent_end events",
39
+ source.includes("_sessionSwitchInFlight"),
40
+ "auto-loop.ts must declare _sessionSwitchInFlight guard",
36
41
  );
37
42
  });
38
43
 
39
- test("AutoSession reset clears pending agent_end queue state", () => {
44
+ test("AutoSession no longer holds promise state (moved to auto-loop.ts module scope)", () => {
40
45
  const source = getSessionTsSource();
41
- const resetIdx = source.indexOf("reset(): void");
42
- assert.ok(resetIdx > -1, "AutoSession must have a reset() method");
43
- const resetBlock = source.slice(resetIdx, resetIdx + 4000);
46
+ // Properties should NOT exist as class fields
44
47
  assert.ok(
45
- resetBlock.includes("this.pendingResolve = null"),
46
- "reset() must clear pendingResolve",
48
+ !source.includes("pendingResolve:"),
49
+ "AutoSession must not declare pendingResolve (moved to auto-loop.ts)",
47
50
  );
48
51
  assert.ok(
49
- resetBlock.includes("this.pendingAgentEndQueue = []"),
50
- "reset() must clear pendingAgentEndQueue",
52
+ !source.includes("pendingAgentEndQueue:"),
53
+ "AutoSession must not declare pendingAgentEndQueue (removed — events are dropped)",
51
54
  );
52
55
  });
53
56