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
@@ -1,23 +0,0 @@
1
- import { Module } from '@nestjs/common';
2
- import { OpsCommand } from './ops.command';
3
- import { OpsAddCommand } from './ops-add.command';
4
- import { OpsInitCommand } from './ops-init.command';
5
- import { OpsListCommand } from './ops-list.command';
6
- import { OpsPathCommand } from './ops-path.command';
7
- import { OpsRunbookCommand } from './ops-runbook.command';
8
- import { OpsStatusCommand } from './ops-status.command';
9
- import { OpsUseCommand } from './ops-use.command';
10
-
11
- @Module({
12
- providers: [
13
- OpsCommand,
14
- OpsInitCommand,
15
- OpsListCommand,
16
- OpsAddCommand,
17
- OpsStatusCommand,
18
- OpsUseCommand,
19
- OpsPathCommand,
20
- OpsRunbookCommand,
21
- ],
22
- })
23
- export class OpsModule {}
@@ -1,347 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import path from 'node:path';
3
- import clipboard from 'clipboardy';
4
- import { Command, CommandRunner, Option } from 'nest-commander';
5
- import { ConfigService } from '../../core/config/config.service';
6
- import { PromptService } from '../../core/config/prompt.service';
7
- import { FsService } from '../../core/file-system/fs.service';
8
- import { UiService } from '../../core/ui/ui.service';
9
- import { CleanerService } from '../../shared/cleaner/cleaner.service';
10
- import { DepsService } from '../../shared/deps/deps.service';
11
- import { TokenizerService } from '../../shared/tokenizer/tokenizer.service';
12
-
13
- type OutputFormat = 'xml' | 'text';
14
-
15
- type PackOptions = {
16
- copy?: boolean;
17
- template?: string;
18
- out?: string;
19
- path?: string[];
20
- exclude?: string[];
21
- list?: boolean;
22
- format?: OutputFormat;
23
- clean?: boolean;
24
- deps?: boolean;
25
- depsDepth?: number;
26
- explain?: boolean;
27
- };
28
-
29
- type TemplateContext = {
30
- context: string;
31
- fileList: string;
32
- tokenCount: number;
33
- usdEstimate: number;
34
- };
35
-
36
- @Command({
37
- name: 'pack',
38
- description: 'Collect project context into a single file',
39
- })
40
- export class PackCommand extends CommandRunner {
41
- constructor(
42
- private readonly ui: UiService,
43
- private readonly configService: ConfigService,
44
- private readonly promptService: PromptService,
45
- private readonly fsService: FsService,
46
- private readonly tokenizer: TokenizerService,
47
- private readonly cleaner: CleanerService,
48
- private readonly depsService: DepsService,
49
- ) {
50
- super();
51
- }
52
-
53
- @Option({ flags: '-c, --copy', description: 'Copy result to clipboard' })
54
- parseCopy(): boolean {
55
- return true;
56
- }
57
-
58
- @Option({
59
- flags: '-t, --template <name>',
60
- description: 'Template name from .kodu/prompts',
61
- })
62
- parseTemplate(value: string): string {
63
- return value;
64
- }
65
-
66
- @Option({
67
- flags: '-o, --out <path>',
68
- description: 'Path to save result',
69
- })
70
- parseOut(value: string): string {
71
- return value;
72
- }
73
-
74
- @Option({
75
- flags: '-p, --path <path>',
76
- description: 'Directory or glob to include (repeatable)',
77
- })
78
- parsePath(value: string, previous: string[] = []): string[] {
79
- return [...previous, value];
80
- }
81
-
82
- @Option({
83
- flags: '-e, --exclude <pattern>',
84
- description: 'Additional exclude pattern (repeatable)',
85
- })
86
- parseExclude(value: string, previous: string[] = []): string[] {
87
- return [...previous, value];
88
- }
89
-
90
- @Option({
91
- flags: '-l, --list',
92
- description: 'Print file list only, without content',
93
- })
94
- parseList(): boolean {
95
- return true;
96
- }
97
-
98
- @Option({
99
- flags: '--clean',
100
- description: 'Strip comments in-memory before packing (files not modified)',
101
- })
102
- parseClean(): boolean {
103
- return true;
104
- }
105
-
106
- @Option({
107
- flags: '--deps',
108
- description:
109
- 'Trace imports from entry point(s) and include their dependencies',
110
- })
111
- parseDeps(): boolean {
112
- return true;
113
- }
114
-
115
- @Option({
116
- flags: '--deps-depth <n>',
117
- description: 'Max import depth when using --deps (default: unlimited)',
118
- })
119
- parseDepsDepth(value: string): number {
120
- const n = Number.parseInt(value, 10);
121
- if (Number.isNaN(n) || n < 1) {
122
- this.ui.log.warn(`Invalid --deps-depth "${value}", ignoring`);
123
- return Infinity;
124
- }
125
- return n;
126
- }
127
-
128
- @Option({
129
- flags: '--explain',
130
- description: 'Print why each file was included (requires --deps)',
131
- })
132
- parseExplain(): boolean {
133
- return true;
134
- }
135
-
136
- @Option({
137
- flags: '-f, --format <format>',
138
- description: 'Output format: xml (default) or text',
139
- })
140
- parseFormat(value: string): OutputFormat {
141
- if (value !== 'xml' && value !== 'text') {
142
- this.ui.log.warn(`Unknown format "${value}", using "xml"`);
143
- return 'xml';
144
- }
145
- return value;
146
- }
147
-
148
- async run(inputs: string[], options: PackOptions): Promise<void> {
149
- const spinner = this.ui
150
- .createSpinner({ text: 'Collecting files...' })
151
- .start();
152
-
153
- try {
154
- const { packer } = this.configService.getConfig();
155
- const extraExcludes = options.exclude ?? [];
156
-
157
- let files: string[];
158
- let explainMap: Map<string, string> | undefined;
159
-
160
- if (options.deps) {
161
- if (inputs.length === 0) {
162
- spinner.error('--deps requires at least one entry file as argument');
163
- this.ui.log.error('Usage: kodu pack <entry.ts> [more.ts...] --deps');
164
- process.exitCode = 1;
165
- return;
166
- }
167
-
168
- spinner.text = 'Resolving dependency graph...';
169
- const result = this.depsService.collectDependencies(
170
- inputs,
171
- process.cwd(),
172
- {
173
- maxDepth: options.depsDepth,
174
- includeTypes: true,
175
- includeDynamic: false,
176
- },
177
- );
178
- files = result.files;
179
- explainMap = result.explain;
180
- } else {
181
- files = await this.fsService.findProjectFiles({
182
- excludeBinary: true,
183
- useGitignore: packer.useGitignore,
184
- ignore: [...packer.ignore, ...extraExcludes],
185
- contentBasedBinaryDetection: packer.contentBasedBinaryDetection,
186
- rootPaths: inputs.length > 0 ? inputs : options.path,
187
- });
188
- }
189
-
190
- if (files.length === 0) {
191
- spinner.stop('No files to pack.');
192
- this.ui.log.warn('No files to pack.');
193
- return;
194
- }
195
-
196
- if (options.list) {
197
- spinner.success(`Found ${files.length} files`);
198
- for (const file of files) {
199
- if (options.explain && explainMap) {
200
- const absFile = path.resolve(process.cwd(), file);
201
- const reason =
202
- explainMap.get(absFile) ?? explainMap.get(file) ?? '';
203
- this.ui.log.info(`${file} ← ${reason}`);
204
- } else {
205
- this.ui.log.info(file);
206
- }
207
- }
208
- return;
209
- }
210
-
211
- if (options.explain && explainMap) {
212
- spinner.success(`Found ${files.length} files`);
213
- this.ui.log.info('Dependency explanation:');
214
- for (const file of files) {
215
- const absFile = path.resolve(process.cwd(), file);
216
- const reason = explainMap.get(absFile) ?? explainMap.get(file) ?? '';
217
- this.ui.log.info(` ${file} ← ${reason}`);
218
- }
219
- }
220
-
221
- const format: OutputFormat = options.format ?? 'xml';
222
- const context = await this.buildContext(files, format, options.clean);
223
- const fileList = files.join('\n');
224
- const { tokens, usdEstimate } = this.tokenizer.count(context);
225
-
226
- const basePrompt = await this.applyConfiguredPrompt({
227
- context,
228
- fileList,
229
- tokenCount: tokens,
230
- usdEstimate,
231
- });
232
-
233
- const templateApplied = options.template
234
- ? await this.applyTemplate(options.template, {
235
- context,
236
- fileList,
237
- tokenCount: tokens,
238
- usdEstimate,
239
- })
240
- : basePrompt;
241
-
242
- const outputPath = await this.writeOutput(templateApplied, options.out);
243
-
244
- if (options.copy) {
245
- await clipboard.write(templateApplied);
246
- }
247
-
248
- spinner.success('Collection complete');
249
- this.ui.log.info(`Files: ${files.length}`);
250
- this.ui.log.info(`Tokens: ${tokens}`);
251
- this.ui.log.info(`Cost estimate: ~$${usdEstimate.toFixed(4)}`);
252
- this.ui.log.info(
253
- `Format: ${format}${options.clean ? ' (comments stripped)' : ''}`,
254
- );
255
- this.ui.log.success(`Saved to ${outputPath}`);
256
-
257
- if (options.copy) {
258
- this.ui.log.success('Result copied to clipboard');
259
- }
260
- } catch (error) {
261
- spinner.error('Error collecting context');
262
- const message = error instanceof Error ? error.message : 'Unknown error';
263
- this.ui.log.error(message);
264
- process.exitCode = 1;
265
- }
266
- }
267
-
268
- private async buildContext(
269
- files: string[],
270
- format: OutputFormat,
271
- clean = false,
272
- ): Promise<string> {
273
- const chunks = await Promise.all(
274
- files.map(async (file) => {
275
- let content = await this.fsService.readFileRelative(file);
276
- if (clean) {
277
- content = this.cleaner.cleanContent(file, content);
278
- }
279
- if (format === 'xml') {
280
- return `<file path="${file}">\n${content}\n</file>`;
281
- }
282
- return `// file: ${file}\n${content}`;
283
- }),
284
- );
285
-
286
- if (format === 'xml') {
287
- return `<files>\n${chunks.join('\n\n')}\n</files>`;
288
- }
289
- return chunks.join('\n\n');
290
- }
291
-
292
- private async applyTemplate(
293
- name: string,
294
- ctx: TemplateContext,
295
- ): Promise<string> {
296
- const template = await this.loadTemplate(name);
297
- return this.fillTemplate(template, ctx);
298
- }
299
-
300
- private async loadTemplate(name: string): Promise<string> {
301
- return this.promptService.loadFromPromptsDir(name);
302
- }
303
-
304
- private async writeOutput(
305
- content: string,
306
- outPath?: string,
307
- ): Promise<string> {
308
- const target = outPath ?? path.join(process.cwd(), '.kodu', 'context.txt');
309
- const dir = path.dirname(target);
310
- await fs.mkdir(dir, { recursive: true });
311
- await fs.writeFile(target, `${content}\n`, 'utf8');
312
- return target;
313
- }
314
-
315
- private async applyConfiguredPrompt(ctx: TemplateContext): Promise<string> {
316
- const config = this.configService.getConfig();
317
- const packPrompt = config.prompts?.pack;
318
-
319
- if (!packPrompt) {
320
- return ctx.context;
321
- }
322
-
323
- try {
324
- const template = await this.promptService.load(packPrompt);
325
- return this.fillTemplate(template, ctx);
326
- } catch {
327
- this.ui.log.warn(
328
- `Prompt file not found: ${packPrompt}, using raw context`,
329
- );
330
- return ctx.context;
331
- }
332
- }
333
-
334
- private fillTemplate(template: string, ctx: TemplateContext): string {
335
- const filled = template
336
- .replace(/\{\{context\}\}/g, ctx.context)
337
- .replace(/\{\{fileList\}\}/g, ctx.fileList)
338
- .replace(/\{\{tokenCount\}\}/g, ctx.tokenCount.toString())
339
- .replace(/\{\{usdEstimate\}\}/g, ctx.usdEstimate.toFixed(4));
340
-
341
- if (!template.includes('{{context}}')) {
342
- return `${filled}\n\n${ctx.context}`;
343
- }
344
-
345
- return filled;
346
- }
347
- }
@@ -1,14 +0,0 @@
1
- import { Module } from '@nestjs/common';
2
- import { ConfigModule } from '../../core/config/config.module';
3
- import { FsModule } from '../../core/file-system/fs.module';
4
- import { UiModule } from '../../core/ui/ui.module';
5
- import { CleanerService } from '../../shared/cleaner/cleaner.service';
6
- import { DepsModule } from '../../shared/deps/deps.module';
7
- import { TokenizerModule } from '../../shared/tokenizer/tokenizer.module';
8
- import { PackCommand } from './pack.command';
9
-
10
- @Module({
11
- imports: [ConfigModule, UiModule, FsModule, TokenizerModule, DepsModule],
12
- providers: [PackCommand, CleanerService],
13
- })
14
- export class PackModule {}
@@ -1,10 +0,0 @@
1
- import { Global, Module } from '@nestjs/common';
2
- import { ConfigService } from './config.service';
3
- import { PromptService } from './prompt.service';
4
-
5
- @Global()
6
- @Module({
7
- providers: [ConfigService, PromptService],
8
- exports: [ConfigService, PromptService],
9
- })
10
- export class ConfigModule {}
@@ -1,58 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- const cleanerSchema = z.object({
4
- whitelist: z.array(z.string()).default(['//!']),
5
- keepJSDoc: z.boolean().default(true),
6
- useGitignore: z.boolean().default(true),
7
- ignore: z.array(z.string()).default([]),
8
- });
9
-
10
- const packerSchema = z.object({
11
- ignore: z
12
- .array(z.string())
13
- .default([
14
- 'package-lock.json',
15
- 'yarn.lock',
16
- 'pnpm-lock.yaml',
17
- '.git',
18
- '.kodu',
19
- 'node_modules',
20
- 'dist',
21
- 'coverage',
22
- ]),
23
- useGitignore: z.boolean().default(true),
24
- contentBasedBinaryDetection: z.boolean().default(false),
25
- });
26
-
27
- const promptsSchema = z
28
- .object({
29
- pack: z.string().optional(),
30
- })
31
- .optional();
32
-
33
- export const configSchema = z.object({
34
- $schema: z.string().optional(),
35
- cleaner: cleanerSchema.default({
36
- whitelist: ['//!'],
37
- keepJSDoc: true,
38
- useGitignore: true,
39
- ignore: [],
40
- }),
41
- packer: packerSchema.default({
42
- ignore: [
43
- 'package-lock.json',
44
- 'yarn.lock',
45
- 'pnpm-lock.yaml',
46
- '.git',
47
- '.kodu',
48
- 'node_modules',
49
- 'dist',
50
- 'coverage',
51
- ],
52
- useGitignore: true,
53
- contentBasedBinaryDetection: false,
54
- }),
55
- prompts: promptsSchema,
56
- });
57
-
58
- export type KoduConfig = z.infer<typeof configSchema>;
@@ -1,43 +0,0 @@
1
- import { Injectable } from '@nestjs/common';
2
- import { lilconfigSync } from 'lilconfig';
3
- import pc from 'picocolors';
4
- import { configSchema, type KoduConfig } from './config.schema';
5
-
6
- @Injectable()
7
- export class ConfigService {
8
- private config?: KoduConfig;
9
-
10
- getConfig(): KoduConfig {
11
- if (!this.config) {
12
- this.config = this.loadConfig();
13
- }
14
-
15
- return this.config;
16
- }
17
-
18
- private loadConfig(): KoduConfig {
19
- const explorer = lilconfigSync('kodu', { searchPlaces: ['kodu.json'] });
20
- const result = explorer.search(process.cwd());
21
-
22
- const rawConfig =
23
- result && !result.isEmpty && result.config ? result.config : {};
24
-
25
- const parsed = configSchema.safeParse(rawConfig);
26
-
27
- if (!parsed.success) {
28
- console.error(pc.red('kodu.json is invalid:'));
29
- parsed.error.issues.forEach((issue) => {
30
- const path = issue.path.join('.') || '(root)';
31
- console.error(pc.red(`- ${path}: ${issue.message}`));
32
- });
33
- this.terminate('Fix the config and run the command again.');
34
- }
35
-
36
- return parsed.data;
37
- }
38
-
39
- private terminate(message: string): never {
40
- console.error(pc.red(message));
41
- process.exit(1);
42
- }
43
- }
@@ -1,80 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import path from 'node:path';
3
- import { Injectable } from '@nestjs/common';
4
-
5
- @Injectable()
6
- export class PromptService {
7
- private readonly cache = new Map<string, string>();
8
-
9
- async load(source: string): Promise<string> {
10
- return this.readSource(source);
11
- }
12
-
13
- async loadFromPromptsDir(name: string): Promise<string> {
14
- const candidates = this.buildCandidates(name);
15
-
16
- for (const candidate of candidates) {
17
- if (await this.exists(candidate)) {
18
- return this.readAndCache(candidate);
19
- }
20
- }
21
-
22
- throw new Error(
23
- `Template ${name} not found. Expected files: ${candidates
24
- .map((c) => path.relative(process.cwd(), c))
25
- .join(', ')}`,
26
- );
27
- }
28
-
29
- private async readSource(source: string): Promise<string> {
30
- const resolved = path.isAbsolute(source)
31
- ? source
32
- : path.resolve(process.cwd(), source);
33
-
34
- if (await this.exists(resolved)) {
35
- return this.readAndCache(resolved);
36
- }
37
-
38
- if (this.looksLikeInline(source)) {
39
- return source;
40
- }
41
-
42
- throw new Error(
43
- `Prompt file not found: ${path.relative(process.cwd(), resolved)}`,
44
- );
45
- }
46
-
47
- private async readAndCache(target: string): Promise<string> {
48
- const cached = this.cache.get(target);
49
- if (cached) {
50
- return cached;
51
- }
52
- const content = await fs.readFile(target, 'utf8');
53
- this.cache.set(target, content);
54
- return content;
55
- }
56
-
57
- private looksLikeInline(value: string): boolean {
58
- if (value.includes('\n')) {
59
- return true;
60
- }
61
- const hasPathSegments = value.includes('/') || value.includes('\\');
62
- const hasExtension = path.extname(value) !== '';
63
- return value.trim().length > 0 && !hasPathSegments && !hasExtension;
64
- }
65
-
66
- private buildCandidates(name: string): string[] {
67
- const names = path.extname(name) ? [name] : [`${name}.md`, `${name}.txt`];
68
- const root = path.join(process.cwd(), '.kodu', 'prompts');
69
- return names.map((variant) => path.join(root, variant));
70
- }
71
-
72
- private async exists(target: string): Promise<boolean> {
73
- try {
74
- await fs.access(target);
75
- return true;
76
- } catch {
77
- return false;
78
- }
79
- }
80
- }
@@ -1,8 +0,0 @@
1
- import { Module } from '@nestjs/common';
2
- import { FsService } from './fs.service';
3
-
4
- @Module({
5
- providers: [FsService],
6
- exports: [FsService],
7
- })
8
- export class FsModule {}