complexity-cli 1.0.0 → 1.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.
Files changed (54) hide show
  1. package/README.md +14 -6
  2. package/dist/index.js +487 -46
  3. package/package.json +10 -6
  4. package/dist/commands/add.d.ts +0 -4
  5. package/dist/commands/add.d.ts.map +0 -1
  6. package/dist/commands/add.js +0 -109
  7. package/dist/commands/add.js.map +0 -1
  8. package/dist/commands/init.d.ts +0 -5
  9. package/dist/commands/init.d.ts.map +0 -1
  10. package/dist/commands/init.js +0 -85
  11. package/dist/commands/init.js.map +0 -1
  12. package/dist/commands/list.d.ts +0 -2
  13. package/dist/commands/list.d.ts.map +0 -1
  14. package/dist/commands/list.js +0 -58
  15. package/dist/commands/list.js.map +0 -1
  16. package/dist/commands/prompt.d.ts +0 -2
  17. package/dist/commands/prompt.d.ts.map +0 -1
  18. package/dist/commands/prompt.js +0 -38
  19. package/dist/commands/prompt.js.map +0 -1
  20. package/dist/commands/remove.d.ts +0 -4
  21. package/dist/commands/remove.d.ts.map +0 -1
  22. package/dist/commands/remove.js +0 -86
  23. package/dist/commands/remove.js.map +0 -1
  24. package/dist/commands/update.d.ts +0 -6
  25. package/dist/commands/update.d.ts.map +0 -1
  26. package/dist/commands/update.js +0 -125
  27. package/dist/commands/update.js.map +0 -1
  28. package/dist/core/generator.d.ts +0 -5
  29. package/dist/core/generator.d.ts.map +0 -1
  30. package/dist/core/generator.js +0 -96
  31. package/dist/core/generator.js.map +0 -1
  32. package/dist/core/parser.d.ts +0 -4
  33. package/dist/core/parser.d.ts.map +0 -1
  34. package/dist/core/parser.js +0 -144
  35. package/dist/core/parser.js.map +0 -1
  36. package/dist/core/validator.d.ts +0 -6
  37. package/dist/core/validator.d.ts.map +0 -1
  38. package/dist/core/validator.js +0 -22
  39. package/dist/core/validator.js.map +0 -1
  40. package/dist/index.d.ts +0 -3
  41. package/dist/index.d.ts.map +0 -1
  42. package/dist/index.js.map +0 -1
  43. package/dist/utils/error-helpers.d.ts +0 -4
  44. package/dist/utils/error-helpers.d.ts.map +0 -1
  45. package/dist/utils/error-helpers.js +0 -26
  46. package/dist/utils/error-helpers.js.map +0 -1
  47. package/dist/utils/prompts.d.ts +0 -3
  48. package/dist/utils/prompts.d.ts.map +0 -1
  49. package/dist/utils/prompts.js +0 -37
  50. package/dist/utils/prompts.js.map +0 -1
  51. package/dist/utils/types.d.ts +0 -17
  52. package/dist/utils/types.d.ts.map +0 -1
  53. package/dist/utils/types.js +0 -3
  54. package/dist/utils/types.js.map +0 -1
package/README.md CHANGED
@@ -1,25 +1,29 @@
1
1
  # complexity-cli
2
2
 
