viepilot 2.4.0 → 2.15.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.
@@ -2,6 +2,14 @@
2
2
  Convert brainstorm sessions into structured artifacts for autonomous AI execution.
3
3
  </purpose>
4
4
 
5
+ ## Adapter Compatibility
6
+
7
+ | Feature | Claude Code (terminal) | Cursor (Agent/Skills) | Codex CLI | Antigravity (native) |
8
+ |---------|----------------------|-----------------------|-----------|----------------------|
9
+ | Interactive prompts | ✅ `AskUserQuestion` tool | ❌ text fallback | ❌ text fallback | ❌ text fallback |
10
+
11
+ When `AskUserQuestion` is not available, each prompt block falls back to the plain-text numbered list shown below it — no configuration needed.
12
+
5
13
  ## ViePilot Skill Scope Policy (BUG-004)
6
14
 
7
15
  - Default behavior: only use and suggest skills under `vp-*`.
@@ -80,7 +88,7 @@ Ask the user for project information:
80
88
 
81
89
  9. Lead developer email?
82
90
 
83
- 10. GitHub username? (optional)
91
+ 10. Git remote host / username? (optional — e.g. github.com/johndoe, gitlab.com/org, bitbucket.org/team)
84
92
  ```
85
93
 
86
94
  ### Repository Info
@@ -92,6 +100,15 @@ Ask the user for project information:
92
100
  ```
93
101
 
94
102
  ### License & Year
