@kb-labs/devkit 1.0.0 → 1.2.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.
@@ -0,0 +1,28 @@
1
+ name: Deploy
2
+
3
+ # Deploys all services to VPS via docker compose
4
+ # Triggered manually with a specific image tag
5
+ #
6
+ # Prerequisites:
7
+ # Add secrets in GitHub → Settings → Secrets → Actions:
8
+ # VPS_HOST - server IP or hostname
9
+ # VPS_USER - SSH username (e.g. deploy)
10
+ # VPS_SSH_KEY - SSH private key
11
+
12
+ on:
13
+ workflow_dispatch:
14
+ inputs:
15
+ tag:
16
+ description: Image tag to deploy (e.g. release-1.0.0)
17
+ required: true
18
+ type: string
19
+
20
+ jobs:
21
+ call:
22
+ uses: KirillBaranov/kb-labs-devkit/.github/workflows/deploy-reusable.yml@main
23
+ with:
24
+ image-tag: ${{ inputs.tag }}
25
+ compose-file: docker-compose.production.yml
26
+ deploy-dir: /opt/kb-labs
27
+ health-check-url: http://localhost:5050/api/v1/health
28
+ secrets: inherit
@@ -0,0 +1,25 @@
1
+ name: Docker Build
2
+
3
+ # Builds and pushes Docker image to ghcr.io
4
+ #
5
+ # Flow:
6
+ # rc branch → build image tagged rc-<sha> (for testing)
7
+ # release tag → build image tagged release-x.y.z + latest (for deploy)
8
+ #
9
+ # Configure:
10
+ # - Set image-name to your service name (e.g. kb-labs-rest-api)
11
+ # - Set dockerfile path if not at repo root
12
+
13
+ on:
14
+ push:
15
+ branches: [rc]
16
+ tags: ['release-*']
17
+ workflow_dispatch: {}
18
+
19
+ jobs:
20
+ call:
21
+ uses: KirillBaranov/kb-labs-devkit/.github/workflows/docker-build-reusable.yml@main
22
+ with:
23
+ image-name: kb-labs-my-service # TODO: change to your service name
24
+ dockerfile: Dockerfile
25
+ context: .
@@ -0,0 +1,110 @@
1
+ name: reusable-deploy
2
+
3
+ # Reusable workflow: deploy to VPS via SSH + docker compose
4
+ #
5
+ # Usage in orchestrator repo (e.g. kb-labs root):
6
+ # jobs:
7
+ # call:
8
+ # uses: KirillBaranov/kb-labs-devkit/.github/workflows/deploy-reusable.yml@main
9
+ # with:
10
+ # image-tag: release-1.0.0
11
+ # compose-file: docker-compose.production.yml
12
+ # secrets: inherit
13
+ #
14
+ # Required secrets in calling repo:
15
+ # VPS_HOST, VPS_USER, VPS_SSH_KEY
16
+
17
+ on:
18
+ workflow_call:
19
+ inputs:
20
+ image-tag:
21
+ description: Docker image tag to deploy (e.g. release-1.0.0)
22
+ required: true
23
+ type: string
24
+ compose-file:
25
+ description: Path to docker-compose file on VPS
26
+ required: false
27
+ default: docker-compose.production.yml
28
+ type: string
29
+ deploy-dir:
30
+ description: Working directory on VPS
31
+ required: false
32
+ default: /opt/kb-labs
33
+ type: string
34
+ registry:
35
+ description: Container registry host
36
+ required: false
37
+ default: ghcr.io
38
+ type: string
39
+ health-check-url:
40
+ description: URL to check after deploy (e.g. http://localhost:5050/api/v1/health)
41
+ required: false
42
+ default: ''
43
+ type: string
44
+ health-check-wait:
45
+ description: Seconds to wait before health check
46
+ required: false
47
+ default: '15'
48
+ type: string
49
+ secrets:
50
+ VPS_HOST:
51
+ required: true
52
+ VPS_USER:
53
+ required: true
54
+ VPS_SSH_KEY:
55
+ required: true
56
+
57
+ jobs:
58
+ deploy:
59
+ name: Deploy to VPS
60
+ runs-on: ubuntu-latest
61
+ environment: production
62
+
63
+ steps:
64
+ - name: Deploy via SSH
65
+ uses: appleboy/ssh-action@v1
66
+ env:
67
+ DEPLOY_TAG: ${{ inputs.image-tag }}
68
+ DEPLOY_REGISTRY: ${{ inputs.registry }}/${{ github.repository_owner }}
69
+ with:
70
+ host: ${{ secrets.VPS_HOST }}
71
+ username: ${{ secrets.VPS_USER }}
72
+ key: ${{ secrets.VPS_SSH_KEY }}
73
+ envs: DEPLOY_TAG,DEPLOY_REGISTRY
74
+ script: |
75
+ set -euo pipefail
76
+ cd ${{ inputs.deploy-dir }}
77
+
78
+ echo "→ Deploying tag: $DEPLOY_TAG"
79
+ echo "→ Registry: $DEPLOY_REGISTRY"
80
+
81
+ # Save current tag for rollback
82
+ [ -f .current-tag ] && cp .current-tag .previous-tag
83
+ echo "$DEPLOY_TAG" > .current-tag
84
+
85
+ # Pull new images
86
+ TAG="$DEPLOY_TAG" REGISTRY="$DEPLOY_REGISTRY" \
87
+ docker compose -f ${{ inputs.compose-file }} pull
88
+
89
+ # Rolling update
90
+ TAG="$DEPLOY_TAG" REGISTRY="$DEPLOY_REGISTRY" \
91
+ docker compose -f ${{ inputs.compose-file }} up -d --remove-orphans
92
+
93
+ echo "→ Containers started, waiting ${{ inputs.health-check-wait }}s..."
94
+ sleep ${{ inputs.health-check-wait }}
95
+
96
+ # Show status
97
+ docker compose -f ${{ inputs.compose-file }} ps
98
+
99
+ # Health check (if URL provided)
100
+ if [ -n "${{ inputs.health-check-url }}" ]; then
101
+ echo "→ Health check: ${{ inputs.health-check-url }}"
102
+ wget -qO- "${{ inputs.health-check-url }}" \
103
+ && echo "✓ Health check passed" \
104
+ || { echo "✗ Health check failed!"; exit 1; }
105
+ fi
106
+
107
+ # Cleanup old images
108
+ docker image prune -f > /dev/null 2>&1 || true
109
+
110
+ echo "✓ Deploy complete: $DEPLOY_TAG"
@@ -0,0 +1,110 @@
1
+ name: reusable-docker-build
2
+
3
+ # Reusable workflow: build Docker image and push to ghcr.io
4
+ #
5
+ # Usage in consumer repo:
6
+ # jobs:
7
+ # call:
8
+ # uses: KirillBaranov/kb-labs-devkit/.github/workflows/docker-build-reusable.yml@main
9
+ # with:
10
+ # image-name: kb-labs-rest-api
11
+ # dockerfile: Dockerfile
12
+ # secrets: inherit
13
+
14
+ on:
15
+ workflow_call:
16
+ inputs:
17
+ image-name:
18
+ description: Docker image name (e.g. kb-labs-rest-api)
19
+ required: true
20
+ type: string
21
+ dockerfile:
22
+ description: Path to Dockerfile (relative to context)
23
+ required: false
24
+ default: Dockerfile
25
+ type: string
26
+ context:
27
+ description: Docker build context (default is repo root)
28
+ required: false
29
+ default: .
30
+ type: string
31
+ build-args:
32
+ description: Build args as multiline string (KEY=VALUE per line)
33
+ required: false
34
+ default: ''
35
+ type: string
36
+ platforms:
37
+ description: Target platforms (e.g. linux/amd64,linux/arm64)
38
+ required: false
39
+ default: linux/amd64
40
+ type: string
41
+ registry:
42
+ description: Container registry host
43
+ required: false
44
+ default: ghcr.io
45
+ type: string
46
+ outputs:
47
+ image-tag:
48
+ description: Full image tag that was pushed
49
+ value: ${{ jobs.build.outputs.image-tag }}
50
+ short-sha:
51
+ description: Short commit SHA used as tag
52
+ value: ${{ jobs.build.outputs.short-sha }}
53
+
54
+ jobs:
55
+ build:
56
+ name: Build & Push
57
+ runs-on: ubuntu-latest
58
+ permissions:
59
+ contents: read
60
+ packages: write
61
+
62
+ outputs:
63
+ image-tag: ${{ steps.meta.outputs.version }}
64
+ short-sha: ${{ steps.sha.outputs.short }}
65
+
66
+ steps:
67
+ - name: Checkout
68
+ uses: actions/checkout@v4
69
+ with:
70
+ submodules: recursive
71
+
72
+ - name: Get short SHA
73
+ id: sha
74
+ run: echo "short=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
75
+
76
+ - name: Log in to Container Registry
77
+ uses: docker/login-action@v3
78
+ with:
79
+ registry: ${{ inputs.registry }}
80
+ username: ${{ github.actor }}
81
+ password: ${{ secrets.GITHUB_TOKEN }}
82
+
83
+ - name: Set up Docker Buildx
84
+ uses: docker/setup-buildx-action@v3
85
+
86
+ - name: Docker metadata
87
+ id: meta
88
+ uses: docker/metadata-action@v5
89
+ with:
90
+ images: ${{ inputs.registry }}/${{ github.repository_owner }}/${{ inputs.image-name }}
91
+ tags: |
92
+ # rc branch → rc-<sha>
93
+ type=raw,value=rc-${{ steps.sha.outputs.short }},enable=${{ github.ref == 'refs/heads/rc' }}
94
+ # release tag → release-1.0.0
95
+ type=raw,value=${{ github.ref_name }},enable=${{ startsWith(github.ref, 'refs/tags/release-') }}
96
+ # always tag latest on release
97
+ type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/release-') }}
98
+
99
+ - name: Build and push
100
+ uses: docker/build-push-action@v6
101
+ with:
102
+ context: ${{ inputs.context }}
103
+ file: ${{ inputs.dockerfile }}
104
+ platforms: ${{ inputs.platforms }}
105
+ push: true
106
+ tags: ${{ steps.meta.outputs.tags }}
107
+ labels: ${{ steps.meta.outputs.labels }}
108
+ build-args: ${{ inputs.build-args }}
109
+ cache-from: type=gha,scope=${{ inputs.image-name }}
110
+ cache-to: type=gha,mode=max,scope=${{ inputs.image-name }}
package/README.md CHANGED
@@ -990,6 +990,143 @@ To generate `tsup.external.json` manually (if needed):
990
990
  npx kb-devkit-tsup-external --generate
991
991
  ```
992
992
 
993
+ ### QA History Tracker
994
+
995
+ Track QA metrics over time and detect regressions:
996
+
997
+ ```bash
998
+ npx kb-devkit-qa-history save # Save current QA results with git context
999
+ npx kb-devkit-qa-history show # Show last 20 runs
1000
+ npx kb-devkit-qa-history trends # Show quality trends over time
1001
+ npx kb-devkit-qa-history regressions # Detect new failures since last save
1002
+ ```
1003
+
1004
+ **Root commands:**
1005
+ ```bash
1006
+ pnpm qa:save # Save current QA state
1007
+ pnpm qa:history # Show history
1008
+ pnpm qa:trends # Show trends
1009
+ pnpm qa:regressions # Detect regressions (exits 1 on regression)
1010
+ ```
1011
+
1012
+ ### Core Gate
1013
+
1014
+ Check if the 6 core platform monorepos meet zero-failure quality requirements. Reads from the last `pnpm qa` run — no recompilation:
1015
+
1016
+ ```bash
1017
+ npx kb-devkit-core-gate # Gate check (exits 1 if core is broken)
1018
+ npx kb-devkit-core-gate --verbose # Show failing packages
1019
+ npx kb-devkit-core-gate --json # JSON output
1020
+ ```
1021
+
1022
+ **Core monorepos checked:** `kb-labs-cli`, `kb-labs-core`, `kb-labs-shared`, `kb-labs-sdk`, `kb-labs-rest-api`, `kb-labs-plugin`
1023
+
1024
+ **Requirements:** build → zero failures, types → zero errors, lint → zero errors
1025
+
1026
+ **Typical flow:**
1027
+ ```bash
1028
+ pnpm qa # Run checks
1029
+ pnpm core:gate # Inspect core result
1030
+ ```
1031
+
1032
+ ### Architecture Audit
1033
+
1034
+ Analyze monorepo architecture and detect structural issues:
1035
+
1036
+ ```bash
1037
+ npx kb-devkit-architecture # Full audit (JSON + HTML graph + markdown report)
1038
+ npx kb-devkit-architecture --json # AI-readable JSON output
1039
+ npx kb-devkit-architecture --md # Markdown report only
1040
+ npx kb-devkit-architecture --trends # Compare with previous runs
1041
+ ```
1042
+
1043
+ **Detects 10 anomaly types** (scored by severity):
1044
+ - Circular dependencies (100), Layer violations (90), God packages (80)
1045
+ - Unstable core (75), Bidirectional dependencies (70)
1046
+ - Large packages >10K LOC (60), Orphan packages (60)
1047
+ - Many dependencies >10 (50), Deep chains >7 (50), Missing docs (40)
1048
+
1049
+ ### Freshness Tracker
1050
+
1051
+ Detect stale packages — when package A uses an old build of dependency B:
1052
+
1053
+ ```bash
1054
+ npx kb-devkit-freshness # Default table output
1055
+ npx kb-devkit-freshness --json # JSON for AI agents
1056
+ npx kb-devkit-freshness --only-stale # Show only stale packages
1057
+ npx kb-devkit-freshness --suggest-rebuild # Show rebuild order
1058
+ npx kb-devkit-freshness --package=cli-core # Single package analysis
1059
+ npx kb-devkit-freshness --age-days=30 # Packages not built in 30+ days
1060
+ npx kb-devkit-freshness --high-impact=5 # Packages affecting 5+ others
1061
+ ```
1062
+
1063
+ ### Config Checker
1064
+
1065
+ Check all packages for configuration drift from standard DevKit templates:
1066
+
1067
+ ```bash
1068
+ npx kb-devkit-check-configs # Check all packages
1069
+ npx kb-devkit-check-configs --fix # Auto-fix with backup
1070
+ npx kb-devkit-check-configs --package=cli-core # Check specific package
1071
+ npx kb-devkit-check-configs --ci # CI mode (fail on drift)
1072
+ ```
1073
+
1074
+ **What it validates:**
1075
+ - Missing or modified config files vs standard templates
1076
+ - `tsup.config.ts` structure (nodePreset usage, `dts: true`, etc.)
1077
+ - Duplicate external/ignores declarations
1078
+
1079
+ ### Scripts Checker
1080
+
1081
+ Validate that all packages have required scripts and devDependencies:
1082
+
1083
+ ```bash
1084
+ npx kb-devkit-check-scripts # Check all packages
1085
+ npx kb-devkit-check-scripts --fix # Auto-add missing scripts
1086
+ npx kb-devkit-check-scripts --package=cli-core # Check specific package
1087
+ ```
1088
+
1089
+ **Required scripts validated:** `clean`, `build`, `dev`, `lint`, `lint:fix`, `type-check`, `test`, `test:watch`
1090
+
1091
+ ### Deprecated Code Checker
1092
+
1093
+ Find all `@deprecated` markers in the codebase with context:
1094
+
1095
+ ```bash
1096
+ npx kb-devkit-check-deprecated # Check all packages
1097
+ npx kb-devkit-check-deprecated --package cli-core # Check specific package
1098
+ npx kb-devkit-check-deprecated --json # JSON output
1099
+ npx kb-devkit-check-deprecated --stats # Statistics only
1100
+ npx kb-devkit-check-deprecated --verbose # Full context
1101
+ ```
1102
+
1103
+ **Finds:** JSDoc `@deprecated` tags, TypeScript `@deprecated` patterns, inline `// @deprecated` comments.
1104
+
1105
+ ### Build Readiness Checker
1106
+
1107
+ Analyze whether packages can be successfully bundled (useful before creating standalone executables):
1108
+
1109
+ ```bash
1110
+ npx kb-devkit-check-build-readiness
1111
+ npx kb-devkit-check-build-readiness --package @kb-labs/cli-bin
1112
+ npx kb-devkit-check-build-readiness --fix
1113
+ ```
1114
+
1115
+ **Checks:** missing packages in imports, broken import paths, dependency chains that would fail bundling.
1116
+
1117
+ ### Config Migrator
1118
+
1119
+ Mass migration to standardize all package configurations to current DevKit templates:
1120
+
1121
+ ```bash
1122
+ npx kb-devkit-migrate-configs # Dry run (preview only)
1123
+ npx kb-devkit-migrate-configs --apply # Apply changes
1124
+ npx kb-devkit-migrate-configs --package=cli-core # Migrate specific package
1125
+ npx kb-devkit-migrate-configs --force # Skip confirmation prompts
1126
+ ```
1127
+
1128
+ Creates backups before applying changes and generates a migration report.
1129
+
993
1130
  ## ✨ Features
994
1131
 
995
1132
  - **TypeScript**: Ready-to-use `tsconfig` for libraries, Node services, and CLIs
@@ -1493,48 +1630,63 @@ jobs:
1493
1630
 
1494
1631
  ## 📦 Complete Tools Summary
1495
1632
 
1496
- DevKit provides **19 tools** for monorepo management and quality assurance:
1497
-
1498
- ### Analysis Tools (8)
1499
- 1. **Import Checker** - Find broken imports, unused dependencies, circular deps
1500
- 2. **Export Checker** - Find unused exports and dead code
1501
- 3. **Duplicate Checker** - Find duplicate dependencies
1502
- 4. **Structure Checker** - Validate package structure
1503
- 5. **Naming Validator** - Enforce Pyramid Rule naming convention
1504
- 6. **Path Validator** - Validate workspace deps, exports, bin paths
1505
- 7. **TypeScript Types Audit** - Deep type safety analysis across monorepo
1506
- 8. **Visualizer** - Generate dependency graphs and stats
1507
-
1508
- ### Automation Tools (8)
1509
- 1. **⚡ QA Runner** - Comprehensive quality checks with incremental builds (NEW!)
1510
- 2. **Quick Statistics** - Get health scores and metrics
1511
- 3. **Dependency Auto-Fixer** - Auto-fix dependency issues
1512
- 4. **CI Combo Tool** - Run all checks in one command
1513
- 5. **Build Order Calculator** - Determine correct build order
1514
- 6. **Types Order Calculator** - Calculate types generation order
1515
- 7. **Command Health Checker** - Verify all CLI commands work
1516
- 8. **TypeScript Types Checker** - Ensure all packages generate types
1517
-
1518
- ### Infrastructure Tools (3)
1519
- 1. **Repository Sync** - Sync DevKit assets across projects
1520
- 2. **Path Aliases Generator** - Generate workspace path aliases
1521
- 3. **Tsup External Generator** - Generate external dependencies list
1633
+ DevKit provides **29 tools** for monorepo management and quality assurance:
1634
+
1635
+ ### Analysis Tools (12)
1636
+ 1. **Import Checker** (`kb-devkit-check-imports`) - Broken imports, unused deps, circular deps
1637
+ 2. **Export Checker** (`kb-devkit-check-exports`) - Unused exports and dead code
1638
+ 3. **Duplicate Checker** (`kb-devkit-check-duplicates`) - Duplicate dependencies
1639
+ 4. **Structure Checker** (`kb-devkit-check-structure`) - Package structure validation
1640
+ 5. **Naming Validator** (`kb-devkit-validate-naming`) - Pyramid Rule naming convention
1641
+ 6. **Path Validator** (`kb-devkit-check-paths`) - Workspace deps, exports, bin paths
1642
+ 7. **TypeScript Types Audit** (`kb-devkit-types-audit`) - Deep type safety across monorepo
1643
+ 8. **Visualizer** (`kb-devkit-visualize`) - Dependency graphs and stats
1644
+ 9. **Architecture Audit** (`kb-devkit-architecture`) - Structural anomalies, layer violations
1645
+ 10. **Deprecated Checker** (`kb-devkit-check-deprecated`) - Find `@deprecated` markers
1646
+ 11. **Build Readiness** (`kb-devkit-check-build-readiness`) - Pre-bundle dependency analysis
1647
+ 12. **Config Checker** (`kb-devkit-check-configs`) - Drift from standard templates
1648
+
1649
+ ### Automation Tools (11)
1650
+ 1. **QA Runner** (`kb-devkit-qa`) - ⚡ Incremental builds + lint + types + tests
1651
+ 2. **QA History** (`kb-devkit-qa-history`) - Track metrics over time, detect regressions
1652
+ 3. **Core Gate** (`kb-devkit-core-gate`) - Zero-failure gate for 6 core monorepos
1653
+ 4. **Quick Statistics** (`kb-devkit-stats`) - Health scores and metrics
1654
+ 5. **Dependency Auto-Fixer** (`kb-devkit-fix-deps`) - Auto-fix dependency issues
1655
+ 6. **CI Combo Tool** (`kb-devkit-ci`) - All static checks in one command
1656
+ 7. **Build Order Calculator** (`kb-devkit-build-order`) - Correct build order with layers
1657
+ 8. **Types Order Calculator** (`kb-devkit-types-order`) - Types generation order
1658
+ 9. **Command Health Checker** (`kb-devkit-check-commands`) - Verify all CLI commands work
1659
+ 10. **TypeScript Types Checker** (`kb-devkit-check-types`) - Ensure packages generate types
1660
+ 11. **Scripts Checker** (`kb-devkit-check-scripts`) - Validate required package scripts
1661
+
1662
+ ### Infrastructure Tools (6)
1663
+ 1. **Repository Sync** (`kb-devkit-sync`) - Sync DevKit assets across projects
1664
+ 2. **Path Aliases Generator** (`kb-devkit-paths`) - Generate workspace path aliases
1665
+ 3. **Tsup External Generator** (`kb-devkit-tsup-external`) - Generate external deps list
1666
+ 4. **Freshness Tracker** (`kb-devkit-freshness`) - Detect stale packages in dep chains
1667
+ 5. **Config Migrator** (`kb-devkit-migrate-configs`) - Mass migrate configs to current templates
1668
+ 6. **Health Check** (`kb-devkit-health`) - Comprehensive health check with grade A–F
1522
1669
 
1523
1670
  ### Quick Access
1524
1671
  ```bash
1525
1672
  # Quality Assurance (recommended)
1526
- npx kb-devkit-qa # ⚡ Incremental builds (~20s)
1527
- npx kb-devkit-ci # All static checks
1673
+ npx kb-devkit-qa # ⚡ Incremental builds (~20s)
1674
+ npx kb-devkit-qa-history regressions # Detect regressions
1675
+ npx kb-devkit-core-gate # Zero-failure gate for core
1676
+ npx kb-devkit-ci # All static checks
1528
1677
 
1529
1678
  # Analysis
1530
- npx kb-devkit-check-imports # Imports
1531
- npx kb-devkit-check-exports # Exports
1532
- npx kb-devkit-types-audit # Type safety
1679
+ npx kb-devkit-check-imports # Imports
1680
+ npx kb-devkit-check-exports # Exports
1681
+ npx kb-devkit-types-audit # Type safety
1682
+ npx kb-devkit-architecture --md # Architecture report
1683
+ npx kb-devkit-freshness --only-stale # Stale packages
1533
1684
 
1534
1685
  # Automation
1535
- npx kb-devkit-fix-deps --dry-run # Fix dependencies
1536
- npx kb-devkit-build-order --layers # Build order
1537
- npx kb-devkit-stats --health # Health score
1686
+ npx kb-devkit-fix-deps --dry-run # Fix dependencies
1687
+ npx kb-devkit-build-order --layers # Build order
1688
+ npx kb-devkit-stats --health # Health score
1689
+ npx kb-devkit-check-configs # Config drift
1538
1690
  ```
1539
1691
 
1540
1692
  ## License
@@ -42,6 +42,9 @@ import fs from 'node:fs';
42
42
  import path from 'node:path';
43
43
  import { fileURLToPath } from 'node:url';
44
44
 
45
+ // Shared package discovery — supports both flat and categorized layouts
46
+ import { findPackages } from './lib/find-packages.mjs';
47
+
45
48
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
46
49
 
47
50
  // ANSI colors
@@ -80,38 +83,13 @@ const options = {
80
83
  // ============================
81
84
 
82
85
  /**
83
- * Find all packages in monorepo
86
+ * Extract repo name from a package.json path.
87
+ * Handles both flat (kb-labs-core/packages/...) and categorized (platform/kb-labs-core/packages/...) layouts.
84
88
  */
85
- function findPackages(rootDir) {
86
- const packages = [];
87
- const entries = fs.readdirSync(rootDir, { withFileTypes: true });
88
-
89
- for (const entry of entries) {
90
- if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
91
-
92
- const repoPath = path.join(rootDir, entry.name);
93
- const packagesDir = path.join(repoPath, 'packages');
94
-
95
- if (!fs.existsSync(packagesDir)) {continue;}
96
-
97
- const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
98
-
99
- for (const pkgDir of packageDirs) {
100
- if (!pkgDir.isDirectory()) {continue;}
101
-
102
- const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
103
-
104
- if (fs.existsSync(packageJsonPath)) {
105
- packages.push({
106
- path: packageJsonPath,
107
- dir: path.join(packagesDir, pkgDir.name),
108
- repository: entry.name,
109
- });
110
- }
111
- }
112
- }
113
-
114
- return packages;
89
+ function extractRepoFromPath(pkgPath) {
90
+ const parts = pkgPath.split(path.sep);
91
+ const repo = parts.find((p) => p.startsWith('kb-labs-'));
92
+ return repo || 'unknown';
115
93
  }
116
94
 
117
95
  /**
@@ -1074,7 +1052,11 @@ async function main() {
1074
1052
  }
1075
1053
 
1076
1054
  // Phase 1: Data Collection
1077
- const packages = findPackages(rootDir);
1055
+ const packages = findPackages(rootDir).map((pkgPath) => ({
1056
+ path: pkgPath,
1057
+ dir: path.dirname(pkgPath),
1058
+ repository: extractRepoFromPath(pkgPath),
1059
+ }));
1078
1060
 
1079
1061
  if (packages.length === 0) {
1080
1062
  log('⚠️ No KB Labs packages found', 'yellow');
@@ -49,36 +49,8 @@ const options = {
49
49
  package: args.find((arg) => arg.startsWith('--package='))?.split('=')[1],
50
50
  };
51
51
 
52
- /**
53
- * Find all packages
54
- */
55
- function findPackages(rootDir) {
56
- const packages = [];
57
- const entries = fs.readdirSync(rootDir, { withFileTypes: true });
58
-
59
- for (const entry of entries) {
60
- if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
61
-
62
- const repoPath = path.join(rootDir, entry.name);
63
- const packagesDir = path.join(repoPath, 'packages');
64
-
65
- if (!fs.existsSync(packagesDir)) {continue;}
66
-
67
- const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
68
-
69
- for (const pkgDir of packageDirs) {
70
- if (!pkgDir.isDirectory()) {continue;}
71
-
72
- const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
73
-
74
- if (fs.existsSync(packageJsonPath)) {
75
- packages.push(packageJsonPath);
76
- }
77
- }
78
- }
79
-
80
- return packages;
81
- }
52
+ // Shared package discovery — supports both flat and categorized layouts
53
+ import { findPackages } from './lib/find-packages.mjs';
82
54
 
83
55
  /**
84
56
  * Build dependency graph
@@ -22,6 +22,9 @@ import { readFileSync, existsSync, readdirSync } from 'fs';
22
22
  import { join, dirname } from 'path';
23
23
  import { fileURLToPath } from 'url';
24
24
 
25
+ // Shared package discovery — supports both flat and categorized layouts
26
+ import { findPackages } from './lib/find-packages.mjs';
27
+
25
28
  const __dirname = dirname(fileURLToPath(import.meta.url));
26
29
  const rootDir = join(__dirname, '../..');
27
30
 
@@ -34,31 +37,18 @@ console.log('🔍 KB Labs Build Readiness Checker\n');
34
37
 
35
38
  // Step 1: Find all workspace packages
36
39
  function findWorkspacePackages() {
37
- const packages = [];
38
- const monorepos = readdirSync(rootDir).filter(d => d.startsWith('kb-labs-'));
39
-
40
- for (const monorepo of monorepos) {
41
- const packagesDir = join(rootDir, monorepo, 'packages');
42
- if (!existsSync(packagesDir)) {continue;}
43
-
44
- for (const pkg of readdirSync(packagesDir)) {
45
- const pkgJsonPath = join(packagesDir, pkg, 'package.json');
46
- if (!existsSync(pkgJsonPath)) {continue;}
47
-
48
- try {
49
- const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
50
- packages.push({
51
- name: pkgJson.name,
52
- path: join(packagesDir, pkg),
53
- pkgJson,
54
- });
55
- } catch (e) {
56
- // Skip invalid package.json
57
- }
40
+ return findPackages(rootDir).map((pkgJsonPath) => {
41
+ try {
42
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
43
+ return {
44
+ name: pkgJson.name,
45
+ path: dirname(pkgJsonPath),
46
+ pkgJson,
47
+ };
48
+ } catch (e) {
49
+ return null;
58
50
  }
59
- }
60
-
61
- return packages;
51
+ }).filter(Boolean);
62
52
  }
63
53
 
64
54
  // Step 2: Get import checker results