wize-dev-kit 0.3.0 → 0.4.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 (83) hide show
  1. package/AGENTS.md +21 -0
  2. package/ARCH.md +40 -4
  3. package/CHANGELOG.md +50 -0
  4. package/DECISIONS.md +13 -0
  5. package/README.md +11 -2
  6. package/package.json +3 -2
  7. package/src/core-skills/module.yaml +4 -0
  8. package/src/core-skills/wize-spec/assets/headless-schemas.md +39 -0
  9. package/src/core-skills/wize-spec/assets/spec-template.md +40 -0
  10. package/src/core-skills/wize-spec/customize.toml +20 -0
  11. package/src/core-skills/wize-spec/skill.md +110 -0
  12. package/src/method-skills/1-analysis/wize-document-project/documentation-requirements.csv +12 -0
  13. package/src/method-skills/1-analysis/wize-document-project/templates/api-contracts-template.md +38 -0
  14. package/src/method-skills/1-analysis/wize-document-project/templates/architecture-template.md +54 -0
  15. package/src/method-skills/1-analysis/wize-document-project/templates/component-inventory-template.md +40 -0
  16. package/src/method-skills/1-analysis/wize-document-project/templates/contribution-guide-template.md +34 -0
  17. package/src/method-skills/1-analysis/wize-document-project/templates/data-models-template.md +36 -0
  18. package/src/method-skills/1-analysis/wize-document-project/templates/deep-dive-template.md +312 -0
  19. package/src/method-skills/1-analysis/wize-document-project/templates/deployment-guide-template.md +42 -0
  20. package/src/method-skills/1-analysis/wize-document-project/templates/development-guide-template.md +61 -0
  21. package/src/method-skills/1-analysis/wize-document-project/templates/index-template.md +185 -0
  22. package/src/method-skills/1-analysis/wize-document-project/templates/project-overview-template.md +110 -0
  23. package/src/method-skills/1-analysis/wize-document-project/templates/project-scan-report-schema.json +159 -0
  24. package/src/method-skills/1-analysis/wize-document-project/templates/source-tree-template.md +142 -0
  25. package/src/method-skills/1-analysis/wize-document-project/workflow.md +23 -0
  26. package/src/method-skills/1-analysis/wize-domain-research/customize.toml +14 -0
  27. package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-01-init.md +49 -0
  28. package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-02-domain-analysis.md +39 -0
  29. package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-03-competitive-landscape.md +39 -0
  30. package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-04-regulatory-focus.md +39 -0
  31. package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-05-technical-trends.md +39 -0
  32. package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-06-research-synthesis.md +43 -0
  33. package/src/method-skills/1-analysis/wize-domain-research/research.template.md +31 -0
  34. package/src/method-skills/1-analysis/wize-domain-research/workflow.md +51 -0
  35. package/src/method-skills/1-analysis/wize-market-research/customize.toml +14 -0
  36. package/src/method-skills/1-analysis/wize-market-research/research.template.md +31 -0
  37. package/src/method-skills/1-analysis/wize-market-research/steps/step-01-init.md +57 -0
  38. package/src/method-skills/1-analysis/wize-market-research/steps/step-02-customer-behavior.md +39 -0
  39. package/src/method-skills/1-analysis/wize-market-research/steps/step-03-customer-pain-points.md +37 -0
  40. package/src/method-skills/1-analysis/wize-market-research/steps/step-04-customer-decisions.md +39 -0
  41. package/src/method-skills/1-analysis/wize-market-research/steps/step-05-competitive-analysis.md +37 -0
  42. package/src/method-skills/1-analysis/wize-market-research/steps/step-06-research-completion.md +47 -0
  43. package/src/method-skills/1-analysis/wize-market-research/workflow.md +59 -0
  44. package/src/method-skills/1-analysis/wize-technical-research/customize.toml +14 -0
  45. package/src/method-skills/1-analysis/wize-technical-research/research.template.md +31 -0
  46. package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-01-init.md +49 -0
  47. package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-02-technical-overview.md +45 -0
  48. package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-03-integration-patterns.md +39 -0
  49. package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-04-architectural-patterns.md +39 -0
  50. package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-05-implementation-research.md +42 -0
  51. package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-06-research-synthesis.md +46 -0
  52. package/src/method-skills/1-analysis/wize-technical-research/workflow.md +53 -0
  53. package/src/method-skills/3-solutioning/wize-create-architecture/architecture-decision-template.md +41 -0
  54. package/src/method-skills/3-solutioning/wize-create-architecture/customize.toml +20 -0
  55. package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-01-init.md +95 -0
  56. package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-01b-continue.md +32 -0
  57. package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-02-context.md +126 -0
  58. package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-03-starter.md +110 -0
  59. package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-04-decisions.md +129 -0
  60. package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-05-patterns.md +109 -0
  61. package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-06-structure.md +97 -0
  62. package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-07-validation.md +124 -0
  63. package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-08-complete.md +59 -0
  64. package/src/method-skills/3-solutioning/wize-create-architecture/workflow.md +38 -201
  65. package/src/method-skills/4-implementation/wize-code-review/customize.toml +21 -0
  66. package/src/method-skills/4-implementation/wize-code-review/steps/step-01-gather-context.md +64 -0
  67. package/src/method-skills/4-implementation/wize-code-review/steps/step-02-review.md +38 -0
  68. package/src/method-skills/4-implementation/wize-code-review/steps/step-03-triage.md +43 -0
  69. package/src/method-skills/4-implementation/wize-code-review/steps/step-04-present.md +100 -0
  70. package/src/method-skills/4-implementation/wize-code-review/workflow.md +34 -89
  71. package/src/method-skills/module.yaml +19 -0
  72. package/src/tea-skills/wize-tea-risk/workflow.md +11 -0
  73. package/tools/installer/commands/doctor.js +27 -1
  74. package/tools/installer/commands/document-project.js +93 -0
  75. package/tools/installer/document-project/batch-scanner.js +93 -0
  76. package/tools/installer/document-project/classify.js +170 -0
  77. package/tools/installer/document-project/modes/deep-dive.js +196 -0
  78. package/tools/installer/document-project/modes/full-rescan.js +15 -0
  79. package/tools/installer/document-project/modes/initial-scan.js +100 -0
  80. package/tools/installer/document-project/modes/quick.js +211 -0
  81. package/tools/installer/document-project/render-index.js +101 -0
  82. package/tools/installer/document-project/state.js +110 -0
  83. package/tools/installer/wize-cli.js +46 -30
