kodu 2.2.0 → 3.0.2

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 (234) hide show
  1. package/README.md +24 -3
  2. package/bin/kodu.js +40 -0
  3. package/package.json +12 -67
  4. package/scripts/install.js +68 -0
  5. package/scripts/postinstall.js +22 -0
  6. package/AGENTS.md +0 -214
  7. package/__tests__/core/fs/fs.service.test.ts +0 -72
  8. package/__tests__/core/registry/registry.service.test.ts +0 -82
  9. package/__tests__/shared/cleaner/cleaner.service.test.ts +0 -102
  10. package/__tests__/shared/git/git.service.test.ts +0 -84
  11. package/__tests__/shared/runbook/runbook.service.test.ts +0 -104
  12. package/__tests__/shared/tokenizer/tokenizer.service.test.ts +0 -45
  13. package/biome.json +0 -50
  14. package/dist/package.json +0 -96
  15. package/dist/src/app.module.d.ts +0 -2
  16. package/dist/src/app.module.js +0 -42
  17. package/dist/src/app.module.js.map +0 -1
  18. package/dist/src/commands/clean/clean.command.d.ts +0 -37
  19. package/dist/src/commands/clean/clean.command.js +0 -240
  20. package/dist/src/commands/clean/clean.command.js.map +0 -1
  21. package/dist/src/commands/clean/clean.module.d.ts +0 -2
  22. package/dist/src/commands/clean/clean.module.js +0 -26
  23. package/dist/src/commands/clean/clean.module.js.map +0 -1
  24. package/dist/src/commands/init/init.command.d.ts +0 -10
  25. package/dist/src/commands/init/init.command.js +0 -96
  26. package/dist/src/commands/init/init.command.js.map +0 -1
  27. package/dist/src/commands/init/init.module.d.ts +0 -2
  28. package/dist/src/commands/init/init.module.js +0 -22
  29. package/dist/src/commands/init/init.module.js.map +0 -1
  30. package/dist/src/commands/ops/ops-add.command.d.ts +0 -18
  31. package/dist/src/commands/ops/ops-add.command.js +0 -102
  32. package/dist/src/commands/ops/ops-add.command.js.map +0 -1
  33. package/dist/src/commands/ops/ops-init.command.d.ts +0 -22
  34. package/dist/src/commands/ops/ops-init.command.js +0 -130
  35. package/dist/src/commands/ops/ops-init.command.js.map +0 -1
  36. package/dist/src/commands/ops/ops-list.command.d.ts +0 -12
  37. package/dist/src/commands/ops/ops-list.command.js +0 -73
  38. package/dist/src/commands/ops/ops-list.command.js.map +0 -1
  39. package/dist/src/commands/ops/ops-path.command.d.ts +0 -9
  40. package/dist/src/commands/ops/ops-path.command.js +0 -52
  41. package/dist/src/commands/ops/ops-path.command.js.map +0 -1
  42. package/dist/src/commands/ops/ops-runbook.command.d.ts +0 -12
  43. package/dist/src/commands/ops/ops-runbook.command.js +0 -81
  44. package/dist/src/commands/ops/ops-runbook.command.js.map +0 -1
  45. package/dist/src/commands/ops/ops-status.command.d.ts +0 -11
  46. package/dist/src/commands/ops/ops-status.command.js +0 -62
  47. package/dist/src/commands/ops/ops-status.command.js.map +0 -1
  48. package/dist/src/commands/ops/ops-use.command.d.ts +0 -12
  49. package/dist/src/commands/ops/ops-use.command.js +0 -76
  50. package/dist/src/commands/ops/ops-use.command.js.map +0 -1
  51. package/dist/src/commands/ops/ops.command.d.ts +0 -7
  52. package/dist/src/commands/ops/ops.command.js +0 -56
  53. package/dist/src/commands/ops/ops.command.js.map +0 -1
  54. package/dist/src/commands/ops/ops.helpers.d.ts +0 -2
  55. package/dist/src/commands/ops/ops.helpers.js +0 -11
  56. package/dist/src/commands/ops/ops.helpers.js.map +0 -1
  57. package/dist/src/commands/ops/ops.module.d.ts +0 -2
  58. package/dist/src/commands/ops/ops.module.js +0 -36
  59. package/dist/src/commands/ops/ops.module.js.map +0 -1
  60. package/dist/src/commands/pack/pack.command.d.ts +0 -51
  61. package/dist/src/commands/pack/pack.command.js +0 -355
  62. package/dist/src/commands/pack/pack.command.js.map +0 -1
  63. package/dist/src/commands/pack/pack.module.d.ts +0 -2
  64. package/dist/src/commands/pack/pack.module.js +0 -27
  65. package/dist/src/commands/pack/pack.module.js.map +0 -1
  66. package/dist/src/core/config/config.module.d.ts +0 -2
  67. package/dist/src/core/config/config.module.js +0 -23
  68. package/dist/src/core/config/config.module.js.map +0 -1
  69. package/dist/src/core/config/config.schema.d.ts +0 -19
  70. package/dist/src/core/config/config.schema.js +0 -56
  71. package/dist/src/core/config/config.schema.js.map +0 -1
  72. package/dist/src/core/config/config.service.d.ts +0 -7
  73. package/dist/src/core/config/config.service.js +0 -49
  74. package/dist/src/core/config/config.service.js.map +0 -1
  75. package/dist/src/core/config/prompt.service.d.ts +0 -10
  76. package/dist/src/core/config/prompt.service.js +0 -80
  77. package/dist/src/core/config/prompt.service.js.map +0 -1
  78. package/dist/src/core/file-system/fs.module.d.ts +0 -2
  79. package/dist/src/core/file-system/fs.module.js +0 -21
  80. package/dist/src/core/file-system/fs.module.js.map +0 -1
  81. package/dist/src/core/file-system/fs.service.d.ts +0 -27
  82. package/dist/src/core/file-system/fs.service.js +0 -203
  83. package/dist/src/core/file-system/fs.service.js.map +0 -1
  84. package/dist/src/core/registry/registry.module.d.ts +0 -2
  85. package/dist/src/core/registry/registry.module.js +0 -22
  86. package/dist/src/core/registry/registry.module.js.map +0 -1
  87. package/dist/src/core/registry/registry.schema.d.ts +0 -24
  88. package/dist/src/core/registry/registry.schema.js +0 -21
  89. package/dist/src/core/registry/registry.schema.js.map +0 -1
  90. package/dist/src/core/registry/registry.service.d.ts +0 -16
  91. package/dist/src/core/registry/registry.service.js +0 -91
  92. package/dist/src/core/registry/registry.service.js.map +0 -1
  93. package/dist/src/core/ui/ui.module.d.ts +0 -2
  94. package/dist/src/core/ui/ui.module.js +0 -22
  95. package/dist/src/core/ui/ui.module.js.map +0 -1
  96. package/dist/src/core/ui/ui.service.d.ts +0 -22
  97. package/dist/src/core/ui/ui.service.js +0 -43
  98. package/dist/src/core/ui/ui.service.js.map +0 -1
  99. package/dist/src/main.d.ts +0 -2
  100. package/dist/src/main.js +0 -16
  101. package/dist/src/main.js.map +0 -1
  102. package/dist/src/shared/cleaner/cleaner.service.d.ts +0 -23
  103. package/dist/src/shared/cleaner/cleaner.service.js +0 -223
  104. package/dist/src/shared/cleaner/cleaner.service.js.map +0 -1
  105. package/dist/src/shared/cleaner/cleaner.types.d.ts +0 -21
  106. package/dist/src/shared/cleaner/cleaner.types.js +0 -3
  107. package/dist/src/shared/cleaner/cleaner.types.js.map +0 -1
  108. package/dist/src/shared/constants.d.ts +0 -4
  109. package/dist/src/shared/constants.js +0 -113
  110. package/dist/src/shared/constants.js.map +0 -1
  111. package/dist/src/shared/deps/deps.module.d.ts +0 -2
  112. package/dist/src/shared/deps/deps.module.js +0 -21
  113. package/dist/src/shared/deps/deps.module.js.map +0 -1
  114. package/dist/src/shared/deps/deps.service.d.ts +0 -15
  115. package/dist/src/shared/deps/deps.service.js +0 -114
  116. package/dist/src/shared/deps/deps.service.js.map +0 -1
  117. package/dist/src/shared/git/git.module.d.ts +0 -2
  118. package/dist/src/shared/git/git.module.js +0 -21
  119. package/dist/src/shared/git/git.module.js.map +0 -1
  120. package/dist/src/shared/git/git.service.d.ts +0 -5
  121. package/dist/src/shared/git/git.service.js +0 -56
  122. package/dist/src/shared/git/git.service.js.map +0 -1
  123. package/dist/src/shared/runbook/runbook.module.d.ts +0 -2
  124. package/dist/src/shared/runbook/runbook.module.js +0 -22
  125. package/dist/src/shared/runbook/runbook.module.js.map +0 -1
  126. package/dist/src/shared/runbook/runbook.service.d.ts +0 -20
  127. package/dist/src/shared/runbook/runbook.service.js +0 -118
  128. package/dist/src/shared/runbook/runbook.service.js.map +0 -1
  129. package/dist/src/shared/runbook/runbook.templates.d.ts +0 -6
  130. package/dist/src/shared/runbook/runbook.templates.js +0 -49
  131. package/dist/src/shared/runbook/runbook.templates.js.map +0 -1
  132. package/dist/src/shared/tokenizer/tokenizer.module.d.ts +0 -2
  133. package/dist/src/shared/tokenizer/tokenizer.module.js +0 -21
  134. package/dist/src/shared/tokenizer/tokenizer.module.js.map +0 -1
  135. package/dist/src/shared/tokenizer/tokenizer.service.d.ts +0 -10
  136. package/dist/src/shared/tokenizer/tokenizer.service.js +0 -36
  137. package/dist/src/shared/tokenizer/tokenizer.service.js.map +0 -1
  138. package/dist/tsconfig.build.tsbuildinfo +0 -1
  139. package/docs/todo.md +0 -7
  140. package/knip.json +0 -10
  141. package/kodu.json +0 -63
  142. package/kodu.schema.json +0 -100
  143. package/lefthook.yml +0 -11
  144. package/nest-cli.json +0 -8
  145. package/registry.schema.json +0 -39
  146. package/scripts/generate-json-schema.ts +0 -27
  147. package/skills/ac/SKILL.md +0 -239
  148. package/skills/al/SKILL.md +0 -98
  149. package/skills/audit/SKILL.md +0 -205
  150. package/skills/audit/audit-baseline-template.yml +0 -188
  151. package/skills/audit/runtime-detect.md +0 -64
  152. package/skills/audit/stacks/_generic.md +0 -41
  153. package/skills/audit/stacks/_registry.md +0 -47
  154. package/skills/audit/stacks/go.md +0 -66
  155. package/skills/audit/stacks/java.md +0 -44
  156. package/skills/audit/stacks/node.md +0 -57
  157. package/skills/audit/stacks/python.md +0 -45
  158. package/skills/audit/stacks/rust.md +0 -44
  159. package/skills/audit-api-contracts/SKILL.md +0 -201
  160. package/skills/audit-architecture/SKILL.md +0 -200
  161. package/skills/audit-bugs/SKILL.md +0 -226
  162. package/skills/audit-concurrency/SKILL.md +0 -197
  163. package/skills/audit-deployment/SKILL.md +0 -218
  164. package/skills/audit-docs/SKILL.md +0 -209
  165. package/skills/audit-errors/SKILL.md +0 -216
  166. package/skills/audit-logging/SKILL.md +0 -197
  167. package/skills/audit-matrix/SKILL.md +0 -245
  168. package/skills/audit-meta/SKILL.md +0 -120
  169. package/skills/audit-naming/SKILL.md +0 -200
  170. package/skills/audit-owasp/SKILL.md +0 -223
  171. package/skills/audit-performance/SKILL.md +0 -199
  172. package/skills/audit-reinvention/SKILL.md +0 -214
  173. package/skills/audit-secrets/SKILL.md +0 -198
  174. package/skills/audit-tests/SKILL.md +0 -210
  175. package/skills/audit-validation/SKILL.md +0 -206
  176. package/skills/audit-verify/SKILL.md +0 -139
  177. package/skills/audit-yagni/SKILL.md +0 -188
  178. package/skills/doc-gen/SKILL.md +0 -490
  179. package/skills/doc-gen/scripts/doc_gen.py +0 -911
  180. package/skills/generate-project-docs/SKILL.md +0 -380
  181. package/skills/implement-project/SKILL.md +0 -409
  182. package/skills/liteend-init/SKILL.md +0 -84
  183. package/skills/litefront-init/SKILL.md +0 -96
  184. package/skills/litefront-prototype/SKILL.md +0 -484
  185. package/skills/ops/SKILL.md +0 -94
  186. package/skills/post-call-task-builder/SKILL.md +0 -419
  187. package/skills/project-setup-standardizer/SKILL.md +0 -285
  188. package/skills/skills-best-practices/SKILL.md +0 -415
  189. package/skills/start/SKILL.md +0 -319
  190. package/skills/tech-blueprint/SKILL.md +0 -890
  191. package/skills/tech-blueprint/scripts/blueprint_validator.py +0 -417
  192. package/src/app.module.ts +0 -29
  193. package/src/commands/clean/clean.command.ts +0 -235
  194. package/src/commands/clean/clean.module.ts +0 -13
  195. package/src/commands/init/init.command.ts +0 -92
  196. package/src/commands/init/init.module.ts +0 -9
  197. package/src/commands/ops/ops-add.command.ts +0 -83
  198. package/src/commands/ops/ops-init.command.ts +0 -125
  199. package/src/commands/ops/ops-list.command.ts +0 -57
  200. package/src/commands/ops/ops-path.command.ts +0 -38
  201. package/src/commands/ops/ops-runbook.command.ts +0 -74
  202. package/src/commands/ops/ops-status.command.ts +0 -47
  203. package/src/commands/ops/ops-use.command.ts +0 -76
  204. package/src/commands/ops/ops.command.ts +0 -42
  205. package/src/commands/ops/ops.helpers.ts +0 -20
  206. package/src/commands/ops/ops.module.ts +0 -23
  207. package/src/commands/pack/pack.command.ts +0 -347
  208. package/src/commands/pack/pack.module.ts +0 -14
  209. package/src/core/config/config.module.ts +0 -10
  210. package/src/core/config/config.schema.ts +0 -58
  211. package/src/core/config/config.service.ts +0 -43
  212. package/src/core/config/prompt.service.ts +0 -80
  213. package/src/core/file-system/fs.module.ts +0 -8
  214. package/src/core/file-system/fs.service.ts +0 -248
  215. package/src/core/registry/registry.module.ts +0 -9
  216. package/src/core/registry/registry.schema.ts +0 -46
  217. package/src/core/registry/registry.service.ts +0 -128
  218. package/src/core/ui/ui.module.ts +0 -9
  219. package/src/core/ui/ui.service.ts +0 -39
  220. package/src/main.ts +0 -12
  221. package/src/shared/cleaner/cleaner.service.ts +0 -289
  222. package/src/shared/cleaner/cleaner.types.ts +0 -23
  223. package/src/shared/constants.ts +0 -118
  224. package/src/shared/deps/deps.module.ts +0 -8
  225. package/src/shared/deps/deps.service.ts +0 -175
  226. package/src/shared/git/git.module.ts +0 -8
  227. package/src/shared/git/git.service.ts +0 -47
  228. package/src/shared/runbook/runbook.module.ts +0 -9
  229. package/src/shared/runbook/runbook.service.ts +0 -164
  230. package/src/shared/runbook/runbook.templates.ts +0 -66
  231. package/src/shared/tokenizer/tokenizer.module.ts +0 -8
  232. package/src/shared/tokenizer/tokenizer.service.ts +0 -30
  233. package/tsconfig.build.json +0 -7
  234. 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
