@kody-ade/kody-engine-lite 0.1.3 → 0.1.5
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 +305 -0
- package/dist/bin/cli.js +86 -5
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Kody Engine Lite
|
|
2
|
+
|
|
3
|
+
Autonomous SDLC pipeline — runs a 7-stage pipeline (taskify → plan → build → verify → review → review-fix → ship) on your codebase using Claude Code as the execution engine.
|
|
4
|
+
|
|
5
|
+
## How it works
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
@kody full <task-id> (comment on a GitHub issue)
|
|
9
|
+
↓
|
|
10
|
+
GitHub Actions workflow triggers
|
|
11
|
+
↓
|
|
12
|
+
Kody Engine Lite (npm package)
|
|
13
|
+
↓
|
|
14
|
+
7-stage pipeline:
|
|
15
|
+
1. taskify — classify the task (haiku)
|
|
16
|
+
2. plan — create implementation plan (sonnet)
|
|
17
|
+
3. build — implement code changes (opus)
|
|
18
|
+
4. verify — run typecheck + tests + lint
|
|
19
|
+
5. review — code review (sonnet)
|
|
20
|
+
6. review-fix — fix review issues (opus)
|
|
21
|
+
7. ship — push branch + create PR
|
|
22
|
+
↓
|
|
23
|
+
PR created on your repo
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g @kody-ade/kody-engine-lite
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. Init (run in your project root)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cd your-project
|
|
38
|
+
kody-engine-lite init
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This will:
|
|
42
|
+
- Copy `.github/workflows/kody.yml` to your repo
|
|
43
|
+
- Create `kody.config.json` (edit this)
|
|
44
|
+
- Create `.kody/memory/architecture.md` (auto-detected)
|
|
45
|
+
- Create `.kody/memory/conventions.md` (seed)
|
|
46
|
+
- Add `.tasks/` to `.gitignore`
|
|
47
|
+
- Run health checks (prerequisites, GitHub auth, secrets)
|
|
48
|
+
|
|
49
|
+
### 3. Configure
|
|
50
|
+
|
|
51
|
+
Edit `kody.config.json`:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"quality": {
|
|
56
|
+
"typecheck": "pnpm tsc --noEmit",
|
|
57
|
+
"lint": "pnpm lint",
|
|
58
|
+
"lintFix": "pnpm lint:fix",
|
|
59
|
+
"format": "",
|
|
60
|
+
"formatFix": "",
|
|
61
|
+
"testUnit": "pnpm test"
|
|
62
|
+
},
|
|
63
|
+
"git": {
|
|
64
|
+
"defaultBranch": "main"
|
|
65
|
+
},
|
|
66
|
+
"github": {
|
|
67
|
+
"owner": "your-org",
|
|
68
|
+
"repo": "your-repo"
|
|
69
|
+
},
|
|
70
|
+
"paths": {
|
|
71
|
+
"taskDir": ".tasks"
|
|
72
|
+
},
|
|
73
|
+
"agent": {
|
|
74
|
+
"runner": "claude-code",
|
|
75
|
+
"modelMap": {
|
|
76
|
+
"cheap": "haiku",
|
|
77
|
+
"mid": "sonnet",
|
|
78
|
+
"strong": "opus"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 4. GitHub Setup
|
|
85
|
+
|
|
86
|
+
#### Required Secret
|
|
87
|
+
|
|
88
|
+
Add `ANTHROPIC_API_KEY` to your repo secrets:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
gh secret set ANTHROPIC_API_KEY --repo owner/repo
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### Required Permissions
|
|
95
|
+
|
|
96
|
+
Go to **Settings → Actions → General → Workflow permissions**:
|
|
97
|
+
|
|
98
|
+
1. Select **"Read and write permissions"**
|
|
99
|
+
2. Check **"Allow GitHub Actions to create and approve pull requests"**
|
|
100
|
+
3. Save
|
|
101
|
+
|
|
102
|
+
Without this, the ship stage cannot create PRs.
|
|
103
|
+
|
|
104
|
+
### 5. Push and Use
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
git add .github/workflows/kody.yml kody.config.json .kody/
|
|
108
|
+
git commit -m "chore: add kody engine"
|
|
109
|
+
git push
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Then comment on any issue:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
@kody full my-task-id
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## CLI Usage
|
|
119
|
+
|
|
120
|
+
### Run locally
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Run against current directory
|
|
124
|
+
kody-engine-lite run --task-id my-task --task "Add a sum function to src/math.ts with tests"
|
|
125
|
+
|
|
126
|
+
# Run against a different project
|
|
127
|
+
kody-engine-lite run --task-id my-task --task "Add feature X" --cwd /path/to/project
|
|
128
|
+
|
|
129
|
+
# Run from a GitHub issue (fetches issue body as task)
|
|
130
|
+
kody-engine-lite run --task-id my-task --issue-number 1 --cwd /path/to/project
|
|
131
|
+
|
|
132
|
+
# Dry run (no agent calls)
|
|
133
|
+
kody-engine-lite run --task-id my-task --task "Test" --dry-run
|
|
134
|
+
|
|
135
|
+
# Resume from a failed stage
|
|
136
|
+
kody-engine-lite rerun --task-id my-task --from review
|
|
137
|
+
|
|
138
|
+
# Check pipeline status
|
|
139
|
+
kody-engine-lite status --task-id my-task
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Init a new project
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
cd your-project
|
|
146
|
+
kody-engine-lite init # setup workflow, config, memory
|
|
147
|
+
kody-engine-lite init --force # overwrite existing workflow
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### GitHub Comment Triggers
|
|
151
|
+
|
|
152
|
+
Comment on any issue:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
@kody full <task-id> # Run full pipeline
|
|
156
|
+
@kody rerun <task-id> --from <stage> # Resume from stage
|
|
157
|
+
@kody status <task-id> # Check status
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Pipeline Stages
|
|
161
|
+
|
|
162
|
+
| Stage | Model | What it does |
|
|
163
|
+
|-------|-------|-------------|
|
|
164
|
+
| taskify | haiku | Classifies task from issue body → `task.json` |
|
|
165
|
+
| plan | sonnet | Creates TDD implementation plan → `plan.md` |
|
|
166
|
+
| build | opus | Implements code changes (uses Claude Code tools) |
|
|
167
|
+
| verify | — | Runs typecheck + tests + lint (from `kody.config.json`) |
|
|
168
|
+
| review | sonnet | Code review → `review.md` (PASS/FAIL + findings) |
|
|
169
|
+
| review-fix | opus | Fixes Critical/Major review findings |
|
|
170
|
+
| ship | — | Pushes branch + creates PR + comments on issue |
|
|
171
|
+
|
|
172
|
+
### Automatic Loops
|
|
173
|
+
|
|
174
|
+
- **Verify + autofix**: If verify fails, runs lint-fix + format-fix + autofix agent, retries up to 2 times
|
|
175
|
+
- **Review + fix**: If review verdict is FAIL, runs review-fix agent then re-reviews
|
|
176
|
+
|
|
177
|
+
## Memory System
|
|
178
|
+
|
|
179
|
+
Kody maintains project memory in `.kody/memory/`:
|
|
180
|
+
|
|
181
|
+
- **`architecture.md`** — auto-detected on `init` (framework, language, testing, directory structure)
|
|
182
|
+
- **`conventions.md`** — auto-learned after each successful pipeline run
|
|
183
|
+
|
|
184
|
+
Memory is prepended to every agent prompt, giving Claude Code project context.
|
|
185
|
+
|
|
186
|
+
## Configuration Reference
|
|
187
|
+
|
|
188
|
+
### `kody.config.json`
|
|
189
|
+
|
|
190
|
+
| Field | Description | Default |
|
|
191
|
+
|-------|-------------|---------|
|
|
192
|
+
| `quality.typecheck` | Typecheck command | `pnpm -s tsc --noEmit` |
|
|
193
|
+
| `quality.lint` | Lint command | `pnpm -s lint` |
|
|
194
|
+
| `quality.lintFix` | Lint fix command | `pnpm lint:fix` |
|
|
195
|
+
| `quality.format` | Format check command | `""` |
|
|
196
|
+
| `quality.formatFix` | Format fix command | `""` |
|
|
197
|
+
| `quality.testUnit` | Test command | `pnpm -s test` |
|
|
198
|
+
| `git.defaultBranch` | Default branch | `dev` |
|
|
199
|
+
| `github.owner` | GitHub org/user | `""` |
|
|
200
|
+
| `github.repo` | GitHub repo name | `""` |
|
|
201
|
+
| `paths.taskDir` | Task artifacts directory | `.tasks` |
|
|
202
|
+
| `agent.modelMap.cheap` | Model for taskify | `haiku` |
|
|
203
|
+
| `agent.modelMap.mid` | Model for plan/review | `sonnet` |
|
|
204
|
+
| `agent.modelMap.strong` | Model for build/review-fix | `opus` |
|
|
205
|
+
|
|
206
|
+
### Environment Variables
|
|
207
|
+
|
|
208
|
+
| Variable | Required | Description |
|
|
209
|
+
|----------|----------|-------------|
|
|
210
|
+
| `ANTHROPIC_API_KEY` | Yes | Claude API key (set as repo secret) |
|
|
211
|
+
| `GH_TOKEN` | Auto | GitHub token (provided by Actions) |
|
|
212
|
+
| `GH_PAT` | No | Personal access token (preferred over GH_TOKEN) |
|
|
213
|
+
| `LOG_LEVEL` | No | `debug`, `info`, `warn`, `error` (default: `info`) |
|
|
214
|
+
| `LITELLM_BASE_URL` | No | LiteLLM proxy URL for model routing |
|
|
215
|
+
|
|
216
|
+
## LiteLLM (Optional)
|
|
217
|
+
|
|
218
|
+
For multi-provider model routing with fallback:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
pip install litellm[proxy]
|
|
222
|
+
litellm --config litellm-config.yaml --port 4000
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Then set in `kody.config.json`:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"agent": {
|
|
230
|
+
"litellmUrl": "http://localhost:4000",
|
|
231
|
+
"modelMap": { "cheap": "cheap", "mid": "mid", "strong": "strong" }
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Task Artifacts
|
|
237
|
+
|
|
238
|
+
Each pipeline run creates artifacts in `.tasks/<task-id>/`:
|
|
239
|
+
|
|
240
|
+
| File | Created by | Content |
|
|
241
|
+
|------|-----------|---------|
|
|
242
|
+
| `task.md` | entry / issue fetch | Task description |
|
|
243
|
+
| `task.json` | taskify stage | Structured classification |
|
|
244
|
+
| `plan.md` | plan stage | Implementation steps |
|
|
245
|
+
| `verify.md` | verify stage | Quality gate results |
|
|
246
|
+
| `review.md` | review stage | Code review + verdict |
|
|
247
|
+
| `ship.md` | ship stage | PR URL |
|
|
248
|
+
| `status.json` | state machine | Pipeline state (per-stage) |
|
|
249
|
+
|
|
250
|
+
## Architecture
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
@kody-ade/kody-engine-lite (npm package)
|
|
254
|
+
├── dist/bin/cli.js — CLI entry point
|
|
255
|
+
├── prompts/ — Stage prompt templates
|
|
256
|
+
│ ├── taskify.md
|
|
257
|
+
│ ├── plan.md
|
|
258
|
+
│ ├── build.md
|
|
259
|
+
│ ├── review.md
|
|
260
|
+
│ ├── review-fix.md
|
|
261
|
+
│ └── autofix.md
|
|
262
|
+
└── templates/
|
|
263
|
+
└── kody.yml — GitHub Actions workflow template
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Source files (in this repo):
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
src/
|
|
270
|
+
├── entry.ts — CLI argument parsing, pipeline dispatch
|
|
271
|
+
├── types.ts — TypeScript interfaces
|
|
272
|
+
├── definitions.ts — 7-stage pipeline configuration
|
|
273
|
+
├── state-machine.ts — Pipeline orchestration loop
|
|
274
|
+
├── agent-runner.ts — Claude Code subprocess wrapper
|
|
275
|
+
├── context.ts — Prompt assembly + task context injection
|
|
276
|
+
├── memory.ts — Project memory reader
|
|
277
|
+
├── config.ts — Config loading (kody.config.json)
|
|
278
|
+
├── logger.ts — Structured logging
|
|
279
|
+
├── preflight.ts — Startup checks
|
|
280
|
+
├── validators.ts — Output validation
|
|
281
|
+
├── verify-runner.ts — Quality gate execution
|
|
282
|
+
├── kody-utils.ts — Task directory utilities
|
|
283
|
+
├── git-utils.ts — Git operations
|
|
284
|
+
├── github-api.ts — GitHub API via gh CLI
|
|
285
|
+
└── bin/cli.ts — Package CLI (init, run, version)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Development
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
# Install deps
|
|
292
|
+
pnpm install
|
|
293
|
+
|
|
294
|
+
# Type check
|
|
295
|
+
pnpm typecheck
|
|
296
|
+
|
|
297
|
+
# Run locally (dev mode)
|
|
298
|
+
pnpm kody run --task-id test --task "Add feature" --cwd /path/to/project
|
|
299
|
+
|
|
300
|
+
# Build package
|
|
301
|
+
pnpm build
|
|
302
|
+
|
|
303
|
+
# Publish
|
|
304
|
+
npm publish --access public
|
|
305
|
+
```
|
package/dist/bin/cli.js
CHANGED
|
@@ -279,7 +279,7 @@ function readPromptFile(stageName) {
|
|
|
279
279
|
}
|
|
280
280
|
throw new Error(`Prompt file not found: tried ${candidates.join(", ")}`);
|
|
281
281
|
}
|
|
282
|
-
function injectTaskContext(prompt, taskId, taskDir) {
|
|
282
|
+
function injectTaskContext(prompt, taskId, taskDir, feedback) {
|
|
283
283
|
let context = `## Task Context
|
|
284
284
|
`;
|
|
285
285
|
context += `Task ID: ${taskId}
|
|
@@ -326,14 +326,20 @@ ${truncated}${spec.length > MAX_TASK_CONTEXT_SPEC ? "\n..." : ""}
|
|
|
326
326
|
context += `
|
|
327
327
|
## Plan Summary
|
|
328
328
|
${truncated}${plan.length > MAX_TASK_CONTEXT_PLAN ? "\n..." : ""}
|
|
329
|
+
`;
|
|
330
|
+
}
|
|
331
|
+
if (feedback) {
|
|
332
|
+
context += `
|
|
333
|
+
## Human Feedback
|
|
334
|
+
${feedback}
|
|
329
335
|
`;
|
|
330
336
|
}
|
|
331
337
|
return prompt.replace("{{TASK_CONTEXT}}", context);
|
|
332
338
|
}
|
|
333
|
-
function buildFullPrompt(stageName, taskId, taskDir, projectDir) {
|
|
339
|
+
function buildFullPrompt(stageName, taskId, taskDir, projectDir, feedback) {
|
|
334
340
|
const memory = readProjectMemory(projectDir);
|
|
335
341
|
const promptTemplate = readPromptFile(stageName);
|
|
336
|
-
const prompt = injectTaskContext(promptTemplate, taskId, taskDir);
|
|
342
|
+
const prompt = injectTaskContext(promptTemplate, taskId, taskDir, feedback);
|
|
337
343
|
return memory ? `${memory}
|
|
338
344
|
---
|
|
339
345
|
|
|
@@ -780,7 +786,7 @@ async function executeAgentStage(ctx, def) {
|
|
|
780
786
|
logger.info(` [dry-run] skipping ${def.name}`);
|
|
781
787
|
return { outcome: "completed", retries: 0 };
|
|
782
788
|
}
|
|
783
|
-
const prompt = buildFullPrompt(def.name, ctx.taskId, ctx.taskDir, ctx.projectDir);
|
|
789
|
+
const prompt = buildFullPrompt(def.name, ctx.taskId, ctx.taskDir, ctx.projectDir, ctx.input.feedback);
|
|
784
790
|
const model = resolveModel(def.modelTier, def.name);
|
|
785
791
|
logger.info(` model=${model} timeout=${def.timeout / 1e3}s`);
|
|
786
792
|
const config = getProjectConfig();
|
|
@@ -798,6 +804,21 @@ async function executeAgentStage(ctx, def) {
|
|
|
798
804
|
if (def.outputFile && result.output) {
|
|
799
805
|
fs4.writeFileSync(path4.join(ctx.taskDir, def.outputFile), result.output);
|
|
800
806
|
}
|
|
807
|
+
if (def.outputFile) {
|
|
808
|
+
const outputPath = path4.join(ctx.taskDir, def.outputFile);
|
|
809
|
+
if (!fs4.existsSync(outputPath)) {
|
|
810
|
+
const ext = path4.extname(def.outputFile);
|
|
811
|
+
const base = path4.basename(def.outputFile, ext);
|
|
812
|
+
const files = fs4.readdirSync(ctx.taskDir);
|
|
813
|
+
const variant = files.find(
|
|
814
|
+
(f) => f.startsWith(base + "-") && f.endsWith(ext)
|
|
815
|
+
);
|
|
816
|
+
if (variant) {
|
|
817
|
+
fs4.renameSync(path4.join(ctx.taskDir, variant), outputPath);
|
|
818
|
+
logger.info(` Renamed variant ${variant} \u2192 ${def.outputFile}`);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
801
822
|
if (def.outputFile) {
|
|
802
823
|
const outputPath = path4.join(ctx.taskDir, def.outputFile);
|
|
803
824
|
if (fs4.existsSync(outputPath)) {
|
|
@@ -947,7 +968,13 @@ function executeShipStage(ctx, _def) {
|
|
|
947
968
|
const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
|
|
948
969
|
title = (lines[0] ?? "Update").slice(0, 72);
|
|
949
970
|
}
|
|
950
|
-
const
|
|
971
|
+
const closesLine = ctx.input.issueNumber ? `
|
|
972
|
+
|
|
973
|
+
Closes #${ctx.input.issueNumber}` : "";
|
|
974
|
+
const body = `Generated by Kody pipeline${closesLine}
|
|
975
|
+
|
|
976
|
+
---
|
|
977
|
+
\u{1F916} Generated by Kody`;
|
|
951
978
|
const pr = createPR(head, base, title, body);
|
|
952
979
|
if (pr) {
|
|
953
980
|
if (ctx.input.issueNumber && !ctx.input.local) {
|
|
@@ -1479,6 +1506,20 @@ Artifacts in ${taskDir}:`);
|
|
|
1479
1506
|
console.log(` ${f}`);
|
|
1480
1507
|
}
|
|
1481
1508
|
if (state.state === "failed") {
|
|
1509
|
+
if (ctx.input.issueNumber && !ctx.input.local) {
|
|
1510
|
+
const failedStage = Object.entries(state.stages).find(
|
|
1511
|
+
([, s]) => s.state === "failed" || s.state === "timeout"
|
|
1512
|
+
);
|
|
1513
|
+
const stageName = failedStage ? failedStage[0] : "unknown";
|
|
1514
|
+
const error = failedStage ? failedStage[1].error ?? "" : "";
|
|
1515
|
+
try {
|
|
1516
|
+
postComment(
|
|
1517
|
+
ctx.input.issueNumber,
|
|
1518
|
+
`\u274C Pipeline failed at **${stageName}**${error ? `: ${error.slice(0, 200)}` : ""}`
|
|
1519
|
+
);
|
|
1520
|
+
} catch {
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1482
1523
|
process.exit(1);
|
|
1483
1524
|
}
|
|
1484
1525
|
}
|
|
@@ -1742,6 +1783,46 @@ function initCommand(opts) {
|
|
|
1742
1783
|
console.log(` \u2717 ${c.name} \u2014 ${c.fix}`);
|
|
1743
1784
|
}
|
|
1744
1785
|
}
|
|
1786
|
+
const labels = [
|
|
1787
|
+
{ name: "kody:planning", color: "c5def5", description: "Kody is analyzing and planning" },
|
|
1788
|
+
{ name: "kody:building", color: "0e8a16", description: "Kody is building code" },
|
|
1789
|
+
{ name: "kody:review", color: "fbca04", description: "Kody is reviewing code" },
|
|
1790
|
+
{ name: "kody:done", color: "0e8a16", description: "Kody completed successfully" },
|
|
1791
|
+
{ name: "kody:failed", color: "d93f0b", description: "Kody pipeline failed" }
|
|
1792
|
+
];
|
|
1793
|
+
console.log("\n\u2500\u2500 Labels \u2500\u2500");
|
|
1794
|
+
for (const label of labels) {
|
|
1795
|
+
try {
|
|
1796
|
+
execFileSync7("gh", [
|
|
1797
|
+
"label",
|
|
1798
|
+
"create",
|
|
1799
|
+
label.name,
|
|
1800
|
+
"--repo",
|
|
1801
|
+
repoSlug,
|
|
1802
|
+
"--color",
|
|
1803
|
+
label.color,
|
|
1804
|
+
"--description",
|
|
1805
|
+
label.description,
|
|
1806
|
+
"--force"
|
|
1807
|
+
], {
|
|
1808
|
+
encoding: "utf-8",
|
|
1809
|
+
timeout: 1e4,
|
|
1810
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1811
|
+
});
|
|
1812
|
+
console.log(` \u2713 ${label.name}`);
|
|
1813
|
+
} catch {
|
|
1814
|
+
try {
|
|
1815
|
+
execFileSync7("gh", ["label", "list", "--repo", repoSlug, "--search", label.name], {
|
|
1816
|
+
encoding: "utf-8",
|
|
1817
|
+
timeout: 1e4,
|
|
1818
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1819
|
+
});
|
|
1820
|
+
console.log(` \u25CB ${label.name} (exists)`);
|
|
1821
|
+
} catch {
|
|
1822
|
+
console.log(` \u2717 ${label.name} \u2014 failed to create`);
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1745
1826
|
}
|
|
1746
1827
|
console.log("\n\u2500\u2500 Config \u2500\u2500");
|
|
1747
1828
|
if (fs7.existsSync(configDest)) {
|