Skip to content

Commit 4e19a80

Browse files
feat: migration of last scripts into hexagonal architecture raw state
1 parent 726ae71 commit 4e19a80

228 files changed

Lines changed: 9627 additions & 8256 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gemini/skills/research-buddy/skill.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,6 @@ These commands skip the questionnaire and go straight to the relevant step:
8484
- Never advance to FINALIZE without user approval of `findings-summary.md`
8585
- After creating wiki pages, always run:
8686
```bash
87-
node scripts/generate-wiki-manifest.mjs
88-
node scripts/generate-wiki-index.mjs
87+
npx nx run wiki-cli:generate-manifest
88+
npx nx run wiki-cli:generate-index
8989
```

.kiro/steering/article-research.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ All research is organized in `.kiro/research/sessions/[session-id]/`:
139139
- If reciprocal reference fails → preserve source page, report which targets failed
140140

141141
**Manifest and Index Regeneration:**
142-
- Run `node scripts/generate-wiki-manifest.mjs` from workspace root
143-
- Run `node scripts/generate-wiki-index.mjs` from workspace root
142+
- Run `npx nx run wiki-cli:generate-manifest` from workspace root
143+
- Run `npx nx run wiki-cli:generate-index` from workspace root
144144
- If either script fails → delete all wiki pages created in session, set `wikiPages: []`
145145

146146
**Decline Path — No Wiki Pages:**
@@ -261,8 +261,8 @@ Invoke `#research-buddy` to start. The assistant will ask what you want to resea
261261
|----------------|----------|
262262
| Wiki page write failure | Report per-page status, record only successful pages, offer retry |
263263
| Reciprocal reference failure | Preserve source page, report which targets failed |
264-
| generate-wiki-manifest.mjs failure | Delete all wiki pages created in session, set wikiPages to [] |
265-
| generate-wiki-index.mjs failure | Delete all wiki pages created in session, set wikiPages to [] |
264+
| wiki-cli:generate-manifest failure | Delete all wiki pages created in session, set wikiPages to [] |
265+
| wiki-cli:generate-index failure | Delete all wiki pages created in session, set wikiPages to [] |
266266

267267
### Session_Manager Errors
268268

.kiro/steering/library-research.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -807,12 +807,12 @@ After the wiki publication decision has been resolved (either declined or all ac
807807
- `finalizedAt`: current date in ISO format (`YYYY-MM-DD`)
808808
- `wikiPages`: array of paths of all successfully created wiki pages, or `[]` if none were created
809809

810-
2. **If any wiki pages were created**, regenerate the manifest and index by running both scripts from the workspace root:
810+
2. **If any wiki pages were created**, regenerate the manifest and index by running both `wiki-cli` targets from the workspace root:
811811
```bash
812-
node scripts/generate-wiki-manifest.mjs
813-
node scripts/generate-wiki-index.mjs
812+
npx nx run wiki-cli:generate-manifest
813+
npx nx run wiki-cli:generate-index
814814
```
815-
These scripts scan `wiki/entities/`, `wiki/concepts/`, and `wiki/sources/` and rewrite `wiki/manifest.json` and `wiki/index.md` from the actual files on disk. Always run them after wiki publication — the manually maintained index and manifest will otherwise be stale.
815+
These targets scan `wiki/entities/`, `wiki/concepts/`, and `wiki/sources/` and rewrite `wiki/manifest.json` and `wiki/index.md` from the actual files on disk. Always run them after wiki publication — the manually maintained index and manifest will otherwise be stale.
816816

817817
Example `session.json` finalization fields:
818818

GEMINI.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This is an Angular/NX monorepo used for library research, prototyping, and build
1010
- `libs/prototype-playground` — All research prototypes; every prototype gets a Storybook story
1111
- `.kiro/research/sessions/` — Research session artifacts and state
1212
- `wiki/` — Published knowledge base (entities, concepts, sources)
13-
- `scripts/`Wiki manifest and index generation scripts
13+
- `apps/wiki-cli`Nx application exposing wiki manifest/index generation, tag validation, and scaffolding as `nx run wiki-cli:*` targets
1414

1515
## Always-On Rules
1616

@@ -46,10 +46,10 @@ After any research session, wiki pages are generated under:
4646
- `wiki/concepts/` — pattern and principle pages
4747
- `wiki/sources/` — citable research session references
4848

49-
Always run both scripts after creating wiki pages:
49+
Always run both targets after creating wiki pages:
5050
```bash
51-
node scripts/generate-wiki-manifest.mjs
52-
node scripts/generate-wiki-index.mjs
51+
npx nx run wiki-cli:generate-manifest
52+
npx nx run wiki-cli:generate-index
5353
```
5454

5555
### Prototype Validation

apps/wiki-cli/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "wiki-cli",
3+
"version": "1.0.0",
4+
"description": "Driver_Adapter composition roots for wiki manifest/index generation, tag validation, and scaffolding",
5+
"private": true,
6+
"bin": {
7+
"wiki-cli": "./dist/apps/wiki-cli/index.cjs"
8+
},
9+
"scripts": {
10+
"test": "vitest run --config vitest.config.ts"
11+
}
12+
}

