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.
- package/AGENTS.md +21 -0
- package/ARCH.md +40 -4
- package/CHANGELOG.md +50 -0
- package/DECISIONS.md +13 -0
- package/README.md +11 -2
- package/package.json +3 -2
- package/src/core-skills/module.yaml +4 -0
- package/src/core-skills/wize-spec/assets/headless-schemas.md +39 -0
- package/src/core-skills/wize-spec/assets/spec-template.md +40 -0
- package/src/core-skills/wize-spec/customize.toml +20 -0
- package/src/core-skills/wize-spec/skill.md +110 -0
- package/src/method-skills/1-analysis/wize-document-project/documentation-requirements.csv +12 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/api-contracts-template.md +38 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/architecture-template.md +54 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/component-inventory-template.md +40 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/contribution-guide-template.md +34 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/data-models-template.md +36 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/deep-dive-template.md +312 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/deployment-guide-template.md +42 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/development-guide-template.md +61 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/index-template.md +185 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/project-overview-template.md +110 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/project-scan-report-schema.json +159 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/source-tree-template.md +142 -0
- package/src/method-skills/1-analysis/wize-document-project/workflow.md +23 -0
- package/src/method-skills/1-analysis/wize-domain-research/customize.toml +14 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-01-init.md +49 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-02-domain-analysis.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-03-competitive-landscape.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-04-regulatory-focus.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-05-technical-trends.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-06-research-synthesis.md +43 -0
- package/src/method-skills/1-analysis/wize-domain-research/research.template.md +31 -0
- package/src/method-skills/1-analysis/wize-domain-research/workflow.md +51 -0
- package/src/method-skills/1-analysis/wize-market-research/customize.toml +14 -0
- package/src/method-skills/1-analysis/wize-market-research/research.template.md +31 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-01-init.md +57 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-02-customer-behavior.md +39 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-03-customer-pain-points.md +37 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-04-customer-decisions.md +39 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-05-competitive-analysis.md +37 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-06-research-completion.md +47 -0
- package/src/method-skills/1-analysis/wize-market-research/workflow.md +59 -0
- package/src/method-skills/1-analysis/wize-technical-research/customize.toml +14 -0
- package/src/method-skills/1-analysis/wize-technical-research/research.template.md +31 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-01-init.md +49 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-02-technical-overview.md +45 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-03-integration-patterns.md +39 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-04-architectural-patterns.md +39 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-05-implementation-research.md +42 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-06-research-synthesis.md +46 -0
- package/src/method-skills/1-analysis/wize-technical-research/workflow.md +53 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/architecture-decision-template.md +41 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/customize.toml +20 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-01-init.md +95 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-01b-continue.md +32 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-02-context.md +126 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-03-starter.md +110 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-04-decisions.md +129 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-05-patterns.md +109 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-06-structure.md +97 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-07-validation.md +124 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-08-complete.md +59 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/workflow.md +38 -201
- package/src/method-skills/4-implementation/wize-code-review/customize.toml +21 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-01-gather-context.md +64 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-02-review.md +38 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-03-triage.md +43 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-04-present.md +100 -0
- package/src/method-skills/4-implementation/wize-code-review/workflow.md +34 -89
- package/src/method-skills/module.yaml +19 -0
- package/src/tea-skills/wize-tea-risk/workflow.md +11 -0
- package/tools/installer/commands/doctor.js +27 -1
- package/tools/installer/commands/document-project.js +93 -0
- package/tools/installer/document-project/batch-scanner.js +93 -0
- package/tools/installer/document-project/classify.js +170 -0
- package/tools/installer/document-project/modes/deep-dive.js +196 -0
- package/tools/installer/document-project/modes/full-rescan.js +15 -0
- package/tools/installer/document-project/modes/initial-scan.js +100 -0
- package/tools/installer/document-project/modes/quick.js +211 -0
- package/tools/installer/document-project/render-index.js +101 -0
- package/tools/installer/document-project/state.js +110 -0
- 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
|
|
5
|
+
owner: wize-agent-dev # Shuri (peer review)
|
|
6
6
|
status: ready
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Code Review
|
|
10
10
|
|
|
11
|
-
**Goal.**
|
|
11
|
+
**Goal.** Review code changes adversarially using parallel review layers and structured triage into actionable categories.
|
|
12
12
|
|
|
13
|
-
Shuri
|
|
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
|
|
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
|
|
22
|
-
-
|
|
23
|
-
-
|
|
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
|
-
##
|
|
25
|
+
## Outputs
|
|
26
26
|
|
|
27
|
-
- Inline
|
|
28
|
-
-
|
|
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
|
-
##
|
|
32
|
+
## Workflow architecture
|
|
31
33
|
|
|
32
|
-
|
|
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
|
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
##
|
|
55
|
+
## Steps
|
|
69
56
|
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
>
|
|
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
|
-
|
|
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 };
|