@duypham93/openkit 0.2.5 → 0.2.6
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/package.json
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
+
function removePathIfPresent(targetPath) {
|
|
5
|
+
try {
|
|
6
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
7
|
+
} catch {
|
|
8
|
+
// ignore cleanup failures
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
function ensureParent(filePath) {
|
|
5
13
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
6
14
|
}
|
|
@@ -22,7 +30,7 @@ function createSymlinkOrCopy({ linkPath, targetPath, type = 'file' }) {
|
|
|
22
30
|
|
|
23
31
|
try {
|
|
24
32
|
if (fs.existsSync(linkPath) || fs.lstatSync(linkPath)) {
|
|
25
|
-
|
|
33
|
+
removePathIfPresent(linkPath);
|
|
26
34
|
}
|
|
27
35
|
} catch {
|
|
28
36
|
// ignore cleanup misses
|
|
@@ -42,28 +50,40 @@ function createSymlinkOrCopy({ linkPath, targetPath, type = 'file' }) {
|
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
52
|
|
|
53
|
+
function createIfMissing(createdPaths, { linkPath, targetPath, type = 'file' }) {
|
|
54
|
+
if (fs.existsSync(linkPath)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const mode = createSymlinkOrCopy({ linkPath, targetPath, type });
|
|
59
|
+
createdPaths.push(linkPath);
|
|
60
|
+
return mode;
|
|
61
|
+
}
|
|
62
|
+
|
|
45
63
|
export function ensureWorkspaceShim(paths) {
|
|
64
|
+
const createdPaths = [];
|
|
65
|
+
|
|
46
66
|
fs.mkdirSync(paths.workspaceShimDir, { recursive: true });
|
|
47
67
|
|
|
48
|
-
|
|
68
|
+
createIfMissing(createdPaths, {
|
|
49
69
|
linkPath: paths.workspaceShimAgentsPath,
|
|
50
70
|
targetPath: path.join(paths.kitRoot, 'AGENTS.md'),
|
|
51
71
|
type: 'file',
|
|
52
72
|
});
|
|
53
73
|
|
|
54
|
-
|
|
74
|
+
createIfMissing(createdPaths, {
|
|
55
75
|
linkPath: paths.workspaceShimContextDir,
|
|
56
76
|
targetPath: path.join(paths.kitRoot, 'context'),
|
|
57
77
|
type: 'dir',
|
|
58
78
|
});
|
|
59
79
|
|
|
60
|
-
|
|
80
|
+
createIfMissing(createdPaths, {
|
|
61
81
|
linkPath: paths.workspaceShimTemplatesDir,
|
|
62
82
|
targetPath: path.join(paths.kitRoot, 'docs', 'templates'),
|
|
63
83
|
type: 'dir',
|
|
64
84
|
});
|
|
65
85
|
|
|
66
|
-
|
|
86
|
+
createIfMissing(createdPaths, {
|
|
67
87
|
linkPath: paths.workspaceShimWorkflowStatePath,
|
|
68
88
|
targetPath: paths.workflowStatePath,
|
|
69
89
|
type: 'file',
|
|
@@ -85,20 +105,83 @@ if (result.error) {
|
|
|
85
105
|
process.exit(typeof result.status === 'number' ? result.status : 1);
|
|
86
106
|
`;
|
|
87
107
|
|
|
88
|
-
|
|
108
|
+
if (!fs.existsSync(paths.workspaceShimWorkflowCliPath)) {
|
|
109
|
+
writeFile(paths.workspaceShimWorkflowCliPath, workflowCli, 0o755);
|
|
110
|
+
createdPaths.push(paths.workspaceShimWorkflowCliPath);
|
|
111
|
+
}
|
|
89
112
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
113
|
+
createIfMissing(createdPaths, {
|
|
114
|
+
linkPath: path.join(paths.projectRoot, 'AGENTS.md'),
|
|
115
|
+
targetPath: paths.workspaceShimAgentsPath,
|
|
116
|
+
type: 'file',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
createIfMissing(createdPaths, {
|
|
120
|
+
linkPath: path.join(paths.projectRoot, 'context'),
|
|
121
|
+
targetPath: paths.workspaceShimContextDir,
|
|
122
|
+
type: 'dir',
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
createIfMissing(createdPaths, {
|
|
126
|
+
linkPath: path.join(paths.projectRoot, '.opencode', 'workflow-state.json'),
|
|
127
|
+
targetPath: paths.workspaceShimWorkflowStatePath,
|
|
128
|
+
type: 'file',
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (!fs.existsSync(path.join(paths.projectRoot, '.opencode', 'workflow-state.js'))) {
|
|
132
|
+
const rootWorkflowCli = `#!/usr/bin/env node
|
|
133
|
+
import { spawnSync } from 'node:child_process';
|
|
134
|
+
|
|
135
|
+
const args = process.argv.slice(2);
|
|
136
|
+
const result = spawnSync(process.execPath, [${JSON.stringify(paths.workspaceShimWorkflowCliPath)}, ...args], {
|
|
137
|
+
stdio: 'inherit',
|
|
138
|
+
env: process.env,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (result.error) {
|
|
142
|
+
throw result.error;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
process.exit(typeof result.status === 'number' ? result.status : 1);
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
const rootWorkflowCliPath = path.join(paths.projectRoot, '.opencode', 'workflow-state.js');
|
|
149
|
+
writeFile(rootWorkflowCliPath, rootWorkflowCli, 0o755);
|
|
150
|
+
createdPaths.push(rootWorkflowCliPath);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!fs.existsSync(path.join(paths.projectRoot, '.opencode', 'work-items'))) {
|
|
154
|
+
createIfMissing(createdPaths, {
|
|
155
|
+
linkPath: path.join(paths.projectRoot, '.opencode', 'work-items'),
|
|
156
|
+
targetPath: paths.workItemsDir,
|
|
157
|
+
type: 'dir',
|
|
158
|
+
});
|
|
101
159
|
}
|
|
102
160
|
|
|
103
|
-
return
|
|
161
|
+
return {
|
|
162
|
+
paths,
|
|
163
|
+
createdPaths,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function cleanupWorkspaceShim(shim) {
|
|
168
|
+
if (!shim?.createdPaths) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (const createdPath of [...shim.createdPaths].reverse()) {
|
|
173
|
+
removePathIfPresent(createdPath);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const maybeShimDir = shim.paths?.workspaceShimDir;
|
|
177
|
+
if (maybeShimDir && fs.existsSync(maybeShimDir)) {
|
|
178
|
+
try {
|
|
179
|
+
const entries = fs.readdirSync(maybeShimDir);
|
|
180
|
+
if (entries.length === 0) {
|
|
181
|
+
removePathIfPresent(maybeShimDir);
|
|
182
|
+
}
|
|
183
|
+
} catch {
|
|
184
|
+
// ignore cleanup misses
|
|
185
|
+
}
|
|
186
|
+
}
|
|
104
187
|
}
|
|
@@ -51,9 +51,12 @@ export function ensureWorkspaceBootstrap(options = {}) {
|
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
ensureWorkspaceShim(paths);
|
|
54
|
+
const shim = ensureWorkspaceShim(paths);
|
|
55
55
|
|
|
56
|
-
return
|
|
56
|
+
return {
|
|
57
|
+
...paths,
|
|
58
|
+
workspaceShim: shim,
|
|
59
|
+
};
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
export function readWorkspaceMeta(options = {}) {
|
|
@@ -33,6 +33,10 @@ function writeExecutable(filePath, content) {
|
|
|
33
33
|
fs.chmodSync(filePath, 0o755);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function removePathIfPresent(targetPath) {
|
|
37
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
test('openkit --help shows global-install oriented help', () => {
|
|
37
41
|
const result = runCli(['--help']);
|
|
38
42
|
|
|
@@ -226,6 +230,10 @@ process.stdout.write('mock opencode launched\\n');
|
|
|
226
230
|
assert.equal(fs.existsSync(path.join(projectRoot, '.opencode', 'openkit', 'context', 'core', 'workflow.md')), true);
|
|
227
231
|
assert.equal(fs.lstatSync(path.join(projectRoot, '.opencode', 'openkit', 'workflow-state.json')).isSymbolicLink() || fs.existsSync(path.join(projectRoot, '.opencode', 'openkit', 'workflow-state.json')), true);
|
|
228
232
|
assert.equal(fs.existsSync(path.join(projectRoot, '.opencode', 'openkit', 'workflow-state.js')), true);
|
|
233
|
+
assert.equal(fs.existsSync(path.join(projectRoot, 'AGENTS.md')), true);
|
|
234
|
+
assert.equal(fs.existsSync(path.join(projectRoot, 'context', 'core', 'workflow.md')), true);
|
|
235
|
+
assert.equal(fs.lstatSync(path.join(projectRoot, '.opencode', 'workflow-state.json')).isSymbolicLink() || fs.existsSync(path.join(projectRoot, '.opencode', 'workflow-state.json')), true);
|
|
236
|
+
assert.equal(fs.existsSync(path.join(projectRoot, '.opencode', 'workflow-state.js')), true);
|
|
229
237
|
});
|
|
230
238
|
|
|
231
239
|
test('openkit run does not reinstall when the global install already exists', () => {
|
|
@@ -301,6 +309,64 @@ process.stdout.write('mock opencode launched after auto-install\\n');
|
|
|
301
309
|
assert.equal(fs.existsSync(path.join(projectRoot, '.opencode', 'openkit', 'AGENTS.md')), true);
|
|
302
310
|
});
|
|
303
311
|
|
|
312
|
+
test('openkit run does not overwrite existing repo-local workflow files when creating shims', () => {
|
|
313
|
+
const tempHome = makeTempDir();
|
|
314
|
+
const projectRoot = makeTempDir();
|
|
315
|
+
const fakeBinDir = path.join(tempHome, 'bin');
|
|
316
|
+
|
|
317
|
+
fs.mkdirSync(path.join(projectRoot, '.opencode'), { recursive: true });
|
|
318
|
+
fs.mkdirSync(path.join(projectRoot, 'context', 'core'), { recursive: true });
|
|
319
|
+
fs.writeFileSync(path.join(projectRoot, 'AGENTS.md'), 'project agents\n', 'utf8');
|
|
320
|
+
fs.writeFileSync(path.join(projectRoot, 'context', 'core', 'workflow.md'), 'project workflow\n', 'utf8');
|
|
321
|
+
fs.writeFileSync(path.join(projectRoot, '.opencode', 'workflow-state.json'), '{"project":true}\n', 'utf8');
|
|
322
|
+
fs.writeFileSync(path.join(projectRoot, '.opencode', 'workflow-state.js'), '#!/usr/bin/env node\n', 'utf8');
|
|
323
|
+
|
|
324
|
+
writeExecutable(path.join(fakeBinDir, 'opencode'), '#!/bin/sh\nexit 0\n');
|
|
325
|
+
|
|
326
|
+
const result = runCli(['run'], {
|
|
327
|
+
cwd: projectRoot,
|
|
328
|
+
env: {
|
|
329
|
+
...process.env,
|
|
330
|
+
OPENCODE_HOME: tempHome,
|
|
331
|
+
PATH: `${fakeBinDir}${path.delimiter}${process.env.PATH}`,
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
assert.equal(result.status, 0);
|
|
336
|
+
assert.equal(fs.readFileSync(path.join(projectRoot, 'AGENTS.md'), 'utf8'), 'project agents\n');
|
|
337
|
+
assert.equal(fs.readFileSync(path.join(projectRoot, 'context', 'core', 'workflow.md'), 'utf8'), 'project workflow\n');
|
|
338
|
+
assert.equal(fs.readFileSync(path.join(projectRoot, '.opencode', 'workflow-state.json'), 'utf8'), '{"project":true}\n');
|
|
339
|
+
assert.equal(fs.readFileSync(path.join(projectRoot, '.opencode', 'workflow-state.js'), 'utf8'), '#!/usr/bin/env node\n');
|
|
340
|
+
assert.equal(fs.existsSync(path.join(projectRoot, '.opencode', 'openkit', 'AGENTS.md')), true);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test('openkit run cleans root compatibility shims when created files are removed', () => {
|
|
344
|
+
const tempHome = makeTempDir();
|
|
345
|
+
const projectRoot = makeTempDir();
|
|
346
|
+
const fakeBinDir = path.join(tempHome, 'bin');
|
|
347
|
+
|
|
348
|
+
writeExecutable(path.join(fakeBinDir, 'opencode'), '#!/bin/sh\nexit 0\n');
|
|
349
|
+
|
|
350
|
+
const result = runCli(['run'], {
|
|
351
|
+
cwd: projectRoot,
|
|
352
|
+
env: {
|
|
353
|
+
...process.env,
|
|
354
|
+
OPENCODE_HOME: tempHome,
|
|
355
|
+
PATH: `${fakeBinDir}${path.delimiter}${process.env.PATH}`,
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
assert.equal(result.status, 0);
|
|
360
|
+
assert.equal(fs.existsSync(path.join(projectRoot, 'AGENTS.md')), true);
|
|
361
|
+
|
|
362
|
+
removePathIfPresent(path.join(projectRoot, 'AGENTS.md'));
|
|
363
|
+
removePathIfPresent(path.join(projectRoot, 'context'));
|
|
364
|
+
removePathIfPresent(path.join(projectRoot, '.opencode', 'workflow-state.json'));
|
|
365
|
+
removePathIfPresent(path.join(projectRoot, '.opencode', 'workflow-state.js'));
|
|
366
|
+
|
|
367
|
+
assert.equal(fs.existsSync(path.join(projectRoot, '.opencode', 'openkit', 'AGENTS.md')), true);
|
|
368
|
+
});
|
|
369
|
+
|
|
304
370
|
test('openkit run reports missing opencode after first-time setup completes', () => {
|
|
305
371
|
const tempHome = makeTempDir();
|
|
306
372
|
const projectRoot = makeTempDir();
|