@@ -2,118 +2,63 @@
2
2
  code: wize-code-review
3
3
  name: Code Review
4
4
  phase: 4-implementation
5
- owner: wize-agent-dev # Shuri (peer Shuri — done at PR open time)
5
+ owner: wize-agent-dev # Shuri (peer review)
6
6
  status: ready
7
7
  ---
8
8
 
9
9
  # Code Review
10
10
 
11
- **Goal.** Audit **code health** on the PR. Separate from Hawkeye's `tea-review` (which audits AC fulfillment). Both run on every story PR; they're complementary.
11
+ **Goal.** Review code changes adversarially using parallel review layers and structured triage into actionable categories.
12
12
 
13
- Shuri reviews peer PRs. Tony reviews when architecture is at stake.
13
+ This is **Shuri's peer code review** — separate from Hawkeye's `wize-tea-review` (which audits AC fulfillment). Both run on every story PR; they are complementary.
14
14
 
15
15
  ## When to run
16
16
 
17
- Every PR that ships code. Quick-dev PRs get a lighter review (skip code-architecture checks unless they touched architecture).
17
+ Every PR that ships code. Quick-dev PRs get a lighter review (skip architecture checks unless they touched architecture).
18
18
 
19
19
  ## Inputs
20
20
 
21
- - The PR (diff + tests).
22
- - Story file (for context what the PR is supposed to accomplish).
23
- - Linked design system (when components change).
21
+ - The diff, PR, branch, or commit range to review.
22
+ - The spec/story file for context (optional but recommended).
23
+ - Existing code style and project context from `.wize/knowledge/document-project/`.
24
24
 
25
- ## Output
25
+ ## Outputs
26
26
 
27
- - Inline comments on the PR.
28
- - Final review verdict: `approve` / `request-changes` / `comment`.
27
+ - Inline-style findings presented in the conversation.
28
+ - Optional patch application if the user chooses to fix findings now.
29
+ - Updated story file with a `### Review Findings` section when a spec file is provided.
30
+ - Updated sprint status when a story key is discovered.
29
31
 
30
- ## What this checks
32
+ ## Workflow architecture
31
33
 
