gsd-pi 2.75.0-dev.2203010a0 → 2.75.0-dev.a44b82572
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/onboarding.d.ts +5 -1
- package/dist/onboarding.js +5 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +23 -19
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +128 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +17 -4
- package/dist/resources/extensions/gsd/commands/handlers/onboarding.js +52 -68
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands-memory.js +462 -0
- package/dist/resources/extensions/gsd/gsd-db.js +237 -4
- package/dist/resources/extensions/gsd/memory-embeddings.js +219 -0
- package/dist/resources/extensions/gsd/memory-extractor.js +78 -27
- package/dist/resources/extensions/gsd/memory-ingest.js +218 -0
- package/dist/resources/extensions/gsd/memory-relations.js +189 -0
- package/dist/resources/extensions/gsd/memory-source-store.js +113 -0
- package/dist/resources/extensions/gsd/memory-store.js +299 -6
- package/dist/resources/extensions/gsd/model-router.js +9 -5
- package/dist/resources/extensions/gsd/notification-overlay.js +7 -22
- package/dist/resources/extensions/gsd/tools/memory-tools.js +306 -0
- package/dist/resources/extensions/gsd/tools/skip-slice.js +78 -0
- 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 +17 -17
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/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 +17 -17
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +12 -10
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +8 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +113 -14
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/mcp-server.test.ts +40 -4
- package/packages/mcp-server/src/server.ts +12 -10
- package/packages/mcp-server/src/session-manager.ts +10 -3
- package/packages/mcp-server/src/workflow-tools.test.ts +91 -1
- package/packages/mcp-server/src/workflow-tools.ts +128 -18
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/cli/args.d.ts +6 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +14 -4
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/cli/args.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/cli/args.test.js +38 -0
- package/packages/pi-coding-agent/dist/cli/args.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +7 -1
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +3 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/src/cli/args.test.ts +44 -0
- package/packages/pi-coding-agent/src/cli/args.ts +21 -6
- package/packages/pi-coding-agent/src/core/sdk.ts +17 -1
- package/packages/pi-coding-agent/src/main.ts +4 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +24 -20
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +158 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +20 -4
- package/src/resources/extensions/gsd/commands/handlers/onboarding.ts +65 -98
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands-memory.ts +551 -0
- package/src/resources/extensions/gsd/gsd-db.ts +273 -4
- package/src/resources/extensions/gsd/memory-embeddings.ts +235 -0
- package/src/resources/extensions/gsd/memory-extractor.ts +100 -34
- package/src/resources/extensions/gsd/memory-ingest.ts +286 -0
- package/src/resources/extensions/gsd/memory-relations.ts +240 -0
- package/src/resources/extensions/gsd/memory-source-store.ts +138 -0
- package/src/resources/extensions/gsd/memory-store.ts +351 -7
- package/src/resources/extensions/gsd/model-router.ts +10 -5
- package/src/resources/extensions/gsd/notification-overlay.ts +9 -19
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-embeddings.test.ts +213 -0
- package/src/resources/extensions/gsd/tests/memory-ingest.test.ts +153 -0
- package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/memory-relations.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/memory-tools.test.ts +295 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +56 -37
- package/src/resources/extensions/gsd/tests/skip-slice-cascades-tasks.test.ts +125 -0
- package/src/resources/extensions/gsd/tools/memory-tools.ts +380 -0
- package/src/resources/extensions/gsd/tools/skip-slice.ts +133 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
- /package/dist/web/standalone/.next/static/{8FZqxNe9FxQDmsbRzR8tA → iBwPQUj73sn8jxegTo320}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{8FZqxNe9FxQDmsbRzR8tA → iBwPQUj73sn8jxegTo320}/_ssgManifest.js +0 -0
|
@@ -205,7 +205,7 @@ export async function selectAndApplyModel(
|
|
|
205
205
|
budgetPct,
|
|
206
206
|
taskMetadataForPolicy,
|
|
207
207
|
);
|
|
208
|
-
const availableModelIds = routingEligibleModels.map(m => m.id);
|
|
208
|
+
const availableModelIds = routingEligibleModels.map(m => `${m.provider}/${m.id}`);
|
|
209
209
|
|
|
210
210
|
// Escalate tier on retry when escalate_on_failure is enabled (default: true)
|
|
211
211
|
if (
|
|
@@ -776,32 +776,26 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
776
776
|
};
|
|
777
777
|
}
|
|
778
778
|
try {
|
|
779
|
-
const {
|
|
779
|
+
const { handleSkipSlice } = await import("../tools/skip-slice.js");
|
|
780
780
|
const { invalidateStateCache } = await import("../state.js");
|
|
781
781
|
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
if (slice.status === "complete" || slice.status === "done") {
|
|
791
|
-
return {
|
|
792
|
-
content: [{ type: "text" as const, text: `Error: Slice ${params.sliceId} is already complete — cannot skip.` }],
|
|
793
|
-
details: { operation: "skip_slice", error: "already_complete" } as any,
|
|
794
|
-
};
|
|
795
|
-
}
|
|
782
|
+
const result = handleSkipSlice({
|
|
783
|
+
milestoneId: params.milestoneId,
|
|
784
|
+
sliceId: params.sliceId,
|
|
785
|
+
reason: params.reason,
|
|
786
|
+
});
|
|
796
787
|
|
|
797
|
-
if (
|
|
788
|
+
if (result.error) {
|
|
798
789
|
return {
|
|
799
|
-
content: [{ type: "text" as const, text: `
|
|
800
|
-
details: {
|
|
790
|
+
content: [{ type: "text" as const, text: `Error: ${result.error}` }],
|
|
791
|
+
details: {
|
|
792
|
+
operation: "skip_slice",
|
|
793
|
+
error: result.error,
|
|
794
|
+
errorCode: result.errorCode ?? "skip_failed",
|
|
795
|
+
} as any,
|
|
801
796
|
};
|
|
802
797
|
}
|
|
803
798
|
|
|
804
|
-
updateSliceStatus(params.milestoneId, params.sliceId, "skipped");
|
|
805
799
|
invalidateStateCache();
|
|
806
800
|
|
|
807
801
|
// Rebuild STATE.md so it reflects the skip immediately (#3477).
|
|
@@ -814,13 +808,21 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
814
808
|
logError("tool", `skip_slice rebuildState failed: ${(err as Error).message}`, { tool: "gsd_skip_slice" });
|
|
815
809
|
}
|
|
816
810
|
|
|
811
|
+
const suffix = result.wasAlreadySkipped
|
|
812
|
+
? result.tasksSkipped > 0
|
|
813
|
+
? ` (already skipped; cascaded ${result.tasksSkipped} leftover task(s) to skipped).`
|
|
814
|
+
: " (already skipped; no pending tasks to cascade)."
|
|
815
|
+
: ` Cascaded ${result.tasksSkipped} task(s) to skipped. Auto-mode will advance past this slice.`;
|
|
816
|
+
|
|
817
817
|
return {
|
|
818
|
-
content: [{ type: "text" as const, text: `Skipped slice ${params.sliceId} (${params.milestoneId}). Reason: ${params.reason ?? "User-directed skip"}
|
|
818
|
+
content: [{ type: "text" as const, text: `Skipped slice ${params.sliceId} (${params.milestoneId}). Reason: ${params.reason ?? "User-directed skip"}.${suffix}` }],
|
|
819
819
|
details: {
|
|
820
820
|
operation: "skip_slice",
|
|
821
821
|
sliceId: params.sliceId,
|
|
822
822
|
milestoneId: params.milestoneId,
|
|
823
823
|
reason: params.reason,
|
|
824
|
+
tasksSkipped: result.tasksSkipped,
|
|
825
|
+
wasAlreadySkipped: result.wasAlreadySkipped,
|
|
824
826
|
} as any,
|
|
825
827
|
};
|
|
826
828
|
} catch (err) {
|
|
@@ -838,12 +840,14 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
838
840
|
label: "Skip Slice",
|
|
839
841
|
description:
|
|
840
842
|
"Mark a slice as skipped so auto-mode advances past it without executing. " +
|
|
843
|
+
"Non-closed tasks within the slice are cascaded to skipped so milestone completion is not blocked by leftover pending tasks (#4375). " +
|
|
841
844
|
"The slice data is preserved for reference. The state machine treats skipped slices like completed ones for dependency satisfaction.",
|
|
842
845
|
promptSnippet: "Skip a GSD slice (mark as skipped, auto-mode will advance past it)",
|
|
843
846
|
promptGuidelines: [
|
|
844
847
|
"Use gsd_skip_slice when a slice should be bypassed — descoped, superseded, or no longer relevant.",
|
|
845
848
|
"Cannot skip a slice that is already complete.",
|
|
846
849
|
"Skipped slices satisfy downstream dependencies just like completed slices.",
|
|
850
|
+
"All pending/active tasks in the slice are cascaded to skipped; completed tasks are never downgraded.",
|
|
847
851
|
],
|
|
848
852
|
parameters: Type.Object({
|
|
849
853
|
sliceId: Type.String({ description: "Slice ID (e.g. S02)" }),
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// GSD2 — Memory tool registration
|
|
2
|
+
//
|
|
3
|
+
// Exposes the memory-layer tools (capture_thought, memory_query, gsd_graph)
|
|
4
|
+
// to the LLM over MCP. All three degrade gracefully when the GSD database
|
|
5
|
+
// is unavailable.
|
|
6
|
+
|
|
7
|
+
import { Type } from "@sinclair/typebox";
|
|
8
|
+
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
9
|
+
|
|
10
|
+
import { ensureDbOpen } from "./dynamic-tools.js";
|
|
11
|
+
import {
|
|
12
|
+
executeGsdGraph,
|
|
13
|
+
executeMemoryCapture,
|
|
14
|
+
executeMemoryQuery,
|
|
15
|
+
} from "../tools/memory-tools.js";
|
|
16
|
+
|
|
17
|
+
export function registerMemoryTools(pi: ExtensionAPI): void {
|
|
18
|
+
// ─── capture_thought ────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
pi.registerTool({
|
|
21
|
+
name: "capture_thought",
|
|
22
|
+
label: "Capture Thought",
|
|
23
|
+
description:
|
|
24
|
+
"Record a durable piece of project knowledge (decision, convention, gotcha, pattern, " +
|
|
25
|
+
"preference, or environment detail) into the GSD memory store. Use sparingly — one memory " +
|
|
26
|
+
"per genuinely reusable insight, not per task.",
|
|
27
|
+
promptSnippet:
|
|
28
|
+
"Capture a durable project insight into the GSD memory store (categories: architecture, convention, gotcha, pattern, preference, environment)",
|
|
29
|
+
promptGuidelines: [
|
|
30
|
+
"Use capture_thought for insights that will remain useful across future sessions.",
|
|
31
|
+
"Do NOT capture one-off bug fixes, temporary state, secrets, or task-specific details.",
|
|
32
|
+
"Keep content to 1–3 sentences.",
|
|
33
|
+
"Set confidence: 0.6 tentative, 0.8 solid, 0.95 well-confirmed (default 0.8).",
|
|
34
|
+
],
|
|
35
|
+
parameters: Type.Object({
|
|
36
|
+
category: Type.Union(
|
|
37
|
+
[
|
|
38
|
+
Type.Literal("architecture"),
|
|
39
|
+
Type.Literal("convention"),
|
|
40
|
+
Type.Literal("gotcha"),
|
|
41
|
+
Type.Literal("preference"),
|
|
42
|
+
Type.Literal("environment"),
|
|
43
|
+
Type.Literal("pattern"),
|
|
44
|
+
],
|
|
45
|
+
{ description: "Memory category" },
|
|
46
|
+
),
|
|
47
|
+
content: Type.String({ description: "The memory text (1–3 sentences, no secrets)" }),
|
|
48
|
+
confidence: Type.Optional(
|
|
49
|
+
Type.Number({ description: "0.1–0.99, default 0.8", minimum: 0.1, maximum: 0.99 }),
|
|
50
|
+
),
|
|
51
|
+
tags: Type.Optional(Type.Array(Type.String(), { description: "Free-form tags (reserved for future use)" })),
|
|
52
|
+
scope: Type.Optional(Type.String({ description: "Scope name (reserved for future use; defaults to project)" })),
|
|
53
|
+
}),
|
|
54
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
55
|
+
const ok = await ensureDbOpen();
|
|
56
|
+
if (!ok) {
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot capture memory." }],
|
|
59
|
+
details: { operation: "memory_capture", error: "db_unavailable" },
|
|
60
|
+
isError: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return executeMemoryCapture(params as Parameters<typeof executeMemoryCapture>[0]);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ─── memory_query ───────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
pi.registerTool({
|
|
70
|
+
name: "memory_query",
|
|
71
|
+
label: "Query Memory",
|
|
72
|
+
description:
|
|
73
|
+
"Search the GSD memory store for relevant memories. Phase 1 uses keyword matching ranked " +
|
|
74
|
+
"by confidence and reinforcement; future phases add semantic (embedding) retrieval.",
|
|
75
|
+
promptSnippet:
|
|
76
|
+
"Search the GSD memory store by keyword; returns ranked memories with id, category, and content",
|
|
77
|
+
promptGuidelines: [
|
|
78
|
+
"Use memory_query when you need durable project context that may not be in the current prompt.",
|
|
79
|
+
"Provide a short keyword-style query — not a full question.",
|
|
80
|
+
"Use category to narrow results to gotchas, conventions, architecture notes, etc.",
|
|
81
|
+
],
|
|
82
|
+
parameters: Type.Object({
|
|
83
|
+
query: Type.String({ description: "Keyword query (2+ char terms)" }),
|
|
84
|
+
k: Type.Optional(Type.Number({ description: "Max results (default 10, max 50)", minimum: 1, maximum: 50 })),
|
|
85
|
+
category: Type.Optional(
|
|
86
|
+
Type.Union(
|
|
87
|
+
[
|
|
88
|
+
Type.Literal("architecture"),
|
|
89
|
+
Type.Literal("convention"),
|
|
90
|
+
Type.Literal("gotcha"),
|
|
91
|
+
Type.Literal("preference"),
|
|
92
|
+
Type.Literal("environment"),
|
|
93
|
+
Type.Literal("pattern"),
|
|
94
|
+
],
|
|
95
|
+
{ description: "Restrict results to a single category" },
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
scope: Type.Optional(Type.String({ description: "Only include memories with this scope (e.g. 'project', 'global')" })),
|
|
99
|
+
tag: Type.Optional(Type.String({ description: "Only include memories tagged with this value" })),
|
|
100
|
+
include_superseded: Type.Optional(Type.Boolean({ description: "Include superseded memories (default false)" })),
|
|
101
|
+
reinforce_hits: Type.Optional(
|
|
102
|
+
Type.Boolean({ description: "Increment hit_count on returned memories (default false)" }),
|
|
103
|
+
),
|
|
104
|
+
}),
|
|
105
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
106
|
+
const ok = await ensureDbOpen();
|
|
107
|
+
if (!ok) {
|
|
108
|
+
return {
|
|
109
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot query memory." }],
|
|
110
|
+
details: { operation: "memory_query", error: "db_unavailable" },
|
|
111
|
+
isError: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return executeMemoryQuery(params as Parameters<typeof executeMemoryQuery>[0]);
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ─── gsd_graph ──────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
pi.registerTool({
|
|
121
|
+
name: "gsd_graph",
|
|
122
|
+
label: "GSD Knowledge Graph",
|
|
123
|
+
description:
|
|
124
|
+
"Inspect the relationship graph between memories. mode=query walks supersedes edges from a " +
|
|
125
|
+
"given memoryId; mode=build is a placeholder that future phases will use to rebuild graph " +
|
|
126
|
+
"edges from milestone LEARNINGS artifacts.",
|
|
127
|
+
promptSnippet: "Query the memory relationship graph or trigger a rebuild",
|
|
128
|
+
promptGuidelines: [
|
|
129
|
+
"Use mode=query with a memoryId when you want to see how a memory relates to others.",
|
|
130
|
+
"Phase 1 only exposes supersedes edges; additional relation types arrive in later phases.",
|
|
131
|
+
],
|
|
132
|
+
parameters: Type.Object({
|
|
133
|
+
mode: Type.Union([Type.Literal("build"), Type.Literal("query")], {
|
|
134
|
+
description: "build = recompute graph (placeholder), query = inspect edges",
|
|
135
|
+
}),
|
|
136
|
+
memoryId: Type.Optional(Type.String({ description: "Memory ID (required when mode=query)" })),
|
|
137
|
+
depth: Type.Optional(Type.Number({ description: "Hops to traverse (0–5, default 1)", minimum: 0, maximum: 5 })),
|
|
138
|
+
rel: Type.Optional(Type.Union([
|
|
139
|
+
Type.Literal("related_to"),
|
|
140
|
+
Type.Literal("depends_on"),
|
|
141
|
+
Type.Literal("contradicts"),
|
|
142
|
+
Type.Literal("elaborates"),
|
|
143
|
+
Type.Literal("supersedes"),
|
|
144
|
+
], { description: "Only include edges with this relation type" })),
|
|
145
|
+
}),
|
|
146
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
147
|
+
const ok = await ensureDbOpen();
|
|
148
|
+
if (!ok) {
|
|
149
|
+
return {
|
|
150
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available." }],
|
|
151
|
+
details: { operation: "gsd_graph", error: "db_unavailable" },
|
|
152
|
+
isError: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return executeGsdGraph(params as Parameters<typeof executeGsdGraph>[0]);
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
@@ -9,6 +9,7 @@ import { loadEcosystemExtensions } from "../ecosystem/loader.js";
|
|
|
9
9
|
import { registerDbTools } from "./db-tools.js";
|
|
10
10
|
import { registerDynamicTools } from "./dynamic-tools.js";
|
|
11
11
|
import { registerJournalTools } from "./journal-tools.js";
|
|
12
|
+
import { registerMemoryTools } from "./memory-tools.js";
|
|
12
13
|
import { registerQueryTools } from "./query-tools.js";
|
|
13
14
|
import { registerHooks } from "./register-hooks.js";
|
|
14
15
|
import { registerShortcuts } from "./register-shortcuts.js";
|
|
@@ -85,6 +86,7 @@ export function registerGsdExtension(pi: ExtensionAPI): void {
|
|
|
85
86
|
["db-tools", () => registerDbTools(pi)],
|
|
86
87
|
["journal-tools", () => registerJournalTools(pi)],
|
|
87
88
|
["query-tools", () => registerQueryTools(pi)],
|
|
89
|
+
["memory-tools", () => registerMemoryTools(pi)],
|
|
88
90
|
["shortcuts", () => registerShortcuts(pi)],
|
|
89
91
|
["hooks", () => registerHooks(pi, ecosystemHandlers)],
|
|
90
92
|
["ecosystem", () => {
|
|
@@ -113,10 +113,26 @@ export async function buildBeforeAgentStartResult(
|
|
|
113
113
|
|
|
114
114
|
let memoryBlock = "";
|
|
115
115
|
try {
|
|
116
|
-
const { formatMemoriesForPrompt, getActiveMemoriesRanked } = await import("../memory-store.js");
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
const { formatMemoriesForPrompt, getActiveMemoriesRanked, queryMemoriesRanked } = await import("../memory-store.js");
|
|
117
|
+
|
|
118
|
+
// Always-on "critical" set — small, stable memories that belong in every
|
|
119
|
+
// turn (gotchas, environment, conventions). Ranked by the existing score.
|
|
120
|
+
const CRITICAL_CATEGORIES = new Set(["gotcha", "environment", "convention"]);
|
|
121
|
+
const allRanked = getActiveMemoriesRanked(60);
|
|
122
|
+
const critical = allRanked.filter((m) => CRITICAL_CATEGORIES.has(m.category)).slice(0, 5);
|
|
123
|
+
const criticalIds = new Set(critical.map((m) => m.id));
|
|
124
|
+
|
|
125
|
+
// Prompt-relevance set — hybrid FTS5 + (future) semantic retrieval.
|
|
126
|
+
let relevant: typeof allRanked = [];
|
|
127
|
+
const userPrompt = (event.prompt ?? "").trim();
|
|
128
|
+
if (userPrompt) {
|
|
129
|
+
const hits = queryMemoriesRanked({ query: userPrompt, k: 10 });
|
|
130
|
+
relevant = hits.map((h) => h.memory).filter((m) => !criticalIds.has(m.id));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const merged = [...critical, ...relevant];
|
|
134
|
+
if (merged.length > 0) {
|
|
135
|
+
const formatted = formatMemoriesForPrompt(merged, 2000);
|
|
120
136
|
if (formatted) {
|
|
121
137
|
memoryBlock = `\n\n${formatted}`;
|
|
122
138
|
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
// GSD — /gsd onboarding command handler (re-entry
|
|
1
|
+
// GSD — /gsd onboarding command handler (re-entry hub)
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// The first-run wizard in src/onboarding.ts uses @clack/prompts and takes over
|
|
4
|
+
// raw stdin. Running it from inside the pi-coding-agent TUI wedges the TUI
|
|
5
|
+
// (clack leaves stdin paused + cooked, pi-tui's data handler then receives no
|
|
6
|
+
// keypresses). So re-entry cannot replay the clack wizard — instead it routes
|
|
7
|
+
// to a setup hub built from ctx.ui.select, which the TUI owns.
|
|
8
|
+
//
|
|
9
|
+
// Clack-only steps (llm/search/remote/tool-keys via the first-run wizard) are
|
|
10
|
+
// surfaced as notifications pointing the user at the canonical per-step
|
|
11
|
+
// commands (/login, /gsd keys, /gsd remote) that are already ctx.ui-safe.
|
|
6
12
|
|
|
7
13
|
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent"
|
|
8
|
-
import { AuthStorage } from "@gsd/pi-coding-agent"
|
|
9
|
-
import { homedir } from "node:os"
|
|
10
|
-
import { join } from "node:path"
|
|
11
14
|
import {
|
|
12
15
|
ONBOARDING_STEPS,
|
|
13
16
|
isValidStepId,
|
|
14
|
-
nearestResumeStep,
|
|
15
17
|
type OnboardingStepId,
|
|
16
18
|
} from "../../setup-catalog.js"
|
|
17
19
|
import {
|
|
@@ -20,37 +22,7 @@ import {
|
|
|
20
22
|
resetOnboarding,
|
|
21
23
|
} from "../../onboarding-state.js"
|
|
22
24
|
|
|
23
|
-
// Inline auth path (mirrors src/app-paths.ts) — keep this module rootDir-clean
|
|
24
|
-
// for the resources tsconfig. Importing from src/ pulls files outside
|
|
25
|
-
// src/resources and breaks the build.
|
|
26
|
-
const AUTH_FILE_PATH = join(
|
|
27
|
-
process.env.GSD_CODING_AGENT_DIR ||
|
|
28
|
-
join(process.env.GSD_HOME || join(homedir(), ".gsd"), "agent"),
|
|
29
|
-
"auth.json",
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Dynamic import shim for the first-run wizard.
|
|
34
|
-
*
|
|
35
|
-
* src/onboarding.ts lives outside the resources rootDir, so a static import
|
|
36
|
-
* pulls it into this tsconfig project and triggers TS6059. We resolve the
|
|
37
|
-
* specifier through a variable + opaque type so TS can't pull the file at
|
|
38
|
-
* compile time; the path resolves correctly at runtime via dist/onboarding.js.
|
|
39
|
-
*/
|
|
40
|
-
type FirstRunWizardModule = {
|
|
41
|
-
runOnboarding: (storage: AuthStorage) => Promise<void>
|
|
42
|
-
runLlmStep: (...args: unknown[]) => Promise<unknown>
|
|
43
|
-
runWebSearchStep: (...args: unknown[]) => Promise<unknown>
|
|
44
|
-
runRemoteQuestionsStep: (...args: unknown[]) => Promise<unknown>
|
|
45
|
-
runToolKeysStep: (...args: unknown[]) => Promise<unknown>
|
|
46
|
-
}
|
|
47
|
-
async function loadFirstRunWizard(): Promise<FirstRunWizardModule> {
|
|
48
|
-
const specifier = "../../../../../onboarding.js"
|
|
49
|
-
return (await import(/* @vite-ignore */ specifier)) as FirstRunWizardModule
|
|
50
|
-
}
|
|
51
|
-
|
|
52
25
|
interface ParsedArgs {
|
|
53
|
-
resume: boolean
|
|
54
26
|
reset: boolean
|
|
55
27
|
step: string | null
|
|
56
28
|
stepValid: boolean | null
|
|
@@ -58,11 +30,13 @@ interface ParsedArgs {
|
|
|
58
30
|
|
|
59
31
|
function parseArgs(raw: string): ParsedArgs {
|
|
60
32
|
const tokens = raw.split(/\s+/).filter(Boolean)
|
|
61
|
-
const out: ParsedArgs = {
|
|
33
|
+
const out: ParsedArgs = { reset: false, step: null, stepValid: null }
|
|
62
34
|
for (let i = 0; i < tokens.length; i++) {
|
|
63
35
|
const t = tokens[i]
|
|
64
|
-
if (t === "--
|
|
65
|
-
else if (t === "--
|
|
36
|
+
if (t === "--reset" || t === "reset") out.reset = true
|
|
37
|
+
else if (t === "--resume" || t === "resume") {
|
|
38
|
+
// Re-entry no longer replays the wizard; --resume collapses into the hub.
|
|
39
|
+
}
|
|
66
40
|
else if (t === "--step" || t === "step") {
|
|
67
41
|
const next = tokens[i + 1]
|
|
68
42
|
if (next) {
|
|
@@ -79,52 +53,39 @@ function parseArgs(raw: string): ParsedArgs {
|
|
|
79
53
|
return out
|
|
80
54
|
}
|
|
81
55
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const authStorage = await getAuthStorage()
|
|
88
|
-
// The first-run wizard ignores the resume hint today — it always walks the
|
|
89
|
-
// full sequence with skip prompts. We still mark completion at the end and
|
|
90
|
-
// record the resume hint for next time. This keeps the wizard linear and
|
|
91
|
-
// simple; per-step jump support comes via --step.
|
|
92
|
-
if (fromStep) {
|
|
93
|
-
ctx.ui.notify(
|
|
94
|
-
`Resuming from step: ${fromStep}. The wizard runs all remaining steps; press skip on any you've already configured.`,
|
|
95
|
-
"info",
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
const { runOnboarding } = await loadFirstRunWizard()
|
|
99
|
-
await runOnboarding(authStorage)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async function runSingleStep(ctx: ExtensionCommandContext, stepId: OnboardingStepId): Promise<void> {
|
|
103
|
-
const authStorage = await getAuthStorage()
|
|
104
|
-
const ob = await loadFirstRunWizard()
|
|
105
|
-
// Lazy-load clack + chalk via the same path the wizard uses
|
|
106
|
-
const p = await import("@clack/prompts")
|
|
107
|
-
const { default: chalk } = await import("chalk")
|
|
108
|
-
const pc = {
|
|
109
|
-
cyan: chalk.cyan, green: chalk.green, yellow: chalk.yellow,
|
|
110
|
-
dim: chalk.dim, bold: chalk.bold, red: chalk.red, reset: chalk.reset,
|
|
111
|
-
}
|
|
56
|
+
// ─── Per-step routing ────────────────────────────────────────────────────────
|
|
57
|
+
//
|
|
58
|
+
// Clack-based steps are surfaced as notifications — running them inline from
|
|
59
|
+
// the TUI would wedge stdin (see header comment). Everything else routes to an
|
|
60
|
+
// existing ctx.ui-safe handler.
|
|
112
61
|
|
|
62
|
+
async function runStep(ctx: ExtensionCommandContext, stepId: OnboardingStepId): Promise<void> {
|
|
113
63
|
switch (stepId) {
|
|
114
64
|
case "llm":
|
|
115
|
-
|
|
65
|
+
ctx.ui.notify(
|
|
66
|
+
"LLM provider setup: run /login to sign in via OAuth, or /gsd keys add to paste an API key.",
|
|
67
|
+
"info",
|
|
68
|
+
)
|
|
116
69
|
return
|
|
117
70
|
case "search":
|
|
118
|
-
|
|
71
|
+
ctx.ui.notify(
|
|
72
|
+
"Web search setup: run /gsd keys add and pick a search provider (brave, tavily, etc.).",
|
|
73
|
+
"info",
|
|
74
|
+
)
|
|
119
75
|
return
|
|
120
76
|
case "remote":
|
|
121
|
-
|
|
77
|
+
ctx.ui.notify(
|
|
78
|
+
"Remote questions setup: run /gsd remote to configure Discord / Slack / Telegram notifications.",
|
|
79
|
+
"info",
|
|
80
|
+
)
|
|
122
81
|
return
|
|
123
82
|
case "tool-keys":
|
|
124
|
-
|
|
83
|
+
ctx.ui.notify(
|
|
84
|
+
"Tool keys setup: run /gsd keys add to save API keys for Context7, Jina, Groq voice, etc.",
|
|
85
|
+
"info",
|
|
86
|
+
)
|
|
125
87
|
return
|
|
126
88
|
case "model": {
|
|
127
|
-
// Delegate to /gsd model picker
|
|
128
89
|
const { handleCoreCommand } = await import("./core.js")
|
|
129
90
|
await handleCoreCommand("model", ctx)
|
|
130
91
|
return
|
|
@@ -137,7 +98,6 @@ async function runSingleStep(ctx: ExtensionCommandContext, stepId: OnboardingSte
|
|
|
137
98
|
return
|
|
138
99
|
}
|
|
139
100
|
case "doctor": {
|
|
140
|
-
// Best-effort: surface provider doctor results inline
|
|
141
101
|
try {
|
|
142
102
|
const { runProviderDoctor } = await import("../../doctor-providers.js") as any
|
|
143
103
|
if (typeof runProviderDoctor === "function") {
|
|
@@ -148,10 +108,9 @@ async function runSingleStep(ctx: ExtensionCommandContext, stepId: OnboardingSte
|
|
|
148
108
|
ctx.ui.notify("Run /gsd doctor to validate your setup.", "info")
|
|
149
109
|
return
|
|
150
110
|
}
|
|
151
|
-
case "skills":
|
|
152
|
-
ctx.ui.notify("Skill install runs
|
|
111
|
+
case "skills":
|
|
112
|
+
ctx.ui.notify("Skill install runs during /gsd init. Use /gsd init or /skill manage.", "info")
|
|
153
113
|
return
|
|
154
|
-
}
|
|
155
114
|
case "project": {
|
|
156
115
|
const { handleCoreCommand } = await import("./core.js")
|
|
157
116
|
await handleCoreCommand("init", ctx)
|
|
@@ -160,6 +119,27 @@ async function runSingleStep(ctx: ExtensionCommandContext, stepId: OnboardingSte
|
|
|
160
119
|
}
|
|
161
120
|
}
|
|
162
121
|
|
|
122
|
+
// ─── Setup hub ───────────────────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
async function renderSetupHub(ctx: ExtensionCommandContext): Promise<void> {
|
|
125
|
+
const record = readOnboardingRecord()
|
|
126
|
+
const completed = new Set(record.completedSteps)
|
|
127
|
+
const skipped = new Set(record.skippedSteps)
|
|
128
|
+
|
|
129
|
+
const labels = ONBOARDING_STEPS.map(step => {
|
|
130
|
+
const mark = completed.has(step.id) ? "✓" : skipped.has(step.id) ? "↷" : "○"
|
|
131
|
+
const req = step.required ? " (required)" : ""
|
|
132
|
+
return `${mark} ${step.label}${req} — ${step.hint}`
|
|
133
|
+
})
|
|
134
|
+
const labelToStep = new Map(labels.map((label, i) => [label, ONBOARDING_STEPS[i].id]))
|
|
135
|
+
|
|
136
|
+
const choice = await ctx.ui.select("GSD Setup — pick a step to configure", labels)
|
|
137
|
+
if (typeof choice !== "string") return
|
|
138
|
+
const stepId = labelToStep.get(choice)
|
|
139
|
+
if (!stepId) return
|
|
140
|
+
await runStep(ctx, stepId)
|
|
141
|
+
}
|
|
142
|
+
|
|
163
143
|
function renderStatus(): string {
|
|
164
144
|
const r = readOnboardingRecord()
|
|
165
145
|
const lines: string[] = ["GSD Onboarding\n"]
|
|
@@ -192,38 +172,25 @@ export async function handleOnboarding(rawArgs: string, ctx: ExtensionCommandCon
|
|
|
192
172
|
ctx.ui.notify(`Unknown step "${args.step}". Valid: ${validIds}`, "warning")
|
|
193
173
|
return
|
|
194
174
|
}
|
|
195
|
-
await
|
|
175
|
+
await runStep(ctx, args.step as OnboardingStepId)
|
|
196
176
|
return
|
|
197
177
|
}
|
|
198
178
|
|
|
199
179
|
if (args.reset) {
|
|
200
180
|
resetOnboarding()
|
|
201
181
|
ctx.ui.notify(
|
|
202
|
-
"Onboarding
|
|
182
|
+
"Onboarding state cleared. API keys/credentials are unchanged — manage them with /gsd keys. Restart GSD to re-run the first-run wizard, or pick a step below.",
|
|
203
183
|
"info",
|
|
204
184
|
)
|
|
205
|
-
await
|
|
185
|
+
await renderSetupHub(ctx)
|
|
206
186
|
return
|
|
207
187
|
}
|
|
208
188
|
|
|
209
|
-
|
|
210
|
-
const r = readOnboardingRecord()
|
|
211
|
-
const next = nearestResumeStep(r.lastResumePoint, r.completedSteps)
|
|
212
|
-
await runWholeWizard(ctx, next)
|
|
213
|
-
return
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// No flags. If already complete, show status + offer choice.
|
|
189
|
+
// No flags (or --resume). Show status if complete, then open the hub.
|
|
217
190
|
if (isOnboardingComplete()) {
|
|
218
191
|
ctx.ui.notify(renderStatus(), "info")
|
|
219
|
-
ctx.ui.notify(
|
|
220
|
-
"Onboarding already complete. Use /gsd onboarding --reset to start over, or --step <name> to redo one section.",
|
|
221
|
-
"info",
|
|
222
|
-
)
|
|
223
|
-
return
|
|
224
192
|
}
|
|
225
|
-
|
|
226
|
-
await runWholeWizard(ctx)
|
|
193
|
+
await renderSetupHub(ctx)
|
|
227
194
|
}
|
|
228
195
|
|
|
229
196
|
export { renderStatus as renderOnboardingStatus }
|
|
@@ -251,6 +251,11 @@ Examples:
|
|
|
251
251
|
await handleExtractLearnings(trimmed.replace(/^extract-learnings\s*/, "").trim(), ctx, pi);
|
|
252
252
|
return true;
|
|
253
253
|
}
|
|
254
|
+
if (trimmed === "memory" || trimmed.startsWith("memory ") || trimmed === "memory help") {
|
|
255
|
+
const { handleMemory } = await import("../../commands-memory.js");
|
|
256
|
+
await handleMemory(trimmed.replace(/^memory\s*/, "").trim(), ctx, pi);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
254
259
|
if (trimmed === "scan" || trimmed.startsWith("scan ")) {
|
|
255
260
|
const { handleScan } = await import("../../commands-scan.js");
|
|
256
261
|
// \s* (not \s+) is intentional: handles both /gsd scan (no args) and /gsd scan --focus X
|