Skip to content

Commit 80d3611

Browse files
committed
feat(angular-mcp-server): add save location for contracts
1 parent 619fa8a commit 80d3611

File tree

4 files changed

+78
-70
lines changed

4 files changed

+78
-70
lines changed

packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/build-component-contract.tool.ts

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,36 @@ import {
55
import { buildComponentContractSchema } from './models/schema.js';
66
import { buildComponentContract } from './utils/build-contract.js';
77
import {
8-
saveContract,
98
generateContractSummary,
109
} from '../shared/utils/contract-file-ops.js';
1110
import { ContractResult } from './models/types.js';
1211
import { resolveCrossPlatformPath } from '../../shared/utils/cross-platform-path.js';
1312

1413
interface BuildComponentContractOptions extends BaseHandlerOptions {
15-
directory: string;
14+
saveLocation: string;
1615
templateFile: string;
1716
styleFile: string;
1817
typescriptFile: string;
19-
dsComponentName: string;
18+
dsComponentName?: string;
2019
}
2120

2221
export const buildComponentContractHandler = createHandler<
2322
BuildComponentContractOptions,
2423
ContractResult
2524
>(
2625
buildComponentContractSchema.name,
27-
async (params, { cwd, workspaceRoot }) => {
26+
async (params, { cwd, workspaceRoot: _workspaceRoot }) => {
2827
const {
29-
directory,
28+
saveLocation,
3029
templateFile,
3130
styleFile,
3231
typescriptFile,
33-
dsComponentName,
32+
dsComponentName = '',
3433
} = params;
3534

36-
const effectiveTemplatePath = resolveCrossPlatformPath(
37-
directory,
38-
templateFile,
39-
);
40-
const effectiveScssPath = resolveCrossPlatformPath(directory, styleFile);
41-
const effectiveTypescriptPath = resolveCrossPlatformPath(
42-
directory,
43-
typescriptFile,
44-
);
35+
const effectiveTemplatePath = resolveCrossPlatformPath(cwd, templateFile);
36+
const effectiveScssPath = resolveCrossPlatformPath(cwd, styleFile);
37+
const effectiveTypescriptPath = resolveCrossPlatformPath(cwd, typescriptFile);
4538

4639
const contract = await buildComponentContract(
4740
effectiveTemplatePath,
@@ -50,18 +43,40 @@ export const buildComponentContractHandler = createHandler<
5043
effectiveTypescriptPath,
5144
);
5245

53-
const { contractFilePath, hash } = await saveContract(
46+
// Custom save logic using the provided saveLocation
47+
const contractString = JSON.stringify(contract, null, 2);
48+
const hash = require('node:crypto').createHash('sha256').update(contractString).digest('hex');
49+
50+
const effectiveSaveLocation = resolveCrossPlatformPath(cwd, saveLocation);
51+
52+
// Ensure directory exists
53+
const { mkdir, writeFile } = await import('node:fs/promises');
54+
const { dirname } = await import('node:path');
55+
await mkdir(dirname(effectiveSaveLocation), { recursive: true });
56+
57+
const contractData = {
5458
contract,
55-
workspaceRoot,
56-
effectiveTemplatePath,
57-
effectiveScssPath,
58-
cwd,
59-
dsComponentName,
59+
hash: `sha256-${hash}`,
60+
metadata: {
61+
templatePath: effectiveTemplatePath,
62+
scssPath: effectiveScssPath,
63+
typescriptPath: effectiveTypescriptPath,
64+
timestamp: new Date().toISOString(),
65+
dsComponentName,
66+
},
67+
};
68+
69+
await writeFile(
70+
effectiveSaveLocation,
71+
JSON.stringify(contractData, null, 2),
72+
'utf-8',
6073
);
6174

75+
const contractFilePath = effectiveSaveLocation;
76+
6277
return {
6378
contract,
64-
hash,
79+
hash: `sha256-${hash}`,
6580
contractFilePath,
6681
};
6782
},

packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/models/schema.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { ToolSchemaOptions } from '@push-based/models';
22
import {
33
COMMON_ANNOTATIONS,
4-
createProjectAnalysisSchema,
54
} from '../../../shared';
65

76
/**
@@ -12,32 +11,38 @@ export const buildComponentContractSchema: ToolSchemaOptions = {
1211
description:
1312
"Generate a static surface contract for a component's template and SCSS.",
1413
inputSchema: {
15-
...createProjectAnalysisSchema({
14+
type: 'object',
15+
properties: {
16+
saveLocation: {
17+
type: 'string',
18+
description:
19+
'Path where to save the contract file. Supports both absolute and relative paths.',
20+
},
1621
templateFile: {
1722
type: 'string',
1823
description:
19-
'File name of the component template file (.html) or TypeScript component file (.ts) for inline templates',
24+
'Path to the component template file (.html) or TypeScript component file (.ts) for inline templates. Supports both absolute and relative paths.',
2025
},
2126
styleFile: {
2227
type: 'string',
2328
description:
24-
'File name of the component style file (.scss, .sass, .less, .css)',
29+
'Path to the component style file (.scss, .sass, .less, .css). Supports both absolute and relative paths.',
2530
},
2631
typescriptFile: {
2732
type: 'string',
28-
description: 'File name of the TypeScript component file (.ts)',
33+
description: 'Path to the TypeScript component file (.ts). Supports both absolute and relative paths.',
2934
},
3035
dsComponentName: {
3136
type: 'string',
3237
description: 'The name of the design system component being used',
38+
default: '',
3339
},
34-
}),
40+
},
3541
required: [
36-
'directory',
42+
'saveLocation',
3743
'templateFile',
3844
'styleFile',
3945
'typescriptFile',
40-
'dsComponentName',
4146
],
4247
},
4348
annotations: {

packages/angular-mcp-server/src/lib/tools/ds/component-contract/diff/diff-component-contract.tool.ts

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import {
99
import { diffComponentContractSchema } from './models/schema.js';
1010
import type { DomPathDictionary } from '../shared/models/types.js';
1111
import { loadContract } from '../shared/utils/contract-file-ops.js';
12-
import { componentNameToKebabCase } from '../../shared/utils/component-validation.js';
13-
import { basename } from 'node:path';
1412
import {
1513
consolidateAndPruneRemoveOperationsWithDeduplication,
1614
groupChangesByDomainAndType,
@@ -20,10 +18,10 @@ import { writeFile, mkdir } from 'node:fs/promises';
2018
import diff from 'microdiff';
2119

2220
interface DiffComponentContractOptions extends BaseHandlerOptions {
23-
directory: string;
21+
saveLocation: string;
2422
contractBeforePath: string;
2523
contractAfterPath: string;
26-
dsComponentName: string;
24+
dsComponentName?: string;
2725
}
2826

2927
export const diffComponentContractHandler = createHandler<
@@ -34,15 +32,11 @@ export const diffComponentContractHandler = createHandler<
3432
}
3533
>(
3634
diffComponentContractSchema.name,
37-
async (params, { workspaceRoot }) => {
38-
const effectiveBeforePath = resolveCrossPlatformPath(
39-
params.directory,
40-
params.contractBeforePath,
41-
);
42-
const effectiveAfterPath = resolveCrossPlatformPath(
43-
params.directory,
44-
params.contractAfterPath,
45-
);
35+
async (params, { cwd, workspaceRoot }) => {
36+
const { saveLocation, contractBeforePath, contractAfterPath, dsComponentName = '' } = params;
37+
38+
const effectiveBeforePath = resolveCrossPlatformPath(cwd, contractBeforePath);
39+
const effectiveAfterPath = resolveCrossPlatformPath(cwd, contractAfterPath);
4640

4741
const contractBefore = await loadContract(effectiveBeforePath);
4842
const contractAfter = await loadContract(effectiveAfterPath);
@@ -57,7 +51,7 @@ export const diffComponentContractHandler = createHandler<
5751
const diffData = {
5852
before: effectiveBeforePath,
5953
after: effectiveAfterPath,
60-
dsComponentName: params.dsComponentName,
54+
dsComponentName,
6155
timestamp: new Date().toISOString(),
6256
domPathDictionary: domPathDict.paths,
6357
changes: groupedChanges,
@@ -67,25 +61,14 @@ export const diffComponentContractHandler = createHandler<
6761
// Normalize absolute paths to relative paths for portability
6862
const normalizedDiffData = normalizePathsInObject(diffData, workspaceRoot);
6963

70-
// Create component-specific diffs directory
71-
const componentKebab = componentNameToKebabCase(params.dsComponentName);
72-
const diffDir = resolveCrossPlatformPath(
73-
workspaceRoot,
74-
`.cursor/tmp/contracts/${componentKebab}/diffs`,
75-
);
76-
await mkdir(diffDir, { recursive: true });
77-
78-
// Generate simplified diff filename: diff-{componentName}-{timestamp}.json
79-
const componentBaseName = basename(
80-
effectiveBeforePath,
81-
'.contract.json',
82-
).split('-')[0]; // Extract component name before timestamp
83-
const timestamp = new Date()
84-
.toISOString()
85-
.replace(/[-:]/g, '')
86-
.replace(/\.\d+Z$/, 'Z');
87-
const diffFileName = `diff-${componentBaseName}-${timestamp}.json`;
88-
const diffFilePath = resolveCrossPlatformPath(diffDir, diffFileName);
64+
// Use the provided saveLocation
65+
const effectiveSaveLocation = resolveCrossPlatformPath(cwd, saveLocation);
66+
67+
// Ensure directory exists
68+
const { dirname } = await import('node:path');
69+
await mkdir(dirname(effectiveSaveLocation), { recursive: true });
70+
71+
const diffFilePath = effectiveSaveLocation;
8972

9073
const formattedJson = JSON.stringify(normalizedDiffData, null, 2);
9174
await writeFile(diffFilePath, formattedJson, 'utf-8');

packages/angular-mcp-server/src/lib/tools/ds/component-contract/diff/models/schema.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ToolSchemaOptions } from '@push-based/models';
22
import {
3-
createProjectAnalysisSchema,
43
COMMON_ANNOTATIONS,
54
} from '../../../shared';
65

@@ -12,25 +11,31 @@ export const diffComponentContractSchema: ToolSchemaOptions = {
1211
description:
1312
'Compare before/after contracts for parity and surface breaking changes.',
1413
inputSchema: {
15-
...createProjectAnalysisSchema({
14+
type: 'object',
15+
properties: {
16+
saveLocation: {
17+
type: 'string',
18+
description:
19+
'Path where to save the diff result file. Supports both absolute and relative paths.',
20+
},
1621
contractBeforePath: {
1722
type: 'string',
18-
description: 'Path to the contract file before refactoring',
23+
description: 'Path to the contract file before refactoring. Supports both absolute and relative paths.',
1924
},
2025
contractAfterPath: {
2126
type: 'string',
22-
description: 'Path to the contract file after refactoring',
27+
description: 'Path to the contract file after refactoring. Supports both absolute and relative paths.',
2328
},
2429
dsComponentName: {
2530
type: 'string',
2631
description: 'The name of the design system component being used',
32+
default: '',
2733
},
28-
}),
34+
},
2935
required: [
30-
'directory',
36+
'saveLocation',
3137
'contractBeforePath',
3238
'contractAfterPath',
33-
'dsComponentName',
3439
],
3540
},
3641
annotations: {

0 commit comments

Comments
 (0)