103
+
104
+ > **Adapter-aware prompt (question 13):**
105
+ > - **Claude Code (terminal):** use `AskUserQuestion` tool — spec:
106
+ > - question: "Which license for this project?"
107
+ > - header: "License"
108
+ > - options: [{ label: "MIT", description: "Permissive — most common open-source choice" }, { label: "Apache-2.0", description: "Permissive with patent grant — preferred for enterprise OSS" }, { label: "GPL-3.0", description: "Copyleft — derivative works must stay open-source" }, { label: "Proprietary", description: "All rights reserved — no public redistribution" }]
109
+ > - multiSelect: false
110
+ > - **Cursor / Codex / Antigravity / other:** use text list below
111
+
95
112
  ```
96
113
  13. License?
97
114
  Options: MIT, Apache-2.0, GPL-3.0, BSD-3-Clause, Proprietary
@@ -104,6 +121,671 @@ Ask the user for project information:
104
121
  Store all metadata for template generation.
105
122
  </step>
106
123
 
124
+ <step name="brownfield_detection">
125
+ ## Step 0-B: Brownfield Mode Detection (FEAT-018)
126
+
127
+ **Trigger brownfield mode when ANY of the following is true:**
128
+ - `--brownfield` flag passed explicitly, OR
129
+ - `docs/brainstorm/` directory does not exist or is empty (no `session-*.md` files) **AND** `.viepilot/` directory does not yet exist
130
+
131
+ **If NEITHER condition is met** (greenfield path): skip this entire step; proceed to Step 1.
132
+
133
+ **If brownfield triggered AND `.viepilot/` already exists:**
134
+ - Warn: "`.viepilot/` already exists. Re-running brownfield mode will overwrite artifacts."
135
+
136
+ > **Adapter-aware prompt:**
137
+ > - **Claude Code (terminal):** use `AskUserQuestion` tool — spec:
138
+ > - question: "`.viepilot/` already exists. Re-running brownfield mode will overwrite artifacts. Continue?"
139
+ > - header: "Overwrite?"
140
+ > - options: [{ label: "Yes, continue", description: "Overwrite existing .viepilot/ artifacts with new scan results" }, { label: "No, abort", description: "Stop here — keep existing artifacts unchanged" }]
141
+ > - multiSelect: false
142
+ > - **Cursor / Codex / Antigravity / other:** use text prompt below
143
+ >
144
+ > Ask: "Continue? (y/n)" — abort if n.
145
+
146
+ **When brownfield mode is active:**
147
+ 1. Run the full 12-category codebase scanner (Signal Categories 1–12 below).
148
+ 2. Produce a structured **Scan Report** (see schema at end of this step).
149
+ 3. Classify every field as DETECTED / ASSUMED / MISSING per Gap Detection Rules.
150
+ 4. Present Scan Report summary to user; interactively fill every MISSING MUST-DETECT field.
151
+ 5. User confirms ASSUMED fields (may accept all or override individually).
152
+ 6. After confirmation: proceed to Step 0-C (brainstorm stub generation).
153
+ 7. Then skip Step 1 (`analyze_brainstorm`); continue from Step 0 (metadata collection) → Step 2 onward using the confirmed Scan Report as input.
154
+
155
+ ---
156
+
157
+ ### Signal Category 1 — Build Manifest & Package Identity
158
+
159
+ Probe the following files in order of priority (first match per language wins):
160
+
161
+ | Language / Platform | Files to probe |
162
+ |---------------------|----------------|
163
+ | Node.js | `package.json` |
164
+ | Java / Maven | `pom.xml` |
165
+ | Java / Gradle | `build.gradle`, `build.gradle.kts`, `settings.gradle` |
166
+ | Python | `pyproject.toml`, `setup.py`, `setup.cfg`, `requirements.txt`, `Pipfile` |
167
+ | Rust | `Cargo.toml` |
168
+ | Go | `go.mod` |
169
+ | PHP | `composer.json` |
170
+ | Ruby | `Gemfile`, `*.gemspec` |
171
+ | .NET | `*.csproj`, `*.sln`, `global.json` |
172
+ | Elixir / Erlang | `mix.exs` |
173
+ | Swift | `Package.swift` |
174
+
175
+ **Fields to extract per manifest:**
176
+ - `project_name` — package name / artifactId / module name
177
+ - `current_version` — version field
178
+ - `primary_language` — derived from manifest type
179
+ - `runtime_version` — engines/node, java.version, python_requires, go version
180
+ - `entry_points` — main, bin, spring-boot:run target, etc.
181
+ - `raw_dependencies[]` — full dependency list (used by Signal Category 2)
182
+ - `raw_dev_dependencies[]` — dev/test deps
183
+
184
+ **Monorepo detection** (check before single-manifest scan):
185
+ - npm workspaces: `package.json` → `"workspaces"` field + `packages/*/package.json`
186
+ - Nx / Turborepo: `nx.json`, `turbo.json` + `apps/*/` and `libs/*/`
187
+ - Maven multi-module: `pom.xml` with `<modules>`
188
+ - Gradle multi-project: `settings.gradle` with `include`
189
+ - Cargo workspace: `Cargo.toml` with `[workspace]`
190
+ - Go workspace: `go.work`
191
+
192
+ If monorepo detected → scan each module separately; aggregate into `modules[]` in Scan Report.
193
+
194
+ **Git Submodule Detection** (run after monorepo check):
195
+ - Check for `.gitmodules` file at repo root
196
+ - If present: parse all `[submodule "name"]` blocks → extract `name`, `path`, `url`
197
+ - For each submodule path:
198
+ - If path exists on disk (initialized):
199
+ - Run Signal Cat 1 (manifest scan) on `{path}/`
200
+ - Run Signal Cat 2 (framework) on `{path}/`
201
+ - Run Signal Cat 4 (DB signals) on `{path}/`
202
+ - Record `initialized: true`
203
+ - If path absent (not initialized):
204
+ - Record `initialized: false`, `primary_language: MISSING`
205
+ - Add open question: "Submodule '{name}' not initialized — run `git submodule update --init {path}` to scan it"
206
+ - Add each submodule to `modules[]` with `type: submodule`
207
+
208
+ > **SAFETY RULE**: Never run `git submodule update`, `git submodule init`, or any git network command during scan. Read the local filesystem only.
209
+
210
+ **Polyrepo / Multi-Repo Detection** (run after submodule check):
211
+
212
+ Scan the following signals to determine if this repo is part of a larger multi-repo system:
213
+
214
+ | Signal Source | Pattern | Interpretation |
215
+ |--------------|---------|----------------|
216
+ | `docker-compose.yml` / `docker-compose*.yml` | `build: ../path` or `context: ../path` (value starts with `../`) | Sibling repo used as build context |
217
+ | `docker-compose.yml` | Multiple services with external `image:` and no local `build:` | External microservices — possible sibling repos |
218
+ | `package.json` | `"dependencies"` or `"devDependencies"` value matching `"file:../..."` | Local `file:` sibling repo dependency |
219
+ | `.github/workflows/*.yml` / `.gitlab-ci.yml` / `ci/*.yml` | `git clone` steps, or `uses: org/other-repo/.github/workflows/` referencing a different repo | CI clones or calls a sibling repo |
220
+ | `README.md` / `CONTRIBUTING.md` | Lines containing a repo URL whose path differs from the current repo's remote | Related repo link in docs |
221
+ | `Makefile` / `justfile` | Targets containing `cd ../` followed by build/test commands | Cross-repo build orchestration |
222
+
223
+ For each match: record `{ source, hint, inferred_repo }` in `polyrepo_hints[]`.
224
+ Deduplicate by `inferred_repo` name.
225
+ If `polyrepo_hints` is empty → skip this section entirely (no empty array in clean single-repo Scan Reports).
226
+
227
+ **Interactive prompt** (fire when `polyrepo_hints` non-empty):
228
+
229
+ > **Adapter-aware prompt:**
230
+ > - **Claude Code (terminal):** use `AskUserQuestion` tool — spec:
231
+ > - question: "Polyrepo signals detected — this repo may be part of a multi-repo system. Would you like to provide related repo URLs?"
232
+ > - header: "Polyrepo?"
233
+ > - options: [{ label: "Yes, I'll list them", description: "Provide sibling repo URLs — improves system-level context accuracy" }, { label: "Skip for now", description: "Continue without related repos — affected fields will be marked ASSUMED" }]
234
+ > - multiSelect: false
235
+ > - **Cursor / Codex / Antigravity / other:** use text prompt below
236
+
237
+ ```
238
+ ⚠️ Polyrepo signals detected:
239
+ {list polyrepo_hints}
240
+
241
+ This repo appears to be part of a multi-repo system.
242
+ Would you like to list related repos? (optional — press Enter to skip)
243
+ Format: one URL per line, e.g. https://github.com/org/api-service [backend]
244
+ ```
245
+ - User-supplied repos → stored in `related_repos[]` as `{ url, role }`
246
+ - If user skips → `related_repos: []`; system-level context fields set to **ASSUMED** tier
247
+
248
+ **Gap-fill rule for polyrepo:**
249
+ - `polyrepo_hints` non-empty AND `related_repos` empty → system-level fields (e.g. `deployment_topology`) = ASSUMED (not MISSING; single-repo scan is still valid)
250
+ - `related_repos` populated → system-level fields = DETECTED for user-supplied context
251
+
252
+ If no manifest found → `primary_language` = MISSING; user must provide.
253
+
254
+ ---
255
+
256
+ ### Signal Category 2 — Framework & Library Detection
257
+
258
+ Infer from `raw_dependencies[]` + `raw_dev_dependencies[]`:
259
+
260
+ **Backend frameworks:**
261
+ | Signal pattern | Framework |
262
+ |---------------|-----------|
263
+ | `spring-boot-starter-*` | Spring Boot |
264
+ | `express`, `fastify`, `koa`, `hapi` | Node.js HTTP |
265
+ | `nestjs/core` | NestJS |
266
+ | `fastapi`, `flask`, `django` | Python HTTP |
267
+ | `gin-gonic/gin`, `gofiber/fiber`, `labstack/echo` | Go HTTP |
268
+ | `rails`, `sinatra` | Ruby |
269
+ | `laravel/framework`, `slim/slim` | PHP |
270
+ | `actix-web`, `axum`, `rocket` | Rust HTTP |
271
+ | `phoenix` (mix.exs) | Elixir |
272
+ | `Microsoft.AspNetCore.*` | .NET ASP.NET Core |
273
+
274
+ **Frontend frameworks:**
275
+ | Signal pattern | Framework |
276
+ |---------------|-----------|
277
+ | `react`, `react-dom` | React |
278
+ | `vue` | Vue.js |
279
+ | `@angular/core` | Angular |
280
+ | `svelte` | Svelte |
281
+ | `next` | Next.js |
282
+ | `nuxt` | Nuxt.js |
283
+ | `remix` | Remix |
284
+ | `astro` | Astro |
285
+ | `solid-js` | SolidJS |
286
+
287
+ **ORM / Database clients:**
288
+ | Signal pattern | ORM / DB |
289
+ |---------------|----------|
290
+ | `mybatis-spring-boot-starter`, `mybatis` | MyBatis |
291
+ | `spring-boot-starter-data-jpa`, `hibernate-*` | Hibernate / JPA |
292
+ | `typeorm` | TypeORM |
293
+ | `prisma` | Prisma |
294
+ | `sequelize` | Sequelize |
295
+ | `sqlalchemy`, `alembic` | SQLAlchemy |
296
+ | `gorm.io/gorm` | GORM |
297
+ | `mongoose` | Mongoose (MongoDB) |
298
+ | `pg`, `mysql2`, `mariadb`, `better-sqlite3` | Raw SQL clients |
299
+ | `redis`, `ioredis` | Redis client |
300
+
301
+ **Message broker clients:**
302
+ | Signal pattern | Broker |
303
+ |---------------|--------|
304
+ | `spring-kafka`, `kafka-clients` | Apache Kafka |
305
+ | `amqplib`, `spring-rabbit` | RabbitMQ |
306
+ | `@aws-sdk/client-sqs` | AWS SQS |
307
+ | `nats` | NATS |
308
+ | `bullmq`, `bull` | Redis-based queue |
309
+
310
+ **Auth libraries:**
311
+ | Signal pattern | Auth mechanism |
312
+ |---------------|----------------|
313
+ | `spring-security`, `spring-boot-starter-security` | Spring Security |
314
+ | `jsonwebtoken`, `jose`, `passport-jwt` | JWT |
315
+ | `passport` | OAuth / multi-strategy |
316
+ | `keycloak-*`, `keycloak-connect` | Keycloak |
317
+ | `next-auth`, `@auth/core` | NextAuth |
318
+ | `authlib`, `python-jose`, `djangorestframework-simplejwt` | Python auth |
319
+
320
+ **Test frameworks:**
321
+ | Signal pattern | Framework |
322
+ |---------------|-----------|
323
+ | `jest`, `@jest/core` | Jest |
324
+ | `vitest` | Vitest |
325
+ | `mocha` | Mocha |
326
+ | `jasmine` | Jasmine |
327
+ | `junit`, `spring-boot-starter-test` | JUnit |
328
+ | `pytest` | pytest |
329
+ | `rspec-rails`, `rspec` | RSpec |
330
+ | `cypress` | Cypress E2E |
331
+ | `playwright` | Playwright E2E |
332
+ | `@testing-library/*` | Testing Library |
333
+ | `testify` | Testify (Go) |
334
+
335
+ Rule: If zero backend AND zero frontend frameworks detected → add entry to `open_questions[]` and ask user.
336
+
337
+ ---
338
+
339
+ ### Signal Category 3 — Architecture Layer Inference (Directory Structure)
340
+
341
+ Glob the following path patterns; presence implies the corresponding layer:
342
+
343
+ | Pattern(s) | Inferred layer |
344
+ |------------|----------------|
345
+ | `src/main/java/**`, `src/main/kotlin/**` | Java/Kotlin Maven standard layout |
346
+ | `src/controllers/`, `app/controllers/`, `**/controller/**` | MVC — Controller layer |
347
+ | `src/api/`, `api/`, `routes/`, `src/routes/` | API / Router layer |
348
+ | `src/services/`, `services/`, `**/service/**` | Service / Business logic layer |
349
+ | `src/repositories/`, `repositories/`, `**/repository/**`, `**/dao/**` | Repository / DAO layer |
350
+ | `src/models/`, `models/`, `**/model/**`, `**/entity/**` | Domain models / Entities |
351
+ | `src/middleware/`, `middleware/` | Middleware layer |
352
+ | `src/utils/`, `utils/`, `helpers/`, `common/` | Utilities / Shared |
353
+ | `frontend/`, `client/`, `web/`, `src/client/` | Frontend module |
354
+ | `backend/`, `server/`, `src/server/` | Backend module |
355
+ | `packages/`, `apps/`, `libs/` | Monorepo layout |
356
+ | `infrastructure/`, `infra/`, `terraform/`, `helm/`, `k8s/`, `kubernetes/` | Infrastructure / IaC |
357
+ | `scripts/`, `bin/`, `tools/` | Developer tooling |
358
+ | `public/`, `static/`, `assets/` | Static assets |
359
+ | `docs/`, `documentation/` | Documentation |
360
+ | `config/`, `configs/`, `settings/` | Configuration |
361
+ | `tests/`, `test/`, `__tests__/`, `spec/`, `e2e/` | Test suite root |
362
+ | `migrations/`, `db/migrate/`, `alembic/` | Database migrations |
363
+
364
+ Output: `architecture_layers[]` — each with `{ layer, evidence_path }`.
365
+
366
+ ---
367
+
368
+ ### Signal Category 4 — Database Schema Signals
369
+
370
+ Probe for migration/schema evidence:
371
+
372
+ | Pattern | Evidence type |
373
+ |---------|---------------|
374
+ | `db/migrate/*.rb` | Rails ActiveRecord migrations |
375
+ | `migrations/*.sql`, `migrations/*.js`, `migrations/*.ts` | Generic migrations |
376
+ | `src/main/resources/db/migration/V*.sql` | Flyway |
377
+ | `db/changelog*.xml`, `db/changelog*.yaml` | Liquibase |
378
+ | `alembic/versions/*.py` | SQLAlchemy Alembic |
379
+ | `src/migrations/**` | TypeORM / Sequelize migrations |
380
+ | `prisma/schema.prisma` | Prisma schema |
381
+ | `**/schema.sql`, `**/init.sql`, `**/seed.sql` | Raw SQL schema / seed |
382
+ | `docker-compose.yml` → service names | Implied DB types (postgres, mysql, mongo, redis…) |
383
+
384
+ Output: `database_signals[]` — each with `{ type, evidence_path, migration_tool }`.
385
+
386
+ Rule: If none found but ORM dep exists → `database_signals` = ASSUMED with rationale note.
387
+
388
+ ---
389
+
390
+ ### Signal Category 5 — API Contract Files
391
+
392
+ | File pattern | Contract type |
393
+ |-------------|---------------|
394
+ | `openapi.yaml`, `openapi.json`, `swagger.yaml`, `swagger.json` | OpenAPI / Swagger |
395
+ | `api-docs.yaml`, `api.yaml`, `api-spec.yaml`, `api/*.yaml` | OpenAPI (alternate) |
396
+ | `*.proto`, `proto/**/*.proto` | gRPC / Protocol Buffers |
397
+ | `schema.graphql`, `**/*.graphql` | GraphQL |
398
+ | `src/main/resources/static/v3/api-docs*` | Spring Swagger (generated) |
399
+ | `postman_collection.json`, `*.postman_collection.json` | Postman |
400
+ | `insomnia.yaml`, `.insomnia/` | Insomnia |
401
+
402
+ `api_style` inference:
403
+ - REST → if OpenAPI/Swagger file found
404
+ - gRPC → if `*.proto` found (set `mixed` if REST also found)
405
+ - GraphQL → if `schema.graphql` / `*.graphql` found
406
+ - ASSUMED REST → if REST framework detected but no contract file found
407
+ - MISSING → if api_style still unknown after all signals; user prompted
408
+
409
+ ---
410
+
411
+ ### Signal Category 6 — Infrastructure & Deployment Configuration
412
+
413
+ | File pattern | Deployment signal |
414
+ |-------------|------------------|
415
+ | `Dockerfile` | Containerized — read `FROM` for base image |
416
+ | `docker-compose.yml`, `docker-compose*.yml` | Service topology (list all services) |
417
+ | `.github/workflows/*.yml` | GitHub Actions CI/CD |
418
+ | `.gitlab-ci.yml` | GitLab CI |
419
+ | `Jenkinsfile` | Jenkins pipeline |
420
+ | `.circleci/config.yml` | CircleCI |
421
+ | `bitbucket-pipelines.yml` | Bitbucket Pipelines |
422
+ | `k8s/**/*.yaml`, `kubernetes/**/*.yaml` | Kubernetes manifests |
423
+ | `helm/**`, `Chart.yaml` | Helm charts |
424
+ | `terraform/**/*.tf` | Terraform IaC |
425
+ | `pulumi/**`, `Pulumi.yaml` | Pulumi IaC |
426
+ | `ansible/**`, `playbook.yml` | Ansible |
427
+ | `nginx.conf`, `nginx/**` | Nginx reverse proxy |
428
+ | `serverless.yml`, `serverless.ts` | Serverless Framework |
429
+ | `fly.toml` | Fly.io |
430
+ | `vercel.json`, `.vercel/` | Vercel |
431
+ | `netlify.toml` | Netlify |
432
+ | `render.yaml` | Render |
433
+
434
+ Output: `deployment_signals[]` — each with `{ platform, file_path, notes }`.
435
+
436
+ Rule: If no deployment signal found → `deployment_signals` = MISSING; user prompted.
437
+
438
+ ---
439
+
440
+ ### Signal Category 7 — Environment & Configuration Shape
441
+
442
+ | File pattern | Purpose |
443
+ |-------------|---------|
444
+ | `.env.example`, `.env.sample`, `.env.template` | Required env key shape |
445
+ | `application.properties`, `application.yml`, `application-*.yml` | Spring Boot config |
446
+ | `config/database.yml` | Rails DB config |
447
+ | `config/settings.py`, `config/*.py` | Django / Python config |
448
+ | `appsettings.json`, `appsettings.*.json` | .NET config |
449
+ | `config/config.exs`, `config/runtime.exs` | Elixir config |
450
+ | `config.yaml`, `config.yml` (project root or config/) | Generic config |
451
+
452
+ **Rule:** Read `.env.example` / `.env.sample` / `.env.template` to extract key names into `config_keys[]`.
453
+ **SAFETY: Never read `.env` (live secrets file) — scanner explicitly skips it.**
454
+
455
+ ---
456
+
457
+ ### Signal Category 8 — Test Coverage Signals
458
+
459
+ | File pattern | Test framework |
460
+ |-------------|----------------|
461
+ | `jest.config.js`, `jest.config.ts`, `jest.config.mjs` | Jest |
462
+ | `vitest.config.ts`, `vitest.config.js` | Vitest |
463
+ | `.mocharc.js`, `.mocharc.yaml` | Mocha |
464
+ | `pytest.ini`, `setup.cfg [tool:pytest]`, `pyproject.toml [tool.pytest.ini_options]` | pytest |
465
+ | `karma.conf.js` | Karma |
466
+ | `cypress.config.js`, `cypress.config.ts`, `cypress.json` | Cypress |
467
+ | `playwright.config.ts`, `playwright.config.js` | Playwright |
468
+ | `phpunit.xml`, `phpunit.xml.dist` | PHPUnit |
469
+
470
+ **Coverage indicators:** presence of `coverage/`, `htmlcov/`, `.nyc_output/`, `target/site/jacoco/` → `has_coverage_reports = true`.
471
+
472
+ Output: `test_frameworks[]`, `test_root_dirs[]`, `has_coverage_reports`.
473
+
474
+ Rule: If no test signals found → `test_frameworks` = MISSING; note added to generated `SYSTEM-RULES.md` quality gates section.
475
+
476
+ ---
477
+
478
+ ### Signal Category 9 — Code Quality & Tooling
479
+
480
+ | File pattern | Tool |
481
+ |-------------|------|
482
+ | `.eslintrc*`, `eslint.config.*` | ESLint |
483
+ | `.prettierrc*`, `prettier.config.*` | Prettier |
484
+ | `sonar-project.properties`, `sonar-project.yml` | SonarQube |
485
+ | `.pre-commit-config.yaml` | pre-commit hooks |
486
+ | `checkstyle.xml` | Checkstyle (Java) |
487
+ | `.pylintrc`, `pylint.cfg` | Pylint |
488
+ | `.flake8`, `setup.cfg [flake8]` | Flake8 |
489
+ | `mypy.ini`, `pyproject.toml [tool.mypy]` | mypy |
490
+ | `rustfmt.toml`, `.rustfmt.toml` | rustfmt |
491
+ | `.golangci.yml` | golangci-lint |
492
+ | `.editorconfig` | EditorConfig |
493
+ | `commitlint.config.js`, `.commitlintrc*` | Conventional Commits enforcement |
494
+ | `.husky/` | Husky git hooks |
495
+ | `lint-staged.config.*`, `package.json "lint-staged"` | lint-staged |
496
+
497
+ Output: `quality_tools[]` — used to populate `SYSTEM-RULES.md` quality gates section.
498
+
499
+ ---
500
+
501
+ ### Signal Category 10 — Documentation Files
502
+
503
+ | File | Priority |
504
+ |------|----------|
505
+ | `README.md` | MUST-READ — extract: project name, description, quickstart |
506
+ | `CHANGELOG.md`, `HISTORY.md`, `RELEASES.md` | SHOULD-READ — extract: version history, latest changes |
507
+ | `CONTRIBUTING.md` | SHOULD-READ — extract: contribution rules |
508
+ | `ARCHITECTURE.md`, `docs/architecture*.md` | SHOULD-READ — extract: any existing arch notes |
509
+ | `docs/adr/`, `ADR/`, `decisions/` | SHOULD-READ — Architecture Decision Records |
510
+ | `docs/**/*.md` (top 10 by mtime) | NICE-TO-READ — project-specific docs |
511
+ | `LICENSE` | MUST-READ — extract license type |
512
+
513
+ Output: `docs_extracted[]` — each with `{ file, summary, key_facts[] }`.
514
+
515
+ Rule: If `README.md` absent → `project_name` elevated to MISSING (must ask user).
516
+
517
+ ---
518
+
519
+ ### Signal Category 11 — Git History & Version Signals
520
+
521
+ Run the following git commands (read-only):
522
+
523
+ | Command | Purpose |
524
+ |---------|---------|
525
+ | `git log --oneline -100` | Commit message patterns (Conventional Commits? Jira refs? free-form?) |
526
+ | `git tag --sort=-version:refname \| head -20` | Version history + tag naming convention |
527
+ | `git log --format="%H %s" --diff-filter=A -- "*.md" \| head -20` | When key docs were added |
528
+ | `git branch -a \| head -20` | Branch naming convention |
529
+ | `git log --stat -3` | Most recently changed files |
530
+ | `git shortlog -sn --no-merges \| head -10` | Top contributors |
531
+ | `git remote get-url origin` | Repository URL |
532
+
533
+ Extract: `commit_convention` (Conventional Commits / Jira-ref / free-form / mixed), `version_pattern` (semver / calver / custom), `latest_tag`, `active_branches[]`, `top_contributors[]`, `repo_url`.
534
+
535
+ Rule: If not a git repo → all git fields = MISSING; user warned.
536
+
537
+ ---
538
+
539
+ ### Signal Category 12 — File Extension Language Survey
540
+
541
+ Glob source file extensions to detect secondary languages:
542
+
543
+ ```
544
+ Glob: src/**/*.{ts,tsx,js,jsx,py,java,kt,go,rs,rb,php,cs,swift,ex,exs,scala,clj,elm,dart}
545
+ (also check project root if no src/ directory)
546
+ ```
547
+
548
+ Count files per extension → `language_distribution{}` (e.g. `{ ts: 142, java: 38, sql: 12 }`).
549
+
550
+ Rule: `secondary_languages[]` = languages with ≥5 files that are not `primary_language`.
551
+
552
+ ---
553
+
554
+ ### Scan Report Schema (finalized)
555
+
556
+ After running all 12 signal categories, produce this structured Scan Report:
557
+
558
+ ```yaml
559
+ # ViePilot Brownfield Scan Report
560
+ project_name: string # from manifest or README
561
+ current_version: string # from manifest or latest git tag
562
+ primary_language: string # from manifest
563
+ secondary_languages: [] # from file extension survey
564
+ runtime_version: string # node/java/python/go version
565
+ frameworks:
566
+ backend: []
567
+ frontend: []
568
+ orm: []
569
+ auth: []
570
+ message_broker: []
571
+ build_tool: string
572
+ package_manager: string
573
+ monorepo: bool
574
+ gap_tier: DETECTED | ASSUMED | MISSING # root rollup = worst tier across all modules
575
+ modules: # if monorepo or submodules detected
576
+ - name: string # workspace package name or submodule name
577
+ type: workspace | submodule | root # workspace = monorepo member; submodule = git submodule
578
+ path: string # repo-relative path
579
+ submodule_url: string | null # remote URL from .gitmodules (null if not submodule)
580
+ initialized: bool # true if path exists on disk (submodules only)
581
+ primary_language: string
582
+ framework: string | null
583
+ module_purpose: string # inferred from dir name + manifest description
584
+ entry_point: string | null # main entry file path
585
+ gap_tier: DETECTED | ASSUMED | MISSING
586
+ must_detect_status: # evidence record per MUST-DETECT field
587
+ primary_language: { value: string, source: string, tier: string }
588
+ framework: { value: string, source: string, tier: string }
589
+ module_purpose: { value: string, source: string, tier: string }
590
+ entry_point: { value: string, source: string, tier: string }
591
+ open_questions: [] # per-module open questions
592
+ polyrepo_hints: # present only when polyrepo signals detected
593
+ - source: string # e.g. docker-compose.yml
594
+ hint: string # raw signal text
595
+ inferred_repo: string # guessed sibling repo name
596
+ related_repos: # present only when user supplied input after prompt
597
+ - url: string
598
+ role: string # backend | frontend | shared-library | infra | etc.
599
+ architecture_layers: [] # { layer, evidence_path }
600
+ module_dependencies: [] # { from, to, type, evidence_path } — Gap D (Phase 78)
601
+ dependency_cycles: [] # cycle paths detected — Gap D (Phase 78)
602
+ database_signals: [] # { type, evidence_path, migration_tool }
603
+ api_contracts: [] # { style, file_path }
604
+ api_style: string # REST | GraphQL | gRPC | mixed | unknown
605
+ deployment_signals: [] # { platform, file_path }
606
+ test_frameworks: []
607
+ test_root_dirs: []
608
+ has_coverage_reports: bool
609
+ quality_tools: []
610
+ config_keys: [] # from .env.example (key names only — no values)
611
+ commit_convention: string # conventional | jira | free-form | mixed
612
+ version_pattern: string # semver | calver | custom
613
+ latest_tag: string
614
+ repo_url: string
615
+ top_contributors: []
616
+ docs_extracted: [] # { file, summary, key_facts[] }
617
+ language_distribution: {} # { ts: 142, java: 38, ... }
618
+ open_questions: [] # root-level open questions (includes rollup from modules)
619
+ ```
620
+
621
+ ---
622
+
623
+ ### Gap Detection Rules
624
+
625
+ Every field in Scan Report is classified as:
626
+
627
+ | Status | Meaning | Required action |
628
+ |--------|---------|-----------------|
629
+ | **DETECTED** | Inferred from codebase with high confidence | Show for confirmation — no user action required |
630
+ | **ASSUMED** | Inferred with low confidence or indirect signal | Show to user with rationale; user may correct |
631
+ | **MISSING** | Not found anywhere in codebase | **Must ask user** before generating artifacts |
632
+
633
+ **MUST-DETECT fields** (MISSING = hard blocker — cannot generate artifacts until user fills):
634
+ - `project_name`
635
+ - `primary_language`
636
+ - At least one entry in `frameworks.backend` OR `frameworks.frontend`
637
+ - `current_version`
638
+
639
+ **SHOULD-DETECT fields** (MISSING = warning — document assumption, continue with user acknowledgment):
640
+ - `api_style`
641
+ - At least one entry in `database_signals` (if ORM dep found)
642
+ - `test_frameworks`
643
+ - `commit_convention`
644
+ - `deployment_signals`
645
+
646
+ **NICE-TO-DETECT fields** (MISSING = note only — generate with placeholder):
647
+ - `auth` frameworks, `message_broker`, `config_keys`, `top_contributors`, `has_coverage_reports`
648
+
649
+ **Assumption documentation rule:** For every ASSUMED or MISSING field that proceeds without user fill, append to `open_questions[]` and insert a `> ⚠️ Assumed: {rationale}` callout in the relevant `.viepilot/` artifact section.
650
+
651
+ ---
652
+
653
+ ### Per-Module Gap Detection
654
+
655
+ Applies to every entry in `modules[]` (monorepo workspace members, git submodules, and root if single-repo). Each module is assessed independently.
656
+
657
+ **Per-module MUST-DETECT fields:**
658
+
659
+ | Field | Source signals | Tier if absent |
660
+ |-------|---------------|----------------|
661
+ | `primary_language` | Manifest extension, file survey (Signal Cat 12), `tsconfig.json`, `pyproject.toml` | MISSING — must ask user |
662
+ | `framework` | Signal Cat 2 dep patterns scanned on module path | ASSUMED if no dep match; MISSING if no manifest found |
663
+ | `module_purpose` | Manifest `description` field, directory name convention, README first line | ASSUMED (infer from dir name); MISSING if none of the above |
664
+ | `entry_point` | `main` in `package.json`; `src/index.*`; `cmd/main.go`; `*Application.java` | ASSUMED if standard path exists; MISSING otherwise |
665
+
666
+ **Gap tier assignment per module:**
667
+ ```
668
+ DETECTED — all MUST-DETECT fields sourced directly from file evidence (no inference)
669
+ ASSUMED — ≥1 MUST-DETECT field inferred by convention (no direct file evidence, but plausible)
670
+ MISSING — ≥1 MUST-DETECT field has no evidence and cannot be inferred
671
+ ```
672
+
673
+ **`must_detect_status` evidence conventions:**
674
+ - `source: "tsconfig.json"` — read from a specific file
675
+ - `source: "inferred"` — derived by directory name / naming convention (tier = ASSUMED)
676
+ - `source: "absent"` — no evidence found (tier = MISSING)
677
+ - `source: "user"` — provided by user during gap-filling (tier = DETECTED)
678
+
679
+ **Root gap tier rollup:**
680
+ ```
681
+ root gap_tier = worst tier across all modules
682
+ Priority order: MISSING > ASSUMED > DETECTED
683
+ ```
684
+ If any module is MISSING → root `gap_tier` = MISSING → artifact generation blocked until resolved.
685
+ If all modules are DETECTED or ASSUMED → root `gap_tier` matches the worst module tier.
686
+
687
+ **Scan summary printout** (show after all modules scanned):
688
+ ```
689
+ Module scan summary:
690
+ ┌─────────────────┬──────────────────┬────────────┬──────────────┬───────────┐
691
+ │ Module │ Path │ Language │ Framework │ Gap Tier │
692
+ ├─────────────────┼──────────────────┼────────────┼──────────────┼───────────┤
693
+ │ api-service │ apps/api │ TypeScript │ NestJS │ DETECTED │
694
+ │ web-client │ apps/web │ TypeScript │ React │ DETECTED │
695
+ │ shared-lib │ libs/shared │ TypeScript │ — │ ASSUMED │
696
+ │ legacy-worker │ services/worker │ MISSING │ MISSING │ MISSING │
697
+ └─────────────────┴──────────────────┴────────────┴──────────────┴───────────┘
698
+ Root gap tier: MISSING (worst across modules)
699
+ ```
700
+
701
+ ---
702
+
703
+ ### Interactive Gap-Filling (Step 0-B-ii)
704
+
705
+ After scanner completes:
706
+
707
+ 1. Display Scan Report summary table to user (field | value | status).
708
+ 2. **Per-module MISSING fields** — for each module with `gap_tier: MISSING`, pause and ask per field:
709
+ ```
710
+ ⛔ Module '{name}' (path: {path}) has MISSING required fields:
711
+ - {field}: no evidence found
712
+ Please provide {field}:
713
+ ```
714
+ Record answer as `{ value: user_input, source: "user", tier: DETECTED }`.
715
+ Do NOT proceed to artifact generation until all MISSING module fields are filled.
716
+ 3. For each root-level MUST-DETECT field that is MISSING → **pause and ask user to provide value**.
717
+ 4. Present ASSUMED fields (root + per-module) in a confirmation table → user may accept all with "y" or override individually.
718
+ 5. Capture all user responses; update Scan Report fields accordingly.
719
+ 6. All remaining unresolved items → `open_questions[]` (roll up per-module `open_questions[]` into root).
720
+
721
+ ---
722
+
723
+ ### Brownfield Brainstorm Stub Generation (Step 0-C)
724
+
725
+ After gap-filling is complete, write:
726
+
727
+ **Path:** `docs/brainstorm/session-brownfield-import.md`
728
+
729
+ **Content:**
730
+ ```markdown
731
+ # Brownfield Import — {project_name}
732
+
733
+ ## Meta
734
+ - **Import date**: {scan_date}
735
+ - **Import source**: `vp-crystallize --brownfield`
736
+ - **Scanner version**: FEAT-018
737
+
738
+ ## Scan Report
739
+
740
+ \`\`\`yaml
741
+ {full confirmed Scan Report YAML}
742
+ \`\`\`
743
+ ```
744
+
745
+ **Purpose:** This stub allows `vp-audit` and other ViePilot tools to not error on missing brainstorm session files. The presence of `session-brownfield-import.md` is treated as a valid brownfield import.
746
+
747
+ ---
748
+
749
+ ### Safety Rules
750
+
751
+ The brownfield scanner MUST:
752
+
753
+ ```
754
+ NEVER read:
755
+ .env (live secrets)
756
+ *.key, *.pem, *.p12, *.jks, id_rsa, id_ed25519
757
+
758
+ ALWAYS skip these directories:
759
+ node_modules/
760
+ .git/
761
+ target/
762
+ build/
763
+ dist/
764
+ __pycache__/
765
+ .venv/
766
+ vendor/
767
+
768
+ NEVER write any files until the user has confirmed the Scan Report.
769
+
770
+ NEVER overwrite existing .viepilot/ without explicit user confirmation (y/n prompt).
771
+ ```
772
+
773
+ ---
774
+
775
+ ### TRACKER.md Continuity Annotation
776
+
777
+ When generating TRACKER.md in Step 9 (brownfield mode only), append:
778
+
779
+ ```markdown
780
+ ## Brownfield Import
781
+ - **Import date**: {scan_date}
782
+ - **Imported version**: {current_version}
783
+ - **Note**: Project history pre-dates ViePilot adoption.
784
+ - **Scan Report**: `docs/brainstorm/session-brownfield-import.md`
785
+ ```
786
+
787
+ </step>
788
+
107
789
  <step name="analyze_brainstorm">
