kodu 2.1.3 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -3
- package/bin/kodu.js +23 -0
- package/package.json +11 -67
- package/scripts/postinstall.js +69 -0
- package/AGENTS.md +0 -214
- package/__tests__/core/fs/fs.service.test.ts +0 -72
- package/__tests__/shared/cleaner/cleaner.service.test.ts +0 -102
- package/__tests__/shared/git/git.service.test.ts +0 -84
- package/__tests__/shared/tokenizer/tokenizer.service.test.ts +0 -45
- package/biome.json +0 -50
- package/dist/package.json +0 -96
- package/dist/src/app.module.d.ts +0 -2
- package/dist/src/app.module.js +0 -36
- package/dist/src/app.module.js.map +0 -1
- package/dist/src/commands/clean/clean.command.d.ts +0 -37
- package/dist/src/commands/clean/clean.command.js +0 -240
- package/dist/src/commands/clean/clean.command.js.map +0 -1
- package/dist/src/commands/clean/clean.module.d.ts +0 -2
- package/dist/src/commands/clean/clean.module.js +0 -26
- package/dist/src/commands/clean/clean.module.js.map +0 -1
- package/dist/src/commands/init/init.command.d.ts +0 -10
- package/dist/src/commands/init/init.command.js +0 -96
- package/dist/src/commands/init/init.command.js.map +0 -1
- package/dist/src/commands/init/init.module.d.ts +0 -2
- package/dist/src/commands/init/init.module.js +0 -22
- package/dist/src/commands/init/init.module.js.map +0 -1
- package/dist/src/commands/pack/pack.command.d.ts +0 -51
- package/dist/src/commands/pack/pack.command.js +0 -355
- package/dist/src/commands/pack/pack.command.js.map +0 -1
- package/dist/src/commands/pack/pack.module.d.ts +0 -2
- package/dist/src/commands/pack/pack.module.js +0 -27
- package/dist/src/commands/pack/pack.module.js.map +0 -1
- package/dist/src/core/config/config.module.d.ts +0 -2
- package/dist/src/core/config/config.module.js +0 -23
- package/dist/src/core/config/config.module.js.map +0 -1
- package/dist/src/core/config/config.schema.d.ts +0 -19
- package/dist/src/core/config/config.schema.js +0 -56
- package/dist/src/core/config/config.schema.js.map +0 -1
- package/dist/src/core/config/config.service.d.ts +0 -7
- package/dist/src/core/config/config.service.js +0 -49
- package/dist/src/core/config/config.service.js.map +0 -1
- package/dist/src/core/config/prompt.service.d.ts +0 -10
- package/dist/src/core/config/prompt.service.js +0 -80
- package/dist/src/core/config/prompt.service.js.map +0 -1
- package/dist/src/core/file-system/fs.module.d.ts +0 -2
- package/dist/src/core/file-system/fs.module.js +0 -21
- package/dist/src/core/file-system/fs.module.js.map +0 -1
- package/dist/src/core/file-system/fs.service.d.ts +0 -27
- package/dist/src/core/file-system/fs.service.js +0 -203
- package/dist/src/core/file-system/fs.service.js.map +0 -1
- package/dist/src/core/ui/ui.module.d.ts +0 -2
- package/dist/src/core/ui/ui.module.js +0 -22
- package/dist/src/core/ui/ui.module.js.map +0 -1
- package/dist/src/core/ui/ui.service.d.ts +0 -22
- package/dist/src/core/ui/ui.service.js +0 -43
- package/dist/src/core/ui/ui.service.js.map +0 -1
- package/dist/src/main.d.ts +0 -2
- package/dist/src/main.js +0 -16
- package/dist/src/main.js.map +0 -1
- package/dist/src/shared/cleaner/cleaner.service.d.ts +0 -23
- package/dist/src/shared/cleaner/cleaner.service.js +0 -223
- package/dist/src/shared/cleaner/cleaner.service.js.map +0 -1
- package/dist/src/shared/cleaner/cleaner.types.d.ts +0 -21
- package/dist/src/shared/cleaner/cleaner.types.js +0 -3
- package/dist/src/shared/cleaner/cleaner.types.js.map +0 -1
- package/dist/src/shared/constants.d.ts +0 -4
- package/dist/src/shared/constants.js +0 -113
- package/dist/src/shared/constants.js.map +0 -1
- package/dist/src/shared/deps/deps.module.d.ts +0 -2
- package/dist/src/shared/deps/deps.module.js +0 -21
- package/dist/src/shared/deps/deps.module.js.map +0 -1
- package/dist/src/shared/deps/deps.service.d.ts +0 -15
- package/dist/src/shared/deps/deps.service.js +0 -114
- package/dist/src/shared/deps/deps.service.js.map +0 -1
- package/dist/src/shared/git/git.module.d.ts +0 -2
- package/dist/src/shared/git/git.module.js +0 -21
- package/dist/src/shared/git/git.module.js.map +0 -1
- package/dist/src/shared/git/git.service.d.ts +0 -5
- package/dist/src/shared/git/git.service.js +0 -56
- package/dist/src/shared/git/git.service.js.map +0 -1
- package/dist/src/shared/tokenizer/tokenizer.module.d.ts +0 -2
- package/dist/src/shared/tokenizer/tokenizer.module.js +0 -21
- package/dist/src/shared/tokenizer/tokenizer.module.js.map +0 -1
- package/dist/src/shared/tokenizer/tokenizer.service.d.ts +0 -10
- package/dist/src/shared/tokenizer/tokenizer.service.js +0 -36
- package/dist/src/shared/tokenizer/tokenizer.service.js.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/docs/todo.md +0 -7
- package/knip.json +0 -10
- package/kodu.json +0 -63
- package/kodu.schema.json +0 -100
- package/lefthook.yml +0 -11
- package/nest-cli.json +0 -8
- package/scripts/generate-json-schema.ts +0 -18
- package/skills/doc-gen/SKILL.md +0 -490
- package/skills/doc-gen/scripts/doc_gen.py +0 -911
- package/skills/implement-project/SKILL.md +0 -409
- package/skills/liteend-init/SKILL.md +0 -84
- package/skills/litefront-init/SKILL.md +0 -96
- package/skills/litefront-prototype/SKILL.md +0 -484
- package/skills/project-setup-standardizer/SKILL.md +0 -285
- package/skills/start/SKILL.md +0 -319
- package/skills/tech-blueprint/SKILL.md +0 -890
- package/skills/tech-blueprint/scripts/blueprint_validator.py +0 -417
- package/src/app.module.ts +0 -23
- package/src/commands/clean/clean.command.ts +0 -235
- package/src/commands/clean/clean.module.ts +0 -13
- package/src/commands/init/init.command.ts +0 -92
- package/src/commands/init/init.module.ts +0 -9
- package/src/commands/pack/pack.command.ts +0 -347
- package/src/commands/pack/pack.module.ts +0 -14
- package/src/core/config/config.module.ts +0 -10
- package/src/core/config/config.schema.ts +0 -58
- package/src/core/config/config.service.ts +0 -43
- package/src/core/config/prompt.service.ts +0 -80
- package/src/core/file-system/fs.module.ts +0 -8
- package/src/core/file-system/fs.service.ts +0 -248
- package/src/core/ui/ui.module.ts +0 -9
- package/src/core/ui/ui.service.ts +0 -39
- package/src/main.ts +0 -12
- package/src/shared/cleaner/cleaner.service.ts +0 -289
- package/src/shared/cleaner/cleaner.types.ts +0 -23
- package/src/shared/constants.ts +0 -118
- package/src/shared/deps/deps.module.ts +0 -8
- package/src/shared/deps/deps.service.ts +0 -175
- package/src/shared/git/git.module.ts +0 -8
- package/src/shared/git/git.service.ts +0 -47
- package/src/shared/tokenizer/tokenizer.module.ts +0 -8
- package/src/shared/tokenizer/tokenizer.service.ts +0 -30
- package/tsconfig.build.json +0 -7
- package/tsconfig.json +0 -28
package/README.md
CHANGED
|
@@ -23,14 +23,32 @@
|
|
|
23
23
|
|
|
24
24
|
## Install
|
|
25
25
|
|
|
26
|
+
Kodu is a single native binary (written in Go). Pick any channel:
|
|
27
|
+
|
|
26
28
|
```bash
|
|
29
|
+
# npm (downloads the prebuilt binary for your platform on install)
|
|
27
30
|
npm install -g kodu
|
|
31
|
+
|
|
32
|
+
# Go toolchain
|
|
33
|
+
go install github.com/uxname/kodu/cmd/kodu@latest
|
|
34
|
+
|
|
35
|
+
# Prebuilt binaries (linux/macOS/windows × amd64/arm64)
|
|
36
|
+
# https://github.com/uxname/kodu/releases
|
|
28
37
|
```
|
|
29
38
|
|
|
30
|
-
|
|
39
|
+
> The npm package ships a thin launcher whose `postinstall` fetches the matching
|
|
40
|
+
> binary from GitHub Releases. If the download fails (offline, proxy), it prints
|
|
41
|
+
> a hint to use `go install` instead — `npm install` itself never breaks.
|
|
42
|
+
|
|
43
|
+
### Build from source
|
|
44
|
+
|
|
45
|
+
Requires Go 1.25+ and a C toolchain (CGO is used for the tree-sitter parsers).
|
|
31
46
|
|
|
32
47
|
```bash
|
|
33
|
-
|
|
48
|
+
git clone https://github.com/uxname/kodu.git && cd kodu
|
|
49
|
+
make build # → dist/kodu
|
|
50
|
+
make test # run the test suite
|
|
51
|
+
make lint # gofmt + go vet + golangci-lint
|
|
34
52
|
```
|
|
35
53
|
|
|
36
54
|
---
|
|
@@ -101,7 +119,10 @@ src/core/config/config.service.ts ← import from src/core/config/config.module
|
|
|
101
119
|
src/shared/constants.ts ← import from src/core/file-system/fs.service.ts
|
|
102
120
|
```
|
|
103
121
|
|
|
104
|
-
`--deps`
|
|
122
|
+
`--deps` resolves the TypeScript/JavaScript import graph (relative imports, file
|
|
123
|
+
extensions, index files, `tsconfig` path aliases, re-exports, and type-only
|
|
124
|
+
imports), excluding `node_modules`. Resolution is best-effort and does not cover
|
|
125
|
+
exotic cases like package.json `exports` maps.
|
|
105
126
|
|
|
106
127
|
### Output format
|
|
107
128
|
|
package/bin/kodu.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin launcher: runs the native kodu binary downloaded by postinstall.
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const fs = require('node:fs');
|
|
7
|
+
const { spawnSync } = require('node:child_process');
|
|
8
|
+
|
|
9
|
+
const binName = process.platform === 'win32' ? 'kodu.exe' : 'kodu';
|
|
10
|
+
const binPath = path.join(__dirname, binName);
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(binPath)) {
|
|
13
|
+
console.error('[kodu] Native binary not found. Reinstall the package or build it from source:');
|
|
14
|
+
console.error('[kodu] go install github.com/uxname/kodu/cmd/kodu@latest');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const res = spawnSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
|
|
19
|
+
if (res.error) {
|
|
20
|
+
console.error(`[kodu] ${res.error.message}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
process.exit(res.status ?? 0);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kodu",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "High-performance CLI to prepare codebase for LLMs, automate reviews, and draft commits.",
|
|
3
|
+
"version": "3.0.1",
|
|
4
|
+
"description": "High-performance CLI to prepare a codebase for LLMs, automate reviews, and draft commits. Native Go binary.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/uxname/kodu.git"
|
|
@@ -16,81 +16,25 @@
|
|
|
16
16
|
"llm",
|
|
17
17
|
"developer-tools",
|
|
18
18
|
"productivity",
|
|
19
|
-
"
|
|
19
|
+
"golang",
|
|
20
20
|
"openai",
|
|
21
21
|
"automation",
|
|
22
22
|
"code-review",
|
|
23
23
|
"context-window",
|
|
24
24
|
"git-tools"
|
|
25
25
|
],
|
|
26
|
-
"private": false,
|
|
27
26
|
"license": "MIT",
|
|
28
27
|
"bin": {
|
|
29
|
-
"kodu": "
|
|
28
|
+
"kodu": "bin/kodu.js"
|
|
30
29
|
},
|
|
30
|
+
"files": [
|
|
31
|
+
"bin/kodu.js",
|
|
32
|
+
"scripts/postinstall.js"
|
|
33
|
+
],
|
|
31
34
|
"scripts": {
|
|
32
|
-
"
|
|
33
|
-
"build": "nest build && chmod +x dist/src/main.js",
|
|
34
|
-
"generate:schema": "ts-node scripts/generate-json-schema.ts",
|
|
35
|
-
"postbuild": "npm run generate:schema",
|
|
36
|
-
"start:prod": "node dist/main.js",
|
|
37
|
-
"start:dev": "nest start --watch",
|
|
38
|
-
"new:command": "nest g -c nest-commander-schematics command",
|
|
39
|
-
"new:question": "nest g -c nest-commander-schematics question",
|
|
40
|
-
"________________ FORMAT AND LINT ________________": "",
|
|
41
|
-
"lint": "biome check",
|
|
42
|
-
"lint:fix": "biome check --write",
|
|
43
|
-
"lint:fix:unsafe": "biome check --write --unsafe",
|
|
44
|
-
"ts:check": "tsc --noEmit",
|
|
45
|
-
"knip": "knip --production",
|
|
46
|
-
"check": "run-p ts:check lint:fix knip",
|
|
47
|
-
"________________ TEST ________________": "",
|
|
48
|
-
"test": "vitest run",
|
|
49
|
-
"test:watch": "vitest",
|
|
50
|
-
"test:cov": "vitest run --coverage",
|
|
51
|
-
"________________ OTHER ________________": "",
|
|
52
|
-
"prepare": "is-ci || lefthook install",
|
|
53
|
-
"update": "npx npm-check-updates -u && rimraf node_modules package-lock.json && npm i",
|
|
54
|
-
"postupdate": "npm run lint:fix && npm run check"
|
|
55
|
-
},
|
|
56
|
-
"dependencies": {
|
|
57
|
-
"@inquirer/confirm": "^6.0.4",
|
|
58
|
-
"@inquirer/input": "^5.0.4",
|
|
59
|
-
"@inquirer/select": "^5.0.4",
|
|
60
|
-
"@nestjs/common": "^11.0.1",
|
|
61
|
-
"@nestjs/core": "^11.0.1",
|
|
62
|
-
"clipboardy": "^5.0.2",
|
|
63
|
-
"execa": "^9.6.1",
|
|
64
|
-
"ignore": "^7.0.5",
|
|
65
|
-
"js-tiktoken": "^1.0.21",
|
|
66
|
-
"lilconfig": "^3.1.3",
|
|
67
|
-
"nest-commander": "^3.20.1",
|
|
68
|
-
"picocolors": "^1.1.1",
|
|
69
|
-
"reflect-metadata": "^0.2.2",
|
|
70
|
-
"rxjs": "^7.8.1",
|
|
71
|
-
"source-map-support": "^0.5.21",
|
|
72
|
-
"tinyglobby": "^0.2.15",
|
|
73
|
-
"ts-morph": "^24.0.0",
|
|
74
|
-
"yocto-spinner": "^1.0.0",
|
|
75
|
-
"zod": "^4.3.6"
|
|
35
|
+
"postinstall": "node scripts/postinstall.js"
|
|
76
36
|
},
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"@nestjs/cli": "^11.0.0",
|
|
80
|
-
"@nestjs/schematics": "^11.0.0",
|
|
81
|
-
"@nestjs/testing": "^11.0.1",
|
|
82
|
-
"@types/node": "^22.10.7",
|
|
83
|
-
"is-ci": "^4.1.0",
|
|
84
|
-
"knip": "^5.82.1",
|
|
85
|
-
"lefthook": "^2.0.15",
|
|
86
|
-
"nest-commander-schematics": "^3.2.0",
|
|
87
|
-
"npm-check-updates": "^18.3.1",
|
|
88
|
-
"npm-run-all": "^4.1.5",
|
|
89
|
-
"rimraf": "^6.1.3",
|
|
90
|
-
"ts-loader": "^9.5.2",
|
|
91
|
-
"ts-node": "^10.9.2",
|
|
92
|
-
"tsconfig-paths": "^4.2.0",
|
|
93
|
-
"typescript": "^5.7.3",
|
|
94
|
-
"vitest": "^3.2.4"
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
95
39
|
}
|
|
96
40
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Downloads the native kodu binary for the current platform from GitHub Releases.
|
|
3
|
+
// On failure it does not break the install — it prints a hint (graceful degradation).
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const fs = require('node:fs');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const https = require('node:https');
|
|
9
|
+
const { execFileSync } = require('node:child_process');
|
|
10
|
+
|
|
11
|
+
const REPO = 'uxname/kodu';
|
|
12
|
+
const pkg = require('../package.json');
|
|
13
|
+
|
|
14
|
+
const PLATFORMS = { linux: 'linux', darwin: 'darwin', win32: 'windows' };
|
|
15
|
+
const ARCHES = { x64: 'amd64', arm64: 'arm64' };
|
|
16
|
+
|
|
17
|
+
function fail(msg) {
|
|
18
|
+
console.warn(`\n[kodu] ${msg}`);
|
|
19
|
+
console.warn('[kodu] Install manually: go install github.com/uxname/kodu/cmd/kodu@latest');
|
|
20
|
+
console.warn(`[kodu] or download the binary: https://github.com/${REPO}/releases\n`);
|
|
21
|
+
process.exit(0); // do not block npm install
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const goos = PLATFORMS[process.platform];
|
|
25
|
+
const goarch = ARCHES[process.arch];
|
|
26
|
+
if (!goos || !goarch) fail(`Platform ${process.platform}/${process.arch} is not supported by a prebuilt binary.`);
|
|
27
|
+
|
|
28
|
+
const ext = goos === 'windows' ? 'zip' : 'tar.gz';
|
|
29
|
+
const binName = goos === 'windows' ? 'kodu.exe' : 'kodu';
|
|
30
|
+
const asset = `kodu_v${pkg.version}_${goos}_${goarch}.${ext}`;
|
|
31
|
+
const url = `https://github.com/${REPO}/releases/download/v${pkg.version}/${asset}`;
|
|
32
|
+
const binDir = path.join(__dirname, '..', 'bin');
|
|
33
|
+
|
|
34
|
+
function download(u, dest, redirects = 0) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
if (redirects > 5) return reject(new Error('too many redirects'));
|
|
37
|
+
https.get(u, (res) => {
|
|
38
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
39
|
+
res.resume();
|
|
40
|
+
return resolve(download(res.headers.location, dest, redirects + 1));
|
|
41
|
+
}
|
|
42
|
+
if (res.statusCode !== 200) {
|
|
43
|
+
res.resume();
|
|
44
|
+
return reject(new Error(`HTTP ${res.statusCode} for ${u}`));
|
|
45
|
+
}
|
|
46
|
+
const file = fs.createWriteStream(dest);
|
|
47
|
+
res.pipe(file);
|
|
48
|
+
file.on('finish', () => file.close(resolve));
|
|
49
|
+
file.on('error', reject);
|
|
50
|
+
}).on('error', reject);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
(async () => {
|
|
55
|
+
try {
|
|
56
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
57
|
+
const archivePath = path.join(binDir, asset);
|
|
58
|
+
await download(url, archivePath);
|
|
59
|
+
// bsdtar (tar) extracts both .tar.gz and .zip on Linux/macOS/Win10+.
|
|
60
|
+
execFileSync('tar', ['-xf', archivePath, '-C', binDir], { stdio: 'ignore' });
|
|
61
|
+
fs.rmSync(archivePath, { force: true });
|
|
62
|
+
const binPath = path.join(binDir, binName);
|
|
63
|
+
if (!fs.existsSync(binPath)) throw new Error('binary not found after extraction');
|
|
64
|
+
if (goos !== 'windows') fs.chmodSync(binPath, 0o755);
|
|
65
|
+
console.log(`[kodu] Installed binary ${goos}/${goarch} v${pkg.version}`);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
fail(`Failed to download the binary: ${err.message}`);
|
|
68
|
+
}
|
|
69
|
+
})();
|
package/AGENTS.md
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
This file provides guidelines and instructions for AI assistants working on the Kodu project.
|
|
4
|
-
|
|
5
|
-
## 1. Project Overview
|
|
6
|
-
|
|
7
|
-
**Kodu** is a high-performance CLI utility that bridges local development environments with LLMs. It automates context preparation and code cleaning.
|
|
8
|
-
|
|
9
|
-
- **Key Goals:** Speed (<0.5s startup), Determinism (no AI for critical file ops), DX (Developer Experience)
|
|
10
|
-
- **Available Commands:** `pack`, `clean`
|
|
11
|
-
|
|
12
|
-
## 2. Technology Stack (Enforced)
|
|
13
|
-
|
|
14
|
-
| Category | USE | DO NOT USE |
|
|
15
|
-
|----------|-----|------------|
|
|
16
|
-
| Framework | NestJS + nest-commander | Pure Node.js, Oclif |
|
|
17
|
-
| File System | node:fs/promises + tinyglobby | fs-extra, glob, rimraf |
|
|
18
|
-
| Config | lilconfig | cosmiconfig, rc |
|
|
19
|
-
| Validation | zod | class-validator, joi |
|
|
20
|
-
| CLI UI | @inquirer/prompts + picocolors | inquirer (legacy), chalk |
|
|
21
|
-
| Spinners | yocto-spinner | ora, cli-spinners |
|
|
22
|
-
| AST/Parsing | ts-morph | Regex, babel |
|
|
23
|
-
| Tokens | js-tiktoken | gpt-3-encoder |
|
|
24
|
-
| Clipboard | clipboardy | Native APIs |
|
|
25
|
-
|
|
26
|
-
## 3. Architecture
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
src/
|
|
30
|
-
├── app.module.ts # Root Orchestrator
|
|
31
|
-
├── main.ts # Entry Point
|
|
32
|
-
├── core/ # Global Infrastructure
|
|
33
|
-
│ ├── config/ # ConfigModule (Zod + lilconfig)
|
|
34
|
-
│ ├── file-system/ # FsModule (tinyglobby)
|
|
35
|
-
│ └── ui/ # UiModule (spinners, loggers)
|
|
36
|
-
├── shared/ # Shared Business Logic
|
|
37
|
-
│ ├── tokenizer/ # TokenizerModule
|
|
38
|
-
│ ├── git/ # GitModule
|
|
39
|
-
│ └── cleaner/ # CleanerService (AST)
|
|
40
|
-
└── commands/ # Feature Commands
|
|
41
|
-
├── pack/ # kodu pack
|
|
42
|
-
└── clean/ # kodu clean
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## 4. Build, Lint & Test Commands
|
|
46
|
-
|
|
47
|
-
### Essential Commands
|
|
48
|
-
```bash
|
|
49
|
-
# Build the project
|
|
50
|
-
npm run build # Full build (Nest build) + make executable
|
|
51
|
-
|
|
52
|
-
# Run the built artifact
|
|
53
|
-
npm run start:prod # Run from dist/
|
|
54
|
-
|
|
55
|
-
# Type check
|
|
56
|
-
npm run ts:check # TypeScript compilation check
|
|
57
|
-
|
|
58
|
-
# Lint and format
|
|
59
|
-
npm run lint # Run Biome linter
|
|
60
|
-
npm run lint:fix # Biome with auto-fix
|
|
61
|
-
|
|
62
|
-
# Full check (required before commit)
|
|
63
|
-
npm run check # TypeCheck + Biome + Knip
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## 5. Code Style Guidelines
|
|
67
|
-
|
|
68
|
-
### General
|
|
69
|
-
- **ESM Only:** Use `import` statements (nodenext mode)
|
|
70
|
-
- **Strict Mode:** `strictNullChecks` is ON. Avoid `any`; use `unknown` with narrowing
|
|
71
|
-
- **Quotes:** Single quotes preferred
|
|
72
|
-
- **Indentation:** 2 spaces
|
|
73
|
-
- **No Comments:** Unless explicitly requested by user
|
|
74
|
-
|
|
75
|
-
### Imports
|
|
76
|
-
- Use explicit relative imports: `import { Foo } from './foo'`
|
|
77
|
-
- Avoid barrel exports (`index.ts`) unless necessary
|
|
78
|
-
- No circular dependencies (NestJS module structure enforces this)
|
|
79
|
-
|
|
80
|
-
### Types
|
|
81
|
-
- Prefer explicit types over `any`
|
|
82
|
-
- Use `unknown` and narrow with type guards or Zod validation
|
|
83
|
-
- Interface over type for object shapes
|
|
84
|
-
- Use readonly for immutable data
|
|
85
|
-
|
|
86
|
-
### Naming Conventions
|
|
87
|
-
- **Files:** kebab-case (`my-file.ts`)
|
|
88
|
-
- **Classes:** PascalCase (`MyClass`)
|
|
89
|
-
- **Functions:** camelCase (`myFunction`)
|
|
90
|
-
- **Constants:** UPPER_SNAKE_CASE for compile-time constants
|
|
91
|
-
- **Interfaces:** PascalCase, no `I` prefix (`User` not `IUser`)
|
|
92
|
-
|
|
93
|
-
### Error Handling
|
|
94
|
-
- Use custom error classes extending `Error`
|
|
95
|
-
- Never swallow errors silently
|
|
96
|
-
- Provide meaningful error messages
|
|
97
|
-
- Use try/catch with specific error types
|
|
98
|
-
- Validate inputs with Zod schemas
|
|
99
|
-
|
|
100
|
-
### NestJS Specifics
|
|
101
|
-
- All commands extend `CommandRunner` from `nest-commander`
|
|
102
|
-
- Use Dependency Injection - never import services directly
|
|
103
|
-
- Register modules in `app.module.ts`
|
|
104
|
-
- Use `@Injectable()` decorator for services
|
|
105
|
-
|
|
106
|
-
## 6. Configuration (kodu.json)
|
|
107
|
-
|
|
108
|
-
```json
|
|
109
|
-
{
|
|
110
|
-
"cleaner": {
|
|
111
|
-
"whitelist": ["//!"],
|
|
112
|
-
"keepJSDoc": true,
|
|
113
|
-
"useGitignore": true
|
|
114
|
-
},
|
|
115
|
-
"packer": {
|
|
116
|
-
"ignore": ["*.lock", "node_modules", "dist"],
|
|
117
|
-
"useGitignore": true
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
- Config validated via Zod on startup
|
|
123
|
-
- `kodu.json` must be in current working directory
|
|
124
|
-
|
|
125
|
-
## 7. Commands Reference
|
|
126
|
-
|
|
127
|
-
### `kodu init`
|
|
128
|
-
|
|
129
|
-
Add `.kodu/context.txt` to `.gitignore` (if `.gitignore` exists). Run once after cloning or setting up a project.
|
|
130
|
-
|
|
131
|
-
### `kodu pack`
|
|
132
|
-
|
|
133
|
-
Bundle project files into a single context file for LLMs.
|
|
134
|
-
|
|
135
|
-
| Option | Description |
|
|
136
|
-
|--------|-------------|
|
|
137
|
-
| `-c, --copy` | Copy result to clipboard |
|
|
138
|
-
| `-o, --out <path>` | Path to save result (default: `.kodu/context.txt`) |
|
|
139
|
-
| `-p, --path <path>` | Directory or glob to include (repeatable) |
|
|
140
|
-
| `-e, --exclude <pattern>` | Additional exclude pattern (repeatable) |
|
|
141
|
-
| `-l, --list` | Print file list only, without content |
|
|
142
|
-
| `-f, --format <format>` | Output format: `xml` (default) or `text` |
|
|
143
|
-
| `-t, --template <name>` | Template name from `.kodu/prompts` |
|
|
144
|
-
|
|
145
|
-
Output format `xml` wraps each file in `<file path="...">` tags — recommended for LLM consumption. Format `text` uses `// file: ...` header comments.
|
|
146
|
-
|
|
147
|
-
### `kodu clean`
|
|
148
|
-
|
|
149
|
-
Remove comments from source files using AST-based parsing (deterministic, no AI).
|
|
150
|
-
|
|
151
|
-
| Option | Description |
|
|
152
|
-
|--------|-------------|
|
|
153
|
-
| `-d, --dry-run` | Show what will be removed without modifying files |
|
|
154
|
-
| `-c, --changed` | Clean only git-changed files |
|
|
155
|
-
|
|
156
|
-
## 8. Critical Constraints
|
|
157
|
-
|
|
158
|
-
1. **No AI:** Both commands are deterministic — no AI integration
|
|
159
|
-
2. **Validation First:** Invalid `kodu.json` causes graceful crash with Zod error
|
|
160
|
-
3. **Performance:** Mindful of import costs. Use lightweight libraries
|
|
161
|
-
4. **Config Location:** `kodu.json` must be in current working directory
|
|
162
|
-
|
|
163
|
-
## 9. Development Workflow
|
|
164
|
-
|
|
165
|
-
### Adding a New Command
|
|
166
|
-
1. Create `src/commands/<name>/`
|
|
167
|
-
2. Create `<name>.command.ts` and `<name>.module.ts`
|
|
168
|
-
3. Implement `run()` extending `CommandRunner`
|
|
169
|
-
4. Decorate with `@Command()` from `nest-commander`
|
|
170
|
-
5. Register module in `app.module.ts`
|
|
171
|
-
6. Test: `npm run build && node dist/src/main.js <name>`
|
|
172
|
-
|
|
173
|
-
### Before Commit
|
|
174
|
-
Always run:
|
|
175
|
-
```bash
|
|
176
|
-
npm run check
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
This executes: TypeScript check + Biome lint + Knip dead code detection.
|
|
180
|
-
|
|
181
|
-
## 10. Testing Strategy
|
|
182
|
-
|
|
183
|
-
- **Primary Gate:** Static analysis (TypeScript + Biome + Knip)
|
|
184
|
-
- **No Legacy Tests:** Project relies on strict static typing
|
|
185
|
-
- If tests exist: place in `__tests__/` or `*.test.ts` files
|
|
186
|
-
|
|
187
|
-
## 11. Release Process
|
|
188
|
-
|
|
189
|
-
### Prerequisites
|
|
190
|
-
- Working directory must be clean (`git status` — no dirty files)
|
|
191
|
-
- All changes committed and pushed
|
|
192
|
-
- You have npm publish access
|
|
193
|
-
|
|
194
|
-
### Steps
|
|
195
|
-
```bash
|
|
196
|
-
# 1. Ensure clean working directory
|
|
197
|
-
git status
|
|
198
|
-
|
|
199
|
-
# 2. Bump version, build, publish
|
|
200
|
-
npm version patch && npm run build && npm publish --access public
|
|
201
|
-
|
|
202
|
-
# 3. Push the version bump commit and tag
|
|
203
|
-
git push && git push --tags
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
- Use `npm version minor` for new features, `npm version major` for breaking changes
|
|
207
|
-
- The `npm version` command creates a git tag automatically (e.g. `v2.1.3`)
|
|
208
|
-
|
|
209
|
-
## 12. Handling Uncertainties
|
|
210
|
-
|
|
211
|
-
- Unclear requirements? Ask the user first
|
|
212
|
-
- Library not in Tech Stack section? Prefer native Node.js APIs
|
|
213
|
-
- New dependency? Ensure it follows "Fresh & Modern" strategy
|
|
214
|
-
- Breaking changes? Create an OpenSpec proposal
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { ConfigService } from '../../../src/core/config/config.service';
|
|
3
|
-
import { FsService } from '../../../src/core/file-system/fs.service';
|
|
4
|
-
import { UiService } from '../../../src/core/ui/ui.service';
|
|
5
|
-
|
|
6
|
-
vi.mock('../../../src/core/config/config.service', () => ({
|
|
7
|
-
ConfigService: vi.fn().mockImplementation(() => ({
|
|
8
|
-
getConfig: () => ({
|
|
9
|
-
cleaner: { whitelist: [], keepJSDoc: false },
|
|
10
|
-
packer: { ignore: ['node_modules', 'dist'], useGitignore: false },
|
|
11
|
-
}),
|
|
12
|
-
})),
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
vi.mock('../../../src/core/ui/ui.service', () => ({
|
|
16
|
-
UiService: vi.fn().mockImplementation(() => ({
|
|
17
|
-
log: { warn: vi.fn() },
|
|
18
|
-
})),
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
describe('FsService', () => {
|
|
22
|
-
let fsService: FsService;
|
|
23
|
-
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
vi.clearAllMocks();
|
|
26
|
-
const configService = new ConfigService() as never;
|
|
27
|
-
const uiService = new UiService() as never;
|
|
28
|
-
fsService = new FsService(configService, uiService);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe('findProjectFiles', () => {
|
|
32
|
-
it('should find ts files in current directory', async () => {
|
|
33
|
-
const files = await fsService.findProjectFiles({
|
|
34
|
-
ignore: ['node_modules', 'dist'],
|
|
35
|
-
useGitignore: false,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const tsFiles = files.filter((f) => f.endsWith('.ts'));
|
|
39
|
-
expect(tsFiles.length).toBeGreaterThan(0);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should exclude node_modules by default', async () => {
|
|
43
|
-
const files = await fsService.findProjectFiles({
|
|
44
|
-
ignore: ['node_modules', 'dist'],
|
|
45
|
-
useGitignore: false,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const hasNodeModules = files.some((f) => f.includes('node_modules'));
|
|
49
|
-
expect(hasNodeModules).toBe(false);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should return relative paths', async () => {
|
|
53
|
-
const files = await fsService.findProjectFiles({
|
|
54
|
-
ignore: [],
|
|
55
|
-
useGitignore: false,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const hasAbsolute = files.some((f) => f.startsWith('/'));
|
|
59
|
-
expect(hasAbsolute).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should return sorted paths', async () => {
|
|
63
|
-
const files = await fsService.findProjectFiles({
|
|
64
|
-
ignore: [],
|
|
65
|
-
useGitignore: false,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const sorted = [...files].sort((a, b) => a.localeCompare(b));
|
|
69
|
-
expect(files).toEqual(sorted);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { ConfigService } from '../../../src/core/config/config.service';
|
|
3
|
-
|
|
4
|
-
vi.mock('../../../src/core/config/config.service', () => ({
|
|
5
|
-
ConfigService: vi.fn().mockImplementation(() => ({
|
|
6
|
-
getConfig: () => ({
|
|
7
|
-
cleaner: {
|
|
8
|
-
whitelist: [],
|
|
9
|
-
keepJSDoc: false,
|
|
10
|
-
useGitignore: false,
|
|
11
|
-
},
|
|
12
|
-
packer: {
|
|
13
|
-
ignore: [],
|
|
14
|
-
useGitignore: false,
|
|
15
|
-
},
|
|
16
|
-
}),
|
|
17
|
-
})),
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
vi.mock('../../../src/core/file-system/fs.service', () => ({
|
|
21
|
-
FsService: vi.fn().mockImplementation(() => ({})),
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
describe('CleanerService', () => {
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
vi.clearAllMocks();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should remove single-line comments', async () => {
|
|
30
|
-
const { CleanerService } = await import(
|
|
31
|
-
'../../../src/shared/cleaner/cleaner.service'
|
|
32
|
-
);
|
|
33
|
-
const configService = new ConfigService() as never;
|
|
34
|
-
const fsService = {} as never;
|
|
35
|
-
const cleaner = new CleanerService(configService, fsService);
|
|
36
|
-
|
|
37
|
-
const input = `const x = 1; // comment
|
|
38
|
-
const y = 2;`;
|
|
39
|
-
const result = cleaner.cleanContent('test.ts', input);
|
|
40
|
-
|
|
41
|
-
expect(result).toBe('const x = 1; \nconst y = 2;');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should remove multi-line comments', async () => {
|
|
45
|
-
const { CleanerService } = await import(
|
|
46
|
-
'../../../src/shared/cleaner/cleaner.service'
|
|
47
|
-
);
|
|
48
|
-
const configService = new ConfigService() as never;
|
|
49
|
-
const fsService = {} as never;
|
|
50
|
-
const cleaner = new CleanerService(configService, fsService);
|
|
51
|
-
|
|
52
|
-
const input = `/* comment */
|
|
53
|
-
const x = 1;`;
|
|
54
|
-
const result = cleaner.cleanContent('test.ts', input);
|
|
55
|
-
|
|
56
|
-
expect(result).toBe('\nconst x = 1;');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should preserve comments in whitelist', async () => {
|
|
60
|
-
const { CleanerService } = await import(
|
|
61
|
-
'../../../src/shared/cleaner/cleaner.service'
|
|
62
|
-
);
|
|
63
|
-
const configService = new ConfigService() as never;
|
|
64
|
-
const fsService = {} as never;
|
|
65
|
-
const cleaner = new CleanerService(configService, fsService);
|
|
66
|
-
|
|
67
|
-
const input = `const x = 1; // eslint-disable-line
|
|
68
|
-
const y = 2;`;
|
|
69
|
-
const result = cleaner.cleanContent('test.ts', input);
|
|
70
|
-
|
|
71
|
-
expect(result).toBe('const x = 1; // eslint-disable-line\nconst y = 2;');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should keep JSDoc when option is set', async () => {
|
|
75
|
-
const { CleanerService } = await import(
|
|
76
|
-
'../../../src/shared/cleaner/cleaner.service'
|
|
77
|
-
);
|
|
78
|
-
const configService = new ConfigService() as never;
|
|
79
|
-
const fsService = {} as never;
|
|
80
|
-
const cleaner = new CleanerService(configService, fsService);
|
|
81
|
-
|
|
82
|
-
const input = `/** JSDoc */
|
|
83
|
-
const x = 1;`;
|
|
84
|
-
const result = cleaner.cleanContent('test.ts', input, true);
|
|
85
|
-
|
|
86
|
-
expect(result).toBe('/** JSDoc */\nconst x = 1;');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should remove JSX expression without content', async () => {
|
|
90
|
-
const { CleanerService } = await import(
|
|
91
|
-
'../../../src/shared/cleaner/cleaner.service'
|
|
92
|
-
);
|
|
93
|
-
const configService = new ConfigService() as never;
|
|
94
|
-
const fsService = {} as never;
|
|
95
|
-
const cleaner = new CleanerService(configService, fsService);
|
|
96
|
-
|
|
97
|
-
const input = `const x = <>{/* comment */}</>;`;
|
|
98
|
-
const result = cleaner.cleanContent('test.tsx', input);
|
|
99
|
-
|
|
100
|
-
expect(result).toBe('const x = <></>;');
|
|
101
|
-
});
|
|
102
|
-
});
|