@planu/cli 4.3.14 → 4.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +18 -20
- package/dist/tools/create-spec/autopilot-analyzer.js +0 -39
- package/dist/tools/create-spec.js +1 -3
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +0 -1
- package/package.json +9 -9
- package/planu-native.json +1 -1
- package/planu-plugin.json +1 -1
- package/dist/config/criteria-injection-rules.json +0 -82
- package/dist/engine/acceptance-criteria-injector/criteria-filter.d.ts +0 -12
- package/dist/engine/acceptance-criteria-injector/criteria-filter.js +0 -60
- package/dist/types/criteria-injection.d.ts +0 -12
- package/dist/types/criteria-injection.js +0 -3
package/dist/index.js
CHANGED
|
@@ -33,8 +33,9 @@ if (firstArg && KNOWN_COMMANDS.includes(firstArg)) {
|
|
|
33
33
|
process.exit(0);
|
|
34
34
|
}
|
|
35
35
|
// Minimal static imports — only what createMcpServer() needs at module load time.
|
|
36
|
-
//
|
|
37
|
-
//
|
|
36
|
+
// The official tool surface is registered in main() before transport connection
|
|
37
|
+
// so the first MCP tools/list response is complete for hosts that ignore
|
|
38
|
+
// notifications/tools/list_changed.
|
|
38
39
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
39
40
|
import { SERVER_INSTRUCTIONS } from './config/server-instructions.js';
|
|
40
41
|
import { PLANU_VERSION } from './config/version.js';
|
|
@@ -44,13 +45,10 @@ function createMcpServer() {
|
|
|
44
45
|
instructions: SERVER_INSTRUCTIONS,
|
|
45
46
|
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
46
47
|
}));
|
|
47
|
-
// Pre-initialize
|
|
48
|
+
// Pre-initialize resource/prompt request handlers BEFORE transport connects.
|
|
48
49
|
// McpServer calls registerCapabilities + sets up list/call handlers only on the
|
|
49
50
|
// FIRST invocation of each surface. After this, later registrations only update
|
|
50
51
|
// in-memory registries — safe to call even after transport connects.
|
|
51
|
-
s.registerTool('_planu_boot', { description: 'Planu is starting…', inputSchema: {} }, () => ({
|
|
52
|
-
content: [],
|
|
53
|
-
}));
|
|
54
52
|
s.registerResource('_planu_boot', '_planu://boot', {}, () => ({ contents: [] }));
|
|
55
53
|
s.registerPrompt('_planu_boot', { title: 'Planu Boot', description: 'Internal prompt handler bootstrap', argsSchema: {} }, () => ({
|
|
56
54
|
messages: [],
|
|
@@ -59,13 +57,13 @@ function createMcpServer() {
|
|
|
59
57
|
}
|
|
60
58
|
// Primary server instance — created immediately at module load with minimal deps.
|
|
61
59
|
const server = createMcpServer();
|
|
62
|
-
// Loads
|
|
60
|
+
// Loads post-handshake modules after the MCP handshake has completed.
|
|
63
61
|
// Separated from main() to keep main() within the 80-line function budget.
|
|
64
|
-
async function setupPostHandshake(handshakeGate) {
|
|
62
|
+
async function setupPostHandshake(handshakeGate, officialToolNames) {
|
|
65
63
|
// Wait for initialize + notifications/initialized before firing any notifications.
|
|
66
64
|
await handshakeGate;
|
|
67
65
|
const envKey = process.env.SDD_LICENSE_KEY;
|
|
68
|
-
const [{ bootstrapAutopilotHandlers }, { registerAgentSquadHandlers }, { startupSync }, { checkForUpdates }, { setConnectedClient }, { handleActivateLicense }, { GroupManager }, { setGroupManager },
|
|
66
|
+
const [{ bootstrapAutopilotHandlers }, { registerAgentSquadHandlers }, { startupSync }, { checkForUpdates }, { setConnectedClient }, { handleActivateLicense }, { GroupManager }, { setGroupManager },] = await Promise.all([
|
|
69
67
|
import('./engine/autopilot/bootstrap.js'),
|
|
70
68
|
import('./tools/register-agent-squad-tools.js'),
|
|
71
69
|
import('./tools/sync-spec-state-handler.js'),
|
|
@@ -74,7 +72,6 @@ async function setupPostHandshake(handshakeGate) {
|
|
|
74
72
|
import('./tools/activate-license.js'),
|
|
75
73
|
import('./engine/tool-groups/group-manager.js'),
|
|
76
74
|
import('./tools/tool-group-handler.js'),
|
|
77
|
-
import('./tools/register-sdd-tools.js'),
|
|
78
75
|
]);
|
|
79
76
|
// SPEC-450: Wire autopilot trigger rules to the event bus
|
|
80
77
|
bootstrapAutopilotHandlers();
|
|
@@ -121,9 +118,6 @@ async function setupPostHandshake(handshakeGate) {
|
|
|
121
118
|
});
|
|
122
119
|
}
|
|
123
120
|
}
|
|
124
|
-
// Register the single official SDD MCP surface and UX surfaces.
|
|
125
|
-
registerSddTools(server);
|
|
126
|
-
getRegisteredTools().get('_planu_boot')?.disable();
|
|
127
121
|
try {
|
|
128
122
|
const { registerPlanuMcpResources, registerPlanuMcpPrompts } = await import('./hosts/claude-code/ux/index.js');
|
|
129
123
|
registerPlanuMcpResources(server);
|
|
@@ -138,7 +132,7 @@ async function setupPostHandshake(handshakeGate) {
|
|
|
138
132
|
server.sendToolListChanged();
|
|
139
133
|
});
|
|
140
134
|
await groupManager.init();
|
|
141
|
-
for (const toolName of
|
|
135
|
+
for (const toolName of officialToolNames) {
|
|
142
136
|
toolMap.get(toolName)?.enable();
|
|
143
137
|
}
|
|
144
138
|
setGroupManager(groupManager);
|
|
@@ -147,9 +141,14 @@ async function setupPostHandshake(handshakeGate) {
|
|
|
147
141
|
}
|
|
148
142
|
// Start server
|
|
149
143
|
async function main() {
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
// Register the official tools before connecting transport. Some hosts cache the
|
|
145
|
+
// first tools/list response and do not reliably consume list_changed updates.
|
|
146
|
+
const [{ selectTransport }, { registerSddTools, OFFICIAL_SDD_TOOL_NAMES }] = await Promise.all([
|
|
147
|
+
import('./transports/transport-factory.js'),
|
|
148
|
+
import('./tools/register-sdd-tools.js'),
|
|
149
|
+
]);
|
|
150
|
+
registerSddTools(server);
|
|
151
|
+
// Gate non-tool startup work until the MCP handshake completes (initialize +
|
|
153
152
|
// notifications/initialized). This guarantees the initialize response goes out
|
|
154
153
|
// before any notifications/tools/list_changed events, which is required by the
|
|
155
154
|
// MCP protocol. Without this gate module evaluation (synchronous, ~3-4 s) blocks
|
|
@@ -175,9 +174,8 @@ async function main() {
|
|
|
175
174
|
await new Promise((res) => {
|
|
176
175
|
setImmediate(res);
|
|
177
176
|
});
|
|
178
|
-
// Fire-and-forget: setupPostHandshake awaits the gate
|
|
179
|
-
|
|
180
|
-
void setupPostHandshake(handshakeGate);
|
|
177
|
+
// Fire-and-forget: setupPostHandshake awaits the gate before optional startup work.
|
|
178
|
+
void setupPostHandshake(handshakeGate, OFFICIAL_SDD_TOOL_NAMES);
|
|
181
179
|
}
|
|
182
180
|
process.on('uncaughtException', (error) => {
|
|
183
181
|
/* v8 ignore next 1 */
|
|
@@ -71,7 +71,6 @@ export async function analyzeProjectForSpec(projectPath, description, title, kno
|
|
|
71
71
|
// Detect stack patterns and suggest relevant criteria
|
|
72
72
|
if (knowledge) {
|
|
73
73
|
result.detectedPatterns = detectPatterns(knowledge, description);
|
|
74
|
-
result.suggestedCriteria = generatePatternCriteria(result.detectedPatterns, knowledge);
|
|
75
74
|
}
|
|
76
75
|
return result;
|
|
77
76
|
}
|
|
@@ -196,42 +195,4 @@ function detectPatterns(knowledge, description) {
|
|
|
196
195
|
}
|
|
197
196
|
return patterns;
|
|
198
197
|
}
|
|
199
|
-
/** Generate stack-specific criteria based on detected patterns — GIVEN/WHEN/THEN format. */
|
|
200
|
-
function generatePatternCriteria(patterns, _knowledge) {
|
|
201
|
-
const criteria = [];
|
|
202
|
-
for (const pattern of patterns) {
|
|
203
|
-
switch (pattern) {
|
|
204
|
-
case 'database':
|
|
205
|
-
criteria.push('GIVEN migration WHEN applied to the database THEN target table exists with columns [id, created_at] and correct data types');
|
|
206
|
-
break;
|
|
207
|
-
case 'supabase':
|
|
208
|
-
criteria.push('GIVEN unauthenticated request WHEN RLS policy is active on the table THEN query returns empty array instead of data');
|
|
209
|
-
criteria.push('GIVEN schema change WHEN supabase gen types runs THEN TypeScript types reflect the new columns without manual edits');
|
|
210
|
-
break;
|
|
211
|
-
case 'api-endpoint':
|
|
212
|
-
criteria.push('GIVEN POST handler WHEN called with invalid body (missing required field) THEN returns {status: 400, error: string}');
|
|
213
|
-
criteria.push('GIVEN POST handler WHEN called with valid body THEN validates input with Zod schema before processing');
|
|
214
|
-
break;
|
|
215
|
-
case 'authentication':
|
|
216
|
-
criteria.push('GIVEN endpoint WHEN called with no Authorization header THEN returns {status: 401}');
|
|
217
|
-
break;
|
|
218
|
-
case 'react-components':
|
|
219
|
-
criteria.push('GIVEN component WHEN rendered THEN has role attribute and aria-label set correctly for screen readers');
|
|
220
|
-
break;
|
|
221
|
-
case 'nextjs-pages':
|
|
222
|
-
criteria.push('GIVEN page component WHEN rendered THEN uses Server Component (no "use client" directive) where data-fetching occurs');
|
|
223
|
-
break;
|
|
224
|
-
case 'testing':
|
|
225
|
-
criteria.push('GIVEN test file WHEN run with vitest THEN coverage >= threshold defined in vitest.config for branches and lines');
|
|
226
|
-
break;
|
|
227
|
-
// SPEC-535: EU AI Act Article 53-55 compliance criteria for foundation model features
|
|
228
|
-
case 'llm-feature':
|
|
229
|
-
criteria.push('GIVEN the feature uses a foundation model WHEN the spec is implemented THEN spec.md documents in its inline Technical section: model name, provider (Anthropic/OpenAI/Google), and access date — required by EU AI Act Article 53(1)(a)');
|
|
230
|
-
criteria.push('GIVEN EU users interact with this feature WHEN the feature is deployed THEN an acceptable use policy is visible before the first AI interaction — required by EU AI Act Article 53(1)(c)');
|
|
231
|
-
criteria.push("GIVEN the feature processes user-generated content via LLM WHEN any EU user's data is involved THEN the privacy notice explicitly states LLM processing and links to the model provider's data processing terms");
|
|
232
|
-
break;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return criteria;
|
|
236
|
-
}
|
|
237
198
|
//# sourceMappingURL=autopilot-analyzer.js.map
|
|
@@ -24,7 +24,6 @@ import { analyzeProjectForSpec, getEmptyAutopilotResult, } from './create-spec/a
|
|
|
24
24
|
import { AutopilotSummaryCollector } from '../engine/autopilot/summary-collector.js';
|
|
25
25
|
import { trackCost } from '../engine/cost-tracking/operation-tracker.js';
|
|
26
26
|
import { analyzeSimplicity } from '../engine/simplicity-detector.js';
|
|
27
|
-
import { filterCriteriaByTags } from '../engine/acceptance-criteria-injector/criteria-filter.js';
|
|
28
27
|
import { withBudget, unwrapBudget, withTotalBudget } from '../engine/timing/budget.js';
|
|
29
28
|
import { measureStep } from '../engine/timing/structured-log.js';
|
|
30
29
|
import { resolveProjectIdOrAutoDetect } from './resolve-project-id.js';
|
|
@@ -514,8 +513,7 @@ export async function handleCreateSpec(inputParams, server) {
|
|
|
514
513
|
}
|
|
515
514
|
// Create spec directory and write lean files (SPEC-461)
|
|
516
515
|
await measureStep('mkdir-specDir', () => mkdir(specDir, { recursive: true }));
|
|
517
|
-
|
|
518
|
-
const filteredCriteria = await filterCriteriaByTags(autopilot.suggestedCriteria, spec.tags, spec.target, spec.scope).catch(() => autopilot.suggestedCriteria);
|
|
516
|
+
const filteredCriteria = autopilot.suggestedCriteria;
|
|
519
517
|
const technologyContract = await readTechnologySelectionContract(params.projectPath ?? '');
|
|
520
518
|
const contractNote = technologyContract
|
|
521
519
|
? [
|
package/dist/types/index.d.ts
CHANGED
|
@@ -236,7 +236,6 @@ export * from './observatory.js';
|
|
|
236
236
|
export * from './orphan-spec-refs.js';
|
|
237
237
|
export * from './session-safeguard.js';
|
|
238
238
|
export * from './impact-detection.js';
|
|
239
|
-
export * from './criteria-injection.js';
|
|
240
239
|
export * from './gemini.js';
|
|
241
240
|
export * from './claude-code-runtime.js';
|
|
242
241
|
export * from './claude-code-ux.js';
|
package/dist/types/index.js
CHANGED
|
@@ -233,7 +233,6 @@ export * from './observatory.js';
|
|
|
233
233
|
export * from './orphan-spec-refs.js';
|
|
234
234
|
export * from './session-safeguard.js';
|
|
235
235
|
export * from './impact-detection.js';
|
|
236
|
-
export * from './criteria-injection.js';
|
|
237
236
|
export * from './gemini.js';
|
|
238
237
|
export * from './claude-code-runtime.js';
|
|
239
238
|
export * from './claude-code-ux.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planu/cli",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.16",
|
|
4
4
|
"description": "Planu — MCP Server for Spec Driven Development with native Rust acceleration for hot paths. Cross-platform (Linux/macOS/Windows, x64/arm64, glibc/musl).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"packageName": "@planu/core"
|
|
35
35
|
},
|
|
36
36
|
"optionalDependencies": {
|
|
37
|
-
"@planu/core-darwin-arm64": "4.3.
|
|
38
|
-
"@planu/core-darwin-x64": "4.3.
|
|
39
|
-
"@planu/core-linux-arm64-gnu": "4.3.
|
|
40
|
-
"@planu/core-linux-arm64-musl": "4.3.
|
|
41
|
-
"@planu/core-linux-x64-gnu": "4.3.
|
|
42
|
-
"@planu/core-linux-x64-musl": "4.3.
|
|
43
|
-
"@planu/core-win32-arm64-msvc": "4.3.
|
|
44
|
-
"@planu/core-win32-x64-msvc": "4.3.
|
|
37
|
+
"@planu/core-darwin-arm64": "4.3.16",
|
|
38
|
+
"@planu/core-darwin-x64": "4.3.16",
|
|
39
|
+
"@planu/core-linux-arm64-gnu": "4.3.16",
|
|
40
|
+
"@planu/core-linux-arm64-musl": "4.3.16",
|
|
41
|
+
"@planu/core-linux-x64-gnu": "4.3.16",
|
|
42
|
+
"@planu/core-linux-x64-musl": "4.3.16",
|
|
43
|
+
"@planu/core-win32-arm64-msvc": "4.3.16",
|
|
44
|
+
"@planu/core-win32-x64-msvc": "4.3.16"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": ">=24.0.0"
|
package/planu-native.json
CHANGED
package/planu-plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dev.planu.cli",
|
|
3
3
|
"displayName": "Planu — Spec Driven Development",
|
|
4
4
|
"description": "Manage software specs, estimations, and autonomous SDD workflows. Language-agnostic MCP server for Claude Code.",
|
|
5
|
-
"version": "4.3.
|
|
5
|
+
"version": "4.3.16",
|
|
6
6
|
"icon": "assets/plugin/icon.svg",
|
|
7
7
|
"command": ["npx", "@planu/cli@latest"],
|
|
8
8
|
"packageName": "@planu/cli",
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"rules": [
|
|
4
|
-
{
|
|
5
|
-
"id": "eu-ai-act-53-1a",
|
|
6
|
-
"criterion": "GIVEN the feature uses a foundation model WHEN the spec is implemented THEN spec.md documents in its inline Technical section: model name, provider, and access date — EU AI Act Article 53(1)(a)",
|
|
7
|
-
"requiresTags": ["llm", "foundation-model", "ai-feature"],
|
|
8
|
-
"requiresTargets": [],
|
|
9
|
-
"requiresScopes": []
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"id": "eu-ai-act-53-1c",
|
|
13
|
-
"criterion": "GIVEN EU users interact with this feature WHEN the feature is deployed THEN an acceptable use policy is visible before the first AI interaction — required by EU AI Act Article 53(1)(c)",
|
|
14
|
-
"requiresTags": ["llm", "foundation-model", "ai-feature"],
|
|
15
|
-
"requiresTargets": [],
|
|
16
|
-
"requiresScopes": []
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"id": "eu-ai-act-privacy",
|
|
20
|
-
"criterion": "GIVEN the feature processes user-generated content via LLM WHEN any EU user's data is involved THEN the privacy notice explicitly states LLM processing and links to the model provider's data processing terms",
|
|
21
|
-
"requiresTags": ["llm", "foundation-model", "ai-feature"],
|
|
22
|
-
"requiresTargets": [],
|
|
23
|
-
"requiresScopes": []
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"id": "vitest-coverage",
|
|
27
|
-
"criterion": "GIVEN test file WHEN run with vitest THEN coverage >= threshold defined in vitest.config for branches and lines",
|
|
28
|
-
"requiresTags": [],
|
|
29
|
-
"requiresTargets": ["backend", "frontend", "shared", "fullstack"],
|
|
30
|
-
"requiresScopes": []
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"id": "db-migration",
|
|
34
|
-
"criterion": "GIVEN migration WHEN applied to the database THEN target table exists with columns [id, created_at] and correct data types",
|
|
35
|
-
"requiresTags": ["database", "migration"],
|
|
36
|
-
"requiresTargets": ["database"],
|
|
37
|
-
"requiresScopes": []
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"id": "api-zod-invalid",
|
|
41
|
-
"criterion": "GIVEN POST handler WHEN called with invalid body THEN returns {status: 400, error: string}",
|
|
42
|
-
"requiresTags": ["api", "endpoint", "http"],
|
|
43
|
-
"requiresTargets": [],
|
|
44
|
-
"requiresScopes": []
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"id": "api-zod-valid",
|
|
48
|
-
"criterion": "GIVEN POST handler WHEN called with valid body THEN validates input with Zod schema before processing",
|
|
49
|
-
"requiresTags": ["api", "endpoint", "http"],
|
|
50
|
-
"requiresTargets": [],
|
|
51
|
-
"requiresScopes": []
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"id": "otel-http-metrics",
|
|
55
|
-
"criterion": "GIVEN HTTP requests WHEN the service runs THEN histogram http.server.request.duration records P50/P95/P99 latency with http.method, http.status_code, and http.route attributes",
|
|
56
|
-
"requiresTags": ["otel", "observability", "monitoring"],
|
|
57
|
-
"requiresTargets": [],
|
|
58
|
-
"requiresScopes": []
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"id": "otel-traces",
|
|
62
|
-
"criterion": "GIVEN any inbound request WHEN processed THEN a trace span is created with service.name, trace_id, and span_id attributes exported to the configured OTel collector",
|
|
63
|
-
"requiresTags": ["otel", "observability", "monitoring"],
|
|
64
|
-
"requiresTargets": [],
|
|
65
|
-
"requiresScopes": []
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"id": "resilience-retry",
|
|
69
|
-
"criterion": "GIVEN an external service call WHEN the call fails with a transient error THEN the client retries up to 3 times with exponential backoff before returning an error",
|
|
70
|
-
"requiresTags": ["resilience", "reliability"],
|
|
71
|
-
"requiresTargets": [],
|
|
72
|
-
"requiresScopes": []
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"id": "resilience-circuit-breaker",
|
|
76
|
-
"criterion": "GIVEN repeated failures from an external dependency WHEN the failure rate exceeds 50% in a 10s window THEN the circuit breaker opens and fast-fails subsequent calls for 30s",
|
|
77
|
-
"requiresTags": ["resilience", "reliability"],
|
|
78
|
-
"requiresTargets": [],
|
|
79
|
-
"requiresScopes": []
|
|
80
|
-
}
|
|
81
|
-
]
|
|
82
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/** Invalidate the rules cache (useful in tests). */
|
|
2
|
-
export declare function clearRulesCache(): void;
|
|
3
|
-
/**
|
|
4
|
-
* Filter a list of candidate criteria against the injection rules.
|
|
5
|
-
* Returns only criteria that pass the tag/target/scope gate.
|
|
6
|
-
*/
|
|
7
|
-
export declare function filterCriteriaByTags(candidates: string[], specTags: string[], specTarget: string, specScope: string): Promise<string[]>;
|
|
8
|
-
/**
|
|
9
|
-
* Check whether the EU AI Act criteria should be injected for this spec.
|
|
10
|
-
*/
|
|
11
|
-
export declare function shouldInjectEuAiActCriteria(specTags: string[]): Promise<boolean>;
|
|
12
|
-
//# sourceMappingURL=criteria-filter.d.ts.map
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// engine/acceptance-criteria-injector/criteria-filter.ts — SPEC-586: Tag-aware criteria filter
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { join, dirname } from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
let cachedRules = null;
|
|
6
|
-
const RULES_PATH = join(dirname(fileURLToPath(import.meta.url)), '../../config/criteria-injection-rules.json');
|
|
7
|
-
async function loadRules() {
|
|
8
|
-
if (cachedRules !== null) {
|
|
9
|
-
return cachedRules;
|
|
10
|
-
}
|
|
11
|
-
try {
|
|
12
|
-
const content = await readFile(RULES_PATH, 'utf-8');
|
|
13
|
-
const parsed = JSON.parse(content);
|
|
14
|
-
cachedRules = parsed.rules;
|
|
15
|
-
return cachedRules;
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
/** Invalidate the rules cache (useful in tests). */
|
|
22
|
-
export function clearRulesCache() {
|
|
23
|
-
cachedRules = null;
|
|
24
|
-
}
|
|
25
|
-
function ruleApplies(rule, specTags, specTarget, specScope) {
|
|
26
|
-
const tagsMatch = rule.requiresTags.length === 0 || rule.requiresTags.some((t) => specTags.includes(t));
|
|
27
|
-
const targetsMatch = rule.requiresTargets.length === 0 || rule.requiresTargets.includes(specTarget);
|
|
28
|
-
const scopesMatch = rule.requiresScopes.length === 0 || rule.requiresScopes.includes(specScope);
|
|
29
|
-
return tagsMatch && targetsMatch && scopesMatch;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Filter a list of candidate criteria against the injection rules.
|
|
33
|
-
* Returns only criteria that pass the tag/target/scope gate.
|
|
34
|
-
*/
|
|
35
|
-
export async function filterCriteriaByTags(candidates, specTags, specTarget, specScope) {
|
|
36
|
-
const rules = await loadRules();
|
|
37
|
-
if (rules.length === 0) {
|
|
38
|
-
return candidates;
|
|
39
|
-
}
|
|
40
|
-
return candidates.filter((criterion) => {
|
|
41
|
-
const matchingRule = rules.find((r) => criterion.toLowerCase().includes(r.criterion.toLowerCase().slice(0, 40)) ||
|
|
42
|
-
r.criterion.toLowerCase().includes(criterion.toLowerCase().slice(0, 40)));
|
|
43
|
-
if (!matchingRule) {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
return ruleApplies(matchingRule, specTags, specTarget, specScope);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Check whether the EU AI Act criteria should be injected for this spec.
|
|
51
|
-
*/
|
|
52
|
-
export async function shouldInjectEuAiActCriteria(specTags) {
|
|
53
|
-
const rules = await loadRules();
|
|
54
|
-
const euRule = rules.find((r) => r.id === 'eu-ai-act-53-1a');
|
|
55
|
-
if (!euRule) {
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
return euRule.requiresTags.some((t) => specTags.includes(t));
|
|
59
|
-
}
|
|
60
|
-
//# sourceMappingURL=criteria-filter.js.map
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export interface InjectionRule {
|
|
2
|
-
id: string;
|
|
3
|
-
criterion: string;
|
|
4
|
-
requiresTags: string[];
|
|
5
|
-
requiresTargets: string[];
|
|
6
|
-
requiresScopes: string[];
|
|
7
|
-
}
|
|
8
|
-
export interface RulesFile {
|
|
9
|
-
version: number;
|
|
10
|
-
rules: InjectionRule[];
|
|
11
|
-
}
|
|
12
|
-
//# sourceMappingURL=criteria-injection.d.ts.map
|