apps/wiki-cli/project.json

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"name": "wiki-cli",
3+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
4+
"projectType": "application",
5+
"sourceRoot": "apps/wiki-cli/src",
6+
"tags": ["presentation"],
7+
"implicitDependencies": [
8+
"application-index-manager",
9+
"application-tag-validation",
10+
"application-scaffolding",
11+
"infrastructure-filesystem",
12+
"infrastructure-frontmatter",
13+
"infrastructure-markdown"
14+
],
15+
"targets": {
16+
"build": {
17+
"executor": "@nx/esbuild:esbuild",
18+
"outputs": ["{options.outputPath}"],
19+
"options": {
20+
"main": "apps/wiki-cli/src/index.ts",
21+
"tsConfig": "apps/wiki-cli/tsconfig.app.json",
22+
"outputPath": "dist/apps/wiki-cli",
23+
"platform": "node",
24+
"format": ["cjs"],
25+
"bundle": true,
26+
"thirdParty": true,
27+
"esbuildOptions": {
28+
"banner": {
29+
"js": "#!/usr/bin/env node"
30+
}
31+
}
32+
}
33+
},
34+
"test": {
35+
"executor": "nx:run-commands",
36+
"options": {
37+
"command": "npx vitest run --config apps/wiki-cli/vitest.config.ts",
38+
"cwd": "{workspaceRoot}"
39+
}
40+
},
41+
"lint": {
42+
"executor": "@nx/eslint:lint"
43+
},
44+
"generate-manifest": {
45+
"executor": "nx:run-commands",
46+
"dependsOn": ["build"],
47+
"options": {
48+
"command": "node dist/apps/wiki-cli/index.cjs generate-manifest",
49+
"cwd": "{workspaceRoot}"
50+
}
51+
},
52+
"generate-index": {
53+
"executor": "nx:run-commands",
54+
"dependsOn": ["build"],
55+
"options": {
56+
"command": "node dist/apps/wiki-cli/index.cjs generate-index",
57+
"cwd": "{workspaceRoot}"
58+
}
59+
},
60+
"validate-tags": {
61+
"executor": "nx:run-commands",
62+
"dependsOn": ["build"],
63+
"options": {
64+
"command": "node dist/apps/wiki-cli/index.cjs validate-tags",
65+
"cwd": "{workspaceRoot}"
66+
}
67+
},
68+
"init": {
69+
"executor": "nx:run-commands",
70+
"dependsOn": ["build"],
71+
"options": {
72+
"command": "node dist/apps/wiki-cli/index.cjs init",
73+
"cwd": "{workspaceRoot}"
74+
}
75+
}
76+
}
77+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Thin Driver_Adapter composition root for wiki index generation.
3+
*
4+
* Wires the Infrastructure `FileSystemAdapter`, `FrontmatterAdapter`, and
5+
* `MarkdownAdapter` into the Application `GenerateIndexUseCase`, invokes
6+
* it, and prints the resulting summary. No business logic lives here —
7+
* scanning wiki/entities/, wiki/concepts/, and wiki/sources/, parsing
8+
* frontmatter, deriving descriptions, sorting, and rendering index.md is
9+
* handled entirely by the use case in @wiki/application-index-manager.
10+
*/
11+
12+
import { FileSystemAdapter } from '@wiki/infrastructure-filesystem';
13+
import { FrontmatterAdapter } from '@wiki/infrastructure-frontmatter';
14+
import { MarkdownAdapter } from '@wiki/infrastructure-markdown';
15+
import { GenerateIndexUseCase } from '@wiki/application-index-manager';
16+
17+
export async function runGenerateIndex(workspaceRoot: string): Promise<void> {
18+
const fsAdapter = new FileSystemAdapter({
19+
rootDir: workspaceRoot,
20+
rawDir: 'raw',
21+
wikiDir: 'wiki',
22+
});
23+
const frontmatterAdapter = new FrontmatterAdapter();
24+
const markdownAdapter = new MarkdownAdapter();
25+
26+
const useCase = new GenerateIndexUseCase(fsAdapter, frontmatterAdapter, markdownAdapter);
27+
const { entities, concepts, sources } = await useCase.execute();
28+
29+
console.log(`wiki/index.md regenerated — ${entities} entities, ${concepts} concepts, ${sources} sources`);
30+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Thin Driver_Adapter composition root for manifest generation.
3+
*
4+
* Wires the Infrastructure `FileSystemAdapter` into the Application
5+
* `GenerateManifestUseCase`, invokes it, and prints the resulting summary.
6+
* No business logic lives here — scanning wiki/entities/, wiki/concepts/,
7+
* and wiki/sources/ and writing wiki/manifest.json is handled entirely by
8+
* the use case in @wiki/application-index-manager.
9+
*/
10+
11+
import { FileSystemAdapter } from '@wiki/infrastructure-filesystem';
12+
import { GenerateManifestUseCase } from '@wiki/application-index-manager';
13+
14+
export async function runGenerateManifest(workspaceRoot: string): Promise<void> {
15+
const fsAdapter = new FileSystemAdapter({
16+
rootDir: workspaceRoot,
17+
rawDir: 'raw',
18+
wikiDir: 'wiki',
19+
});
20+
21+
const useCase = new GenerateManifestUseCase(fsAdapter);
22+
const { manifest } = await useCase.execute();
23+
24+
console.log(`wiki/manifest.json written with ${manifest.files.length} file(s):`);
25+
manifest.files.forEach((f) => console.log(` ${f}`));
26+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* Thin Driver_Adapter composition root for wiki scaffolding.
3+
*
4+
* Wires the Infrastructure `FileSystemAdapter` into the Application
5+
* `ScaffoldWikiUseCase` and reports per-directory progress. Contains no
6+
* business logic — directory scaffolding lives in
7+
* @wiki/application-scaffolding's ScaffoldWikiUseCase.
8+
*
9+
* Angular-project detection below is presentation/informational stdout
10+
* only; it does not affect scaffolding behavior.
11+
*/
12+
13+
import * as fs from 'fs';
14+
import * as path from 'path';
15+
import { FileSystemAdapter } from '@wiki/infrastructure-filesystem';
16+
import { ScaffoldWikiUseCase } from '@wiki/application-scaffolding';
17+
18+
const ANGULAR_DIRECTORIES = ['apps', 'libs', '.kiro'];
19+
20+
function checkAngularProject(workspaceRoot: string): boolean {
21+
console.log('Checking for existing Angular project...');
22+
23+
for (const dir of ANGULAR_DIRECTORIES) {
24+
const dirPath = path.join(workspaceRoot, dir);
25+
if (fs.existsSync(dirPath)) {
26+
console.log(`✓ Found ${dir}/ directory`);
27+
} else {
28+
console.warn(`⚠ Warning: ${dir}/ directory not found`);
29+
}
30+
}
31+
32+
const angularJson = path.join(workspaceRoot, 'angular.json');
33+
if (fs.existsSync(angularJson)) {
34+
console.log('✓ Found angular.json');
35+
return true;
36+
}
37+
38+
console.warn('⚠ Warning: angular.json not found');
39+
return false;
40+
}
41+
42+
export async function runInit(workspaceRoot: string): Promise<void> {
43+
console.log('\n=== LLM Wiki Second Brain Initialization ===\n');
44+
45+
const hasAngularProject = checkAngularProject(workspaceRoot);
46+
47+
if (hasAngularProject) {
48+
console.log('\n✓ Angular project detected - will preserve existing structure\n');
49+
} else {
50+
console.log('\n⚠ No Angular project detected - continuing anyway\n');
51+
}
52+
53+
console.log('Creating wiki directory structure...\n');
54+
55+
const fsAdapter = new FileSystemAdapter({
56+
rootDir: workspaceRoot,
57+
rawDir: 'raw',
58+
wikiDir: 'wiki',
59+
});
60+
const useCase = new ScaffoldWikiUseCase(fsAdapter);
61+
const { created, existing } = await useCase.execute();
62+
63+
for (const dir of created) {
64+
console.log(`✓ Created directory: ${dir}`);
65+
}
66+
for (const dir of existing) {
67+
console.log(`✓ Directory already exists: ${dir}`);
68+
}
69+
70+
console.log('\n✓ Wiki directory structure initialized successfully!');
71+
console.log('\nNext steps:');
72+
console.log('1. Run the schema configuration setup');
73+
console.log('2. Create initial wiki pages');
74+
console.log('3. Add README files to raw/ and wiki/ directories');
75+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* Thin Driver_Adapter composition root for tag distribution validation.
3+
*
4+
* Wires the Infrastructure `FileSystemAdapter` and `FrontmatterAdapter`
5+
* into the Application `ValidateTagDistributionUseCase`, invokes it, and
6+
* prints the frequency table + violations. No business logic lives here —
7+
* scanning wiki/entities/, wiki/concepts/, wiki/sources/, extracting tags
8+
* from frontmatter, and computing the 60% frequency threshold is handled
9+
* entirely by the use case in @wiki/application-tag-validation.
10+
*
11+
* Returns the process exit code the caller should use:
12+
* - 0: All tags pass validation (no tag exceeds 60% threshold)
13+
* - 1: Validation failed (one or more tags exceed 60% threshold)
14+
*/
15+
16+
import { FileSystemAdapter } from '@wiki/infrastructure-filesystem';
17+
import { FrontmatterAdapter } from '@wiki/infrastructure-frontmatter';
18+
import { ValidateTagDistributionUseCase } from '@wiki/application-tag-validation';
19+
20+
const MAX_TAG_FREQUENCY = 0.6; // 60%
21+
22+
function formatFrequency(count: number, total: number): string {
23+
if (total === 0) return '0.0%';
24+
return `${((count / total) * 100).toFixed(1)}%`;
25+
}
26+
27+
export async function runValidateTags(workspaceRoot: string): Promise<number> {
28+
console.log('Tag Distribution Validation');
29+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
30+
console.log('');
31+
32+
const fsAdapter = new FileSystemAdapter({
33+
rootDir: workspaceRoot,
34+
rawDir: 'raw',
35+
wikiDir: 'wiki',
36+
});
37+
const frontmatterAdapter = new FrontmatterAdapter();
38+
const useCase = new ValidateTagDistributionUseCase(fsAdapter, frontmatterAdapter);
39+
40+
const result = await useCase.execute();
41+
const { totalPages, tagCounts, violations, passed } = result;
42+
43+
if (totalPages === 0) {
44+
console.log('⚠ No pages with tags found - nothing to validate');
45+
return 0;
46+
}
47+
48+
console.log(`📊 Tag Statistics:`);
49+
console.log(` Total pages: ${totalPages}`);
50+
console.log(` Unique tags: ${tagCounts.size}`);
51+
console.log(` Max threshold: ${(MAX_TAG_FREQUENCY * 100).toFixed(0)}%`);
52+
console.log('');
53+
54+
const sortedTags = Array.from(tagCounts.entries()).sort((a, b) => b[1] - a[1]);
55+
56+
console.log('🏷️ Top 20 Most Frequent Tags:');
57+
console.log('');
58+
console.log('Tag Count Frequency Status');
59+
console.log('───────────────────────────── ───── ───────── ──────');
60+
61+
const topTags = sortedTags.slice(0, 20);
62+
for (const [tag, count] of topTags) {
63+
const frequency = count / totalPages;
64+
const freqStr = formatFrequency(count, totalPages);
65+
const status = frequency > MAX_TAG_FREQUENCY ? '❌ FAIL' : '✅ PASS';
66+
const paddedTag = tag.padEnd(30);
67+
const paddedCount = count.toString().padStart(5);
68+
const paddedFreq = freqStr.padStart(9);
69+
console.log(`${paddedTag} ${paddedCount} ${paddedFreq} ${status}`);
70+
}
71+
72+
console.log('');
73+
74+
if (passed) {
75+
console.log('✅ VALIDATION PASSED');
76+
console.log('');
77+
console.log(`All ${tagCounts.size} tags are within the ${(MAX_TAG_FREQUENCY * 100).toFixed(0)}% frequency threshold.`);
78+
console.log('');
79+
return 0;
80+
}
81+
82+
console.log('❌ VALIDATION FAILED');
83+
console.log('');
84+
console.log(`${violations.length} tag(s) exceed the ${(MAX_TAG_FREQUENCY * 100).toFixed(0)}% frequency threshold:`);
85+
console.log('');
86+
87+
for (const violation of violations) {
88+
const freqStr = formatFrequency(violation.count, totalPages);
89+
console.log(` - "${violation.tag}": ${freqStr} (${violation.count}/${totalPages} pages)`);
90+
91+
const pages = violation.pages;
92+
console.log(` Pages: ${pages.slice(0, 5).join(', ')}${pages.length > 5 ? ` ... and ${pages.length - 5} more` : ''}`);
93+
console.log('');
94+
}
95+
96+
console.log('💡 Recommendations:');
97+
console.log(' - Distribute tags more evenly across pages');
98+
console.log(' - Use more specific tags instead of broad categories');
99+
console.log(' - Consider splitting high-frequency tags into sub-categories');
100+
console.log('');
101+
return 1;
102+
}

0 commit comments

Comments
 (0)