@cod3vil/kount-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +375 -0
- package/bin/kount.js +2 -0
- package/package.json +65 -0
- package/src/cli/.gitkeep +0 -0
- package/src/cli/config-resolver.ts +175 -0
- package/src/cli/parser.ts +52 -0
- package/src/core/.gitkeep +0 -0
- package/src/core/aggregator.ts +204 -0
- package/src/core/cache.ts +130 -0
- package/src/index.tsx +167 -0
- package/src/plugins/.gitkeep +0 -0
- package/src/plugins/built-in/blank-lines.ts +26 -0
- package/src/plugins/built-in/comment-lines.ts +90 -0
- package/src/plugins/built-in/file-size.ts +20 -0
- package/src/plugins/built-in/language-distribution.ts +95 -0
- package/src/plugins/built-in/largest-files.ts +41 -0
- package/src/plugins/built-in/total-files.ts +18 -0
- package/src/plugins/built-in/total-lines.ts +21 -0
- package/src/plugins/index.ts +10 -0
- package/src/plugins/types.ts +58 -0
- package/src/reporters/.gitkeep +0 -0
- package/src/reporters/html.ts +385 -0
- package/src/reporters/markdown.ts +129 -0
- package/src/reporters/terminal/Progress.tsx +39 -0
- package/src/reporters/terminal/Splash.tsx +32 -0
- package/src/reporters/terminal/Summary.tsx +135 -0
- package/src/reporters/terminal/Wizard.tsx +125 -0
- package/src/reporters/terminal/index.ts +6 -0
- package/src/scanner/.gitkeep +0 -0
- package/src/scanner/ignore-parser.ts +168 -0
- package/src/scanner/stream-reader.ts +99 -0
- package/src/utils/.gitkeep +0 -0
- package/src/utils/language-map.ts +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<pre>
|
|
3
|
+
██╗ ██╗ ██████╗ ██╗ ██╗███╗ ██╗████████╗
|
|
4
|
+
██║ ██╔╝██╔═══██╗██║ ██║████╗ ██║╚══██╔══╝
|
|
5
|
+
█████╔╝ ██║ ██║██║ ██║██╔██╗ ██║ ██║
|
|
6
|
+
██╔═██╗ ██║ ██║██║ ██║██║╚██╗██║ ██║
|
|
7
|
+
██║ ██╗╚██████╔╝╚██████╔╝██║ ╚████║ ██║
|
|
8
|
+
╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝
|
|
9
|
+
</pre>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<strong>Project Intelligence for Codebases</strong>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<em>Analyze your code with precision. Stream-based. Cached. Beautiful.</em>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<a href="#installation">Installation</a> •
|
|
22
|
+
<a href="#quick-start">Quick Start</a> •
|
|
23
|
+
<a href="#features">Features</a> •
|
|
24
|
+
<a href="#cli-reference">CLI Reference</a> •
|
|
25
|
+
<a href="#architecture">Architecture</a> •
|
|
26
|
+
<a href="#configuration">Configuration</a> •
|
|
27
|
+
<a href="#contributing">Contributing</a>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## What is Kount?
|
|
33
|
+
|
|
34
|
+
**Kount** is a codebase intelligence CLI tool that scans your project and delivers precise metrics — total lines, code lines, blank lines, comments, file sizes, language distribution, and more. It outputs results as a stunning **terminal UI**, a clean **Markdown report**, or an interactive **HTML dashboard**.
|
|
35
|
+
|
|
36
|
+
Built with [Bun](https://bun.sh), [TypeScript](https://www.typescriptlang.org/), and [Ink](https://github.com/vadimdemedes/ink) (React for CLIs).
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
| Feature | Description |
|
|
43
|
+
|---------|-------------|
|
|
44
|
+
| **Stream-Based Scanning** | Files are read chunk-by-chunk via `fs.createReadStream`. No file is ever fully loaded into memory. |
|
|
45
|
+
| **Incremental Cache** | Uses `mtime` + `size` invalidation to skip unchanged files on subsequent runs. |
|
|
46
|
+
| **Plugin Architecture** | 7 built-in analyzers, each implementing a clean `AnalyzerPlugin` interface. |
|
|
47
|
+
| **Three Output Modes** | Terminal (Ink), Markdown (with idempotent `KOUNT:START/END` markers), and HTML (Tailwind + Alpine.js dashboard). |
|
|
48
|
+
| **Interactive Wizard** | When run without flags, a step-by-step TUI wizard guides configuration. |
|
|
49
|
+
| **Respects Ignore Rules** | Automatically reads `.gitignore` and `.kountignore` files recursively. |
|
|
50
|
+
| **Config File Support** | Configure defaults via `.kountrc.json` or `.kountrc.yaml`. |
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
Install globally from npm using your preferred package manager:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# npm
|
|
60
|
+
npm install -g @cod3vil/kount-cli
|
|
61
|
+
|
|
62
|
+
# pnpm
|
|
63
|
+
pnpm add -g @cod3vil/kount-cli
|
|
64
|
+
|
|
65
|
+
# bun
|
|
66
|
+
bun add -g @cod3vil/kount-cli
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Once installed, the `kount` command is available globally.
|
|
70
|
+
|
|
71
|
+
### Install from source
|
|
72
|
+
|
|
73
|
+
Alternatively, clone and run directly:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/michaelnji/kount.git
|
|
77
|
+
cd kount
|
|
78
|
+
bun install
|
|
79
|
+
bun run dev
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
### Interactive mode (Wizard)
|
|
87
|
+
|
|
88
|
+
Simply run kount without any flags to launch the interactive wizard:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
kount
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The wizard will guide you through:
|
|
95
|
+
1. **Root directory** — which directory to scan
|
|
96
|
+
2. **Output mode** — terminal, markdown, or html
|
|
97
|
+
3. **Include tests** — whether to include test files
|
|
98
|
+
|
|
99
|
+
### Scan and display in terminal
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
kount -d ./my-project
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Generate a Markdown report
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
kount --output-mode markdown --output ./REPORT.md
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Launch the HTML dashboard
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
kount --output-mode html
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This auto-opens your browser with a sortable, interactive dashboard including a **Help page** with full CLI reference and developer info.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## CLI Reference
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
Usage: kount [options]
|
|
125
|
+
|
|
126
|
+
Project Intelligence for Codebases — analyze your code with precision.
|
|
127
|
+
|
|
128
|
+
Options:
|
|
129
|
+
-V, --version Display version number
|
|
130
|
+
-d, --root-dir <path> Root directory to scan (default: current directory)
|
|
131
|
+
-o, --output-mode <mode> Output mode: terminal, markdown, or html
|
|
132
|
+
-t, --include-tests Include test files in the analysis
|
|
133
|
+
-f, --force Force overwrite output files (for markdown mode)
|
|
134
|
+
--output <path> Output file path (for markdown mode)
|
|
135
|
+
--no-gitignore Ignore .gitignore rules
|
|
136
|
+
--no-cache Disable caching
|
|
137
|
+
--clear-cache Clear the cache before scanning
|
|
138
|
+
-h, --help Display help for command
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Examples
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Scan current directory, display in terminal
|
|
145
|
+
kount
|
|
146
|
+
|
|
147
|
+
# Scan a specific project
|
|
148
|
+
kount -d ~/projects/my-app
|
|
149
|
+
|
|
150
|
+
# Generate markdown report, overwriting any existing KOUNT section
|
|
151
|
+
kount -o markdown -f
|
|
152
|
+
|
|
153
|
+
# Launch HTML dashboard
|
|
154
|
+
kount -o html
|
|
155
|
+
|
|
156
|
+
# Scan including test files, no cache
|
|
157
|
+
kount -t --no-cache
|
|
158
|
+
|
|
159
|
+
# Clear stale cache, then scan
|
|
160
|
+
kount --clear-cache
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Output Modes
|
|
166
|
+
|
|
167
|
+
### Terminal (default)
|
|
168
|
+
|
|
169
|
+
An Ink-powered React TUI with:
|
|
170
|
+
- **Splash screen** — ASCII logo on launch
|
|
171
|
+
- **Progress bar** — color-coded (red → yellow → green) with current file display
|
|
172
|
+
- **Summary panel** — files, lines, code ratio, language distribution, largest files
|
|
173
|
+
|
|
174
|
+
### Markdown
|
|
175
|
+
|
|
176
|
+
Generates a report with `<!-- KOUNT:START -->` / `<!-- KOUNT:END -->` markers. Running kount again will **replace** the existing section in-place, making it safe to commit to your README.
|
|
177
|
+
|
|
178
|
+
| Behavior | When |
|
|
179
|
+
|----------|------|
|
|
180
|
+
| Creates new file | File doesn't exist |
|
|
181
|
+
| Appends section | File exists but has no KOUNT markers |
|
|
182
|
+
| Replaces section | File has existing KOUNT markers |
|
|
183
|
+
| Overwrites file | `--force` flag is used |
|
|
184
|
+
|
|
185
|
+
### HTML Dashboard
|
|
186
|
+
|
|
187
|
+
Serves a Tailwind CSS + Alpine.js dashboard on a local server with:
|
|
188
|
+
- **Summary cards** — files, lines, code ratio, size
|
|
189
|
+
- **Language distribution** — sortable bar chart
|
|
190
|
+
- **Top largest files** — ranked table
|
|
191
|
+
- **Help page** — about, CLI reference, developer info
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Built-in Plugins
|
|
196
|
+
|
|
197
|
+
| Plugin | Metric | Description |
|
|
198
|
+
|--------|--------|-------------|
|
|
199
|
+
| `TotalLinesPlugin` | Total Lines | Counts all lines across all files |
|
|
200
|
+
| `BlankLinesPlugin` | Blank Lines | Counts empty or whitespace-only lines |
|
|
201
|
+
| `CommentLinesPlugin` | Comment Lines | Detects single-line and block comments using language-aware syntax mapping |
|
|
202
|
+
| `FileSizePlugin` | File Size | Sums total file sizes in bytes |
|
|
203
|
+
| `TotalFilesPlugin` | Total Files | Counts all scanned files |
|
|
204
|
+
| `LanguageDistributionPlugin` | Languages | Groups files by detected programming language |
|
|
205
|
+
| `LargestFilesPlugin` | Largest Files | Identifies and ranks the top 10 largest files |
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Architecture
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
src/
|
|
213
|
+
├── cli/
|
|
214
|
+
│ ├── config-resolver.ts # Merges CLI flags > config file > defaults
|
|
215
|
+
│ └── parser.ts # Commander-based CLI argument parsing
|
|
216
|
+
├── core/
|
|
217
|
+
│ ├── aggregator.ts # Orchestrator: Scanner → Plugins → Stats
|
|
218
|
+
│ └── cache.ts # mtime+size invalidation cache (.kountcache.json)
|
|
219
|
+
├── plugins/
|
|
220
|
+
│ ├── types.ts # AnalyzerPlugin interface & ProjectStats type
|
|
221
|
+
│ ├── index.ts # Barrel export
|
|
222
|
+
│ └── built-in/ # 7 built-in analyzer plugins
|
|
223
|
+
├── reporters/
|
|
224
|
+
│ ├── markdown.ts # Markdown report generator with KOUNT markers
|
|
225
|
+
│ ├── html.ts # HTML dashboard with Tailwind + Alpine.js
|
|
226
|
+
│ └── terminal/ # Ink components: Splash, Progress, Summary, Wizard
|
|
227
|
+
├── scanner/
|
|
228
|
+
│ ├── ignore-parser.ts # .gitignore / .kountignore integration
|
|
229
|
+
│ └── stream-reader.ts # Recursive directory walker + chunk streamer
|
|
230
|
+
├── utils/
|
|
231
|
+
│ └── language-map.ts # File extension → comment syntax mapping
|
|
232
|
+
└── index.tsx # Main entry point
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Data Flow
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
CLI Flags + Config File
|
|
239
|
+
│
|
|
240
|
+
▼
|
|
241
|
+
Config Resolver ──→ Resolved KountConfig
|
|
242
|
+
│
|
|
243
|
+
▼
|
|
244
|
+
Aggregator
|
|
245
|
+
│
|
|
246
|
+
├──→ Scanner (discovers files, streams chunks)
|
|
247
|
+
├──→ Plugins (analyze each file's data)
|
|
248
|
+
├──→ Cache (load/save per-file metrics)
|
|
249
|
+
│
|
|
250
|
+
▼
|
|
251
|
+
ProjectStats
|
|
252
|
+
│
|
|
253
|
+
├──→ Terminal Reporter (Ink)
|
|
254
|
+
├──→ Markdown Reporter
|
|
255
|
+
└──→ HTML Reporter
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Configuration
|
|
261
|
+
|
|
262
|
+
### `.kountrc.json`
|
|
263
|
+
|
|
264
|
+
Place a `.kountrc.json` in your project root to set defaults:
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"rootDir": ".",
|
|
269
|
+
"outputMode": "terminal",
|
|
270
|
+
"includeTests": false,
|
|
271
|
+
"respectGitignore": true,
|
|
272
|
+
"cache": {
|
|
273
|
+
"enabled": true,
|
|
274
|
+
"clearFirst": false
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### `.kountrc.yaml`
|
|
280
|
+
|
|
281
|
+
Alternatively, use YAML:
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
rootDir: .
|
|
285
|
+
outputMode: terminal
|
|
286
|
+
includeTests: false
|
|
287
|
+
respectGitignore: true
|
|
288
|
+
cache:
|
|
289
|
+
enabled: true
|
|
290
|
+
clearFirst: false
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### `.kountignore`
|
|
294
|
+
|
|
295
|
+
Works like `.gitignore` — add glob patterns for files/directories kount should skip:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
# Ignore generated files
|
|
299
|
+
dist/
|
|
300
|
+
coverage/
|
|
301
|
+
*.min.js
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Priority
|
|
305
|
+
|
|
306
|
+
Configuration is resolved with this precedence:
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
CLI Flags > Config File (.kountrc.json/.yaml) > Defaults
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Testing
|
|
315
|
+
|
|
316
|
+
Kount has comprehensive tests covering all layers:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
# Run all tests
|
|
320
|
+
bun test
|
|
321
|
+
|
|
322
|
+
# Watch mode
|
|
323
|
+
bun run test:watch
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Test suite: 28 tests, 0 failures**
|
|
327
|
+
|
|
328
|
+
| Test File | Coverage |
|
|
329
|
+
|-----------|----------|
|
|
330
|
+
| `tests/scanner.test.ts` | File discovery, ignore rules, chunk streaming |
|
|
331
|
+
| `tests/plugins.test.ts` | All 7 plugins + edge cases |
|
|
332
|
+
| `tests/cache.test.ts` | Load, save, hit, miss (mtime/size), clear, disabled mode |
|
|
333
|
+
| `tests/reporters.test.ts` | Markdown generation, file write modes, HTML generation |
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Tech Stack
|
|
338
|
+
|
|
339
|
+
| Technology | Role |
|
|
340
|
+
|------------|------|
|
|
341
|
+
| [Bun](https://bun.sh) | Runtime, package manager, bundler |
|
|
342
|
+
| [TypeScript](https://www.typescriptlang.org/) | Strict mode, ESM |
|
|
343
|
+
| [Ink](https://github.com/vadimdemedes/ink) | React-based terminal UI |
|
|
344
|
+
| [Commander](https://github.com/tj/commander.js) | CLI argument parsing |
|
|
345
|
+
| [Vitest](https://vitest.dev/) | Testing framework |
|
|
346
|
+
| [Tailwind CSS](https://tailwindcss.com/) | HTML dashboard styling (CDN) |
|
|
347
|
+
| [Alpine.js](https://alpinejs.dev/) | HTML dashboard interactivity (CDN) |
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Contributing
|
|
352
|
+
|
|
353
|
+
1. Fork the repository
|
|
354
|
+
2. Create your feature branch: `git checkout -b feat/my-feature`
|
|
355
|
+
3. Write tests for your changes
|
|
356
|
+
4. Run `bun test` to ensure all tests pass
|
|
357
|
+
5. Commit your changes: `git commit -m "feat: add my feature"`
|
|
358
|
+
6. Push to the branch: `git push origin feat/my-feature`
|
|
359
|
+
7. Open a Pull Request
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Author
|
|
364
|
+
|
|
365
|
+
**Michael Nji** — Full stack web developer with a passion for building beautiful and robust web projects.
|
|
366
|
+
|
|
367
|
+
- 🌐 [Portfolio](https://michaelnji.codes)
|
|
368
|
+
- 🐙 [GitHub](https://github.com/michaelnji)
|
|
369
|
+
- ✍️ [Blog](https://michaelnji.codes/blog)
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## License
|
|
374
|
+
|
|
375
|
+
MIT © [Michael Nji](https://michaelnji.codes)
|
package/bin/kount.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cod3vil/kount-cli",
|
|
3
|
+
"packageManager": "bun@1.3.5",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Project Intelligence for Codebases — analyze your code with precision.",
|
|
6
|
+
"module": "src/index.tsx",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"kount": "./bin/kount.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src",
|
|
13
|
+
"bin",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "bun run src/index.tsx",
|
|
21
|
+
"start": "bun run src/index.tsx",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"build": "bun build ./src/index.tsx --compile --external react-devtools-core --outfile dist/kount",
|
|
25
|
+
"prepublishOnly": "bun run build",
|
|
26
|
+
"release": "np"
|
|
27
|
+
},
|
|
28
|
+
"np": {
|
|
29
|
+
"yarn": false,
|
|
30
|
+
"testScript": "test",
|
|
31
|
+
"branch": "main"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"cli",
|
|
35
|
+
"codebase",
|
|
36
|
+
"analysis",
|
|
37
|
+
"loc",
|
|
38
|
+
"lines-of-code",
|
|
39
|
+
"project-stats",
|
|
40
|
+
"developer-tools"
|
|
41
|
+
],
|
|
42
|
+
"author": "Michael Nji",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/michaelnji/kount"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/bun": "latest",
|
|
50
|
+
"@types/node": "^25.3.2",
|
|
51
|
+
"@types/react": "^19.2.14",
|
|
52
|
+
"np": "^11.0.2",
|
|
53
|
+
"vitest": "^4.0.18"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"typescript": "^5.9.3"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"chalk": "^5.6.2",
|
|
60
|
+
"commander": "^14.0.3",
|
|
61
|
+
"ignore": "^7.0.5",
|
|
62
|
+
"ink": "^6.8.0",
|
|
63
|
+
"react": "^19.2.4"
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/cli/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolved configuration used throughout the application.
|
|
6
|
+
*/
|
|
7
|
+
export interface KountConfig {
|
|
8
|
+
rootDir: string;
|
|
9
|
+
outputMode: 'terminal' | 'markdown' | 'html';
|
|
10
|
+
includeTests: boolean;
|
|
11
|
+
respectGitignore: boolean;
|
|
12
|
+
cache: {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
clearFirst: boolean;
|
|
15
|
+
};
|
|
16
|
+
force: boolean;
|
|
17
|
+
outputPath?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Shape of the .kountrc.json / .kountrc.yaml config file.
|
|
22
|
+
*/
|
|
23
|
+
interface ConfigFile {
|
|
24
|
+
rootDir?: string;
|
|
25
|
+
outputMode?: string;
|
|
26
|
+
includeTests?: boolean;
|
|
27
|
+
respectGitignore?: boolean;
|
|
28
|
+
cache?: {
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
clearFirst?: boolean;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* CLI flags that can override the config file.
|
|
36
|
+
*/
|
|
37
|
+
export interface CliFlags {
|
|
38
|
+
rootDir?: string;
|
|
39
|
+
outputMode?: string;
|
|
40
|
+
includeTests?: boolean;
|
|
41
|
+
respectGitignore?: boolean;
|
|
42
|
+
cache?: boolean;
|
|
43
|
+
clearCache?: boolean;
|
|
44
|
+
force?: boolean;
|
|
45
|
+
output?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const DEFAULTS: KountConfig = {
|
|
49
|
+
rootDir: '.',
|
|
50
|
+
outputMode: 'terminal',
|
|
51
|
+
includeTests: false,
|
|
52
|
+
respectGitignore: true,
|
|
53
|
+
cache: {
|
|
54
|
+
enabled: true,
|
|
55
|
+
clearFirst: false,
|
|
56
|
+
},
|
|
57
|
+
force: false,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Attempts to load a config file from the given directory.
|
|
62
|
+
* Checks for .kountrc.json first, then .kountrc.yaml.
|
|
63
|
+
*/
|
|
64
|
+
async function loadConfigFile(dir: string): Promise<ConfigFile> {
|
|
65
|
+
const jsonPath = path.join(dir, '.kountrc.json');
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const raw = await fsp.readFile(jsonPath, 'utf8');
|
|
69
|
+
return JSON.parse(raw) as ConfigFile;
|
|
70
|
+
} catch {
|
|
71
|
+
// JSON not found or invalid — try YAML
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// YAML support: we'll parse manually for the simple flat structure
|
|
75
|
+
// rather than adding a heavy dependency. This handles basic key: value pairs.
|
|
76
|
+
const yamlPath = path.join(dir, '.kountrc.yaml');
|
|
77
|
+
try {
|
|
78
|
+
const raw = await fsp.readFile(yamlPath, 'utf8');
|
|
79
|
+
return parseSimpleYaml(raw);
|
|
80
|
+
} catch {
|
|
81
|
+
// No config file found — use defaults
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Minimal YAML parser for flat config files.
|
|
89
|
+
* Handles: string, boolean, and nested single-level objects.
|
|
90
|
+
*/
|
|
91
|
+
function parseSimpleYaml(raw: string): ConfigFile {
|
|
92
|
+
const result: Record<string, unknown> = {};
|
|
93
|
+
let currentObject: Record<string, unknown> | null = null;
|
|
94
|
+
let currentKey = '';
|
|
95
|
+
|
|
96
|
+
for (const line of raw.split('\n')) {
|
|
97
|
+
const trimmed = line.trim();
|
|
98
|
+
if (trimmed === '' || trimmed.startsWith('#')) continue;
|
|
99
|
+
|
|
100
|
+
const indent = line.length - line.trimStart().length;
|
|
101
|
+
|
|
102
|
+
if (indent > 0 && currentObject !== null) {
|
|
103
|
+
// Nested key
|
|
104
|
+
const match = trimmed.match(/^(\w+)\s*:\s*(.+)$/);
|
|
105
|
+
if (match) {
|
|
106
|
+
currentObject[match[1]] = parseYamlValue(match[2]);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
// Top-level key
|
|
110
|
+
const match = trimmed.match(/^(\w+)\s*:\s*(.*)$/);
|
|
111
|
+
if (match) {
|
|
112
|
+
const key = match[1];
|
|
113
|
+
const value = match[2].trim();
|
|
114
|
+
|
|
115
|
+
if (value === '') {
|
|
116
|
+
// Start of nested object
|
|
117
|
+
currentKey = key;
|
|
118
|
+
currentObject = {};
|
|
119
|
+
result[key] = currentObject;
|
|
120
|
+
} else {
|
|
121
|
+
currentObject = null;
|
|
122
|
+
result[key] = parseYamlValue(value);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return result as unknown as ConfigFile;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseYamlValue(val: string): string | boolean | number {
|
|
132
|
+
if (val === 'true') return true;
|
|
133
|
+
if (val === 'false') return false;
|
|
134
|
+
const num = Number(val);
|
|
135
|
+
if (!isNaN(num) && val !== '') return num;
|
|
136
|
+
// Strip quotes
|
|
137
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
138
|
+
return val.slice(1, -1);
|
|
139
|
+
}
|
|
140
|
+
return val;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Resolves the final configuration from:
|
|
145
|
+
* CLI flags > Config file > Defaults
|
|
146
|
+
*
|
|
147
|
+
* @param cliFlags Parsed CLI arguments.
|
|
148
|
+
* @param cwd Current working directory (for finding config files).
|
|
149
|
+
*/
|
|
150
|
+
export async function resolveConfig(cliFlags: CliFlags, cwd: string = process.cwd()): Promise<KountConfig> {
|
|
151
|
+
const fileConfig = await loadConfigFile(cwd);
|
|
152
|
+
|
|
153
|
+
const outputMode = validateOutputMode(
|
|
154
|
+
cliFlags.outputMode ?? fileConfig.outputMode ?? DEFAULTS.outputMode
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
rootDir: path.resolve(cwd, cliFlags.rootDir ?? fileConfig.rootDir ?? DEFAULTS.rootDir),
|
|
159
|
+
outputMode,
|
|
160
|
+
includeTests: cliFlags.includeTests ?? fileConfig.includeTests ?? DEFAULTS.includeTests,
|
|
161
|
+
respectGitignore: cliFlags.respectGitignore ?? fileConfig.respectGitignore ?? DEFAULTS.respectGitignore,
|
|
162
|
+
cache: {
|
|
163
|
+
enabled: cliFlags.cache ?? fileConfig.cache?.enabled ?? DEFAULTS.cache.enabled,
|
|
164
|
+
clearFirst: cliFlags.clearCache ?? fileConfig.cache?.clearFirst ?? DEFAULTS.cache.clearFirst,
|
|
165
|
+
},
|
|
166
|
+
force: cliFlags.force ?? DEFAULTS.force,
|
|
167
|
+
outputPath: cliFlags.output,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function validateOutputMode(mode: string): 'terminal' | 'markdown' | 'html' {
|
|
172
|
+
const valid = ['terminal', 'markdown', 'html'];
|
|
173
|
+
if (valid.includes(mode)) return mode as 'terminal' | 'markdown' | 'html';
|
|
174
|
+
return 'terminal';
|
|
175
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import type { CliFlags } from './config-resolver.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates and configures the Commander CLI program.
|
|
6
|
+
* Returns parsed CLI flags for the config resolver.
|
|
7
|
+
*
|
|
8
|
+
* Follows tuicfg-sensible-defaults, tuicfg-flags-over-args,
|
|
9
|
+
* and tuicfg-help-system guidelines from terminal-ui skill.
|
|
10
|
+
*/
|
|
11
|
+
export function createCli(argv: string[]): CliFlags {
|
|
12
|
+
const program = new Command();
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.name('kount')
|
|
16
|
+
.description('Project Intelligence for Codebases — analyze your code with precision.')
|
|
17
|
+
.version('1.0.0')
|
|
18
|
+
.option('-d, --root-dir <path>', 'Root directory to scan (default: current directory)')
|
|
19
|
+
.option(
|
|
20
|
+
'-o, --output-mode <mode>',
|
|
21
|
+
'Output mode: terminal, markdown, or html (default: terminal)'
|
|
22
|
+
)
|
|
23
|
+
.option('-t, --include-tests', 'Include test files in the analysis')
|
|
24
|
+
.option('--no-gitignore', 'Ignore .gitignore rules')
|
|
25
|
+
.option('--no-cache', 'Disable caching')
|
|
26
|
+
.option('--clear-cache', 'Clear the cache before scanning')
|
|
27
|
+
.option('-f, --force', 'Force overwrite output files (for markdown mode)')
|
|
28
|
+
.option('--output <path>', 'Output file path (for markdown mode)')
|
|
29
|
+
.parse(argv);
|
|
30
|
+
|
|
31
|
+
const opts = program.opts<{
|
|
32
|
+
rootDir?: string;
|
|
33
|
+
outputMode?: string;
|
|
34
|
+
includeTests?: boolean;
|
|
35
|
+
gitignore?: boolean;
|
|
36
|
+
cache?: boolean;
|
|
37
|
+
clearCache?: boolean;
|
|
38
|
+
force?: boolean;
|
|
39
|
+
output?: string;
|
|
40
|
+
}>();
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
rootDir: opts.rootDir,
|
|
44
|
+
outputMode: opts.outputMode,
|
|
45
|
+
includeTests: opts.includeTests,
|
|
46
|
+
respectGitignore: opts.gitignore, // Commander converts --no-gitignore to gitignore: false
|
|
47
|
+
cache: opts.cache, // Commander converts --no-cache to cache: false
|
|
48
|
+
clearCache: opts.clearCache,
|
|
49
|
+
force: opts.force,
|
|
50
|
+
output: opts.output,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
File without changes
|