32
- ### Naming + structure
33
- - Are types, functions, variables named for **what they are**, not **how they're used**?
34
- - Are files in the right folder per the architecture?
35
- - Are exports minimal? Module boundaries respected?
36
- - Are there new abstractions justified by the story or premature?
34
+ This skill uses **step-file architecture**:
37
35
 
38
- ### Tests
39
- - Do tests cover the changed behavior (not just coverage %)?
40
- - Are they fast and isolated?
41
- - Are mocks at the boundary, not inside the unit?
42
- - Any `test.skip` / `.only` left in?
36
+ - Each step is self-contained and followed exactly.
37
+ - Sequential enforcement: complete steps in order, no skipping.
38
+ - State tracked in frontmatter variables set at runtime.
39
+ - Append-only building of findings.
43
40
 
44
- ### Security (obvious-misses)
45
- - Input validation at boundaries.
46
- - Tokens / secrets / PII never logged.
47
- - SQL parameterized, not concatenated.
48
- - New deps audited; no known CVEs introduced.
49
- - Auth context checked on every server entry point.
41
+ ## Critical rules
50
42
 
51
- ### Performance (obvious-misses)
52
- - No N+1 queries.
53
- - No `await` in tight loops without batching.
54
- - No new sync I/O on hot paths.
55
- - Bundle delta acceptable (size of new front-end imports).
43
+ - **Read completely** each step file before acting.
44
+ - **Never** load multiple step files simultaneously.
45
+ - **Always** halt at checkpoints and wait for human input.
46
+ - Use CWD-relative `path:line` for every code reference.
56
47
 
57
- ### Architectural drift
58
- - Story didn't quietly introduce a new layer / new pattern.
59
- - If it did, an ADR was opened or a comment justifies it.
60
- - Components reused from design system; new components added to system if reusable.
48
+ ## On activation
61
49
 
62
- ### Style + convention
63
- - Follows lint / format / type rules.
64
- - Comments explain *why*, not *what*.
65
- - Dead code removed.
66
- - TODOs have an owner + a ticket.
50
+ 1. Load `.wize/config/project.toml` and `.wize/config/user.toml`.
51
+ 2. Resolve `user_name`, `communication_language`, `document_output_language`, `implementation_artifacts`, `planning_artifacts`.
52
+ 3. Greet the user in `communication_language`.
53
+ 4. Read fully and follow `./steps/step-01-gather-context.md`.
67
54
 
68
- ## What this does NOT check
55
+ ## Steps
69
56
 
70
- - Whether ACs are met that's Hawkeye's `tea-review`.
71
- - Whether the design is right that's reviewed in pull-request walk-through, ADR review, or party-mode.
72
-
73
- Don't conflate. Two reviewers, two scopes.
74
-
75
- ## Comment style
76
-
77
- Use these prefixes:
78
-
79
- | Prefix | Meaning |
80
- |---|---|
81
- | `nit:` | Cosmetic; non-blocking |
82
- | `q:` | Question; might be a misunderstanding |
83
- | `praise:` | Real call-outs; teams need them |
84
- | `suggestion:` | Idea, the author decides |
85
- | `blocking:` | Must change before merge |
86
- | `out-of-scope:` | Real issue, separate story |
87
-
88
- Never `LGTM` without scanning. Never `LGTM` with `blocking:` open.
89
-
90
- ## Verdict
91
-
92
- - **approve** — all blockings resolved.
93
- - **request-changes** — at least one `blocking:`.
94
- - **comment** — reviewed, no opinion (rare; used for early-draft PRs).
95
-
96
- ## PR-open checklist (Shuri's self-review)
97
-
98
- Before opening, Shuri runs through:
99
-
100
- - [ ] CI green locally.
101
- - [ ] Lint + format clean.
102
- - [ ] Type-check clean.
103
- - [ ] No `console.log` / `dbg!` / debug printf.
104
- - [ ] No `test.skip` / `.only`.
105
- - [ ] Reading the diff right now, can I explain every line?
106
- - [ ] Self-walk: open the changed screen / call the changed endpoint.
107
- - [ ] Story status flipped to `ready-for-review`.
108
-
109
- ## Anti-patterns Shuri rejects in herself
110
-
111
- - Approving without reading.
112
- - Approving on the basis of green CI alone.
113
- - "Big PR; will trust" — refuse and ask for slicing.
114
- - Inline suggestions for full rewrites — open a follow-up instead.
115
- - Demanding stylistic preferences not in the lint config.
57
+ 1. `step-01-gather-context.md` identify the diff source, construct `{diff_output}`, set `{review_mode}` and `{spec_file}`.
58
+ 2. `step-02-review.md` launch parallel review layers (Blind Hunter, Edge Case Hunter, Acceptance Auditor).
59
+ 3. `step-03-triage.md` — normalize, deduplicate, and classify findings into `decision_needed`, `patch`, `defer`, `dismiss`.
60
+ 4. `step-04-present.md` present findings, resolve decisions, apply patches, update story status.
116
61
 
