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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -0
  3. package/dist/cli.js +545 -0
  4. 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
+ }