devtopia 2.0.0 → 2.0.1
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 +111 -140
- package/dist/commands/categories.js +16 -76
- package/dist/commands/compose.js +11 -2
- package/dist/commands/create.d.ts +7 -0
- package/dist/commands/create.js +302 -0
- package/dist/commands/docs.js +149 -159
- package/dist/commands/idea.d.ts +7 -0
- package/dist/commands/idea.js +83 -0
- package/dist/commands/run-local.d.ts +8 -0
- package/dist/commands/run-local.js +64 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +26 -8
- package/dist/commands/start.js +44 -31
- package/dist/commands/submit.d.ts +1 -0
- package/dist/commands/submit.js +201 -103
- package/dist/executor.d.ts +2 -4
- package/dist/executor.js +313 -18
- package/dist/index.js +47 -8
- package/package.json +3 -3
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { writeFileSync, existsSync } from 'fs';
|
|
2
|
+
import { createInterface } from 'readline';
|
|
3
|
+
function validateName(name) {
|
|
4
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
5
|
+
console.log(`\n❌ Tool name must be lowercase, alphanumeric with hyphens.\n`);
|
|
6
|
+
process.exit(1);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export async function create(name, options) {
|
|
10
|
+
if (!options.intent) {
|
|
11
|
+
console.log(`\n❌ --intent is required.\n Example: devtopia create summarize-article --intent "Summarize a URL"\n`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
validateName(name);
|
|
15
|
+
const language = (options.language || 'javascript').toLowerCase();
|
|
16
|
+
const extensions = {
|
|
17
|
+
javascript: '.js',
|
|
18
|
+
typescript: '.ts',
|
|
19
|
+
python: '.py',
|
|
20
|
+
bash: '.sh',
|
|
21
|
+
ruby: '.rb',
|
|
22
|
+
php: '.php',
|
|
23
|
+
shebang: '.tool',
|
|
24
|
+
};
|
|
25
|
+
if (!extensions[language]) {
|
|
26
|
+
console.log(`\n❌ Unsupported language: ${language}`);
|
|
27
|
+
console.log(` Supported: javascript, typescript, python, bash, ruby, php, shebang\n`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const toolPath = `./${name}${extensions[language]}`;
|
|
31
|
+
const mdPath = `./${name}.md`;
|
|
32
|
+
if (existsSync(toolPath) || existsSync(mdPath)) {
|
|
33
|
+
console.log(`\n⚠️ ${toolPath} or ${mdPath} already exists. Not overwriting.\n`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
const intent = options.intent.trim();
|
|
37
|
+
let gap = (options.gap || '').trim();
|
|
38
|
+
if (!gap) {
|
|
39
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
40
|
+
gap = await new Promise((resolve) => {
|
|
41
|
+
rl.question('\nWhy does this tool need to exist (gap justification)? ', (answer) => {
|
|
42
|
+
rl.close();
|
|
43
|
+
resolve(String(answer || '').trim());
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (!gap) {
|
|
48
|
+
console.log(`\n❌ Gap justification is required to create a new primitive.\n`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const jsSource = `/**
|
|
52
|
+
* ${name} - ${intent}
|
|
53
|
+
*
|
|
54
|
+
* Intent:
|
|
55
|
+
* ${intent}
|
|
56
|
+
*
|
|
57
|
+
* Gap Justification:
|
|
58
|
+
* ${gap}
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} params
|
|
61
|
+
* @returns {Object}
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
const input = JSON.parse(process.argv[2] || '{}');
|
|
65
|
+
|
|
66
|
+
// TODO: validate required fields from input
|
|
67
|
+
// if (!input.someField) {
|
|
68
|
+
// console.log(JSON.stringify({ ok: false, error: 'Missing required field: someField' }));
|
|
69
|
+
// process.exit(1);
|
|
70
|
+
// }
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
// TODO: implement your logic here
|
|
74
|
+
console.log(JSON.stringify({ ok: false, error: 'TODO: implement ${name}' }));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.log(JSON.stringify({ ok: false, error: error.message }));
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
const tsSource = `/**
|
|
82
|
+
* ${name} - ${intent}
|
|
83
|
+
*
|
|
84
|
+
* Intent:
|
|
85
|
+
* ${intent}
|
|
86
|
+
*
|
|
87
|
+
* Gap Justification:
|
|
88
|
+
* ${gap}
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
type Input = Record<string, unknown>;
|
|
92
|
+
const input: Input = JSON.parse(process.argv[2] || '{}');
|
|
93
|
+
|
|
94
|
+
// TODO: validate required fields from input
|
|
95
|
+
// if (!input.someField) {
|
|
96
|
+
// console.log(JSON.stringify({ ok: false, error: 'Missing required field: someField' }));
|
|
97
|
+
// process.exit(1);
|
|
98
|
+
// }
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// TODO: implement your logic here
|
|
102
|
+
console.log(JSON.stringify({ ok: false, error: 'TODO: implement ${name}' }));
|
|
103
|
+
process.exit(1);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
106
|
+
console.log(JSON.stringify({ ok: false, error: message }));
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
const pySource = `#!/usr/bin/env python3
|
|
111
|
+
# ${name} - ${intent}
|
|
112
|
+
#
|
|
113
|
+
# Intent:
|
|
114
|
+
# ${intent}
|
|
115
|
+
#
|
|
116
|
+
# Gap Justification:
|
|
117
|
+
# ${gap}
|
|
118
|
+
|
|
119
|
+
import json
|
|
120
|
+
import sys
|
|
121
|
+
|
|
122
|
+
input_data = json.loads(sys.argv[1] if len(sys.argv) > 1 else '{}')
|
|
123
|
+
|
|
124
|
+
# TODO: validate required fields
|
|
125
|
+
# if 'someField' not in input_data:
|
|
126
|
+
# print(json.dumps({ 'ok': False, 'error': 'Missing required field: someField' }))
|
|
127
|
+
# sys.exit(1)
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
# TODO: implement your logic here
|
|
131
|
+
print(json.dumps({ 'ok': False, 'error': 'TODO: implement ${name}' }))
|
|
132
|
+
sys.exit(1)
|
|
133
|
+
except Exception as error:
|
|
134
|
+
print(json.dumps({ 'ok': False, 'error': str(error) }))
|
|
135
|
+
sys.exit(1)
|
|
136
|
+
`;
|
|
137
|
+
const bashSource = `#!/usr/bin/env bash
|
|
138
|
+
# ${name} - ${intent}
|
|
139
|
+
#
|
|
140
|
+
# Intent:
|
|
141
|
+
# ${intent}
|
|
142
|
+
#
|
|
143
|
+
# Gap Justification:
|
|
144
|
+
# ${gap}
|
|
145
|
+
|
|
146
|
+
set -euo pipefail
|
|
147
|
+
|
|
148
|
+
INPUT="\${1:-{}}"
|
|
149
|
+
|
|
150
|
+
# TODO: parse fields from JSON input using node (available with Devtopia CLI)
|
|
151
|
+
# Example:
|
|
152
|
+
# TEXT=$(node -e "const i=JSON.parse(process.argv[1]||'{}'); console.log(i.text||'');" "$INPUT")
|
|
153
|
+
# if [ -z "$TEXT" ]; then
|
|
154
|
+
# echo '{"ok":false,"error":"Missing required field: text"}'
|
|
155
|
+
# exit 1
|
|
156
|
+
# fi
|
|
157
|
+
|
|
158
|
+
# TODO: implement your logic here
|
|
159
|
+
echo '{"ok":false,"error":"TODO: implement ${name}"}'
|
|
160
|
+
exit 1
|
|
161
|
+
`;
|
|
162
|
+
const rubySource = `#!/usr/bin/env ruby
|
|
163
|
+
# ${name} - ${intent}
|
|
164
|
+
#
|
|
165
|
+
# Intent:
|
|
166
|
+
# ${intent}
|
|
167
|
+
#
|
|
168
|
+
# Gap Justification:
|
|
169
|
+
# ${gap}
|
|
170
|
+
|
|
171
|
+
require 'json'
|
|
172
|
+
|
|
173
|
+
input = JSON.parse(ARGV[0] || '{}')
|
|
174
|
+
|
|
175
|
+
# TODO: validate required fields
|
|
176
|
+
# unless input['someField']
|
|
177
|
+
# puts JSON.generate({ ok: false, error: 'Missing required field: someField' })
|
|
178
|
+
# exit 1
|
|
179
|
+
# end
|
|
180
|
+
|
|
181
|
+
begin
|
|
182
|
+
# TODO: implement your logic here
|
|
183
|
+
puts JSON.generate({ ok: false, error: 'TODO: implement ${name}' })
|
|
184
|
+
exit 1
|
|
185
|
+
rescue => e
|
|
186
|
+
puts JSON.generate({ ok: false, error: e.message })
|
|
187
|
+
exit 1
|
|
188
|
+
end
|
|
189
|
+
`;
|
|
190
|
+
const phpSource = `#!/usr/bin/env php
|
|
191
|
+
<?php
|
|
192
|
+
// ${name} - ${intent}
|
|
193
|
+
//
|
|
194
|
+
// Intent:
|
|
195
|
+
// ${intent}
|
|
196
|
+
//
|
|
197
|
+
// Gap Justification:
|
|
198
|
+
// ${gap}
|
|
199
|
+
|
|
200
|
+
$input = json_decode($argv[1] ?? '{}', true);
|
|
201
|
+
if ($input === null && json_last_error() !== JSON_ERROR_NONE) {
|
|
202
|
+
echo json_encode([\"ok\" => false, \"error\" => \"Invalid JSON input\"]);
|
|
203
|
+
exit(1);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// TODO: validate required fields
|
|
207
|
+
// if (!isset($input['someField'])) {
|
|
208
|
+
// echo json_encode([\"ok\" => false, \"error\" => \"Missing required field: someField\"]);
|
|
209
|
+
// exit(1);
|
|
210
|
+
// }
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
// TODO: implement your logic here
|
|
214
|
+
echo json_encode([\"ok\" => false, \"error\" => \"TODO: implement ${name}\"]);
|
|
215
|
+
exit(1);
|
|
216
|
+
} catch (Throwable $e) {
|
|
217
|
+
echo json_encode([\"ok\" => false, \"error\" => $e->getMessage()]);
|
|
218
|
+
exit(1);
|
|
219
|
+
}
|
|
220
|
+
?>`;
|
|
221
|
+
const shebangSource = `#!/usr/bin/env bash
|
|
222
|
+
# ${name} - ${intent}
|
|
223
|
+
#
|
|
224
|
+
# Intent:
|
|
225
|
+
# ${intent}
|
|
226
|
+
#
|
|
227
|
+
# Gap Justification:
|
|
228
|
+
# ${gap}
|
|
229
|
+
#
|
|
230
|
+
# Replace "bash" above with the interpreter you want (python3, ruby, php, etc).
|
|
231
|
+
|
|
232
|
+
set -euo pipefail
|
|
233
|
+
|
|
234
|
+
INPUT="\${1:-{}}"
|
|
235
|
+
|
|
236
|
+
# TODO: parse fields from JSON input
|
|
237
|
+
# Example using node:
|
|
238
|
+
# FIELD=$(node -e "const i=JSON.parse(process.argv[1]||'{}'); console.log(i.field||'');" "$INPUT")
|
|
239
|
+
|
|
240
|
+
# TODO: implement your logic here
|
|
241
|
+
echo '{"ok":false,"error":"TODO: implement ${name}"}'
|
|
242
|
+
exit 1
|
|
243
|
+
`;
|
|
244
|
+
const mdSource = `# ${name}
|
|
245
|
+
|
|
246
|
+
${intent}
|
|
247
|
+
|
|
248
|
+
## Intent
|
|
249
|
+
|
|
250
|
+
${intent}
|
|
251
|
+
|
|
252
|
+
## Gap Justification
|
|
253
|
+
|
|
254
|
+
${gap}
|
|
255
|
+
|
|
256
|
+
## External Systems (if any)
|
|
257
|
+
|
|
258
|
+
<!-- Required for gravity tools. Examples: github, slack, openai, postgres, s3, filesystem -->
|
|
259
|
+
|
|
260
|
+
## Usage
|
|
261
|
+
|
|
262
|
+
\`\`\`bash
|
|
263
|
+
devtopia run ${name} --json --quiet '{ "TODO": "add input" }'
|
|
264
|
+
\`\`\`
|
|
265
|
+
|
|
266
|
+
## Input
|
|
267
|
+
|
|
268
|
+
\`\`\`json
|
|
269
|
+
{
|
|
270
|
+
"TODO": "document required fields"
|
|
271
|
+
}
|
|
272
|
+
\`\`\`
|
|
273
|
+
|
|
274
|
+
## Output
|
|
275
|
+
|
|
276
|
+
\`\`\`json
|
|
277
|
+
{
|
|
278
|
+
"ok": true,
|
|
279
|
+
"result": "..."
|
|
280
|
+
}
|
|
281
|
+
\`\`\`
|
|
282
|
+
`;
|
|
283
|
+
const sources = {
|
|
284
|
+
javascript: jsSource,
|
|
285
|
+
typescript: tsSource,
|
|
286
|
+
python: pySource,
|
|
287
|
+
bash: bashSource,
|
|
288
|
+
ruby: rubySource,
|
|
289
|
+
php: phpSource,
|
|
290
|
+
shebang: shebangSource,
|
|
291
|
+
};
|
|
292
|
+
writeFileSync(toolPath, sources[language]);
|
|
293
|
+
writeFileSync(mdPath, mdSource);
|
|
294
|
+
console.log(`\n✅ Scaffold generated!`);
|
|
295
|
+
console.log(`\n Files created:`);
|
|
296
|
+
console.log(` ${toolPath} — Tool source (edit the TODOs)`);
|
|
297
|
+
console.log(` ${mdPath} — README (edit the TODOs)`);
|
|
298
|
+
console.log(`\n Next steps:`);
|
|
299
|
+
console.log(` 1. Edit ${toolPath} — implement your logic`);
|
|
300
|
+
console.log(` 2. Test: devtopia run ${name} --json --quiet '{...}'`);
|
|
301
|
+
console.log(` 3. Submit: devtopia submit ${name} ${toolPath}\n`);
|
|
302
|
+
}
|