@tmddev/tmd 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 +201 -0
- package/README.md +424 -0
- package/bin/tmd.js +3 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +92 -0
- package/dist/commands/act.d.ts +3 -0
- package/dist/commands/act.js +210 -0
- package/dist/commands/check.d.ts +3 -0
- package/dist/commands/check.js +183 -0
- package/dist/commands/do.d.ts +3 -0
- package/dist/commands/do.js +310 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.js +56 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.js +89 -0
- package/dist/commands/show.d.ts +2 -0
- package/dist/commands/show.js +69 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.js +243 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.js +5 -0
- package/dist/utils/act-processing.d.ts +64 -0
- package/dist/utils/act-processing.js +222 -0
- package/dist/utils/analysis.d.ts +34 -0
- package/dist/utils/analysis.js +159 -0
- package/dist/utils/comparison.d.ts +34 -0
- package/dist/utils/comparison.js +217 -0
- package/dist/utils/language-validator.d.ts +11 -0
- package/dist/utils/language-validator.js +39 -0
- package/dist/utils/openspec.d.ts +5 -0
- package/dist/utils/openspec.js +91 -0
- package/dist/utils/paths.d.ts +10 -0
- package/dist/utils/paths.js +33 -0
- package/dist/utils/skills.d.ts +3 -0
- package/dist/utils/skills.js +248 -0
- package/dist/utils/skillssh.d.ts +12 -0
- package/dist/utils/skillssh.js +135 -0
- package/dist/utils/standardization.d.ts +26 -0
- package/dist/utils/standardization.js +106 -0
- package/dist/utils/task-chain.d.ts +35 -0
- package/dist/utils/task-chain.js +146 -0
- package/dist/utils/task-id.d.ts +5 -0
- package/dist/utils/task-id.js +15 -0
- package/dist/utils/task-status.d.ts +6 -0
- package/dist/utils/task-status.js +61 -0
- package/dist/utils/task-validator.d.ts +42 -0
- package/dist/utils/task-validator.js +178 -0
- package/dist/utils/tasks.d.ts +27 -0
- package/dist/utils/tasks.js +125 -0
- package/dist/utils/templates.d.ts +12 -0
- package/dist/utils/templates.js +143 -0
- package/package.json +84 -0
- package/scripts/postinstall.js +92 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identify deviations from comparison results
|
|
3
|
+
*/
|
|
4
|
+
export function identifyDeviations(comparisons) {
|
|
5
|
+
const deviations = [];
|
|
6
|
+
for (const comparison of comparisons) {
|
|
7
|
+
if (comparison.status === 'met' || comparison.status === 'no-data') {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
// Determine deviation type based on goal description
|
|
11
|
+
let type = 'scope';
|
|
12
|
+
const goalLower = comparison.goal.description.toLowerCase();
|
|
13
|
+
if (goalLower.includes('time') || goalLower.includes('deadline') || goalLower.includes('schedule')) {
|
|
14
|
+
type = 'timing';
|
|
15
|
+
}
|
|
16
|
+
else if (goalLower.includes('quality') || goalLower.includes('standard') || goalLower.includes('criteria')) {
|
|
17
|
+
type = 'quality';
|
|
18
|
+
}
|
|
19
|
+
else if (goalLower.includes('resource') || goalLower.includes('budget') || goalLower.includes('cost')) {
|
|
20
|
+
type = 'resources';
|
|
21
|
+
}
|
|
22
|
+
// Determine severity
|
|
23
|
+
let severity = 'minor';
|
|
24
|
+
if (comparison.status === 'unmet') {
|
|
25
|
+
if (comparison.deviationPercent && Math.abs(comparison.deviationPercent) > 50) {
|
|
26
|
+
severity = 'critical';
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
severity = 'major';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (comparison.status === 'partial') {
|
|
33
|
+
severity = 'minor';
|
|
34
|
+
}
|
|
35
|
+
deviations.push({
|
|
36
|
+
comparison,
|
|
37
|
+
type,
|
|
38
|
+
severity,
|
|
39
|
+
description: `${comparison.goal.description}: ${comparison.status}`
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return deviations;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Analyze root causes of deviations
|
|
46
|
+
*/
|
|
47
|
+
export function analyzeRootCauses(deviations) {
|
|
48
|
+
const rootCauses = [];
|
|
49
|
+
const patterns = new Map();
|
|
50
|
+
// Count deviation types
|
|
51
|
+
for (const deviation of deviations) {
|
|
52
|
+
const key = `${deviation.type}-${deviation.severity}`;
|
|
53
|
+
patterns.set(key, (patterns.get(key) ?? 0) + 1);
|
|
54
|
+
}
|
|
55
|
+
// Analyze patterns
|
|
56
|
+
const timingCount = deviations.filter(d => d.type === 'timing').length;
|
|
57
|
+
const scopeCount = deviations.filter(d => d.type === 'scope').length;
|
|
58
|
+
const qualityCount = deviations.filter(d => d.type === 'quality').length;
|
|
59
|
+
const resourceCount = deviations.filter(d => d.type === 'resources').length;
|
|
60
|
+
// Suggest root causes based on patterns
|
|
61
|
+
if (timingCount >= 2) {
|
|
62
|
+
rootCauses.push({
|
|
63
|
+
category: 'planning',
|
|
64
|
+
description: 'Multiple timing deviations suggest planning or scheduling issues',
|
|
65
|
+
evidence: deviations.filter(d => d.type === 'timing').map(d => d.description),
|
|
66
|
+
confidence: timingCount >= 3 ? 'high' : 'medium'
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (scopeCount >= 2) {
|
|
70
|
+
rootCauses.push({
|
|
71
|
+
category: 'execution',
|
|
72
|
+
description: 'Multiple scope deviations suggest execution or scope management issues',
|
|
73
|
+
evidence: deviations.filter(d => d.type === 'scope').map(d => d.description),
|
|
74
|
+
confidence: scopeCount >= 3 ? 'high' : 'medium'
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (qualityCount >= 1) {
|
|
78
|
+
rootCauses.push({
|
|
79
|
+
category: 'execution',
|
|
80
|
+
description: 'Quality deviations suggest quality control or process issues',
|
|
81
|
+
evidence: deviations.filter(d => d.type === 'quality').map(d => d.description),
|
|
82
|
+
confidence: qualityCount >= 2 ? 'high' : 'medium'
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (resourceCount >= 1) {
|
|
86
|
+
rootCauses.push({
|
|
87
|
+
category: 'resources',
|
|
88
|
+
description: 'Resource deviations suggest resource allocation or availability issues',
|
|
89
|
+
evidence: deviations.filter(d => d.type === 'resources').map(d => d.description),
|
|
90
|
+
confidence: resourceCount >= 2 ? 'high' : 'medium'
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// If no clear pattern, suggest general planning issue
|
|
94
|
+
if (rootCauses.length === 0 && deviations.length > 0) {
|
|
95
|
+
rootCauses.push({
|
|
96
|
+
category: 'planning',
|
|
97
|
+
description: 'Multiple deviations suggest potential planning or estimation issues',
|
|
98
|
+
evidence: deviations.map(d => d.description),
|
|
99
|
+
confidence: 'low'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return rootCauses;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Recommend adjustments based on analysis
|
|
106
|
+
*/
|
|
107
|
+
export function recommendAdjustments(deviations, rootCauses) {
|
|
108
|
+
const recommendations = [];
|
|
109
|
+
const criticalDeviations = deviations.filter(d => d.severity === 'critical');
|
|
110
|
+
const majorDeviations = deviations.filter(d => d.severity === 'major');
|
|
111
|
+
// Immediate corrective actions for critical deviations
|
|
112
|
+
if (criticalDeviations.length > 0) {
|
|
113
|
+
recommendations.push({
|
|
114
|
+
priority: 'high',
|
|
115
|
+
category: 'corrective',
|
|
116
|
+
action: 'Address critical deviations immediately',
|
|
117
|
+
description: `Take immediate action to address ${String(criticalDeviations.length)} critical deviation(s). Review root causes and implement corrective measures.`
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Preventive measures for recurring issues
|
|
121
|
+
const timingRootCause = rootCauses.find(rc => rc.category === 'planning' && rc.description.includes('timing'));
|
|
122
|
+
if (timingRootCause) {
|
|
123
|
+
recommendations.push({
|
|
124
|
+
priority: 'medium',
|
|
125
|
+
category: 'preventive',
|
|
126
|
+
action: 'Improve planning and estimation processes',
|
|
127
|
+
description: 'Consider improving time estimation and planning processes to prevent future timing deviations.'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
const executionRootCause = rootCauses.find(rc => rc.category === 'execution');
|
|
131
|
+
if (executionRootCause) {
|
|
132
|
+
recommendations.push({
|
|
133
|
+
priority: 'medium',
|
|
134
|
+
category: 'preventive',
|
|
135
|
+
action: 'Strengthen execution monitoring',
|
|
136
|
+
description: 'Implement better execution monitoring and control mechanisms to catch issues earlier.'
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// Plan modifications
|
|
140
|
+
if (majorDeviations.length + criticalDeviations.length >= 3) {
|
|
141
|
+
recommendations.push({
|
|
142
|
+
priority: 'high',
|
|
143
|
+
category: 'plan-modification',
|
|
144
|
+
action: 'Review and adjust plan for next cycle',
|
|
145
|
+
description: 'Consider adjusting goals, timelines, or resource allocation in the next PDCA cycle based on current deviations.'
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// If no critical issues, suggest minor improvements
|
|
149
|
+
if (recommendations.length === 0 && deviations.length > 0) {
|
|
150
|
+
recommendations.push({
|
|
151
|
+
priority: 'low',
|
|
152
|
+
category: 'plan-modification',
|
|
153
|
+
action: 'Review minor deviations for continuous improvement',
|
|
154
|
+
description: 'While no critical issues were found, review minor deviations to identify opportunities for improvement.'
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return recommendations;
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=analysis.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface Goal {
|
|
2
|
+
description: string;
|
|
3
|
+
targetValue?: number | string;
|
|
4
|
+
deadline?: string;
|
|
5
|
+
criteria?: string;
|
|
6
|
+
index: number;
|
|
7
|
+
isQuantitative: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface Result {
|
|
10
|
+
value?: number | string;
|
|
11
|
+
description?: string;
|
|
12
|
+
source: 'execution' | 'data';
|
|
13
|
+
section?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ComparisonResult {
|
|
16
|
+
goal: Goal;
|
|
17
|
+
result?: Result;
|
|
18
|
+
status: 'met' | 'unmet' | 'over-achieved' | 'partial' | 'no-data';
|
|
19
|
+
deviation?: number;
|
|
20
|
+
deviationPercent?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Extract goals from plan.md's Goals section
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractGoals(planPath: string): Goal[];
|
|
26
|
+
/**
|
|
27
|
+
* Extract results from execution.md and data.md
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractResults(executionPath: string, dataPath: string): Result[];
|
|
30
|
+
/**
|
|
31
|
+
* Compare goals with results
|
|
32
|
+
*/
|
|
33
|
+
export declare function compareGoalsAndResults(goals: Goal[], results: Result[]): ComparisonResult[];
|
|
34
|
+
//# sourceMappingURL=comparison.d.ts.map
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'fs';
|
|
2
|
+
import yaml from 'js-yaml';
|
|
3
|
+
/**
|
|
4
|
+
* Extract goals from plan.md's Goals section
|
|
5
|
+
*/
|
|
6
|
+
export function extractGoals(planPath) {
|
|
7
|
+
if (!existsSync(planPath)) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
const content = readFileSync(planPath, 'utf-8');
|
|
11
|
+
const lines = content.split('\n');
|
|
12
|
+
const goals = [];
|
|
13
|
+
let inGoalsSection = false;
|
|
14
|
+
let goalIndex = 0;
|
|
15
|
+
for (let i = 0; i < lines.length; i++) {
|
|
16
|
+
const line = lines[i];
|
|
17
|
+
if (!line)
|
|
18
|
+
continue;
|
|
19
|
+
const trimmed = line.trim();
|
|
20
|
+
// Detect Goals section header
|
|
21
|
+
if (trimmed.startsWith('##') && trimmed.toLowerCase().includes('goals')) {
|
|
22
|
+
inGoalsSection = true;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
// Stop at next section (## header)
|
|
26
|
+
if (inGoalsSection && trimmed.startsWith('##') && !trimmed.toLowerCase().includes('goals')) {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
// Parse goal items
|
|
30
|
+
if (inGoalsSection) {
|
|
31
|
+
// Support both checkbox and bullet formats
|
|
32
|
+
const goalMatch = trimmed.match(/^-\s+(?:\[([ x])\]\s+)?(.+)$/);
|
|
33
|
+
if (goalMatch?.[2]) {
|
|
34
|
+
const goalText = goalMatch[2];
|
|
35
|
+
// Try to extract quantitative values (numbers, percentages)
|
|
36
|
+
const numberMatch = goalText.match(/(\d+(?:\.\d+)?)\s*(%|seconds?|minutes?|hours?|days?|weeks?|months?|years?|ms|kb|mb|gb|times?|x)/i);
|
|
37
|
+
const targetValue = numberMatch?.[1] ? parseFloat(numberMatch[1]) : undefined;
|
|
38
|
+
const isQuantitative = !!targetValue;
|
|
39
|
+
// Try to extract deadline (dates, timeframes)
|
|
40
|
+
const deadlineMatch = goalText.match(/(?:by|before|within|deadline:?)\s+([^\n,]+)/i);
|
|
41
|
+
const deadline = deadlineMatch?.[1] ? deadlineMatch[1].trim() : undefined;
|
|
42
|
+
const goal = {
|
|
43
|
+
description: goalText,
|
|
44
|
+
index: goalIndex++,
|
|
45
|
+
isQuantitative
|
|
46
|
+
};
|
|
47
|
+
if (targetValue !== undefined) {
|
|
48
|
+
goal.targetValue = targetValue;
|
|
49
|
+
}
|
|
50
|
+
if (deadline !== undefined) {
|
|
51
|
+
goal.deadline = deadline;
|
|
52
|
+
}
|
|
53
|
+
goals.push(goal);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return goals;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract results from execution.md and data.md
|
|
61
|
+
*/
|
|
62
|
+
export function extractResults(executionPath, dataPath) {
|
|
63
|
+
const results = [];
|
|
64
|
+
// Extract from execution.md
|
|
65
|
+
if (existsSync(executionPath)) {
|
|
66
|
+
const executionContent = readFileSync(executionPath, 'utf-8');
|
|
67
|
+
// Extract from Actions Taken section
|
|
68
|
+
const actionsMatch = executionContent.match(/## Actions Taken\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
69
|
+
if (actionsMatch?.[1]) {
|
|
70
|
+
const actions = actionsMatch[1].trim();
|
|
71
|
+
if (actions) {
|
|
72
|
+
results.push({
|
|
73
|
+
description: actions,
|
|
74
|
+
source: 'execution',
|
|
75
|
+
section: 'Actions Taken'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Extract from Data Collected section
|
|
80
|
+
const dataMatch = executionContent.match(/## Data Collected\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
81
|
+
if (dataMatch?.[1]) {
|
|
82
|
+
const data = dataMatch[1].trim();
|
|
83
|
+
if (data) {
|
|
84
|
+
// Try to extract numbers from data
|
|
85
|
+
const numberMatch = data.match(/(\d+(?:\.\d+)?)/);
|
|
86
|
+
const value = numberMatch?.[1] ? parseFloat(numberMatch[1]) : undefined;
|
|
87
|
+
const result = {
|
|
88
|
+
description: data,
|
|
89
|
+
source: 'execution',
|
|
90
|
+
section: 'Data Collected'
|
|
91
|
+
};
|
|
92
|
+
if (value !== undefined) {
|
|
93
|
+
result.value = value;
|
|
94
|
+
}
|
|
95
|
+
results.push(result);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Extract from Observations section
|
|
99
|
+
const observationsMatch = executionContent.match(/## Observations\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
100
|
+
if (observationsMatch?.[1]) {
|
|
101
|
+
const observations = observationsMatch[1].trim();
|
|
102
|
+
if (observations) {
|
|
103
|
+
results.push({
|
|
104
|
+
description: observations,
|
|
105
|
+
source: 'execution',
|
|
106
|
+
section: 'Observations'
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Extract from data.md (structured format)
|
|
112
|
+
if (existsSync(dataPath)) {
|
|
113
|
+
try {
|
|
114
|
+
const dataContent = readFileSync(dataPath, 'utf-8');
|
|
115
|
+
// Try to parse as YAML
|
|
116
|
+
try {
|
|
117
|
+
const data = yaml.load(dataContent);
|
|
118
|
+
for (const [key, value] of Object.entries(data)) {
|
|
119
|
+
results.push({
|
|
120
|
+
value: typeof value === 'number' ? value : String(value),
|
|
121
|
+
description: `${key}: ${String(value)}`,
|
|
122
|
+
source: 'data',
|
|
123
|
+
section: key
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Not YAML, try to parse as key-value pairs
|
|
129
|
+
const lines = dataContent.split('\n');
|
|
130
|
+
for (const line of lines) {
|
|
131
|
+
const kvMatch = line.match(/^-\s*([^:]+):\s*(.+)$/);
|
|
132
|
+
if (kvMatch?.[1] && kvMatch[2]) {
|
|
133
|
+
const key = kvMatch[1].trim();
|
|
134
|
+
const value = kvMatch[2].trim();
|
|
135
|
+
const numValue = parseFloat(value);
|
|
136
|
+
const resultValue = isNaN(numValue) ? value : numValue;
|
|
137
|
+
results.push({
|
|
138
|
+
value: resultValue,
|
|
139
|
+
description: `${key}: ${typeof resultValue === 'number' ? String(resultValue) : resultValue}`,
|
|
140
|
+
source: 'data',
|
|
141
|
+
section: key
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Ignore parsing errors
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return results;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Compare goals with results
|
|
155
|
+
*/
|
|
156
|
+
export function compareGoalsAndResults(goals, results) {
|
|
157
|
+
const comparisons = [];
|
|
158
|
+
for (const goal of goals) {
|
|
159
|
+
let matchedResult;
|
|
160
|
+
let status = 'no-data';
|
|
161
|
+
// Try to match goal with results
|
|
162
|
+
// Simple matching: look for similar keywords or values
|
|
163
|
+
if (goal.isQuantitative && goal.targetValue !== undefined) {
|
|
164
|
+
// For quantitative goals, try to find matching numeric values
|
|
165
|
+
for (const result of results) {
|
|
166
|
+
if (result.value !== undefined && typeof result.value === 'number') {
|
|
167
|
+
const goalValue = typeof goal.targetValue === 'number'
|
|
168
|
+
? goal.targetValue
|
|
169
|
+
: parseFloat(typeof goal.targetValue === 'string' ? goal.targetValue : '0');
|
|
170
|
+
if (!isNaN(goalValue)) {
|
|
171
|
+
// Found a numeric result, use it
|
|
172
|
+
matchedResult = result;
|
|
173
|
+
const deviation = result.value - goalValue;
|
|
174
|
+
const deviationPercent = (deviation / goalValue) * 100;
|
|
175
|
+
if (Math.abs(deviationPercent) < 5) {
|
|
176
|
+
status = 'met';
|
|
177
|
+
}
|
|
178
|
+
else if (deviationPercent > 0) {
|
|
179
|
+
status = 'over-achieved';
|
|
180
|
+
}
|
|
181
|
+
else if (Math.abs(deviationPercent) < 50) {
|
|
182
|
+
status = 'partial';
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
status = 'unmet';
|
|
186
|
+
}
|
|
187
|
+
comparisons.push({
|
|
188
|
+
goal,
|
|
189
|
+
result: matchedResult,
|
|
190
|
+
status,
|
|
191
|
+
deviation,
|
|
192
|
+
deviationPercent
|
|
193
|
+
});
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// For qualitative goals, check if there's any result
|
|
201
|
+
if (results.length > 0) {
|
|
202
|
+
matchedResult = results[0]; // Use first available result
|
|
203
|
+
// Simple heuristic: if there's a result, assume partial or met
|
|
204
|
+
status = 'partial';
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// If no match found, mark as no-data
|
|
208
|
+
if (!matchedResult) {
|
|
209
|
+
comparisons.push({
|
|
210
|
+
goal,
|
|
211
|
+
status: 'no-data'
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return comparisons;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=comparison.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language validation utilities
|
|
3
|
+
* Ensures all documents use English as per specification
|
|
4
|
+
*/
|
|
5
|
+
export declare function detectLanguage(text: string): 'en' | 'non-en';
|
|
6
|
+
export declare function validateEnglish(text: string): {
|
|
7
|
+
isValid: boolean;
|
|
8
|
+
warnings: string[];
|
|
9
|
+
};
|
|
10
|
+
export declare function warnIfNonEnglish(text: string, context?: string): void;
|
|
11
|
+
//# sourceMappingURL=language-validator.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language validation utilities
|
|
3
|
+
* Ensures all documents use English as per specification
|
|
4
|
+
*/
|
|
5
|
+
export function detectLanguage(text) {
|
|
6
|
+
// Simple heuristic: check for common non-English characters
|
|
7
|
+
// This is a basic implementation - can be enhanced with proper language detection library
|
|
8
|
+
const nonEnglishPatterns = [
|
|
9
|
+
/[\u4e00-\u9fff]/, // Chinese
|
|
10
|
+
/[\u3040-\u309f\u30a0-\u30ff]/, // Japanese
|
|
11
|
+
/[\u0400-\u04ff]/, // Cyrillic
|
|
12
|
+
/[\u0600-\u06ff]/, // Arabic
|
|
13
|
+
/[\u0590-\u05ff]/, // Hebrew
|
|
14
|
+
];
|
|
15
|
+
for (const pattern of nonEnglishPatterns) {
|
|
16
|
+
if (pattern.test(text)) {
|
|
17
|
+
return 'non-en';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return 'en';
|
|
21
|
+
}
|
|
22
|
+
export function validateEnglish(text) {
|
|
23
|
+
const warnings = [];
|
|
24
|
+
const language = detectLanguage(text);
|
|
25
|
+
if (language === 'non-en') {
|
|
26
|
+
warnings.push('Non-English content detected. All documents should use English.');
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
isValid: language === 'en',
|
|
30
|
+
warnings
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function warnIfNonEnglish(text, context = 'document') {
|
|
34
|
+
const validation = validateEnglish(text);
|
|
35
|
+
if (!validation.isValid) {
|
|
36
|
+
console.warn(`⚠ Warning: ${validation.warnings.join(', ')} (${context})`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=language-validator.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function isOpenSpecProject(): boolean;
|
|
2
|
+
export declare function generateChangeId(description: string): string;
|
|
3
|
+
export declare function createOpenSpecChange(description: string, taskId: string): string | null;
|
|
4
|
+
export declare function linkOpenSpecChange(taskId: string, changeId: string): boolean;
|
|
5
|
+
//# sourceMappingURL=openspec.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
const OPENSPEC_DIR = 'openspec';
|
|
5
|
+
const CHANGES_DIR = join(OPENSPEC_DIR, 'changes');
|
|
6
|
+
export function isOpenSpecProject() {
|
|
7
|
+
return existsSync(OPENSPEC_DIR) && existsSync(join(OPENSPEC_DIR, 'project.md'));
|
|
8
|
+
}
|
|
9
|
+
export function generateChangeId(description) {
|
|
10
|
+
// Generate OpenSpec-compatible change-id
|
|
11
|
+
const desc = description
|
|
12
|
+
.toLowerCase()
|
|
13
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
14
|
+
.replace(/^-+|-+$/g, '')
|
|
15
|
+
.substring(0, 50);
|
|
16
|
+
// OpenSpec uses verb-led format
|
|
17
|
+
return `add-${desc}`;
|
|
18
|
+
}
|
|
19
|
+
export function createOpenSpecChange(description, taskId) {
|
|
20
|
+
if (!isOpenSpecProject()) {
|
|
21
|
+
console.log(chalk.yellow(' OpenSpec project not detected'));
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const changeId = generateChangeId(description);
|
|
25
|
+
const changeDir = join(CHANGES_DIR, changeId);
|
|
26
|
+
if (!existsSync(changeDir)) {
|
|
27
|
+
mkdirSync(changeDir, { recursive: true });
|
|
28
|
+
mkdirSync(join(changeDir, 'specs'), { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
// Create proposal.md
|
|
31
|
+
const proposalContent = `# Change: ${description}
|
|
32
|
+
|
|
33
|
+
## Why
|
|
34
|
+
<!-- Describe the problem or opportunity -->
|
|
35
|
+
|
|
36
|
+
## What Changes
|
|
37
|
+
- <!-- List changes -->
|
|
38
|
+
|
|
39
|
+
## Impact
|
|
40
|
+
- Affected specs: <!-- List capabilities -->
|
|
41
|
+
- Affected code: <!-- List files/systems -->
|
|
42
|
+
- TMD Task: ${taskId}
|
|
43
|
+
`;
|
|
44
|
+
writeFileSync(join(changeDir, 'proposal.md'), proposalContent);
|
|
45
|
+
// Create tasks.md
|
|
46
|
+
const tasksContent = `# Implementation Tasks
|
|
47
|
+
|
|
48
|
+
## 1. Implementation
|
|
49
|
+
- [ ] <!-- Add tasks -->
|
|
50
|
+
`;
|
|
51
|
+
writeFileSync(join(changeDir, 'tasks.md'), tasksContent);
|
|
52
|
+
// Link TMD task to OpenSpec change
|
|
53
|
+
const metadataPath = join('tmd', 'plan', taskId, 'metadata.json');
|
|
54
|
+
if (existsSync(metadataPath)) {
|
|
55
|
+
const metadata = JSON.parse(readFileSync(metadataPath, 'utf-8'));
|
|
56
|
+
metadata['openspecChange'] = changeId;
|
|
57
|
+
writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
58
|
+
}
|
|
59
|
+
console.log(chalk.green(` Created OpenSpec change: ${changeId}`));
|
|
60
|
+
console.log(chalk.blue(` Directory: ${changeDir}`));
|
|
61
|
+
return changeId;
|
|
62
|
+
}
|
|
63
|
+
export function linkOpenSpecChange(taskId, changeId) {
|
|
64
|
+
if (!isOpenSpecProject()) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const changeDir = join(CHANGES_DIR, changeId);
|
|
68
|
+
if (!existsSync(changeDir)) {
|
|
69
|
+
console.log(chalk.red(` OpenSpec change not found: ${changeId}`));
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// Link in task metadata
|
|
73
|
+
const metadataPath = join('tmd', 'plan', taskId, 'metadata.json');
|
|
74
|
+
if (existsSync(metadataPath)) {
|
|
75
|
+
const metadata = JSON.parse(readFileSync(metadataPath, 'utf-8'));
|
|
76
|
+
metadata['openspecChange'] = changeId;
|
|
77
|
+
writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
78
|
+
}
|
|
79
|
+
// Link in OpenSpec change proposal
|
|
80
|
+
const proposalPath = join(changeDir, 'proposal.md');
|
|
81
|
+
if (existsSync(proposalPath)) {
|
|
82
|
+
const content = readFileSync(proposalPath, 'utf-8');
|
|
83
|
+
if (!content.includes(`TMD Task: ${taskId}`)) {
|
|
84
|
+
const updated = content.replace(/## Impact\n/, `## Impact\n- TMD Task: ${taskId}\n`);
|
|
85
|
+
writeFileSync(proposalPath, updated);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
console.log(chalk.green(` Linked to OpenSpec change: ${changeId}`));
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=openspec.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function getTmdDir(): string;
|
|
2
|
+
export declare function getTaskDir(phase: string, taskId: string): string;
|
|
3
|
+
export declare function ensureDir(path: string): void;
|
|
4
|
+
export declare function getPlanDir(taskId: string): string;
|
|
5
|
+
export declare function getDoDir(taskId: string): string;
|
|
6
|
+
export declare function getCheckDir(taskId: string): string;
|
|
7
|
+
export declare function getActDir(taskId: string): string;
|
|
8
|
+
export declare function getSkillsDir(): string;
|
|
9
|
+
export declare function getSkillDir(skillName: string): string;
|
|
10
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
3
|
+
const TMD_DIR = 'tmd';
|
|
4
|
+
export function getTmdDir() {
|
|
5
|
+
return TMD_DIR;
|
|
6
|
+
}
|
|
7
|
+
export function getTaskDir(phase, taskId) {
|
|
8
|
+
return join(TMD_DIR, phase, taskId);
|
|
9
|
+
}
|
|
10
|
+
export function ensureDir(path) {
|
|
11
|
+
if (!existsSync(path)) {
|
|
12
|
+
mkdirSync(path, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function getPlanDir(taskId) {
|
|
16
|
+
return getTaskDir('plan', taskId);
|
|
17
|
+
}
|
|
18
|
+
export function getDoDir(taskId) {
|
|
19
|
+
return getTaskDir('do', taskId);
|
|
20
|
+
}
|
|
21
|
+
export function getCheckDir(taskId) {
|
|
22
|
+
return getTaskDir('check', taskId);
|
|
23
|
+
}
|
|
24
|
+
export function getActDir(taskId) {
|
|
25
|
+
return getTaskDir('act', taskId);
|
|
26
|
+
}
|
|
27
|
+
export function getSkillsDir() {
|
|
28
|
+
return join(TMD_DIR, 'skills');
|
|
29
|
+
}
|
|
30
|
+
export function getSkillDir(skillName) {
|
|
31
|
+
return join(getSkillsDir(), skillName);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=paths.js.map
|