117
62
  ## Hand-off
118
63
 
119
- > Reviewed PR #418 (E02-S02). 2 nits, 1 blocking on auth-context check missing in one new route. Shuri to fix; re-review needed; then Hawkeye runs `tea-review`.
64
+ > Code review complete for `{story_key or change}`. `{decision_needed}` decision(s), `{patch}` patch(es), `{defer}` deferred, `{dismissed}` dismissed as noise. Next: re-run review or continue to `wize-tea-review` (Hawkeye).
@@ -97,9 +97,28 @@ agents:
97
97
  team: software-development
98
98
  description: "Wakanda forever — agora compila. TDD red-green-refactor, segurança, performance, código limpo. Estilo: gênio inovadora, rápida, protetora do ecossistema, sem fluff."
99
99
 
100
+ # Notable skills shipped by method (discovery is automatic via workflow.md/skill.md walkers).
101
+ skills:
102
+ - code: wize-market-research
103
+ name: "Market Research"
104
+ description: "Competition and customer research for the product brief and PRD."
105
+ - code: wize-domain-research
106
+ name: "Domain Research"
107
+ description: "Industry, regulatory, and technical-trend research."
108
+ - code: wize-technical-research
109
+ name: "Technical Research"
110
+ description: "Technology, architecture, and implementation-pattern research."
111
+ - code: wize-create-architecture
112
+ name: "Create Architecture"
113
+ description: "Collaborative step-by-step architecture design with 8 steps."
114
+ - code: wize-code-review
115
+ name: "Code Review"
116
+ description: "Adversarial code review with parallel layers and structured triage."
117
+
100
118
  # Phase directories (declarative discovery for the installer):
101
119
  phases:
102
120
  - "1-analysis"
103
121
  - "2-plan-workflows"
104
122
  - "3-solutioning"
105
123
  - "4-implementation"
124
+
@@ -19,6 +19,7 @@ Hawkeye drives. Tony co-signs. Runs **once**, right after architecture is signed
19
19
  - `.wize/solutioning/epics/`
20
20
  - `.wize/planning/nfr-principles.md`
21
21
  - `.wize/knowledge/document-project/risk-spots.md` (brownfield)
22
+ - `.wize/knowledge/document-project/index.md` (documentation gap markers)
22
23
 
23
24
  ## Output
24
25
 
@@ -115,6 +116,16 @@ The narrative explains the matrix; the YAML is the structured truth. Hawkeye wri
115
116
  - Other stories follow the default 70/20/10 split.
