aidoc-kit 0.1.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/LICENSE +21 -0
- package/README.md +231 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +200 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/chunker.d.ts +41 -0
- package/dist/core/chunker.d.ts.map +1 -0
- package/dist/core/chunker.js +217 -0
- package/dist/core/chunker.js.map +1 -0
- package/dist/core/config.d.ts +20 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +83 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/scanner.d.ts +10 -0
- package/dist/core/scanner.d.ts.map +1 -0
- package/dist/core/scanner.js +224 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/core/transformer.d.ts +7 -0
- package/dist/core/transformer.d.ts.map +1 -0
- package/dist/core/transformer.js +219 -0
- package/dist/core/transformer.js.map +1 -0
- package/dist/core/writer.d.ts +5 -0
- package/dist/core/writer.d.ts.map +1 -0
- package/dist/core/writer.js +99 -0
- package/dist/core/writer.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/index.d.ts +7 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +64 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Clément Tournier
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# aidoc-kit
|
|
2
|
+
|
|
3
|
+
> AI-native documentation scanner for JS/TS projects
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/aidoc-kit)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](tsconfig.json)
|
|
8
|
+
|
|
9
|
+
## The problem
|
|
10
|
+
|
|
11
|
+
When AI agents (GitHub Copilot, Cursor, Claude) work on large Next.js projects with 200–400 files, they lose context between sessions. They re-discover architecture, miss existing conventions, and break established patterns.
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. **Scan** — aidoc-kit reads your entire project via TypeScript's native AST compiler
|
|
16
|
+
2. **Generate** — it creates `@ai-*` doc blocks on every file and builds a reverse dependency graph
|
|
17
|
+
3. **Agents understand** — Copilot and any AI agent reads `.codemod/` and `AGENTS.md` to instantly understand your project without reading 400 files
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -D aidoc-kit
|
|
23
|
+
# or run directly without installing:
|
|
24
|
+
npx aidoc-kit scan
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# In any JS/TS project root:
|
|
31
|
+
npx aidoc-kit scan
|
|
32
|
+
|
|
33
|
+
# Output:
|
|
34
|
+
# ✓ 47 fichiers scannés
|
|
35
|
+
# ✓ 12 fichiers avec blocs @ai-*
|
|
36
|
+
# ✗ 35 fichiers sans docs
|
|
37
|
+
#
|
|
38
|
+
# ✓ .codemod/ai-knowledge-base.json mis à jour
|
|
39
|
+
# ✓ AGENTS.md mis à jour
|
|
40
|
+
|
|
41
|
+
# Preview generated blocks without writing anything:
|
|
42
|
+
npx aidoc-kit scan --dry
|
|
43
|
+
|
|
44
|
+
# Write missing blocks to files (asks confirmation):
|
|
45
|
+
npx aidoc-kit scan --write
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## What gets generated
|
|
49
|
+
|
|
50
|
+
### On each file (auto-generated block)
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
/**
|
|
54
|
+
* @ai-agent auth-expert
|
|
55
|
+
* @ai-runtime CLIENT UNIQUEMENT
|
|
56
|
+
*
|
|
57
|
+
* @ai-context
|
|
58
|
+
* [GÉNÉRÉ] Ce fichier exporte : AuthProvider, useAuthContext
|
|
59
|
+
* Importé dans : src/app/layout.tsx, src/hooks/use-auth.ts
|
|
60
|
+
*
|
|
61
|
+
* @ai-when-modifying
|
|
62
|
+
* 1. Vérifier les fichiers en cascade ci-dessous
|
|
63
|
+
* 2. Lancer @ai-validate après toute modification
|
|
64
|
+
*
|
|
65
|
+
* @ai-cascade
|
|
66
|
+
* → src/hooks/use-auth.ts
|
|
67
|
+
* → src/lib/types.ts
|
|
68
|
+
*
|
|
69
|
+
* @ai-validate
|
|
70
|
+
* npm run typecheck
|
|
71
|
+
*/
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### At project root
|
|
75
|
+
|
|
76
|
+
- `AGENTS.md` — entry point for any AI agent working on the project
|
|
77
|
+
- `.codemod/ai-knowledge-base.json` — full indexed knowledge base (agents, cascade graph, runtime map)
|
|
78
|
+
|
|
79
|
+
## Supported `@ai-*` tags
|
|
80
|
+
|
|
81
|
+
| Tag | Description |
|
|
82
|
+
|-----|-------------|
|
|
83
|
+
| `@ai-agent` | Domain expert responsible for this file |
|
|
84
|
+
| `@ai-agents-related` | Other agents to consult when modifying |
|
|
85
|
+
| `@ai-runtime` | `CLIENT UNIQUEMENT` / `SERVER UNIQUEMENT` / `UNIVERSEL` |
|
|
86
|
+
| `@ai-context` | What this file does and exposes |
|
|
87
|
+
| `@ai-when-modifying` | Rules to follow when editing |
|
|
88
|
+
| `@ai-always` | Invariants that must always be respected |
|
|
89
|
+
| `@ai-never` | Absolute prohibitions |
|
|
90
|
+
| `@ai-pattern` | Correct usage code example |
|
|
91
|
+
| `@ai-cascade` | Files that must be checked after modifying this one |
|
|
92
|
+
| `@ai-validate` | Command to run after modifications |
|
|
93
|
+
|
|
94
|
+
## Runtime auto-detection
|
|
95
|
+
|
|
96
|
+
aidoc-kit detects the execution environment automatically:
|
|
97
|
+
|
|
98
|
+
| Signal | Runtime |
|
|
99
|
+
|--------|---------|
|
|
100
|
+
| `'use client'` directive | `CLIENT UNIQUEMENT` |
|
|
101
|
+
| `'use server'` directive | `SERVER UNIQUEMENT` |
|
|
102
|
+
| `firebase-admin` import | `SERVER UNIQUEMENT` |
|
|
103
|
+
| `useState` / `useEffect` usage | `CLIENT UNIQUEMENT` |
|
|
104
|
+
| None of the above | `UNIVERSEL` |
|
|
105
|
+
|
|
106
|
+
## Agent inference
|
|
107
|
+
|
|
108
|
+
The responsible agent is inferred from imports and file path, in priority order:
|
|
109
|
+
|
|
110
|
+
1. **Custom rules** from `aidoc.config.js` — your project-specific mappings
|
|
111
|
+
2. **Built-in rules** — `firebase-admin` → `firebase-admin-expert`, `stripe` → `billing-expert`, `@tanstack/react-query` → `data-fetching-expert`, `zustand` → `state-expert`, `react-hook-form` → `forms-expert`, …
|
|
112
|
+
3. **Path heuristics** — `/hooks/` → `hooks-expert`, `/api/` → `api-expert`, `/components/` → `ui-expert`, …
|
|
113
|
+
|
|
114
|
+
## Configuration
|
|
115
|
+
|
|
116
|
+
Create `aidoc.config.js` at your project root:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
// aidoc.config.js
|
|
120
|
+
module.exports = {
|
|
121
|
+
agents: {
|
|
122
|
+
'firebase-admin': 'firebase-admin-expert',
|
|
123
|
+
'stripe': 'billing-expert',
|
|
124
|
+
'@/lib/permissions': 'permissions-expert',
|
|
125
|
+
},
|
|
126
|
+
ignore: ['src/generated/', '*.test.ts', '*.spec.ts'],
|
|
127
|
+
validate: 'npm run typecheck',
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Or as JSON:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"agents": { "stripe": "billing-expert" },
|
|
136
|
+
"ignore": ["src/generated/"],
|
|
137
|
+
"validate": "npm run typecheck"
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Large file chunking
|
|
142
|
+
|
|
143
|
+
AI agents often truncate large files — reading only the first 50-100 lines of a 500-line file. `aidoc-kit chunk` solves this by pre-summarising every file over 150 lines into a structured Markdown file inside `.codemod/chunks/`.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npx aidoc-kit chunk
|
|
147
|
+
# 📦 src/contexts/auth-context.tsx (320 lignes)
|
|
148
|
+
# 📦 src/lib/firebase.ts (210 lignes)
|
|
149
|
+
#
|
|
150
|
+
# ✓ 2 fichier(s) chunkés → .codemod/chunks/
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Each generated chunk looks like:
|
|
154
|
+
|
|
155
|
+
```markdown
|
|
156
|
+
# Chunk — src/contexts/auth-context.tsx
|
|
157
|
+
> 320 lignes — généré par aidoc-kit chunk
|
|
158
|
+
|
|
159
|
+
## Exports publics
|
|
160
|
+
- `AuthProvider` (function) — ligne 45
|
|
161
|
+
- `AuthContextType` (interface) — ligne 12
|
|
162
|
+
- `useAuthContext` (const) — ligne 89
|
|
163
|
+
|
|
164
|
+
## Imports critiques
|
|
165
|
+
- firebase/auth (getAuth, onAuthStateChanged, signInWithEmailAndPassword)
|
|
166
|
+
- react (createContext, useContext, useState, useEffect)
|
|
167
|
+
- @/lib/types (User, UserRole)
|
|
168
|
+
|
|
169
|
+
## Structure du fichier
|
|
170
|
+
- L. 1-50 : imports
|
|
171
|
+
- L. 51-100 : types et interfaces
|
|
172
|
+
- L. 101-150 : hooks React
|
|
173
|
+
- L. 151-200 : fonctions asynchrones
|
|
174
|
+
- L. 201-320 : rendu JSX
|
|
175
|
+
|
|
176
|
+
## Fonctions et méthodes
|
|
177
|
+
| Nom | Lignes | Paramètres | Retour |
|
|
178
|
+
|-----|--------|------------|--------|
|
|
179
|
+
| AuthProvider | 45-180 | children: ReactNode | JSX.Element |
|
|
180
|
+
| useAuthContext | 181-200 | — | AuthContextType |
|
|
181
|
+
|
|
182
|
+
## Importé par
|
|
183
|
+
- `src/app/layout.tsx`
|
|
184
|
+
- `src/hooks/use-auth.ts`
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Tell your AI agent: *"Read `.codemod/chunks/` before modifying any large file."*
|
|
188
|
+
|
|
189
|
+
## CLI reference
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
aidoc-kit — AI-native documentation toolkit
|
|
193
|
+
|
|
194
|
+
Commands:
|
|
195
|
+
scan Scan a project and build the knowledge base
|
|
196
|
+
--path <dir> Project root (default: .)
|
|
197
|
+
--write Write missing @ai-* blocks (interactive confirmation)
|
|
198
|
+
--dry Preview generated blocks without writing anything
|
|
199
|
+
|
|
200
|
+
chunk Summarise large files (≥150 lines) into .codemod/chunks/*.md
|
|
201
|
+
--path <dir> Project root (default: .)
|
|
202
|
+
|
|
203
|
+
run Apply transformation rules
|
|
204
|
+
--path <dir> Project root (default: .)
|
|
205
|
+
--dry Preview changes without writing
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Reverse dependency graph
|
|
209
|
+
|
|
210
|
+
One of aidoc-kit's core features is the **reverse import map**: for every file, it resolves which other files import it (via the TypeScript AST, not text search). This powers the accurate `@ai-cascade` section — when you touch `types.ts`, the agent knows exactly which 4 files depend on it.
|
|
211
|
+
|
|
212
|
+
## Why zero dependencies?
|
|
213
|
+
|
|
214
|
+
aidoc-kit uses only Node.js built-ins + the TypeScript compiler already installed in every TypeScript project. No extra packages, no version conflicts, no supply chain risk.
|
|
215
|
+
|
|
216
|
+
## Roadmap
|
|
217
|
+
|
|
218
|
+
- [ ] VS Code extension with `@ai-*` tag highlighting and hover docs
|
|
219
|
+
- [ ] MCP server to expose scan/transform as AI agent tools
|
|
220
|
+
- [ ] `--watch` mode (rebuild knowledge base on save)
|
|
221
|
+
- [ ] Public rules registry for popular lib migrations (Next.js, Firebase, Stripe)
|
|
222
|
+
- [ ] CI/CD integration to validate `@ai-*` coverage on each PR
|
|
223
|
+
- [ ] HTML visual report of the dependency graph
|
|
224
|
+
|
|
225
|
+
## Contributing
|
|
226
|
+
|
|
227
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
228
|
+
|
|
229
|
+
## Author
|
|
230
|
+
|
|
231
|
+
Made by [Clément Tournier](https://github.com/Clemsrec)
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_readline_1 = require("node:readline");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const scanner_1 = require("./core/scanner");
|
|
8
|
+
const transformer_1 = require("./core/transformer");
|
|
9
|
+
const writer_1 = require("./core/writer");
|
|
10
|
+
const chunker_1 = require("./core/chunker");
|
|
11
|
+
const config_1 = require("./core/config");
|
|
12
|
+
const index_1 = require("./rules/index");
|
|
13
|
+
// ─── Arg helpers ───────────────────────────────────────────────────────────
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const command = args[0];
|
|
16
|
+
function getFlag(flag) {
|
|
17
|
+
const idx = args.indexOf(flag);
|
|
18
|
+
return idx !== -1 ? args[idx + 1] : undefined;
|
|
19
|
+
}
|
|
20
|
+
function hasFlag(flag) {
|
|
21
|
+
return args.includes(flag);
|
|
22
|
+
}
|
|
23
|
+
// ─── Confirmation prompt ───────────────────────────────────────────────────
|
|
24
|
+
function confirm(message) {
|
|
25
|
+
// Non-interactive (piped input, CI) → proceed without prompt
|
|
26
|
+
if (!process.stdin.isTTY)
|
|
27
|
+
return Promise.resolve(true);
|
|
28
|
+
return new Promise(resolve => {
|
|
29
|
+
const rl = (0, node_readline_1.createInterface)({ input: process.stdin, output: process.stdout });
|
|
30
|
+
rl.question(`${message} (y/n) `, answer => {
|
|
31
|
+
rl.close();
|
|
32
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// ─── scan ──────────────────────────────────────────────────────────────────
|
|
37
|
+
async function cmdScan() {
|
|
38
|
+
const projectRoot = (0, node_path_1.resolve)(getFlag('--path') ?? '.');
|
|
39
|
+
const write = hasFlag('--write');
|
|
40
|
+
const dry = hasFlag('--dry');
|
|
41
|
+
const config = (0, config_1.loadConfig)(projectRoot);
|
|
42
|
+
console.log(`\naidoc-kit scan → ${projectRoot}\n`);
|
|
43
|
+
const result = (0, scanner_1.scanProject)(projectRoot);
|
|
44
|
+
// Filter out files matching config.ignore patterns
|
|
45
|
+
const ignorePatterns = config.ignore ?? [];
|
|
46
|
+
const filteredWithoutDocs = ignorePatterns.length > 0
|
|
47
|
+
? result.filesWithoutDocs.filter(f => !(0, config_1.isIgnored)(f, ignorePatterns))
|
|
48
|
+
: result.filesWithoutDocs;
|
|
49
|
+
console.log(`✓ ${result.totalScanned} fichiers scannés`);
|
|
50
|
+
console.log(`✓ ${result.docs.length} fichiers avec blocs @ai-*`);
|
|
51
|
+
console.log(`✗ ${filteredWithoutDocs.length} fichiers sans docs${ignorePatterns.length > 0 ? ' (après filtres ignore)' : ''}\n`);
|
|
52
|
+
if (filteredWithoutDocs.length > 0 && (write || dry)) {
|
|
53
|
+
// Build reverse import map once for all files
|
|
54
|
+
const allAbsFiles = [
|
|
55
|
+
...result.docs.map(d => (0, node_path_1.resolve)(projectRoot, d.file)),
|
|
56
|
+
...filteredWithoutDocs.map(f => (0, node_path_1.resolve)(projectRoot, f)),
|
|
57
|
+
];
|
|
58
|
+
const reverseMap = (0, scanner_1.buildReverseImportMap)(allAbsFiles);
|
|
59
|
+
if (dry) {
|
|
60
|
+
console.log('Aperçu des blocs générés :');
|
|
61
|
+
for (const relFile of filteredWithoutDocs) {
|
|
62
|
+
const absFile = (0, node_path_1.resolve)(projectRoot, relFile);
|
|
63
|
+
const importers = (reverseMap.get(absFile) ?? []).map(f => (0, node_path_1.relative)(projectRoot, f));
|
|
64
|
+
const block = (0, transformer_1.generateAiDocBlock)(absFile, importers, config);
|
|
65
|
+
if (block)
|
|
66
|
+
console.log(`\n── ${relFile} ──\n${block}`);
|
|
67
|
+
}
|
|
68
|
+
console.log();
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Ask confirmation before writing
|
|
72
|
+
const ok = await confirm(`→ Écrire les blocs @ai-* dans ${filteredWithoutDocs.length} fichier(s) ?`);
|
|
73
|
+
if (!ok) {
|
|
74
|
+
console.log('Annulé.');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
console.log();
|
|
78
|
+
for (const relFile of filteredWithoutDocs) {
|
|
79
|
+
const absFile = (0, node_path_1.resolve)(projectRoot, relFile);
|
|
80
|
+
const importers = (reverseMap.get(absFile) ?? []).map(f => (0, node_path_1.relative)(projectRoot, f));
|
|
81
|
+
const block = (0, transformer_1.generateAiDocBlock)(absFile, importers, config);
|
|
82
|
+
if (!block)
|
|
83
|
+
continue;
|
|
84
|
+
(0, writer_1.writeDocBlock)(absFile, block);
|
|
85
|
+
console.log(` ✓ ${relFile}`);
|
|
86
|
+
}
|
|
87
|
+
console.log();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!dry) {
|
|
91
|
+
(0, writer_1.writeKnowledgeBase)(result, projectRoot);
|
|
92
|
+
(0, writer_1.writeAgentsMd)(result, projectRoot);
|
|
93
|
+
console.log('✓ .codemod/ai-knowledge-base.json mis à jour');
|
|
94
|
+
console.log('✓ AGENTS.md mis à jour');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// ─── run ───────────────────────────────────────────────────────────────────
|
|
98
|
+
function cmdRun() {
|
|
99
|
+
const projectRoot = (0, node_path_1.resolve)(getFlag('--path') ?? '.');
|
|
100
|
+
const dry = hasFlag('--dry');
|
|
101
|
+
console.log(`\naidoc-kit run → ${projectRoot}\n`);
|
|
102
|
+
const result = (0, scanner_1.scanProject)(projectRoot);
|
|
103
|
+
let changedCount = 0;
|
|
104
|
+
for (const doc of result.docs) {
|
|
105
|
+
const absFile = (0, node_path_1.resolve)(projectRoot, doc.file);
|
|
106
|
+
const { changed, source } = (0, transformer_1.applyRules)(absFile, index_1.defaultRules);
|
|
107
|
+
if (changed) {
|
|
108
|
+
if (!dry)
|
|
109
|
+
(0, node_fs_1.writeFileSync)(absFile, source, 'utf-8');
|
|
110
|
+
console.log(` ${dry ? '[dry]' : '✓'} ${doc.file}`);
|
|
111
|
+
changedCount++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (changedCount === 0) {
|
|
115
|
+
console.log('Aucune transformation à appliquer.');
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log(`\n${dry ? '[dry]' : '✓'} ${changedCount} fichier(s) transformé(s)`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// ─── chunk ────────────────────────────────────────────────────────────────
|
|
122
|
+
async function cmdChunk() {
|
|
123
|
+
const projectRoot = (0, node_path_1.resolve)(getFlag('--path') ?? '.');
|
|
124
|
+
console.log(`\naidoc-kit chunk → ${projectRoot}\n`);
|
|
125
|
+
const allFiles = (0, scanner_1.walkDir)(projectRoot);
|
|
126
|
+
const reverseMap = (0, scanner_1.buildReverseImportMap)(allFiles);
|
|
127
|
+
const codemodDir = (0, node_path_1.join)(projectRoot, '.codemod');
|
|
128
|
+
let chunked = 0;
|
|
129
|
+
for (const filePath of allFiles) {
|
|
130
|
+
const importedBy = reverseMap.get(filePath) ?? [];
|
|
131
|
+
const chunk = (0, chunker_1.chunkFile)(filePath, importedBy, projectRoot);
|
|
132
|
+
if (chunk) {
|
|
133
|
+
(0, chunker_1.writeChunk)(chunk, codemodDir);
|
|
134
|
+
console.log(` 📦 ${chunk.filePath} (${chunk.totalLines} lignes)`);
|
|
135
|
+
chunked++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (chunked === 0) {
|
|
139
|
+
console.log(`Aucun fichier ne dépasse le seuil (150 lignes). Chunking non nécessaire.`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.log(`\n✓ ${chunked} fichier(s) chunkés → .codemod/chunks/`);
|
|
143
|
+
console.log('💡 Dis à ton agent : "Lis .codemod/chunks/ avant de modifier un gros fichier"');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// ─── help ──────────────────────────────────────────────────────────────────
|
|
147
|
+
function printHelp() {
|
|
148
|
+
console.log(`
|
|
149
|
+
aidoc-kit — AI-native documentation toolkit
|
|
150
|
+
|
|
151
|
+
Commandes :
|
|
152
|
+
scan Scanner un projet et construire la knowledge base
|
|
153
|
+
--path <dir> Dossier racine (défaut: .)
|
|
154
|
+
--write Écrire les blocs @ai-* manquants (confirmation interactive)
|
|
155
|
+
--dry Afficher les blocs générés sans modifier les fichiers
|
|
156
|
+
|
|
157
|
+
run Appliquer les règles de transformation
|
|
158
|
+
--path <dir> Dossier racine (défaut: .)
|
|
159
|
+
--dry Afficher les changements sans modifier les fichiers
|
|
160
|
+
|
|
161
|
+
Config :
|
|
162
|
+
Créer un fichier aidoc.config.js (ou .json) à la racine du projet :
|
|
163
|
+
|
|
164
|
+
module.exports = {
|
|
165
|
+
agents: { '@/lib/permissions': 'permissions-expert', 'stripe': 'billing-expert' },
|
|
166
|
+
ignore: ['src/generated/**', '**/*.test.ts'],
|
|
167
|
+
validate: 'npm run typecheck',
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
Exemples :
|
|
171
|
+
npx aidoc-kit scan
|
|
172
|
+
npx aidoc-kit scan --path ./src --dry
|
|
173
|
+
npx aidoc-kit scan --write
|
|
174
|
+
npx aidoc-kit run --dry
|
|
175
|
+
npx aidoc-kit chunk
|
|
176
|
+
npx aidoc-kit chunk --path ./src
|
|
177
|
+
`);
|
|
178
|
+
}
|
|
179
|
+
// ─── Dispatch ─────────────────────────────────────────────────────────────
|
|
180
|
+
switch (command) {
|
|
181
|
+
case 'scan':
|
|
182
|
+
cmdScan().catch((err) => {
|
|
183
|
+
console.error(err);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
});
|
|
186
|
+
break;
|
|
187
|
+
case 'run':
|
|
188
|
+
cmdRun();
|
|
189
|
+
break;
|
|
190
|
+
case 'chunk':
|
|
191
|
+
cmdChunk().catch((err) => {
|
|
192
|
+
console.error(err);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
});
|
|
195
|
+
break;
|
|
196
|
+
default:
|
|
197
|
+
printHelp();
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface FileChunk {
|
|
2
|
+
filePath: string;
|
|
3
|
+
totalLines: number;
|
|
4
|
+
exports: ExportInfo[];
|
|
5
|
+
imports: ImportInfo[];
|
|
6
|
+
functions: FunctionInfo[];
|
|
7
|
+
structure: StructureBlock[];
|
|
8
|
+
importedBy: string[];
|
|
9
|
+
}
|
|
10
|
+
interface ExportInfo {
|
|
11
|
+
name: string;
|
|
12
|
+
kind: 'function' | 'interface' | 'type' | 'const' | 'class';
|
|
13
|
+
line: number;
|
|
14
|
+
}
|
|
15
|
+
interface ImportInfo {
|
|
16
|
+
from: string;
|
|
17
|
+
names: string[];
|
|
18
|
+
}
|
|
19
|
+
interface FunctionInfo {
|
|
20
|
+
name: string;
|
|
21
|
+
startLine: number;
|
|
22
|
+
endLine: number;
|
|
23
|
+
params: string;
|
|
24
|
+
returnType: string;
|
|
25
|
+
}
|
|
26
|
+
interface StructureBlock {
|
|
27
|
+
description: string;
|
|
28
|
+
startLine: number;
|
|
29
|
+
endLine: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse a source file and return a structured chunk summary.
|
|
33
|
+
* Returns null if the file has fewer lines than CHUNK_THRESHOLD.
|
|
34
|
+
*/
|
|
35
|
+
export declare function chunkFile(filePath: string, importedBy: string[], rootDir: string): FileChunk | null;
|
|
36
|
+
/**
|
|
37
|
+
* Serialise a FileChunk to a Markdown file under `<outputDir>/chunks/<filePath>.md`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function writeChunk(chunk: FileChunk, outputDir: string): void;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=chunker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunker.d.ts","sourceRoot":"","sources":["../../src/core/chunker.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,SAAS,EAAE,YAAY,EAAE,CAAA;IACzB,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;IAC3D,IAAI,EAAE,MAAM,CAAA;CACb;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,UAAU,cAAc;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB;AAID;;;GAGG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAAE,EACpB,OAAO,EAAE,MAAM,GACd,SAAS,GAAG,IAAI,CAyGlB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAqDpE"}
|