@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.
Files changed (53) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +424 -0
  3. package/bin/tmd.js +3 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.js +92 -0
  6. package/dist/commands/act.d.ts +3 -0
  7. package/dist/commands/act.js +210 -0
  8. package/dist/commands/check.d.ts +3 -0
  9. package/dist/commands/check.js +183 -0
  10. package/dist/commands/do.d.ts +3 -0
  11. package/dist/commands/do.js +310 -0
  12. package/dist/commands/list.d.ts +3 -0
  13. package/dist/commands/list.js +56 -0
  14. package/dist/commands/plan.d.ts +3 -0
  15. package/dist/commands/plan.js +89 -0
  16. package/dist/commands/show.d.ts +2 -0
  17. package/dist/commands/show.js +69 -0
  18. package/dist/commands/skills.d.ts +3 -0
  19. package/dist/commands/skills.js +243 -0
  20. package/dist/types.d.ts +79 -0
  21. package/dist/types.js +5 -0
  22. package/dist/utils/act-processing.d.ts +64 -0
  23. package/dist/utils/act-processing.js +222 -0
  24. package/dist/utils/analysis.d.ts +34 -0
  25. package/dist/utils/analysis.js +159 -0
  26. package/dist/utils/comparison.d.ts +34 -0
  27. package/dist/utils/comparison.js +217 -0
  28. package/dist/utils/language-validator.d.ts +11 -0
  29. package/dist/utils/language-validator.js +39 -0
  30. package/dist/utils/openspec.d.ts +5 -0
  31. package/dist/utils/openspec.js +91 -0
  32. package/dist/utils/paths.d.ts +10 -0
  33. package/dist/utils/paths.js +33 -0
  34. package/dist/utils/skills.d.ts +3 -0
  35. package/dist/utils/skills.js +248 -0
  36. package/dist/utils/skillssh.d.ts +12 -0
  37. package/dist/utils/skillssh.js +135 -0
  38. package/dist/utils/standardization.d.ts +26 -0
  39. package/dist/utils/standardization.js +106 -0
  40. package/dist/utils/task-chain.d.ts +35 -0
  41. package/dist/utils/task-chain.js +146 -0
  42. package/dist/utils/task-id.d.ts +5 -0
  43. package/dist/utils/task-id.js +15 -0
  44. package/dist/utils/task-status.d.ts +6 -0
  45. package/dist/utils/task-status.js +61 -0
  46. package/dist/utils/task-validator.d.ts +42 -0
  47. package/dist/utils/task-validator.js +178 -0
  48. package/dist/utils/tasks.d.ts +27 -0
  49. package/dist/utils/tasks.js +125 -0
  50. package/dist/utils/templates.d.ts +12 -0
  51. package/dist/utils/templates.js +143 -0
  52. package/package.json +84 -0
  53. 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
@@ -0,0 +1,3 @@
1
+ export declare function createSkill(description: string, taskId: string): string | null;
2
+ export declare function executeSkill(skillName: string, _taskId: string, parallel?: boolean, maxConcurrency?: number): Promise<void>;
3
+ //# sourceMappingURL=skills.d.ts.map