@ai-sdlc/mcp-advisor 0.1.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.
Files changed (43) hide show
  1. package/dist/bin.d.ts +3 -0
  2. package/dist/bin.js +6 -0
  3. package/dist/index.d.ts +12 -0
  4. package/dist/index.js +14 -0
  5. package/dist/issue-linker.d.ts +19 -0
  6. package/dist/issue-linker.js +93 -0
  7. package/dist/plugin.d.ts +13 -0
  8. package/dist/plugin.js +8 -0
  9. package/dist/resources/budget.d.ts +4 -0
  10. package/dist/resources/budget.js +17 -0
  11. package/dist/resources/codebase-profile.d.ts +4 -0
  12. package/dist/resources/codebase-profile.js +18 -0
  13. package/dist/resources/conventions.d.ts +4 -0
  14. package/dist/resources/conventions.js +15 -0
  15. package/dist/resources/history.d.ts +4 -0
  16. package/dist/resources/history.js +18 -0
  17. package/dist/resources/hotspots.d.ts +4 -0
  18. package/dist/resources/hotspots.js +15 -0
  19. package/dist/resources/index.d.ts +4 -0
  20. package/dist/resources/index.js +15 -0
  21. package/dist/resources/my-tasks.d.ts +4 -0
  22. package/dist/resources/my-tasks.js +15 -0
  23. package/dist/server.d.ts +22 -0
  24. package/dist/server.js +32 -0
  25. package/dist/session.d.ts +49 -0
  26. package/dist/session.js +67 -0
  27. package/dist/tools/check-file.d.ts +15 -0
  28. package/dist/tools/check-file.js +46 -0
  29. package/dist/tools/check-task.d.ts +15 -0
  30. package/dist/tools/check-task.js +47 -0
  31. package/dist/tools/get-context.d.ts +15 -0
  32. package/dist/tools/get-context.js +94 -0
  33. package/dist/tools/index.d.ts +10 -0
  34. package/dist/tools/index.js +21 -0
  35. package/dist/tools/session-end.d.ts +21 -0
  36. package/dist/tools/session-end.js +62 -0
  37. package/dist/tools/session-start.d.ts +15 -0
  38. package/dist/tools/session-start.js +42 -0
  39. package/dist/tools/track-usage.d.ts +16 -0
  40. package/dist/tools/track-usage.js +43 -0
  41. package/dist/types.d.ts +13 -0
  42. package/dist/types.js +5 -0
  43. package/package.json +45 -0
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
package/dist/bin.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { createMcpServer } from './server.js';
4
+ const { server } = await createMcpServer();
5
+ await server.connect(new StdioServerTransport());
6
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1,12 @@
1
+ export { createMcpServer, type CreateServerOptions } from './server.js';
2
+ export { SessionManager, type SessionState, type IssueLinkMethod, type UsageEntry, type AccumulatedCost, type CreateSessionOpts, } from './session.js';
3
+ export { resolveIssue, type IssueResolution } from './issue-linker.js';
4
+ export type { ServerDeps } from './types.js';
5
+ export type { McpAdvisorPlugin } from './plugin.js';
6
+ export { handleSessionStart } from './tools/session-start.js';
7
+ export { handleGetContext } from './tools/get-context.js';
8
+ export { handleCheckTask } from './tools/check-task.js';
9
+ export { handleTrackUsage } from './tools/track-usage.js';
10
+ export { handleCheckFile } from './tools/check-file.js';
11
+ export { handleSessionEnd } from './tools/session-end.js';
12
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ // Server factory
2
+ export { createMcpServer } from './server.js';
3
+ // Session management
4
+ export { SessionManager, } from './session.js';
5
+ // Issue linker
6
+ export { resolveIssue } from './issue-linker.js';
7
+ // Tool handlers (for direct testing / programmatic use)
8
+ export { handleSessionStart } from './tools/session-start.js';
9
+ export { handleGetContext } from './tools/get-context.js';
10
+ export { handleCheckTask } from './tools/check-task.js';
11
+ export { handleTrackUsage } from './tools/track-usage.js';
12
+ export { handleCheckFile } from './tools/check-file.js';
13
+ export { handleSessionEnd } from './tools/session-end.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 4-tier issue resolution chain.
3
+ * Attempts to link a session to an issue number via:
4
+ * 1. Branch name pattern
5
+ * 2. Explicit declaration
6
+ * 3. Git log context
7
+ * 4. Unattributed fallback
8
+ */
9
+ import type { IssueLinkMethod } from './session.js';
10
+ export interface IssueResolution {
11
+ issueNumber: number | null;
12
+ method: IssueLinkMethod;
13
+ confidence: number;
14
+ }
15
+ /**
16
+ * Resolve issue number through the 4-tier chain.
17
+ */
18
+ export declare function resolveIssue(repoPath: string, explicitIssue?: number): Promise<IssueResolution>;
19
+ //# sourceMappingURL=issue-linker.d.ts.map
@@ -0,0 +1,93 @@
1
+ /**
2
+ * 4-tier issue resolution chain.
3
+ * Attempts to link a session to an issue number via:
4
+ * 1. Branch name pattern
5
+ * 2. Explicit declaration
6
+ * 3. Git log context
7
+ * 4. Unattributed fallback
8
+ */
9
+ import { execFile } from 'node:child_process';
10
+ import { promisify } from 'node:util';
11
+ import { extractIssueNumber } from '@ai-sdlc/orchestrator';
12
+ const execFileAsync = promisify(execFile);
13
+ /** Loose pattern for branch names like `issue-42`, `issue_42`, `issue#42`. */
14
+ const LOOSE_BRANCH_PATTERN = /issue[- _]?#?(\d+)/i;
15
+ /** Pattern for `#N` or `fixes #N` in commit messages. */
16
+ const COMMIT_ISSUE_PATTERN = /(?:fix(?:es|ed)?|close[sd]?|resolve[sd]?)\s+#(\d+)|#(\d+)/gi;
17
+ /**
18
+ * Resolve the current git branch name.
19
+ */
20
+ async function getCurrentBranch(repoPath) {
21
+ try {
22
+ const { stdout } = await execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
23
+ cwd: repoPath,
24
+ });
25
+ return stdout.trim() || null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ /**
32
+ * Parse recent git log for issue references.
33
+ */
34
+ async function getIssueFromGitLog(repoPath) {
35
+ try {
36
+ const { stdout } = await execFileAsync('git', ['log', '--oneline', '-20'], { cwd: repoPath });
37
+ const counts = new Map();
38
+ for (const line of stdout.split('\n')) {
39
+ let match;
40
+ COMMIT_ISSUE_PATTERN.lastIndex = 0;
41
+ while ((match = COMMIT_ISSUE_PATTERN.exec(line)) !== null) {
42
+ const num = parseInt(match[1] ?? match[2], 10);
43
+ if (!isNaN(num)) {
44
+ counts.set(num, (counts.get(num) ?? 0) + 1);
45
+ }
46
+ }
47
+ }
48
+ if (counts.size === 0)
49
+ return null;
50
+ // Return the most frequently referenced issue
51
+ let best = null;
52
+ let bestCount = 0;
53
+ for (const [num, count] of counts) {
54
+ if (count > bestCount) {
55
+ best = num;
56
+ bestCount = count;
57
+ }
58
+ }
59
+ return best;
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ }
65
+ /**
66
+ * Resolve issue number through the 4-tier chain.
67
+ */
68
+ export async function resolveIssue(repoPath, explicitIssue) {
69
+ // Tier 1: Branch name
70
+ const branch = await getCurrentBranch(repoPath);
71
+ if (branch) {
72
+ const strict = extractIssueNumber(branch);
73
+ if (strict !== null) {
74
+ return { issueNumber: strict, method: 'branch', confidence: 1.0 };
75
+ }
76
+ const loose = branch.match(LOOSE_BRANCH_PATTERN);
77
+ if (loose) {
78
+ return { issueNumber: parseInt(loose[1], 10), method: 'branch', confidence: 0.8 };
79
+ }
80
+ }
81
+ // Tier 2: Explicit declaration
82
+ if (explicitIssue != null) {
83
+ return { issueNumber: explicitIssue, method: 'explicit', confidence: 1.0 };
84
+ }
85
+ // Tier 3: Git context
86
+ const gitIssue = await getIssueFromGitLog(repoPath);
87
+ if (gitIssue !== null) {
88
+ return { issueNumber: gitIssue, method: 'git-context', confidence: 0.6 };
89
+ }
90
+ // Tier 4: Unattributed
91
+ return { issueNumber: null, method: 'unattributed', confidence: 0 };
92
+ }
93
+ //# sourceMappingURL=issue-linker.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * MCP advisor plugin interface — extension point for custom tools/resources.
3
+ *
4
+ * Plugins register additional tools and resources on the MCP server,
5
+ * following the same (server, deps) pattern used by built-in registrations.
6
+ */
7
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ import type { ServerDeps } from './types.js';
9
+ export interface McpAdvisorPlugin {
10
+ name: string;
11
+ register(server: McpServer, deps: ServerDeps): void | Promise<void>;
12
+ }
13
+ //# sourceMappingURL=plugin.d.ts.map
package/dist/plugin.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * MCP advisor plugin interface — extension point for custom tools/resources.
3
+ *
4
+ * Plugins register additional tools and resources on the MCP server,
5
+ * following the same (server, deps) pattern used by built-in registrations.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export declare function registerBudgetResource(server: McpServer, deps: ServerDeps): void;
4
+ //# sourceMappingURL=budget.d.ts.map
@@ -0,0 +1,17 @@
1
+ export function registerBudgetResource(server, deps) {
2
+ server.resource('budget', 'ai-sdlc://context/budget', { description: 'Current cost budget status and per-agent breakdown' }, async () => {
3
+ const budgetStatus = deps.costTracker.getBudgetStatus();
4
+ const costByAgent = deps.costTracker.getCostByAgent();
5
+ const data = { budgetStatus, costByAgent };
6
+ return {
7
+ contents: [
8
+ {
9
+ uri: 'ai-sdlc://context/budget',
10
+ text: JSON.stringify(data, null, 2),
11
+ mimeType: 'application/json',
12
+ },
13
+ ],
14
+ };
15
+ });
16
+ }
17
+ //# sourceMappingURL=budget.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export declare function registerCodebaseProfileResource(server: McpServer, deps: ServerDeps): void;
4
+ //# sourceMappingURL=codebase-profile.d.ts.map
@@ -0,0 +1,18 @@
1
+ export function registerCodebaseProfileResource(server, deps) {
2
+ server.resource('codebase-profile', 'ai-sdlc://context/codebase-profile', {
3
+ description: 'Latest codebase complexity profile including score, modules, patterns, and hotspots',
4
+ }, async () => {
5
+ const profile = deps.store.getLatestComplexityProfile(deps.repoPath);
6
+ const data = profile ?? { message: 'No codebase profile available. Run analysis first.' };
7
+ return {
8
+ contents: [
9
+ {
10
+ uri: 'ai-sdlc://context/codebase-profile',
11
+ text: JSON.stringify(data, null, 2),
12
+ mimeType: 'application/json',
13
+ },
14
+ ],
15
+ };
16
+ });
17
+ }
18
+ //# sourceMappingURL=codebase-profile.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export declare function registerConventionsResource(server: McpServer, deps: ServerDeps): void;
4
+ //# sourceMappingURL=conventions.d.ts.map
@@ -0,0 +1,15 @@
1
+ export function registerConventionsResource(server, deps) {
2
+ server.resource('conventions', 'ai-sdlc://context/conventions', { description: 'Detected coding conventions: naming, testing, imports, structure, formatting' }, async () => {
3
+ const conventions = deps.store.getConventions();
4
+ return {
5
+ contents: [
6
+ {
7
+ uri: 'ai-sdlc://context/conventions',
8
+ text: JSON.stringify(conventions, null, 2),
9
+ mimeType: 'application/json',
10
+ },
11
+ ],
12
+ };
13
+ });
14
+ }
15
+ //# sourceMappingURL=conventions.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export declare function registerHistoryResource(server: McpServer, deps: ServerDeps): void;
4
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { findRelevantEpisodes } from '@ai-sdlc/orchestrator';
2
+ export function registerHistoryResource(server, deps) {
3
+ server.resource('history', 'ai-sdlc://context/history', { description: 'Episodic memory relevant to the active session or recent work' }, async () => {
4
+ const activeSession = deps.sessions.getActive();
5
+ const issueNumber = activeSession?.linkedIssue ?? undefined;
6
+ const episodes = findRelevantEpisodes(deps.store, { issueNumber });
7
+ return {
8
+ contents: [
9
+ {
10
+ uri: 'ai-sdlc://context/history',
11
+ text: JSON.stringify(episodes, null, 2),
12
+ mimeType: 'application/json',
13
+ },
14
+ ],
15
+ };
16
+ });
17
+ }
18
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export declare function registerHotspotsResource(server: McpServer, deps: ServerDeps): void;
4
+ //# sourceMappingURL=hotspots.d.ts.map
@@ -0,0 +1,15 @@
1
+ export function registerHotspotsResource(server, deps) {
2
+ server.resource('hotspots', 'ai-sdlc://context/hotspots', { description: 'Top file hotspots by churn rate and complexity — files that need extra care' }, async () => {
3
+ const hotspots = deps.store.getHotspots(deps.repoPath, 20);
4
+ return {
5
+ contents: [
6
+ {
7
+ uri: 'ai-sdlc://context/hotspots',
8
+ text: JSON.stringify(hotspots, null, 2),
9
+ mimeType: 'application/json',
10
+ },
11
+ ],
12
+ };
13
+ });
14
+ }
15
+ //# sourceMappingURL=hotspots.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export declare function registerAllResources(server: McpServer, deps: ServerDeps): void;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { registerCodebaseProfileResource } from './codebase-profile.js';
2
+ import { registerConventionsResource } from './conventions.js';
3
+ import { registerHotspotsResource } from './hotspots.js';
4
+ import { registerMyTasksResource } from './my-tasks.js';
5
+ import { registerBudgetResource } from './budget.js';
6
+ import { registerHistoryResource } from './history.js';
7
+ export function registerAllResources(server, deps) {
8
+ registerCodebaseProfileResource(server, deps);
9
+ registerConventionsResource(server, deps);
10
+ registerHotspotsResource(server, deps);
11
+ registerMyTasksResource(server, deps);
12
+ registerBudgetResource(server, deps);
13
+ registerHistoryResource(server, deps);
14
+ }
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export declare function registerMyTasksResource(server: McpServer, deps: ServerDeps): void;
4
+ //# sourceMappingURL=my-tasks.d.ts.map
@@ -0,0 +1,15 @@
1
+ export function registerMyTasksResource(server, deps) {
2
+ server.resource('my-tasks', 'ai-sdlc://context/my-tasks', { description: 'Recent pipeline runs and task history' }, async () => {
3
+ const runs = deps.store.getPipelineRuns(undefined, 50);
4
+ return {
5
+ contents: [
6
+ {
7
+ uri: 'ai-sdlc://context/my-tasks',
8
+ text: JSON.stringify(runs, null, 2),
9
+ mimeType: 'application/json',
10
+ },
11
+ ],
12
+ };
13
+ });
14
+ }
15
+ //# sourceMappingURL=my-tasks.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * MCP server factory — creates and wires the advisor server.
3
+ */
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import Database from 'better-sqlite3';
6
+ import type { ServerDeps } from './types.js';
7
+ import type { McpAdvisorPlugin } from './plugin.js';
8
+ export interface CreateServerOptions {
9
+ /** Path to SQLite database. Defaults to AI_SDLC_DB env var or '.ai-sdlc/state.db'. */
10
+ dbPath?: string;
11
+ /** Override database instance (for testing with :memory:). */
12
+ db?: InstanceType<typeof Database>;
13
+ /** Repository path. Defaults to cwd. */
14
+ repoPath?: string;
15
+ /** Plugins to register additional tools/resources. */
16
+ plugins?: McpAdvisorPlugin[];
17
+ }
18
+ export declare function createMcpServer(opts?: CreateServerOptions): Promise<{
19
+ server: McpServer;
20
+ deps: ServerDeps;
21
+ }>;
22
+ //# sourceMappingURL=server.d.ts.map
package/dist/server.js ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * MCP server factory — creates and wires the advisor server.
3
+ */
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import Database from 'better-sqlite3';
6
+ import { StateStore } from '@ai-sdlc/orchestrator/state';
7
+ import { CostTracker } from '@ai-sdlc/orchestrator';
8
+ import { SessionManager } from './session.js';
9
+ import { registerAllTools } from './tools/index.js';
10
+ import { registerAllResources } from './resources/index.js';
11
+ export async function createMcpServer(opts) {
12
+ const db = opts?.db ?? new Database(opts?.dbPath ?? process.env['AI_SDLC_DB'] ?? '.ai-sdlc/state.db');
13
+ const store = StateStore.open(db);
14
+ const costTracker = new CostTracker(store);
15
+ const sessions = new SessionManager();
16
+ const repoPath = opts?.repoPath ?? process.cwd();
17
+ const deps = { store, costTracker, sessions, repoPath };
18
+ const server = new McpServer({
19
+ name: 'ai-sdlc-advisor',
20
+ version: '0.1.0',
21
+ });
22
+ registerAllTools(server, deps);
23
+ registerAllResources(server, deps);
24
+ // Register plugins
25
+ if (opts?.plugins) {
26
+ for (const plugin of opts.plugins) {
27
+ await plugin.register(server, deps);
28
+ }
29
+ }
30
+ return { server, deps };
31
+ }
32
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1,49 @@
1
+ /**
2
+ * In-memory session state manager.
3
+ * Sessions are ephemeral — one per MCP server / IDE window.
4
+ */
5
+ export type IssueLinkMethod = 'branch' | 'explicit' | 'git-context' | 'unattributed';
6
+ export interface UsageEntry {
7
+ model: string;
8
+ inputTokens: number;
9
+ outputTokens: number;
10
+ costUsd: number;
11
+ }
12
+ export interface AccumulatedCost {
13
+ totalInputTokens: number;
14
+ totalOutputTokens: number;
15
+ totalCostUsd: number;
16
+ byModel: Record<string, {
17
+ inputTokens: number;
18
+ outputTokens: number;
19
+ costUsd: number;
20
+ }>;
21
+ }
22
+ export interface SessionState {
23
+ sessionId: string;
24
+ startedAt: string;
25
+ developer: string;
26
+ tool: string;
27
+ repoPath: string;
28
+ project: string;
29
+ linkedIssue: number | null;
30
+ linkMethod: IssueLinkMethod | null;
31
+ accumulatedCost: AccumulatedCost;
32
+ active: boolean;
33
+ }
34
+ export interface CreateSessionOpts {
35
+ developer: string;
36
+ tool: string;
37
+ repoPath?: string;
38
+ project?: string;
39
+ }
40
+ export declare class SessionManager {
41
+ private sessions;
42
+ create(opts: CreateSessionOpts): SessionState;
43
+ get(sessionId: string): SessionState | undefined;
44
+ getActive(): SessionState | undefined;
45
+ addUsage(sessionId: string, entry: UsageEntry): void;
46
+ linkIssue(sessionId: string, issueNumber: number | null, method: IssueLinkMethod): void;
47
+ end(sessionId: string): SessionState | undefined;
48
+ }
49
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * In-memory session state manager.
3
+ * Sessions are ephemeral — one per MCP server / IDE window.
4
+ */
5
+ import { randomUUID } from 'node:crypto';
6
+ function emptyAccumulatedCost() {
7
+ return { totalInputTokens: 0, totalOutputTokens: 0, totalCostUsd: 0, byModel: {} };
8
+ }
9
+ export class SessionManager {
10
+ sessions = new Map();
11
+ create(opts) {
12
+ const sessionId = randomUUID();
13
+ const session = {
14
+ sessionId,
15
+ startedAt: new Date().toISOString(),
16
+ developer: opts.developer,
17
+ tool: opts.tool,
18
+ repoPath: opts.repoPath ?? process.cwd(),
19
+ project: opts.project ?? process.env['AI_SDLC_PROJECT'] ?? '',
20
+ linkedIssue: null,
21
+ linkMethod: null,
22
+ accumulatedCost: emptyAccumulatedCost(),
23
+ active: true,
24
+ };
25
+ this.sessions.set(sessionId, session);
26
+ return session;
27
+ }
28
+ get(sessionId) {
29
+ return this.sessions.get(sessionId);
30
+ }
31
+ getActive() {
32
+ for (const s of this.sessions.values()) {
33
+ if (s.active)
34
+ return s;
35
+ }
36
+ return undefined;
37
+ }
38
+ addUsage(sessionId, entry) {
39
+ const session = this.sessions.get(sessionId);
40
+ if (!session)
41
+ return;
42
+ const cost = session.accumulatedCost;
43
+ cost.totalInputTokens += entry.inputTokens;
44
+ cost.totalOutputTokens += entry.outputTokens;
45
+ cost.totalCostUsd += entry.costUsd;
46
+ const model = cost.byModel[entry.model] ?? { inputTokens: 0, outputTokens: 0, costUsd: 0 };
47
+ model.inputTokens += entry.inputTokens;
48
+ model.outputTokens += entry.outputTokens;
49
+ model.costUsd += entry.costUsd;
50
+ cost.byModel[entry.model] = model;
51
+ }
52
+ linkIssue(sessionId, issueNumber, method) {
53
+ const session = this.sessions.get(sessionId);
54
+ if (!session)
55
+ return;
56
+ session.linkedIssue = issueNumber;
57
+ session.linkMethod = method;
58
+ }
59
+ end(sessionId) {
60
+ const session = this.sessions.get(sessionId);
61
+ if (!session)
62
+ return undefined;
63
+ session.active = false;
64
+ return session;
65
+ }
66
+ }
67
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1,15 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export interface CheckFileResult {
4
+ filePath: string;
5
+ isHotspot: boolean;
6
+ isBlocked: boolean;
7
+ crossesModuleBoundary: boolean;
8
+ warnings: string[];
9
+ }
10
+ export declare function handleCheckFile(deps: ServerDeps, input: {
11
+ sessionId?: string;
12
+ filePath: string;
13
+ }): CheckFileResult;
14
+ export declare function registerCheckFile(server: McpServer, deps: ServerDeps): void;
15
+ //# sourceMappingURL=check-file.d.ts.map
@@ -0,0 +1,46 @@
1
+ import { z } from 'zod';
2
+ export function handleCheckFile(deps, input) {
3
+ const warnings = [];
4
+ let isHotspot = false;
5
+ let isBlocked = false;
6
+ const crossesModuleBoundary = false;
7
+ // Check hotspots
8
+ const hotspots = deps.store.getHotspots(deps.repoPath, 100);
9
+ const matchingHotspot = hotspots.find((h) => h.filePath === input.filePath || input.filePath.endsWith(h.filePath));
10
+ if (matchingHotspot) {
11
+ isHotspot = true;
12
+ warnings.push(`This file is a hotspot (churn: ${matchingHotspot.churnRate}, complexity: ${matchingHotspot.complexity}). Extra care recommended.`);
13
+ }
14
+ // Check blocked paths from complexity profile raw data
15
+ const profile = deps.store.getLatestComplexityProfile(deps.repoPath);
16
+ if (profile?.rawData) {
17
+ try {
18
+ const raw = JSON.parse(profile.rawData);
19
+ const blockedPaths = raw.blockedPaths ?? [];
20
+ if (blockedPaths.some((bp) => input.filePath.startsWith(bp) || input.filePath.includes(bp))) {
21
+ isBlocked = true;
22
+ warnings.push('This file is in a blocked path. Modifications may be restricted by policy.');
23
+ }
24
+ }
25
+ catch {
26
+ // raw data not parseable
27
+ }
28
+ }
29
+ return {
30
+ filePath: input.filePath,
31
+ isHotspot,
32
+ isBlocked,
33
+ crossesModuleBoundary,
34
+ warnings,
35
+ };
36
+ }
37
+ export function registerCheckFile(server, deps) {
38
+ server.tool('check_file', 'Check if a file is a hotspot, blocked, or crosses module boundaries. Advisory only.', {
39
+ sessionId: z.string().optional().describe('Session ID (defaults to active session)'),
40
+ filePath: z.string().describe('File path to check'),
41
+ }, async ({ sessionId, filePath }) => {
42
+ const result = handleCheckFile(deps, { sessionId, filePath });
43
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
44
+ });
45
+ }
46
+ //# sourceMappingURL=check-file.js.map
@@ -0,0 +1,15 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export interface CheckTaskResult {
4
+ issueNumber: number | null;
5
+ autonomyLevel: number | null;
6
+ pipelineRuns: number;
7
+ constraints: string[];
8
+ advisoryNotes: string[];
9
+ }
10
+ export declare function handleCheckTask(deps: ServerDeps, input: {
11
+ sessionId?: string;
12
+ issueNumber?: number;
13
+ }): CheckTaskResult;
14
+ export declare function registerCheckTask(server: McpServer, deps: ServerDeps): void;
15
+ //# sourceMappingURL=check-task.d.ts.map
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+ export function handleCheckTask(deps, input) {
3
+ const session = input.sessionId ? deps.sessions.get(input.sessionId) : deps.sessions.getActive();
4
+ const issueNumber = input.issueNumber ?? session?.linkedIssue ?? null;
5
+ const advisoryNotes = [];
6
+ const constraints = [];
7
+ // Look up pipeline runs for the issue
8
+ const runs = issueNumber != null ? deps.store.getPipelineRuns(issueNumber, 10) : [];
9
+ if (runs.length > 0) {
10
+ const latest = runs[0];
11
+ if (latest.status === 'running') {
12
+ advisoryNotes.push(`Pipeline run ${latest.runId} is currently running (stage: ${latest.currentStage ?? 'unknown'})`);
13
+ }
14
+ if (latest.status === 'failed') {
15
+ advisoryNotes.push(`Last pipeline run failed: ${latest.result ?? 'no details'}`);
16
+ }
17
+ }
18
+ // Autonomy level from ledger (use 'interactive' as the agent name for IDE sessions)
19
+ let autonomyLevel = null;
20
+ const ledger = deps.store.getAutonomyLedger('interactive');
21
+ if (ledger) {
22
+ autonomyLevel = ledger.currentLevel;
23
+ if (ledger.currentLevel <= 1) {
24
+ advisoryNotes.push('Low autonomy level — consider requesting human review for significant changes.');
25
+ }
26
+ }
27
+ if (issueNumber == null) {
28
+ advisoryNotes.push('No issue linked to this session — work will be unattributed.');
29
+ }
30
+ return {
31
+ issueNumber,
32
+ autonomyLevel,
33
+ pipelineRuns: runs.length,
34
+ constraints,
35
+ advisoryNotes,
36
+ };
37
+ }
38
+ export function registerCheckTask(server, deps) {
39
+ server.tool('check_task', 'Check task status, constraints, and autonomy level for the current session or issue.', {
40
+ sessionId: z.string().optional().describe('Session ID (defaults to active session)'),
41
+ issueNumber: z.number().int().optional().describe('Override issue number'),
42
+ }, async ({ sessionId, issueNumber }) => {
43
+ const result = handleCheckTask(deps, { sessionId, issueNumber });
44
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
45
+ });
46
+ }
47
+ //# sourceMappingURL=check-task.js.map
@@ -0,0 +1,15 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ declare const SECTION_NAMES: readonly ["profile", "conventions", "hotspots", "patterns", "history"];
4
+ type Section = (typeof SECTION_NAMES)[number];
5
+ export interface GetContextResult {
6
+ markdown: string;
7
+ sections: string[];
8
+ }
9
+ export declare function handleGetContext(deps: ServerDeps, input: {
10
+ sessionId?: string;
11
+ sections?: Section[];
12
+ }): GetContextResult;
13
+ export declare function registerGetContext(server: McpServer, deps: ServerDeps): void;
14
+ export {};
15
+ //# sourceMappingURL=get-context.d.ts.map
@@ -0,0 +1,94 @@
1
+ import { z } from 'zod';
2
+ import { buildCodebaseContext, formatContextForPrompt, findRelevantEpisodes, formatEpisodicContext, } from '@ai-sdlc/orchestrator';
3
+ const SECTION_NAMES = ['profile', 'conventions', 'hotspots', 'patterns', 'history'];
4
+ export function handleGetContext(deps, input) {
5
+ const session = input.sessionId ? deps.sessions.get(input.sessionId) : deps.sessions.getActive();
6
+ const requestedSections = input.sections ?? [...SECTION_NAMES];
7
+ const parts = [];
8
+ // Profile + patterns (derived from complexity profile)
9
+ if (requestedSections.includes('profile') || requestedSections.includes('patterns')) {
10
+ const profile = deps.store.getLatestComplexityProfile(deps.repoPath);
11
+ if (profile) {
12
+ // Parse raw data if available to reconstruct CodebaseProfile
13
+ const rawData = (profile.rawData ? safeJsonParse(profile.rawData) : undefined);
14
+ const hotspots = (profile.hotspots ? safeJsonParse(profile.hotspots) : []);
15
+ const patterns = (profile.architecturalPatterns ? safeJsonParse(profile.architecturalPatterns) : []);
16
+ const conventions = (profile.conventionsData ? safeJsonParse(profile.conventionsData) : []);
17
+ const codebaseProfile = {
18
+ repoPath: profile.repoPath,
19
+ score: profile.score,
20
+ filesCount: profile.filesCount ?? 0,
21
+ modulesCount: profile.modulesCount ?? 0,
22
+ dependencyCount: profile.dependencyCount ?? 0,
23
+ modules: (rawData?.modules ?? []),
24
+ moduleGraph: (rawData?.moduleGraph ?? {
25
+ modules: [],
26
+ edges: [],
27
+ externalDependencies: [],
28
+ cycles: [],
29
+ }),
30
+ architecturalPatterns: patterns,
31
+ hotspots,
32
+ conventions,
33
+ analyzedAt: profile.analyzedAt ?? new Date().toISOString(),
34
+ };
35
+ const ctx = buildCodebaseContext(codebaseProfile);
36
+ parts.push(formatContextForPrompt(ctx));
37
+ }
38
+ }
39
+ // Conventions from store
40
+ if (requestedSections.includes('conventions')) {
41
+ const conventions = deps.store.getConventions();
42
+ if (conventions.length > 0) {
43
+ const lines = ['## Project Conventions', ''];
44
+ for (const c of conventions) {
45
+ lines.push(`- **${c.category}**: ${c.pattern}`);
46
+ }
47
+ parts.push(lines.join('\n'));
48
+ }
49
+ }
50
+ // Hotspots
51
+ if (requestedSections.includes('hotspots')) {
52
+ const hotspots = deps.store.getHotspots(deps.repoPath, 20);
53
+ if (hotspots.length > 0) {
54
+ const lines = ['## File Hotspots', ''];
55
+ for (const h of hotspots.slice(0, 10)) {
56
+ lines.push(`- \`${h.filePath}\` — churn: ${h.churnRate}, complexity: ${h.complexity}`);
57
+ }
58
+ parts.push(lines.join('\n'));
59
+ }
60
+ }
61
+ // History (episodic memory)
62
+ if (requestedSections.includes('history')) {
63
+ const issueNumber = session?.linkedIssue ?? undefined;
64
+ const episodes = findRelevantEpisodes(deps.store, { issueNumber });
65
+ const formatted = formatEpisodicContext(episodes);
66
+ if (formatted) {
67
+ parts.push(formatted);
68
+ }
69
+ }
70
+ return {
71
+ markdown: parts.join('\n\n'),
72
+ sections: requestedSections,
73
+ };
74
+ }
75
+ export function registerGetContext(server, deps) {
76
+ server.tool('get_context', 'Get rich codebase context: architecture, conventions, hotspots, and history.', {
77
+ sessionId: z.string().optional().describe('Session ID (defaults to active session)'),
78
+ sections: z.array(z.enum(SECTION_NAMES)).optional().describe('Sections to include'),
79
+ }, async ({ sessionId, sections }) => {
80
+ const result = handleGetContext(deps, { sessionId, sections });
81
+ return {
82
+ content: [{ type: 'text', text: result.markdown || 'No context available.' }],
83
+ };
84
+ });
85
+ }
86
+ function safeJsonParse(s) {
87
+ try {
88
+ return JSON.parse(s);
89
+ }
90
+ catch {
91
+ return undefined;
92
+ }
93
+ }
94
+ //# sourceMappingURL=get-context.js.map
@@ -0,0 +1,10 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export declare function registerAllTools(server: McpServer, deps: ServerDeps): void;
4
+ export { handleSessionStart } from './session-start.js';
5
+ export { handleGetContext } from './get-context.js';
6
+ export { handleCheckTask } from './check-task.js';
7
+ export { handleTrackUsage } from './track-usage.js';
8
+ export { handleCheckFile } from './check-file.js';
9
+ export { handleSessionEnd } from './session-end.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,21 @@
1
+ import { registerSessionStart } from './session-start.js';
2
+ import { registerGetContext } from './get-context.js';
3
+ import { registerCheckTask } from './check-task.js';
4
+ import { registerTrackUsage } from './track-usage.js';
5
+ import { registerCheckFile } from './check-file.js';
6
+ import { registerSessionEnd } from './session-end.js';
7
+ export function registerAllTools(server, deps) {
8
+ registerSessionStart(server, deps);
9
+ registerGetContext(server, deps);
10
+ registerCheckTask(server, deps);
11
+ registerTrackUsage(server, deps);
12
+ registerCheckFile(server, deps);
13
+ registerSessionEnd(server, deps);
14
+ }
15
+ export { handleSessionStart } from './session-start.js';
16
+ export { handleGetContext } from './get-context.js';
17
+ export { handleCheckTask } from './check-task.js';
18
+ export { handleTrackUsage } from './track-usage.js';
19
+ export { handleCheckFile } from './check-file.js';
20
+ export { handleSessionEnd } from './session-end.js';
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,21 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export interface SessionEndResult {
4
+ sessionId: string;
5
+ durationMs: number;
6
+ linkedIssue: number | null;
7
+ totalCostUsd: number;
8
+ totalInputTokens: number;
9
+ totalOutputTokens: number;
10
+ byModel: Record<string, {
11
+ inputTokens: number;
12
+ outputTokens: number;
13
+ costUsd: number;
14
+ }>;
15
+ }
16
+ export declare function handleSessionEnd(deps: ServerDeps, input: {
17
+ sessionId?: string;
18
+ summary?: string;
19
+ }): SessionEndResult | null;
20
+ export declare function registerSessionEnd(server: McpServer, deps: ServerDeps): void;
21
+ //# sourceMappingURL=session-end.d.ts.map
@@ -0,0 +1,62 @@
1
+ import { z } from 'zod';
2
+ import { randomUUID } from 'node:crypto';
3
+ export function handleSessionEnd(deps, input) {
4
+ const sessionId = input.sessionId ?? deps.sessions.getActive()?.sessionId;
5
+ if (!sessionId)
6
+ return null;
7
+ const session = deps.sessions.end(sessionId);
8
+ if (!session)
9
+ return null;
10
+ const durationMs = Date.now() - new Date(session.startedAt).getTime();
11
+ // Save episodic record
12
+ deps.store.saveEpisodicRecord({
13
+ issueNumber: session.linkedIssue ?? undefined,
14
+ pipelineType: 'interactive',
15
+ outcome: 'completed',
16
+ durationMs,
17
+ agentName: session.developer,
18
+ costUsd: session.accumulatedCost.totalCostUsd,
19
+ metadata: JSON.stringify({
20
+ tool: session.tool,
21
+ summary: input.summary,
22
+ linkMethod: session.linkMethod,
23
+ byModel: session.accumulatedCost.byModel,
24
+ }),
25
+ });
26
+ // Save audit entry
27
+ deps.store.saveAuditEntry({
28
+ entryId: randomUUID(),
29
+ actor: session.developer,
30
+ action: 'session.end',
31
+ resourceType: 'session',
32
+ resourceId: session.sessionId,
33
+ detail: JSON.stringify({
34
+ durationMs,
35
+ totalCostUsd: session.accumulatedCost.totalCostUsd,
36
+ linkedIssue: session.linkedIssue,
37
+ summary: input.summary,
38
+ }),
39
+ });
40
+ return {
41
+ sessionId: session.sessionId,
42
+ durationMs,
43
+ linkedIssue: session.linkedIssue,
44
+ totalCostUsd: session.accumulatedCost.totalCostUsd,
45
+ totalInputTokens: session.accumulatedCost.totalInputTokens,
46
+ totalOutputTokens: session.accumulatedCost.totalOutputTokens,
47
+ byModel: session.accumulatedCost.byModel,
48
+ };
49
+ }
50
+ export function registerSessionEnd(server, deps) {
51
+ server.tool('session_end', 'End the current session and get a cost receipt with full breakdown.', {
52
+ sessionId: z.string().optional().describe('Session ID (defaults to active session)'),
53
+ summary: z.string().optional().describe('Brief summary of what was accomplished'),
54
+ }, async ({ sessionId, summary }) => {
55
+ const result = handleSessionEnd(deps, { sessionId, summary });
56
+ if (!result) {
57
+ return { content: [{ type: 'text', text: 'No active session to end.' }] };
58
+ }
59
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
60
+ });
61
+ }
62
+ //# sourceMappingURL=session-end.js.map
@@ -0,0 +1,15 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export interface SessionStartResult {
4
+ sessionId: string;
5
+ linkedIssue: number | null;
6
+ linkMethod: string | null;
7
+ project: string;
8
+ }
9
+ export declare function handleSessionStart(deps: ServerDeps, input: {
10
+ developer: string;
11
+ tool: string;
12
+ issueNumber?: number;
13
+ }): Promise<SessionStartResult>;
14
+ export declare function registerSessionStart(server: McpServer, deps: ServerDeps): void;
15
+ //# sourceMappingURL=session-start.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod';
2
+ import { resolveIssue } from '../issue-linker.js';
3
+ import { randomUUID } from 'node:crypto';
4
+ export async function handleSessionStart(deps, input) {
5
+ const session = deps.sessions.create({
6
+ developer: input.developer,
7
+ tool: input.tool,
8
+ repoPath: deps.repoPath,
9
+ });
10
+ const resolution = await resolveIssue(deps.repoPath, input.issueNumber);
11
+ deps.sessions.linkIssue(session.sessionId, resolution.issueNumber, resolution.method);
12
+ // Save audit entry
13
+ deps.store.saveAuditEntry({
14
+ entryId: randomUUID(),
15
+ actor: input.developer,
16
+ action: 'session.start',
17
+ resourceType: 'session',
18
+ resourceId: session.sessionId,
19
+ detail: JSON.stringify({
20
+ tool: input.tool,
21
+ linkedIssue: resolution.issueNumber,
22
+ linkMethod: resolution.method,
23
+ }),
24
+ });
25
+ return {
26
+ sessionId: session.sessionId,
27
+ linkedIssue: resolution.issueNumber,
28
+ linkMethod: resolution.method,
29
+ project: session.project,
30
+ };
31
+ }
32
+ export function registerSessionStart(server, deps) {
33
+ server.tool('session_start', 'Start a governed AI session. Returns session ID, linked issue, and project context.', {
34
+ developer: z.string().describe('Developer name or identifier'),
35
+ tool: z.enum(['claude-code', 'copilot', 'cursor', 'other']).describe('AI tool in use'),
36
+ issueNumber: z.number().int().optional().describe('Explicit issue number to link'),
37
+ }, async ({ developer, tool, issueNumber }) => {
38
+ const result = await handleSessionStart(deps, { developer, tool, issueNumber });
39
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
40
+ });
41
+ }
42
+ //# sourceMappingURL=session-start.js.map
@@ -0,0 +1,16 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerDeps } from '../types.js';
3
+ export interface TrackUsageResult {
4
+ entryCostUsd: number;
5
+ cumulativeCostUsd: number;
6
+ cumulativeInputTokens: number;
7
+ cumulativeOutputTokens: number;
8
+ }
9
+ export declare function handleTrackUsage(deps: ServerDeps, input: {
10
+ sessionId?: string;
11
+ model: string;
12
+ inputTokens: number;
13
+ outputTokens: number;
14
+ }): TrackUsageResult;
15
+ export declare function registerTrackUsage(server: McpServer, deps: ServerDeps): void;
16
+ //# sourceMappingURL=track-usage.d.ts.map
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+ import { CostTracker } from '@ai-sdlc/orchestrator';
3
+ export function handleTrackUsage(deps, input) {
4
+ const session = input.sessionId ? deps.sessions.get(input.sessionId) : deps.sessions.getActive();
5
+ const costUsd = CostTracker.computeCost(input.inputTokens, input.outputTokens, input.model);
6
+ // Record in the cost ledger
7
+ deps.costTracker.recordCost({
8
+ runId: session?.sessionId ?? 'unattributed',
9
+ agentName: session?.developer ?? 'unknown',
10
+ pipelineType: 'interactive',
11
+ model: input.model,
12
+ inputTokens: input.inputTokens,
13
+ outputTokens: input.outputTokens,
14
+ issueNumber: session?.linkedIssue ?? undefined,
15
+ });
16
+ // Accumulate in the session
17
+ if (session) {
18
+ deps.sessions.addUsage(session.sessionId, {
19
+ model: input.model,
20
+ inputTokens: input.inputTokens,
21
+ outputTokens: input.outputTokens,
22
+ costUsd,
23
+ });
24
+ }
25
+ return {
26
+ entryCostUsd: costUsd,
27
+ cumulativeCostUsd: session?.accumulatedCost.totalCostUsd ?? costUsd,
28
+ cumulativeInputTokens: session?.accumulatedCost.totalInputTokens ?? input.inputTokens,
29
+ cumulativeOutputTokens: session?.accumulatedCost.totalOutputTokens ?? input.outputTokens,
30
+ };
31
+ }
32
+ export function registerTrackUsage(server, deps) {
33
+ server.tool('track_usage', 'Record token usage for cost tracking and budget monitoring.', {
34
+ sessionId: z.string().optional().describe('Session ID (defaults to active session)'),
35
+ model: z.string().describe('Model name (e.g. claude-opus-4-6)'),
36
+ inputTokens: z.number().int().min(0).describe('Number of input tokens'),
37
+ outputTokens: z.number().int().min(0).describe('Number of output tokens'),
38
+ }, async ({ sessionId, model, inputTokens, outputTokens }) => {
39
+ const result = handleTrackUsage(deps, { sessionId, model, inputTokens, outputTokens });
40
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
41
+ });
42
+ }
43
+ //# sourceMappingURL=track-usage.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Shared dependency injection types for tools and resources.
3
+ */
4
+ import type { StateStore } from '@ai-sdlc/orchestrator/state';
5
+ import type { CostTracker } from '@ai-sdlc/orchestrator';
6
+ import type { SessionManager } from './session.js';
7
+ export interface ServerDeps {
8
+ store: StateStore;
9
+ costTracker: CostTracker;
10
+ sessions: SessionManager;
11
+ repoPath: string;
12
+ }
13
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared dependency injection types for tools and resources.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@ai-sdlc/mcp-advisor",
3
+ "version": "0.1.0",
4
+ "description": "MCP server bridging human-directed AI sessions with the AI-SDLC orchestrator",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/ai-sdlc-framework/ai-sdlc.git",
9
+ "directory": "mcp-advisor"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public",
13
+ "registry": "https://registry.npmjs.org/"
14
+ },
15
+ "files": ["dist", "!dist/**/*.test.*", "!dist/**/*.map"],
16
+ "type": "module",
17
+ "main": "dist/index.js",
18
+ "types": "dist/index.d.ts",
19
+ "bin": {
20
+ "ai-sdlc-mcp-advisor": "dist/bin.js"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js"
26
+ }
27
+ },
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "clean": "rm -rf dist *.tsbuildinfo",
31
+ "test": "vitest run"
32
+ },
33
+ "dependencies": {
34
+ "@ai-sdlc/orchestrator": "workspace:*",
35
+ "@modelcontextprotocol/sdk": "^1.26.0",
36
+ "better-sqlite3": "^11.0.0",
37
+ "zod": "^3.23.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/better-sqlite3": "^7.6.0",
41
+ "@types/node": "^22.0.0",
42
+ "typescript": "^5.7.0",
43
+ "vitest": "^3.0.0"
44
+ }
45
+ }