@mwturnbull/papi-mcp 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.
- package/LICENSE +21 -0
- package/README.md +320 -0
- package/dist/config/assembler.d.ts +6 -0
- package/dist/config/assembler.d.ts.map +1 -0
- package/dist/config/assembler.js +56 -0
- package/dist/config/assembler.js.map +1 -0
- package/dist/config/parser.d.ts +6 -0
- package/dist/config/parser.d.ts.map +1 -0
- package/dist/config/parser.js +79 -0
- package/dist/config/parser.js.map +1 -0
- package/dist/config/snippets.d.ts +12 -0
- package/dist/config/snippets.d.ts.map +1 -0
- package/dist/config/snippets.js +75 -0
- package/dist/config/snippets.js.map +1 -0
- package/dist/git/git-ops.d.ts +16 -0
- package/dist/git/git-ops.d.ts.map +1 -0
- package/dist/git/git-ops.js +64 -0
- package/dist/git/git-ops.js.map +1 -0
- package/dist/git/github.d.ts +12 -0
- package/dist/git/github.d.ts.map +1 -0
- package/dist/git/github.js +83 -0
- package/dist/git/github.js.map +1 -0
- package/dist/git/gitlab.d.ts +11 -0
- package/dist/git/gitlab.d.ts.map +1 -0
- package/dist/git/gitlab.js +65 -0
- package/dist/git/gitlab.js.map +1 -0
- package/dist/git/provider.d.ts +33 -0
- package/dist/git/provider.d.ts.map +1 -0
- package/dist/git/provider.js +49 -0
- package/dist/git/provider.js.map +1 -0
- package/dist/papi/auth.d.ts +12 -0
- package/dist/papi/auth.d.ts.map +1 -0
- package/dist/papi/auth.js +74 -0
- package/dist/papi/auth.js.map +1 -0
- package/dist/papi/client.d.ts +19 -0
- package/dist/papi/client.d.ts.map +1 -0
- package/dist/papi/client.js +115 -0
- package/dist/papi/client.js.map +1 -0
- package/dist/papi/types.d.ts +487 -0
- package/dist/papi/types.d.ts.map +1 -0
- package/dist/papi/types.js +94 -0
- package/dist/papi/types.js.map +1 -0
- package/dist/redaction/redactor.d.ts +25 -0
- package/dist/redaction/redactor.d.ts.map +1 -0
- package/dist/redaction/redactor.js +128 -0
- package/dist/redaction/redactor.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +219 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/activate.d.ts +16 -0
- package/dist/tools/activate.d.ts.map +1 -0
- package/dist/tools/activate.js +59 -0
- package/dist/tools/activate.js.map +1 -0
- package/dist/tools/diff-configs.d.ts +3 -0
- package/dist/tools/diff-configs.d.ts.map +1 -0
- package/dist/tools/diff-configs.js +152 -0
- package/dist/tools/diff-configs.js.map +1 -0
- package/dist/tools/get-property-config.d.ts +9 -0
- package/dist/tools/get-property-config.d.ts.map +1 -0
- package/dist/tools/get-property-config.js +53 -0
- package/dist/tools/get-property-config.js.map +1 -0
- package/dist/tools/pipeline.d.ts +11 -0
- package/dist/tools/pipeline.d.ts.map +1 -0
- package/dist/tools/pipeline.js +9 -0
- package/dist/tools/pipeline.js.map +1 -0
- package/dist/tools/sync-property.d.ts +9 -0
- package/dist/tools/sync-property.d.ts.map +1 -0
- package/dist/tools/sync-property.js +65 -0
- package/dist/tools/sync-property.js.map +1 -0
- package/dist/tools/write-snippet.d.ts +30 -0
- package/dist/tools/write-snippet.d.ts.map +1 -0
- package/dist/tools/write-snippet.js +92 -0
- package/dist/tools/write-snippet.js.map +1 -0
- package/dist/validation/local.d.ts +3 -0
- package/dist/validation/local.d.ts.map +1 -0
- package/dist/validation/local.js +290 -0
- package/dist/validation/local.js.map +1 -0
- package/dist/validation/papi.d.ts +6 -0
- package/dist/validation/papi.d.ts.map +1 -0
- package/dist/validation/papi.js +59 -0
- package/dist/validation/papi.js.map +1 -0
- package/package.json +55 -0
- package/src/skill/system-prompt.md +205 -0
- package/templates/.gitkeep +0 -0
- package/templates/CLAUDE.md +115 -0
- package/templates/copilot-instructions.md +78 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-configs.js","sourceRoot":"","sources":["../../src/tools/diff-configs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface GetPropertyConfigParams {
|
|
2
|
+
propertyId?: string;
|
|
3
|
+
version?: number;
|
|
4
|
+
repoPath?: string;
|
|
5
|
+
edgercPath?: string;
|
|
6
|
+
edgercSection?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function getPropertyConfig(params: GetPropertyConfigParams): Promise<string>;
|
|
9
|
+
//# sourceMappingURL=get-property-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-property-config.d.ts","sourceRoot":"","sources":["../../src/tools/get-property-config.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCxF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { PapiClient } from '../papi/client.js';
|
|
4
|
+
import { resolveCredentials } from '../papi/auth.js';
|
|
5
|
+
import { findPmDirectory } from '../config/snippets.js';
|
|
6
|
+
export async function getPropertyConfig(params) {
|
|
7
|
+
const { version, edgercPath, edgercSection } = params;
|
|
8
|
+
let { propertyId } = params;
|
|
9
|
+
// Auto-detect propertyId from envInfo.json if repoPath provided
|
|
10
|
+
if (!propertyId && params.repoPath) {
|
|
11
|
+
propertyId = await detectPropertyId(params.repoPath);
|
|
12
|
+
}
|
|
13
|
+
if (!propertyId) {
|
|
14
|
+
throw new Error('Either propertyId or repoPath must be provided to identify the property');
|
|
15
|
+
}
|
|
16
|
+
const credentials = await resolveCredentials({ edgercPath, section: edgercSection });
|
|
17
|
+
const client = new PapiClient(credentials);
|
|
18
|
+
const ruleTree = await client.getRuleTree(propertyId, version);
|
|
19
|
+
// Best-effort: store the etag for future sync comparisons
|
|
20
|
+
if (params.repoPath && ruleTree.etag) {
|
|
21
|
+
try {
|
|
22
|
+
const pmDir = await findPmDirectory(params.repoPath);
|
|
23
|
+
const envInfoPath = resolve(pmDir, 'envInfo.json');
|
|
24
|
+
const envContent = await readFile(envInfoPath, 'utf-8');
|
|
25
|
+
const envInfo = JSON.parse(envContent);
|
|
26
|
+
envInfo.lastKnownRuleTreeEtag = ruleTree.etag;
|
|
27
|
+
await writeFile(envInfoPath, JSON.stringify(envInfo, null, 2) + '\n', 'utf-8');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Don't fail the tool call if writing the etag fails
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return JSON.stringify(ruleTree, null, 2);
|
|
34
|
+
}
|
|
35
|
+
async function detectPropertyId(repoPath) {
|
|
36
|
+
const pmDir = await findPmDirectory(repoPath);
|
|
37
|
+
const envInfoPath = resolve(pmDir, 'envInfo.json');
|
|
38
|
+
try {
|
|
39
|
+
const content = await readFile(envInfoPath, 'utf-8');
|
|
40
|
+
const envInfo = JSON.parse(content);
|
|
41
|
+
if (!envInfo.propertyId) {
|
|
42
|
+
throw new Error('propertyId field not found in envInfo.json');
|
|
43
|
+
}
|
|
44
|
+
return String(envInfo.propertyId);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error.code === 'ENOENT') {
|
|
48
|
+
throw new Error(`envInfo.json not found at ${envInfoPath}`);
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=get-property-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-property-config.js","sourceRoot":"","sources":["../../src/tools/get-property-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TriggerPipelineParams {
|
|
2
|
+
repoPath: string;
|
|
3
|
+
branch?: string;
|
|
4
|
+
variables?: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
export declare function triggerPipeline(params: TriggerPipelineParams): Promise<{
|
|
7
|
+
url: string;
|
|
8
|
+
id: number | string;
|
|
9
|
+
provider: string;
|
|
10
|
+
}>;
|
|
11
|
+
//# sourceMappingURL=pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/tools/pipeline.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAMD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createProvider } from '../git/provider.js';
|
|
2
|
+
import { getCurrentBranch } from '../git/git-ops.js';
|
|
3
|
+
export async function triggerPipeline(params) {
|
|
4
|
+
const { repoPath, variables } = params;
|
|
5
|
+
const branch = params.branch ?? await getCurrentBranch(repoPath);
|
|
6
|
+
const provider = await createProvider(repoPath);
|
|
7
|
+
return provider.triggerPipeline({ branch, variables });
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/tools/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAQrD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAA6B;IAKjE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;IAEhD,OAAO,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SyncResult } from '../papi/types.js';
|
|
2
|
+
export interface SyncPropertyParams {
|
|
3
|
+
repoPath: string;
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
edgercPath?: string;
|
|
6
|
+
edgercSection?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function syncProperty(params: SyncPropertyParams): Promise<SyncResult>;
|
|
9
|
+
//# sourceMappingURL=sync-property.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-property.d.ts","sourceRoot":"","sources":["../../src/tools/sync-property.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAW,UAAU,EAAc,MAAM,kBAAkB,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CA8DlF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { PapiClient } from '../papi/client.js';
|
|
4
|
+
import { resolveCredentials } from '../papi/auth.js';
|
|
5
|
+
import { findPmDirectory } from '../config/snippets.js';
|
|
6
|
+
export async function syncProperty(params) {
|
|
7
|
+
const { repoPath, edgercPath, edgercSection } = params;
|
|
8
|
+
// Read local envInfo
|
|
9
|
+
const pmDir = await findPmDirectory(repoPath);
|
|
10
|
+
const envInfoPath = resolve(pmDir, 'envInfo.json');
|
|
11
|
+
let envInfo;
|
|
12
|
+
try {
|
|
13
|
+
const content = await readFile(envInfoPath, 'utf-8');
|
|
14
|
+
envInfo = JSON.parse(content);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (error.code === 'ENOENT') {
|
|
18
|
+
throw new Error(`envInfo.json not found at ${envInfoPath}`);
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
// Fetch remote version info
|
|
23
|
+
const credentials = await resolveCredentials({ edgercPath, section: edgercSection });
|
|
24
|
+
const client = new PapiClient(credentials);
|
|
25
|
+
const propertyId = String(envInfo.propertyId);
|
|
26
|
+
const versions = await client.getPropertyVersions(propertyId);
|
|
27
|
+
if (versions.length === 0) {
|
|
28
|
+
throw new Error(`No versions found for property ${propertyId}`);
|
|
29
|
+
}
|
|
30
|
+
// Get latest remote version
|
|
31
|
+
const sorted = [...versions].sort((a, b) => b.propertyVersion - a.propertyVersion);
|
|
32
|
+
const latestRemote = sorted[0];
|
|
33
|
+
// Determine sync status by version number comparison
|
|
34
|
+
const localVersion = envInfo.latestVersionInfo.propertyVersion;
|
|
35
|
+
let status;
|
|
36
|
+
let localEtag;
|
|
37
|
+
let remoteEtag;
|
|
38
|
+
if (localVersion === latestRemote.propertyVersion) {
|
|
39
|
+
// Same version — check for content drift via etag comparison
|
|
40
|
+
localEtag = envInfo.lastKnownRuleTreeEtag;
|
|
41
|
+
if (localEtag) {
|
|
42
|
+
const remoteRuleTree = await client.getRuleTree(propertyId, latestRemote.propertyVersion);
|
|
43
|
+
remoteEtag = remoteRuleTree.etag;
|
|
44
|
+
status = localEtag === remoteEtag ? 'in-sync' : 'content-modified';
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// No local etag stored — can't compare, assume OK
|
|
48
|
+
status = 'in-sync';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (localVersion < latestRemote.propertyVersion) {
|
|
52
|
+
status = 'remote-ahead';
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
status = 'local-ahead';
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
status,
|
|
59
|
+
localVersion,
|
|
60
|
+
remoteVersion: latestRemote.propertyVersion,
|
|
61
|
+
localEtag,
|
|
62
|
+
remoteEtag,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=sync-property.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-property.js","sourceRoot":"","sources":["../../src/tools/sync-property.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { PapiRule } from '../papi/types.js';
|
|
2
|
+
export interface WriteSnippetParams {
|
|
3
|
+
repoPath: string;
|
|
4
|
+
snippetName: string;
|
|
5
|
+
content: string;
|
|
6
|
+
addToMainJson?: boolean;
|
|
7
|
+
position?: 'beginning' | 'end';
|
|
8
|
+
}
|
|
9
|
+
export interface WriteSnippetResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
path: string;
|
|
12
|
+
addedToMain: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function writeSnippet(params: WriteSnippetParams): Promise<WriteSnippetResult>;
|
|
15
|
+
export interface ApplyBehaviorParams {
|
|
16
|
+
repoPath: string;
|
|
17
|
+
snippetName: string;
|
|
18
|
+
behavior: string;
|
|
19
|
+
criteria?: string;
|
|
20
|
+
criteriaMustSatisfy?: 'all' | 'any';
|
|
21
|
+
}
|
|
22
|
+
export interface ApplyBehaviorResult {
|
|
23
|
+
success: boolean;
|
|
24
|
+
action: 'added' | 'replaced';
|
|
25
|
+
snippetPath: string;
|
|
26
|
+
before: PapiRule;
|
|
27
|
+
after: PapiRule;
|
|
28
|
+
}
|
|
29
|
+
export declare function applyBehavior(params: ApplyBehaviorParams): Promise<ApplyBehaviorResult>;
|
|
30
|
+
//# sourceMappingURL=write-snippet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-snippet.d.ts","sourceRoot":"","sources":["../../src/tools/write-snippet.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,kBAAkB,CAAC;AAK/D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAiD1F;AAID,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,GAAG,UAAU,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAiD7F"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { findPmDirectory, readSnippet } from '../config/snippets.js';
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
const REQUIRED_FIELDS = ['name', 'children', 'behaviors', 'criteria', 'criteriaMustSatisfy'];
|
|
6
|
+
export async function writeSnippet(params) {
|
|
7
|
+
const { repoPath, snippetName, content, addToMainJson = false, position = 'end' } = params;
|
|
8
|
+
// Parse and validate content
|
|
9
|
+
let rule;
|
|
10
|
+
try {
|
|
11
|
+
rule = JSON.parse(content);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new Error(`Invalid JSON in snippet content: ${content.slice(0, 100)}...`);
|
|
15
|
+
}
|
|
16
|
+
// Validate required PapiRule fields
|
|
17
|
+
for (const field of REQUIRED_FIELDS) {
|
|
18
|
+
if (!(field in rule)) {
|
|
19
|
+
throw new Error(`Missing required field '${field}' in snippet content`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Write snippet file
|
|
23
|
+
const pmDir = await findPmDirectory(repoPath);
|
|
24
|
+
const snippetPath = resolve(pmDir, 'config-snippets', snippetName);
|
|
25
|
+
await writeFile(snippetPath, JSON.stringify(rule, null, 2) + '\n', 'utf-8');
|
|
26
|
+
// Optionally add #include: to main.json
|
|
27
|
+
let addedToMain = false;
|
|
28
|
+
if (addToMainJson) {
|
|
29
|
+
const mainPath = resolve(pmDir, 'config-snippets', 'main.json');
|
|
30
|
+
const mainContent = await readFile(mainPath, 'utf-8');
|
|
31
|
+
const main = JSON.parse(mainContent);
|
|
32
|
+
const includeDirective = `#include:${snippetName}`;
|
|
33
|
+
// Check if already included
|
|
34
|
+
const alreadyIncluded = main.rules.children.some(c => typeof c === 'string' && c === includeDirective);
|
|
35
|
+
if (!alreadyIncluded) {
|
|
36
|
+
if (position === 'beginning') {
|
|
37
|
+
main.rules.children.unshift(includeDirective);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
main.rules.children.push(includeDirective);
|
|
41
|
+
}
|
|
42
|
+
await writeFile(mainPath, JSON.stringify(main, null, 2) + '\n', 'utf-8');
|
|
43
|
+
addedToMain = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { success: true, path: snippetPath, addedToMain };
|
|
47
|
+
}
|
|
48
|
+
export async function applyBehavior(params) {
|
|
49
|
+
const { repoPath, snippetName, criteriaMustSatisfy } = params;
|
|
50
|
+
// Parse behavior
|
|
51
|
+
let behavior;
|
|
52
|
+
try {
|
|
53
|
+
behavior = JSON.parse(params.behavior);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new Error('Invalid JSON in behavior parameter');
|
|
57
|
+
}
|
|
58
|
+
// Read existing snippet
|
|
59
|
+
const snippet = await readSnippet(repoPath, snippetName);
|
|
60
|
+
const before = structuredClone(snippet);
|
|
61
|
+
// Find existing behavior by name
|
|
62
|
+
const existingIdx = snippet.behaviors.findIndex(b => b.name === behavior.name);
|
|
63
|
+
let action;
|
|
64
|
+
if (existingIdx >= 0) {
|
|
65
|
+
// Replace in-place
|
|
66
|
+
snippet.behaviors[existingIdx] = behavior;
|
|
67
|
+
action = 'replaced';
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Append
|
|
71
|
+
snippet.behaviors.push(behavior);
|
|
72
|
+
action = 'added';
|
|
73
|
+
}
|
|
74
|
+
// Optionally update criteria
|
|
75
|
+
if (params.criteria) {
|
|
76
|
+
snippet.criteria = JSON.parse(params.criteria);
|
|
77
|
+
}
|
|
78
|
+
if (criteriaMustSatisfy) {
|
|
79
|
+
snippet.criteriaMustSatisfy = criteriaMustSatisfy;
|
|
80
|
+
}
|
|
81
|
+
// Validate and write
|
|
82
|
+
for (const field of REQUIRED_FIELDS) {
|
|
83
|
+
if (!(field in snippet)) {
|
|
84
|
+
throw new Error(`Modified snippet missing required field '${field}'`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const pmDir = await findPmDirectory(repoPath);
|
|
88
|
+
const snippetPath = resolve(pmDir, 'config-snippets', snippetName);
|
|
89
|
+
await writeFile(snippetPath, JSON.stringify(snippet, null, 2) + '\n', 'utf-8');
|
|
90
|
+
return { success: true, action, snippetPath, before, after: snippet };
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=write-snippet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-snippet.js","sourceRoot":"","sources":["../../src/tools/write-snippet.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/validation/local.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAA6B,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpF,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqB/E"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { findPmDirectory } from '../config/snippets.js';
|
|
4
|
+
export async function validateLocal(repoPath) {
|
|
5
|
+
const pmDir = await findPmDirectory(repoPath);
|
|
6
|
+
const snippetsDir = resolve(pmDir, 'config-snippets');
|
|
7
|
+
const checks = [];
|
|
8
|
+
// Run all 7 checks independently
|
|
9
|
+
checks.push(...await checkJsonSyntax(snippetsDir));
|
|
10
|
+
checks.push(...await checkRequiredFields(snippetsDir));
|
|
11
|
+
checks.push(...await checkIncludeResolution(snippetsDir));
|
|
12
|
+
checks.push(...await checkOrphans(snippetsDir));
|
|
13
|
+
checks.push(...await checkVariableReferences(snippetsDir, pmDir));
|
|
14
|
+
checks.push(...await checkDuplicateRuleNames(snippetsDir));
|
|
15
|
+
checks.push(...await checkNoopRules(snippetsDir));
|
|
16
|
+
return {
|
|
17
|
+
valid: checks.filter(c => c.severity === 'error').length === 0,
|
|
18
|
+
errors: checks.filter(c => c.severity === 'error'),
|
|
19
|
+
warnings: checks.filter(c => c.severity === 'warning'),
|
|
20
|
+
infos: checks.filter(c => c.severity === 'info'),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Check 1: JSON syntax — every .json in config-snippets/ must parse (severity: error)
|
|
24
|
+
async function checkJsonSyntax(snippetsDir) {
|
|
25
|
+
const checks = [];
|
|
26
|
+
const files = await readdir(snippetsDir);
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
if (!file.endsWith('.json'))
|
|
29
|
+
continue;
|
|
30
|
+
try {
|
|
31
|
+
const content = await readFile(resolve(snippetsDir, file), 'utf-8');
|
|
32
|
+
JSON.parse(content);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
checks.push({
|
|
36
|
+
check: 'json-syntax',
|
|
37
|
+
severity: 'error',
|
|
38
|
+
message: `Invalid JSON in ${file}: ${e.message}`,
|
|
39
|
+
location: file,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return checks;
|
|
44
|
+
}
|
|
45
|
+
// Check 2: Required fields — each rule must have: name, children, behaviors, criteria, criteriaMustSatisfy (severity: error)
|
|
46
|
+
async function checkRequiredFields(snippetsDir) {
|
|
47
|
+
const checks = [];
|
|
48
|
+
const required = ['name', 'children', 'behaviors', 'criteria', 'criteriaMustSatisfy'];
|
|
49
|
+
const files = await readdir(snippetsDir);
|
|
50
|
+
for (const file of files) {
|
|
51
|
+
if (!file.endsWith('.json'))
|
|
52
|
+
continue;
|
|
53
|
+
try {
|
|
54
|
+
const content = await readFile(resolve(snippetsDir, file), 'utf-8');
|
|
55
|
+
const parsed = JSON.parse(content);
|
|
56
|
+
// main.json has { rules: { ... } } envelope
|
|
57
|
+
const rule = file === 'main.json' ? parsed.rules : parsed;
|
|
58
|
+
if (!rule)
|
|
59
|
+
continue;
|
|
60
|
+
const checkRule = (r, path) => {
|
|
61
|
+
for (const field of required) {
|
|
62
|
+
if (!(field in r)) {
|
|
63
|
+
checks.push({
|
|
64
|
+
check: 'required-fields',
|
|
65
|
+
severity: 'error',
|
|
66
|
+
message: `Missing required field '${field}' in ${path}`,
|
|
67
|
+
location: `${file}:${path}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Check children recursively (but only actual objects, not #include strings)
|
|
72
|
+
if (Array.isArray(r['children'])) {
|
|
73
|
+
for (const child of r['children']) {
|
|
74
|
+
if (typeof child === 'object' && child !== null) {
|
|
75
|
+
checkRule(child, `${path} > ${child.name ?? 'unnamed'}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
checkRule(rule, rule.name ?? file);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// JSON syntax errors handled by check 1
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return checks;
|
|
87
|
+
}
|
|
88
|
+
// Check 3: Include resolution — every #include: in main.json references an existing file (severity: error)
|
|
89
|
+
async function checkIncludeResolution(snippetsDir) {
|
|
90
|
+
const checks = [];
|
|
91
|
+
try {
|
|
92
|
+
const mainContent = await readFile(resolve(snippetsDir, 'main.json'), 'utf-8');
|
|
93
|
+
const main = JSON.parse(mainContent);
|
|
94
|
+
const rules = main.rules;
|
|
95
|
+
if (!rules?.children)
|
|
96
|
+
return checks;
|
|
97
|
+
const files = await readdir(snippetsDir);
|
|
98
|
+
const checkIncludes = (children, path) => {
|
|
99
|
+
for (const child of children) {
|
|
100
|
+
if (typeof child === 'string' && child.startsWith('#include:')) {
|
|
101
|
+
const filename = child.slice('#include:'.length);
|
|
102
|
+
if (!files.includes(filename)) {
|
|
103
|
+
checks.push({
|
|
104
|
+
check: 'include-resolution',
|
|
105
|
+
severity: 'error',
|
|
106
|
+
message: `Include target not found: ${filename}`,
|
|
107
|
+
location: `main.json:${path}`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
checkIncludes(rules.children, 'default');
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// No main.json or parse error — handled by other checks
|
|
117
|
+
}
|
|
118
|
+
return checks;
|
|
119
|
+
}
|
|
120
|
+
// Check 4: Orphan detection — snippets not referenced by any #include: (severity: warning)
|
|
121
|
+
async function checkOrphans(snippetsDir) {
|
|
122
|
+
const checks = [];
|
|
123
|
+
try {
|
|
124
|
+
const mainContent = await readFile(resolve(snippetsDir, 'main.json'), 'utf-8');
|
|
125
|
+
const main = JSON.parse(mainContent);
|
|
126
|
+
const rules = main.rules;
|
|
127
|
+
// Collect all referenced includes
|
|
128
|
+
const referenced = new Set();
|
|
129
|
+
const collectIncludes = (children) => {
|
|
130
|
+
for (const child of children) {
|
|
131
|
+
if (typeof child === 'string' && child.startsWith('#include:')) {
|
|
132
|
+
referenced.add(child.slice('#include:'.length));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
if (rules?.children)
|
|
137
|
+
collectIncludes(rules.children);
|
|
138
|
+
// Check all snippet files
|
|
139
|
+
const files = await readdir(snippetsDir);
|
|
140
|
+
for (const file of files) {
|
|
141
|
+
if (file === 'main.json' || !file.endsWith('.json'))
|
|
142
|
+
continue;
|
|
143
|
+
if (!referenced.has(file)) {
|
|
144
|
+
checks.push({
|
|
145
|
+
check: 'orphan-detection',
|
|
146
|
+
severity: 'warning',
|
|
147
|
+
message: `Snippet '${file}' is not referenced by any #include: directive`,
|
|
148
|
+
location: file,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Handled elsewhere
|
|
155
|
+
}
|
|
156
|
+
return checks;
|
|
157
|
+
}
|
|
158
|
+
// Check 5: Variable references — {{user.PMUSER_*}} patterns must match declared variables (severity: warning)
|
|
159
|
+
async function checkVariableReferences(snippetsDir, _pmDir) {
|
|
160
|
+
const checks = [];
|
|
161
|
+
try {
|
|
162
|
+
// Get declared variables from main.json
|
|
163
|
+
const mainContent = await readFile(resolve(snippetsDir, 'main.json'), 'utf-8');
|
|
164
|
+
const main = JSON.parse(mainContent);
|
|
165
|
+
const declaredVars = new Set();
|
|
166
|
+
if (main.rules?.variables) {
|
|
167
|
+
for (const v of main.rules.variables) {
|
|
168
|
+
declaredVars.add(v.name);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Scan all snippet files for variable references
|
|
172
|
+
const files = await readdir(snippetsDir);
|
|
173
|
+
for (const file of files) {
|
|
174
|
+
if (!file.endsWith('.json'))
|
|
175
|
+
continue;
|
|
176
|
+
const content = await readFile(resolve(snippetsDir, file), 'utf-8');
|
|
177
|
+
const refs = content.match(/\{\{user\.(PMUSER_\w+)\}\}/g);
|
|
178
|
+
if (!refs)
|
|
179
|
+
continue;
|
|
180
|
+
for (const ref of refs) {
|
|
181
|
+
const varName = ref.match(/\{\{user\.(\w+)\}\}/)[1];
|
|
182
|
+
if (!declaredVars.has(varName)) {
|
|
183
|
+
checks.push({
|
|
184
|
+
check: 'variable-references',
|
|
185
|
+
severity: 'warning',
|
|
186
|
+
message: `Variable '${varName}' referenced in ${file} but not declared in main.json variables`,
|
|
187
|
+
location: file,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// Handled elsewhere
|
|
195
|
+
}
|
|
196
|
+
return checks;
|
|
197
|
+
}
|
|
198
|
+
// Check 6: Duplicate rule names — sibling rules with same name (severity: warning)
|
|
199
|
+
async function checkDuplicateRuleNames(snippetsDir) {
|
|
200
|
+
const checks = [];
|
|
201
|
+
const files = await readdir(snippetsDir);
|
|
202
|
+
for (const file of files) {
|
|
203
|
+
if (!file.endsWith('.json'))
|
|
204
|
+
continue;
|
|
205
|
+
try {
|
|
206
|
+
const content = await readFile(resolve(snippetsDir, file), 'utf-8');
|
|
207
|
+
const parsed = JSON.parse(content);
|
|
208
|
+
const rule = file === 'main.json' ? parsed.rules : parsed;
|
|
209
|
+
if (!rule)
|
|
210
|
+
continue;
|
|
211
|
+
const checkChildren = (r, path) => {
|
|
212
|
+
if (!Array.isArray(r.children))
|
|
213
|
+
return;
|
|
214
|
+
const names = new Map();
|
|
215
|
+
for (const child of r.children) {
|
|
216
|
+
if (typeof child === 'object' && child !== null) {
|
|
217
|
+
const name = child.name;
|
|
218
|
+
if (name) {
|
|
219
|
+
names.set(name, (names.get(name) ?? 0) + 1);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
for (const [name, count] of names) {
|
|
224
|
+
if (count > 1) {
|
|
225
|
+
checks.push({
|
|
226
|
+
check: 'duplicate-rule-names',
|
|
227
|
+
severity: 'warning',
|
|
228
|
+
message: `Duplicate rule name '${name}' (${count} occurrences) under ${path}`,
|
|
229
|
+
location: `${file}:${path}`,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Recurse
|
|
234
|
+
for (const child of r.children) {
|
|
235
|
+
if (typeof child === 'object' && child !== null) {
|
|
236
|
+
checkChildren(child, `${path} > ${child.name ?? 'unnamed'}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
checkChildren(rule, rule.name ?? file);
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// Handled by syntax check
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return checks;
|
|
247
|
+
}
|
|
248
|
+
// Check 7: No-op rules — rules with empty behaviors, criteria, AND children (severity: info)
|
|
249
|
+
async function checkNoopRules(snippetsDir) {
|
|
250
|
+
const checks = [];
|
|
251
|
+
const files = await readdir(snippetsDir);
|
|
252
|
+
for (const file of files) {
|
|
253
|
+
if (!file.endsWith('.json'))
|
|
254
|
+
continue;
|
|
255
|
+
try {
|
|
256
|
+
const content = await readFile(resolve(snippetsDir, file), 'utf-8');
|
|
257
|
+
const parsed = JSON.parse(content);
|
|
258
|
+
const rule = file === 'main.json' ? parsed.rules : parsed;
|
|
259
|
+
if (!rule)
|
|
260
|
+
continue;
|
|
261
|
+
const checkNoop = (r, path) => {
|
|
262
|
+
const hasNoBehaviors = !r.behaviors || r.behaviors.length === 0;
|
|
263
|
+
const hasNoCriteria = !r.criteria || r.criteria.length === 0;
|
|
264
|
+
const hasNoChildren = !r.children || r.children.length === 0;
|
|
265
|
+
if (hasNoBehaviors && hasNoCriteria && hasNoChildren) {
|
|
266
|
+
checks.push({
|
|
267
|
+
check: 'noop-rules',
|
|
268
|
+
severity: 'info',
|
|
269
|
+
message: `Rule '${r.name}' has no behaviors, criteria, or children`,
|
|
270
|
+
location: `${file}:${path}`,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
// Recurse into children
|
|
274
|
+
if (Array.isArray(r.children)) {
|
|
275
|
+
for (const child of r.children) {
|
|
276
|
+
if (typeof child === 'object' && child !== null) {
|
|
277
|
+
checkNoop(child, `${path} > ${child.name}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
checkNoop(rule, rule.name ?? file);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
// Handled by syntax check
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return checks;
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=local.js.map
|