@planu/cli 1.11.0 → 1.12.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/dist/config/license-plans.json +27 -2
- package/dist/engine/api-compat/compatibility-checker.d.ts +4 -0
- package/dist/engine/api-compat/compatibility-checker.d.ts.map +1 -0
- package/dist/engine/api-compat/compatibility-checker.js +118 -0
- package/dist/engine/api-compat/compatibility-checker.js.map +1 -0
- package/dist/engine/checkpoint/checkpoint-manager.d.ts +22 -0
- package/dist/engine/checkpoint/checkpoint-manager.d.ts.map +1 -0
- package/dist/engine/checkpoint/checkpoint-manager.js +76 -0
- package/dist/engine/checkpoint/checkpoint-manager.js.map +1 -0
- package/dist/engine/checkpoint/policy-engine.d.ts +10 -0
- package/dist/engine/checkpoint/policy-engine.d.ts.map +1 -0
- package/dist/engine/checkpoint/policy-engine.js +87 -0
- package/dist/engine/checkpoint/policy-engine.js.map +1 -0
- package/dist/engine/compliance/auto-remediator.d.ts +9 -0
- package/dist/engine/compliance/auto-remediator.d.ts.map +1 -0
- package/dist/engine/compliance/auto-remediator.js +118 -0
- package/dist/engine/compliance/auto-remediator.js.map +1 -0
- package/dist/engine/context-profile/profile-catalog.d.ts +5 -0
- package/dist/engine/context-profile/profile-catalog.d.ts.map +1 -0
- package/dist/engine/context-profile/profile-catalog.js +145 -0
- package/dist/engine/context-profile/profile-catalog.js.map +1 -0
- package/dist/engine/critical-path/path-analyzer.d.ts +3 -0
- package/dist/engine/critical-path/path-analyzer.d.ts.map +1 -0
- package/dist/engine/critical-path/path-analyzer.js +145 -0
- package/dist/engine/critical-path/path-analyzer.js.map +1 -0
- package/dist/engine/drift/violation-resolver.d.ts +9 -0
- package/dist/engine/drift/violation-resolver.d.ts.map +1 -0
- package/dist/engine/drift/violation-resolver.js +128 -0
- package/dist/engine/drift/violation-resolver.js.map +1 -0
- package/dist/engine/ears/criterion-scorer.d.ts +7 -0
- package/dist/engine/ears/criterion-scorer.d.ts.map +1 -0
- package/dist/engine/ears/criterion-scorer.js +87 -0
- package/dist/engine/ears/criterion-scorer.js.map +1 -0
- package/dist/engine/ears/pattern-matcher.d.ts +5 -0
- package/dist/engine/ears/pattern-matcher.d.ts.map +1 -0
- package/dist/engine/ears/pattern-matcher.js +48 -0
- package/dist/engine/ears/pattern-matcher.js.map +1 -0
- package/dist/engine/ears/rewriter.d.ts +7 -0
- package/dist/engine/ears/rewriter.d.ts.map +1 -0
- package/dist/engine/ears/rewriter.js +45 -0
- package/dist/engine/ears/rewriter.js.map +1 -0
- package/dist/engine/ears/spec-linter.d.ts +7 -0
- package/dist/engine/ears/spec-linter.d.ts.map +1 -0
- package/dist/engine/ears/spec-linter.js +127 -0
- package/dist/engine/ears/spec-linter.js.map +1 -0
- package/dist/engine/health/auto-fixer.d.ts +7 -0
- package/dist/engine/health/auto-fixer.d.ts.map +1 -0
- package/dist/engine/health/auto-fixer.js +130 -0
- package/dist/engine/health/auto-fixer.js.map +1 -0
- package/dist/engine/mcp-catalog/catalog-advisor.d.ts +3 -0
- package/dist/engine/mcp-catalog/catalog-advisor.d.ts.map +1 -0
- package/dist/engine/mcp-catalog/catalog-advisor.js +180 -0
- package/dist/engine/mcp-catalog/catalog-advisor.js.map +1 -0
- package/dist/engine/similar-problems/similarity-finder.d.ts +3 -0
- package/dist/engine/similar-problems/similarity-finder.d.ts.map +1 -0
- package/dist/engine/similar-problems/similarity-finder.js +144 -0
- package/dist/engine/similar-problems/similarity-finder.js.map +1 -0
- package/dist/engine/sync/asana-puller.d.ts +9 -0
- package/dist/engine/sync/asana-puller.d.ts.map +1 -0
- package/dist/engine/sync/asana-puller.js +91 -0
- package/dist/engine/sync/asana-puller.js.map +1 -0
- package/dist/engine/sync/conflict-resolver.d.ts +17 -0
- package/dist/engine/sync/conflict-resolver.d.ts.map +1 -0
- package/dist/engine/sync/conflict-resolver.js +58 -0
- package/dist/engine/sync/conflict-resolver.js.map +1 -0
- package/dist/engine/sync/monday-puller.d.ts +9 -0
- package/dist/engine/sync/monday-puller.d.ts.map +1 -0
- package/dist/engine/sync/monday-puller.js +110 -0
- package/dist/engine/sync/monday-puller.js.map +1 -0
- package/dist/engine/sync/notion-puller.d.ts +15 -0
- package/dist/engine/sync/notion-puller.d.ts.map +1 -0
- package/dist/engine/sync/notion-puller.js +101 -0
- package/dist/engine/sync/notion-puller.js.map +1 -0
- package/dist/engine/verifier/code-scanner.d.ts +8 -0
- package/dist/engine/verifier/code-scanner.d.ts.map +1 -0
- package/dist/engine/verifier/code-scanner.js +73 -0
- package/dist/engine/verifier/code-scanner.js.map +1 -0
- package/dist/engine/verifier/compliance-scorer.d.ts +17 -0
- package/dist/engine/verifier/compliance-scorer.d.ts.map +1 -0
- package/dist/engine/verifier/compliance-scorer.js +131 -0
- package/dist/engine/verifier/compliance-scorer.js.map +1 -0
- package/dist/engine/verifier/criterion-matcher.d.ts +15 -0
- package/dist/engine/verifier/criterion-matcher.d.ts.map +1 -0
- package/dist/engine/verifier/criterion-matcher.js +210 -0
- package/dist/engine/verifier/criterion-matcher.js.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/compliance-score-store.d.ts +16 -0
- package/dist/storage/compliance-score-store.d.ts.map +1 -0
- package/dist/storage/compliance-score-store.js +30 -0
- package/dist/storage/compliance-score-store.js.map +1 -0
- package/dist/storage/context-profile-store.d.ts +14 -0
- package/dist/storage/context-profile-store.d.ts.map +1 -0
- package/dist/storage/context-profile-store.js +34 -0
- package/dist/storage/context-profile-store.js.map +1 -0
- package/dist/storage/workflow-checkpoint-store.d.ts +16 -0
- package/dist/storage/workflow-checkpoint-store.d.ts.map +1 -0
- package/dist/storage/workflow-checkpoint-store.js +71 -0
- package/dist/storage/workflow-checkpoint-store.js.map +1 -0
- package/dist/tools/checkpoint/approve-checkpoint-handler.d.ts +3 -0
- package/dist/tools/checkpoint/approve-checkpoint-handler.d.ts.map +1 -0
- package/dist/tools/checkpoint/approve-checkpoint-handler.js +32 -0
- package/dist/tools/checkpoint/approve-checkpoint-handler.js.map +1 -0
- package/dist/tools/checkpoint/configure-policy-handler.d.ts +3 -0
- package/dist/tools/checkpoint/configure-policy-handler.d.ts.map +1 -0
- package/dist/tools/checkpoint/configure-policy-handler.js +60 -0
- package/dist/tools/checkpoint/configure-policy-handler.js.map +1 -0
- package/dist/tools/checkpoint/list-checkpoints-handler.d.ts +3 -0
- package/dist/tools/checkpoint/list-checkpoints-handler.d.ts.map +1 -0
- package/dist/tools/checkpoint/list-checkpoints-handler.js +25 -0
- package/dist/tools/checkpoint/list-checkpoints-handler.js.map +1 -0
- package/dist/tools/checkpoint/reject-checkpoint-handler.d.ts +3 -0
- package/dist/tools/checkpoint/reject-checkpoint-handler.d.ts.map +1 -0
- package/dist/tools/checkpoint/reject-checkpoint-handler.js +32 -0
- package/dist/tools/checkpoint/reject-checkpoint-handler.js.map +1 -0
- package/dist/tools/checkpoint/require-checkpoint-handler.d.ts +3 -0
- package/dist/tools/checkpoint/require-checkpoint-handler.d.ts.map +1 -0
- package/dist/tools/checkpoint/require-checkpoint-handler.js +44 -0
- package/dist/tools/checkpoint/require-checkpoint-handler.js.map +1 -0
- package/dist/tools/pull-sync-handler.d.ts +25 -0
- package/dist/tools/pull-sync-handler.d.ts.map +1 -0
- package/dist/tools/pull-sync-handler.js +161 -0
- package/dist/tools/pull-sync-handler.js.map +1 -0
- package/dist/tools/register-auto-remediation.d.ts +3 -0
- package/dist/tools/register-auto-remediation.d.ts.map +1 -0
- package/dist/tools/register-auto-remediation.js +174 -0
- package/dist/tools/register-auto-remediation.js.map +1 -0
- package/dist/tools/register-checkpoints.d.ts +3 -0
- package/dist/tools/register-checkpoints.d.ts.map +1 -0
- package/dist/tools/register-checkpoints.js +134 -0
- package/dist/tools/register-checkpoints.js.map +1 -0
- package/dist/tools/register-context-profile.d.ts +3 -0
- package/dist/tools/register-context-profile.d.ts.map +1 -0
- package/dist/tools/register-context-profile.js +106 -0
- package/dist/tools/register-context-profile.js.map +1 -0
- package/dist/tools/register-ears.d.ts +3 -0
- package/dist/tools/register-ears.d.ts.map +1 -0
- package/dist/tools/register-ears.js +148 -0
- package/dist/tools/register-ears.js.map +1 -0
- package/dist/tools/register-enterprise-compliance.js +1 -1
- package/dist/tools/register-enterprise-compliance.js.map +1 -1
- package/dist/tools/register-pull-sync.d.ts +3 -0
- package/dist/tools/register-pull-sync.d.ts.map +1 -0
- package/dist/tools/register-pull-sync.js +71 -0
- package/dist/tools/register-pull-sync.js.map +1 -0
- package/dist/tools/register-spec405-tools.d.ts +7 -0
- package/dist/tools/register-spec405-tools.d.ts.map +1 -0
- package/dist/tools/register-spec405-tools.js +194 -0
- package/dist/tools/register-spec405-tools.js.map +1 -0
- package/dist/tools/register-verifier.d.ts +3 -0
- package/dist/tools/register-verifier.d.ts.map +1 -0
- package/dist/tools/register-verifier.js +141 -0
- package/dist/tools/register-verifier.js.map +1 -0
- package/dist/types/analysis.d.ts +98 -0
- package/dist/types/analysis.d.ts.map +1 -1
- package/dist/types/context-profile.d.ts +22 -0
- package/dist/types/context-profile.d.ts.map +1 -0
- package/dist/types/context-profile.js +2 -0
- package/dist/types/context-profile.js.map +1 -0
- package/dist/types/ears.d.ts +34 -0
- package/dist/types/ears.d.ts.map +1 -0
- package/dist/types/ears.js +3 -0
- package/dist/types/ears.js.map +1 -0
- package/dist/types/health.d.ts +40 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/health.js +3 -0
- package/dist/types/health.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/notion-asana-monday.d.ts +38 -0
- package/dist/types/notion-asana-monday.d.ts.map +1 -1
- package/dist/types/workflow-checkpoint.d.ts +66 -0
- package/dist/types/workflow-checkpoint.d.ts.map +1 -0
- package/dist/types/workflow-checkpoint.js +4 -0
- package/dist/types/workflow-checkpoint.js.map +1 -0
- package/package.json +1 -1
- package/src/config/license-plans.json +27 -2
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
export const PROFILE_CATALOG = {
|
|
2
|
+
brainstorm: {
|
|
3
|
+
phase: 'brainstorm',
|
|
4
|
+
name: 'Brainstorm Mode',
|
|
5
|
+
description: 'Explore ideas, capture requirements, define scope',
|
|
6
|
+
focusTools: [
|
|
7
|
+
'capture_idea',
|
|
8
|
+
'clarify_requirements',
|
|
9
|
+
'elicit_requirements',
|
|
10
|
+
'suggest_stack',
|
|
11
|
+
'create_spec',
|
|
12
|
+
'list_backlog',
|
|
13
|
+
'promote_idea',
|
|
14
|
+
'discard_idea',
|
|
15
|
+
'search_suggestions',
|
|
16
|
+
'suggest_criteria',
|
|
17
|
+
],
|
|
18
|
+
avoidTools: ['validate', 'lock_spec', 'approve_spec', 'create_release', 'generate_changelog'],
|
|
19
|
+
tips: [
|
|
20
|
+
'Use capture_idea for rough thoughts before formalizing',
|
|
21
|
+
'Use clarify_requirements to interview stakeholders',
|
|
22
|
+
'Use elicit_requirements for structured requirement gathering',
|
|
23
|
+
'Use create_spec only when ready to commit to a feature',
|
|
24
|
+
],
|
|
25
|
+
estimatedTokensSaved: 85,
|
|
26
|
+
},
|
|
27
|
+
plan: {
|
|
28
|
+
phase: 'plan',
|
|
29
|
+
name: 'Planning Mode',
|
|
30
|
+
description: 'Design specs, estimate effort, check readiness',
|
|
31
|
+
focusTools: [
|
|
32
|
+
'create_spec',
|
|
33
|
+
'decompose_spec',
|
|
34
|
+
'estimate',
|
|
35
|
+
'check_readiness',
|
|
36
|
+
'challenge_spec',
|
|
37
|
+
'inject_quality_gates',
|
|
38
|
+
'suggest_criteria',
|
|
39
|
+
'clarify_requirements',
|
|
40
|
+
'analyze_spec_dependencies',
|
|
41
|
+
'critical_path_analyzer',
|
|
42
|
+
],
|
|
43
|
+
avoidTools: ['lock_spec', 'create_release', 'generate_changelog', 'tdd_scaffold'],
|
|
44
|
+
tips: [
|
|
45
|
+
'Use challenge_spec to stress-test before approving',
|
|
46
|
+
'Use check_readiness to verify sufficient detail',
|
|
47
|
+
'Use inject_quality_gates to add quality checklist automatically',
|
|
48
|
+
'Use decompose_spec for large specs that should be split',
|
|
49
|
+
],
|
|
50
|
+
estimatedTokensSaved: 82,
|
|
51
|
+
},
|
|
52
|
+
implement: {
|
|
53
|
+
phase: 'implement',
|
|
54
|
+
name: 'Implementation Mode',
|
|
55
|
+
description: 'Build features, track progress, manage sessions',
|
|
56
|
+
focusTools: [
|
|
57
|
+
'list_specs',
|
|
58
|
+
'update_status',
|
|
59
|
+
'lock_spec',
|
|
60
|
+
'unlock_spec',
|
|
61
|
+
'tdd_scaffold',
|
|
62
|
+
'validate',
|
|
63
|
+
'session_checkpoint',
|
|
64
|
+
'session_handoff',
|
|
65
|
+
'planu_status',
|
|
66
|
+
'generate_tests',
|
|
67
|
+
'verify_spec_compliance',
|
|
68
|
+
],
|
|
69
|
+
avoidTools: [
|
|
70
|
+
'create_spec',
|
|
71
|
+
'elicit_requirements',
|
|
72
|
+
'create_release',
|
|
73
|
+
'generate_changelog',
|
|
74
|
+
'team_analytics',
|
|
75
|
+
],
|
|
76
|
+
tips: [
|
|
77
|
+
'Use lock_spec before starting to prevent conflicts',
|
|
78
|
+
'Use session_checkpoint before context resets',
|
|
79
|
+
'Use validate after implementing to check compliance',
|
|
80
|
+
'Use update_status to track progress through lifecycle',
|
|
81
|
+
],
|
|
82
|
+
estimatedTokensSaved: 88,
|
|
83
|
+
},
|
|
84
|
+
review: {
|
|
85
|
+
phase: 'review',
|
|
86
|
+
name: 'Review Mode',
|
|
87
|
+
description: 'Validate, approve, request changes, verify quality',
|
|
88
|
+
focusTools: [
|
|
89
|
+
'validate',
|
|
90
|
+
'verify_spec_compliance',
|
|
91
|
+
'spec_quality_score',
|
|
92
|
+
'approve_spec',
|
|
93
|
+
'request_changes',
|
|
94
|
+
'challenge_spec',
|
|
95
|
+
'check_readiness',
|
|
96
|
+
'review_pr',
|
|
97
|
+
'ears_lint',
|
|
98
|
+
'validate_criteria_quality',
|
|
99
|
+
],
|
|
100
|
+
avoidTools: ['lock_spec', 'create_release', 'tdd_scaffold', 'capture_idea'],
|
|
101
|
+
tips: [
|
|
102
|
+
'Use validate to check acceptance criteria compliance',
|
|
103
|
+
'Use verify_spec_compliance to score code coverage',
|
|
104
|
+
'Use ears_lint to check criteria quality',
|
|
105
|
+
'Use approve_spec only when all criteria are verified',
|
|
106
|
+
],
|
|
107
|
+
estimatedTokensSaved: 87,
|
|
108
|
+
},
|
|
109
|
+
release: {
|
|
110
|
+
phase: 'release',
|
|
111
|
+
name: 'Release Mode',
|
|
112
|
+
description: 'Prepare releases, generate changelogs, version specs',
|
|
113
|
+
focusTools: [
|
|
114
|
+
'generate_changelog',
|
|
115
|
+
'create_release',
|
|
116
|
+
'version_spec',
|
|
117
|
+
'export_specs_markdown',
|
|
118
|
+
'generate_docs_site',
|
|
119
|
+
'spec_usage_report',
|
|
120
|
+
'velocity_report',
|
|
121
|
+
'update_status',
|
|
122
|
+
],
|
|
123
|
+
avoidTools: [
|
|
124
|
+
'create_spec',
|
|
125
|
+
'lock_spec',
|
|
126
|
+
'tdd_scaffold',
|
|
127
|
+
'capture_idea',
|
|
128
|
+
'clarify_requirements',
|
|
129
|
+
],
|
|
130
|
+
tips: [
|
|
131
|
+
'Use generate_changelog to compile release notes from done specs',
|
|
132
|
+
'Use create_release to tag and publish the version',
|
|
133
|
+
'Use velocity_report to measure team performance this cycle',
|
|
134
|
+
'Mark all specs done before creating the release',
|
|
135
|
+
],
|
|
136
|
+
estimatedTokensSaved: 90,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
export function getProfile(phase) {
|
|
140
|
+
return PROFILE_CATALOG[phase];
|
|
141
|
+
}
|
|
142
|
+
export function getAllProfiles() {
|
|
143
|
+
return Object.values(PROFILE_CATALOG);
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=profile-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-catalog.js","sourceRoot":"","sources":["../../../src/engine/context-profile/profile-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-analyzer.d.ts","sourceRoot":"","sources":["../../../src/engine/critical-path/path-analyzer.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,yBAAyB,EAAmB,MAAM,sBAAsB,CAAC;AA8FvF,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAyEjG"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// engine/critical-path/path-analyzer.ts — SPEC-405
|
|
2
|
+
// Build a dependency graph from spec frontmatter and compute the critical path.
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { glob } from 'glob';
|
|
6
|
+
import { parseFrontmatter } from '../frontmatter-parser.js';
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// File reading helpers
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
async function readAllSpecMeta(projectPath) {
|
|
11
|
+
const pattern = join(projectPath, 'planu', 'specs', '*', 'spec.md');
|
|
12
|
+
const files = await glob(pattern, { nodir: true });
|
|
13
|
+
const results = [];
|
|
14
|
+
await Promise.all(files.map(async (filePath) => {
|
|
15
|
+
try {
|
|
16
|
+
const content = await readFile(filePath, 'utf-8');
|
|
17
|
+
const { metadata } = parseFrontmatter(content);
|
|
18
|
+
const id = typeof metadata.id === 'string' ? metadata.id : '';
|
|
19
|
+
if (!id) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const estimatedHours = typeof metadata.estimatedHours === 'number' ? metadata.estimatedHours : 0;
|
|
23
|
+
const rawDeps = metadata.dependencies;
|
|
24
|
+
const dependencies = Array.isArray(rawDeps)
|
|
25
|
+
? rawDeps.filter((d) => typeof d === 'string')
|
|
26
|
+
: [];
|
|
27
|
+
const status = typeof metadata.status === 'string' ? metadata.status : 'pending';
|
|
28
|
+
results.push({ id, estimatedHours, dependencies, status });
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// skip unreadable files
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
return results;
|
|
35
|
+
}
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// DAG critical path (longest path in hours)
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
/**
|
|
40
|
+
* Compute the longest-duration path through the spec dependency DAG.
|
|
41
|
+
* Uses memoized DFS. Returns the spec IDs in topological order.
|
|
42
|
+
*/
|
|
43
|
+
function longestPath(specId, adjacency, hours, memo) {
|
|
44
|
+
const cached = memo.get(specId);
|
|
45
|
+
if (cached) {
|
|
46
|
+
return cached;
|
|
47
|
+
}
|
|
48
|
+
const children = adjacency.get(specId) ?? [];
|
|
49
|
+
let best = {
|
|
50
|
+
length: hours.get(specId) ?? 0,
|
|
51
|
+
path: [specId],
|
|
52
|
+
};
|
|
53
|
+
for (const child of children) {
|
|
54
|
+
const sub = longestPath(child, adjacency, hours, memo);
|
|
55
|
+
const total = (hours.get(specId) ?? 0) + sub.length;
|
|
56
|
+
if (total > best.length) {
|
|
57
|
+
best = { length: total, path: [specId, ...sub.path] };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
memo.set(specId, best);
|
|
61
|
+
return best;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Count how many specs depend on each spec (in-degree of reverse graph).
|
|
65
|
+
*/
|
|
66
|
+
function countDependents(specs) {
|
|
67
|
+
const count = new Map();
|
|
68
|
+
for (const spec of specs) {
|
|
69
|
+
if (!count.has(spec.id)) {
|
|
70
|
+
count.set(spec.id, 0);
|
|
71
|
+
}
|
|
72
|
+
for (const dep of spec.dependencies) {
|
|
73
|
+
count.set(dep, (count.get(dep) ?? 0) + 1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return count;
|
|
77
|
+
}
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Public function
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
export async function analyzeCriticalPath(projectPath) {
|
|
82
|
+
const specs = await readAllSpecMeta(projectPath);
|
|
83
|
+
if (specs.length === 0) {
|
|
84
|
+
return { criticalPath: [], totalHours: 0, parallelizableSpecs: [], bottlenecks: [] };
|
|
85
|
+
}
|
|
86
|
+
// Build adjacency list (spec → its dependents, i.e. specs that depend on it)
|
|
87
|
+
const adjacency = new Map();
|
|
88
|
+
const hours = new Map();
|
|
89
|
+
const allIds = new Set(specs.map((s) => s.id));
|
|
90
|
+
for (const spec of specs) {
|
|
91
|
+
hours.set(spec.id, spec.estimatedHours);
|
|
92
|
+
if (!adjacency.has(spec.id)) {
|
|
93
|
+
adjacency.set(spec.id, []);
|
|
94
|
+
}
|
|
95
|
+
for (const dep of spec.dependencies) {
|
|
96
|
+
if (allIds.has(dep)) {
|
|
97
|
+
const list = adjacency.get(dep) ?? [];
|
|
98
|
+
list.push(spec.id);
|
|
99
|
+
adjacency.set(dep, list);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Find root nodes (no dependencies pointing to them from known specs)
|
|
104
|
+
const hasIncoming = new Set();
|
|
105
|
+
for (const spec of specs) {
|
|
106
|
+
for (const dep of spec.dependencies) {
|
|
107
|
+
if (allIds.has(dep)) {
|
|
108
|
+
hasIncoming.add(spec.id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const roots = specs.filter((s) => !hasIncoming.has(s.id));
|
|
113
|
+
// Compute longest path from each root
|
|
114
|
+
const memo = new Map();
|
|
115
|
+
let bestPath = [];
|
|
116
|
+
let bestLength = 0;
|
|
117
|
+
for (const root of roots) {
|
|
118
|
+
const result = longestPath(root.id, adjacency, hours, memo);
|
|
119
|
+
if (result.length > bestLength) {
|
|
120
|
+
bestLength = result.length;
|
|
121
|
+
bestPath = result.path;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const totalHours = specs.reduce((acc, s) => acc + s.estimatedHours, 0);
|
|
125
|
+
// Parallelizable: specs NOT on the critical path
|
|
126
|
+
const criticalSet = new Set(bestPath);
|
|
127
|
+
const parallelizableSpecs = specs
|
|
128
|
+
.filter((s) => !criticalSet.has(s.id))
|
|
129
|
+
.map((s) => s.id)
|
|
130
|
+
.slice(0, 20);
|
|
131
|
+
// Bottlenecks: top 3 specs with most dependents
|
|
132
|
+
const dependentCount = countDependents(specs);
|
|
133
|
+
const bottlenecks = [...dependentCount.entries()]
|
|
134
|
+
.filter(([, count]) => count > 0)
|
|
135
|
+
.sort((a, b) => b[1] - a[1])
|
|
136
|
+
.slice(0, 3)
|
|
137
|
+
.map(([id]) => id);
|
|
138
|
+
return {
|
|
139
|
+
criticalPath: bestPath,
|
|
140
|
+
totalHours,
|
|
141
|
+
parallelizableSpecs,
|
|
142
|
+
bottlenecks,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=path-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-analyzer.js","sourceRoot":"","sources":["../../../src/engine/critical-path/path-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DriftResolutionResult } from '../../types/health.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves drift violations semi-automatically by inspecting cached drift
|
|
4
|
+
* entries and taking action based on the spec's current status.
|
|
5
|
+
*
|
|
6
|
+
* When dryRun=true, returns the planned resolutions without writing any files.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveDriftViolations(projectPath: string, projectId: string, dryRun?: boolean): Promise<DriftResolutionResult>;
|
|
9
|
+
//# sourceMappingURL=violation-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"violation-resolver.d.ts","sourceRoot":"","sources":["../../../src/engine/drift/violation-resolver.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAiB,MAAM,uBAAuB,CAAC;AAwFlF;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,UAAQ,GACb,OAAO,CAAC,qBAAqB,CAAC,CA+DhC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// engine/drift/violation-resolver.ts — Semi-automatic drift violation resolution (SPEC-408)
|
|
2
|
+
import { mkdir, writeFile, access } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { specStore } from '../../storage/index.js';
|
|
5
|
+
import { DriftCacheStore } from '../../storage/drift-cache-store.js';
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Helpers
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
async function specDirExists(specDir) {
|
|
10
|
+
try {
|
|
11
|
+
await access(specDir);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function createFollowUpSpec(projectPath, parentSpecId, reason, dryRun) {
|
|
19
|
+
const timestamp = Date.now();
|
|
20
|
+
const specId = `SPEC-followup-${parentSpecId.toLowerCase()}-${timestamp}`;
|
|
21
|
+
if (!dryRun) {
|
|
22
|
+
const specDir = join(projectPath, 'planu', 'specs', specId);
|
|
23
|
+
await mkdir(specDir, { recursive: true });
|
|
24
|
+
const now = new Date().toISOString();
|
|
25
|
+
const specContent = [
|
|
26
|
+
'---',
|
|
27
|
+
`id: ${specId}`,
|
|
28
|
+
`title: "Follow-up: ${parentSpecId} drift resolution"`,
|
|
29
|
+
`status: draft`,
|
|
30
|
+
`tags: [drift, follow-up, auto-generated]`,
|
|
31
|
+
`createdAt: ${now}`,
|
|
32
|
+
'---',
|
|
33
|
+
'',
|
|
34
|
+
`# Follow-up for ${parentSpecId}`,
|
|
35
|
+
'',
|
|
36
|
+
reason,
|
|
37
|
+
'',
|
|
38
|
+
'## Acceptance Criteria',
|
|
39
|
+
'',
|
|
40
|
+
`- [ ] Review all criteria from ${parentSpecId} against current implementation`,
|
|
41
|
+
'- [ ] Update spec or implementation to re-align',
|
|
42
|
+
'- [ ] Run detect_drift to confirm score returns to 0',
|
|
43
|
+
'',
|
|
44
|
+
].join('\n');
|
|
45
|
+
await writeFile(join(specDir, 'spec.md'), specContent, 'utf-8');
|
|
46
|
+
await writeFile(join(specDir, 'progress.md'), `# ${specId} — Progress\n\nstatus: draft\n`, 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
return specId;
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Violation handlers per status
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
function buildDoneButDivergedResolution(specId) {
|
|
54
|
+
return {
|
|
55
|
+
specId,
|
|
56
|
+
action: 'follow-up-spec-queued',
|
|
57
|
+
description: 'Spec is done but code has diverged — follow-up spec created to track delta',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function buildInProgressAheadResolution(specId) {
|
|
61
|
+
return {
|
|
62
|
+
specId,
|
|
63
|
+
action: 'spec-note-added',
|
|
64
|
+
description: 'Spec is in-progress and code is ahead — criteria note added to flag untracked work',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Public API
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
/**
|
|
71
|
+
* Resolves drift violations semi-automatically by inspecting cached drift
|
|
72
|
+
* entries and taking action based on the spec's current status.
|
|
73
|
+
*
|
|
74
|
+
* When dryRun=true, returns the planned resolutions without writing any files.
|
|
75
|
+
*/
|
|
76
|
+
export async function resolveDriftViolations(projectPath, projectId, dryRun = false) {
|
|
77
|
+
const cacheStore = new DriftCacheStore(projectId);
|
|
78
|
+
const cacheData = await cacheStore.load();
|
|
79
|
+
const cachedEntries = Object.values(cacheData.entries);
|
|
80
|
+
const specs = await specStore.listSpecs(projectId);
|
|
81
|
+
const specMap = new Map(specs.map((s) => [s.id, s]));
|
|
82
|
+
const resolved = [];
|
|
83
|
+
const followUpSpecsCreated = [];
|
|
84
|
+
const manualReview = [];
|
|
85
|
+
for (const entry of cachedEntries) {
|
|
86
|
+
const spec = specMap.get(entry.specId);
|
|
87
|
+
if (!spec) {
|
|
88
|
+
manualReview.push(`${entry.specId}: spec no longer exists — remove orphaned drift cache`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (spec.status === 'done') {
|
|
92
|
+
// Code diverged from a completed spec — create follow-up
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
94
|
+
const reason = `Drift detected (score: ${entry.driftScore.toFixed(2)}) in files: ${(entry.driftedFiles ?? []).join(', ') || 'unknown'}`;
|
|
95
|
+
const followUpId = await createFollowUpSpec(projectPath, spec.id, reason, dryRun);
|
|
96
|
+
followUpSpecsCreated.push(followUpId);
|
|
97
|
+
resolved.push(buildDoneButDivergedResolution(spec.id));
|
|
98
|
+
}
|
|
99
|
+
else if (spec.status === 'implementing') {
|
|
100
|
+
// In-progress spec with code ahead — annotate but don't block
|
|
101
|
+
resolved.push(buildInProgressAheadResolution(spec.id));
|
|
102
|
+
}
|
|
103
|
+
else if (spec.status === 'draft') {
|
|
104
|
+
// Draft spec but code already exists — flag for human review
|
|
105
|
+
manualReview.push(`${spec.id}: spec is in draft status but drift detected — review if implementation started`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
manualReview.push(`${spec.id}: drift detected (status: ${spec.status}) — manual review needed`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (cachedEntries.length === 0) {
|
|
112
|
+
// Check all implementing/done specs without cached drift entries
|
|
113
|
+
const activeSpecs = specs.filter((s) => s.status === 'implementing' || s.status === 'done' || s.status === 'draft');
|
|
114
|
+
for (const spec of activeSpecs) {
|
|
115
|
+
const specDir = join(projectPath, 'planu', 'specs', spec.id);
|
|
116
|
+
const hasProgress = await specDirExists(join(specDir, 'progress.md'));
|
|
117
|
+
if (!hasProgress && spec.status === 'draft') {
|
|
118
|
+
manualReview.push(`${spec.id}: draft spec with no progress tracking — run auto_fix_health`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
resolved,
|
|
124
|
+
followUpSpecsCreated,
|
|
125
|
+
manualReview,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=violation-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"violation-resolver.js","sourceRoot":"","sources":["../../../src/engine/drift/violation-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CriterionQuality } from '../../types/ears.js';
|
|
2
|
+
/**
|
|
3
|
+
* Score a single acceptance criterion using EARS and testability heuristics.
|
|
4
|
+
*/
|
|
5
|
+
export declare function scoreCriterion(text: string): CriterionQuality;
|
|
6
|
+
export declare function gradeSpec(avgScore: number): 'A' | 'B' | 'C' | 'D' | 'F';
|
|
7
|
+
//# sourceMappingURL=criterion-scorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"criterion-scorer.d.ts","sourceRoot":"","sources":["../../../src/engine/ears/criterion-scorer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA8B5D;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAyD7D;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAcvE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// engine/ears/criterion-scorer.ts — EARS criterion scoring (SPEC-410)
|
|
2
|
+
import { detectEarsPattern, detectVaguenessFlags, isEarsCompliant } from './pattern-matcher.js';
|
|
3
|
+
import { generateRewrites } from './rewriter.js';
|
|
4
|
+
// Patterns that indicate a measurable numeric threshold
|
|
5
|
+
const NUMERIC_VALUE_REGEX = /\d+\s*(?:ms|milliseconds?|seconds?|minutes?|hours?|items?|%|percent|kb|mb|gb|px|requests?|users?|calls?|retries?)/i;
|
|
6
|
+
// Patterns for specific API routes/endpoints
|
|
7
|
+
const ENDPOINT_REGEX = /(?:\/api\/|GET\s+|POST\s+|PUT\s+|DELETE\s+|PATCH\s+)/i;
|
|
8
|
+
// Patterns for clear success conditions
|
|
9
|
+
const SUCCESS_CONDITION_REGEX = /\b(?:returns?|displays?|shows?|navigates?\s+to|renders?|saves?|stores?|loads?|creates?|sends?|emits?)\b/i;
|
|
10
|
+
// Patterns for a measurable threshold (numeric value with unit or comparison)
|
|
11
|
+
const MEASURABLE_THRESHOLD_REGEX = /\b(?:at (?:most|least)|no more than|within|fewer than|greater than|more than|\d+\s*(?:ms|seconds?|items?|%|kb|mb))\b/i;
|
|
12
|
+
// Patterns for named components/features
|
|
13
|
+
const COMPONENT_REGEX = /\b(?:[A-Z][a-zA-Z]+(?:Component|Page|Modal|Service|Store|API|Endpoint|Handler|Manager|Controller|View|Form|Button|Panel|Screen)|\w+[-_]\w+)\b/;
|
|
14
|
+
// Patterns for trigger conditions
|
|
15
|
+
const TRIGGER_REGEX = /\b(?:when|if|while|where|after|before|on(?:ce)?)\b/i;
|
|
16
|
+
// Patterns for actors
|
|
17
|
+
const ACTOR_REGEX = /\b(?:user|admin|system|operator|manager|client|server|guest|subscriber|owner)\b/i;
|
|
18
|
+
/**
|
|
19
|
+
* Score a single acceptance criterion using EARS and testability heuristics.
|
|
20
|
+
*/
|
|
21
|
+
export function scoreCriterion(text) {
|
|
22
|
+
const earsPattern = detectEarsPattern(text);
|
|
23
|
+
const earsCompliant = isEarsCompliant(text);
|
|
24
|
+
const vaguenessFlagsFound = detectVaguenessFlags(text);
|
|
25
|
+
const vaguenessDeduction = Math.min(vaguenessFlagsFound.length * 2, 10);
|
|
26
|
+
// --- Testability Score (0-10) ---
|
|
27
|
+
let testabilityScore = 0;
|
|
28
|
+
if (NUMERIC_VALUE_REGEX.test(text)) {
|
|
29
|
+
testabilityScore += 3;
|
|
30
|
+
}
|
|
31
|
+
if (earsCompliant) {
|
|
32
|
+
testabilityScore += 3;
|
|
33
|
+
}
|
|
34
|
+
if (ENDPOINT_REGEX.test(text)) {
|
|
35
|
+
testabilityScore += 2;
|
|
36
|
+
}
|
|
37
|
+
if (SUCCESS_CONDITION_REGEX.test(text)) {
|
|
38
|
+
testabilityScore += 2;
|
|
39
|
+
}
|
|
40
|
+
testabilityScore = Math.max(0, testabilityScore - vaguenessDeduction);
|
|
41
|
+
testabilityScore = Math.min(10, testabilityScore);
|
|
42
|
+
// --- Specificity Score (0-10) ---
|
|
43
|
+
let specificityScore = 0;
|
|
44
|
+
if (MEASURABLE_THRESHOLD_REGEX.test(text) || NUMERIC_VALUE_REGEX.test(text)) {
|
|
45
|
+
specificityScore += 4;
|
|
46
|
+
}
|
|
47
|
+
if (COMPONENT_REGEX.test(text)) {
|
|
48
|
+
specificityScore += 3;
|
|
49
|
+
}
|
|
50
|
+
if (TRIGGER_REGEX.test(text)) {
|
|
51
|
+
specificityScore += 2;
|
|
52
|
+
}
|
|
53
|
+
if (ACTOR_REGEX.test(text)) {
|
|
54
|
+
specificityScore += 1;
|
|
55
|
+
}
|
|
56
|
+
specificityScore = Math.max(0, specificityScore - vaguenessDeduction);
|
|
57
|
+
specificityScore = Math.min(10, specificityScore);
|
|
58
|
+
const overallScore = Math.round(((testabilityScore + specificityScore) / 2) * 10) / 10;
|
|
59
|
+
// Generate rewrite suggestions only when score is below threshold
|
|
60
|
+
const rewriteSuggestions = overallScore < 7 ? generateRewrites(text, earsPattern) : [];
|
|
61
|
+
return {
|
|
62
|
+
text,
|
|
63
|
+
testabilityScore,
|
|
64
|
+
specificityScore,
|
|
65
|
+
earsPattern,
|
|
66
|
+
earsCompliant,
|
|
67
|
+
vaguenessFlagsFound,
|
|
68
|
+
overallScore,
|
|
69
|
+
rewriteSuggestions,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function gradeSpec(avgScore) {
|
|
73
|
+
if (avgScore >= 9) {
|
|
74
|
+
return 'A';
|
|
75
|
+
}
|
|
76
|
+
if (avgScore >= 7) {
|
|
77
|
+
return 'B';
|
|
78
|
+
}
|
|
79
|
+
if (avgScore >= 5) {
|
|
80
|
+
return 'C';
|
|
81
|
+
}
|
|
82
|
+
if (avgScore >= 3) {
|
|
83
|
+
return 'D';
|
|
84
|
+
}
|
|
85
|
+
return 'F';
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=criterion-scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"criterion-scorer.js","sourceRoot":"","sources":["../../../src/engine/ears/criterion-scorer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { EarsPattern } from '../../types/ears.js';
|
|
2
|
+
export declare function detectEarsPattern(criterion: string): EarsPattern;
|
|
3
|
+
export declare function detectVaguenessFlags(criterion: string): string[];
|
|
4
|
+
export declare function isEarsCompliant(criterion: string): boolean;
|
|
5
|
+
//# sourceMappingURL=pattern-matcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern-matcher.d.ts","sourceRoot":"","sources":["../../../src/engine/ears/pattern-matcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AA+BvD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAOhE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAOhE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE1D"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// engine/ears/pattern-matcher.ts — EARS pattern detection (SPEC-410)
|
|
2
|
+
// EARS pattern regexes (case-insensitive).
|
|
3
|
+
// Order matters: more specific patterns are tested before generic ones.
|
|
4
|
+
const EARS_PATTERN_CHECKS = [
|
|
5
|
+
{ pattern: 'conditional', regex: /\bif\b.+\bthen\b.+\bthe system shall\b/i },
|
|
6
|
+
{ pattern: 'event-driven', regex: /\bwhen\b.+\bthe system shall\b/i },
|
|
7
|
+
{ pattern: 'state-driven', regex: /\bwhile\b.+\bthe system shall\b/i },
|
|
8
|
+
{ pattern: 'optional', regex: /\bwhere\b.+\bthe system shall\b/i },
|
|
9
|
+
{ pattern: 'ubiquitous', regex: /\bthe system shall\b/i },
|
|
10
|
+
];
|
|
11
|
+
// Vagueness words that make criteria hard to test
|
|
12
|
+
const VAGUENESS_FLAGS = [
|
|
13
|
+
'should',
|
|
14
|
+
'easy',
|
|
15
|
+
'fast',
|
|
16
|
+
'user-friendly',
|
|
17
|
+
'intuitive',
|
|
18
|
+
'simple',
|
|
19
|
+
'quickly',
|
|
20
|
+
'efficiently',
|
|
21
|
+
'nice',
|
|
22
|
+
'good',
|
|
23
|
+
'better',
|
|
24
|
+
'appropriate',
|
|
25
|
+
'suitable',
|
|
26
|
+
'seamlessly',
|
|
27
|
+
'ideally',
|
|
28
|
+
];
|
|
29
|
+
export function detectEarsPattern(criterion) {
|
|
30
|
+
for (const { pattern, regex } of EARS_PATTERN_CHECKS) {
|
|
31
|
+
if (regex.test(criterion)) {
|
|
32
|
+
return pattern;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return 'none';
|
|
36
|
+
}
|
|
37
|
+
export function detectVaguenessFlags(criterion) {
|
|
38
|
+
const lower = criterion.toLowerCase();
|
|
39
|
+
return VAGUENESS_FLAGS.filter((flag) => {
|
|
40
|
+
// Match whole word to avoid false positives (e.g., "easily" vs "easy")
|
|
41
|
+
const wordBoundary = new RegExp(`\\b${flag.replace('-', '[- ]')}\\b`, 'i');
|
|
42
|
+
return wordBoundary.test(lower);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export function isEarsCompliant(criterion) {
|
|
46
|
+
return detectEarsPattern(criterion) !== 'none';
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=pattern-matcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern-matcher.js","sourceRoot":"","sources":["../../../src/engine/ears/pattern-matcher.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAIrE,2CAA2C;AAC3C,wEAAwE;AACxE,MAAM,mBAAmB,GAA8C;IACrE,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,yCAAyC,EAAE;IAC5E,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,iCAAiC,EAAE;IACrE,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,kCAAkC,EAAE;IACtE,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,kCAAkC,EAAE;IAClE,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,uBAAuB,EAAE;CAC1D,CAAC;AAEF,kDAAkD;AAClD,MAAM,eAAe,GAAsB;IACzC,QAAQ;IACR,MAAM;IACN,MAAM;IACN,eAAe;IACf,WAAW;IACX,QAAQ;IACR,SAAS;IACT,aAAa;IACb,MAAM;IACN,MAAM;IACN,QAAQ;IACR,aAAa;IACb,UAAU;IACV,YAAY;IACZ,SAAS;CACV,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,KAAK,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,mBAAmB,EAAE,CAAC;QACrD,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;IACtC,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QACrC,uEAAuE;QACvE,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC3E,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,iBAAiB,CAAC,SAAS,CAAC,KAAK,MAAM,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EarsPattern } from '../../types/ears.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate 2 EARS-format rewrite suggestions using templates.
|
|
4
|
+
* Template-driven only — no LLM calls.
|
|
5
|
+
*/
|
|
6
|
+
export declare function generateRewrites(criterion: string, _pattern: EarsPattern): string[];
|
|
7
|
+
//# sourceMappingURL=rewriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewriter.d.ts","sourceRoot":"","sources":["../../../src/engine/ears/rewriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AA2CvD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,MAAM,EAAE,CAQnF"}
|