kodu 2.2.0 → 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.
Files changed (233) hide show
  1. package/README.md +24 -3
  2. package/bin/kodu.js +23 -0
  3. package/package.json +11 -67
  4. package/scripts/postinstall.js +69 -0
  5. package/AGENTS.md +0 -214
  6. package/__tests__/core/fs/fs.service.test.ts +0 -72
  7. package/__tests__/core/registry/registry.service.test.ts +0 -82
  8. package/__tests__/shared/cleaner/cleaner.service.test.ts +0 -102
  9. package/__tests__/shared/git/git.service.test.ts +0 -84
  10. package/__tests__/shared/runbook/runbook.service.test.ts +0 -104
  11. package/__tests__/shared/tokenizer/tokenizer.service.test.ts +0 -45
  12. package/biome.json +0 -50
  13. package/dist/package.json +0 -96
  14. package/dist/src/app.module.d.ts +0 -2
  15. package/dist/src/app.module.js +0 -42
  16. package/dist/src/app.module.js.map +0 -1
  17. package/dist/src/commands/clean/clean.command.d.ts +0 -37
  18. package/dist/src/commands/clean/clean.command.js +0 -240
  19. package/dist/src/commands/clean/clean.command.js.map +0 -1
  20. package/dist/src/commands/clean/clean.module.d.ts +0 -2
  21. package/dist/src/commands/clean/clean.module.js +0 -26
  22. package/dist/src/commands/clean/clean.module.js.map +0 -1
  23. package/dist/src/commands/init/init.command.d.ts +0 -10
  24. package/dist/src/commands/init/init.command.js +0 -96
  25. package/dist/src/commands/init/init.command.js.map +0 -1
  26. package/dist/src/commands/init/init.module.d.ts +0 -2
  27. package/dist/src/commands/init/init.module.js +0 -22
  28. package/dist/src/commands/init/init.module.js.map +0 -1
  29. package/dist/src/commands/ops/ops-add.command.d.ts +0 -18
  30. package/dist/src/commands/ops/ops-add.command.js +0 -102
  31. package/dist/src/commands/ops/ops-add.command.js.map +0 -1
  32. package/dist/src/commands/ops/ops-init.command.d.ts +0 -22
  33. package/dist/src/commands/ops/ops-init.command.js +0 -130
  34. package/dist/src/commands/ops/ops-init.command.js.map +0 -1
  35. package/dist/src/commands/ops/ops-list.command.d.ts +0 -12
  36. package/dist/src/commands/ops/ops-list.command.js +0 -73
  37. package/dist/src/commands/ops/ops-list.command.js.map +0 -1
  38. package/dist/src/commands/ops/ops-path.command.d.ts +0 -9
  39. package/dist/src/commands/ops/ops-path.command.js +0 -52
  40. package/dist/src/commands/ops/ops-path.command.js.map +0 -1
  41. package/dist/src/commands/ops/ops-runbook.command.d.ts +0 -12
  42. package/dist/src/commands/ops/ops-runbook.command.js +0 -81
  43. package/dist/src/commands/ops/ops-runbook.command.js.map +0 -1
  44. package/dist/src/commands/ops/ops-status.command.d.ts +0 -11
  45. package/dist/src/commands/ops/ops-status.command.js +0 -62
  46. package/dist/src/commands/ops/ops-status.command.js.map +0 -1
  47. package/dist/src/commands/ops/ops-use.command.d.ts +0 -12
  48. package/dist/src/commands/ops/ops-use.command.js +0 -76
  49. package/dist/src/commands/ops/ops-use.command.js.map +0 -1
  50. package/dist/src/commands/ops/ops.command.d.ts +0 -7
  51. package/dist/src/commands/ops/ops.command.js +0 -56
  52. package/dist/src/commands/ops/ops.command.js.map +0 -1
  53. package/dist/src/commands/ops/ops.helpers.d.ts +0 -2
  54. package/dist/src/commands/ops/ops.helpers.js +0 -11
  55. package/dist/src/commands/ops/ops.helpers.js.map +0 -1
  56. package/dist/src/commands/ops/ops.module.d.ts +0 -2
  57. package/dist/src/commands/ops/ops.module.js +0 -36
  58. package/dist/src/commands/ops/ops.module.js.map +0 -1
  59. package/dist/src/commands/pack/pack.command.d.ts +0 -51
  60. package/dist/src/commands/pack/pack.command.js +0 -355
  61. package/dist/src/commands/pack/pack.command.js.map +0 -1
  62. package/dist/src/commands/pack/pack.module.d.ts +0 -2
  63. package/dist/src/commands/pack/pack.module.js +0 -27
  64. package/dist/src/commands/pack/pack.module.js.map +0 -1
  65. package/dist/src/core/config/config.module.d.ts +0 -2
  66. package/dist/src/core/config/config.module.js +0 -23
  67. package/dist/src/core/config/config.module.js.map +0 -1
  68. package/dist/src/core/config/config.schema.d.ts +0 -19
  69. package/dist/src/core/config/config.schema.js +0 -56
  70. package/dist/src/core/config/config.schema.js.map +0 -1
  71. package/dist/src/core/config/config.service.d.ts +0 -7
  72. package/dist/src/core/config/config.service.js +0 -49
  73. package/dist/src/core/config/config.service.js.map +0 -1
  74. package/dist/src/core/config/prompt.service.d.ts +0 -10
  75. package/dist/src/core/config/prompt.service.js +0 -80
  76. package/dist/src/core/config/prompt.service.js.map +0 -1
  77. package/dist/src/core/file-system/fs.module.d.ts +0 -2
  78. package/dist/src/core/file-system/fs.module.js +0 -21
  79. package/dist/src/core/file-system/fs.module.js.map +0 -1
  80. package/dist/src/core/file-system/fs.service.d.ts +0 -27
  81. package/dist/src/core/file-system/fs.service.js +0 -203
  82. package/dist/src/core/file-system/fs.service.js.map +0 -1
  83. package/dist/src/core/registry/registry.module.d.ts +0 -2
  84. package/dist/src/core/registry/registry.module.js +0 -22
  85. package/dist/src/core/registry/registry.module.js.map +0 -1
  86. package/dist/src/core/registry/registry.schema.d.ts +0 -24
  87. package/dist/src/core/registry/registry.schema.js +0 -21
  88. package/dist/src/core/registry/registry.schema.js.map +0 -1
  89. package/dist/src/core/registry/registry.service.d.ts +0 -16
  90. package/dist/src/core/registry/registry.service.js +0 -91
  91. package/dist/src/core/registry/registry.service.js.map +0 -1
  92. package/dist/src/core/ui/ui.module.d.ts +0 -2
  93. package/dist/src/core/ui/ui.module.js +0 -22
  94. package/dist/src/core/ui/ui.module.js.map +0 -1
  95. package/dist/src/core/ui/ui.service.d.ts +0 -22
  96. package/dist/src/core/ui/ui.service.js +0 -43
  97. package/dist/src/core/ui/ui.service.js.map +0 -1
  98. package/dist/src/main.d.ts +0 -2
  99. package/dist/src/main.js +0 -16
  100. package/dist/src/main.js.map +0 -1
  101. package/dist/src/shared/cleaner/cleaner.service.d.ts +0 -23
  102. package/dist/src/shared/cleaner/cleaner.service.js +0 -223
  103. package/dist/src/shared/cleaner/cleaner.service.js.map +0 -1
  104. package/dist/src/shared/cleaner/cleaner.types.d.ts +0 -21
  105. package/dist/src/shared/cleaner/cleaner.types.js +0 -3
  106. package/dist/src/shared/cleaner/cleaner.types.js.map +0 -1
  107. package/dist/src/shared/constants.d.ts +0 -4
  108. package/dist/src/shared/constants.js +0 -113
  109. package/dist/src/shared/constants.js.map +0 -1
  110. package/dist/src/shared/deps/deps.module.d.ts +0 -2
  111. package/dist/src/shared/deps/deps.module.js +0 -21
  112. package/dist/src/shared/deps/deps.module.js.map +0 -1
  113. package/dist/src/shared/deps/deps.service.d.ts +0 -15
  114. package/dist/src/shared/deps/deps.service.js +0 -114
  115. package/dist/src/shared/deps/deps.service.js.map +0 -1
  116. package/dist/src/shared/git/git.module.d.ts +0 -2
  117. package/dist/src/shared/git/git.module.js +0 -21
  118. package/dist/src/shared/git/git.module.js.map +0 -1
  119. package/dist/src/shared/git/git.service.d.ts +0 -5
  120. package/dist/src/shared/git/git.service.js +0 -56
  121. package/dist/src/shared/git/git.service.js.map +0 -1
  122. package/dist/src/shared/runbook/runbook.module.d.ts +0 -2
  123. package/dist/src/shared/runbook/runbook.module.js +0 -22
  124. package/dist/src/shared/runbook/runbook.module.js.map +0 -1
  125. package/dist/src/shared/runbook/runbook.service.d.ts +0 -20
  126. package/dist/src/shared/runbook/runbook.service.js +0 -118
  127. package/dist/src/shared/runbook/runbook.service.js.map +0 -1
  128. package/dist/src/shared/runbook/runbook.templates.d.ts +0 -6
  129. package/dist/src/shared/runbook/runbook.templates.js +0 -49
  130. package/dist/src/shared/runbook/runbook.templates.js.map +0 -1
  131. package/dist/src/shared/tokenizer/tokenizer.module.d.ts +0 -2
  132. package/dist/src/shared/tokenizer/tokenizer.module.js +0 -21
  133. package/dist/src/shared/tokenizer/tokenizer.module.js.map +0 -1
  134. package/dist/src/shared/tokenizer/tokenizer.service.d.ts +0 -10
  135. package/dist/src/shared/tokenizer/tokenizer.service.js +0 -36
  136. package/dist/src/shared/tokenizer/tokenizer.service.js.map +0 -1
  137. package/dist/tsconfig.build.tsbuildinfo +0 -1
  138. package/docs/todo.md +0 -7
  139. package/knip.json +0 -10
  140. package/kodu.json +0 -63
  141. package/kodu.schema.json +0 -100
  142. package/lefthook.yml +0 -11
  143. package/nest-cli.json +0 -8
  144. package/registry.schema.json +0 -39
  145. package/scripts/generate-json-schema.ts +0 -27
  146. package/skills/ac/SKILL.md +0 -239
  147. package/skills/al/SKILL.md +0 -98
  148. package/skills/audit/SKILL.md +0 -205
  149. package/skills/audit/audit-baseline-template.yml +0 -188
  150. package/skills/audit/runtime-detect.md +0 -64
  151. package/skills/audit/stacks/_generic.md +0 -41
  152. package/skills/audit/stacks/_registry.md +0 -47
  153. package/skills/audit/stacks/go.md +0 -66
  154. package/skills/audit/stacks/java.md +0 -44
  155. package/skills/audit/stacks/node.md +0 -57
  156. package/skills/audit/stacks/python.md +0 -45
  157. package/skills/audit/stacks/rust.md +0 -44
  158. package/skills/audit-api-contracts/SKILL.md +0 -201
  159. package/skills/audit-architecture/SKILL.md +0 -200
  160. package/skills/audit-bugs/SKILL.md +0 -226
  161. package/skills/audit-concurrency/SKILL.md +0 -197
  162. package/skills/audit-deployment/SKILL.md +0 -218
  163. package/skills/audit-docs/SKILL.md +0 -209
  164. package/skills/audit-errors/SKILL.md +0 -216
  165. package/skills/audit-logging/SKILL.md +0 -197
  166. package/skills/audit-matrix/SKILL.md +0 -245
  167. package/skills/audit-meta/SKILL.md +0 -120
  168. package/skills/audit-naming/SKILL.md +0 -200
  169. package/skills/audit-owasp/SKILL.md +0 -223
  170. package/skills/audit-performance/SKILL.md +0 -199
  171. package/skills/audit-reinvention/SKILL.md +0 -214
  172. package/skills/audit-secrets/SKILL.md +0 -198
  173. package/skills/audit-tests/SKILL.md +0 -210
  174. package/skills/audit-validation/SKILL.md +0 -206
  175. package/skills/audit-verify/SKILL.md +0 -139
  176. package/skills/audit-yagni/SKILL.md +0 -188
  177. package/skills/doc-gen/SKILL.md +0 -490
  178. package/skills/doc-gen/scripts/doc_gen.py +0 -911
  179. package/skills/generate-project-docs/SKILL.md +0 -380
  180. package/skills/implement-project/SKILL.md +0 -409
  181. package/skills/liteend-init/SKILL.md +0 -84
  182. package/skills/litefront-init/SKILL.md +0 -96
  183. package/skills/litefront-prototype/SKILL.md +0 -484
  184. package/skills/ops/SKILL.md +0 -94
  185. package/skills/post-call-task-builder/SKILL.md +0 -419
  186. package/skills/project-setup-standardizer/SKILL.md +0 -285
  187. package/skills/skills-best-practices/SKILL.md +0 -415
  188. package/skills/start/SKILL.md +0 -319
  189. package/skills/tech-blueprint/SKILL.md +0 -890
  190. package/skills/tech-blueprint/scripts/blueprint_validator.py +0 -417
  191. package/src/app.module.ts +0 -29
  192. package/src/commands/clean/clean.command.ts +0 -235
  193. package/src/commands/clean/clean.module.ts +0 -13
  194. package/src/commands/init/init.command.ts +0 -92
  195. package/src/commands/init/init.module.ts +0 -9
  196. package/src/commands/ops/ops-add.command.ts +0 -83
  197. package/src/commands/ops/ops-init.command.ts +0 -125
  198. package/src/commands/ops/ops-list.command.ts +0 -57
  199. package/src/commands/ops/ops-path.command.ts +0 -38
  200. package/src/commands/ops/ops-runbook.command.ts +0 -74
  201. package/src/commands/ops/ops-status.command.ts +0 -47
  202. package/src/commands/ops/ops-use.command.ts +0 -76
  203. package/src/commands/ops/ops.command.ts +0 -42
  204. package/src/commands/ops/ops.helpers.ts +0 -20
  205. package/src/commands/ops/ops.module.ts +0 -23
  206. package/src/commands/pack/pack.command.ts +0 -347
  207. package/src/commands/pack/pack.module.ts +0 -14
  208. package/src/core/config/config.module.ts +0 -10
  209. package/src/core/config/config.schema.ts +0 -58
  210. package/src/core/config/config.service.ts +0 -43
  211. package/src/core/config/prompt.service.ts +0 -80
  212. package/src/core/file-system/fs.module.ts +0 -8
  213. package/src/core/file-system/fs.service.ts +0 -248
  214. package/src/core/registry/registry.module.ts +0 -9
  215. package/src/core/registry/registry.schema.ts +0 -46
  216. package/src/core/registry/registry.service.ts +0 -128
  217. package/src/core/ui/ui.module.ts +0 -9
  218. package/src/core/ui/ui.service.ts +0 -39
  219. package/src/main.ts +0 -12
  220. package/src/shared/cleaner/cleaner.service.ts +0 -289
  221. package/src/shared/cleaner/cleaner.types.ts +0 -23
  222. package/src/shared/constants.ts +0 -118
  223. package/src/shared/deps/deps.module.ts +0 -8
  224. package/src/shared/deps/deps.service.ts +0 -175
  225. package/src/shared/git/git.module.ts +0 -8
  226. package/src/shared/git/git.service.ts +0 -47
  227. package/src/shared/runbook/runbook.module.ts +0 -9
  228. package/src/shared/runbook/runbook.service.ts +0 -164
  229. package/src/shared/runbook/runbook.templates.ts +0 -66
  230. package/src/shared/tokenizer/tokenizer.module.ts +0 -8
  231. package/src/shared/tokenizer/tokenizer.service.ts +0 -30
  232. package/tsconfig.build.json +0 -7
  233. 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 {}