3
+ [![npm package](https://img.shields.io/npm/v/complexity-cli.svg)](https://www.npmjs.com/package/complexity-cli)
4
+
3
5
  A CLI tool for managing project complexity and knowledge maps. Perfect for
4
6
  tracking what developers need to know to work on your project, with AI-friendly
5
7
  workflows.
6
8
 
7
9
  ## Installation
8
10
 
11
+ ```bash
12
+ npm install -g complexity-cli
13
+ ```
14
+
9
15
  ### Local Development
10
16
 
17
+ If you want to contribute or develop locally:
18
+
11
19
  ```bash
20
+ git clone https://github.com/SamBWagner/complexity-cli.git
21
+ cd complexity-cli
12
22
  npm install
13
23
  npm run build
14
24
  npm link
15
25
  ```
16
26
 
17
- ### From npm (Coming Soon)
18
-
19
- ```bash
20
- npm install -g complexity-cli
21
- ```
22
-
23
27
  ## Usage
24
28
 
25
29
  ### Initialize a Complexity Map
@@ -112,6 +116,10 @@ This outputs a comprehensive prompt that instructs an AI to:
112
116
  - Use the `complexity` CLI to maintain consistency
113
117
  - Consider all relevant technologies and concepts
114
118
 
119
+ > Note: I really recommend using a larger, thinking AI model like Claude Opus or
120
+ > GPT 5.x. This tool's duty isn't the analysis of the project. But, rather the
121
+ > maintenance of a simple markdown document. Bigger model, better time.
122
+
115
123
  ## Features
116
124
 
117
125
  - **Simple CLI**: Easy-to-use commands for managing complexity documentation
package/dist/index.js CHANGED
@@ -1,48 +1,489 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- const commander_1 = require("commander");
5
- const init_1 = require("./commands/init");
6
- const add_1 = require("./commands/add");
7
- const remove_1 = require("./commands/remove");
8
- const update_1 = require("./commands/update");
9
- const list_1 = require("./commands/list");
10
- const prompt_1 = require("./commands/prompt");
11
- const program = new commander_1.Command();
12
- program
13
- .name('complexity')
14
- .description('A CLI tool for managing project complexity and knowledge maps')
15
- .version('1.0.0');
16
- program
17
- .command('init')
18
- .description('Initialize a COMPLEXITY.md file in the current directory')
19
- .option('-n, --name <name>', 'Project name')
20
- .option('-f, --force', 'Overwrite existing file')
21
- .action(init_1.initCommand);
22
- program
23
- .command('add <concept> [level] [area]')
24
- .description('Add a concept to the complexity map')
25
- .option('-l, --level <level>', 'Criticality level (1-3)')
26
- .action(add_1.addCommand);
27
- program
28
- .command('remove <concept>')
29
- .description('Remove a concept from the complexity map')
30
- .option('-f, --force', 'Skip confirmation')
31
- .action(remove_1.removeCommand);
32
- program
33
- .command('update <concept>')
34
- .description('Update an existing concept')
35
- .option('-l, --level <level>', 'New criticality level (1-3)')
36
- .option('-a, --area <area>', 'New area')
37
- .option('-n, --name <name>', 'New concept name')
38
- .action(update_1.updateCommand);
39
- program
40
- .command('list')
41
- .description('List all concepts in the complexity map')
42
- .action(list_1.listCommand);
43
- program
44
- .command('prompt')
45
- .description('Display the AI prompt for complexity analysis')
46
- .action(prompt_1.promptCommand);
2
+ import { Command } from "commander";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import * as fs from "fs";
6
+ import * as path$1 from "path";
7
+ import chalk from "chalk";
8
+ import inquirer from "inquirer";
9
+
10
+ //#region node_modules/tsdown/esm-shims.js
11
+ const getFilename = () => fileURLToPath(import.meta.url);
12
+ const getDirname = () => path.dirname(getFilename());
13
+ const __dirname = /* @__PURE__ */ getDirname();
14
+
15
+ //#endregion
16
+ //#region src/utils/prompts.ts
17
+ async function confirmAction(message) {
18
+ const { confirmed } = await inquirer.prompt([{
19
+ type: "confirm",
20
+ name: "confirmed",
21
+ message,
22
+ default: false
23
+ }]);
24
+ return confirmed;
25
+ }
26
+ async function promptForInput(message, defaultValue) {
27
+ const { value } = await inquirer.prompt([{
28
+ type: "input",
29
+ name: "value",
30
+ message,
31
+ default: defaultValue,
32
+ validate: (input) => {
33
+ if (!input || input.trim() === "") return "This field is required";
34
+ return true;
35
+ }
36
+ }]);
37
+ return value;
38
+ }
39
+
40
+ //#endregion
41
+ //#region src/utils/error-helpers.ts
42
+ function exitWithError(message, hint) {
43
+ console.error(chalk.red(`Error: ${message}`));
44
+ if (hint) console.error(chalk.yellow(hint));
45
+ process.exit(1);
46
+ }
47
+ function exitWithUsageError(message, usageExample) {
48
+ console.error(chalk.red(`Error: ${message}`));
49
+ console.error(chalk.yellow(`Usage: ${usageExample}`));
50
+ process.exit(1);
51
+ }
52
+ function exitCancelled() {
53
+ console.log(chalk.yellow("Cancelled."));
54
+ process.exit(0);
55
+ }
56
+
57
+ //#endregion
58
+ //#region src/commands/init.ts
59
+ const TEMPLATE_FILE = path$1.join(__dirname, "../Complexity.template.md");
60
+ const OUTPUT_FILE = "COMPLEXITY.md";
61
+ function getOutputPath() {
62
+ return path$1.join(process.cwd(), OUTPUT_FILE);
63
+ }
64
+ function validateFileDoesNotExist(outputPath, force) {
65
+ if (fs.existsSync(outputPath) && !force) exitWithError(`${OUTPUT_FILE} already exists in this directory.`, "Use --force to overwrite it.");
66
+ }
67
+ async function getProjectName(providedName) {
68
+ if (providedName) return providedName;
69
+ return promptForInput("Enter project name:", path$1.basename(process.cwd()));
70
+ }
71
+ function readTemplateFile() {
72
+ if (!fs.existsSync(TEMPLATE_FILE)) exitWithError(`Template file not found at ${TEMPLATE_FILE}`);
73
+ return fs.readFileSync(TEMPLATE_FILE, "utf-8");
74
+ }
75
+ function initializeTemplateContent(template, projectName) {
76
+ return template.replace(/\{Project Name\}/g, projectName).replace(/\{X\}/g, "0").replace(/\{Y\}/g, "0").replace(/\{A\}/g, "0").replace(/\{B\}/g, "0").replace(/\{C\}/g, "0");
77
+ }
78
+ async function initCommand(options) {
79
+ const outputPath = getOutputPath();
80
+ validateFileDoesNotExist(outputPath, options.force ?? false);
81
+ const projectName = await getProjectName(options.name);
82
+ const content = initializeTemplateContent(readTemplateFile(), projectName);
83
+ fs.writeFileSync(outputPath, content, "utf-8");
84
+ console.log(chalk.green(`✓ Created ${OUTPUT_FILE} for project: ${projectName}`));
85
+ }
86
+
87
+ //#endregion
88
+ //#region src/core/parser.ts
89
+ const COMPLEXITY_FILE = "COMPLEXITY.md";
90
+ const BLANK_AREA$2 = "___";
91
+ const DEFAULT_PROJECT_NAME = "Project Name";
92
+ function extractProjectName(lines) {
93
+ const projectNameLine = lines[3];
94
+ if (!projectNameLine) return DEFAULT_PROJECT_NAME;
95
+ const match = projectNameLine.match(/A guide to what you need to know to work on (.+?), broken/);
96
+ return match ? match[1] : DEFAULT_PROJECT_NAME;
97
+ }
98
+ function findTableStartIndex(lines) {
99
+ for (let i = 0; i < lines.length; i++) if (lines[i].trim().startsWith("| Topic | Area | Criticality")) return i + 2;
100
+ return -1;
101
+ }
102
+ function isValidCriticality(criticality) {
103
+ return !isNaN(criticality) && criticality >= 1 && criticality <= 3;
104
+ }
105
+ function parseTableRow(line) {
106
+ const parts = line.split("|").map((p) => p.trim()).filter((p) => p);
107
+ if (parts.length !== 3) return null;
108
+ const [topic, area, criticalityStr] = parts;
109
+ if (!topic || topic === "Topic") return null;
110
+ const criticality = parseInt(criticalityStr);
111
+ if (!isValidCriticality(criticality)) return null;
112
+ return {
113
+ topic,
114
+ area: area || BLANK_AREA$2,
115
+ criticality
116
+ };
117
+ }
118
+ function parseTableRows(lines, startIndex) {
119
+ if (startIndex === -1) return [];
120
+ const concepts = [];
121
+ for (let i = startIndex; i < lines.length; i++) {
122
+ const line = lines[i].trim();
123
+ if (!line.startsWith("|")) break;
124
+ const concept = parseTableRow(line);
125
+ if (concept) concepts.push(concept);
126
+ }
127
+ return concepts;
128
+ }
129
+ function parseComplexityFile(filePath) {
130
+ if (!fs.existsSync(filePath)) throw new Error(`COMPLEXITY.md not found at: ${filePath}`);
131
+ const lines = fs.readFileSync(filePath, "utf-8").split("\n");
132
+ return {
133
+ projectName: extractProjectName(lines),
134
+ concepts: parseTableRows(lines, findTableStartIndex(lines))
135
+ };
136
+ }
137
+ function findGitRoot(startDir) {
138
+ let searchDir = startDir;
139
+ const filesystemRoot = path$1.parse(searchDir).root;
140
+ while (searchDir !== filesystemRoot) {
141
+ if (fs.existsSync(path$1.join(searchDir, ".git"))) return searchDir;
142
+ searchDir = path$1.dirname(searchDir);
143
+ }
144
+ return null;
145
+ }
146
+ function searchUpwardForFile(startDir, filename, stopAtDir) {
147
+ let currentDir = startDir;
148
+ while (true) {
149
+ const filePath = path$1.join(currentDir, filename);
150
+ if (fs.existsSync(filePath)) return filePath;
151
+ if (currentDir === stopAtDir) break;
152
+ const parentDir = path$1.dirname(currentDir);
153
+ if (parentDir === currentDir) break;
154
+ currentDir = parentDir;
155
+ }
156
+ return null;
157
+ }
158
+ function findComplexityFile(startDir = process.cwd()) {
159
+ const resolvedStartDir = path$1.resolve(startDir);
160
+ return searchUpwardForFile(resolvedStartDir, COMPLEXITY_FILE, findGitRoot(resolvedStartDir) || path$1.parse(resolvedStartDir).root);
161
+ }
162
+
163
+ //#endregion
164
+ //#region src/core/generator.ts
165
+ function calculateStats(concepts) {
166
+ const uniqueAreas = new Set(concepts.map((c) => c.area).filter((a) => a && a !== "___"));
167
+ return {
168
+ totalConcepts: concepts.length,
169
+ totalAreas: uniqueAreas.size,
170
+ criticalCount: concepts.filter((c) => c.criticality === 3).length,
171
+ importantCount: concepts.filter((c) => c.criticality === 2).length,
172
+ situationalCount: concepts.filter((c) => c.criticality === 1).length
173
+ };
174
+ }
175
+ function sortConcepts(concepts) {
176
+ return [...concepts].sort((a, b) => {
177
+ if (a.criticality !== b.criticality) return b.criticality - a.criticality;
178
+ return a.topic.localeCompare(b.topic);
179
+ });
180
+ }
181
+ function generateMarkdown(projectName, concepts) {
182
+ const stats = calculateStats(concepts);
183
+ const sorted = sortConcepts(concepts);
184
+ const critical = sorted.filter((c) => c.criticality === 3);
185
+ const important = sorted.filter((c) => c.criticality === 2);
186
+ const situational = sorted.filter((c) => c.criticality === 1);
187
+ const lines = [];
188
+ lines.push("# Project Complexity & Knowledge Map");
189
+ lines.push("");
190
+ lines.push(`<!-- Replace {Project Name} with your project name -->`);
191
+ lines.push(`A guide to what you need to know to work on ${projectName}, broken into three tiers.`);
192
+ lines.push("");
193
+ lines.push(`> **${stats.totalConcepts} technologies/concepts** across ${stats.totalAreas} areas`);
194
+ lines.push(`> **${stats.criticalCount} critical**`);
195
+ lines.push(`> **${stats.importantCount} important**`);
196
+ lines.push(`> **${stats.situationalCount} situational**`);
197
+ lines.push("");
198
+ lines.push("---");
199
+ lines.push("");
200
+ lines.push("## **What you NEED to know to do any meaningful work**");
201
+ lines.push("");
202
+ if (critical.length > 0) critical.forEach((c) => lines.push(`- ${c.topic}`));
203
+ else lines.push("<!-- Criticality 3 items from the table below -->");
204
+ lines.push("");
205
+ lines.push("## **What you SHOULD know to be very helpful**");
206
+ lines.push("");
207
+ if (important.length > 0) important.forEach((c) => lines.push(`- ${c.topic}`));
208
+ else lines.push("<!-- Everything above, plus Criticality 2 items -->");
209
+ lines.push("");
210
+ lines.push("## **What you should EVENTUALLY learn for specific areas**");
211
+ lines.push("");
212
+ if (situational.length > 0) situational.forEach((c) => lines.push(`- ${c.topic}`));
213
+ else lines.push("<!-- Criticality 1 items -->");
214
+ lines.push("");
215
+ lines.push("---");
216
+ lines.push("");
217
+ lines.push("## Full Reference");
218
+ lines.push("");
219
+ lines.push("<!--");
220
+ lines.push(" Criticality scale:");
221
+ lines.push(" 3 = Can't do meaningful work without it");
222
+ lines.push(" 2 = Will encounter regularly; gaps will slow you down");
223
+ lines.push(" 1 = Comes up occasionally or is abstracted away enough to learn on the job");
224
+ lines.push("-->");
225
+ lines.push("");
226
+ lines.push("| Topic | Area | Criticality (1-3) |");
227
+ lines.push("|---|---|---|");
228
+ if (sorted.length > 0) sorted.forEach((c) => {
229
+ lines.push(`| ${c.topic} | ${c.area} | ${c.criticality} |`);
230
+ });
231
+ else lines.push("| | | |");
232
+ lines.push("");
233
+ return lines.join("\n");
234
+ }
235
+
236
+ //#endregion
237
+ //#region src/core/validator.ts
238
+ function isDuplicate(concept, existingConcepts) {
239
+ return existingConcepts.some((c) => c.topic.toLowerCase() === concept.toLowerCase());
240
+ }
241
+ function isValidLevel(level) {
242
+ const num = parseInt(level);
243
+ return !isNaN(num) && num >= 1 && num <= 3;
244
+ }
245
+ function isExistingArea(area, existingConcepts) {
246
+ if (!area || area === "___") return true;
247
+ return existingConcepts.some((c) => c.area.toLowerCase() === area.toLowerCase());
248
+ }
249
+ function findConcept(conceptName, concepts) {
250
+ return concepts.find((c) => c.topic.toLowerCase() === conceptName.toLowerCase());
251
+ }
252
+
253
+ //#endregion
254
+ //#region src/commands/add.ts
255
+ const BLANK_AREA$1 = "___";
256
+ function getLevelInput(levelArg, options) {
257
+ const levelInput = options.level || levelArg;
258
+ if (!levelInput) exitWithUsageError("Criticality level is required.", "complexity add <concept> <level> [area]\n or: complexity add <concept> --level <level> [area]");
259
+ return levelInput;
260
+ }
261
+ function validateLevel(levelInput) {
262
+ if (!isValidLevel(levelInput)) exitWithError("Level must be 1, 2, or 3.");
263
+ return parseInt(levelInput);
264
+ }
265
+ function requireComplexityFile$3() {
266
+ const filePath = findComplexityFile();
267
+ if (!filePath) exitWithError("COMPLEXITY.md not found.", "Run \"complexity init\" to create one.");
268
+ return filePath;
269
+ }
270
+ function validateConceptDoesNotExist(concept, existingConcepts) {
271
+ if (isDuplicate(concept, existingConcepts)) exitWithError(`Concept "${concept}" already exists.`, "Use \"complexity update\" to modify it.");
272
+ }
273
+ async function confirmNewAreaIfNeeded$1(area, existingConcepts) {
274
+ if (area === BLANK_AREA$1) return;
275
+ if (isExistingArea(area, existingConcepts)) return;
276
+ if (!await confirmAction(`No pre-existing area "${area}" found. Would you still like to add it?`)) exitCancelled();
277
+ }
278
+ function createConcept(concept, level, area) {
279
+ return {
280
+ topic: concept,
281
+ area,
282
+ criticality: level
283
+ };
284
+ }
285
+ function saveComplexityFile$2(filePath, projectName, concepts) {
286
+ const markdown = generateMarkdown(projectName, concepts);
287
+ fs.writeFileSync(filePath, markdown, "utf-8");
288
+ }
289
+ async function addCommand(concept, levelArg, areaArg, options) {
290
+ const level = validateLevel(getLevelInput(levelArg, options));
291
+ const area = areaArg || BLANK_AREA$1;
292
+ const filePath = requireComplexityFile$3();
293
+ const doc = parseComplexityFile(filePath);
294
+ validateConceptDoesNotExist(concept, doc.concepts);
295
+ await confirmNewAreaIfNeeded$1(area, doc.concepts);
296
+ const newConcept = createConcept(concept, level, area);
297
+ const updatedConcepts = [...doc.concepts, newConcept];
298
+ saveComplexityFile$2(filePath, doc.projectName, updatedConcepts);
299
+ console.log(chalk.green(`✓ Added "${concept}" (Level ${level}, Area: ${area})`));
300
+ }
301
+
302
+ //#endregion
303
+ //#region src/commands/remove.ts
304
+ function requireComplexityFile$2() {
305
+ const filePath = findComplexityFile();
306
+ if (!filePath) exitWithError("COMPLEXITY.md not found.", "Run \"complexity init\" to create one.");
307
+ return filePath;
308
+ }
309
+ function requireConceptExists$1(conceptName, concepts) {
310
+ const existing = findConcept(conceptName, concepts);
311
+ if (!existing) exitWithError(`Concept "${conceptName}" not found.`);
312
+ return existing;
313
+ }
314
+ async function confirmRemovalIfNeeded(concept, force) {
315
+ if (force) return;
316
+ if (!await confirmAction(`Remove "${concept.topic}" (Level ${concept.criticality}, Area: ${concept.area})?`)) exitCancelled();
317
+ }
318
+ function removeConcept(conceptName, concepts) {
319
+ return concepts.filter((c) => c.topic.toLowerCase() !== conceptName.toLowerCase());
320
+ }
321
+ function saveComplexityFile$1(filePath, projectName, concepts) {
322
+ const markdown = generateMarkdown(projectName, concepts);
323
+ fs.writeFileSync(filePath, markdown, "utf-8");
324
+ }
325
+ async function removeCommand(concept, options) {
326
+ const filePath = requireComplexityFile$2();
327
+ const doc = parseComplexityFile(filePath);
328
+ const existing = requireConceptExists$1(concept, doc.concepts);
329
+ await confirmRemovalIfNeeded(existing, options.force ?? false);
330
+ const updatedConcepts = removeConcept(concept, doc.concepts);
331
+ saveComplexityFile$1(filePath, doc.projectName, updatedConcepts);
332
+ console.log(chalk.green(`✓ Removed "${existing.topic}"`));
333
+ }
334
+
335
+ //#endregion
336
+ //#region src/commands/update.ts
337
+ const BLANK_AREA = "___";
338
+ function validateAtLeastOneOptionProvided(options) {
339
+ if (!options.level && !options.area && !options.name) exitWithUsageError("At least one option (--level, --area, or --name) is required.", "complexity update <concept> [--level <level>] [--area <area>] [--name <new-name>]");
340
+ }
341
+ function requireComplexityFile$1() {
342
+ const filePath = findComplexityFile();
343
+ if (!filePath) exitWithError("COMPLEXITY.md not found.", "Run \"complexity init\" to create one.");
344
+ return filePath;
345
+ }
346
+ function requireConceptExists(conceptName, concepts) {
347
+ const existing = findConcept(conceptName, concepts);
348
+ if (!existing) exitWithError(`Concept "${conceptName}" not found.`);
349
+ return existing;
350
+ }
351
+ function validateLevelIfProvided(level) {
352
+ if (!level) return;
353
+ if (!isValidLevel(level)) exitWithError("Level must be 1, 2, or 3.");
354
+ }
355
+ async function confirmNewAreaIfNeeded(area, existingConcepts) {
356
+ if (!area || area === BLANK_AREA) return;
357
+ if (isExistingArea(area, existingConcepts)) return;
358
+ if (!await confirmAction(`No pre-existing area "${area}" found. Would you still like to use it?`)) exitCancelled();
359
+ }
360
+ function createUpdatedConcept(existing, options) {
361
+ return {
362
+ topic: options.name || existing.topic,
363
+ area: options.area !== void 0 ? options.area : existing.area,
364
+ criticality: options.level ? parseInt(options.level) : existing.criticality
365
+ };
366
+ }
367
+ function replaceConceptInList(conceptName, updatedConcept, concepts) {
368
+ return concepts.map((c) => c.topic.toLowerCase() === conceptName.toLowerCase() ? updatedConcept : c);
369
+ }
370
+ function saveComplexityFile(filePath, projectName, concepts) {
371
+ const markdown = generateMarkdown(projectName, concepts);
372
+ fs.writeFileSync(filePath, markdown, "utf-8");
373
+ }
374
+ function displayUpdateSummary(conceptName, updatedConcept, options) {
375
+ console.log(chalk.green(`✓ Updated "${conceptName}"`));
376
+ if (options.name) console.log(chalk.gray(` → Name: ${updatedConcept.topic}`));
377
+ if (options.level) console.log(chalk.gray(` → Level: ${updatedConcept.criticality}`));
378
+ if (options.area !== void 0) console.log(chalk.gray(` → Area: ${updatedConcept.area}`));
379
+ }
380
+ async function updateCommand(concept, options) {
381
+ validateAtLeastOneOptionProvided(options);
382
+ validateLevelIfProvided(options.level);
383
+ const filePath = requireComplexityFile$1();
384
+ const doc = parseComplexityFile(filePath);
385
+ const existing = requireConceptExists(concept, doc.concepts);
386
+ await confirmNewAreaIfNeeded(options.area, doc.concepts);
387
+ const updatedConcept = createUpdatedConcept(existing, options);
388
+ const updatedConcepts = replaceConceptInList(concept, updatedConcept, doc.concepts);
389
+ saveComplexityFile(filePath, doc.projectName, updatedConcepts);
390
+ displayUpdateSummary(concept, updatedConcept, options);
391
+ }
392
+
393
+ //#endregion
394
+ //#region src/commands/list.ts
395
+ function requireComplexityFile() {
396
+ const filePath = findComplexityFile();
397
+ if (!filePath) exitWithError("COMPLEXITY.md not found.", "Run \"complexity init\" to create one.");
398
+ return filePath;
399
+ }
400
+ function displayEmptyState(filePath) {
401
+ console.log(chalk.yellow("No concepts found in COMPLEXITY.md"));
402
+ console.log(chalk.gray(`File location: ${filePath}`));
403
+ }
404
+ function displayHeader(projectName, filePath, totalConcepts) {
405
+ console.log(chalk.bold(`\nProject: ${projectName}`));
406
+ console.log(chalk.gray(`File: ${filePath}`));
407
+ console.log(chalk.gray(`Total concepts: ${totalConcepts}\n`));
408
+ }
409
+ function displayConceptGroup(title, concepts, color) {
410
+ if (concepts.length === 0) return;
411
+ console.log(color(title));
412
+ concepts.forEach((concept) => {
413
+ console.log(` ${chalk.bold(concept.topic)} ${chalk.gray(`[${concept.area}]`)}`);
414
+ });
415
+ console.log("");
416
+ }
417
+ function groupConceptsByCriticality(concepts) {
418
+ return {
419
+ critical: concepts.filter((c) => c.criticality === 3),
420
+ important: concepts.filter((c) => c.criticality === 2),
421
+ situational: concepts.filter((c) => c.criticality === 1)
422
+ };
423
+ }
424
+ function listCommand() {
425
+ const filePath = requireComplexityFile();
426
+ const doc = parseComplexityFile(filePath);
427
+ const sorted = sortConcepts(doc.concepts);
428
+ if (sorted.length === 0) {
429
+ displayEmptyState(filePath);
430
+ return;
431
+ }
432
+ displayHeader(doc.projectName, filePath, sorted.length);
433
+ const groups = groupConceptsByCriticality(sorted);
434
+ displayConceptGroup("Critical (Level 3):", groups.critical, chalk.red.bold);
435
+ displayConceptGroup("Important (Level 2):", groups.important, chalk.yellow.bold);
436
+ displayConceptGroup("Situational (Level 1):", groups.situational, chalk.green.bold);
437
+ }
438
+
439
+ //#endregion
440
+ //#region src/commands/prompt.ts
441
+ function promptCommand() {
442
+ const prompt = `
443
+ I'd like you to analyze this project and create a comprehensive complexity map. This map should list everything someone needs to know to take full ownership of this software project.
444
+
445
+ For each technology, concept, or skill:
446
+ - Rate its criticality from 1-3:
447
+ * 3 = Can't do meaningful work without it
448
+ * 2 = Will encounter regularly; gaps will slow you down
449
+ * 1 = Comes up occasionally or is abstracted away
450
+
451
+ - Categorize it by area (e.g., backend, frontend, infrastructure, devops, etc.)
452
+
453
+ Use the following CLI commands to maintain the COMPLEXITY.md file:
454
+ - \`complexity add <concept> <level> [area]\` - Add a new technology
455
+ - \`complexity update <concept> --level <level> --area <area>\` - Update existing entry
456
+ - \`complexity remove <concept>\` - Remove an entry
457
+ - \`complexity list\` - View current entries
458
+
459
+ Be thorough and accurate. Consider:
460
+ - Programming languages and frameworks
461
+ - Databases and data stores
462
+ - DevOps and infrastructure tools
463
+ - Testing frameworks and methodologies
464
+ - Build tools and package managers
465
+ - Domain-specific knowledge
466
+ - Architectural patterns and design principles
467
+ - Third-party services and APIs
468
+
469
+ Start by reviewing the existing entries with \`complexity list\`, then add, update, or remove entries as needed to ensure completeness and accuracy.
470
+
471
+ Do not update the COMPLEXITY.md file manually.
472
+ `.trim();
473
+ console.log(prompt);
474
+ }
475
+
476
+ //#endregion
477
+ //#region src/index.ts
478
+ const program = new Command();
479
+ program.name("complexity").description("A CLI tool for managing project complexity and knowledge maps").version("1.0.0");
480
+ program.command("init").description("Initialize a COMPLEXITY.md file in the current directory").option("-n, --name <name>", "Project name").option("-f, --force", "Overwrite existing file").action(initCommand);
481
+ program.command("add <concept> [level] [area]").description("Add a concept to the complexity map").option("-l, --level <level>", "Criticality level (1-3)").action(addCommand);
482
+ program.command("remove <concept>").description("Remove a concept from the complexity map").option("-f, --force", "Skip confirmation").action(removeCommand);
483
+ program.command("update <concept>").description("Update an existing concept").option("-l, --level <level>", "New criticality level (1-3)").option("-a, --area <area>", "New area").option("-n, --name <name>", "New concept name").action(updateCommand);
484
+ program.command("list").description("List all concepts in the complexity map").action(listCommand);
485
+ program.command("prompt").description("Display the AI prompt for complexity analysis").action(promptCommand);
47
486
  program.parse();
48
- //# sourceMappingURL=index.js.map
487
+
488
+ //#endregion
489
+ export { };
package/package.json CHANGED
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "complexity-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A CLI tool for managing project complexity and knowledge maps",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
7
  "bin": {
7
8
  "complexity": "dist/index.js"
8
9
  },
9
10
  "scripts": {
10
- "build": "tsc",
11
- "dev": "tsc --watch",
11
+ "build": "tsdown",
12
+ "dev": "tsdown --watch",
13
+ "typecheck": "tsc --noEmit",
14
+ "link": "npm run build && npm link",
12
15
  "prepublishOnly": "npm run build"
13
16
  },
14
17
  "keywords": [
@@ -37,13 +40,14 @@
37
40
  "LICENSE"
38
41
  ],
39
42
  "dependencies": {
43
+ "chalk": "^4.1.2",
40
44
  "commander": "^12.0.0",
41
- "inquirer": "^9.2.0",
42
- "chalk": "^4.1.2"
45
+ "inquirer": "^9.2.0"
43
46
  },
44
47
  "devDependencies": {
45
- "@types/node": "^20.0.0",
46
48
  "@types/inquirer": "^9.0.0",
49
+ "@types/node": "^20.0.0",
50
+ "tsdown": "^0.20.3",
47
51
  "typescript": "^5.3.0"
48
52
  },
49
53
  "engines": {
@@ -1,4 +0,0 @@
1
- export declare function addCommand(concept: string, levelArg: string | undefined, areaArg: string | undefined, options: {
2
- level?: string;
3
- }): Promise<void>;
4
- //# sourceMappingURL=add.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AA8EA,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,iBAkB5B"}