@ekaone/json-cli 0.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/LICENSE +21 -0
- package/README.md +105 -0
- package/dist/cli.cjs +317 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/index.cjs +173 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +52 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +145 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eka Prasetia <ekaone3033@gmail.com> (https://prasetia.me)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# json-cli
|
|
2
|
+
|
|
3
|
+
> Under active development - not released yet
|
|
4
|
+
|
|
5
|
+
AI-powered CLI task runner. Describe your goal in plain English — AI generates a validated JSON command plan — runner executes it step by step.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @ekaone/json-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm install @ekaone/json-cli
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
yarn install @ekaone/json-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
export ANTHROPIC_API_KEY=your_key_here
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
json-cli "please run tests"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### For local development
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Usage
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Using Claude (default)
|
|
41
|
+
pnpm dev "install deps and run tests"
|
|
42
|
+
|
|
43
|
+
# Using OpenAI
|
|
44
|
+
pnpm dev "run build and publish" --provider openai
|
|
45
|
+
|
|
46
|
+
# Using Ollama (local)
|
|
47
|
+
pnpm dev "start dev server" --provider ollama
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## How it works
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
User Prompt
|
|
54
|
+
│
|
|
55
|
+
▼
|
|
56
|
+
AI Provider ← Claude / OpenAI / Ollama
|
|
57
|
+
│
|
|
58
|
+
▼
|
|
59
|
+
JSON Plan ← validated by Zod schema
|
|
60
|
+
│
|
|
61
|
+
▼
|
|
62
|
+
Catalog Check ← whitelist prevents hallucinated commands
|
|
63
|
+
│
|
|
64
|
+
▼
|
|
65
|
+
Confirm (y/n) ← user reviews before execution
|
|
66
|
+
│
|
|
67
|
+
▼
|
|
68
|
+
Runner ← executes steps, streams output live
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Allowed commands
|
|
72
|
+
|
|
73
|
+
| Type | Commands |
|
|
74
|
+
|-------|----------|
|
|
75
|
+
| pnpm | install, run, build, test, publish, add, remove |
|
|
76
|
+
| npm | install, run, build, test, publish, ci |
|
|
77
|
+
| yarn | install, run, build, test, publish, add, remove |
|
|
78
|
+
| bun | install, run, build, test, publish, add, remove |
|
|
79
|
+
| git | init, add, commit, push, pull, clone, status, log |
|
|
80
|
+
| shell | any *(requires extra caution)* |
|
|
81
|
+
|
|
82
|
+
## Environment variables
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
86
|
+
OPENAI_API_KEY=sk-...
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Run tests
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pnpm test
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT © [Eka Prasetia](https://prasetia.me/)
|
|
98
|
+
|
|
99
|
+
## Links
|
|
100
|
+
|
|
101
|
+
- [npm Package](https://www.npmjs.com/package/@ekaone/json-cli)
|
|
102
|
+
- [GitHub Repository](https://github.com/ekaone/json-cli)
|
|
103
|
+
- [Issue Tracker](https://github.com/ekaone/json-cli/issues)
|
|
104
|
+
|
|
105
|
+
⭐ If this library helps you, please consider giving it a star on GitHub!
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
"use strict";
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
+
mod
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
// src/cli.ts
|
|
28
|
+
var p = __toESM(require("@clack/prompts"), 1);
|
|
29
|
+
|
|
30
|
+
// src/providers/claude.ts
|
|
31
|
+
var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
|
|
32
|
+
function createClaudeProvider(apiKey) {
|
|
33
|
+
const client = new import_sdk.default({ apiKey: apiKey ?? process.env.ANTHROPIC_API_KEY });
|
|
34
|
+
return {
|
|
35
|
+
name: "claude",
|
|
36
|
+
async generate(userPrompt, systemPrompt) {
|
|
37
|
+
const message = await client.messages.create({
|
|
38
|
+
model: "claude-opus-4-5",
|
|
39
|
+
max_tokens: 1024,
|
|
40
|
+
system: systemPrompt,
|
|
41
|
+
messages: [{ role: "user", content: userPrompt }]
|
|
42
|
+
});
|
|
43
|
+
const block = message.content[0];
|
|
44
|
+
if (block.type !== "text") throw new Error("Unexpected response type from Claude");
|
|
45
|
+
return block.text;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/providers/openai.ts
|
|
51
|
+
var import_openai = __toESM(require("openai"), 1);
|
|
52
|
+
function createOpenAIProvider(apiKey) {
|
|
53
|
+
const client = new import_openai.default({ apiKey: apiKey ?? process.env.OPENAI_API_KEY });
|
|
54
|
+
return {
|
|
55
|
+
name: "openai",
|
|
56
|
+
async generate(userPrompt, systemPrompt) {
|
|
57
|
+
const response = await client.chat.completions.create({
|
|
58
|
+
model: "gpt-4o",
|
|
59
|
+
messages: [
|
|
60
|
+
{ role: "system", content: systemPrompt },
|
|
61
|
+
{ role: "user", content: userPrompt }
|
|
62
|
+
],
|
|
63
|
+
max_tokens: 1024,
|
|
64
|
+
response_format: { type: "json_object" }
|
|
65
|
+
// enforces JSON output
|
|
66
|
+
});
|
|
67
|
+
const content = response.choices[0]?.message.content;
|
|
68
|
+
if (!content) throw new Error("Empty response from OpenAI");
|
|
69
|
+
return content;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/providers/ollama.ts
|
|
75
|
+
function createOllamaProvider(model = "llama3.2", baseUrl = "http://localhost:11434") {
|
|
76
|
+
return {
|
|
77
|
+
name: `ollama/${model}`,
|
|
78
|
+
async generate(userPrompt, systemPrompt) {
|
|
79
|
+
const response = await fetch(`${baseUrl}/api/chat`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { "Content-Type": "application/json" },
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
model,
|
|
84
|
+
stream: false,
|
|
85
|
+
messages: [
|
|
86
|
+
{ role: "system", content: systemPrompt },
|
|
87
|
+
{ role: "user", content: userPrompt }
|
|
88
|
+
]
|
|
89
|
+
})
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) throw new Error(`Ollama error: ${response.statusText}`);
|
|
92
|
+
const data = await response.json();
|
|
93
|
+
const content = data?.message?.content;
|
|
94
|
+
if (!content) throw new Error("Empty response from Ollama");
|
|
95
|
+
return content;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/providers/index.ts
|
|
101
|
+
function resolveProvider(name) {
|
|
102
|
+
switch (name) {
|
|
103
|
+
case "claude":
|
|
104
|
+
return createClaudeProvider();
|
|
105
|
+
case "openai":
|
|
106
|
+
return createOpenAIProvider();
|
|
107
|
+
case "ollama":
|
|
108
|
+
return createOllamaProvider();
|
|
109
|
+
default:
|
|
110
|
+
throw new Error(`Unknown provider: ${name}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/catalog.ts
|
|
115
|
+
var import_zod = require("zod");
|
|
116
|
+
var CATALOG = {
|
|
117
|
+
npm: ["install", "run", "build", "test", "publish", "ci"],
|
|
118
|
+
pnpm: ["install", "run", "build", "test", "publish", "add", "remove"],
|
|
119
|
+
yarn: ["install", "run", "build", "test", "publish", "add", "remove"],
|
|
120
|
+
bun: ["install", "run", "build", "test", "publish", "add", "remove"],
|
|
121
|
+
git: ["init", "add", "commit", "push", "pull", "clone", "status", "log"],
|
|
122
|
+
shell: ["any"]
|
|
123
|
+
// escape hatch — always requires extra confirmation
|
|
124
|
+
};
|
|
125
|
+
var StepSchema = import_zod.z.object({
|
|
126
|
+
id: import_zod.z.number(),
|
|
127
|
+
type: import_zod.z.enum(["npm", "pnpm", "yarn", "bun", "git", "shell"]),
|
|
128
|
+
command: import_zod.z.string(),
|
|
129
|
+
args: import_zod.z.array(import_zod.z.string()).default([]),
|
|
130
|
+
description: import_zod.z.string(),
|
|
131
|
+
cwd: import_zod.z.string().optional()
|
|
132
|
+
// optional working directory override
|
|
133
|
+
});
|
|
134
|
+
var PlanSchema = import_zod.z.object({
|
|
135
|
+
goal: import_zod.z.string(),
|
|
136
|
+
steps: import_zod.z.array(StepSchema).min(1).max(10)
|
|
137
|
+
});
|
|
138
|
+
function validateStep(step) {
|
|
139
|
+
const allowed = CATALOG[step.type];
|
|
140
|
+
if (step.type === "shell") {
|
|
141
|
+
return { valid: true };
|
|
142
|
+
}
|
|
143
|
+
if (!allowed.includes(step.command)) {
|
|
144
|
+
return {
|
|
145
|
+
valid: false,
|
|
146
|
+
reason: `"${step.command}" is not an allowed command for type "${step.type}". Allowed: ${allowed.join(", ")}`
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return { valid: true };
|
|
150
|
+
}
|
|
151
|
+
function buildCatalogPrompt() {
|
|
152
|
+
const lines = Object.entries(CATALOG).map(([type, commands]) => {
|
|
153
|
+
const list = commands[0] === "any" ? "any shell command (use sparingly)" : commands.join(", ");
|
|
154
|
+
return ` - ${type}: [${list}]`;
|
|
155
|
+
});
|
|
156
|
+
return `Allowed command types and commands:
|
|
157
|
+
${lines.join("\n")}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/planner.ts
|
|
161
|
+
function buildSystemPrompt() {
|
|
162
|
+
return `You are a CLI task planner. Given a user's goal, generate a JSON execution plan.
|
|
163
|
+
|
|
164
|
+
${buildCatalogPrompt()}
|
|
165
|
+
|
|
166
|
+
Rules:
|
|
167
|
+
- ONLY use command types and commands listed above
|
|
168
|
+
- Prefer pnpm over npm unless the user specifies otherwise
|
|
169
|
+
- Use "shell" type only when no other type fits
|
|
170
|
+
- Keep steps minimal \u2014 don't add unnecessary steps
|
|
171
|
+
- Each step must have a clear, short description
|
|
172
|
+
|
|
173
|
+
Respond ONLY with valid JSON matching this exact shape, no markdown, no explanation:
|
|
174
|
+
{
|
|
175
|
+
"goal": "string describing the overall goal",
|
|
176
|
+
"steps": [
|
|
177
|
+
{
|
|
178
|
+
"id": 1,
|
|
179
|
+
"type": "pnpm",
|
|
180
|
+
"command": "run",
|
|
181
|
+
"args": ["dev"],
|
|
182
|
+
"description": "Start dev server"
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}`;
|
|
186
|
+
}
|
|
187
|
+
async function generatePlan(userPrompt, provider) {
|
|
188
|
+
const raw = await provider.generate(userPrompt, buildSystemPrompt());
|
|
189
|
+
const cleaned = raw.replace(/```json|```/g, "").trim();
|
|
190
|
+
let parsed;
|
|
191
|
+
try {
|
|
192
|
+
parsed = JSON.parse(cleaned);
|
|
193
|
+
} catch {
|
|
194
|
+
throw new Error(`AI returned invalid JSON:
|
|
195
|
+
${cleaned}`);
|
|
196
|
+
}
|
|
197
|
+
const result = PlanSchema.safeParse(parsed);
|
|
198
|
+
if (!result.success) {
|
|
199
|
+
const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
200
|
+
throw new Error(`Plan failed schema validation:
|
|
201
|
+
${issues}`);
|
|
202
|
+
}
|
|
203
|
+
for (const step of result.data.steps) {
|
|
204
|
+
const check = validateStep(step);
|
|
205
|
+
if (!check.valid) {
|
|
206
|
+
throw new Error(`Step ${step.id} failed catalog validation: ${check.reason}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return result.data;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/runner.ts
|
|
213
|
+
var import_execa = require("execa");
|
|
214
|
+
function resolveCommand(step) {
|
|
215
|
+
if (step.type === "shell") {
|
|
216
|
+
return { bin: step.command, args: step.args };
|
|
217
|
+
}
|
|
218
|
+
return { bin: step.type, args: [step.command, ...step.args] };
|
|
219
|
+
}
|
|
220
|
+
async function runStep(step) {
|
|
221
|
+
const { bin, args } = resolveCommand(step);
|
|
222
|
+
try {
|
|
223
|
+
await (0, import_execa.execa)(bin, args, {
|
|
224
|
+
cwd: step.cwd ?? process.cwd(),
|
|
225
|
+
stdout: "inherit",
|
|
226
|
+
// stream directly to terminal
|
|
227
|
+
stderr: "inherit"
|
|
228
|
+
});
|
|
229
|
+
return { success: true };
|
|
230
|
+
} catch (err) {
|
|
231
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
232
|
+
return { success: false, error: message };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async function runPlan(plan, onStep) {
|
|
236
|
+
const total = plan.steps.length;
|
|
237
|
+
for (let i = 0; i < total; i++) {
|
|
238
|
+
const step = plan.steps[i];
|
|
239
|
+
onStep(step, i, total);
|
|
240
|
+
const result = await runStep(step);
|
|
241
|
+
if (!result.success) {
|
|
242
|
+
return { success: false, failedStep: step, error: result.error };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return { success: true };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/cli.ts
|
|
249
|
+
function parseArgs() {
|
|
250
|
+
const args = process.argv.slice(2);
|
|
251
|
+
const providerFlag = args.indexOf("--provider");
|
|
252
|
+
const provider = providerFlag !== -1 ? args[providerFlag + 1] : "claude";
|
|
253
|
+
const prompt = args.filter(
|
|
254
|
+
(a, i) => !a.startsWith("--") && (providerFlag === -1 || i !== providerFlag + 1)
|
|
255
|
+
).join(" ");
|
|
256
|
+
if (!prompt) {
|
|
257
|
+
console.error(
|
|
258
|
+
'Usage: json-cli "<your goal>" [--provider claude|openai|ollama]'
|
|
259
|
+
);
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
return { prompt, provider };
|
|
263
|
+
}
|
|
264
|
+
function formatStep(step) {
|
|
265
|
+
const cmd = step.type === "shell" ? `${step.command} ${step.args.join(" ")}` : `${step.type} ${step.command} ${step.args.join(" ")}`.trim();
|
|
266
|
+
return `${cmd.padEnd(35)} \u2192 ${step.description}`;
|
|
267
|
+
}
|
|
268
|
+
async function main() {
|
|
269
|
+
const { prompt, provider: providerName } = parseArgs();
|
|
270
|
+
p.intro(`json-cli \u2014 powered by ${providerName}`);
|
|
271
|
+
const spinner2 = p.spinner();
|
|
272
|
+
spinner2.start("Thinking...");
|
|
273
|
+
let plan;
|
|
274
|
+
try {
|
|
275
|
+
const provider = resolveProvider(providerName);
|
|
276
|
+
plan = await generatePlan(prompt, provider);
|
|
277
|
+
spinner2.stop("Plan ready");
|
|
278
|
+
} catch (err) {
|
|
279
|
+
spinner2.stop("Failed to generate plan");
|
|
280
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
p.log.info(`Goal: ${plan.goal}
|
|
284
|
+
`);
|
|
285
|
+
plan.steps.forEach((step, i) => {
|
|
286
|
+
const isShell = step.type === "shell";
|
|
287
|
+
p.log.message(
|
|
288
|
+
` ${i + 1}. ${formatStep(step)}${isShell ? " \u26A0 shell" : ""}`
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
const hasShell = plan.steps.some((s) => s.type === "shell");
|
|
292
|
+
if (hasShell) {
|
|
293
|
+
p.log.warn(
|
|
294
|
+
"Plan contains shell commands \u2014 review carefully before proceeding."
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const confirmed = await p.confirm({ message: "Proceed?" });
|
|
298
|
+
if (p.isCancel(confirmed) || !confirmed) {
|
|
299
|
+
p.cancel("Aborted.");
|
|
300
|
+
process.exit(0);
|
|
301
|
+
}
|
|
302
|
+
console.log("");
|
|
303
|
+
const result = await runPlan(plan, (step, i, total) => {
|
|
304
|
+
p.log.step(`Step ${i + 1}/${total}: ${formatStep(step)}`);
|
|
305
|
+
});
|
|
306
|
+
if (result.success) {
|
|
307
|
+
p.outro("\u2705 All steps completed successfully.");
|
|
308
|
+
} else {
|
|
309
|
+
p.log.error(
|
|
310
|
+
`\u274C Failed at step ${result.failedStep?.id}: ${result.failedStep?.description}
|
|
311
|
+
${result.error ?? ""}`
|
|
312
|
+
);
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
main();
|
|
317
|
+
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/providers/claude.ts","../src/providers/openai.ts","../src/providers/ollama.ts","../src/providers/index.ts","../src/catalog.ts","../src/planner.ts","../src/runner.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as p from \"@clack/prompts\";\nimport { resolveProvider, type ProviderName } from \"./providers/index.js\";\nimport { generatePlan } from \"./planner.js\";\nimport { runPlan } from \"./runner.js\";\nimport type { Step } from \"./catalog.js\";\n\n// ---------------------------------------------------------------------------\n// Parse CLI args\n// e.g. json-cli \"run tests\" --provider claude\n// ---------------------------------------------------------------------------\nfunction parseArgs(): { prompt: string; provider: ProviderName } {\n const args = process.argv.slice(2);\n const providerFlag = args.indexOf(\"--provider\");\n const provider: ProviderName =\n providerFlag !== -1 ? (args[providerFlag + 1] as ProviderName) : \"claude\";\n\n const prompt = args\n .filter(\n (a, i) =>\n !a.startsWith(\"--\") && (providerFlag === -1 || i !== providerFlag + 1),\n )\n .join(\" \");\n\n if (!prompt) {\n console.error(\n 'Usage: json-cli \"<your goal>\" [--provider claude|openai|ollama]',\n );\n process.exit(1);\n }\n\n return { prompt, provider };\n}\n\n// ---------------------------------------------------------------------------\n// Format a step for display\n// ---------------------------------------------------------------------------\nfunction formatStep(step: Step): string {\n const cmd =\n step.type === \"shell\"\n ? `${step.command} ${step.args.join(\" \")}`\n : `${step.type} ${step.command} ${step.args.join(\" \")}`.trim();\n return `${cmd.padEnd(35)} → ${step.description}`;\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\nasync function main() {\n const { prompt, provider: providerName } = parseArgs();\n\n p.intro(`json-cli — powered by ${providerName}`);\n\n // Step 1: Generate plan\n const spinner = p.spinner();\n spinner.start(\"Thinking...\");\n\n let plan;\n try {\n const provider = resolveProvider(providerName);\n plan = await generatePlan(prompt, provider);\n spinner.stop(\"Plan ready\");\n } catch (err) {\n spinner.stop(\"Failed to generate plan\");\n p.log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n\n // Step 2: Show plan\n p.log.info(`Goal: ${plan.goal}\\n`);\n plan.steps.forEach((step, i) => {\n const isShell = step.type === \"shell\";\n p.log.message(\n ` ${i + 1}. ${formatStep(step)}${isShell ? \" ⚠ shell\" : \"\"}`,\n );\n });\n\n // Step 3: Extra warning if any shell steps\n const hasShell = plan.steps.some((s) => s.type === \"shell\");\n if (hasShell) {\n p.log.warn(\n \"Plan contains shell commands — review carefully before proceeding.\",\n );\n }\n\n // Step 4: Confirm\n const confirmed = await p.confirm({ message: \"Proceed?\" });\n if (p.isCancel(confirmed) || !confirmed) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n // Step 5: Execute\n console.log(\"\");\n const result = await runPlan(plan, (step, i, total) => {\n p.log.step(`Step ${i + 1}/${total}: ${formatStep(step)}`);\n });\n\n // Step 6: Result\n if (result.success) {\n p.outro(\"✅ All steps completed successfully.\");\n } else {\n p.log.error(\n `❌ Failed at step ${result.failedStep?.id}: ${result.failedStep?.description}\\n${result.error ?? \"\"}`,\n );\n process.exit(1);\n }\n}\n\nmain();\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { AIProvider } from \"./types.js\";\n\nexport function createClaudeProvider(apiKey?: string): AIProvider {\n const client = new Anthropic({ apiKey: apiKey ?? process.env.ANTHROPIC_API_KEY });\n\n return {\n name: \"claude\",\n async generate(userPrompt, systemPrompt) {\n const message = await client.messages.create({\n model: \"claude-opus-4-5\",\n max_tokens: 1024,\n system: systemPrompt,\n messages: [{ role: \"user\", content: userPrompt }],\n });\n\n const block = message.content[0];\n if (block.type !== \"text\") throw new Error(\"Unexpected response type from Claude\");\n return block.text;\n },\n };\n}\n","import OpenAI from \"openai\";\nimport type { AIProvider } from \"./types.js\";\n\nexport function createOpenAIProvider(apiKey?: string): AIProvider {\n const client = new OpenAI({ apiKey: apiKey ?? process.env.OPENAI_API_KEY });\n\n return {\n name: \"openai\",\n async generate(userPrompt, systemPrompt) {\n const response = await client.chat.completions.create({\n model: \"gpt-4o\",\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userPrompt },\n ],\n max_tokens: 1024,\n response_format: { type: \"json_object\" }, // enforces JSON output\n });\n\n const content = response.choices[0]?.message.content;\n if (!content) throw new Error(\"Empty response from OpenAI\");\n return content;\n },\n };\n}\n","import type { AIProvider } from \"./types.js\";\n\nexport function createOllamaProvider(model = \"llama3.2\", baseUrl = \"http://localhost:11434\"): AIProvider {\n return {\n name: `ollama/${model}`,\n async generate(userPrompt, systemPrompt) {\n const response = await fetch(`${baseUrl}/api/chat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model,\n stream: false,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userPrompt },\n ],\n }),\n });\n\n if (!response.ok) throw new Error(`Ollama error: ${response.statusText}`);\n\n const data = await response.json() as { message?: { content?: string } };\n const content = data?.message?.content;\n if (!content) throw new Error(\"Empty response from Ollama\");\n return content;\n },\n };\n}\n","export { createClaudeProvider } from \"./claude.js\";\nexport { createOpenAIProvider } from \"./openai.js\";\nexport { createOllamaProvider } from \"./ollama.js\";\nexport type { AIProvider } from \"./types.js\";\n\nimport type { AIProvider } from \"./types.js\";\nimport { createClaudeProvider } from \"./claude.js\";\nimport { createOpenAIProvider } from \"./openai.js\";\nimport { createOllamaProvider } from \"./ollama.js\";\n\nexport type ProviderName = \"claude\" | \"openai\" | \"ollama\";\n\nexport function resolveProvider(name: ProviderName): AIProvider {\n switch (name) {\n case \"claude\": return createClaudeProvider();\n case \"openai\": return createOpenAIProvider();\n case \"ollama\": return createOllamaProvider();\n default: throw new Error(`Unknown provider: ${name}`);\n }\n}\n","import { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Allowed commands per type — the whitelist that prevents hallucination\n// ---------------------------------------------------------------------------\nexport const CATALOG = {\n npm: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"ci\"],\n pnpm: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"add\", \"remove\"],\n yarn: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"add\", \"remove\"],\n bun: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"add\", \"remove\"],\n git: [\"init\", \"add\", \"commit\", \"push\", \"pull\", \"clone\", \"status\", \"log\"],\n shell: [\"any\"], // escape hatch — always requires extra confirmation\n} as const;\n\nexport type CommandType = keyof typeof CATALOG;\n\n// ---------------------------------------------------------------------------\n// Zod schemas — Layer 2 defense against hallucinated output\n// ---------------------------------------------------------------------------\nexport const StepSchema = z.object({\n id: z.number(),\n type: z.enum([\"npm\", \"pnpm\", \"yarn\", \"bun\", \"git\", \"shell\"]),\n command: z.string(),\n args: z.array(z.string()).default([]),\n description: z.string(),\n cwd: z.string().optional(), // optional working directory override\n});\n\nexport const PlanSchema = z.object({\n goal: z.string(),\n steps: z.array(StepSchema).min(1).max(10),\n});\n\nexport type Step = z.infer<typeof StepSchema>;\nexport type Plan = z.infer<typeof PlanSchema>;\n\n// ---------------------------------------------------------------------------\n// Catalog validation — Layer 3: check command is in whitelist\n// ---------------------------------------------------------------------------\nexport function validateStep(step: Step): { valid: boolean; reason?: string } {\n const allowed = CATALOG[step.type];\n\n // shell type is always allowed but flagged for extra confirmation\n if (step.type === \"shell\") {\n return { valid: true };\n }\n\n if (!allowed.includes(step.command as never)) {\n return {\n valid: false,\n reason: `\"${step.command}\" is not an allowed command for type \"${step.type}\". Allowed: ${allowed.join(\", \")}`,\n };\n }\n\n return { valid: true };\n}\n\n// ---------------------------------------------------------------------------\n// Build the catalog string injected into AI system prompt\n// ---------------------------------------------------------------------------\nexport function buildCatalogPrompt(): string {\n const lines = Object.entries(CATALOG).map(([type, commands]) => {\n const list = commands[0] === \"any\" ? \"any shell command (use sparingly)\" : commands.join(\", \");\n return ` - ${type}: [${list}]`;\n });\n\n return `Allowed command types and commands:\\n${lines.join(\"\\n\")}`;\n}\n","import { buildCatalogPrompt, PlanSchema, validateStep } from \"./catalog.js\";\nimport type { AIProvider } from \"./providers/types.js\";\nimport type { Plan } from \"./catalog.js\";\n\n// ---------------------------------------------------------------------------\n// System prompt — constrains AI to only produce catalog-valid JSON\n// ---------------------------------------------------------------------------\nfunction buildSystemPrompt(): string {\n return `You are a CLI task planner. Given a user's goal, generate a JSON execution plan.\n\n${buildCatalogPrompt()}\n\nRules:\n- ONLY use command types and commands listed above\n- Prefer pnpm over npm unless the user specifies otherwise\n- Use \"shell\" type only when no other type fits\n- Keep steps minimal — don't add unnecessary steps\n- Each step must have a clear, short description\n\nRespond ONLY with valid JSON matching this exact shape, no markdown, no explanation:\n{\n \"goal\": \"string describing the overall goal\",\n \"steps\": [\n {\n \"id\": 1,\n \"type\": \"pnpm\",\n \"command\": \"run\",\n \"args\": [\"dev\"],\n \"description\": \"Start dev server\"\n }\n ]\n}`;\n}\n\n// ---------------------------------------------------------------------------\n// Main planner function\n// ---------------------------------------------------------------------------\nexport async function generatePlan(userPrompt: string, provider: AIProvider): Promise<Plan> {\n const raw = await provider.generate(userPrompt, buildSystemPrompt());\n\n // Strip markdown fences if any provider wraps output\n const cleaned = raw.replace(/```json|```/g, \"\").trim();\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(cleaned);\n } catch {\n throw new Error(`AI returned invalid JSON:\\n${cleaned}`);\n }\n\n // Layer 2: Zod shape validation\n const result = PlanSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` - ${i.path.join(\".\")}: ${i.message}`).join(\"\\n\");\n throw new Error(`Plan failed schema validation:\\n${issues}`);\n }\n\n // Layer 3: Catalog whitelist validation\n for (const step of result.data.steps) {\n const check = validateStep(step);\n if (!check.valid) {\n throw new Error(`Step ${step.id} failed catalog validation: ${check.reason}`);\n }\n }\n\n return result.data;\n}\n","import { execa } from \"execa\";\nimport type { Plan, Step } from \"./catalog.js\";\n\n// ---------------------------------------------------------------------------\n// Build the actual shell command from a Step\n// ---------------------------------------------------------------------------\nfunction resolveCommand(step: Step): { bin: string; args: string[] } {\n if (step.type === \"shell\") {\n // shell: command is the binary, args are the args\n return { bin: step.command, args: step.args };\n }\n\n // For npm/pnpm/yarn/bun/git: binary is the type, command + args follow\n return { bin: step.type, args: [step.command, ...step.args] };\n}\n\n// ---------------------------------------------------------------------------\n// Run a single step, streaming stdout/stderr live\n// ---------------------------------------------------------------------------\nexport async function runStep(step: Step): Promise<{ success: boolean; error?: string }> {\n const { bin, args } = resolveCommand(step);\n\n try {\n await execa(bin, args, {\n cwd: step.cwd ?? process.cwd(),\n stdout: \"inherit\", // stream directly to terminal\n stderr: \"inherit\",\n });\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: message };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Run the full plan, stopping on first failure\n// ---------------------------------------------------------------------------\nexport async function runPlan(\n plan: Plan,\n onStep: (step: Step, index: number, total: number) => void\n): Promise<{ success: boolean; failedStep?: Step; error?: string }> {\n const total = plan.steps.length;\n\n for (let i = 0; i < total; i++) {\n const step = plan.steps[i];\n onStep(step, i, total);\n\n const result = await runStep(step);\n\n if (!result.success) {\n return { success: false, failedStep: step, error: result.error };\n }\n }\n\n return { success: true };\n}\n"],"mappings":"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","names":["Anthropic","OpenAI","spinner"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
generatePlan: () => generatePlan,
|
|
24
|
+
runPlan: () => runPlan
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(src_exports);
|
|
27
|
+
|
|
28
|
+
// src/catalog.ts
|
|
29
|
+
var import_zod = require("zod");
|
|
30
|
+
var CATALOG = {
|
|
31
|
+
npm: ["install", "run", "build", "test", "publish", "ci"],
|
|
32
|
+
pnpm: ["install", "run", "build", "test", "publish", "add", "remove"],
|
|
33
|
+
yarn: ["install", "run", "build", "test", "publish", "add", "remove"],
|
|
34
|
+
bun: ["install", "run", "build", "test", "publish", "add", "remove"],
|
|
35
|
+
git: ["init", "add", "commit", "push", "pull", "clone", "status", "log"],
|
|
36
|
+
shell: ["any"]
|
|
37
|
+
// escape hatch — always requires extra confirmation
|
|
38
|
+
};
|
|
39
|
+
var StepSchema = import_zod.z.object({
|
|
40
|
+
id: import_zod.z.number(),
|
|
41
|
+
type: import_zod.z.enum(["npm", "pnpm", "yarn", "bun", "git", "shell"]),
|
|
42
|
+
command: import_zod.z.string(),
|
|
43
|
+
args: import_zod.z.array(import_zod.z.string()).default([]),
|
|
44
|
+
description: import_zod.z.string(),
|
|
45
|
+
cwd: import_zod.z.string().optional()
|
|
46
|
+
// optional working directory override
|
|
47
|
+
});
|
|
48
|
+
var PlanSchema = import_zod.z.object({
|
|
49
|
+
goal: import_zod.z.string(),
|
|
50
|
+
steps: import_zod.z.array(StepSchema).min(1).max(10)
|
|
51
|
+
});
|
|
52
|
+
function validateStep(step) {
|
|
53
|
+
const allowed = CATALOG[step.type];
|
|
54
|
+
if (step.type === "shell") {
|
|
55
|
+
return { valid: true };
|
|
56
|
+
}
|
|
57
|
+
if (!allowed.includes(step.command)) {
|
|
58
|
+
return {
|
|
59
|
+
valid: false,
|
|
60
|
+
reason: `"${step.command}" is not an allowed command for type "${step.type}". Allowed: ${allowed.join(", ")}`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { valid: true };
|
|
64
|
+
}
|
|
65
|
+
function buildCatalogPrompt() {
|
|
66
|
+
const lines = Object.entries(CATALOG).map(([type, commands]) => {
|
|
67
|
+
const list = commands[0] === "any" ? "any shell command (use sparingly)" : commands.join(", ");
|
|
68
|
+
return ` - ${type}: [${list}]`;
|
|
69
|
+
});
|
|
70
|
+
return `Allowed command types and commands:
|
|
71
|
+
${lines.join("\n")}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/planner.ts
|
|
75
|
+
function buildSystemPrompt() {
|
|
76
|
+
return `You are a CLI task planner. Given a user's goal, generate a JSON execution plan.
|
|
77
|
+
|
|
78
|
+
${buildCatalogPrompt()}
|
|
79
|
+
|
|
80
|
+
Rules:
|
|
81
|
+
- ONLY use command types and commands listed above
|
|
82
|
+
- Prefer pnpm over npm unless the user specifies otherwise
|
|
83
|
+
- Use "shell" type only when no other type fits
|
|
84
|
+
- Keep steps minimal \u2014 don't add unnecessary steps
|
|
85
|
+
- Each step must have a clear, short description
|
|
86
|
+
|
|
87
|
+
Respond ONLY with valid JSON matching this exact shape, no markdown, no explanation:
|
|
88
|
+
{
|
|
89
|
+
"goal": "string describing the overall goal",
|
|
90
|
+
"steps": [
|
|
91
|
+
{
|
|
92
|
+
"id": 1,
|
|
93
|
+
"type": "pnpm",
|
|
94
|
+
"command": "run",
|
|
95
|
+
"args": ["dev"],
|
|
96
|
+
"description": "Start dev server"
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}`;
|
|
100
|
+
}
|
|
101
|
+
async function generatePlan(userPrompt, provider) {
|
|
102
|
+
const raw = await provider.generate(userPrompt, buildSystemPrompt());
|
|
103
|
+
const cleaned = raw.replace(/```json|```/g, "").trim();
|
|
104
|
+
let parsed;
|
|
105
|
+
try {
|
|
106
|
+
parsed = JSON.parse(cleaned);
|
|
107
|
+
} catch {
|
|
108
|
+
throw new Error(`AI returned invalid JSON:
|
|
109
|
+
${cleaned}`);
|
|
110
|
+
}
|
|
111
|
+
const result = PlanSchema.safeParse(parsed);
|
|
112
|
+
if (!result.success) {
|
|
113
|
+
const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
114
|
+
throw new Error(`Plan failed schema validation:
|
|
115
|
+
${issues}`);
|
|
116
|
+
}
|
|
117
|
+
for (const step of result.data.steps) {
|
|
118
|
+
const check = validateStep(step);
|
|
119
|
+
if (!check.valid) {
|
|
120
|
+
throw new Error(`Step ${step.id} failed catalog validation: ${check.reason}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return result.data;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/runner.ts
|
|
127
|
+
var import_execa = require("execa");
|
|
128
|
+
function resolveCommand(step) {
|
|
129
|
+
if (step.type === "shell") {
|
|
130
|
+
return { bin: step.command, args: step.args };
|
|
131
|
+
}
|
|
132
|
+
return { bin: step.type, args: [step.command, ...step.args] };
|
|
133
|
+
}
|
|
134
|
+
async function runStep(step) {
|
|
135
|
+
const { bin, args } = resolveCommand(step);
|
|
136
|
+
try {
|
|
137
|
+
await (0, import_execa.execa)(bin, args, {
|
|
138
|
+
cwd: step.cwd ?? process.cwd(),
|
|
139
|
+
stdout: "inherit",
|
|
140
|
+
// stream directly to terminal
|
|
141
|
+
stderr: "inherit"
|
|
142
|
+
});
|
|
143
|
+
return { success: true };
|
|
144
|
+
} catch (err) {
|
|
145
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
146
|
+
return { success: false, error: message };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function runPlan(plan, onStep) {
|
|
150
|
+
const total = plan.steps.length;
|
|
151
|
+
for (let i = 0; i < total; i++) {
|
|
152
|
+
const step = plan.steps[i];
|
|
153
|
+
onStep(step, i, total);
|
|
154
|
+
const result = await runStep(step);
|
|
155
|
+
if (!result.success) {
|
|
156
|
+
return { success: false, failedStep: step, error: result.error };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { success: true };
|
|
160
|
+
}
|
|
161
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
162
|
+
0 && (module.exports = {
|
|
163
|
+
generatePlan,
|
|
164
|
+
runPlan
|
|
165
|
+
});
|
|
166
|
+
/**
|
|
167
|
+
* @file index.ts
|
|
168
|
+
* @description Core entry point for @ekaone/json-cli.
|
|
169
|
+
* @author Eka Prasetia
|
|
170
|
+
* @website https://prasetia.me
|
|
171
|
+
* @license MIT
|
|
172
|
+
*/
|
|
173
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/catalog.ts","../src/planner.ts","../src/runner.ts"],"sourcesContent":["/**\r\n * @file index.ts\r\n * @description Core entry point for @ekaone/json-cli.\r\n * @author Eka Prasetia\r\n * @website https://prasetia.me\r\n * @license MIT\r\n */\r\n\r\nexport { generatePlan } from \"./planner.js\";\r\nexport { runPlan } from \"./runner.js\";\r\nexport type { Plan, Step } from \"./catalog.js\";\r\nexport type { AIProvider } from \"./providers/types.js\";\r\n","import { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Allowed commands per type — the whitelist that prevents hallucination\n// ---------------------------------------------------------------------------\nexport const CATALOG = {\n npm: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"ci\"],\n pnpm: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"add\", \"remove\"],\n yarn: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"add\", \"remove\"],\n bun: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"add\", \"remove\"],\n git: [\"init\", \"add\", \"commit\", \"push\", \"pull\", \"clone\", \"status\", \"log\"],\n shell: [\"any\"], // escape hatch — always requires extra confirmation\n} as const;\n\nexport type CommandType = keyof typeof CATALOG;\n\n// ---------------------------------------------------------------------------\n// Zod schemas — Layer 2 defense against hallucinated output\n// ---------------------------------------------------------------------------\nexport const StepSchema = z.object({\n id: z.number(),\n type: z.enum([\"npm\", \"pnpm\", \"yarn\", \"bun\", \"git\", \"shell\"]),\n command: z.string(),\n args: z.array(z.string()).default([]),\n description: z.string(),\n cwd: z.string().optional(), // optional working directory override\n});\n\nexport const PlanSchema = z.object({\n goal: z.string(),\n steps: z.array(StepSchema).min(1).max(10),\n});\n\nexport type Step = z.infer<typeof StepSchema>;\nexport type Plan = z.infer<typeof PlanSchema>;\n\n// ---------------------------------------------------------------------------\n// Catalog validation — Layer 3: check command is in whitelist\n// ---------------------------------------------------------------------------\nexport function validateStep(step: Step): { valid: boolean; reason?: string } {\n const allowed = CATALOG[step.type];\n\n // shell type is always allowed but flagged for extra confirmation\n if (step.type === \"shell\") {\n return { valid: true };\n }\n\n if (!allowed.includes(step.command as never)) {\n return {\n valid: false,\n reason: `\"${step.command}\" is not an allowed command for type \"${step.type}\". Allowed: ${allowed.join(\", \")}`,\n };\n }\n\n return { valid: true };\n}\n\n// ---------------------------------------------------------------------------\n// Build the catalog string injected into AI system prompt\n// ---------------------------------------------------------------------------\nexport function buildCatalogPrompt(): string {\n const lines = Object.entries(CATALOG).map(([type, commands]) => {\n const list = commands[0] === \"any\" ? \"any shell command (use sparingly)\" : commands.join(\", \");\n return ` - ${type}: [${list}]`;\n });\n\n return `Allowed command types and commands:\\n${lines.join(\"\\n\")}`;\n}\n","import { buildCatalogPrompt, PlanSchema, validateStep } from \"./catalog.js\";\nimport type { AIProvider } from \"./providers/types.js\";\nimport type { Plan } from \"./catalog.js\";\n\n// ---------------------------------------------------------------------------\n// System prompt — constrains AI to only produce catalog-valid JSON\n// ---------------------------------------------------------------------------\nfunction buildSystemPrompt(): string {\n return `You are a CLI task planner. Given a user's goal, generate a JSON execution plan.\n\n${buildCatalogPrompt()}\n\nRules:\n- ONLY use command types and commands listed above\n- Prefer pnpm over npm unless the user specifies otherwise\n- Use \"shell\" type only when no other type fits\n- Keep steps minimal — don't add unnecessary steps\n- Each step must have a clear, short description\n\nRespond ONLY with valid JSON matching this exact shape, no markdown, no explanation:\n{\n \"goal\": \"string describing the overall goal\",\n \"steps\": [\n {\n \"id\": 1,\n \"type\": \"pnpm\",\n \"command\": \"run\",\n \"args\": [\"dev\"],\n \"description\": \"Start dev server\"\n }\n ]\n}`;\n}\n\n// ---------------------------------------------------------------------------\n// Main planner function\n// ---------------------------------------------------------------------------\nexport async function generatePlan(userPrompt: string, provider: AIProvider): Promise<Plan> {\n const raw = await provider.generate(userPrompt, buildSystemPrompt());\n\n // Strip markdown fences if any provider wraps output\n const cleaned = raw.replace(/```json|```/g, \"\").trim();\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(cleaned);\n } catch {\n throw new Error(`AI returned invalid JSON:\\n${cleaned}`);\n }\n\n // Layer 2: Zod shape validation\n const result = PlanSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` - ${i.path.join(\".\")}: ${i.message}`).join(\"\\n\");\n throw new Error(`Plan failed schema validation:\\n${issues}`);\n }\n\n // Layer 3: Catalog whitelist validation\n for (const step of result.data.steps) {\n const check = validateStep(step);\n if (!check.valid) {\n throw new Error(`Step ${step.id} failed catalog validation: ${check.reason}`);\n }\n }\n\n return result.data;\n}\n","import { execa } from \"execa\";\nimport type { Plan, Step } from \"./catalog.js\";\n\n// ---------------------------------------------------------------------------\n// Build the actual shell command from a Step\n// ---------------------------------------------------------------------------\nfunction resolveCommand(step: Step): { bin: string; args: string[] } {\n if (step.type === \"shell\") {\n // shell: command is the binary, args are the args\n return { bin: step.command, args: step.args };\n }\n\n // For npm/pnpm/yarn/bun/git: binary is the type, command + args follow\n return { bin: step.type, args: [step.command, ...step.args] };\n}\n\n// ---------------------------------------------------------------------------\n// Run a single step, streaming stdout/stderr live\n// ---------------------------------------------------------------------------\nexport async function runStep(step: Step): Promise<{ success: boolean; error?: string }> {\n const { bin, args } = resolveCommand(step);\n\n try {\n await execa(bin, args, {\n cwd: step.cwd ?? process.cwd(),\n stdout: \"inherit\", // stream directly to terminal\n stderr: \"inherit\",\n });\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: message };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Run the full plan, stopping on first failure\n// ---------------------------------------------------------------------------\nexport async function runPlan(\n plan: Plan,\n onStep: (step: Step, index: number, total: number) => void\n): Promise<{ success: boolean; failedStep?: Step; error?: string }> {\n const total = plan.steps.length;\n\n for (let i = 0; i < total; i++) {\n const step = plan.steps[i];\n onStep(step, i, total);\n\n const result = await runStep(step);\n\n if (!result.success) {\n return { success: false, failedStep: step, error: result.error };\n }\n }\n\n return { success: true };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
interface AIProvider {
|
|
4
|
+
name: string;
|
|
5
|
+
generate(userPrompt: string, systemPrompt: string): Promise<string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare const StepSchema: z.ZodObject<{
|
|
9
|
+
id: z.ZodNumber;
|
|
10
|
+
type: z.ZodEnum<{
|
|
11
|
+
npm: "npm";
|
|
12
|
+
pnpm: "pnpm";
|
|
13
|
+
yarn: "yarn";
|
|
14
|
+
bun: "bun";
|
|
15
|
+
git: "git";
|
|
16
|
+
shell: "shell";
|
|
17
|
+
}>;
|
|
18
|
+
command: z.ZodString;
|
|
19
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
20
|
+
description: z.ZodString;
|
|
21
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
declare const PlanSchema: z.ZodObject<{
|
|
24
|
+
goal: z.ZodString;
|
|
25
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
26
|
+
id: z.ZodNumber;
|
|
27
|
+
type: z.ZodEnum<{
|
|
28
|
+
npm: "npm";
|
|
29
|
+
pnpm: "pnpm";
|
|
30
|
+
yarn: "yarn";
|
|
31
|
+
bun: "bun";
|
|
32
|
+
git: "git";
|
|
33
|
+
shell: "shell";
|
|
34
|
+
}>;
|
|
35
|
+
command: z.ZodString;
|
|
36
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
37
|
+
description: z.ZodString;
|
|
38
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
39
|
+
}, z.core.$strip>>;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
type Step = z.infer<typeof StepSchema>;
|
|
42
|
+
type Plan = z.infer<typeof PlanSchema>;
|
|
43
|
+
|
|
44
|
+
declare function generatePlan(userPrompt: string, provider: AIProvider): Promise<Plan>;
|
|
45
|
+
|
|
46
|
+
declare function runPlan(plan: Plan, onStep: (step: Step, index: number, total: number) => void): Promise<{
|
|
47
|
+
success: boolean;
|
|
48
|
+
failedStep?: Step;
|
|
49
|
+
error?: string;
|
|
50
|
+
}>;
|
|
51
|
+
|
|
52
|
+
export { type AIProvider, type Plan, type Step, generatePlan, runPlan };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
interface AIProvider {
|
|
4
|
+
name: string;
|
|
5
|
+
generate(userPrompt: string, systemPrompt: string): Promise<string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare const StepSchema: z.ZodObject<{
|
|
9
|
+
id: z.ZodNumber;
|
|
10
|
+
type: z.ZodEnum<{
|
|
11
|
+
npm: "npm";
|
|
12
|
+
pnpm: "pnpm";
|
|
13
|
+
yarn: "yarn";
|
|
14
|
+
bun: "bun";
|
|
15
|
+
git: "git";
|
|
16
|
+
shell: "shell";
|
|
17
|
+
}>;
|
|
18
|
+
command: z.ZodString;
|
|
19
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
20
|
+
description: z.ZodString;
|
|
21
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
declare const PlanSchema: z.ZodObject<{
|
|
24
|
+
goal: z.ZodString;
|
|
25
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
26
|
+
id: z.ZodNumber;
|
|
27
|
+
type: z.ZodEnum<{
|
|
28
|
+
npm: "npm";
|
|
29
|
+
pnpm: "pnpm";
|
|
30
|
+
yarn: "yarn";
|
|
31
|
+
bun: "bun";
|
|
32
|
+
git: "git";
|
|
33
|
+
shell: "shell";
|
|
34
|
+
}>;
|
|
35
|
+
command: z.ZodString;
|
|
36
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
37
|
+
description: z.ZodString;
|
|
38
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
39
|
+
}, z.core.$strip>>;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
type Step = z.infer<typeof StepSchema>;
|
|
42
|
+
type Plan = z.infer<typeof PlanSchema>;
|
|
43
|
+
|
|
44
|
+
declare function generatePlan(userPrompt: string, provider: AIProvider): Promise<Plan>;
|
|
45
|
+
|
|
46
|
+
declare function runPlan(plan: Plan, onStep: (step: Step, index: number, total: number) => void): Promise<{
|
|
47
|
+
success: boolean;
|
|
48
|
+
failedStep?: Step;
|
|
49
|
+
error?: string;
|
|
50
|
+
}>;
|
|
51
|
+
|
|
52
|
+
export { type AIProvider, type Plan, type Step, generatePlan, runPlan };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// src/catalog.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var CATALOG = {
|
|
4
|
+
npm: ["install", "run", "build", "test", "publish", "ci"],
|
|
5
|
+
pnpm: ["install", "run", "build", "test", "publish", "add", "remove"],
|
|
6
|
+
yarn: ["install", "run", "build", "test", "publish", "add", "remove"],
|
|
7
|
+
bun: ["install", "run", "build", "test", "publish", "add", "remove"],
|
|
8
|
+
git: ["init", "add", "commit", "push", "pull", "clone", "status", "log"],
|
|
9
|
+
shell: ["any"]
|
|
10
|
+
// escape hatch — always requires extra confirmation
|
|
11
|
+
};
|
|
12
|
+
var StepSchema = z.object({
|
|
13
|
+
id: z.number(),
|
|
14
|
+
type: z.enum(["npm", "pnpm", "yarn", "bun", "git", "shell"]),
|
|
15
|
+
command: z.string(),
|
|
16
|
+
args: z.array(z.string()).default([]),
|
|
17
|
+
description: z.string(),
|
|
18
|
+
cwd: z.string().optional()
|
|
19
|
+
// optional working directory override
|
|
20
|
+
});
|
|
21
|
+
var PlanSchema = z.object({
|
|
22
|
+
goal: z.string(),
|
|
23
|
+
steps: z.array(StepSchema).min(1).max(10)
|
|
24
|
+
});
|
|
25
|
+
function validateStep(step) {
|
|
26
|
+
const allowed = CATALOG[step.type];
|
|
27
|
+
if (step.type === "shell") {
|
|
28
|
+
return { valid: true };
|
|
29
|
+
}
|
|
30
|
+
if (!allowed.includes(step.command)) {
|
|
31
|
+
return {
|
|
32
|
+
valid: false,
|
|
33
|
+
reason: `"${step.command}" is not an allowed command for type "${step.type}". Allowed: ${allowed.join(", ")}`
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return { valid: true };
|
|
37
|
+
}
|
|
38
|
+
function buildCatalogPrompt() {
|
|
39
|
+
const lines = Object.entries(CATALOG).map(([type, commands]) => {
|
|
40
|
+
const list = commands[0] === "any" ? "any shell command (use sparingly)" : commands.join(", ");
|
|
41
|
+
return ` - ${type}: [${list}]`;
|
|
42
|
+
});
|
|
43
|
+
return `Allowed command types and commands:
|
|
44
|
+
${lines.join("\n")}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/planner.ts
|
|
48
|
+
function buildSystemPrompt() {
|
|
49
|
+
return `You are a CLI task planner. Given a user's goal, generate a JSON execution plan.
|
|
50
|
+
|
|
51
|
+
${buildCatalogPrompt()}
|
|
52
|
+
|
|
53
|
+
Rules:
|
|
54
|
+
- ONLY use command types and commands listed above
|
|
55
|
+
- Prefer pnpm over npm unless the user specifies otherwise
|
|
56
|
+
- Use "shell" type only when no other type fits
|
|
57
|
+
- Keep steps minimal \u2014 don't add unnecessary steps
|
|
58
|
+
- Each step must have a clear, short description
|
|
59
|
+
|
|
60
|
+
Respond ONLY with valid JSON matching this exact shape, no markdown, no explanation:
|
|
61
|
+
{
|
|
62
|
+
"goal": "string describing the overall goal",
|
|
63
|
+
"steps": [
|
|
64
|
+
{
|
|
65
|
+
"id": 1,
|
|
66
|
+
"type": "pnpm",
|
|
67
|
+
"command": "run",
|
|
68
|
+
"args": ["dev"],
|
|
69
|
+
"description": "Start dev server"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}`;
|
|
73
|
+
}
|
|
74
|
+
async function generatePlan(userPrompt, provider) {
|
|
75
|
+
const raw = await provider.generate(userPrompt, buildSystemPrompt());
|
|
76
|
+
const cleaned = raw.replace(/```json|```/g, "").trim();
|
|
77
|
+
let parsed;
|
|
78
|
+
try {
|
|
79
|
+
parsed = JSON.parse(cleaned);
|
|
80
|
+
} catch {
|
|
81
|
+
throw new Error(`AI returned invalid JSON:
|
|
82
|
+
${cleaned}`);
|
|
83
|
+
}
|
|
84
|
+
const result = PlanSchema.safeParse(parsed);
|
|
85
|
+
if (!result.success) {
|
|
86
|
+
const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
87
|
+
throw new Error(`Plan failed schema validation:
|
|
88
|
+
${issues}`);
|
|
89
|
+
}
|
|
90
|
+
for (const step of result.data.steps) {
|
|
91
|
+
const check = validateStep(step);
|
|
92
|
+
if (!check.valid) {
|
|
93
|
+
throw new Error(`Step ${step.id} failed catalog validation: ${check.reason}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return result.data;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/runner.ts
|
|
100
|
+
import { execa } from "execa";
|
|
101
|
+
function resolveCommand(step) {
|
|
102
|
+
if (step.type === "shell") {
|
|
103
|
+
return { bin: step.command, args: step.args };
|
|
104
|
+
}
|
|
105
|
+
return { bin: step.type, args: [step.command, ...step.args] };
|
|
106
|
+
}
|
|
107
|
+
async function runStep(step) {
|
|
108
|
+
const { bin, args } = resolveCommand(step);
|
|
109
|
+
try {
|
|
110
|
+
await execa(bin, args, {
|
|
111
|
+
cwd: step.cwd ?? process.cwd(),
|
|
112
|
+
stdout: "inherit",
|
|
113
|
+
// stream directly to terminal
|
|
114
|
+
stderr: "inherit"
|
|
115
|
+
});
|
|
116
|
+
return { success: true };
|
|
117
|
+
} catch (err) {
|
|
118
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
119
|
+
return { success: false, error: message };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function runPlan(plan, onStep) {
|
|
123
|
+
const total = plan.steps.length;
|
|
124
|
+
for (let i = 0; i < total; i++) {
|
|
125
|
+
const step = plan.steps[i];
|
|
126
|
+
onStep(step, i, total);
|
|
127
|
+
const result = await runStep(step);
|
|
128
|
+
if (!result.success) {
|
|
129
|
+
return { success: false, failedStep: step, error: result.error };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return { success: true };
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
generatePlan,
|
|
136
|
+
runPlan
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* @file index.ts
|
|
140
|
+
* @description Core entry point for @ekaone/json-cli.
|
|
141
|
+
* @author Eka Prasetia
|
|
142
|
+
* @website https://prasetia.me
|
|
143
|
+
* @license MIT
|
|
144
|
+
*/
|
|
145
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/catalog.ts","../src/planner.ts","../src/runner.ts"],"sourcesContent":["import { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Allowed commands per type — the whitelist that prevents hallucination\n// ---------------------------------------------------------------------------\nexport const CATALOG = {\n npm: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"ci\"],\n pnpm: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"add\", \"remove\"],\n yarn: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"add\", \"remove\"],\n bun: [\"install\", \"run\", \"build\", \"test\", \"publish\", \"add\", \"remove\"],\n git: [\"init\", \"add\", \"commit\", \"push\", \"pull\", \"clone\", \"status\", \"log\"],\n shell: [\"any\"], // escape hatch — always requires extra confirmation\n} as const;\n\nexport type CommandType = keyof typeof CATALOG;\n\n// ---------------------------------------------------------------------------\n// Zod schemas — Layer 2 defense against hallucinated output\n// ---------------------------------------------------------------------------\nexport const StepSchema = z.object({\n id: z.number(),\n type: z.enum([\"npm\", \"pnpm\", \"yarn\", \"bun\", \"git\", \"shell\"]),\n command: z.string(),\n args: z.array(z.string()).default([]),\n description: z.string(),\n cwd: z.string().optional(), // optional working directory override\n});\n\nexport const PlanSchema = z.object({\n goal: z.string(),\n steps: z.array(StepSchema).min(1).max(10),\n});\n\nexport type Step = z.infer<typeof StepSchema>;\nexport type Plan = z.infer<typeof PlanSchema>;\n\n// ---------------------------------------------------------------------------\n// Catalog validation — Layer 3: check command is in whitelist\n// ---------------------------------------------------------------------------\nexport function validateStep(step: Step): { valid: boolean; reason?: string } {\n const allowed = CATALOG[step.type];\n\n // shell type is always allowed but flagged for extra confirmation\n if (step.type === \"shell\") {\n return { valid: true };\n }\n\n if (!allowed.includes(step.command as never)) {\n return {\n valid: false,\n reason: `\"${step.command}\" is not an allowed command for type \"${step.type}\". Allowed: ${allowed.join(\", \")}`,\n };\n }\n\n return { valid: true };\n}\n\n// ---------------------------------------------------------------------------\n// Build the catalog string injected into AI system prompt\n// ---------------------------------------------------------------------------\nexport function buildCatalogPrompt(): string {\n const lines = Object.entries(CATALOG).map(([type, commands]) => {\n const list = commands[0] === \"any\" ? \"any shell command (use sparingly)\" : commands.join(\", \");\n return ` - ${type}: [${list}]`;\n });\n\n return `Allowed command types and commands:\\n${lines.join(\"\\n\")}`;\n}\n","import { buildCatalogPrompt, PlanSchema, validateStep } from \"./catalog.js\";\nimport type { AIProvider } from \"./providers/types.js\";\nimport type { Plan } from \"./catalog.js\";\n\n// ---------------------------------------------------------------------------\n// System prompt — constrains AI to only produce catalog-valid JSON\n// ---------------------------------------------------------------------------\nfunction buildSystemPrompt(): string {\n return `You are a CLI task planner. Given a user's goal, generate a JSON execution plan.\n\n${buildCatalogPrompt()}\n\nRules:\n- ONLY use command types and commands listed above\n- Prefer pnpm over npm unless the user specifies otherwise\n- Use \"shell\" type only when no other type fits\n- Keep steps minimal — don't add unnecessary steps\n- Each step must have a clear, short description\n\nRespond ONLY with valid JSON matching this exact shape, no markdown, no explanation:\n{\n \"goal\": \"string describing the overall goal\",\n \"steps\": [\n {\n \"id\": 1,\n \"type\": \"pnpm\",\n \"command\": \"run\",\n \"args\": [\"dev\"],\n \"description\": \"Start dev server\"\n }\n ]\n}`;\n}\n\n// ---------------------------------------------------------------------------\n// Main planner function\n// ---------------------------------------------------------------------------\nexport async function generatePlan(userPrompt: string, provider: AIProvider): Promise<Plan> {\n const raw = await provider.generate(userPrompt, buildSystemPrompt());\n\n // Strip markdown fences if any provider wraps output\n const cleaned = raw.replace(/```json|```/g, \"\").trim();\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(cleaned);\n } catch {\n throw new Error(`AI returned invalid JSON:\\n${cleaned}`);\n }\n\n // Layer 2: Zod shape validation\n const result = PlanSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` - ${i.path.join(\".\")}: ${i.message}`).join(\"\\n\");\n throw new Error(`Plan failed schema validation:\\n${issues}`);\n }\n\n // Layer 3: Catalog whitelist validation\n for (const step of result.data.steps) {\n const check = validateStep(step);\n if (!check.valid) {\n throw new Error(`Step ${step.id} failed catalog validation: ${check.reason}`);\n }\n }\n\n return result.data;\n}\n","import { execa } from \"execa\";\nimport type { Plan, Step } from \"./catalog.js\";\n\n// ---------------------------------------------------------------------------\n// Build the actual shell command from a Step\n// ---------------------------------------------------------------------------\nfunction resolveCommand(step: Step): { bin: string; args: string[] } {\n if (step.type === \"shell\") {\n // shell: command is the binary, args are the args\n return { bin: step.command, args: step.args };\n }\n\n // For npm/pnpm/yarn/bun/git: binary is the type, command + args follow\n return { bin: step.type, args: [step.command, ...step.args] };\n}\n\n// ---------------------------------------------------------------------------\n// Run a single step, streaming stdout/stderr live\n// ---------------------------------------------------------------------------\nexport async function runStep(step: Step): Promise<{ success: boolean; error?: string }> {\n const { bin, args } = resolveCommand(step);\n\n try {\n await execa(bin, args, {\n cwd: step.cwd ?? process.cwd(),\n stdout: \"inherit\", // stream directly to terminal\n stderr: \"inherit\",\n });\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: message };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Run the full plan, stopping on first failure\n// ---------------------------------------------------------------------------\nexport async function runPlan(\n plan: Plan,\n onStep: (step: Step, index: number, total: number) => void\n): Promise<{ success: boolean; failedStep?: Step; error?: string }> {\n const total = plan.steps.length;\n\n for (let i = 0; i < total; i++) {\n const step = plan.steps[i];\n onStep(step, i, total);\n\n const result = await runStep(step);\n\n if (!result.success) {\n return { success: false, failedStep: step, error: result.error };\n }\n }\n\n return { success: true };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ekaone/json-cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "AI-powered CLI task runner with JSON command plans",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"agent",
|
|
8
|
+
"cli",
|
|
9
|
+
"task-runner",
|
|
10
|
+
"llm"
|
|
11
|
+
],
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Eka Prasetia",
|
|
14
|
+
"email": "ekaone3033@gmail.com",
|
|
15
|
+
"url": "https://prasetia.me"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/ekaone/json-cli",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/ekaone/json-cli"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"type": "module",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"require": "./dist/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"bin": {
|
|
35
|
+
"json-cli": "./dist/cli.cjs"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@anthropic-ai/sdk": "^0.80.0",
|
|
39
|
+
"@clack/prompts": "^1.1.0",
|
|
40
|
+
"execa": "^9.6.1",
|
|
41
|
+
"openai": "^6.32.0",
|
|
42
|
+
"zod": "^4.3.6"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^25.5.0",
|
|
46
|
+
"tsup": "^8.5.1",
|
|
47
|
+
"tsx": "^4.21.0",
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"vitest": "^4.1.1"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18.0.0"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"dev": "tsx src/cli.ts",
|
|
59
|
+
"build": "tsup",
|
|
60
|
+
"test": "vitest",
|
|
61
|
+
"typecheck": "tsc --noEmit"
|
|
62
|
+
}
|
|
63
|
+
}
|