@tmddev/tmd 0.1.0 → 0.2.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/README.md +195 -3
- package/dist/cli.js +70 -10
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +60 -0
- package/dist/commands/pipeline.d.ts +26 -0
- package/dist/commands/pipeline.js +168 -0
- package/dist/commands/schemas.d.ts +4 -0
- package/dist/commands/schemas.js +57 -0
- package/dist/commands/skills.d.ts +1 -1
- package/dist/commands/skills.js +247 -162
- package/dist/commands/validate.d.ts +9 -0
- package/dist/commands/validate.js +179 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +65 -0
- package/dist/tmd-skills +0 -0
- package/dist/types.d.ts +21 -1
- package/dist/utils/github.d.ts +18 -0
- package/dist/utils/github.js +165 -0
- package/dist/utils/paths.d.ts +1 -0
- package/dist/utils/paths.js +4 -0
- package/dist/utils/pipeline-config.d.ts +37 -0
- package/dist/utils/pipeline-config.js +117 -0
- package/dist/utils/pipeline.d.ts +81 -0
- package/dist/utils/pipeline.js +580 -0
- package/dist/utils/skills.d.ts +10 -2
- package/dist/utils/skills.js +152 -150
- package/dist/utils/skillssh.d.ts +1 -1
- package/dist/utils/skillssh.js +39 -27
- package/dist/utils/step-executor.d.ts +58 -0
- package/dist/utils/step-executor.js +374 -0
- package/dist/utils/templates.d.ts +1 -0
- package/dist/utils/templates.js +30 -0
- package/package.json +22 -21
- package/scripts/postinstall.js +11 -8
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Step executors for automated task and skill execution
|
|
3
|
+
*/
|
|
4
|
+
import { exec } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync, unlinkSync } from 'fs';
|
|
7
|
+
import { resolve } from 'path';
|
|
8
|
+
import { pathToFileURL } from 'url';
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
/**
|
|
11
|
+
* Bash step executor - executes shell commands
|
|
12
|
+
*/
|
|
13
|
+
export class BashStepExecutor {
|
|
14
|
+
type = 'bash';
|
|
15
|
+
async execute(step, context) {
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
const command = step['command'];
|
|
18
|
+
const ignoreErrors = step['ignoreErrors'] ?? false;
|
|
19
|
+
const timeout = step['timeout'] ?? 60000; // Default 60s timeout
|
|
20
|
+
if (!command) {
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
error: 'No command specified for bash step',
|
|
24
|
+
duration: Date.now() - startTime
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (context.dryRun) {
|
|
28
|
+
return {
|
|
29
|
+
success: true,
|
|
30
|
+
output: `[DRY RUN] Would execute: ${command}`,
|
|
31
|
+
duration: Date.now() - startTime
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
36
|
+
cwd: context.workingDir,
|
|
37
|
+
env: { ...process.env, ...context.env },
|
|
38
|
+
timeout
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
success: true,
|
|
42
|
+
output: stdout,
|
|
43
|
+
...(stderr ? { error: stderr } : {}),
|
|
44
|
+
exitCode: 0,
|
|
45
|
+
duration: Date.now() - startTime
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const execError = error;
|
|
50
|
+
const duration = Date.now() - startTime;
|
|
51
|
+
const errMsg = execError.stderr ?? execError.message;
|
|
52
|
+
if (ignoreErrors) {
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
output: execError.stdout ?? '',
|
|
56
|
+
...(errMsg != null ? { error: errMsg } : {}),
|
|
57
|
+
exitCode: execError.code ?? 1,
|
|
58
|
+
duration
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
output: execError.stdout ?? '',
|
|
64
|
+
error: errMsg ?? String(error),
|
|
65
|
+
exitCode: execError.code ?? 1,
|
|
66
|
+
duration
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* File step executor - performs file operations
|
|
73
|
+
*/
|
|
74
|
+
export class FileStepExecutor {
|
|
75
|
+
type = 'file';
|
|
76
|
+
async execute(step, context) {
|
|
77
|
+
await Promise.resolve(); // satisfy require-await; all ops are sync
|
|
78
|
+
const startTime = Date.now();
|
|
79
|
+
const operation = step['operation'];
|
|
80
|
+
const path = step['path'];
|
|
81
|
+
const content = step['content'];
|
|
82
|
+
const destination = step['destination'];
|
|
83
|
+
if (!operation) {
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
error: 'No operation specified for file step',
|
|
87
|
+
duration: Date.now() - startTime
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (!path) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: 'No path specified for file step',
|
|
94
|
+
duration: Date.now() - startTime
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (context.dryRun) {
|
|
98
|
+
return {
|
|
99
|
+
success: true,
|
|
100
|
+
output: `[DRY RUN] Would perform file ${operation} on: ${path}`,
|
|
101
|
+
duration: Date.now() - startTime
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
switch (operation) {
|
|
106
|
+
case 'read': {
|
|
107
|
+
if (!existsSync(path)) {
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: `File not found: ${path}`,
|
|
111
|
+
duration: Date.now() - startTime
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const fileContent = readFileSync(path, 'utf-8');
|
|
115
|
+
return {
|
|
116
|
+
success: true,
|
|
117
|
+
output: fileContent,
|
|
118
|
+
data: { content: fileContent, path },
|
|
119
|
+
duration: Date.now() - startTime
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
case 'write': {
|
|
123
|
+
if (content === undefined) {
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
error: 'No content specified for write operation',
|
|
127
|
+
duration: Date.now() - startTime
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
writeFileSync(path, content);
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
output: `Written ${String(content.length)} bytes to ${path}`,
|
|
134
|
+
data: { path, bytesWritten: content.length },
|
|
135
|
+
duration: Date.now() - startTime
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
case 'copy': {
|
|
139
|
+
if (!destination) {
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
error: 'No destination specified for copy operation',
|
|
143
|
+
duration: Date.now() - startTime
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (!existsSync(path)) {
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
error: `Source file not found: ${path}`,
|
|
150
|
+
duration: Date.now() - startTime
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
copyFileSync(path, destination);
|
|
154
|
+
return {
|
|
155
|
+
success: true,
|
|
156
|
+
output: `Copied ${path} to ${destination}`,
|
|
157
|
+
data: { source: path, destination },
|
|
158
|
+
duration: Date.now() - startTime
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
case 'delete': {
|
|
162
|
+
if (!existsSync(path)) {
|
|
163
|
+
return {
|
|
164
|
+
success: true,
|
|
165
|
+
output: `File does not exist: ${path}`,
|
|
166
|
+
duration: Date.now() - startTime
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
unlinkSync(path);
|
|
170
|
+
return {
|
|
171
|
+
success: true,
|
|
172
|
+
output: `Deleted ${path}`,
|
|
173
|
+
data: { path },
|
|
174
|
+
duration: Date.now() - startTime
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
case 'exists': {
|
|
178
|
+
const exists = existsSync(path);
|
|
179
|
+
return {
|
|
180
|
+
success: true,
|
|
181
|
+
output: exists ? `File exists: ${path}` : `File does not exist: ${path}`,
|
|
182
|
+
data: { path, exists },
|
|
183
|
+
duration: Date.now() - startTime
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
default:
|
|
187
|
+
return {
|
|
188
|
+
success: false,
|
|
189
|
+
error: `Unknown file operation: ${operation}`,
|
|
190
|
+
duration: Date.now() - startTime
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
return {
|
|
196
|
+
success: false,
|
|
197
|
+
error: error instanceof Error ? error.message : String(error),
|
|
198
|
+
duration: Date.now() - startTime
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* API step executor - makes HTTP requests
|
|
205
|
+
*/
|
|
206
|
+
export class ApiStepExecutor {
|
|
207
|
+
type = 'api';
|
|
208
|
+
async execute(step, context) {
|
|
209
|
+
const startTime = Date.now();
|
|
210
|
+
const url = step['url'];
|
|
211
|
+
const method = (step['method'] ?? 'GET').toUpperCase();
|
|
212
|
+
const headers = step['headers'];
|
|
213
|
+
const body = step['body'];
|
|
214
|
+
const timeout = step['timeout'] ?? 30000;
|
|
215
|
+
if (!url) {
|
|
216
|
+
return {
|
|
217
|
+
success: false,
|
|
218
|
+
error: 'No URL specified for API step',
|
|
219
|
+
duration: Date.now() - startTime
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (context.dryRun) {
|
|
223
|
+
return {
|
|
224
|
+
success: true,
|
|
225
|
+
output: `[DRY RUN] Would make ${method} request to: ${url}`,
|
|
226
|
+
duration: Date.now() - startTime
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const controller = new AbortController();
|
|
231
|
+
const timeoutId = setTimeout(() => { controller.abort(); }, timeout);
|
|
232
|
+
const requestBody = body
|
|
233
|
+
? typeof body === 'string' ? body : JSON.stringify(body)
|
|
234
|
+
: undefined;
|
|
235
|
+
const response = await fetch(url, {
|
|
236
|
+
method,
|
|
237
|
+
headers: {
|
|
238
|
+
'Content-Type': 'application/json',
|
|
239
|
+
...headers
|
|
240
|
+
},
|
|
241
|
+
...(requestBody !== undefined ? { body: requestBody } : {}),
|
|
242
|
+
signal: controller.signal
|
|
243
|
+
});
|
|
244
|
+
clearTimeout(timeoutId);
|
|
245
|
+
const responseText = await response.text();
|
|
246
|
+
let responseData;
|
|
247
|
+
try {
|
|
248
|
+
responseData = JSON.parse(responseText);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
responseData = responseText;
|
|
252
|
+
}
|
|
253
|
+
if (!response.ok) {
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
output: responseText,
|
|
257
|
+
error: `HTTP ${String(response.status)}: ${response.statusText}`,
|
|
258
|
+
data: { status: response.status, response: responseData },
|
|
259
|
+
duration: Date.now() - startTime
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
success: true,
|
|
264
|
+
output: responseText,
|
|
265
|
+
data: { status: response.status, response: responseData },
|
|
266
|
+
duration: Date.now() - startTime
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
return {
|
|
271
|
+
success: false,
|
|
272
|
+
error: error instanceof Error ? error.message : String(error),
|
|
273
|
+
duration: Date.now() - startTime
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Script step executor - executes Node.js scripts
|
|
280
|
+
*/
|
|
281
|
+
export class ScriptStepExecutor {
|
|
282
|
+
type = 'script';
|
|
283
|
+
async execute(step, context) {
|
|
284
|
+
const startTime = Date.now();
|
|
285
|
+
const scriptPath = step['script'];
|
|
286
|
+
const args = step['args'];
|
|
287
|
+
const timeout = step['timeout'] ?? 60000;
|
|
288
|
+
if (!scriptPath) {
|
|
289
|
+
return {
|
|
290
|
+
success: false,
|
|
291
|
+
error: 'No script path specified for script step',
|
|
292
|
+
duration: Date.now() - startTime
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
if (context.dryRun) {
|
|
296
|
+
return {
|
|
297
|
+
success: true,
|
|
298
|
+
output: `[DRY RUN] Would execute script: ${scriptPath}`,
|
|
299
|
+
duration: Date.now() - startTime
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
// Resolve script path relative to working directory
|
|
304
|
+
const fullScriptPath = resolve(context.workingDir, scriptPath);
|
|
305
|
+
if (!existsSync(fullScriptPath)) {
|
|
306
|
+
return {
|
|
307
|
+
success: false,
|
|
308
|
+
error: `Script not found: ${fullScriptPath}`,
|
|
309
|
+
duration: Date.now() - startTime
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
const scriptModule = await import(pathToFileURL(fullScriptPath).href);
|
|
313
|
+
const scriptFn = scriptModule.default ?? scriptModule.main ?? scriptModule.execute;
|
|
314
|
+
if (typeof scriptFn !== 'function') {
|
|
315
|
+
return {
|
|
316
|
+
success: false,
|
|
317
|
+
error: `Script does not export a default function, main, or execute function`,
|
|
318
|
+
duration: Date.now() - startTime
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
// Execute script with timeout
|
|
322
|
+
const controller = new AbortController();
|
|
323
|
+
const timeoutId = setTimeout(() => { controller.abort(); }, timeout);
|
|
324
|
+
const result = await Promise.race([
|
|
325
|
+
scriptFn(args ?? {}, context),
|
|
326
|
+
new Promise((_, reject) => {
|
|
327
|
+
controller.signal.addEventListener('abort', () => {
|
|
328
|
+
reject(new Error(`Script execution timeout after ${String(timeout)}ms`));
|
|
329
|
+
});
|
|
330
|
+
})
|
|
331
|
+
]);
|
|
332
|
+
clearTimeout(timeoutId);
|
|
333
|
+
return {
|
|
334
|
+
success: result.success,
|
|
335
|
+
duration: Date.now() - startTime,
|
|
336
|
+
...(result.output !== undefined && { output: result.output }),
|
|
337
|
+
...(result.error !== undefined && { error: result.error }),
|
|
338
|
+
...(result.data !== undefined && { data: result.data })
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
return {
|
|
343
|
+
success: false,
|
|
344
|
+
error: error instanceof Error ? error.message : String(error),
|
|
345
|
+
duration: Date.now() - startTime
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Registry of step executors
|
|
351
|
+
const executors = new Map();
|
|
352
|
+
// Register built-in executors (no optional deps: no image here; see step-executor-image)
|
|
353
|
+
executors.set('bash', new BashStepExecutor());
|
|
354
|
+
executors.set('file', new FileStepExecutor());
|
|
355
|
+
executors.set('api', new ApiStepExecutor());
|
|
356
|
+
executors.set('script', new ScriptStepExecutor());
|
|
357
|
+
export function getStepExecutor(type) {
|
|
358
|
+
return Promise.resolve(executors.get(type));
|
|
359
|
+
}
|
|
360
|
+
export function registerStepExecutor(executor) {
|
|
361
|
+
executors.set(executor.type, executor);
|
|
362
|
+
}
|
|
363
|
+
export async function executeStep(step, context) {
|
|
364
|
+
const executor = await getStepExecutor(step.type);
|
|
365
|
+
if (!executor) {
|
|
366
|
+
return {
|
|
367
|
+
success: false,
|
|
368
|
+
error: `No executor found for step type: ${step.type}`,
|
|
369
|
+
duration: 0
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
return executor.execute(step, context);
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=step-executor.js.map
|
|
@@ -9,4 +9,5 @@ export declare const improvementTemplate = "# Improvement Actions: {{taskId}}\n\
|
|
|
9
9
|
export declare const resourcesTemplate = "# Resources: {{description}}\n\n## People / Roles\n<!-- Who is needed for this task? -->\n-\n\n## Tools & Systems\n<!-- What tools, systems, or infrastructure is required? -->\n-\n\n## Data Requirements\n<!-- What data or information is needed? -->\n-\n\n## Time Estimate\n<!-- Estimated effort and timeline -->\n- Effort:\n- Timeline:\n";
|
|
10
10
|
export declare const standardizationTemplate = "# Standardization: {{taskId}}\n\n## Successful Practices\n<!-- Document practices that worked well -->\n\n## Reusable Patterns\n<!-- Patterns that can be reused -->\n\n## Standard Operating Procedures\n<!-- Document SOPs if applicable -->\n";
|
|
11
11
|
export declare function renderTemplate(template: string, vars: Record<string, string>): string;
|
|
12
|
+
export declare const pipelineConfigTemplate = "# Pipeline Configuration\n# This file controls automated PDCA cycle execution\n\nversion: \"1.0\"\n\nphases:\n # Do phase: Execute tasks and collect data\n do:\n auto: true # Enable automatic execution\n parallel: false # Execute tasks sequentially (set to true for concurrent)\n maxConcurrency: 4 # Max concurrent tasks when parallel is true\n successCriteria:\n minTasksCompleted: \"100%\" # Percentage or absolute number required\n\n # Check phase: Evaluate results and analyze deviations\n check:\n auto: true # Enable automatic comparison\n analyze: true # Perform root cause analysis\n recommend: true # Generate recommendations\n successCriteria:\n allowPartialGoals: true # Continue even if some goals are partial\n allowUnmetGoals: false # Fail if any goals are unmet (set to true to allow)\n\n # Act phase: Process results and take improvement actions\n act:\n auto: true # Enable automatic processing\n standardize: true # Document successful practices\n carryForward: true # Generate next cycle items for unresolved issues\n completeOnSuccess: true # Mark task as completed when pipeline succeeds\n";
|
|
12
13
|
//# sourceMappingURL=templates.d.ts.map
|
package/dist/utils/templates.js
CHANGED
|
@@ -140,4 +140,34 @@ export function renderTemplate(template, vars) {
|
|
|
140
140
|
}
|
|
141
141
|
return result;
|
|
142
142
|
}
|
|
143
|
+
export const pipelineConfigTemplate = `# Pipeline Configuration
|
|
144
|
+
# This file controls automated PDCA cycle execution
|
|
145
|
+
|
|
146
|
+
version: "1.0"
|
|
147
|
+
|
|
148
|
+
phases:
|
|
149
|
+
# Do phase: Execute tasks and collect data
|
|
150
|
+
do:
|
|
151
|
+
auto: true # Enable automatic execution
|
|
152
|
+
parallel: false # Execute tasks sequentially (set to true for concurrent)
|
|
153
|
+
maxConcurrency: 4 # Max concurrent tasks when parallel is true
|
|
154
|
+
successCriteria:
|
|
155
|
+
minTasksCompleted: "100%" # Percentage or absolute number required
|
|
156
|
+
|
|
157
|
+
# Check phase: Evaluate results and analyze deviations
|
|
158
|
+
check:
|
|
159
|
+
auto: true # Enable automatic comparison
|
|
160
|
+
analyze: true # Perform root cause analysis
|
|
161
|
+
recommend: true # Generate recommendations
|
|
162
|
+
successCriteria:
|
|
163
|
+
allowPartialGoals: true # Continue even if some goals are partial
|
|
164
|
+
allowUnmetGoals: false # Fail if any goals are unmet (set to true to allow)
|
|
165
|
+
|
|
166
|
+
# Act phase: Process results and take improvement actions
|
|
167
|
+
act:
|
|
168
|
+
auto: true # Enable automatic processing
|
|
169
|
+
standardize: true # Document successful practices
|
|
170
|
+
carryForward: true # Generate next cycle items for unresolved issues
|
|
171
|
+
completeOnSuccess: true # Mark task as completed when pipeline succeeds
|
|
172
|
+
`;
|
|
143
173
|
//# sourceMappingURL=templates.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tmddev/tmd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Task Markdown Driven - A lightweight PDCA cycle management framework integrated with OpenSpec",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -27,23 +27,6 @@
|
|
|
27
27
|
"!dist/**/__tests__",
|
|
28
28
|
"!dist/**/*.map"
|
|
29
29
|
],
|
|
30
|
-
"scripts": {
|
|
31
|
-
"lint": "eslint src/",
|
|
32
|
-
"build": "tsx build.ts",
|
|
33
|
-
"dev": "tsc --watch",
|
|
34
|
-
"dev:cli": "pnpm build && node bin/tmd.js",
|
|
35
|
-
"test": "vitest run",
|
|
36
|
-
"test:watch": "vitest",
|
|
37
|
-
"test:ui": "vitest --ui",
|
|
38
|
-
"test:coverage": "vitest --coverage",
|
|
39
|
-
"prepare": "pnpm run build",
|
|
40
|
-
"prepublishOnly": "pnpm run build",
|
|
41
|
-
"postinstall": "node scripts/postinstall.js",
|
|
42
|
-
"check:pack-version": "node scripts/pack-version-check.mjs",
|
|
43
|
-
"release": "pnpm run release:ci",
|
|
44
|
-
"release:ci": "pnpm run check:pack-version",
|
|
45
|
-
"changeset": "changeset"
|
|
46
|
-
},
|
|
47
30
|
"keywords": [
|
|
48
31
|
"pdca",
|
|
49
32
|
"openspec",
|
|
@@ -60,7 +43,7 @@
|
|
|
60
43
|
"url": "https://github.com/sdd330/tmd.git"
|
|
61
44
|
},
|
|
62
45
|
"author": "TMD Contributors",
|
|
63
|
-
"license": "
|
|
46
|
+
"license": "Apache-2.0",
|
|
64
47
|
"dependencies": {
|
|
65
48
|
"commander": "^13.1.0",
|
|
66
49
|
"js-yaml": "^4.1.0",
|
|
@@ -79,6 +62,24 @@
|
|
|
79
62
|
"vitest": "^3.2.4"
|
|
80
63
|
},
|
|
81
64
|
"engines": {
|
|
82
|
-
"node": ">=20.19.0"
|
|
65
|
+
"node": ">=20.19.0",
|
|
66
|
+
"bun": ">=1.0"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"lint": "eslint src/",
|
|
70
|
+
"build": "tsx build.ts",
|
|
71
|
+
"build:native": "node scripts/build-native.mjs",
|
|
72
|
+
"dev": "tsc --watch",
|
|
73
|
+
"dev:cli": "pnpm build && node bin/tmd.js",
|
|
74
|
+
"test": "vitest run",
|
|
75
|
+
"test:watch": "vitest",
|
|
76
|
+
"test:ui": "vitest --ui",
|
|
77
|
+
"test:coverage": "vitest --coverage",
|
|
78
|
+
"postinstall": "node scripts/postinstall.js || bun scripts/postinstall.js",
|
|
79
|
+
"check:pack-version": "node scripts/pack-version-check.mjs",
|
|
80
|
+
"check:docs-english": "node scripts/check-docs-english.mjs",
|
|
81
|
+
"release": "pnpm run release:ci",
|
|
82
|
+
"release:ci": "pnpm run check:pack-version",
|
|
83
|
+
"changeset": "changeset"
|
|
83
84
|
}
|
|
84
|
-
}
|
|
85
|
+
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Postinstall script for TMD
|
|
4
|
+
* Postinstall script for TMD. Supports Node and Bun (fs, path, process.env are compatible).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* - CI=true
|
|
8
|
-
* - TMD_NO_POSTINSTALL=1
|
|
9
|
-
* - dist/
|
|
6
|
+
* Runs after npm/pnpm/bun install unless:
|
|
7
|
+
* - CI=true
|
|
8
|
+
* - TMD_NO_POSTINSTALL=1
|
|
9
|
+
* - dist/ does not exist (dev setup)
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* Never fails the install; errors are caught and handled.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { promises as fs } from 'fs';
|
|
15
15
|
import path from 'path';
|
|
16
16
|
import { fileURLToPath } from 'url';
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
const __dirname =
|
|
18
|
+
// Bun: import.meta.dirname; Node: derive from import.meta.url
|
|
19
|
+
const __dirname =
|
|
20
|
+
typeof import.meta.dirname === 'string'
|
|
21
|
+
? import.meta.dirname
|
|
22
|
+
: path.dirname(fileURLToPath(import.meta.url));
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
25
|
* Check if we should skip installation
|