plankit-cli 1.3.1 → 1.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/README.md +53 -3
- package/package.json +1 -1
- package/src/agents.js +8 -4
- package/src/cli.js +106 -5
- package/src/commandBundles.js +105 -0
- package/src/config.js +20 -2
- package/templates/PLANKIT.md +7 -0
- package/templates/dotnet-commands/codex/dotnet-architecture-audit.md +25 -0
- package/templates/dotnet-commands/codex/dotnet-clean-guardrails.md +24 -0
- package/templates/dotnet-commands/codex/dotnet-ef-migration.md +25 -0
- package/templates/dotnet-commands/codex/dotnet-endpoint-from-model.md +26 -0
- package/templates/dotnet-commands/codex/dotnet-extract-service.md +25 -0
- package/templates/dotnet-commands/codex/dotnet-feature.md +26 -0
- package/templates/dotnet-commands/codex/dotnet-generate-tests.md +25 -0
- package/templates/dotnet-commands/codex/dotnet-health.md +24 -0
- package/templates/dotnet-commands/codex/dotnet-migrate-minimal-api.md +25 -0
- package/templates/dotnet-commands/codex/dotnet-modernize.md +25 -0
- package/templates/dotnet-commands/cursor/dotnet-architecture-audit.md +26 -0
- package/templates/dotnet-commands/cursor/dotnet-clean-guardrails.md +25 -0
- package/templates/dotnet-commands/cursor/dotnet-ef-migration.md +26 -0
- package/templates/dotnet-commands/cursor/dotnet-endpoint-from-model.md +27 -0
- package/templates/dotnet-commands/cursor/dotnet-extract-service.md +26 -0
- package/templates/dotnet-commands/cursor/dotnet-feature.md +27 -0
- package/templates/dotnet-commands/cursor/dotnet-generate-tests.md +26 -0
- package/templates/dotnet-commands/cursor/dotnet-health.md +25 -0
- package/templates/dotnet-commands/cursor/dotnet-migrate-minimal-api.md +26 -0
- package/templates/dotnet-commands/cursor/dotnet-modernize.md +26 -0
- package/templates/dotnet-commands/opencode/dotnet-architecture-audit.md +23 -0
- package/templates/dotnet-commands/opencode/dotnet-clean-guardrails.md +22 -0
- package/templates/dotnet-commands/opencode/dotnet-ef-migration.md +23 -0
- package/templates/dotnet-commands/opencode/dotnet-endpoint-from-model.md +24 -0
- package/templates/dotnet-commands/opencode/dotnet-extract-service.md +23 -0
- package/templates/dotnet-commands/opencode/dotnet-feature.md +24 -0
- package/templates/dotnet-commands/opencode/dotnet-generate-tests.md +23 -0
- package/templates/dotnet-commands/opencode/dotnet-health.md +22 -0
- package/templates/dotnet-commands/opencode/dotnet-migrate-minimal-api.md +23 -0
- package/templates/dotnet-commands/opencode/dotnet-modernize.md +23 -0
- package/templates/dotnet-skills/gemini/dotnet-architecture-audit/SKILL.md +26 -0
- package/templates/dotnet-skills/gemini/dotnet-clean-guardrails/SKILL.md +25 -0
- package/templates/dotnet-skills/gemini/dotnet-ef-migration/SKILL.md +26 -0
- package/templates/dotnet-skills/gemini/dotnet-endpoint-from-model/SKILL.md +27 -0
- package/templates/dotnet-skills/gemini/dotnet-extract-service/SKILL.md +26 -0
- package/templates/dotnet-skills/gemini/dotnet-feature/SKILL.md +27 -0
- package/templates/dotnet-skills/gemini/dotnet-generate-tests/SKILL.md +26 -0
- package/templates/dotnet-skills/gemini/dotnet-health/SKILL.md +25 -0
- package/templates/dotnet-skills/gemini/dotnet-migrate-minimal-api/SKILL.md +26 -0
- package/templates/dotnet-skills/gemini/dotnet-modernize/SKILL.md +26 -0
- package/templates/vue-commands/codex/component-pattern-audit.md +24 -0
- package/templates/vue-commands/codex/design-token-discover.md +24 -0
- package/templates/vue-commands/codex/design-token-migrate.md +24 -0
- package/templates/vue-commands/codex/js-to-ts.md +25 -0
- package/templates/vue-commands/codex/reactivity-audit.md +25 -0
- package/templates/vue-commands/codex/state-strategy-audit.md +23 -0
- package/templates/vue-commands/codex/vue-guardrails.md +24 -0
- package/templates/vue-commands/codex/vue-health.md +28 -0
- package/templates/vue-commands/codex/vue-legacy-map.md +25 -0
- package/templates/vue-commands/codex/vue-migration-plan.md +24 -0
- package/templates/vue-commands/codex/vue-modernize-slice.md +26 -0
- package/templates/vue-commands/codex/vue-pattern-map.md +24 -0
- package/templates/vue-commands/codex/vue-standardize.md +26 -0
- package/templates/vue-commands/codex/vue-style-audit.md +24 -0
- package/templates/vue-commands/codex/vue-target-architecture.md +24 -0
- package/templates/vue-commands/cursor/component-pattern-audit.md +25 -0
- package/templates/vue-commands/cursor/design-token-discover.md +25 -0
- package/templates/vue-commands/cursor/design-token-migrate.md +25 -0
- package/templates/vue-commands/cursor/js-to-ts.md +26 -0
- package/templates/vue-commands/cursor/reactivity-audit.md +26 -0
- package/templates/vue-commands/cursor/state-strategy-audit.md +24 -0
- package/templates/vue-commands/cursor/vue-guardrails.md +25 -0
- package/templates/vue-commands/cursor/vue-health.md +29 -0
- package/templates/vue-commands/cursor/vue-legacy-map.md +26 -0
- package/templates/vue-commands/cursor/vue-migration-plan.md +25 -0
- package/templates/vue-commands/cursor/vue-modernize-slice.md +27 -0
- package/templates/vue-commands/cursor/vue-pattern-map.md +25 -0
- package/templates/vue-commands/cursor/vue-standardize.md +27 -0
- package/templates/vue-commands/cursor/vue-style-audit.md +25 -0
- package/templates/vue-commands/cursor/vue-target-architecture.md +25 -0
- package/templates/vue-commands/opencode/component-pattern-audit.md +22 -0
- package/templates/vue-commands/opencode/design-token-discover.md +22 -0
- package/templates/vue-commands/opencode/design-token-migrate.md +22 -0
- package/templates/vue-commands/opencode/js-to-ts.md +23 -0
- package/templates/vue-commands/opencode/reactivity-audit.md +23 -0
- package/templates/vue-commands/opencode/state-strategy-audit.md +21 -0
- package/templates/vue-commands/opencode/vue-guardrails.md +22 -0
- package/templates/vue-commands/opencode/vue-health.md +26 -0
- package/templates/vue-commands/opencode/vue-legacy-map.md +23 -0
- package/templates/vue-commands/opencode/vue-migration-plan.md +22 -0
- package/templates/vue-commands/opencode/vue-modernize-slice.md +24 -0
- package/templates/vue-commands/opencode/vue-pattern-map.md +22 -0
- package/templates/vue-commands/opencode/vue-standardize.md +24 -0
- package/templates/vue-commands/opencode/vue-style-audit.md +22 -0
- package/templates/vue-commands/opencode/vue-target-architecture.md +22 -0
- package/templates/vue-skills/gemini/component-pattern-audit/SKILL.md +25 -0
- package/templates/vue-skills/gemini/design-token-discover/SKILL.md +25 -0
- package/templates/vue-skills/gemini/design-token-migrate/SKILL.md +25 -0
- package/templates/vue-skills/gemini/js-to-ts/SKILL.md +26 -0
- package/templates/vue-skills/gemini/reactivity-audit/SKILL.md +26 -0
- package/templates/vue-skills/gemini/state-strategy-audit/SKILL.md +24 -0
- package/templates/vue-skills/gemini/vue-guardrails/SKILL.md +25 -0
- package/templates/vue-skills/gemini/vue-health/SKILL.md +29 -0
- package/templates/vue-skills/gemini/vue-legacy-map/SKILL.md +26 -0
- package/templates/vue-skills/gemini/vue-migration-plan/SKILL.md +25 -0
- package/templates/vue-skills/gemini/vue-modernize-slice/SKILL.md +27 -0
- package/templates/vue-skills/gemini/vue-pattern-map/SKILL.md +25 -0
- package/templates/vue-skills/gemini/vue-standardize/SKILL.md +27 -0
- package/templates/vue-skills/gemini/vue-style-audit/SKILL.md +25 -0
- package/templates/vue-skills/gemini/vue-target-architecture/SKILL.md +25 -0
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ plankit review my-feature
|
|
|
58
58
|
|
|
59
59
|
| Command | Action |
|
|
60
60
|
|---|---|
|
|
61
|
-
| `plankit init [--agent <name>]` | Create PlanKit config, artifact folders, spec, and the **selected agent's** folders/commands. Prompts for the agent when
|
|
61
|
+
| `plankit init [--agent <name>] [--framework <name>]` | Create PlanKit config, artifact folders, spec, and the **selected agent's** folders/commands. Prompts for the agent and framework suite(s) when run interactively. |
|
|
62
62
|
| `plankit plan <feature> [requirements]` | Create `artifacts/current/<feature>/` with README, phase specs, outputs folder, and state metadata. |
|
|
63
63
|
| `plankit clarify <feature> [phase]` | Create a clarification artifact for requirements and design decisions. |
|
|
64
64
|
| `plankit implement <feature> <phase>` | Validate phase context (spec + prior outputs) and prepare `outputs/phase-N-output.md`. |
|
|
@@ -77,6 +77,8 @@ plankit review my-feature
|
|
|
77
77
|
| `--force` | Overwrite generated files where safe. |
|
|
78
78
|
| `--dry-run` | Show intended actions without writing anything. |
|
|
79
79
|
| `--agent <name>` | Select your coding agent during `init` (`opencode`, `gemini`, `codex`, `cursor`; default `opencode`). |
|
|
80
|
+
| `--framework <name>` | Select framework command suite during `init` (`angular`, `vue`, `dotnet`, `none`, `all`; default `none`). |
|
|
81
|
+
| `--frameworks <list>` | Comma-separated list of framework command suites (e.g. `vue,dotnet`). |
|
|
80
82
|
|
|
81
83
|
### Plan options
|
|
82
84
|
|
|
@@ -162,7 +164,55 @@ plankit init --agent cursor
|
|
|
162
164
|
|
|
163
165
|
---
|
|
164
166
|
|
|
165
|
-
##
|
|
167
|
+
## Framework command suites
|
|
168
|
+
|
|
169
|
+
During `plankit init`, PlanKit prompts you to choose optional framework-specific command suites (or pass `--framework <name>` / `--frameworks <names>`):
|
|
170
|
+
- **None**: Installs only the core PlanKit feature planning workflow.
|
|
171
|
+
- **Angular**: Installs 17 Angular engineering workflows.
|
|
172
|
+
- **Vue**: Installs 15 Vue modernization and architectural standardization workflows.
|
|
173
|
+
- **.NET**: Installs 10 .NET clean architecture and API engineering workflows.
|
|
174
|
+
- **All**: Installs all supported framework suites.
|
|
175
|
+
|
|
176
|
+
### Vue modernization suite (`--framework vue`)
|
|
177
|
+
|
|
178
|
+
Designed for legacy and mixed Vue codebases (JavaScript/TypeScript, Options API, Composition API, `<script setup>`, Vuex/Pinia) following the PlanKit Vue Modernization Specification:
|
|
179
|
+
|
|
180
|
+
| Command | Purpose |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `vue-health` | Audit Vue codebase health, tech debt score, Options vs Composition ratio, and modernization risks. |
|
|
183
|
+
| `vue-pattern-map` | Map architectural patterns across state, data fetching, styling, and components. |
|
|
184
|
+
| `vue-target-architecture` | Define or inspect target architectural conventions (Vue 3, `<script setup>`, TS, Pinia). |
|
|
185
|
+
| `vue-legacy-map` | Catalog legacy Vue files, mixins, filters, Options API components, and hotspots. |
|
|
186
|
+
| `vue-standardize` | Refactor a component or composable to conform to the established target architecture. |
|
|
187
|
+
| `vue-migration-plan` | Generate an incremental, phased migration plan for a legacy feature slice. |
|
|
188
|
+
| `js-to-ts` | Convert JavaScript Vue SFCs and utility modules to TypeScript with typed props/emits. |
|
|
189
|
+
| `vue-style-audit` | Audit styling patterns, global vs scoped CSS/SCSS, deep selectors, and hardcoded values. |
|
|
190
|
+
| `design-token-discover` | Discover repeated color literals, typography, spacing, and shadows into token candidates. |
|
|
191
|
+
| `design-token-migrate` | Migrate hardcoded styles in Vue components to standardized design tokens. |
|
|
192
|
+
| `state-strategy-audit` | Audit Vuex vs Pinia vs composables; detect state duplication and reactivity leaks. |
|
|
193
|
+
| `reactivity-audit` | Audit reactivity anti-patterns (destructuring props without `toRefs`, watcher leaks). |
|
|
194
|
+
| `component-pattern-audit` | Audit SFC component design, god components, prop drilling, and emits declarations. |
|
|
195
|
+
| `vue-modernize-slice` | Orchestrate an end-to-end modernization of a Vue feature slice with validation. |
|
|
196
|
+
| `vue-guardrails` | Configure ESLint rules and TypeScript strict settings to prevent legacy regressions. |
|
|
197
|
+
|
|
198
|
+
### .NET clean architecture suite (`--framework dotnet`)
|
|
199
|
+
|
|
200
|
+
Designed for ASP.NET Core and .NET solutions following Clean Architecture and Vertical Slice patterns:
|
|
201
|
+
|
|
202
|
+
| Command | Purpose |
|
|
203
|
+
|---|---|
|
|
204
|
+
| `dotnet-health` | Audit solution health: SDK versions, NuGet packages, vulnerabilities, and compiler warnings. |
|
|
205
|
+
| `dotnet-architecture-audit` | Audit layer dependencies (Domain, Application, Infrastructure, API) and circular references. |
|
|
206
|
+
| `dotnet-feature` | Build an end-to-end vertical slice feature (Command/Query, MediatR, FluentValidation, Endpoint). |
|
|
207
|
+
| `dotnet-endpoint-from-model` | Generate ASP.NET Core Minimal API or Controller endpoints with typed results and error handling. |
|
|
208
|
+
| `dotnet-migrate-minimal-api` | Migrate traditional controllers to modern Minimal API route groups. |
|
|
209
|
+
| `dotnet-ef-migration` | Safe Entity Framework Core migration lifecycle: entity check, migration, SQL preview, rollback. |
|
|
210
|
+
| `dotnet-extract-service` | Refactor fat services into isolated CQRS handlers or domain services. |
|
|
211
|
+
| `dotnet-generate-tests` | Generate xUnit/NUnit tests with FluentAssertions and WebApplicationFactory integration tests. |
|
|
212
|
+
| `dotnet-clean-guardrails` | Generate NetArchTest architectural unit tests and EditorConfig rules to guard layer boundaries. |
|
|
213
|
+
| `dotnet-modernize` | Upgrade legacy C# syntax to modern C# 12+ / .NET 8+ patterns (primary constructors, collection expressions). |
|
|
214
|
+
|
|
215
|
+
### Angular command suite (`--framework angular`)
|
|
166
216
|
|
|
167
217
|
Alongside the phase workflow, `init` installs a per-agent Angular command library that gives your AI assistant **16 deterministic Angular engineering workflows**, driven by repository discovery rather than generic prompts:
|
|
168
218
|
|
|
@@ -187,7 +237,7 @@ Alongside the phase workflow, `init` installs a per-agent Angular command librar
|
|
|
187
237
|
|
|
188
238
|
Plus a shared `angular-project-discovery` reference that every command leans on (Angular version, standalone vs NgModule, Signals/RxJS, state management, forms strategy, UI library, styling, testing, conventions, architecture).
|
|
189
239
|
|
|
190
|
-
Only your **selected agent's** folder/commands are copied. To customize the
|
|
240
|
+
Only your **selected agent's** folder/commands are copied. To customize the command templates in your own `templatesDir`, mirror the `templates/<framework>-commands/` and `templates/<framework>-skills/` folder layout from the package.
|
|
191
241
|
|
|
192
242
|
---
|
|
193
243
|
|
package/package.json
CHANGED
package/src/agents.js
CHANGED
|
@@ -33,7 +33,7 @@ export const AGENTS = {
|
|
|
33
33
|
{ template: 'opencode/plankit-implement.md', dest: ['.opencode', 'commands', 'plankit-implement.md'] },
|
|
34
34
|
{ template: 'opencode/plankit-review.md', dest: ['.opencode', 'commands', 'plankit-review.md'] }
|
|
35
35
|
],
|
|
36
|
-
commandBundles: [
|
|
36
|
+
commandBundles: [],
|
|
37
37
|
globalSkillEntries: null
|
|
38
38
|
},
|
|
39
39
|
gemini: {
|
|
@@ -44,7 +44,7 @@ export const AGENTS = {
|
|
|
44
44
|
bindings: [
|
|
45
45
|
{ template: 'gemini-instructions.md', dest: ['.gemini', 'instructions.md'] }
|
|
46
46
|
],
|
|
47
|
-
commandBundles: [
|
|
47
|
+
commandBundles: [],
|
|
48
48
|
globalSkillEntries: GEMINI_SKILL_ENTRIES
|
|
49
49
|
},
|
|
50
50
|
codex: {
|
|
@@ -55,7 +55,7 @@ export const AGENTS = {
|
|
|
55
55
|
bindings: [
|
|
56
56
|
{ template: 'AGENTS.md', dest: ['AGENTS.md'] }
|
|
57
57
|
],
|
|
58
|
-
commandBundles: [
|
|
58
|
+
commandBundles: [],
|
|
59
59
|
globalSkillEntries: null
|
|
60
60
|
},
|
|
61
61
|
cursor: {
|
|
@@ -66,11 +66,15 @@ export const AGENTS = {
|
|
|
66
66
|
bindings: [
|
|
67
67
|
{ template: 'cursorrules', dest: ['.cursorrules'] }
|
|
68
68
|
],
|
|
69
|
-
commandBundles: [
|
|
69
|
+
commandBundles: [],
|
|
70
70
|
globalSkillEntries: null
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
+
export function resolveAgentBundles(agentId, frameworks = []) {
|
|
75
|
+
return (frameworks || []).map((fw) => `${agentId}-${fw}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
74
78
|
export function listAgents() {
|
|
75
79
|
return AGENT_ORDER.map((id) => AGENTS[id]);
|
|
76
80
|
}
|
package/src/cli.js
CHANGED
|
@@ -15,8 +15,14 @@ import {
|
|
|
15
15
|
writeManagedFile
|
|
16
16
|
} from './config.js';
|
|
17
17
|
import { renderList, renderTemplate, packageRoot } from './templates.js';
|
|
18
|
-
import { DEFAULT_AGENT, GEMINI_SKILL_ENTRIES, getAgent, listAgents, validAgentNames } from './agents.js';
|
|
19
|
-
import {
|
|
18
|
+
import { DEFAULT_AGENT, GEMINI_SKILL_ENTRIES, getAgent, listAgents, resolveAgentBundles, validAgentNames } from './agents.js';
|
|
19
|
+
import {
|
|
20
|
+
SUPPORTED_FRAMEWORKS,
|
|
21
|
+
bundleEntry,
|
|
22
|
+
bundleSourceExists,
|
|
23
|
+
normalizeFramework,
|
|
24
|
+
parseFrameworksInput
|
|
25
|
+
} from './commandBundles.js';
|
|
20
26
|
|
|
21
27
|
export async function runCli(args, context = {}) {
|
|
22
28
|
const io = createIo(context);
|
|
@@ -71,6 +77,7 @@ async function initProject(parsed, io) {
|
|
|
71
77
|
const { config, path: configPath } = loadConfig(io.cwd);
|
|
72
78
|
const options = writeOptions(parsed, config);
|
|
73
79
|
const agent = await resolveAgent(parsed, config, io, configPath);
|
|
80
|
+
const frameworks = await resolveFrameworks(parsed, config, io, configPath);
|
|
74
81
|
const results = [];
|
|
75
82
|
const artifactsDir = safeJoin(io.cwd, config.artifactsDir);
|
|
76
83
|
|
|
@@ -84,7 +91,7 @@ async function initProject(parsed, io) {
|
|
|
84
91
|
));
|
|
85
92
|
results.push(writeManagedFile(path.join(artifactsDir, 'current', '.gitkeep'), '', options));
|
|
86
93
|
results.push(writeManagedFile(path.join(artifactsDir, 'archived', '.gitkeep'), '', options));
|
|
87
|
-
results.push(createDefaultConfigFile(io.cwd, options));
|
|
94
|
+
results.push(createDefaultConfigFile(io.cwd, options, frameworks));
|
|
88
95
|
|
|
89
96
|
for (const binding of agent.bindings) {
|
|
90
97
|
results.push(writeManagedFile(
|
|
@@ -94,7 +101,8 @@ async function initProject(parsed, io) {
|
|
|
94
101
|
));
|
|
95
102
|
}
|
|
96
103
|
|
|
97
|
-
|
|
104
|
+
const bundlesToInstall = resolveAgentBundles(agent.id, frameworks);
|
|
105
|
+
for (const bundleName of bundlesToInstall) {
|
|
98
106
|
results.push(installCommandBundle(agent, bundleName, config, options, io));
|
|
99
107
|
}
|
|
100
108
|
|
|
@@ -102,7 +110,10 @@ async function initProject(parsed, io) {
|
|
|
102
110
|
results.push(...installGlobalGeminiSkills(config, options));
|
|
103
111
|
}
|
|
104
112
|
|
|
105
|
-
|
|
113
|
+
const fwMsg = frameworks.length > 0
|
|
114
|
+
? ` with framework suite(s): ${frameworks.join(', ')}`
|
|
115
|
+
: ' (base planning commands only)';
|
|
116
|
+
io.stdout(`PlanKit initialized for agent "${agent.id}"${fwMsg}.`);
|
|
106
117
|
printResults(results, io);
|
|
107
118
|
}
|
|
108
119
|
|
|
@@ -522,6 +533,93 @@ async function promptAgent(io) {
|
|
|
522
533
|
return getAgent(trimmed) || null;
|
|
523
534
|
}
|
|
524
535
|
|
|
536
|
+
async function resolveFrameworks(parsed, config, io, configPath = null) {
|
|
537
|
+
const fromFlag = parsed.flags.framework || parsed.flags.frameworks;
|
|
538
|
+
if (fromFlag !== undefined) {
|
|
539
|
+
const frameworks = parseFrameworksInput(fromFlag);
|
|
540
|
+
if (frameworks.length > 0) {
|
|
541
|
+
io.stdout(`Using framework(s) from --framework flag: "${frameworks.join(', ')}".`);
|
|
542
|
+
} else {
|
|
543
|
+
io.stdout('No framework suites selected via --framework flag.');
|
|
544
|
+
}
|
|
545
|
+
return frameworks;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (configPath && Array.isArray(config.frameworks) && config.frameworks.length > 0) {
|
|
549
|
+
io.stdout(`Using configured framework(s) "${config.frameworks.join(', ')}" from plan config.`);
|
|
550
|
+
return config.frameworks;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (io.isTTY && io.promptChoice) {
|
|
554
|
+
const selected = await promptFrameworks(io);
|
|
555
|
+
if (selected !== null) {
|
|
556
|
+
if (selected.length > 0) {
|
|
557
|
+
io.stdout(`Selected framework(s): "${selected.join(', ')}".`);
|
|
558
|
+
} else {
|
|
559
|
+
io.stdout('Selected base planning commands only.');
|
|
560
|
+
}
|
|
561
|
+
return selected;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
io.stdout('No framework suite selected — installing base planning commands only. Pass --framework <angular|vue|dotnet|none> to add framework suites.');
|
|
566
|
+
return [];
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async function promptFrameworks(io) {
|
|
570
|
+
const promptText = `Select framework command suites to install (in addition to base planning commands):
|
|
571
|
+
1. None (Base planning commands only)
|
|
572
|
+
2. Angular command suite
|
|
573
|
+
3. Vue modernization & architecture suite
|
|
574
|
+
4. .NET clean architecture & API suite
|
|
575
|
+
5. All framework suites
|
|
576
|
+
|
|
577
|
+
Enter a number (1-5), comma-separated numbers, or framework names (default: 1)`;
|
|
578
|
+
|
|
579
|
+
const validChoices = ['1', '2', '3', '4', '5', 'none', 'angular', 'vue', 'dotnet', 'all'];
|
|
580
|
+
const answer = await io.promptChoice(promptText, validChoices);
|
|
581
|
+
if (!answer || !answer.trim()) {
|
|
582
|
+
return [];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const raw = answer.trim().toLowerCase();
|
|
586
|
+
const tokens = raw.split(',').map((t) => t.trim()).filter(Boolean);
|
|
587
|
+
const result = new Set();
|
|
588
|
+
|
|
589
|
+
for (const token of tokens) {
|
|
590
|
+
if (token === '1' || token === 'none') {
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
if (token === '2' || token === 'angular' || token === 'ng') {
|
|
594
|
+
result.add('angular');
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (token === '3' || token === 'vue' || token === 'vuejs') {
|
|
598
|
+
result.add('vue');
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (token === '4' || token === 'dotnet' || token === '.net' || token === 'net') {
|
|
602
|
+
result.add('dotnet');
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
if (token === '5' || token === 'all') {
|
|
606
|
+
SUPPORTED_FRAMEWORKS.forEach((fw) => result.add(fw));
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const normalized = normalizeFramework(token);
|
|
611
|
+
if (Array.isArray(normalized)) {
|
|
612
|
+
normalized.forEach((fw) => result.add(fw));
|
|
613
|
+
} else if (normalized) {
|
|
614
|
+
result.add(normalized);
|
|
615
|
+
} else {
|
|
616
|
+
throw new Error(`Unknown framework selection "${token}". Valid options: 1-5, ${SUPPORTED_FRAMEWORKS.join(', ')}, none, all.`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return Array.from(result);
|
|
621
|
+
}
|
|
622
|
+
|
|
525
623
|
function writeOptions(parsed, config) {
|
|
526
624
|
return {
|
|
527
625
|
force: Boolean(parsed.flags.force) || config.overwrite === 'force',
|
|
@@ -645,6 +743,9 @@ Options:
|
|
|
645
743
|
--dry-run Show intended actions without writing
|
|
646
744
|
--agent <name> Select your coding agent during init
|
|
647
745
|
(opencode, gemini, codex, cursor; default: opencode)
|
|
746
|
+
--framework <name> Select framework command suite during init
|
|
747
|
+
(angular, vue, dotnet, none, all; default: none)
|
|
748
|
+
--frameworks <list> Comma-separated list of framework suites during init
|
|
648
749
|
--global-gemini-skills Also install Gemini skills under the user profile
|
|
649
750
|
--phases 3 Generate N default phases during plan
|
|
650
751
|
--phases "Design,Build,Test" Generate named phases during plan
|
package/src/commandBundles.js
CHANGED
|
@@ -4,6 +4,24 @@ import fs from 'node:fs';
|
|
|
4
4
|
|
|
5
5
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
6
6
|
|
|
7
|
+
export const SUPPORTED_FRAMEWORKS = ['angular', 'vue', 'dotnet'];
|
|
8
|
+
|
|
9
|
+
export const FRAMEWORK_ALIASES = {
|
|
10
|
+
ng: 'angular',
|
|
11
|
+
angular: 'angular',
|
|
12
|
+
vue: 'vue',
|
|
13
|
+
vuejs: 'vue',
|
|
14
|
+
dotnet: 'dotnet',
|
|
15
|
+
net: 'dotnet',
|
|
16
|
+
'.net': 'dotnet'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const FRAMEWORK_DESCRIPTIONS = {
|
|
20
|
+
angular: 'Angular command suite',
|
|
21
|
+
vue: 'Vue modernization & architecture suite',
|
|
22
|
+
dotnet: '.NET clean architecture & API suite'
|
|
23
|
+
};
|
|
24
|
+
|
|
7
25
|
export const COMMAND_BUNDLES = {
|
|
8
26
|
'opencode-angular': {
|
|
9
27
|
label: 'Angular command suite',
|
|
@@ -28,13 +46,100 @@ export const COMMAND_BUNDLES = {
|
|
|
28
46
|
src: path.join('angular-commands', 'cursor'),
|
|
29
47
|
dest: ['.cursor', 'rules'],
|
|
30
48
|
kind: 'folder'
|
|
49
|
+
},
|
|
50
|
+
'opencode-vue': {
|
|
51
|
+
label: 'Vue command suite',
|
|
52
|
+
src: path.join('vue-commands', 'opencode'),
|
|
53
|
+
dest: ['.opencode', 'commands'],
|
|
54
|
+
kind: 'folder'
|
|
55
|
+
},
|
|
56
|
+
'gemini-vue': {
|
|
57
|
+
label: 'Vue skill suite',
|
|
58
|
+
src: path.join('vue-skills', 'gemini'),
|
|
59
|
+
dest: ['.gemini', 'skills'],
|
|
60
|
+
kind: 'folder'
|
|
61
|
+
},
|
|
62
|
+
'codex-vue': {
|
|
63
|
+
label: 'Vue command suite',
|
|
64
|
+
src: path.join('vue-commands', 'codex'),
|
|
65
|
+
dest: ['.codex'],
|
|
66
|
+
kind: 'folder'
|
|
67
|
+
},
|
|
68
|
+
'cursor-vue': {
|
|
69
|
+
label: 'Vue command suite',
|
|
70
|
+
src: path.join('vue-commands', 'cursor'),
|
|
71
|
+
dest: ['.cursor', 'rules'],
|
|
72
|
+
kind: 'folder'
|
|
73
|
+
},
|
|
74
|
+
'opencode-dotnet': {
|
|
75
|
+
label: '.NET command suite',
|
|
76
|
+
src: path.join('dotnet-commands', 'opencode'),
|
|
77
|
+
dest: ['.opencode', 'commands'],
|
|
78
|
+
kind: 'folder'
|
|
79
|
+
},
|
|
80
|
+
'gemini-dotnet': {
|
|
81
|
+
label: '.NET skill suite',
|
|
82
|
+
src: path.join('dotnet-skills', 'gemini'),
|
|
83
|
+
dest: ['.gemini', 'skills'],
|
|
84
|
+
kind: 'folder'
|
|
85
|
+
},
|
|
86
|
+
'codex-dotnet': {
|
|
87
|
+
label: '.NET command suite',
|
|
88
|
+
src: path.join('dotnet-commands', 'codex'),
|
|
89
|
+
dest: ['.codex'],
|
|
90
|
+
kind: 'folder'
|
|
91
|
+
},
|
|
92
|
+
'cursor-dotnet': {
|
|
93
|
+
label: '.NET command suite',
|
|
94
|
+
src: path.join('dotnet-commands', 'cursor'),
|
|
95
|
+
dest: ['.cursor', 'rules'],
|
|
96
|
+
kind: 'folder'
|
|
31
97
|
}
|
|
32
98
|
};
|
|
33
99
|
|
|
100
|
+
export function normalizeFramework(input) {
|
|
101
|
+
if (!input) return null;
|
|
102
|
+
const cleaned = String(input).trim().toLowerCase();
|
|
103
|
+
if (cleaned === 'none') return null;
|
|
104
|
+
if (cleaned === 'all') return [...SUPPORTED_FRAMEWORKS];
|
|
105
|
+
return FRAMEWORK_ALIASES[cleaned] || null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function parseFrameworksInput(input) {
|
|
109
|
+
if (!input) return [];
|
|
110
|
+
if (Array.isArray(input)) {
|
|
111
|
+
return input.flatMap((item) => parseFrameworksInput(item));
|
|
112
|
+
}
|
|
113
|
+
const parts = String(input)
|
|
114
|
+
.split(',')
|
|
115
|
+
.map((item) => item.trim())
|
|
116
|
+
.filter(Boolean);
|
|
117
|
+
|
|
118
|
+
const frameworks = new Set();
|
|
119
|
+
for (const part of parts) {
|
|
120
|
+
const normalized = normalizeFramework(part);
|
|
121
|
+
if (Array.isArray(normalized)) {
|
|
122
|
+
normalized.forEach((fw) => frameworks.add(fw));
|
|
123
|
+
} else if (normalized) {
|
|
124
|
+
frameworks.add(normalized);
|
|
125
|
+
} else if (part.toLowerCase() === 'none') {
|
|
126
|
+
// explicit none
|
|
127
|
+
continue;
|
|
128
|
+
} else {
|
|
129
|
+
throw new Error(`Unknown framework "${part}". Valid frameworks: ${SUPPORTED_FRAMEWORKS.join(', ')}, none, all.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return Array.from(frameworks);
|
|
133
|
+
}
|
|
134
|
+
|
|
34
135
|
export function bundleEntry(bundleName) {
|
|
35
136
|
return COMMAND_BUNDLES[bundleName] || null;
|
|
36
137
|
}
|
|
37
138
|
|
|
139
|
+
export function bundleNameFor(agentId, framework) {
|
|
140
|
+
return `${agentId}-${framework}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
38
143
|
export function bundleSourceExists(bundleName, cwd, config) {
|
|
39
144
|
const entry = COMMAND_BUNDLES[bundleName];
|
|
40
145
|
if (!entry) {
|
package/src/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { AGENT_ORDER, DEFAULT_AGENT, getAgent } from './agents.js';
|
|
4
|
+
import { parseFrameworksInput } from './commandBundles.js';
|
|
4
5
|
|
|
5
6
|
export const DEFAULT_CONFIG = {
|
|
6
7
|
artifactsDir: 'artifacts',
|
|
@@ -8,6 +9,7 @@ export const DEFAULT_CONFIG = {
|
|
|
8
9
|
agents: {
|
|
9
10
|
default: DEFAULT_AGENT
|
|
10
11
|
},
|
|
12
|
+
frameworks: [],
|
|
11
13
|
defaultPhases: [
|
|
12
14
|
{
|
|
13
15
|
title: 'Foundation & Setup',
|
|
@@ -57,9 +59,13 @@ export function loadConfig(cwd = process.cwd()) {
|
|
|
57
59
|
};
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
export function createDefaultConfigFile(cwd, options = {}) {
|
|
62
|
+
export function createDefaultConfigFile(cwd, options = {}, frameworks = []) {
|
|
61
63
|
const filePath = path.join(cwd, 'plankit.config.json');
|
|
62
|
-
const
|
|
64
|
+
const baseConfig = structuredClone(DEFAULT_CONFIG);
|
|
65
|
+
if (Array.isArray(frameworks) && frameworks.length > 0) {
|
|
66
|
+
baseConfig.frameworks = frameworks;
|
|
67
|
+
}
|
|
68
|
+
const content = `${JSON.stringify(baseConfig, null, 2)}\n`;
|
|
63
69
|
return writeManagedFile(filePath, content, options);
|
|
64
70
|
}
|
|
65
71
|
|
|
@@ -69,10 +75,22 @@ export function normalizeConfig(config) {
|
|
|
69
75
|
normalized.artifactsDir = normalized.artifactsDir || DEFAULT_CONFIG.artifactsDir;
|
|
70
76
|
normalized.overwrite = normalized.overwrite || DEFAULT_CONFIG.overwrite;
|
|
71
77
|
normalized.agents = normalizeAgents(normalized.agents);
|
|
78
|
+
normalized.frameworks = normalizeFrameworksConfig(normalized.frameworks);
|
|
72
79
|
normalized.verification = { ...DEFAULT_CONFIG.verification, ...(normalized.verification || {}) };
|
|
73
80
|
return normalized;
|
|
74
81
|
}
|
|
75
82
|
|
|
83
|
+
export function normalizeFrameworksConfig(frameworks) {
|
|
84
|
+
if (!frameworks) return [];
|
|
85
|
+
if (Array.isArray(frameworks)) {
|
|
86
|
+
return parseFrameworksInput(frameworks);
|
|
87
|
+
}
|
|
88
|
+
if (typeof frameworks === 'string') {
|
|
89
|
+
return parseFrameworksInput(frameworks);
|
|
90
|
+
}
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
|
|
76
94
|
export function normalizeAgents(agents) {
|
|
77
95
|
if (!agents || typeof agents !== 'object') {
|
|
78
96
|
return { ...DEFAULT_CONFIG.agents };
|
package/templates/PLANKIT.md
CHANGED
|
@@ -35,6 +35,13 @@ Prepares the phase output artifact after validating that the phase spec and prio
|
|
|
35
35
|
|
|
36
36
|
Runs configured verification commands and archives the completed feature.
|
|
37
37
|
|
|
38
|
+
## Framework Suites
|
|
39
|
+
|
|
40
|
+
PlanKit supports modular framework engineering command suites selectable during `plankit init` via prompt or `--framework <angular|vue|dotnet|none|all>`:
|
|
41
|
+
- **Angular**: Deterministic Angular workflows (`component-from`, `feature`, `signalize`, `modernize`, etc.)
|
|
42
|
+
- **Vue**: Legacy modernization and standardization workflows (`vue-health`, `vue-pattern-map`, `vue-standardize`, `vue-modernize-slice`, etc.)
|
|
43
|
+
- **.NET**: Clean Architecture & Minimal API workflows (`dotnet-health`, `dotnet-architecture-audit`, `dotnet-feature`, `dotnet-modernize`, etc.)
|
|
44
|
+
|
|
38
45
|
## Customization
|
|
39
46
|
|
|
40
47
|
Configure PlanKit with `plankit.config.json`. Override generated templates by setting `templatesDir` and matching the package template file names.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# dotnet-architecture-audit
|
|
2
|
+
> Audit layer dependencies (Clean Architecture / Vertical Slices / DDD) and circular dependencies.
|
|
3
|
+
|
|
4
|
+
# /dotnet-architecture-audit
|
|
5
|
+
|
|
6
|
+
Analyze architectural layer boundaries and project references across the .NET solution.
|
|
7
|
+
|
|
8
|
+
## Workflow
|
|
9
|
+
1. Parse project references between .csproj files.
|
|
10
|
+
2. Verify Domain layer has zero external project dependencies.
|
|
11
|
+
3. Verify Application layer depends only on Domain.
|
|
12
|
+
4. Detect illegal Infrastructure/API leaks into core layers or circular dependencies.
|
|
13
|
+
5. Generate dependency graph and compliance matrix.
|
|
14
|
+
|
|
15
|
+
## Guardrail
|
|
16
|
+
Read-only architectural analysis.
|
|
17
|
+
|
|
18
|
+
## Options
|
|
19
|
+
- --strict fail on any layer boundary violation
|
|
20
|
+
- --json output dependency graph as JSON
|
|
21
|
+
|
|
22
|
+
## Execution
|
|
23
|
+
1. Run .NET solution discovery against the repository.
|
|
24
|
+
2. Apply the workflow above with the provided target.
|
|
25
|
+
3. Report files created/modified and patterns applied.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# dotnet-clean-guardrails
|
|
2
|
+
> Generate NetArchTest architectural unit tests and EditorConfig rules to guard layer boundaries.
|
|
3
|
+
|
|
4
|
+
# /dotnet-clean-guardrails
|
|
5
|
+
|
|
6
|
+
Enforce clean architecture layer boundaries with automated architectural unit tests and EditorConfig.
|
|
7
|
+
|
|
8
|
+
## Workflow
|
|
9
|
+
1. Detect solution architecture layers (Domain, Application, Infrastructure, API).
|
|
10
|
+
2. Generate NetArchTest tests asserting layer boundaries (e.g. Domain should not reference Infrastructure).
|
|
11
|
+
3. Generate or update .editorconfig with C# formatting, naming rules, and diagnostic severities.
|
|
12
|
+
4. Run architectural tests to verify current compliance.
|
|
13
|
+
|
|
14
|
+
## Guardrail
|
|
15
|
+
Do not break legacy builds; apply warnings first or scope enforcement gradually.
|
|
16
|
+
|
|
17
|
+
## Options
|
|
18
|
+
- --netarchtest generate NetArchTest test project
|
|
19
|
+
- --editorconfig generate .editorconfig rules
|
|
20
|
+
|
|
21
|
+
## Execution
|
|
22
|
+
1. Run .NET solution discovery against the repository.
|
|
23
|
+
2. Apply the workflow above with the provided target.
|
|
24
|
+
3. Report files created/modified and patterns applied.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# dotnet-ef-migration
|
|
2
|
+
> Safe Entity Framework Core migration workflow with entity audit, SQL review, and rollback.
|
|
3
|
+
|
|
4
|
+
# /dotnet-ef-migration
|
|
5
|
+
|
|
6
|
+
Plan, generate, and review safe Entity Framework Core database migrations.
|
|
7
|
+
|
|
8
|
+
## Workflow
|
|
9
|
+
1. Audit entity configurations (Fluent API vs DataAnnotations) in target DbContext.
|
|
10
|
+
2. Check for missing indexes, foreign key cascading issues, and property lengths.
|
|
11
|
+
3. Generate migration with a descriptive name (dotnet ef migrations add <Name>).
|
|
12
|
+
4. Generate and inspect raw migration SQL script (dotnet ef migrations script).
|
|
13
|
+
5. Check for destructive schema changes (table drops, column drops, data loss warnings).
|
|
14
|
+
|
|
15
|
+
## Guardrail
|
|
16
|
+
Never execute irreversible or destructive migrations without explicit user confirmation.
|
|
17
|
+
|
|
18
|
+
## Options
|
|
19
|
+
- --name <Name> descriptive migration name
|
|
20
|
+
- --preview-sql inspect generated SQL before applying
|
|
21
|
+
|
|
22
|
+
## Execution
|
|
23
|
+
1. Run .NET solution discovery against the repository.
|
|
24
|
+
2. Apply the workflow above with the provided target.
|
|
25
|
+
3. Report files created/modified and patterns applied.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# dotnet-endpoint-from-model
|
|
2
|
+
> Generate ASP.NET Core Minimal API or Controller endpoints from an entity/model with validation.
|
|
3
|
+
|
|
4
|
+
# /dotnet-endpoint-from-model
|
|
5
|
+
|
|
6
|
+
Generate robust CRUD endpoints from a domain model or entity.
|
|
7
|
+
|
|
8
|
+
## Workflow
|
|
9
|
+
1. Inspect entity/model properties, keys, and navigation properties.
|
|
10
|
+
2. Generate Request/Response DTOs to avoid overposting or leaking domain entities.
|
|
11
|
+
3. Generate endpoint handlers using TypedResults (Ok, Created, NotFound, ValidationProblem).
|
|
12
|
+
4. Configure authorization, route groups, and OpenAPI metadata (.WithName(), .Produces()).
|
|
13
|
+
5. Verify endpoint route constraints.
|
|
14
|
+
|
|
15
|
+
## Guardrail
|
|
16
|
+
Never expose raw database entities directly to API responses. Always map through DTOs.
|
|
17
|
+
|
|
18
|
+
## Options
|
|
19
|
+
- --minimal use ASP.NET Core Minimal APIs (default)
|
|
20
|
+
- --controller use Controller-based architecture
|
|
21
|
+
- --dto <Type> use existing DTO type
|
|
22
|
+
|
|
23
|
+
## Execution
|
|
24
|
+
1. Run .NET solution discovery against the repository.
|
|
25
|
+
2. Apply the workflow above with the provided target.
|
|
26
|
+
3. Report files created/modified and patterns applied.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# dotnet-extract-service
|
|
2
|
+
> Refactor fat services or bloated controllers into isolated CQRS handlers or domain services.
|
|
3
|
+
|
|
4
|
+
# /dotnet-extract-service
|
|
5
|
+
|
|
6
|
+
Decompose fat classes into single-responsibility handlers or domain services.
|
|
7
|
+
|
|
8
|
+
## Workflow
|
|
9
|
+
1. Identify oversized methods or multi-responsibility service classes.
|
|
10
|
+
2. Extract target method into an independent Command/Query handler or domain service.
|
|
11
|
+
3. Define required interfaces and register with Microsoft.Extensions.DependencyInjection.
|
|
12
|
+
4. Update caller references and ensure backward compatibility.
|
|
13
|
+
5. Create unit tests for extracted handler.
|
|
14
|
+
|
|
15
|
+
## Guardrail
|
|
16
|
+
Preserve existing behavior and contract signatures. Do not introduce breaking API changes.
|
|
17
|
+
|
|
18
|
+
## Options
|
|
19
|
+
- --class <Name> source class to refactor
|
|
20
|
+
- --method <Name> specific method to extract
|
|
21
|
+
|
|
22
|
+
## Execution
|
|
23
|
+
1. Run .NET solution discovery against the repository.
|
|
24
|
+
2. Apply the workflow above with the provided target.
|
|
25
|
+
3. Report files created/modified and patterns applied.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# dotnet-feature
|
|
2
|
+
> Build an end-to-end vertical slice feature (Command/Query, MediatR, FluentValidation, Endpoint).
|
|
3
|
+
|
|
4
|
+
# /dotnet-feature
|
|
5
|
+
|
|
6
|
+
Generate a complete, self-contained feature slice following Vertical Slice Architecture.
|
|
7
|
+
|
|
8
|
+
## Workflow
|
|
9
|
+
1. Determine feature name, operation type (Command or Query), and target domain.
|
|
10
|
+
2. Generate Request and Response DTO records with immutable properties.
|
|
11
|
+
3. Generate FluentValidation validator with standard validation rules.
|
|
12
|
+
4. Generate MediatR / Wolverine handler with DbContext or repository access.
|
|
13
|
+
5. Expose via Minimal API endpoint or Controller with TypedResults.
|
|
14
|
+
6. Scaffold unit tests and integration tests.
|
|
15
|
+
|
|
16
|
+
## Guardrail
|
|
17
|
+
Adhere to existing solution conventions (Vertical Slices vs Clean Architecture). Do not introduce unnecessary abstractions.
|
|
18
|
+
|
|
19
|
+
## Options
|
|
20
|
+
- --type <command|query> operation type
|
|
21
|
+
- --model <Entity> domain entity to base feature on
|
|
22
|
+
|
|
23
|
+
## Execution
|
|
24
|
+
1. Run .NET solution discovery against the repository.
|
|
25
|
+
2. Apply the workflow above with the provided target.
|
|
26
|
+
3. Report files created/modified and patterns applied.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# dotnet-generate-tests
|
|
2
|
+
> Generate xUnit/NUnit tests with FluentAssertions and WebApplicationFactory integration tests.
|
|
3
|
+
|
|
4
|
+
# /dotnet-generate-tests
|
|
5
|
+
|
|
6
|
+
Generate comprehensive unit and integration tests for .NET handlers, services, and endpoints.
|
|
7
|
+
|
|
8
|
+
## Workflow
|
|
9
|
+
1. Inspect target class (Handler, Validator, Service, or Endpoint).
|
|
10
|
+
2. Generate unit tests covering valid input, boundary conditions, and error cases.
|
|
11
|
+
3. Use Moq or NSubstitute for dependencies and FluentAssertions for assertions.
|
|
12
|
+
4. For endpoints, generate WebApplicationFactory integration tests verifying status codes and payloads.
|
|
13
|
+
5. Run tests with dotnet test.
|
|
14
|
+
|
|
15
|
+
## Guardrail
|
|
16
|
+
Mock only external boundaries (I/O, database, third-party APIs); test real business domain logic.
|
|
17
|
+
|
|
18
|
+
## Options
|
|
19
|
+
- --framework <xunit|nunit> test framework (default: xunit)
|
|
20
|
+
- --integration generate WebApplicationFactory integration tests
|
|
21
|
+
|
|
22
|
+
## Execution
|
|
23
|
+
1. Run .NET solution discovery against the repository.
|
|
24
|
+
2. Apply the workflow above with the provided target.
|
|
25
|
+
3. Report files created/modified and patterns applied.
|