- Or run without installing:
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
- npx kodu pack --copy
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` uses `ts-morph` to resolve the TypeScript import graph, so it correctly handles tsconfig path aliases, re-exports, index files, and type-only imports. `node_modules` are excluded automatically.
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,40 @@
1
+ #!/usr/bin/env node
2
+ // Thin launcher: runs the native kodu binary downloaded by postinstall.
3
+ // If the binary is missing (the package manager skipped the postinstall hook —
4
+ // bun does this by default, or `npm i --ignore-scripts` — or the download failed),
5
+ // it is fetched lazily from GitHub Releases on first run.
6
+ 'use strict';
7
+
8
+ const path = require('node:path');
9
+ const fs = require('node:fs');
10
+ const { spawnSync } = require('node:child_process');
11
+ const { install, binNameFor, REPO } = require('../scripts/install');
12
+
13
+ const goos = process.platform === 'win32' ? 'windows' : process.platform;
14
+ const binName = binNameFor(goos);
15
+ const binDir = __dirname;
16
+ const binPath = path.join(binDir, binName);
17
+
18
+ function run() {
19
+ const res = spawnSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
20
+ if (res.error) {
21
+ console.error(`[kodu] ${res.error.message}`);
22
+ process.exit(1);
23
+ }
24
+ process.exit(res.status ?? 0);
25
+ }
26
+
27
+ if (fs.existsSync(binPath)) {
28
+ run();
29
+ } else {
30
+ console.error('[kodu] Native binary not found, downloading it now…');
31
+ install(binDir)
32
+ .then(run)
33
+ .catch((err) => {
34
+ console.error(`[kodu] Failed to download the binary: ${err.message}`);
35
+ console.error('[kodu] Reinstall the package or build it from source:');
36
+ console.error('[kodu] go install github.com/uxname/kodu/cmd/kodu@latest');
37
+ console.error(`[kodu] https://github.com/${REPO}/releases`);
38
+ process.exit(1);
39
+ });
40
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kodu",
3
- "version": "2.2.0",
4
- "description": "High-performance CLI to prepare codebase for LLMs, automate reviews, and draft commits.",
3
+ "version": "3.0.2",
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,26 @@
16
16
  "llm",
