@morsa/guidance-bank 0.2.0
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/LICENSE +21 -0
- package/README.md +135 -0
- package/bin/gbank.js +3 -0
- package/dist/cli/commands/init.js +27 -0
- package/dist/cli/commands/mcpServe.js +8 -0
- package/dist/cli/commands/stats.js +92 -0
- package/dist/cli/index.js +67 -0
- package/dist/cli/postinstall.js +24 -0
- package/dist/cli/prompts/initPrompts.js +89 -0
- package/dist/cli/prompts/providerAvailability.js +31 -0
- package/dist/core/audit/summarizeEntryContent.js +43 -0
- package/dist/core/audit/types.js +1 -0
- package/dist/core/bank/canonicalEntry.js +106 -0
- package/dist/core/bank/integration.js +16 -0
- package/dist/core/bank/layout.js +159 -0
- package/dist/core/bank/lifecycle.js +24 -0
- package/dist/core/bank/manifest.js +37 -0
- package/dist/core/bank/project.js +105 -0
- package/dist/core/bank/types.js +6 -0
- package/dist/core/context/contextEntryResolver.js +140 -0
- package/dist/core/context/contextTextRenderer.js +116 -0
- package/dist/core/context/detectProjectContext.js +118 -0
- package/dist/core/context/resolveContextService.js +138 -0
- package/dist/core/context/types.js +1 -0
- package/dist/core/init/initService.js +112 -0
- package/dist/core/init/initTypes.js +1 -0
- package/dist/core/projects/createBankDeriveGuidance/index.js +47 -0
- package/dist/core/projects/createBankDeriveGuidance/shared/general.js +49 -0
- package/dist/core/projects/createBankDeriveGuidance/shared/typescript.js +18 -0
- package/dist/core/projects/createBankDeriveGuidance/stacks/angular.js +252 -0
- package/dist/core/projects/createBankDeriveGuidance/stacks/ios.js +254 -0
- package/dist/core/projects/createBankDeriveGuidance/stacks/nextjs.js +220 -0
- package/dist/core/projects/createBankDeriveGuidance/stacks/nodejs.js +221 -0
- package/dist/core/projects/createBankDeriveGuidance/stacks/other.js +34 -0
- package/dist/core/projects/createBankDeriveGuidance/stacks/react.js +214 -0
- package/dist/core/projects/createBankFlow.js +252 -0
- package/dist/core/projects/createBankIterationPrompt.js +294 -0
- package/dist/core/projects/createBankPrompt.js +95 -0
- package/dist/core/projects/createFlowPhases.js +28 -0
- package/dist/core/projects/discoverCurrentProjectBank.js +43 -0
- package/dist/core/projects/discoverExistingGuidance.js +99 -0
- package/dist/core/projects/discoverProjectEvidence.js +87 -0
- package/dist/core/projects/discoverRecentCommits.js +28 -0
- package/dist/core/projects/findReferenceProjects.js +42 -0
- package/dist/core/projects/guidanceStrategies.js +29 -0
- package/dist/core/projects/identity.js +16 -0
- package/dist/core/projects/providerProjectGuidance.js +82 -0
- package/dist/core/providers/providerRegistry.js +51 -0
- package/dist/core/providers/types.js +1 -0
- package/dist/core/stats/statsService.js +117 -0
- package/dist/core/sync/syncService.js +145 -0
- package/dist/core/sync/syncTypes.js +1 -0
- package/dist/core/upgrade/upgradeService.js +134 -0
- package/dist/integrations/claudeCode/install.js +78 -0
- package/dist/integrations/codex/install.js +80 -0
- package/dist/integrations/commandRunner.js +32 -0
- package/dist/integrations/cursor/install.js +118 -0
- package/dist/integrations/shared.js +20 -0
- package/dist/mcp/config.js +19 -0
- package/dist/mcp/createMcpServer.js +31 -0
- package/dist/mcp/launcher.js +49 -0
- package/dist/mcp/registerTools.js +33 -0
- package/dist/mcp/serverMetadata.js +7 -0
- package/dist/mcp/tools/auditUtils.js +49 -0
- package/dist/mcp/tools/createBankApply.js +106 -0
- package/dist/mcp/tools/createBankToolRuntime.js +115 -0
- package/dist/mcp/tools/createBankToolSchemas.js +234 -0
- package/dist/mcp/tools/entryMutationHelpers.js +44 -0
- package/dist/mcp/tools/registerBankManifestTool.js +47 -0
- package/dist/mcp/tools/registerClearProjectBankTool.js +73 -0
- package/dist/mcp/tools/registerCreateBankTool.js +240 -0
- package/dist/mcp/tools/registerDeleteEntryTool.js +98 -0
- package/dist/mcp/tools/registerDeleteGuidanceSourceTool.js +120 -0
- package/dist/mcp/tools/registerListEntriesTool.js +94 -0
- package/dist/mcp/tools/registerReadEntryTool.js +99 -0
- package/dist/mcp/tools/registerResolveContextTool.js +128 -0
- package/dist/mcp/tools/registerSetProjectStateTool.js +121 -0
- package/dist/mcp/tools/registerSyncBankTool.js +113 -0
- package/dist/mcp/tools/registerUpgradeBankTool.js +89 -0
- package/dist/mcp/tools/registerUpsertRuleTool.js +100 -0
- package/dist/mcp/tools/registerUpsertSkillTool.js +102 -0
- package/dist/mcp/tools/sharedSchemas.js +13 -0
- package/dist/shared/errors.js +18 -0
- package/dist/shared/paths.js +11 -0
- package/dist/storage/atomicWrite.js +15 -0
- package/dist/storage/auditLogger.js +20 -0
- package/dist/storage/auditStore.js +22 -0
- package/dist/storage/bankRepository.js +168 -0
- package/dist/storage/entryStore.js +142 -0
- package/dist/storage/manifestStore.js +30 -0
- package/dist/storage/projectBankStore.js +55 -0
- package/dist/storage/providerIntegrationStore.js +22 -0
- package/dist/storage/safeFs.js +202 -0
- package/package.json +64 -0
- package/scripts/postinstall.js +20 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CREATE_FLOW_PHASES } from "../../core/projects/createFlowPhases.js";
|
|
3
|
+
import { resolveGuidanceBankContext } from "../../core/context/resolveContextService.js";
|
|
4
|
+
import { resolveProjectIdentity } from "../../core/projects/identity.js";
|
|
5
|
+
import { ValidationError } from "../../shared/errors.js";
|
|
6
|
+
import { AbsoluteProjectPathSchema, SessionRefSchema } from "./sharedSchemas.js";
|
|
7
|
+
import { writeToolAuditEvent } from "./auditUtils.js";
|
|
8
|
+
const ResolveContextArgsSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
11
|
+
sessionRef: SessionRefSchema,
|
|
12
|
+
})
|
|
13
|
+
.strict();
|
|
14
|
+
export const registerResolveContextTool = (server, options) => {
|
|
15
|
+
server.registerTool("resolve_context", {
|
|
16
|
+
title: "Resolve AI Guidance Bank Context",
|
|
17
|
+
description: "Resolve the primary AI Guidance Bank context for the current repository. AI Guidance Bank is the durable local rules-and-skills layer for reusable project guidance. Call this at the start of work in a project, and again when the working directory changes materially.",
|
|
18
|
+
annotations: {
|
|
19
|
+
readOnlyHint: true,
|
|
20
|
+
destructiveHint: false,
|
|
21
|
+
},
|
|
22
|
+
inputSchema: {
|
|
23
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
24
|
+
sessionRef: SessionRefSchema,
|
|
25
|
+
},
|
|
26
|
+
outputSchema: {
|
|
27
|
+
text: z.string(),
|
|
28
|
+
creationState: z.enum(["unknown", "postponed", "declined", "creating", "ready"]).optional(),
|
|
29
|
+
postponedUntil: z.string().nullable().optional(),
|
|
30
|
+
requiredAction: z.enum(["upgrade_bank", "create_bank", "continue_create_bank", "sync_bank"]).optional(),
|
|
31
|
+
recommendedAction: z.enum(["create_bank"]).optional(),
|
|
32
|
+
createFlowPhase: z.enum(CREATE_FLOW_PHASES).optional(),
|
|
33
|
+
nextIteration: z.number().int().nonnegative().optional(),
|
|
34
|
+
bankRoot: z.string().optional(),
|
|
35
|
+
sourceRoot: z.string().optional(),
|
|
36
|
+
expectedStorageVersion: z.number().int().positive().optional(),
|
|
37
|
+
storageVersion: z.number().int().positive().optional(),
|
|
38
|
+
detectedStacks: z.array(z.string()).optional(),
|
|
39
|
+
rulesCatalog: z
|
|
40
|
+
.array(z.object({
|
|
41
|
+
scope: z.enum(["shared", "project"]),
|
|
42
|
+
kind: z.literal("rules"),
|
|
43
|
+
path: z.string(),
|
|
44
|
+
title: z.string(),
|
|
45
|
+
topics: z.array(z.string()),
|
|
46
|
+
description: z.string().nullable().optional(),
|
|
47
|
+
}))
|
|
48
|
+
.optional(),
|
|
49
|
+
skillsCatalog: z
|
|
50
|
+
.array(z.object({
|
|
51
|
+
scope: z.enum(["shared", "project"]),
|
|
52
|
+
kind: z.literal("skills"),
|
|
53
|
+
path: z.string(),
|
|
54
|
+
title: z.string(),
|
|
55
|
+
topics: z.array(z.string()),
|
|
56
|
+
description: z.string().nullable().optional(),
|
|
57
|
+
}))
|
|
58
|
+
.optional(),
|
|
59
|
+
referenceProjects: z
|
|
60
|
+
.array(z.object({
|
|
61
|
+
projectId: z.string(),
|
|
62
|
+
projectName: z.string(),
|
|
63
|
+
projectPath: z.string(),
|
|
64
|
+
projectBankPath: z.string(),
|
|
65
|
+
detectedStacks: z.array(z.string()),
|
|
66
|
+
sharedStacks: z.array(z.string()),
|
|
67
|
+
}))
|
|
68
|
+
.optional(),
|
|
69
|
+
},
|
|
70
|
+
}, async (args) => {
|
|
71
|
+
const parsedArgs = ResolveContextArgsSchema.safeParse(args);
|
|
72
|
+
if (!parsedArgs.success) {
|
|
73
|
+
return {
|
|
74
|
+
isError: true,
|
|
75
|
+
content: [
|
|
76
|
+
{
|
|
77
|
+
type: "text",
|
|
78
|
+
text: `Invalid arguments for tool resolve_context: ${z.prettifyError(parsedArgs.error)}`,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const identity = resolveProjectIdentity(parsedArgs.data.projectPath);
|
|
85
|
+
const resolvedContext = await resolveGuidanceBankContext({
|
|
86
|
+
repository: options.repository,
|
|
87
|
+
projectPath: parsedArgs.data.projectPath,
|
|
88
|
+
});
|
|
89
|
+
await writeToolAuditEvent({
|
|
90
|
+
auditLogger: options.auditLogger,
|
|
91
|
+
sessionRef: parsedArgs.data.sessionRef,
|
|
92
|
+
tool: "resolve_context",
|
|
93
|
+
action: "resolve",
|
|
94
|
+
projectId: identity.projectId,
|
|
95
|
+
projectPath: identity.projectPath,
|
|
96
|
+
details: {
|
|
97
|
+
creationState: resolvedContext.creationState ?? null,
|
|
98
|
+
requiredAction: resolvedContext.requiredAction ?? null,
|
|
99
|
+
recommendedAction: resolvedContext.recommendedAction ?? null,
|
|
100
|
+
createFlowPhase: resolvedContext.createFlowPhase ?? null,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
content: [
|
|
105
|
+
{
|
|
106
|
+
type: "text",
|
|
107
|
+
text: JSON.stringify(resolvedContext, null, 2),
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
structuredContent: resolvedContext,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error instanceof ValidationError) {
|
|
115
|
+
return {
|
|
116
|
+
isError: true,
|
|
117
|
+
content: [
|
|
118
|
+
{
|
|
119
|
+
type: "text",
|
|
120
|
+
text: error.message,
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { computeProjectBankPostponedUntil, createProjectBankState, updateProjectBankState, } from "../../core/bank/project.js";
|
|
3
|
+
import { PROJECT_CREATION_STATES } from "../../core/bank/types.js";
|
|
4
|
+
import { resolveProjectIdentity } from "../../core/projects/identity.js";
|
|
5
|
+
import { AbsoluteProjectPathSchema, SessionRefSchema } from "./sharedSchemas.js";
|
|
6
|
+
import { writeToolAuditEvent } from "./auditUtils.js";
|
|
7
|
+
const BaseSetProjectStateArgsSchema = z
|
|
8
|
+
.object({
|
|
9
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
10
|
+
sessionRef: SessionRefSchema,
|
|
11
|
+
creationState: z
|
|
12
|
+
.enum(PROJECT_CREATION_STATES)
|
|
13
|
+
.describe("Project AI Guidance Bank creation state to persist for this repository."),
|
|
14
|
+
postponeDays: z.number().int().positive().optional(),
|
|
15
|
+
postponedUntil: z.iso.datetime().optional(),
|
|
16
|
+
})
|
|
17
|
+
.strict();
|
|
18
|
+
const SetProjectStateArgsSchema = BaseSetProjectStateArgsSchema.superRefine((value, ctx) => {
|
|
19
|
+
if (value.postponeDays !== undefined && value.postponedUntil !== undefined) {
|
|
20
|
+
ctx.addIssue({
|
|
21
|
+
code: "custom",
|
|
22
|
+
message: "Use either postponeDays or postponedUntil, not both.",
|
|
23
|
+
path: ["postponedUntil"],
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (value.creationState === "postponed") {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (value.postponeDays !== undefined) {
|
|
30
|
+
ctx.addIssue({
|
|
31
|
+
code: "custom",
|
|
32
|
+
message: "postponeDays is only valid when creationState is postponed.",
|
|
33
|
+
path: ["postponeDays"],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (value.postponedUntil !== undefined) {
|
|
37
|
+
ctx.addIssue({
|
|
38
|
+
code: "custom",
|
|
39
|
+
message: "postponedUntil is only valid when creationState is postponed.",
|
|
40
|
+
path: ["postponedUntil"],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
export const registerSetProjectStateTool = (server, options) => {
|
|
45
|
+
server.registerTool("set_project_state", {
|
|
46
|
+
title: "Set Project AI Guidance Bank State",
|
|
47
|
+
description: "Persist project-level AI Guidance Bank state such as declined creation, so the agent can avoid asking the user repeatedly.",
|
|
48
|
+
annotations: {
|
|
49
|
+
readOnlyHint: false,
|
|
50
|
+
destructiveHint: false,
|
|
51
|
+
},
|
|
52
|
+
inputSchema: {
|
|
53
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
54
|
+
sessionRef: SessionRefSchema,
|
|
55
|
+
creationState: z
|
|
56
|
+
.enum(PROJECT_CREATION_STATES)
|
|
57
|
+
.describe("Project AI Guidance Bank creation state to persist for this repository."),
|
|
58
|
+
postponeDays: z.number().int().positive().optional(),
|
|
59
|
+
postponedUntil: z.iso.datetime().optional(),
|
|
60
|
+
},
|
|
61
|
+
outputSchema: {
|
|
62
|
+
projectId: z.string(),
|
|
63
|
+
projectName: z.string(),
|
|
64
|
+
projectPath: z.string(),
|
|
65
|
+
creationState: z.enum(PROJECT_CREATION_STATES),
|
|
66
|
+
postponedUntil: z.string().nullable(),
|
|
67
|
+
},
|
|
68
|
+
}, async (args) => {
|
|
69
|
+
const parsedArgs = SetProjectStateArgsSchema.safeParse(args);
|
|
70
|
+
if (!parsedArgs.success) {
|
|
71
|
+
return {
|
|
72
|
+
isError: true,
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: "text",
|
|
76
|
+
text: `Invalid arguments for tool set_project_state: ${z.prettifyError(parsedArgs.error)}`,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const identity = resolveProjectIdentity(parsedArgs.data.projectPath);
|
|
82
|
+
const existingState = await options.repository.readProjectStateOptional(identity.projectId);
|
|
83
|
+
const now = new Date();
|
|
84
|
+
const postponedUntil = parsedArgs.data.creationState === "postponed"
|
|
85
|
+
? (parsedArgs.data.postponedUntil ??
|
|
86
|
+
computeProjectBankPostponedUntil(now, parsedArgs.data.postponeDays))
|
|
87
|
+
: null;
|
|
88
|
+
const nextState = existingState === null
|
|
89
|
+
? createProjectBankState(parsedArgs.data.creationState, { postponedUntil }, now)
|
|
90
|
+
: updateProjectBankState(existingState, parsedArgs.data.creationState, { postponedUntil }, now);
|
|
91
|
+
await options.repository.writeProjectState(identity.projectId, nextState);
|
|
92
|
+
await writeToolAuditEvent({
|
|
93
|
+
auditLogger: options.auditLogger,
|
|
94
|
+
sessionRef: parsedArgs.data.sessionRef,
|
|
95
|
+
tool: "set_project_state",
|
|
96
|
+
action: "set_state",
|
|
97
|
+
projectId: identity.projectId,
|
|
98
|
+
projectPath: identity.projectPath,
|
|
99
|
+
details: {
|
|
100
|
+
creationState: nextState.creationState,
|
|
101
|
+
postponedUntil: nextState.postponedUntil,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
const payload = {
|
|
105
|
+
projectId: identity.projectId,
|
|
106
|
+
projectName: identity.projectName,
|
|
107
|
+
projectPath: identity.projectPath,
|
|
108
|
+
creationState: nextState.creationState,
|
|
109
|
+
postponedUntil: nextState.postponedUntil,
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: "text",
|
|
115
|
+
text: JSON.stringify(payload, null, 2),
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
structuredContent: payload,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SyncService } from "../../core/sync/syncService.js";
|
|
3
|
+
import { resolveProjectIdentity } from "../../core/projects/identity.js";
|
|
4
|
+
import { ValidationError } from "../../shared/errors.js";
|
|
5
|
+
import { AbsoluteProjectPathSchema, SessionRefSchema } from "./sharedSchemas.js";
|
|
6
|
+
import { writeToolAuditEvent } from "./auditUtils.js";
|
|
7
|
+
const SyncBankArgsSchema = z
|
|
8
|
+
.object({
|
|
9
|
+
action: z.enum(["run", "postpone"]).describe("Run an explicit sync now or postpone the sync reminder."),
|
|
10
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
11
|
+
sessionRef: SessionRefSchema,
|
|
12
|
+
})
|
|
13
|
+
.strict();
|
|
14
|
+
export const registerSyncBankTool = (server, options) => {
|
|
15
|
+
server.registerTool("sync_bank", {
|
|
16
|
+
title: "Sync AI Guidance Bank",
|
|
17
|
+
description: "Run an explicit reconcile pass for the current project's durable AI Guidance Bank entries. Use this when the user asks to resync the bank or when import or migration flows need a fresh inventory.",
|
|
18
|
+
annotations: {
|
|
19
|
+
readOnlyHint: false,
|
|
20
|
+
destructiveHint: false,
|
|
21
|
+
},
|
|
22
|
+
inputSchema: {
|
|
23
|
+
action: z.enum(["run", "postpone"]).describe("Run an explicit sync now or postpone the sync reminder."),
|
|
24
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
25
|
+
sessionRef: SessionRefSchema,
|
|
26
|
+
},
|
|
27
|
+
outputSchema: {
|
|
28
|
+
action: z.enum(["run", "postpone"]),
|
|
29
|
+
bankRoot: z.string(),
|
|
30
|
+
projectPath: z.string(),
|
|
31
|
+
detectedStacks: z.array(z.string()),
|
|
32
|
+
projectState: z.enum(["unknown", "postponed", "declined", "creating", "ready"]),
|
|
33
|
+
postponedUntil: z.string().nullable(),
|
|
34
|
+
projectManifestUpdated: z.boolean(),
|
|
35
|
+
validatedEntries: z.object({
|
|
36
|
+
shared: z.object({
|
|
37
|
+
rules: z.number().int().nonnegative(),
|
|
38
|
+
skills: z.number().int().nonnegative(),
|
|
39
|
+
}),
|
|
40
|
+
project: z.object({
|
|
41
|
+
rules: z.number().int().nonnegative(),
|
|
42
|
+
skills: z.number().int().nonnegative(),
|
|
43
|
+
}),
|
|
44
|
+
}),
|
|
45
|
+
externalGuidanceSources: z.array(z.object({
|
|
46
|
+
kind: z.enum(["agents", "cursor", "claude", "codex"]),
|
|
47
|
+
path: z.string(),
|
|
48
|
+
})),
|
|
49
|
+
},
|
|
50
|
+
}, async (args) => {
|
|
51
|
+
const parsedArgs = SyncBankArgsSchema.safeParse(args);
|
|
52
|
+
if (!parsedArgs.success) {
|
|
53
|
+
return {
|
|
54
|
+
isError: true,
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: "text",
|
|
58
|
+
text: `Invalid arguments for tool sync_bank: ${z.prettifyError(parsedArgs.error)}`,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const identity = resolveProjectIdentity(parsedArgs.data.projectPath);
|
|
65
|
+
const syncService = new SyncService();
|
|
66
|
+
const result = parsedArgs.data.action === "run"
|
|
67
|
+
? await syncService.run({
|
|
68
|
+
bankRoot: options.repository.rootPath,
|
|
69
|
+
projectPath: parsedArgs.data.projectPath,
|
|
70
|
+
})
|
|
71
|
+
: await syncService.postpone({
|
|
72
|
+
bankRoot: options.repository.rootPath,
|
|
73
|
+
projectPath: parsedArgs.data.projectPath,
|
|
74
|
+
});
|
|
75
|
+
await writeToolAuditEvent({
|
|
76
|
+
auditLogger: options.auditLogger,
|
|
77
|
+
sessionRef: parsedArgs.data.sessionRef,
|
|
78
|
+
tool: "sync_bank",
|
|
79
|
+
action: "sync",
|
|
80
|
+
projectId: identity.projectId,
|
|
81
|
+
projectPath: identity.projectPath,
|
|
82
|
+
details: {
|
|
83
|
+
mode: parsedArgs.data.action,
|
|
84
|
+
projectState: result.projectState,
|
|
85
|
+
postponedUntil: result.postponedUntil,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: "text",
|
|
92
|
+
text: JSON.stringify(result, null, 2),
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
structuredContent: result,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error instanceof ValidationError) {
|
|
100
|
+
return {
|
|
101
|
+
isError: true,
|
|
102
|
+
content: [
|
|
103
|
+
{
|
|
104
|
+
type: "text",
|
|
105
|
+
text: error.message,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { UpgradeService } from "../../core/upgrade/upgradeService.js";
|
|
3
|
+
import { ValidationError } from "../../shared/errors.js";
|
|
4
|
+
import { SessionRefSchema } from "./sharedSchemas.js";
|
|
5
|
+
import { writeToolAuditEvent } from "./auditUtils.js";
|
|
6
|
+
const UpgradeBankArgsSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
sessionRef: SessionRefSchema,
|
|
9
|
+
})
|
|
10
|
+
.strict();
|
|
11
|
+
export const registerUpgradeBankTool = (server, options) => {
|
|
12
|
+
server.registerTool("upgrade_bank", {
|
|
13
|
+
title: "Upgrade AI Guidance Bank",
|
|
14
|
+
description: "Upgrade AI Guidance Bank to the current storage version, migrate the bank root when needed, remove legacy MCP registrations, and reapply the current integrations.",
|
|
15
|
+
annotations: {
|
|
16
|
+
readOnlyHint: false,
|
|
17
|
+
destructiveHint: false,
|
|
18
|
+
},
|
|
19
|
+
inputSchema: {
|
|
20
|
+
sessionRef: SessionRefSchema,
|
|
21
|
+
},
|
|
22
|
+
outputSchema: {
|
|
23
|
+
status: z.enum(["upgraded", "already_current"]),
|
|
24
|
+
bankRoot: z.string(),
|
|
25
|
+
sourceRoot: z.string(),
|
|
26
|
+
migratedBankRoot: z.boolean(),
|
|
27
|
+
previousStorageVersion: z.number().int().positive(),
|
|
28
|
+
storageVersion: z.number().int().positive(),
|
|
29
|
+
enabledProviders: z.array(z.string()),
|
|
30
|
+
},
|
|
31
|
+
}, async (args) => {
|
|
32
|
+
const parsedArgs = UpgradeBankArgsSchema.safeParse(args);
|
|
33
|
+
if (!parsedArgs.success) {
|
|
34
|
+
return {
|
|
35
|
+
isError: true,
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
text: `Invalid arguments for tool upgrade_bank: ${z.prettifyError(parsedArgs.error)}`,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const upgradeService = new UpgradeService();
|
|
46
|
+
const result = await upgradeService.run({
|
|
47
|
+
bankRoot: options.repository.rootPath,
|
|
48
|
+
});
|
|
49
|
+
await writeToolAuditEvent({
|
|
50
|
+
auditLogger: options.auditLogger,
|
|
51
|
+
sessionRef: parsedArgs.data.sessionRef,
|
|
52
|
+
tool: "upgrade_bank",
|
|
53
|
+
action: "upgrade",
|
|
54
|
+
projectId: "bank",
|
|
55
|
+
projectPath: result.bankRoot,
|
|
56
|
+
details: {
|
|
57
|
+
sourceRoot: result.sourceRoot,
|
|
58
|
+
migratedBankRoot: result.migratedBankRoot,
|
|
59
|
+
previousStorageVersion: result.previousStorageVersion,
|
|
60
|
+
storageVersion: result.storageVersion,
|
|
61
|
+
enabledProviders: result.enabledProviders,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: "text",
|
|
68
|
+
text: JSON.stringify(result, null, 2),
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
structuredContent: result,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error instanceof ValidationError) {
|
|
76
|
+
return {
|
|
77
|
+
isError: true,
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: "text",
|
|
81
|
+
text: error.message,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ENTRY_SCOPES } from "../../core/bank/types.js";
|
|
3
|
+
import { AbsoluteProjectPathSchema, SessionRefSchema } from "./sharedSchemas.js";
|
|
4
|
+
import { writeEntryAuditEvent } from "./auditUtils.js";
|
|
5
|
+
import { buildInvalidToolArgsResult, buildStructuredToolResult, readEntryBeforeMutation, resolveScopedMutationContext, } from "./entryMutationHelpers.js";
|
|
6
|
+
const UpsertRuleArgsSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
scope: z.enum(ENTRY_SCOPES).describe("Write target: shared user-level rules or project-specific rules."),
|
|
9
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
10
|
+
sessionRef: SessionRefSchema,
|
|
11
|
+
path: z
|
|
12
|
+
.string()
|
|
13
|
+
.trim()
|
|
14
|
+
.min(1)
|
|
15
|
+
.describe("Rule file path relative to the selected rules root, for example topics/architecture.md."),
|
|
16
|
+
content: z
|
|
17
|
+
.string()
|
|
18
|
+
.min(1)
|
|
19
|
+
.describe("Full markdown content for the thematic rule file, including canonical frontmatter."),
|
|
20
|
+
})
|
|
21
|
+
.strict();
|
|
22
|
+
export const registerUpsertRuleTool = (server, options) => {
|
|
23
|
+
server.registerTool("upsert_rule", {
|
|
24
|
+
title: "Create Or Update AI Guidance Bank Rule File",
|
|
25
|
+
description: "Create or update a thematic rule file in the shared or project AI Guidance Bank layer. If scope is ambiguous, the agent should ask the user before writing.",
|
|
26
|
+
annotations: {
|
|
27
|
+
readOnlyHint: false,
|
|
28
|
+
destructiveHint: false,
|
|
29
|
+
},
|
|
30
|
+
inputSchema: {
|
|
31
|
+
scope: z.enum(ENTRY_SCOPES).describe("Write target: shared user-level rules or project-specific rules."),
|
|
32
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
33
|
+
sessionRef: SessionRefSchema,
|
|
34
|
+
path: z
|
|
35
|
+
.string()
|
|
36
|
+
.trim()
|
|
37
|
+
.min(1)
|
|
38
|
+
.describe("Rule file path relative to the selected rules root, for example topics/architecture.md."),
|
|
39
|
+
content: z
|
|
40
|
+
.string()
|
|
41
|
+
.min(1)
|
|
42
|
+
.describe("Full markdown content for the thematic rule file, including canonical frontmatter."),
|
|
43
|
+
},
|
|
44
|
+
outputSchema: {
|
|
45
|
+
status: z.enum(["created", "updated"]),
|
|
46
|
+
scope: z.enum(ENTRY_SCOPES),
|
|
47
|
+
projectId: z.string(),
|
|
48
|
+
projectName: z.string(),
|
|
49
|
+
projectPath: z.string(),
|
|
50
|
+
path: z.string(),
|
|
51
|
+
absolutePath: z.string(),
|
|
52
|
+
},
|
|
53
|
+
}, async (args) => {
|
|
54
|
+
const parsedArgs = UpsertRuleArgsSchema.safeParse(args);
|
|
55
|
+
if (!parsedArgs.success) {
|
|
56
|
+
return buildInvalidToolArgsResult("upsert_rule", parsedArgs.error);
|
|
57
|
+
}
|
|
58
|
+
const mutationContext = await resolveScopedMutationContext({
|
|
59
|
+
repository: options.repository,
|
|
60
|
+
projectPath: parsedArgs.data.projectPath,
|
|
61
|
+
scope: parsedArgs.data.scope,
|
|
62
|
+
missingProjectMessage: "Project AI Guidance Bank does not exist yet. Call create_bank before writing project-scoped rules.",
|
|
63
|
+
});
|
|
64
|
+
if ("isError" in mutationContext) {
|
|
65
|
+
return mutationContext;
|
|
66
|
+
}
|
|
67
|
+
const { identity, projectId } = mutationContext;
|
|
68
|
+
const beforeContent = await readEntryBeforeMutation({
|
|
69
|
+
repository: options.repository,
|
|
70
|
+
scope: parsedArgs.data.scope,
|
|
71
|
+
kind: "rules",
|
|
72
|
+
path: parsedArgs.data.path,
|
|
73
|
+
...(projectId ? { projectId } : {}),
|
|
74
|
+
});
|
|
75
|
+
const result = await options.repository.upsertRule(parsedArgs.data.scope, parsedArgs.data.path, parsedArgs.data.content, projectId);
|
|
76
|
+
await writeEntryAuditEvent({
|
|
77
|
+
auditLogger: options.auditLogger,
|
|
78
|
+
sessionRef: parsedArgs.data.sessionRef,
|
|
79
|
+
tool: "upsert_rule",
|
|
80
|
+
action: "upsert",
|
|
81
|
+
scope: parsedArgs.data.scope,
|
|
82
|
+
kind: "rules",
|
|
83
|
+
projectId: identity.projectId,
|
|
84
|
+
projectPath: identity.projectPath,
|
|
85
|
+
path: result.path,
|
|
86
|
+
beforeContent,
|
|
87
|
+
afterContent: parsedArgs.data.content,
|
|
88
|
+
});
|
|
89
|
+
const payload = {
|
|
90
|
+
status: result.status,
|
|
91
|
+
scope: parsedArgs.data.scope,
|
|
92
|
+
projectId: identity.projectId,
|
|
93
|
+
projectName: identity.projectName,
|
|
94
|
+
projectPath: identity.projectPath,
|
|
95
|
+
path: result.path,
|
|
96
|
+
absolutePath: result.absolutePath,
|
|
97
|
+
};
|
|
98
|
+
return buildStructuredToolResult(payload);
|
|
99
|
+
});
|
|
100
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ENTRY_SCOPES } from "../../core/bank/types.js";
|
|
3
|
+
import { AbsoluteProjectPathSchema, SessionRefSchema } from "./sharedSchemas.js";
|
|
4
|
+
import { writeEntryAuditEvent } from "./auditUtils.js";
|
|
5
|
+
import { buildInvalidToolArgsResult, buildStructuredToolResult, readEntryBeforeMutation, resolveScopedMutationContext, } from "./entryMutationHelpers.js";
|
|
6
|
+
const UpsertSkillArgsSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
scope: z.enum(ENTRY_SCOPES).describe("Write target: shared user-level skills or project-specific skills."),
|
|
9
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
10
|
+
sessionRef: SessionRefSchema,
|
|
11
|
+
path: z
|
|
12
|
+
.string()
|
|
13
|
+
.trim()
|
|
14
|
+
.min(1)
|
|
15
|
+
.describe("Skill folder path relative to the selected skills root, for example adding-feature or stacks/angular/adding-feature."),
|
|
16
|
+
content: z
|
|
17
|
+
.string()
|
|
18
|
+
.min(1)
|
|
19
|
+
.describe("Full markdown content for the skill SKILL.md file, including canonical frontmatter."),
|
|
20
|
+
})
|
|
21
|
+
.strict();
|
|
22
|
+
export const registerUpsertSkillTool = (server, options) => {
|
|
23
|
+
server.registerTool("upsert_skill", {
|
|
24
|
+
title: "Create Or Update AI Guidance Bank Skill",
|
|
25
|
+
description: "Create or update a skill folder with a single SKILL.md file in the shared or project AI Guidance Bank layer. If scope is ambiguous, the agent should ask the user before writing.",
|
|
26
|
+
annotations: {
|
|
27
|
+
readOnlyHint: false,
|
|
28
|
+
destructiveHint: false,
|
|
29
|
+
},
|
|
30
|
+
inputSchema: {
|
|
31
|
+
scope: z.enum(ENTRY_SCOPES).describe("Write target: shared user-level skills or project-specific skills."),
|
|
32
|
+
projectPath: AbsoluteProjectPathSchema,
|
|
33
|
+
sessionRef: SessionRefSchema,
|
|
34
|
+
path: z
|
|
35
|
+
.string()
|
|
36
|
+
.trim()
|
|
37
|
+
.min(1)
|
|
38
|
+
.describe("Skill folder path relative to the selected skills root, for example adding-feature or stacks/angular/adding-feature."),
|
|
39
|
+
content: z
|
|
40
|
+
.string()
|
|
41
|
+
.min(1)
|
|
42
|
+
.describe("Full markdown content for the skill SKILL.md file, including canonical frontmatter."),
|
|
43
|
+
},
|
|
44
|
+
outputSchema: {
|
|
45
|
+
status: z.enum(["created", "updated"]),
|
|
46
|
+
scope: z.enum(ENTRY_SCOPES),
|
|
47
|
+
projectId: z.string(),
|
|
48
|
+
projectName: z.string(),
|
|
49
|
+
projectPath: z.string(),
|
|
50
|
+
path: z.string(),
|
|
51
|
+
filePath: z.string(),
|
|
52
|
+
absolutePath: z.string(),
|
|
53
|
+
},
|
|
54
|
+
}, async (args) => {
|
|
55
|
+
const parsedArgs = UpsertSkillArgsSchema.safeParse(args);
|
|
56
|
+
if (!parsedArgs.success) {
|
|
57
|
+
return buildInvalidToolArgsResult("upsert_skill", parsedArgs.error);
|
|
58
|
+
}
|
|
59
|
+
const mutationContext = await resolveScopedMutationContext({
|
|
60
|
+
repository: options.repository,
|
|
61
|
+
projectPath: parsedArgs.data.projectPath,
|
|
62
|
+
scope: parsedArgs.data.scope,
|
|
63
|
+
missingProjectMessage: "Project AI Guidance Bank does not exist yet. Call create_bank before writing project-scoped skills.",
|
|
64
|
+
});
|
|
65
|
+
if ("isError" in mutationContext) {
|
|
66
|
+
return mutationContext;
|
|
67
|
+
}
|
|
68
|
+
const { identity, projectId } = mutationContext;
|
|
69
|
+
const beforeContent = await readEntryBeforeMutation({
|
|
70
|
+
repository: options.repository,
|
|
71
|
+
scope: parsedArgs.data.scope,
|
|
72
|
+
kind: "skills",
|
|
73
|
+
path: parsedArgs.data.path,
|
|
74
|
+
...(projectId ? { projectId } : {}),
|
|
75
|
+
});
|
|
76
|
+
const result = await options.repository.upsertSkill(parsedArgs.data.scope, parsedArgs.data.path, parsedArgs.data.content, projectId);
|
|
77
|
+
await writeEntryAuditEvent({
|
|
78
|
+
auditLogger: options.auditLogger,
|
|
79
|
+
sessionRef: parsedArgs.data.sessionRef,
|
|
80
|
+
tool: "upsert_skill",
|
|
81
|
+
action: "upsert",
|
|
82
|
+
scope: parsedArgs.data.scope,
|
|
83
|
+
kind: "skills",
|
|
84
|
+
projectId: identity.projectId,
|
|
85
|
+
projectPath: identity.projectPath,
|
|
86
|
+
path: result.path,
|
|
87
|
+
beforeContent,
|
|
88
|
+
afterContent: parsedArgs.data.content,
|
|
89
|
+
});
|
|
90
|
+
const payload = {
|
|
91
|
+
status: result.status,
|
|
92
|
+
scope: parsedArgs.data.scope,
|
|
93
|
+
projectId: identity.projectId,
|
|
94
|
+
projectName: identity.projectName,
|
|
95
|
+
projectPath: identity.projectPath,
|
|
96
|
+
path: result.path,
|
|
97
|
+
filePath: result.filePath,
|
|
98
|
+
absolutePath: result.absolutePath,
|
|
99
|
+
};
|
|
100
|
+
return buildStructuredToolResult(payload);
|
|
101
|
+
});
|
|
102
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const AbsoluteProjectPathSchema = z
|
|
4
|
+
.string()
|
|
5
|
+
.trim()
|
|
6
|
+
.min(1)
|
|
7
|
+
.refine((value) => path.isAbsolute(value), "Project path must be absolute.")
|
|
8
|
+
.describe("Absolute path to the current repository or working directory.");
|
|
9
|
+
export const SessionRefSchema = z
|
|
10
|
+
.string()
|
|
11
|
+
.trim()
|
|
12
|
+
.min(1)
|
|
13
|
+
.describe("Required agent session reference for audit logging and traceability.");
|