108
790
  ## Step 1: Analyze Brainstorm
109
791
 
@@ -159,6 +841,14 @@ Check if `.viepilot/ui-direction/` exists and contains any session artifacts.
159
841
 
160
842
  If `ui_scope_detected = true` **AND** artifacts are missing → **STOP** and present:
161
843
 
844
+ > **Adapter-aware prompt:**
845
+ > - **Claude Code (terminal):** use `AskUserQuestion` tool — spec:
846
+ > - question: "UI Direction artifacts missing. The brainstorm indicates UI scope but `.viepilot/ui-direction/` has no artifacts. How to proceed?"
847
+ > - header: "UI Direction"
848
+ > - options: [{ label: "Return to /vp-brainstorm --ui (Recommended)", description: "Create UI direction artifacts first for best results" }, { label: "Continue with assumptions", description: "Record assumptions in ARCHITECTURE.md and proceed without visual direction" }]
849
+ > - multiSelect: false
850
+ > - **Cursor / Codex / Antigravity / other:** use text menu below
851
+
162
852
  ```
163
853
  ⚠️ UI Direction artifacts missing
164
854
 
@@ -324,6 +1014,15 @@ If `.viepilot/architect/` exists with at least one session directory:
324
1014
 
325
1015
  If `.viepilot/architect/` does **not** exist but brainstorm shows complex architecture (≥5 services/components detected):
326
1016
  - Suggest (soft prompt — not a hard block):
1017
+
1018
+ > **Adapter-aware prompt:**
1019
+ > - **Claude Code (terminal):** use `AskUserQuestion` tool — spec:
1020
+ > - question: "Complex architecture detected (≥5 services/components). Would you like to create architecture visualizations first with /vp-brainstorm --architect?"
1021
+ > - header: "Architect?"
1022
+ > - options: [{ label: "Yes, go to architect mode", description: "Create visual architecture diagrams before crystallizing (recommended for complex systems)" }, { label: "No, continue now", description: "Continue crystallize with text-only brainstorm — no visual diagrams" }]
1023
+ > - multiSelect: false
1024
+ > - **Cursor / Codex / Antigravity / other:** use text menu below
1025
+
327
1026
  ```
328
1027
  💡 Would you like to return to /vp-brainstorm --architect to create visualizations first?
329
1028
  1. Yes — return to architect mode