17
17
  "developer-tools",
18
18
  "productivity",
19
- "typescript",
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": "dist/src/main.js"
28
+ "kodu": "bin/kodu.js"
30
29
  },
30
+ "files": [
31
+ "bin/kodu.js",
32
+ "scripts/postinstall.js",
33
+ "scripts/install.js"
34
+ ],
31
35
  "scripts": {
32
- "________________ BUILD AND RUN ________________": "",
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"
36
+ "postinstall": "node scripts/postinstall.js"
76
37
  },
77
- "devDependencies": {
78
- "@biomejs/biome": "^2.3.12",
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"
38
+ "engines": {
39
+ "node": ">=18"
95
40
  }
96
41
  }
@@ -0,0 +1,68 @@
1
+ // Shared installer: downloads the native kodu binary for the current platform
2
+ // from GitHub Releases. Used by the postinstall hook AND by the launcher's
3
+ // lazy fallback (so the package works even when the package manager skipped
4
+ // postinstall — e.g. bun by default, or `npm i --ignore-scripts`).
5
+ 'use strict';
6
+
7
+ const fs = require('node:fs');
8
+ const path = require('node:path');
9
+ const https = require('node:https');
10
+ const { execFileSync } = require('node:child_process');
11
+
12
+ const REPO = 'uxname/kodu';
13
+ const pkg = require('../package.json');
14
+
15
+ const PLATFORMS = { linux: 'linux', darwin: 'darwin', win32: 'windows' };
16
+ const ARCHES = { x64: 'amd64', arm64: 'arm64' };
17
+
18
+ function download(u, dest, redirects = 0) {
19
+ return new Promise((resolve, reject) => {
20
+ if (redirects > 5) return reject(new Error('too many redirects'));
21
+ https.get(u, (res) => {
22
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
23
+ res.resume();
24
+ return resolve(download(res.headers.location, dest, redirects + 1));
25
+ }
26
+ if (res.statusCode !== 200) {
27
+ res.resume();
28
+ return reject(new Error(`HTTP ${res.statusCode} for ${u}`));
29
+ }
30
+ const file = fs.createWriteStream(dest);
31
+ res.pipe(file);
32
+ file.on('finish', () => file.close(resolve));
33
+ file.on('error', reject);
34
+ }).on('error', reject);
35
+ });
36
+ }
37
+
38
+ // Resolves the binary path for the running platform, or null if unsupported.
39
+ function binNameFor(goos) {
40
+ return goos === 'windows' ? 'kodu.exe' : 'kodu';
41
+ }
42
+
43
+ // Downloads + extracts the native binary into binDir. Returns the absolute
44
+ // binary path on success; throws on any failure (caller decides how to log).
45
+ async function install(binDir) {
46
+ const goos = PLATFORMS[process.platform];
47
+ const goarch = ARCHES[process.arch];
48
+ if (!goos || !goarch) {
49
+ throw new Error(`Platform ${process.platform}/${process.arch} is not supported by a prebuilt binary.`);
50
+ }
51
+ const ext = goos === 'windows' ? 'zip' : 'tar.gz';
52
+ const binName = binNameFor(goos);
53
+ const asset = `kodu_v${pkg.version}_${goos}_${goarch}.${ext}`;
54
+ const url = `https://github.com/${REPO}/releases/download/v${pkg.version}/${asset}`;
55
+
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
+ return { binPath, goos, goarch, version: pkg.version };
66
+ }
67
+
68
+ module.exports = { install, download, binNameFor, REPO, pkg };
@@ -0,0 +1,22 @@
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
+ // The launcher (bin/kodu.js) will also download lazily on first run if this hook
5
+ // was skipped (bun) or failed, so a missing binary here is never fatal.
6
+ 'use strict';
7
+
8
+ const path = require('node:path');
9
+ const { install, REPO } = require('./install');
10
+
11
+ const binDir = path.join(__dirname, '..', 'bin');
12
+
13
+ install(binDir)
14
+ .then(({ goos, goarch, version }) =>
15
+ console.log(`[kodu] Installed binary ${goos}/${goarch} v${version}`))
16
+ .catch((err) => {
17
+ console.warn(`\n[kodu] Failed to download the binary: ${err.message}`);
18
+ console.warn('[kodu] It will be downloaded automatically on first run, or install manually:');
19
+ console.warn('[kodu] go install github.com/uxname/kodu/cmd/kodu@latest');
20
+ console.warn(`[kodu] https://github.com/${REPO}/releases\n`);
21
+ process.exit(0); // do not block npm install
22
+ });
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,82 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
- import { RegistryService } from '../../../src/core/registry/registry.service';
6
-
7
- describe('RegistryService', () => {
8
- let tmpDir: string;
9
- let originalXdg: string | undefined;
10
- let service: RegistryService;
11
-
12
- beforeEach(async () => {
13
- tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'kodu-registry-'));
14
- originalXdg = process.env.XDG_CONFIG_HOME;
15
- process.env.XDG_CONFIG_HOME = tmpDir;
16
- // Путь реестра читается в конструкторе — создаём после установки env.
17
- service = new RegistryService();
18
- });
19
-
20
- afterEach(async () => {
21
- if (originalXdg === undefined) {
22
- delete process.env.XDG_CONFIG_HOME;
23
- } else {
24
- process.env.XDG_CONFIG_HOME = originalXdg;
25
- }
26
- await fs.rm(tmpDir, { recursive: true, force: true });
27
- });
28
-
29
- it('returns empty registry when file does not exist', async () => {
30
- const registry = await service.load();
31
- expect(registry.projects).toEqual({});
32
- });
33
-
34
- it('does not create the file until something is saved', async () => {
35
- await service.load();
36
- await expect(fs.access(service.getFilePath())).rejects.toBeTruthy();
37
- });
38
-
39
- it('adds a project and persists it to disk', async () => {
40
- await service.add('my-api', { path: '/work/my-api', stands: ['dev'] });
41
-
42
- const onDisk = JSON.parse(await fs.readFile(service.getFilePath(), 'utf8'));
43
- expect(onDisk.projects['my-api']).toEqual({
44
- path: '/work/my-api',
45
- stands: ['dev'],
46
- });
47
- });
48
-
49
- it('rejects a duplicate project name by default', async () => {
50
- await service.add('my-api', { path: '/work/my-api', stands: ['dev'] });
51
-
52
- await expect(
53
- service.add('my-api', { path: '/other', stands: ['dev'] }),
54
- ).rejects.toThrow(/уже есть в реестре/);
55
- });
56
-
57
- it('overwrites when overwrite option is set', async () => {
58
- await service.add('my-api', { path: '/work/my-api', stands: ['dev'] });
59
- await service.add(
60
- 'my-api',
61
- { path: '/new', stands: ['prod'] },
62
- { overwrite: true },
63
- );
64
-
65
- const entry = await service.get('my-api');
66
- expect(entry?.path).toBe('/new');
67
- });
68
-
69
- it('updates an existing project', async () => {
70
- await service.add('my-api', { path: '/work/my-api', stands: ['dev'] });
71
- await service.update('my-api', { path: '/moved' });
72
-
73
- expect((await service.get('my-api'))?.path).toBe('/moved');
74
- });
75
-
76
- it('throws when reading an invalid registry file', async () => {
77
- await fs.mkdir(path.dirname(service.getFilePath()), { recursive: true });
78
- await fs.writeFile(service.getFilePath(), '{ not json', 'utf8');
79
-
80
- await expect(service.load()).rejects.toThrow();
81
- });
82
- });