mokout-cli 0.1.4
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 +100 -0
- package/dist/cli.js +545 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohan Tupakula
|
|
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,100 @@
|
|
|
1
|
+
# mokout-cli
|
|
2
|
+
|
|
3
|
+
Quick scaffolding for agentic AI projects. One command sets up version control,
|
|
4
|
+
a package manager, and a Coding Agent-ready `AGENTS.md` — a lean, essentials-only
|
|
5
|
+
starter so you can build instead of configure.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx mokout project init
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
`mokout project init` scaffolds the current directory with **only the essentials**:
|
|
14
|
+
|
|
15
|
+
- **`git init`** — version-controlled from the first commit
|
|
16
|
+
- **Package manager** — [`uv`](https://docs.astral.sh/uv/) (Python, with `pytest`, `ruff`, `pyrefly` dev deps) or `npm` (JavaScript, with `@biomejs/biome` dev dep)
|
|
17
|
+
- **`CLAUDE.md`** — a workflow doctrine for coding agents, auto-tailored with a **Project Setup** section (your actual lint/test commands) and a **Definition of Done**
|
|
18
|
+
- **`AGENTS.md`** → symlinked to `CLAUDE.md`, so any agent reads one source of truth
|
|
19
|
+
- **`.claude/settings.json`** — a permission allowlist for the project's safe commands, so Claude Code runs them without prompting
|
|
20
|
+
- **`tasks/`** — structured `todo.md` + `lessons.md`, referenced by the doctrine
|
|
21
|
+
- **Lint + format config** — [ruff](https://docs.astral.sh/ruff/) (folded into `pyproject.toml`) or [Biome](https://biomejs.dev/) (`biome.json`)
|
|
22
|
+
- **`.gitignore`** + **`.env.example`**
|
|
23
|
+
|
|
24
|
+
That's it — no `justfile`, hooks, or CI cruft. It is **idempotent and safe**:
|
|
25
|
+
existing files are never overwritten. The `CLAUDE.md` doctrine lives in a managed
|
|
26
|
+
block that mokout updates in place on re-run — any content you add around it is
|
|
27
|
+
preserved. Run it in a fresh directory or an existing project.
|
|
28
|
+
|
|
29
|
+
## Output
|
|
30
|
+
|
|
31
|
+
**Python** (`mokout project init --python`) — 10 files:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
pyproject.toml # uv project + [tool.ruff] + pytest/ruff dev deps
|
|
35
|
+
.python-version
|
|
36
|
+
uv.lock
|
|
37
|
+
.gitignore
|
|
38
|
+
.env.example
|
|
39
|
+
CLAUDE.md
|
|
40
|
+
AGENTS.md → CLAUDE.md
|
|
41
|
+
.claude/settings.json
|
|
42
|
+
tasks/todo.md
|
|
43
|
+
tasks/lessons.md
|
|
44
|
+
.agents/skills/
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**JavaScript** (`mokout project init --js`) — 9 files:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
package.json
|
|
51
|
+
biome.json
|
|
52
|
+
.gitignore
|
|
53
|
+
.env.example
|
|
54
|
+
CLAUDE.md
|
|
55
|
+
AGENTS.md → CLAUDE.md
|
|
56
|
+
.claude/settings.json
|
|
57
|
+
tasks/todo.md
|
|
58
|
+
tasks/lessons.md
|
|
59
|
+
.agents/skills/
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
mokout project init # interactive — asks Python or JavaScript
|
|
66
|
+
mokout project init --python # Python project
|
|
67
|
+
mokout project init --js # JavaScript project
|
|
68
|
+
mokout project init --dry-run # print what would be created, write nothing
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Add just the agent files to an existing project
|
|
72
|
+
|
|
73
|
+
Already have a project and only want it agent-ready? `code-fast init` drops in
|
|
74
|
+
`CLAUDE.md`, the `AGENTS.md` symlink, and `tasks/` — no package manager, git, or
|
|
75
|
+
tooling changes:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
mokout code-fast init # add CLAUDE.md + AGENTS.md + tasks/ here
|
|
79
|
+
mokout code-fast init --dry-run # preview
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Idempotent: re-running refreshes the doctrine's managed block in `CLAUDE.md`
|
|
83
|
+
and leaves everything else (including your own notes) untouched.
|
|
84
|
+
|
|
85
|
+
## Install
|
|
86
|
+
|
|
87
|
+
No install needed — `npx mokout project init` runs the latest version. To install globally:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm install -g mokout
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Requirements
|
|
94
|
+
|
|
95
|
+
- Node ≥ 18 (for `npx`)
|
|
96
|
+
- [`uv`](https://docs.astral.sh/uv/) on PATH for Python projects; `npm` for JavaScript
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Builtins, Cli } from "clipanion";
|
|
5
|
+
|
|
6
|
+
// src/commands/code-fast-init.ts
|
|
7
|
+
import * as p from "@clack/prompts";
|
|
8
|
+
import { Command, Option } from "clipanion";
|
|
9
|
+
import nodePlop from "node-plop";
|
|
10
|
+
|
|
11
|
+
// src/generators/engine.ts
|
|
12
|
+
import {
|
|
13
|
+
appendFileSync,
|
|
14
|
+
existsSync,
|
|
15
|
+
mkdirSync,
|
|
16
|
+
readFileSync,
|
|
17
|
+
symlinkSync,
|
|
18
|
+
writeFileSync
|
|
19
|
+
} from "fs";
|
|
20
|
+
import { dirname, join } from "path";
|
|
21
|
+
var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22
|
+
function managedAction(file) {
|
|
23
|
+
const marker = file.marker ?? "mokout";
|
|
24
|
+
const begin = `<!-- mokout:${marker}:start -->`;
|
|
25
|
+
const end = `<!-- mokout:${marker}:end -->`;
|
|
26
|
+
const block = `${begin}
|
|
27
|
+
${file.content}
|
|
28
|
+
${end}
|
|
29
|
+
`;
|
|
30
|
+
return (_answers, _config, plop) => {
|
|
31
|
+
const dest = join(plop.getDestBasePath(), file.path);
|
|
32
|
+
if (!existsSync(dest)) {
|
|
33
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
34
|
+
writeFileSync(dest, block);
|
|
35
|
+
return `created ${file.path} [${marker}]`;
|
|
36
|
+
}
|
|
37
|
+
const current = readFileSync(dest, "utf8");
|
|
38
|
+
const region = new RegExp(`${escapeRegExp(begin)}[\\s\\S]*?${escapeRegExp(end)}\\n?`);
|
|
39
|
+
if (region.test(current)) {
|
|
40
|
+
writeFileSync(dest, current.replace(region, block));
|
|
41
|
+
return `updated ${file.path} [${marker}]`;
|
|
42
|
+
}
|
|
43
|
+
appendFileSync(dest, `${current.endsWith("\n") ? "\n" : "\n\n"}${block}`);
|
|
44
|
+
return `added ${file.path} [${marker}]`;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function toAction(file) {
|
|
48
|
+
if (file.mode === "managed") return managedAction(file);
|
|
49
|
+
return { type: "add", path: file.path, template: file.content, skipIfExists: true };
|
|
50
|
+
}
|
|
51
|
+
function symlinkAction(link) {
|
|
52
|
+
return (_answers, _config, plop) => {
|
|
53
|
+
const dest = join(plop.getDestBasePath(), link.path);
|
|
54
|
+
try {
|
|
55
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
56
|
+
symlinkSync(link.target, dest);
|
|
57
|
+
return `linked ${link.path} -> ${link.target}`;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
const code = err.code;
|
|
60
|
+
if (code === "EEXIST") return `skipped ${link.path} (exists)`;
|
|
61
|
+
return `skipped ${link.path} (symlink unsupported: ${code})`;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function registerGenerator(plop, spec) {
|
|
66
|
+
plop.setGenerator(spec.name, {
|
|
67
|
+
description: spec.description,
|
|
68
|
+
prompts: [],
|
|
69
|
+
actions: [...spec.files.map(toAction), ...(spec.symlinks ?? []).map(symlinkAction)]
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/lib/exec.ts
|
|
74
|
+
import { execFileSync } from "child_process";
|
|
75
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync2, readFileSync as readFileSync2, rmSync } from "fs";
|
|
76
|
+
import { join as join2 } from "path";
|
|
77
|
+
function exists(cwd, rel) {
|
|
78
|
+
return existsSync2(join2(cwd, rel));
|
|
79
|
+
}
|
|
80
|
+
function readText(cwd, rel) {
|
|
81
|
+
try {
|
|
82
|
+
return readFileSync2(join2(cwd, rel), "utf8");
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function appendText(cwd, rel, text) {
|
|
88
|
+
appendFileSync2(join2(cwd, rel), text);
|
|
89
|
+
}
|
|
90
|
+
function remove(cwd, rel) {
|
|
91
|
+
rmSync(join2(cwd, rel), { force: true, recursive: true });
|
|
92
|
+
}
|
|
93
|
+
function hasCommand(cmd) {
|
|
94
|
+
try {
|
|
95
|
+
execFileSync(process.platform === "win32" ? "where" : "which", [cmd], {
|
|
96
|
+
stdio: "ignore"
|
|
97
|
+
});
|
|
98
|
+
return true;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function run(cmd, args, cwd) {
|
|
104
|
+
execFileSync(cmd, args, { cwd, stdio: "pipe" });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/templates/claude.ts
|
|
108
|
+
var DOCTRINE = `## Workflow Orchestration
|
|
109
|
+
|
|
110
|
+
### 1. Plan Node Default
|
|
111
|
+
- Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions)
|
|
112
|
+
- If something goes sideways, STOP and re-plan immediately \u2013 don't keep pushing
|
|
113
|
+
- Use plan mode for verification steps, not just building
|
|
114
|
+
- Write detailed specs upfront to reduce ambiguity
|
|
115
|
+
|
|
116
|
+
### 2. Subagent Strategy
|
|
117
|
+
- Use subagents liberally to keep main context window clean
|
|
118
|
+
- Offload research, exploration, and parallel analysis to subagents
|
|
119
|
+
- For complex problems, throw more compute at it via subagents
|
|
120
|
+
- One task per subagent for focused execution
|
|
121
|
+
|
|
122
|
+
### 3. Self-Improvement Loop
|
|
123
|
+
- After ANY correction from the user: update \`tasks/lessons.md\` with the pattern
|
|
124
|
+
- Write rules for yourself that prevent the same mistake
|
|
125
|
+
- Ruthlessly iterate on these lessons until mistake rate drops
|
|
126
|
+
- Review lessons at session start for relevant project
|
|
127
|
+
|
|
128
|
+
### 4. Verification Before Done
|
|
129
|
+
- Never mark a task complete without proving it works
|
|
130
|
+
- Diff behavior between main and your changes when relevant
|
|
131
|
+
- Ask yourself: "Would a staff engineer approve this?"
|
|
132
|
+
- Run tests, check logs, demonstrate correctness
|
|
133
|
+
|
|
134
|
+
### 5. Demand Elegance (Balanced)
|
|
135
|
+
- For non-trivial changes: pause and ask "is there a more elegant way?"
|
|
136
|
+
- If a fix feels hacky: "Knowing everything I know now, implement the elegant solution"
|
|
137
|
+
- Skip this for simple, obvious fixes \u2013 don't over-engineer
|
|
138
|
+
- Challenge your own work before presenting it
|
|
139
|
+
|
|
140
|
+
### 6. Autonomous Bug Fixing
|
|
141
|
+
- When given a bug report: just fix it. Don't ask for hand-holding
|
|
142
|
+
- Point at logs, errors, failing tests \u2013 then resolve them
|
|
143
|
+
- Zero context switching required from the user
|
|
144
|
+
- Go fix failing CI tests without being told how
|
|
145
|
+
|
|
146
|
+
## Task Management
|
|
147
|
+
|
|
148
|
+
1. **Plan First**: Write plan to \`tasks/todo.md\` with checkable items
|
|
149
|
+
2. **Verify Plan**: Check in before starting implementation
|
|
150
|
+
3. **Track Progress**: Mark items complete as you go
|
|
151
|
+
4. **Explain Changes**: High-level summary at each step
|
|
152
|
+
5. **Document Results**: Add review section to \`tasks/todo.md\`
|
|
153
|
+
6. **Capture Lessons**: Update \`tasks/lessons.md\` after corrections
|
|
154
|
+
|
|
155
|
+
## Core Principles
|
|
156
|
+
|
|
157
|
+
- **Simplicity First**: Make every change as simple as possible. Impact minimal code.
|
|
158
|
+
- **No Laziness**: Find root causes. No temporary fixes. Senior developer standards.
|
|
159
|
+
- **Minimal Impact**: Changes should only touch what's necessary. Avoid introducing bugs.
|
|
160
|
+
`;
|
|
161
|
+
|
|
162
|
+
// src/templates/javascript.ts
|
|
163
|
+
var GITIGNORE = `node_modules/
|
|
164
|
+
dist/
|
|
165
|
+
.env
|
|
166
|
+
.next/
|
|
167
|
+
*.log
|
|
168
|
+
.DS_Store
|
|
169
|
+
`;
|
|
170
|
+
var BIOME_JSON = `{
|
|
171
|
+
"formatter": {
|
|
172
|
+
"enabled": true,
|
|
173
|
+
"indentStyle": "space",
|
|
174
|
+
"indentWidth": 2,
|
|
175
|
+
"lineWidth": 100
|
|
176
|
+
},
|
|
177
|
+
"linter": {
|
|
178
|
+
"enabled": true,
|
|
179
|
+
"rules": {
|
|
180
|
+
"recommended": true
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
var SETUP = `## Project Setup
|
|
186
|
+
|
|
187
|
+
- **Stack:** JavaScript (npm + Biome)
|
|
188
|
+
- **Run:** \`node <file>\` \xB7 **Add deps:** \`npm install <pkg>\`
|
|
189
|
+
- **Lint + format:** \`npx biome check .\` / \`npx biome check --write .\`
|
|
190
|
+
- **Test:** \`npm test\`
|
|
191
|
+
|
|
192
|
+
## Definition of Done
|
|
193
|
+
|
|
194
|
+
- \`npx biome check .\` and \`npm test\` pass
|
|
195
|
+
- No secrets committed \u2014 use \`.env\` (see \`.env.example\`)
|
|
196
|
+
- Change is minimal and verified (see "Verification Before Done")`;
|
|
197
|
+
var SETTINGS_JSON = `{
|
|
198
|
+
"permissions": {
|
|
199
|
+
"allow": [
|
|
200
|
+
"Bash(npm run:*)",
|
|
201
|
+
"Bash(npm test:*)",
|
|
202
|
+
"Bash(npm install:*)",
|
|
203
|
+
"Bash(npm ci:*)",
|
|
204
|
+
"Bash(npx biome:*)",
|
|
205
|
+
"Bash(node:*)",
|
|
206
|
+
"Bash(git status:*)",
|
|
207
|
+
"Bash(git diff:*)",
|
|
208
|
+
"Bash(git log:*)"
|
|
209
|
+
],
|
|
210
|
+
"deny": ["Read(./.env)", "Read(./.env.*)"]
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
`;
|
|
214
|
+
|
|
215
|
+
// src/templates/python.ts
|
|
216
|
+
var GITIGNORE2 = `.venv/
|
|
217
|
+
__pycache__/
|
|
218
|
+
*.pyc
|
|
219
|
+
.env
|
|
220
|
+
dist/
|
|
221
|
+
*.egg-info/
|
|
222
|
+
.ruff_cache/
|
|
223
|
+
.pytest_cache/
|
|
224
|
+
`;
|
|
225
|
+
var RUFF_PYPROJECT = `[tool.ruff]
|
|
226
|
+
line-length = 100
|
|
227
|
+
target-version = "py312"
|
|
228
|
+
|
|
229
|
+
[tool.ruff.lint]
|
|
230
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
231
|
+
|
|
232
|
+
[tool.ruff.format]
|
|
233
|
+
quote-style = "double"
|
|
234
|
+
`;
|
|
235
|
+
var SETUP2 = `## Project Setup
|
|
236
|
+
|
|
237
|
+
- **Stack:** Python (uv + ruff)
|
|
238
|
+
- **Run:** \`uv run <script>\` \xB7 **Add deps:** \`uv add <pkg>\`
|
|
239
|
+
- **Lint + format:** \`uv run ruff check .\` / \`uv run ruff format .\`
|
|
240
|
+
- **Type check:** \`uv run pyrefly .\`
|
|
241
|
+
- **Test:** \`uv run pytest\`
|
|
242
|
+
|
|
243
|
+
## Definition of Done
|
|
244
|
+
|
|
245
|
+
- \`uv run ruff check .\`, \`uv run pyrefly .\` and \`uv run pytest\` pass
|
|
246
|
+
- No secrets committed \u2014 use \`.env\` (see \`.env.example\`)
|
|
247
|
+
- Change is minimal and verified (see "Verification Before Done")`;
|
|
248
|
+
var SETTINGS_JSON2 = `{
|
|
249
|
+
"permissions": {
|
|
250
|
+
"allow": [
|
|
251
|
+
"Bash(uv:*)",
|
|
252
|
+
"Bash(uvx:*)",
|
|
253
|
+
"Bash(ruff:*)",
|
|
254
|
+
"Bash(pyrefly:*)",
|
|
255
|
+
"Bash(pytest:*)",
|
|
256
|
+
"Bash(git status:*)",
|
|
257
|
+
"Bash(git diff:*)",
|
|
258
|
+
"Bash(git log:*)"
|
|
259
|
+
],
|
|
260
|
+
"deny": ["Read(./.env)", "Read(./.env.*)"]
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
`;
|
|
264
|
+
|
|
265
|
+
// src/templates/shared.ts
|
|
266
|
+
var ENV_EXAMPLE = `# Copy to .env and fill in. Never commit .env.
|
|
267
|
+
ANTHROPIC_API_KEY=
|
|
268
|
+
`;
|
|
269
|
+
var TASKS_TODO = `# Tasks
|
|
270
|
+
|
|
271
|
+
## In Progress
|
|
272
|
+
|
|
273
|
+
## To Do
|
|
274
|
+
|
|
275
|
+
- [ ]
|
|
276
|
+
|
|
277
|
+
## Done
|
|
278
|
+
|
|
279
|
+
## Review
|
|
280
|
+
|
|
281
|
+
<!-- After completing work, summarize what changed and why here. -->
|
|
282
|
+
`;
|
|
283
|
+
var TASKS_LESSONS = `# Lessons
|
|
284
|
+
|
|
285
|
+
Patterns learned from corrections. Review at session start; add an entry after
|
|
286
|
+
any course-correction so the same mistake doesn't recur.
|
|
287
|
+
|
|
288
|
+
<!-- Format:
|
|
289
|
+
## <short title>
|
|
290
|
+
- **Pattern:** what went wrong
|
|
291
|
+
- **Trigger:** the situation where it happens
|
|
292
|
+
- **Rule:** what to do instead
|
|
293
|
+
-->
|
|
294
|
+
`;
|
|
295
|
+
|
|
296
|
+
// src/templates/index.ts
|
|
297
|
+
var SYMLINKS = [{ path: "AGENTS.md", target: "CLAUDE.md" }];
|
|
298
|
+
var DOCTRINE_FILE = {
|
|
299
|
+
path: "CLAUDE.md",
|
|
300
|
+
content: DOCTRINE,
|
|
301
|
+
mode: "managed",
|
|
302
|
+
marker: "doctrine"
|
|
303
|
+
};
|
|
304
|
+
var AGENT_FILES = [
|
|
305
|
+
{ path: "tasks/todo.md", content: TASKS_TODO, mode: "skip" },
|
|
306
|
+
{ path: "tasks/lessons.md", content: TASKS_LESSONS, mode: "skip" },
|
|
307
|
+
DOCTRINE_FILE
|
|
308
|
+
];
|
|
309
|
+
var PROJECT_FILES = [
|
|
310
|
+
{ path: ".env.example", content: ENV_EXAMPLE, mode: "skip" }
|
|
311
|
+
];
|
|
312
|
+
var STACK_AGENT_EXTRAS = {
|
|
313
|
+
python: [
|
|
314
|
+
{ path: "CLAUDE.md", content: SETUP2, mode: "managed", marker: "project" },
|
|
315
|
+
{ path: ".claude/settings.json", content: SETTINGS_JSON2, mode: "skip" }
|
|
316
|
+
],
|
|
317
|
+
javascript: [
|
|
318
|
+
{ path: "CLAUDE.md", content: SETUP, mode: "managed", marker: "project" },
|
|
319
|
+
{ path: ".claude/settings.json", content: SETTINGS_JSON, mode: "skip" }
|
|
320
|
+
]
|
|
321
|
+
};
|
|
322
|
+
var STACK = {
|
|
323
|
+
python: [{ path: ".gitignore", content: GITIGNORE2, mode: "skip" }],
|
|
324
|
+
javascript: [
|
|
325
|
+
{ path: ".gitignore", content: GITIGNORE, mode: "skip" },
|
|
326
|
+
{ path: "biome.json", content: BIOME_JSON, mode: "skip" }
|
|
327
|
+
]
|
|
328
|
+
};
|
|
329
|
+
function agentFiles() {
|
|
330
|
+
return [...AGENT_FILES];
|
|
331
|
+
}
|
|
332
|
+
function filesFor(stack) {
|
|
333
|
+
return [...PROJECT_FILES, ...STACK[stack], ...AGENT_FILES, ...STACK_AGENT_EXTRAS[stack]];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/commands/code-fast-init.ts
|
|
337
|
+
var CodeFastInitCommand = class extends Command {
|
|
338
|
+
static paths = [["code-fast", "init"]];
|
|
339
|
+
static usage = Command.Usage({
|
|
340
|
+
category: "code-fast",
|
|
341
|
+
description: "Add agent files (CLAUDE.md, AGENTS.md, tasks/) to the current project.",
|
|
342
|
+
examples: [
|
|
343
|
+
["Add agent files here", "mokout code-fast init"],
|
|
344
|
+
["Preview without writing", "mokout code-fast init --dry-run"]
|
|
345
|
+
]
|
|
346
|
+
});
|
|
347
|
+
dryRun = Option.Boolean("--dry-run", false, {
|
|
348
|
+
description: "Print what would be created without writing anything"
|
|
349
|
+
});
|
|
350
|
+
async execute() {
|
|
351
|
+
const cwd = process.cwd();
|
|
352
|
+
p.intro("mokout code-fast init");
|
|
353
|
+
const files = agentFiles();
|
|
354
|
+
const paths = [...new Set(files.map((f) => f.path))];
|
|
355
|
+
const links = SYMLINKS.map((l) => `${l.path} -> ${l.target}`);
|
|
356
|
+
if (this.dryRun) {
|
|
357
|
+
p.note([...paths, ...links].join("\n"), "Would create");
|
|
358
|
+
p.outro("Dry run \u2014 nothing written.");
|
|
359
|
+
return 0;
|
|
360
|
+
}
|
|
361
|
+
const s = p.spinner();
|
|
362
|
+
s.start("Adding agent files");
|
|
363
|
+
const plop = await nodePlop(void 0, { destBasePath: cwd, force: false });
|
|
364
|
+
registerGenerator(plop, {
|
|
365
|
+
name: "code-fast-init",
|
|
366
|
+
description: "Add agent files to an existing project",
|
|
367
|
+
files,
|
|
368
|
+
symlinks: SYMLINKS
|
|
369
|
+
});
|
|
370
|
+
await plop.getGenerator("code-fast-init").runActions({});
|
|
371
|
+
s.stop("Agent files added");
|
|
372
|
+
const cloneSpinner = p.spinner();
|
|
373
|
+
cloneSpinner.start("Adding agent-skills to .agents/skills");
|
|
374
|
+
try {
|
|
375
|
+
if (!exists(cwd, ".agents/skills")) {
|
|
376
|
+
run(
|
|
377
|
+
"git",
|
|
378
|
+
["clone", "--depth", "1", "https://github.com/addyosmani/agent-skills", ".agents/skills"],
|
|
379
|
+
cwd
|
|
380
|
+
);
|
|
381
|
+
remove(cwd, ".agents/skills/.git");
|
|
382
|
+
}
|
|
383
|
+
cloneSpinner.stop("Added agent-skills to .agents/skills");
|
|
384
|
+
} catch {
|
|
385
|
+
cloneSpinner.stop("Skipped agent-skills (offline?)");
|
|
386
|
+
}
|
|
387
|
+
p.note(
|
|
388
|
+
[...paths.map((f) => `\u2022 ${f}`), ...links.map((l) => `\u2022 ${l}`), "\u2022 .agents/skills/"].join(
|
|
389
|
+
"\n"
|
|
390
|
+
),
|
|
391
|
+
"Added"
|
|
392
|
+
);
|
|
393
|
+
p.outro("Done. CLAUDE.md holds the doctrine; AGENTS.md mirrors it for other agents.");
|
|
394
|
+
return 0;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// src/commands/init.ts
|
|
399
|
+
import * as p2 from "@clack/prompts";
|
|
400
|
+
import { Command as Command2, Option as Option2 } from "clipanion";
|
|
401
|
+
import nodePlop2 from "node-plop";
|
|
402
|
+
var UV_BOILERPLATE = ["hello.py", "main.py", "README.md"];
|
|
403
|
+
var PY_DEV_DEPS = ["pytest", "ruff", "pyrefly"];
|
|
404
|
+
var JS_DEV_DEPS = ["@biomejs/biome"];
|
|
405
|
+
var InitCommand = class extends Command2 {
|
|
406
|
+
static paths = [["project", "init"]];
|
|
407
|
+
static usage = Command2.Usage({
|
|
408
|
+
description: "Scaffold an agentic AI project: uv/npm, CLAUDE.md, and modern tooling.",
|
|
409
|
+
examples: [
|
|
410
|
+
["Interactive (asks for stack)", "mokout project init"],
|
|
411
|
+
["Python project", "mokout project init --python"],
|
|
412
|
+
["JavaScript project", "mokout project init --js"],
|
|
413
|
+
["Preview without writing", "mokout project init --dry-run"]
|
|
414
|
+
]
|
|
415
|
+
});
|
|
416
|
+
js = Option2.Boolean("--js", false, { description: "Scaffold a Node/JS project" });
|
|
417
|
+
python = Option2.Boolean("--python", false, {
|
|
418
|
+
description: "Scaffold a Python project (default)"
|
|
419
|
+
});
|
|
420
|
+
dryRun = Option2.Boolean("--dry-run", false, {
|
|
421
|
+
description: "Print what would be created without writing anything"
|
|
422
|
+
});
|
|
423
|
+
async execute() {
|
|
424
|
+
const cwd = process.cwd();
|
|
425
|
+
p2.intro("mokout project init");
|
|
426
|
+
const stack = await this.resolveStack();
|
|
427
|
+
if (stack === null) {
|
|
428
|
+
p2.cancel("Cancelled.");
|
|
429
|
+
return 1;
|
|
430
|
+
}
|
|
431
|
+
const files = filesFor(stack);
|
|
432
|
+
const paths = [...new Set(files.map((f) => f.path))];
|
|
433
|
+
const links = SYMLINKS.map((l) => `${l.path} -> ${l.target}`);
|
|
434
|
+
const steps = stack === "javascript" ? ["npm init -y", `npm install --save-dev ${JS_DEV_DEPS.join(" ")}`] : ["uv init", `uv add --dev ${PY_DEV_DEPS.join(" ")}`];
|
|
435
|
+
if (this.dryRun) {
|
|
436
|
+
p2.note(["git init", ...steps, ...paths, ...links].join("\n"), "Would create");
|
|
437
|
+
p2.outro("Dry run \u2014 nothing written.");
|
|
438
|
+
return 0;
|
|
439
|
+
}
|
|
440
|
+
if (!exists(cwd, ".git")) run("git", ["init", "-q"], cwd);
|
|
441
|
+
if (stack === "javascript") {
|
|
442
|
+
if (!exists(cwd, "package.json")) {
|
|
443
|
+
if (!hasCommand("npm")) {
|
|
444
|
+
p2.cancel("npm not found on PATH.");
|
|
445
|
+
return 1;
|
|
446
|
+
}
|
|
447
|
+
run("npm", ["init", "-y"], cwd);
|
|
448
|
+
const dep = p2.spinner();
|
|
449
|
+
dep.start(`Adding dev dependencies (${JS_DEV_DEPS.join(", ")})`);
|
|
450
|
+
try {
|
|
451
|
+
run("npm", ["install", "--save-dev", ...JS_DEV_DEPS], cwd);
|
|
452
|
+
dep.stop(`Dev dependencies added (${JS_DEV_DEPS.join(", ")})`);
|
|
453
|
+
} catch {
|
|
454
|
+
dep.stop(
|
|
455
|
+
`Skipped dev deps \u2014 run \`npm install --save-dev ${JS_DEV_DEPS.join(" ")}\` (offline?)`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
} else if (!exists(cwd, "pyproject.toml")) {
|
|
460
|
+
if (!hasCommand("uv")) {
|
|
461
|
+
p2.cancel("uv not found on PATH \u2014 install from https://docs.astral.sh/uv/");
|
|
462
|
+
return 1;
|
|
463
|
+
}
|
|
464
|
+
const preexisting = UV_BOILERPLATE.filter((f) => exists(cwd, f));
|
|
465
|
+
run("uv", ["init"], cwd);
|
|
466
|
+
for (const f of UV_BOILERPLATE) {
|
|
467
|
+
if (!preexisting.includes(f)) remove(cwd, f);
|
|
468
|
+
}
|
|
469
|
+
const dep = p2.spinner();
|
|
470
|
+
dep.start(`Adding dev dependencies (${PY_DEV_DEPS.join(", ")})`);
|
|
471
|
+
try {
|
|
472
|
+
run("uv", ["add", "--dev", ...PY_DEV_DEPS], cwd);
|
|
473
|
+
dep.stop(`Dev dependencies added (${PY_DEV_DEPS.join(", ")})`);
|
|
474
|
+
} catch {
|
|
475
|
+
dep.stop(`Skipped dev deps \u2014 run \`uv add --dev ${PY_DEV_DEPS.join(" ")}\` (offline?)`);
|
|
476
|
+
}
|
|
477
|
+
const pyproject = readText(cwd, "pyproject.toml");
|
|
478
|
+
if (pyproject && !pyproject.includes("[tool.ruff]")) {
|
|
479
|
+
appendText(cwd, "pyproject.toml", `
|
|
480
|
+
${RUFF_PYPROJECT}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const s = p2.spinner();
|
|
484
|
+
s.start("Writing project files");
|
|
485
|
+
const plop = await nodePlop2(void 0, { destBasePath: cwd, force: false });
|
|
486
|
+
registerGenerator(plop, {
|
|
487
|
+
name: "init",
|
|
488
|
+
description: `Scaffold a ${stack} project`,
|
|
489
|
+
files: filesFor(stack),
|
|
490
|
+
symlinks: SYMLINKS
|
|
491
|
+
});
|
|
492
|
+
await plop.getGenerator("init").runActions({});
|
|
493
|
+
s.stop("Files written");
|
|
494
|
+
const cloneSpinner = p2.spinner();
|
|
495
|
+
cloneSpinner.start("Adding agent-skills to .agents/skills");
|
|
496
|
+
try {
|
|
497
|
+
if (!exists(cwd, ".agents/skills")) {
|
|
498
|
+
run(
|
|
499
|
+
"git",
|
|
500
|
+
["clone", "--depth", "1", "https://github.com/addyosmani/agent-skills", ".agents/skills"],
|
|
501
|
+
cwd
|
|
502
|
+
);
|
|
503
|
+
remove(cwd, ".agents/skills/.git");
|
|
504
|
+
}
|
|
505
|
+
cloneSpinner.stop("Added agent-skills to .agents/skills");
|
|
506
|
+
} catch {
|
|
507
|
+
cloneSpinner.stop("Skipped agent-skills (offline?)");
|
|
508
|
+
}
|
|
509
|
+
p2.note(
|
|
510
|
+
[...paths.map((f) => `\u2022 ${f}`), ...links.map((l) => `\u2022 ${l}`), "\u2022 .agents/skills/"].join(
|
|
511
|
+
"\n"
|
|
512
|
+
),
|
|
513
|
+
`Scaffolded (${stack})`
|
|
514
|
+
);
|
|
515
|
+
p2.outro("Done. CLAUDE.md lists the project commands; AGENTS.md mirrors it.");
|
|
516
|
+
return 0;
|
|
517
|
+
}
|
|
518
|
+
/** Resolve the stack from flags, else prompt. Returns null if the user cancels. */
|
|
519
|
+
async resolveStack() {
|
|
520
|
+
if (this.js) return "javascript";
|
|
521
|
+
if (this.python) return "python";
|
|
522
|
+
if (!process.stdin.isTTY) return "python";
|
|
523
|
+
const choice = await p2.select({
|
|
524
|
+
message: "Project stack?",
|
|
525
|
+
initialValue: "python",
|
|
526
|
+
options: [
|
|
527
|
+
{ value: "python", label: "Python", hint: "uv + ruff" },
|
|
528
|
+
{ value: "javascript", label: "JavaScript", hint: "npm + biome" }
|
|
529
|
+
]
|
|
530
|
+
});
|
|
531
|
+
return p2.isCancel(choice) ? null : choice;
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// src/cli.ts
|
|
536
|
+
var cli = new Cli({
|
|
537
|
+
binaryLabel: "mokout",
|
|
538
|
+
binaryName: "mokout",
|
|
539
|
+
binaryVersion: "0.1.0"
|
|
540
|
+
});
|
|
541
|
+
cli.register(InitCommand);
|
|
542
|
+
cli.register(CodeFastInitCommand);
|
|
543
|
+
cli.register(Builtins.HelpCommand);
|
|
544
|
+
cli.register(Builtins.VersionCommand);
|
|
545
|
+
cli.runExit(process.argv.slice(2));
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mokout-cli",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "Quick scaffolding for agentic AI projects — uv/npm, Claude-ready CLAUDE.md, and modern tooling in one command.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"packageManager": "pnpm@8.15.6",
|
|
8
|
+
"author": "Mohan Tupakula <tmskoushik@gmail.com>",
|
|
9
|
+
"bin": {
|
|
10
|
+
"mokout": "dist/cli.js",
|
|
11
|
+
"miracle": "dist/cli.js",
|
|
12
|
+
"akash": "dist/cli.js",
|
|
13
|
+
"sangeeta": "dist/cli.js",
|
|
14
|
+
"poorna": "dist/cli.js",
|
|
15
|
+
"vijay": "dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": ["dist"],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"lint": "biome check .",
|
|
26
|
+
"format": "biome check --write .",
|
|
27
|
+
"prepublishOnly": "pnpm run build"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@clack/prompts": "^0.7.0",
|
|
31
|
+
"clipanion": "4.0.0-rc.4",
|
|
32
|
+
"node-plop": "^0.32.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@biomejs/biome": "^1.9.4",
|
|
36
|
+
"@types/node": "^22.10.0",
|
|
37
|
+
"lefthook": "^1.10.0",
|
|
38
|
+
"tsup": "^8.3.5",
|
|
39
|
+
"typescript": "^5.7.2"
|
|
40
|
+
}
|
|
41
|
+
}
|