116
117
  ```
117
118
 
119
+ ## Documentation gap risk category
120
+
121
+ In brownfield repos, missing or stale documentation is an operational risk: new contributors make unsafe assumptions, AI agents hallucinate context, and reviews miss implicit contracts. Hawkeye treats it as a first-class risk area.
122
+
123
+ | Symptom | Severity | Rationale | Mitigation |
124
+ |---|---|---|---|
125
+ | `index.md` contains `_(To be generated)_` markers | medium | Conditional docs were skipped; the baseline is incomplete. | Schedule `wize-document-project initial_scan` for the next cooldown. |
126
+ | Baseline files older than 60 days | medium | Docs may no longer reflect the codebase. | Run `wize-refresh-knowledge` at sprint end. |
127
+ | No baseline exists in a brownfield repo | high | Team is flying blind; architecture and conventions are tribal knowledge. | Run `wize-dev-kit document-project quick` immediately. |
128
+
118
129
  ## Anti-patterns Hawkeye rejects
119
130
 
120
131
  - **Findings without mitigation.** A finding without a contract is a wish.
@@ -130,7 +130,21 @@ function knowledgeStatus(projectRoot) {
130
130
  const pendingLines = fileExists(pendingFile)
131
131
  ? readSafe(pendingFile).split('\n').filter(l => l.trim() && !l.startsWith('#')).length
132
132
  : 0;
133
- return { exists: true, files: refreshed, pendingLines };
133
+
134
+ const indexPath = path.join(root, 'index.md');
135
+ const indexContent = fileExists(indexPath) ? readSafe(indexPath) : '';
136
+ const toBeGeneratedMarkers = (indexContent.match(/_\(To be generated\)_/g) || []).length;
137
+
138
+ const statePath = path.join(root, 'project-scan-report.json');
139
+ const stateExists = fileExists(statePath);
140
+ const stateContent = stateExists ? readSafe(statePath) : '';
141
+ let stateAgeDays = null;
142
+ try {
143
+ const state = JSON.parse(stateContent);
144
+ stateAgeDays = daysAgo(state.timestamps && state.timestamps.last_updated);
145
+ } catch (_) {}
146
+
147
+ return { exists: true, files: refreshed, pendingLines, toBeGeneratedMarkers, stateExists, stateAgeDays };
134
148
  }
135
149
 
136
150
  function gitInfo(projectRoot) {
@@ -247,6 +261,18 @@ async function cmdDoctor({ kitRoot, projectRoot, opts = {} } = {}) {
247
261
  suggestions.push({ level: 'info', text: `${knowledge.pendingLines} notes piled up in _pending.md. Time to run \`wize-refresh-knowledge\`.` });
248
262
  }
249
263
  }
264
+ const markerText = knowledge.toBeGeneratedMarkers > 0 ? `${knowledge.toBeGeneratedMarkers} marker(s)` : 'none';
265
+ log(` To be generated markers: ${markerText}`);
266
+ if (knowledge.toBeGeneratedMarkers >= 5) {
267
+ suggestions.push({ level: 'warn', text: `${knowledge.toBeGeneratedMarkers} docs marked "To be generated" in index.md. Run \`wize-dev-kit document-project initial_scan deep\` to fill them.` });
268
+ }
269
+ const stateText = knowledge.stateExists
270
+ ? (knowledge.stateAgeDays == null ? 'project-scan-report.json exists (age unknown)' : `project-scan-report.json ${knowledge.stateAgeDays}d old`)
271
+ : 'no project-scan-report.json';
272
+ log(` Scan state: ${stateText}`);
273
+ if (!knowledge.stateExists) {
274
+ suggestions.push({ level: 'info', text: 'No scan state file found. Run `wize-dev-kit document-project quick` to create a baseline + state.' });
275
+ }
250
276
  }
251
277
 
252
278
  // -- Section: Harness CLIs --
@@ -0,0 +1,93 @@
1
+ // `wize-dev-kit document-project` — CLI command entry point.
2
+ //
3
+ // Parses mode + flags and delegates to the appropriate scan implementation.
4
+ // Non-quick modes are stubs here; later stories fill them in.
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('node:fs');
9
+ const path = require('node:path');
10
+ const { runQuick } = require('../document-project/modes/quick.js');
11
+ const { runInitialScan } = require('../document-project/modes/initial-scan.js');
12
+ const { runFullRescan } = require('../document-project/modes/full-rescan.js');
13
+ const { runDeepDive } = require('../document-project/modes/deep-dive.js');
14
+ const { loadState, archiveOldState } = require('../document-project/state.js');
15
+
16
+ const VALID_MODES = new Set(['quick', 'initial_scan', 'full_rescan', 'deep_dive']);
17
+ const VALID_SCAN_LEVELS = new Set(['quick', 'deep', 'exhaustive']);
18
+
19
+ function parseMode(args) {
20
+ let mode = 'quick';
21
+ let resume = false;
22
+ let scanLevel = 'quick';
23
+ let target = null;
24
+
25
+ for (let i = 0; i < args.length; i++) {
26
+ const arg = args[i];
27
+ if (arg === '--resume') {
28
+ resume = true;
29
+ continue;
30
+ }
31
+ if (arg === '--target') {
32
+ target = args[i + 1];
33
+ i++;
34
+ continue;
35
+ }
36
+ if (arg.startsWith('--')) {
37
+ return { mode: null, error: `unknown option: ${arg}` };
38
+ }
39
+ if (VALID_SCAN_LEVELS.has(arg)) {
40
+ scanLevel = arg;
41
+ continue;
42
+ }
43
+ if (mode === 'quick' && VALID_MODES.has(arg)) {
44
+ mode = arg;
45
+ } else if (mode === 'quick') {
46
+ return { mode: null, error: `unknown mode: ${arg}. Valid modes: ${[...VALID_MODES].join(', ')}` };
47
+ } else {
48
+ return { mode: null, error: `unexpected extra argument: ${arg}` };
49
+ }
50
+ }
51
+
52
+ if (!VALID_MODES.has(mode)) {
53
+ return { mode: null, error: `unknown mode: ${mode}. Valid modes: ${[...VALID_MODES].join(', ')}` };
54
+ }
55
+
56
+ return { mode, resume, scanLevel, target };
57
+ }
58
+
59
+ function cmdDocumentProject({ kitRoot, projectRoot, args = [], opts = {} }) {
60
+ const log = opts.log || console.log;
61
+ const parsed = parseMode(args);
62
+
63
+ if (parsed.mode === null) {
64
+ log(parsed.error);
65
+ return { ok: false, error: parsed.error, exitCode: 1 };
66
+ }
67
+
68
+ if (parsed.mode === 'quick') {
69
+ const r = runQuick(projectRoot, { log });
70
+ return { ok: true, mode: 'quick', ...r };
71
+ }
72
+
73
+ if (parsed.mode === 'initial_scan') {
74
+ const r = runInitialScan(projectRoot, { scanLevel: parsed.scanLevel, log });
75
+ return { ok: true, mode: 'initial_scan', ...r };
76
+ }
77
+
78
+ if (parsed.mode === 'full_rescan') {
79
+ const r = runFullRescan(projectRoot, { scanLevel: parsed.scanLevel, log });
80
+ return { ok: true, mode: 'full_rescan', ...r };
81
+ }
82
+
83
+ if (parsed.mode === 'deep_dive') {
84
+ const r = runDeepDive(projectRoot, { target: parsed.target, log });
85
+ return r;
86
+ }
87
+
88
+ log(`document-project ${parsed.mode} not implemented`);
89
+ return { ok: false, error: 'not implemented', exitCode: 1 };
90
+ }
91
+
92
+ module.exports = { cmdDocumentProject, parseMode };
93
+
@@ -0,0 +1,93 @@
1
+ // Batch scanner for `wize-dev-kit document-project`.
2
+ //
3
+ // Walks a repo in subfolder-sized batches, skipping noise directories.
4
+ // Returns summaries without loading entire trees into memory.
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('node:fs');
9
+ const path = require('node:path');
10
+
11
+ const DEFAULT_IGNORE = new Set([
12
+ 'node_modules',
13
+ '.git',
14
+ 'dist',
15
+ 'build',
16
+ 'coverage',
17
+ '.cache',
18
+ '.tmp',
19
+ '.wize',
20
+ '.claude',
21
+ '.cursor',
22
+ '.kimi',
23
+ '.opencode',
24
+ '.windsurf',
25
+ '.continue',
26
+ '.codex',
27
+ '.antigravity'
28
+ ]);
29
+
30
+ const MAX_FILE_LOC = 5000;
31
+
32
+ function shouldIgnore(name, customIgnore = []) {
33
+ if (DEFAULT_IGNORE.has(name)) return true;
34
+ for (const pattern of customIgnore) {
35
+ if (name === pattern || (pattern.startsWith('*') && name.endsWith(pattern.slice(1)))) return true;
36
+ }
37
+ return false;
38
+ }
39
+
40
+ function listSubfolders(rootDir, options = {}) {
41
+ const out = [];
42
+ let entries;
43
+ try { entries = fs.readdirSync(rootDir, { withFileTypes: true }); } catch { return out; }
44
+ for (const e of entries) {
45
+ if (!e.isDirectory()) continue;
46
+ if (shouldIgnore(e.name, options.ignore)) continue;
47
+ out.push(path.join(rootDir, e.name));
48
+ }
49
+ return out;
50
+ }
51
+
52
+ function scanFolder(folderPath, options = {}) {
53
+ const files = [];
54
+ let totalLoc = 0;
55
+ const stack = [folderPath];
56
+ while (stack.length) {
57
+ const dir = stack.pop();
58
+ let entries;
59
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { continue; }
60
+ for (const e of entries) {
61
+ const full = path.join(dir, e.name);
62
+ if (e.isDirectory()) {
63
+ if (!shouldIgnore(e.name, options.ignore)) stack.push(full);
64
+ continue;
65
+ }
66
+ if (!e.isFile()) continue;
67
+ if (/\.(min\.js|map)$/.test(e.name)) continue;
68
+ const stat = fs.statSync(full);
69
+ const loc = stat.size; // proxy; exact line count is optional
70
+ const info = {
71
+ path: full,
72
+ relative: path.relative(folderPath, full),
73
+ size: stat.size,
74
+ loc: loc > MAX_FILE_LOC ? MAX_FILE_LOC : loc,
75
+ skipped: loc > MAX_FILE_LOC
76
+ };
77
+ files.push(info);
78
+ totalLoc += info.loc;
79
+ }
80
+ }
81
+ return { folder: folderPath, files, totalLoc, fileCount: files.length };
82
+ }
83
+
84
+ function batchScanner(rootDir, options = {}) {
85
+ const folders = options.folders || listSubfolders(rootDir, options);
86
+ const results = [];
87
+ for (const folder of folders) {
88
+ results.push(scanFolder(folder, options));
89
+ }
90
+ return results;
91
+ }
92
+
93
+ module.exports = { batchScanner, listSubfolders, scanFolder, shouldIgnore, MAX_FILE_LOC };
@@ -0,0 +1,170 @@
1
+ // Project-type classifier for `wize-dev-kit document-project`.
2
+ //
3
+ // Detects the project type(s) of a repo from file patterns, then decides
4
+ // whether the repo is a monolith, multi-part, or monorepo.
5
+ // No source files are read — this is pattern-only detection.
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('node:fs');
10
+ const path = require('node:path');
11
+
12
+ const MULTI_PART_FOLDERS = ['client', 'server', 'api', 'web', 'app', 'frontend', 'backend', 'mobile', 'desktop'];
13
+
14
+ function toBool(v) {
15
+ return String(v).toLowerCase() === 'true';
16
+ }
17
+
18
+ function splitPatterns(str) {
19
+ if (!str || str === 'N/A') return [];
20
+ return str.split(';').map(s => s.trim()).filter(Boolean);
21
+ }
22
+
23
+ function loadRequirements(csvPath) {
24
+ const content = fs.readFileSync(csvPath, 'utf-8');
25
+ const lines = content.split(/\r?\n/).filter(l => l.trim());
26
+ const header = lines[0].split(',').map(h => h.trim());
27
+ const rows = [];
28
+ for (let i = 1; i < lines.length; i++) {
29
+ const values = lines[i].split(',').map(v => v.trim());
30
+ const row = {};
31
+ for (let j = 0; j < header.length; j++) {
32
+ const key = header[j];
33
+ const raw = values[j];
34
+ if (key.startsWith('requires_')) {
35
+ row[key] = toBool(raw);
36
+ } else {
37
+ row[key] = raw || '';
38
+ }
39
+ }
40
+ rows.push(row);
41
+ }
42
+ return rows;
43
+ }
44
+
45
+ function globMatch(rootDir, pattern) {
46
+ // Very small pattern matcher: supports * wildcard and trailing / for dirs.
47
+ const isDirPattern = pattern.endsWith('/');
48
+ const base = isDirPattern ? pattern.slice(0, -1) : pattern;
49
+ const parts = base.split('/');
50
+ return walkMatch(rootDir, parts, 0, isDirPattern);
51
+ }
52
+
53
+ function walkMatch(dir, parts, depth, isDirPattern) {
54
+ if (depth >= parts.length) return true;
55
+ let entries;
56
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return false; }
57
+ const part = parts[depth];
58
+ const isLast = depth === parts.length - 1;
59
+ const regex = globToRegex(part);
60
+ for (const e of entries) {
61
+ if (!regex.test(e.name)) continue;
62
+ if (isLast) {
63
+ if (isDirPattern && e.isDirectory()) return true;
64
+ if (!isDirPattern && e.isFile()) return true;
65
+ if (!isDirPattern && e.isDirectory()) {
66
+ // pattern like vite.config.* can match a directory named vite.config.ts? no.
67
+ // But pattern may be a prefix; treat directory match as false for files.
68
+ return false;
69
+ }
70
+ } else if (e.isDirectory()) {
71
+ if (walkMatch(path.join(dir, e.name), parts, depth + 1, isDirPattern)) return true;
72
+ }
73
+ }
74
+ return false;
75
+ }
76
+
77
+ function globToRegex(pattern) {
78
+ const escaped = pattern
79
+ .replace(/\./g, '\\.')
80
+ .replace(/\*/g, '.*')
81
+ .replace(/\?/g, '.');
82
+ return new RegExp(`^${escaped}$`);
83
+ }
84
+
85
+ function scorePart(rootDir, row, { prefer = [] } = {}) {
86
+ let score = 0;
87
+ const keyPatterns = splitPatterns(row.key_file_patterns);
88
+ const dirPatterns = splitPatterns(row.critical_directories);
89
+ for (const p of keyPatterns) {
90
+ if (globMatch(rootDir, p)) score += 2;
91
+ }
92
+ for (const p of dirPatterns) {
93
+ if (globMatch(rootDir, p)) score += 1;
94
+ }
95
+ if (prefer.includes(row.project_type_id)) score += 0.5;
96
+ return score;
97
+ }
98
+
99
+ function detectParts(rootDir) {
100
+ const parts = [];
101
+ const candidates = [];
102
+ let entries;
103
+ try { entries = fs.readdirSync(rootDir, { withFileTypes: true }); } catch { return parts; }
104
+
105
+ for (const e of entries) {
106
+ if (!e.isDirectory()) continue;
107
+ if (!MULTI_PART_FOLDERS.includes(e.name.toLowerCase())) continue;
108
+ candidates.push(e.name);
109
+ }
110
+
111
+ if (candidates.length < 2) return parts;
112
+
113
+ const rows = loadRequirements(requirementsPath());
114
+ for (const partId of candidates) {
115
+ const partRoot = path.join(rootDir, partId);
116
+ const prefer = partId.toLowerCase() === 'server' || partId.toLowerCase() === 'backend' ? ['backend'] : [];
117
+ const scored = rows.map(r => ({ ...r, score: scorePart(partRoot, r, { prefer }) }));
118
+ scored.sort((a, b) => b.score - a.score);
119
+ const best = scored[0];
120
+ if (best && best.score > 0) {
121
+ parts.push({ part_id: partId, project_type_id: best.project_type_id, root_path: partRoot });
122
+ }
123
+ }
124
+ return parts;
125
+ }
126
+
127
+ function isMultiPart(rootDir) {
128
+ return detectParts(rootDir).length >= 2;
129
+ }
130
+
131
+ function classifyProject(rootDir, options = {}) {
132
+ const csvPath = options.csvPath || requirementsPath();
133
+ const rows = loadRequirements(csvPath);
134
+ const parts = detectParts(rootDir);
135
+
136
+ if (parts.length >= 2) {
137
+ return { projectTypes: [...new Set(parts.map(p => p.project_type_id))], parts };
138
+ }
139
+
140
+ const scored = rows.map(r => ({ ...r, score: scorePart(rootDir, r, { prefer: options.prefer || [] }) }));
141
+ scored.sort((a, b) => b.score - a.score);
142
+ const best = scored[0];
143
+ const tied = scored.filter(s => s.score === best.score).map(s => s.project_type_id);
144
+
145
+ if (!best || best.score === 0) {
146
+ return { projectTypes: [], parts: [] };
147
+ }
148
+
149
+ // Cli + library often appear together; prefer both when tied at the top.
150
+ const projectTypes = tied.includes('cli') && tied.includes('library')
151
+ ? ['cli', 'library']
152
+ : [best.project_type_id];
153
+
154
+ return {
155
+ projectTypes,
156
+ parts: [{ part_id: 'root', project_type_id: best.project_type_id, root_path: rootDir }]
157
+ };
158
+ }
159
+
160
+ function requirementsPath() {
161
+ // When running from the installed package, the CSV is next to this file under
162
+ // node_modules/wize-dev-kit/... When running from the source repo, resolve from
163
+ // the repo root via the known relative path.
164
+ const installed = path.resolve(__dirname, '..', '..', '..', 'src', 'method-skills', '1-analysis', 'wize-document-project', 'documentation-requirements.csv');
165
+ if (fs.existsSync(installed)) return installed;
166
+ // Fallback for source repo layout.
167
+ return path.resolve(__dirname, '..', '..', '..', '..', 'src', 'method-skills', '1-analysis', 'wize-document-project', 'documentation-requirements.csv');
168
+ }
169
+
170
+ module.exports = { classifyProject, loadRequirements, isMultiPart, scorePart, requirementsPath, globMatch };