@multileaf/ai-workflow 0.1.3 → 0.1.4
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/CHANGELOG.md +18 -1
- package/dist/adapter.js +8 -6
- package/dist/generator.js +5 -3
- package/dist/profile.d.ts +19 -0
- package/dist/profile.js +145 -10
- package/dist/recommendations.js +64 -4
- package/dist/selection.js +7 -1
- package/dist/workflow.js +44 -5
- package/docs/ai-init-workflow-spec.md +45 -0
- package/docs/product-requirements.md +1 -0
- package/package.json +1 -1
- package/resources/agents/nestjs-engineer.md +3 -0
- package/resources/agents/prisma-engineer.md +3 -0
- package/resources/package.yaml +64 -4
- package/resources/rules/data-access-policy.md +7 -0
- package/resources/skills/nestjs-development.md +8 -0
- package/resources/skills/prisma-data-access.md +8 -0
- package/resources/skills/vite-frontend.md +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ All notable changes to this project are documented in this file. The format foll
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.4] - 2026-09-12
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Guided `/ai-init` onboarding with model-cost guidance, parallel project analysis, monorepo-aware profiles, selectable resource blocks, and project-specific resource personalization.
|
|
12
|
+
- NestJS, Prisma, and Vite capability recommendations with matching skills, agents, and data-access guidance.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Allow recommendation selection by individual resource, including an explicit select-all option.
|
|
17
|
+
- Expand project profiles with workspace-level technologies, quality tools, test runners, architecture, patterns, and evidence.
|
|
18
|
+
|
|
19
|
+
### Documentation
|
|
20
|
+
|
|
21
|
+
- Specify onboarding flow, host capabilities, MCP recommendations, safety expectations, and acceptance criteria.
|
|
22
|
+
|
|
7
23
|
## [0.1.3] - 2026-09-12
|
|
8
24
|
|
|
9
25
|
### Fixed
|
|
@@ -50,7 +66,8 @@ All notable changes to this project are documented in this file. The format foll
|
|
|
50
66
|
|
|
51
67
|
- Path confinement, symlink protection, explicit permission gates, secret redaction, strict parsers, atomic migrations, and immutable execution checkpoints.
|
|
52
68
|
|
|
53
|
-
[Unreleased]: https://github.com/MultiLeaf/aiw/compare/v0.1.
|
|
69
|
+
[Unreleased]: https://github.com/MultiLeaf/aiw/compare/v0.1.4...HEAD
|
|
70
|
+
[0.1.4]: https://github.com/MultiLeaf/aiw/compare/v0.1.3...v0.1.4
|
|
54
71
|
[0.1.3]: https://github.com/MultiLeaf/aiw/compare/v0.1.2...v0.1.3
|
|
55
72
|
[0.1.2]: https://github.com/MultiLeaf/aiw/compare/v0.1.1...v0.1.2
|
|
56
73
|
[0.1.1]: https://github.com/MultiLeaf/aiw/releases/tag/v0.1.1
|
package/dist/adapter.js
CHANGED
|
@@ -40,12 +40,14 @@ This skill is the bootstrap for AI Workflow. The installer intentionally install
|
|
|
40
40
|
|
|
41
41
|
## First-run setup
|
|
42
42
|
|
|
43
|
-
1.
|
|
44
|
-
2.
|
|
45
|
-
3. Run \`npx --yes --package=@multileaf/ai-workflow -- aiw
|
|
46
|
-
4.
|
|
47
|
-
5.
|
|
48
|
-
6.
|
|
43
|
+
1. Before scanning, recommend that the user switch to a cheaper model for this onboarding. Ask whether they switched or prefer to continue with the current model; never change model settings yourself. Explain that analysis is read-only and optional resources will not be installed without their selection.
|
|
44
|
+
2. Use the host's native subagent mechanism to launch these read-only analyses in parallel: (a) technologies, root and nested manifests, workspaces, and monorepo boundaries; (b) architecture, modules, entry points, and recurring code patterns; (c) tests, quality commands, CI, and developer tools. Keep the main agent as orchestrator. Wait for all results, verify important claims against cited project files, reconcile disagreements, and consolidate one evidence-backed project profile. Do not inspect secrets, ignored files, dependency directories such as \`node_modules\`, or generated output; inspect declared dependencies in project manifests. If subagents are unavailable, perform the same analyses directly and disclose the fallback.
|
|
45
|
+
3. Run \`npx --yes --package=@multileaf/ai-workflow -- aiw scan\` and read \`.aiw/profile.yml\`. Treat this deterministic scan as another evidence source, not as a replacement for workspace-level analysis. Summarize each workspace separately; do not assume root dependencies or commands apply to every module. Ask the user to confirm uncertain or conflicting facts before using them as project policy. Add confirmed architecture and recurring patterns to the matching entries in the \`project_modules\` JSON array in \`.aiw/profile.yml\`, including file-path evidence; create a root-level entry when only the root project exists. Preserve existing detected fields and never store secrets or source-code excerpts there.
|
|
46
|
+
4. Run \`npx --yes --package=@multileaf/ai-workflow -- aiw recommend\` and read \`.aiw/recommendations.yml\`. Combine catalog results with the consolidated project profile. Recommend matching skills, rules, agents, hooks, templates, and MCP servers. For every MCP, disclose its publisher/source, purpose, capabilities, permissions, transport, and credential needs; verify them against current official host and server documentation. Recommend only integrations supported by project evidence and never invent credentials.
|
|
47
|
+
5. Use the host's native selection widget. First offer three choices: install all recommendations, customize, or install none. If the user chooses customization, show the install plan in separate selectable blocks for skills, rules, agents, hooks, templates, and MCPs, with each item, its rationale, project-specific changes, and dependencies visible. If the host lacks a multi-select widget, use a numbered all/custom/none prompt and grouped checklists. MCP selection does not grant permission to execute the server or use external access; ask separately for those permissions.
|
|
48
|
+
6. Record all recommendations with \`npx --yes --package=@multileaf/ai-workflow -- aiw recommend --select=all\`; for customized item-level choices, pass the selected resource references such as \`skills/verification,rules/tdd-policy,agents/test-engineer\`. Use \`npx --yes --package=@multileaf/ai-workflow -- aiw recommend --select=\` when none are chosen. If any AI Workflow resources are selected, run \`npx --yes --package=@multileaf/ai-workflow -- aiw sync\`, adding \`--allow=network:external\` only after separate approval. Install only selected resources and declared dependencies. Tailor instructions only where project evidence changes their usefulness (such as test commands, workspace boundaries, framework patterns, or module architecture). Preserve existing files, configuration, and secrets; report conflicts rather than overwriting them.
|
|
49
|
+
7. For selected MCPs, add only the selected servers to the host's supported project configuration. Check current official host and server documentation for the configuration format, merge existing settings without replacing unrelated entries, and use environment-variable references instead of literal credentials. Do not run server install/start/authentication commands without separate approval. If the target requires user-level changes or safe configuration cannot be validated, give the user exact manual steps and report the MCP as pending rather than installed.
|
|
50
|
+
8. Report installed resources by block, project-specific changes, skipped recommendations, MCP trust/authentication steps, and conflicts. Recommend restarting or reloading the coding-agent session so it discovers newly installed resources; follow known host behavior if it reloads changes automatically. Introduce only the gates supported by installed resources: brainstorming → specification → architecture decision when needed → implementation plan → implementation with tests → verification → traceability → review.
|
|
49
51
|
|
|
50
52
|
If the user declines all recommendations, leave the project with only the bootstrap skill and scan state. Do not treat scanning or recommendation as consent to install.
|
|
51
53
|
|
package/dist/generator.js
CHANGED
|
@@ -3,11 +3,13 @@ export function generateProjectResources(profile, selected, write) {
|
|
|
3
3
|
const linterCommand = safeInlineValue(profile.quality.linter?.command);
|
|
4
4
|
const formatterCommand = safeInlineValue(profile.quality.formatter?.command);
|
|
5
5
|
const testCommand = safeInlineValue(profile.testing?.command);
|
|
6
|
-
if (selected.includes("typescript-quality"))
|
|
6
|
+
if (selected.includes("typescript-quality") || selected.includes("rules/project-quality"))
|
|
7
7
|
write(resolveGeneratedPath("rules/project-quality.md"), `# Project Quality Rules\n\n- Use ${profile.runtime.languages.map((language) => (language === "typescript" ? "TypeScript" : language)).join(", ")} with strict typing.\n${linterCommand ? `- Run \`${linterCommand}\` before completion.\n` : ""}${formatterCommand ? `- Run \`${formatterCommand}\` before review.\n` : ""}- Keep all generated AI Workflow artifacts in English.\n`);
|
|
8
|
-
if (selected.includes("tdd-development"))
|
|
8
|
+
if (selected.includes("tdd-development") || selected.includes("rules/tdd-project-policy"))
|
|
9
9
|
write(resolveGeneratedPath("rules/tdd-project-policy.md"), `# TDD Project Policy\n\n${testCommand ? `- Run \`${testCommand}\` for verification.\n` : ""}- Write a failing behavioral test before implementation.\n- Refactor only after the test is green.\n`);
|
|
10
|
-
if (selected.includes("nextjs-development") ||
|
|
10
|
+
if (selected.includes("nextjs-development") ||
|
|
11
|
+
selected.includes("react-best-practices") ||
|
|
12
|
+
selected.includes("rules/architecture-policy"))
|
|
11
13
|
write(resolveGeneratedPath("agents/project-context.md"), `# Project Context\n\nThis project uses ${profile.frameworks.map((framework) => (framework === "nextjs" ? "Next.js" : framework)).join(", ")}. Follow the detected project structure and existing framework conventions.\n`);
|
|
12
14
|
if (selected.length > 0) {
|
|
13
15
|
write(resolveGeneratedPath("context/project-profile.md"), `# Confirmed Project Profile\n\n- Languages: ${profile.runtime.languages.join(", ") || "none detected"}\n- Frameworks: ${profile.frameworks.join(", ") || "none detected"}\n- Package manager: ${profile.packageManager}\n- All generated content is in English.\n`);
|
package/dist/profile.d.ts
CHANGED
|
@@ -3,6 +3,24 @@ export type Tool = {
|
|
|
3
3
|
name: string;
|
|
4
4
|
command?: string;
|
|
5
5
|
};
|
|
6
|
+
export type ProjectModule = {
|
|
7
|
+
path: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
runtime: {
|
|
10
|
+
languages: string[];
|
|
11
|
+
};
|
|
12
|
+
frameworks: string[];
|
|
13
|
+
packageManager: string;
|
|
14
|
+
quality: {
|
|
15
|
+
linter?: Tool;
|
|
16
|
+
formatter?: Tool;
|
|
17
|
+
typecheck?: Tool;
|
|
18
|
+
};
|
|
19
|
+
testing?: Tool;
|
|
20
|
+
architecture?: string[];
|
|
21
|
+
patterns?: string[];
|
|
22
|
+
evidence?: string[];
|
|
23
|
+
};
|
|
6
24
|
export type ProjectProfile = {
|
|
7
25
|
runtime: {
|
|
8
26
|
languages: string[];
|
|
@@ -17,6 +35,7 @@ export type ProjectProfile = {
|
|
|
17
35
|
testing?: Tool;
|
|
18
36
|
ci: string[];
|
|
19
37
|
workspaces: string[];
|
|
38
|
+
modules?: ProjectModule[];
|
|
20
39
|
facts: EvidenceFact[];
|
|
21
40
|
};
|
|
22
41
|
export declare function parseProjectProfile(content: string): ProjectProfile;
|
package/dist/profile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
3
|
import { scanProject } from "./scanner.js";
|
|
4
4
|
import { buildFacts } from "./evidence.js";
|
|
5
5
|
function scalar(content, key) {
|
|
@@ -36,6 +36,15 @@ export function parseProjectProfile(content) {
|
|
|
36
36
|
const toolName = scalar(content, name);
|
|
37
37
|
return toolName ? { name: toolName, command: scalar(content, command) } : undefined;
|
|
38
38
|
};
|
|
39
|
+
let modules = [];
|
|
40
|
+
try {
|
|
41
|
+
const parsed = JSON.parse(scalar(content, "project_modules") ?? "[]");
|
|
42
|
+
if (Array.isArray(parsed))
|
|
43
|
+
modules = parseModules(parsed);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
modules = [];
|
|
47
|
+
}
|
|
39
48
|
return {
|
|
40
49
|
runtime: { languages },
|
|
41
50
|
frameworks,
|
|
@@ -48,12 +57,68 @@ export function parseProjectProfile(content) {
|
|
|
48
57
|
testing: tool("testing", "testing_command"),
|
|
49
58
|
ci: list(content, "ci"),
|
|
50
59
|
workspaces: list(content, "workspaces"),
|
|
60
|
+
modules,
|
|
51
61
|
facts: [],
|
|
52
62
|
};
|
|
53
63
|
}
|
|
64
|
+
function parseModules(values) {
|
|
65
|
+
return values.slice(0, 256).flatMap((value) => {
|
|
66
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
67
|
+
return [];
|
|
68
|
+
const item = value;
|
|
69
|
+
if (typeof item.path !== "string" || !item.path.trim())
|
|
70
|
+
return [];
|
|
71
|
+
const runtime = item.runtime && typeof item.runtime === "object"
|
|
72
|
+
? item.runtime
|
|
73
|
+
: {};
|
|
74
|
+
const quality = item.quality && typeof item.quality === "object"
|
|
75
|
+
? item.quality
|
|
76
|
+
: {};
|
|
77
|
+
const tool = (candidate) => {
|
|
78
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
|
|
79
|
+
return undefined;
|
|
80
|
+
const details = candidate;
|
|
81
|
+
if (typeof details.name !== "string" || !details.name.trim())
|
|
82
|
+
return undefined;
|
|
83
|
+
return {
|
|
84
|
+
name: details.name.slice(0, 180),
|
|
85
|
+
command: typeof details.command === "string" ? details.command.slice(0, 180) : undefined,
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
return [
|
|
89
|
+
{
|
|
90
|
+
path: item.path.slice(0, 240),
|
|
91
|
+
name: typeof item.name === "string" ? item.name.slice(0, 180) : undefined,
|
|
92
|
+
runtime: { languages: stringArray(runtime.languages) },
|
|
93
|
+
frameworks: stringArray(item.frameworks),
|
|
94
|
+
packageManager: typeof item.packageManager === "string" ? item.packageManager.slice(0, 80) : "unknown",
|
|
95
|
+
quality: {
|
|
96
|
+
linter: tool(quality.linter),
|
|
97
|
+
formatter: tool(quality.formatter),
|
|
98
|
+
typecheck: tool(quality.typecheck),
|
|
99
|
+
},
|
|
100
|
+
testing: tool(item.testing),
|
|
101
|
+
architecture: stringArray(item.architecture),
|
|
102
|
+
patterns: stringArray(item.patterns),
|
|
103
|
+
evidence: stringArray(item.evidence),
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function stringArray(value) {
|
|
109
|
+
return Array.isArray(value)
|
|
110
|
+
? value
|
|
111
|
+
.filter((item) => typeof item === "string")
|
|
112
|
+
.slice(0, 32)
|
|
113
|
+
.map((item) => item.slice(0, 240))
|
|
114
|
+
: [];
|
|
115
|
+
}
|
|
54
116
|
async function packageManifest(root) {
|
|
117
|
+
return packageManifestFile(join(root, "package.json"));
|
|
118
|
+
}
|
|
119
|
+
async function packageManifestFile(path) {
|
|
55
120
|
try {
|
|
56
|
-
return JSON.parse(await readFile(
|
|
121
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
57
122
|
}
|
|
58
123
|
catch {
|
|
59
124
|
return {};
|
|
@@ -69,17 +134,21 @@ export async function profileProject(root) {
|
|
|
69
134
|
const scan = await scanProject(root);
|
|
70
135
|
const manifest = await packageManifest(root);
|
|
71
136
|
const files = scan.files;
|
|
137
|
+
const moduleManifests = files.filter((file) => /(^|\/)package\.json$/.test(file));
|
|
138
|
+
const packageDirectories = moduleManifests.map((file) => dirname(file));
|
|
139
|
+
const modules = await Promise.all(moduleManifests
|
|
140
|
+
.filter((file) => dirname(file) !== ".")
|
|
141
|
+
.map((file) => profileModule(root, file, files, scan.evidence, undefined, packageDirectories)));
|
|
142
|
+
const rootModule = await profileModule(root, "package.json", files, scan.evidence, manifest, packageDirectories);
|
|
72
143
|
const languages = [
|
|
73
|
-
...new Set(
|
|
74
|
-
.
|
|
75
|
-
.
|
|
144
|
+
...new Set([
|
|
145
|
+
...rootModule.runtime.languages,
|
|
146
|
+
...modules.flatMap((item) => item.runtime.languages),
|
|
147
|
+
]),
|
|
76
148
|
];
|
|
77
149
|
const frameworks = [
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
hasDependency(manifest, "vue") ? "vue" : "",
|
|
81
|
-
hasDependency(manifest, "express") ? "express" : "",
|
|
82
|
-
].filter(Boolean);
|
|
150
|
+
...new Set([...rootModule.frameworks, ...modules.flatMap((item) => item.frameworks)]),
|
|
151
|
+
];
|
|
83
152
|
const packageManager = files.includes("pnpm-lock.yaml")
|
|
84
153
|
? "pnpm"
|
|
85
154
|
: files.includes("yarn.lock")
|
|
@@ -116,8 +185,74 @@ export async function profileProject(root) {
|
|
|
116
185
|
testing: testingName ? { name: testingName, command: commandFor(manifest, "test") } : undefined,
|
|
117
186
|
ci: files.some((file) => file.startsWith(".github/workflows/")) ? ["github-actions"] : [],
|
|
118
187
|
workspaces,
|
|
188
|
+
modules,
|
|
119
189
|
facts: [],
|
|
120
190
|
};
|
|
121
191
|
profile.facts = buildFacts(profile, scan.evidence);
|
|
122
192
|
return profile;
|
|
123
193
|
}
|
|
194
|
+
async function profileModule(root, manifestPath, files, evidence, existingManifest, packageDirectories = []) {
|
|
195
|
+
const manifest = existingManifest ?? (await packageManifestFile(join(root, manifestPath)));
|
|
196
|
+
const modulePath = dirname(manifestPath) === "." ? "." : dirname(manifestPath).split("\\").join("/");
|
|
197
|
+
const prefix = modulePath === "." ? "" : `${modulePath}/`;
|
|
198
|
+
const childPrefixes = packageDirectories
|
|
199
|
+
.filter((directory) => directory !== modulePath && directory.startsWith(prefix))
|
|
200
|
+
.map((directory) => `${directory}/`);
|
|
201
|
+
const moduleFiles = files.filter((file) => file.startsWith(prefix) && !childPrefixes.some((childPrefix) => file.startsWith(childPrefix)));
|
|
202
|
+
const languages = [
|
|
203
|
+
...new Set(evidence
|
|
204
|
+
.filter((item) => item.fact.startsWith("language.") && item.source.startsWith(prefix))
|
|
205
|
+
.map((item) => item.fact.slice("language.".length))),
|
|
206
|
+
];
|
|
207
|
+
const frameworkSet = new Set();
|
|
208
|
+
const addDependency = (dependency, label) => {
|
|
209
|
+
if (hasDependency(manifest, dependency))
|
|
210
|
+
frameworkSet.add(label);
|
|
211
|
+
};
|
|
212
|
+
addDependency("next", "nextjs");
|
|
213
|
+
addDependency("react", "react");
|
|
214
|
+
addDependency("vue", "vue");
|
|
215
|
+
addDependency("express", "express");
|
|
216
|
+
addDependency("@nestjs/core", "nestjs");
|
|
217
|
+
addDependency("@prisma/client", "prisma");
|
|
218
|
+
addDependency("prisma", "prisma");
|
|
219
|
+
addDependency("vite", "vite");
|
|
220
|
+
addDependency("fastify", "fastify");
|
|
221
|
+
if (moduleFiles.some((file) => file.startsWith(`${prefix}prisma/`) && file.endsWith(".prisma")))
|
|
222
|
+
frameworkSet.add("prisma");
|
|
223
|
+
const testingName = hasDependency(manifest, "vitest")
|
|
224
|
+
? "vitest"
|
|
225
|
+
: hasDependency(manifest, "jest")
|
|
226
|
+
? "jest"
|
|
227
|
+
: hasDependency(manifest, "@playwright/test")
|
|
228
|
+
? "playwright"
|
|
229
|
+
: hasDependency(manifest, "cypress")
|
|
230
|
+
? "cypress"
|
|
231
|
+
: undefined;
|
|
232
|
+
const qualityTool = (dependency, config, script, name) => hasDependency(manifest, dependency) ||
|
|
233
|
+
moduleFiles.some((file) => file.startsWith(`${prefix}${config}`))
|
|
234
|
+
? { name, command: commandFor(manifest, script) }
|
|
235
|
+
: undefined;
|
|
236
|
+
const packageManager = moduleFiles.some((file) => file === `${prefix}pnpm-lock.yaml`)
|
|
237
|
+
? "pnpm"
|
|
238
|
+
: moduleFiles.some((file) => file === `${prefix}yarn.lock`)
|
|
239
|
+
? "yarn"
|
|
240
|
+
: moduleFiles.some((file) => file === `${prefix}package-lock.json`)
|
|
241
|
+
? "npm"
|
|
242
|
+
: "unknown";
|
|
243
|
+
return {
|
|
244
|
+
path: modulePath,
|
|
245
|
+
name: typeof manifest.name === "string" ? manifest.name : undefined,
|
|
246
|
+
runtime: { languages },
|
|
247
|
+
frameworks: [...frameworkSet],
|
|
248
|
+
packageManager,
|
|
249
|
+
quality: {
|
|
250
|
+
linter: qualityTool("eslint", "eslint.config", "lint", "eslint"),
|
|
251
|
+
formatter: qualityTool("prettier", "prettier", "format", "prettier"),
|
|
252
|
+
typecheck: commandFor(manifest, "typecheck")
|
|
253
|
+
? { name: "typescript", command: commandFor(manifest, "typecheck") }
|
|
254
|
+
: undefined,
|
|
255
|
+
},
|
|
256
|
+
testing: testingName ? { name: testingName, command: commandFor(manifest, "test") } : undefined,
|
|
257
|
+
};
|
|
258
|
+
}
|
package/dist/recommendations.js
CHANGED
|
@@ -85,6 +85,36 @@ const CATALOG = [
|
|
|
85
85
|
? { evidence: ["language:typescript"], confidence: 1 }
|
|
86
86
|
: undefined,
|
|
87
87
|
},
|
|
88
|
+
{
|
|
89
|
+
id: "nestjs-backend",
|
|
90
|
+
provider: "multileaf",
|
|
91
|
+
rationale: "NestJS is detected in this project; its module, dependency-injection, and test conventions can guide backend work.",
|
|
92
|
+
permissions: [],
|
|
93
|
+
resources: local([
|
|
94
|
+
{ type: "skills", id: "nestjs-development" },
|
|
95
|
+
{ type: "agents", id: "nestjs-engineer" },
|
|
96
|
+
{ type: "rules", id: "architecture-policy" },
|
|
97
|
+
{ type: "templates", id: "technical-design" },
|
|
98
|
+
]),
|
|
99
|
+
matches: (profile) => profile.frameworks.includes("nestjs")
|
|
100
|
+
? { evidence: projectEvidence(profile, "nestjs"), confidence: 1 }
|
|
101
|
+
: undefined,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "prisma-data-access",
|
|
105
|
+
provider: "multileaf",
|
|
106
|
+
rationale: "Prisma is detected; schema and data-access changes need migration-aware guidance aligned to the owning workspace.",
|
|
107
|
+
permissions: [],
|
|
108
|
+
resources: local([
|
|
109
|
+
{ type: "skills", id: "prisma-data-access" },
|
|
110
|
+
{ type: "agents", id: "prisma-engineer" },
|
|
111
|
+
{ type: "rules", id: "data-access-policy" },
|
|
112
|
+
{ type: "rules", id: "security-policy" },
|
|
113
|
+
]),
|
|
114
|
+
matches: (profile) => profile.frameworks.includes("prisma")
|
|
115
|
+
? { evidence: projectEvidence(profile, "prisma"), confidence: 1 }
|
|
116
|
+
: undefined,
|
|
117
|
+
},
|
|
88
118
|
{
|
|
89
119
|
id: "vitest-testing",
|
|
90
120
|
provider: "multileaf",
|
|
@@ -130,6 +160,21 @@ const CATALOG = [
|
|
|
130
160
|
? { evidence: ["framework:nextjs"], confidence: 1 }
|
|
131
161
|
: undefined,
|
|
132
162
|
},
|
|
163
|
+
{
|
|
164
|
+
id: "vite-frontend",
|
|
165
|
+
provider: "multileaf",
|
|
166
|
+
rationale: "Vite is detected; frontend build, test, and module guidance should follow the owning app's framework and scripts.",
|
|
167
|
+
permissions: [],
|
|
168
|
+
resources: local([
|
|
169
|
+
{ type: "skills", id: "vite-frontend" },
|
|
170
|
+
{ type: "agents", id: "typescript-engineer" },
|
|
171
|
+
{ type: "rules", id: "quality-gates" },
|
|
172
|
+
{ type: "templates", id: "technical-design" },
|
|
173
|
+
]),
|
|
174
|
+
matches: (profile) => profile.frameworks.includes("vite")
|
|
175
|
+
? { evidence: projectEvidence(profile, "vite"), confidence: 1 }
|
|
176
|
+
: undefined,
|
|
177
|
+
},
|
|
133
178
|
{
|
|
134
179
|
id: "security-review",
|
|
135
180
|
provider: "multileaf",
|
|
@@ -163,15 +208,21 @@ export function recommendCapabilities(profile) {
|
|
|
163
208
|
});
|
|
164
209
|
}
|
|
165
210
|
export function resourcesForCapabilities(ids) {
|
|
166
|
-
const
|
|
167
|
-
const
|
|
211
|
+
const selectedCapabilities = new Set(CATALOG.filter(({ id }) => ids.includes(id)).map(({ id }) => id));
|
|
212
|
+
const selectedResources = new Set(ids.filter((id) => id.includes("/")));
|
|
213
|
+
const resources = CATALOG.flatMap(({ id, resources }) => selectedCapabilities.has(id)
|
|
214
|
+
? resources
|
|
215
|
+
: resources.filter(({ type, id: resourceId }) => selectedResources.has(`${type}/${resourceId}`)));
|
|
168
216
|
return [
|
|
169
217
|
...new Map(resources.map((resource) => [`${resource.type}/${resource.id}`, resource])).values(),
|
|
170
218
|
];
|
|
171
219
|
}
|
|
172
220
|
export function serializeRecommendations(items, selected = []) {
|
|
173
221
|
return `schema: 1\nrecommendations:\n${items
|
|
174
|
-
.map((item) => ` - id: ${item.id}\n provider: ${item.provider}\n confidence: ${item.confidence}\n selected: ${selected.includes(item.id)}\n resources: [${item.resources.map(({ type, id }) => `${type}/${id}`).join(", ")}]\n
|
|
222
|
+
.map((item) => ` - id: ${item.id}\n provider: ${item.provider}\n confidence: ${item.confidence}\n selected: ${selected.includes(item.id)}\n resources: [${item.resources.map(({ type, id }) => `${type}/${id}`).join(", ")}]\n selected_resources: [${item.resources
|
|
223
|
+
.filter(({ type, id }) => selected.includes(`${type}/${id}`))
|
|
224
|
+
.map(({ type, id }) => `${type}/${id}`)
|
|
225
|
+
.join(", ")}]\n evidence: [${item.evidence.map((evidence) => `"${evidence}"`).join(", ")}]\n rationale: ${item.rationale}`)
|
|
175
226
|
.join("\n")}\n`;
|
|
176
227
|
}
|
|
177
228
|
function hasProjectSignals(profile) {
|
|
@@ -182,7 +233,15 @@ function hasProjectSignals(profile) {
|
|
|
182
233
|
profile.quality.linter ||
|
|
183
234
|
profile.ci.length);
|
|
184
235
|
}
|
|
185
|
-
function projectEvidence(profile) {
|
|
236
|
+
function projectEvidence(profile, framework) {
|
|
237
|
+
const moduleEvidence = (profile.modules ?? [])
|
|
238
|
+
.filter((module) => !framework || module.frameworks.includes(framework))
|
|
239
|
+
.flatMap((module) => [
|
|
240
|
+
`workspace:${module.path}`,
|
|
241
|
+
...module.frameworks.map((name) => `framework:${name}`),
|
|
242
|
+
...(module.testing ? [`testing:${module.testing.name}`] : []),
|
|
243
|
+
...(module.testing?.command ? [`test-command:${module.testing.command}`] : []),
|
|
244
|
+
]);
|
|
186
245
|
return [
|
|
187
246
|
...profile.runtime.languages.map((language) => `language:${language}`),
|
|
188
247
|
...profile.frameworks.map((framework) => `framework:${framework}`),
|
|
@@ -190,5 +249,6 @@ function projectEvidence(profile) {
|
|
|
190
249
|
...(profile.testing ? [`testing:${profile.testing.name}`] : []),
|
|
191
250
|
...(profile.quality.linter ? [`linter:${profile.quality.linter.name}`] : []),
|
|
192
251
|
...(profile.ci.length ? profile.ci.map((system) => `ci:${system}`) : []),
|
|
252
|
+
...moduleEvidence,
|
|
193
253
|
];
|
|
194
254
|
}
|
package/dist/selection.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export async function selectRecommendations(items, answer, selectedIds) {
|
|
2
|
+
const available = new Set(items.flatMap((item) => [
|
|
3
|
+
item.id,
|
|
4
|
+
...(item.resources ?? []).map(({ type, id }) => `${type}/${id}`),
|
|
5
|
+
]));
|
|
6
|
+
if (selectedIds?.includes("all"))
|
|
7
|
+
return items.map(({ id }) => id);
|
|
2
8
|
if (selectedIds)
|
|
3
|
-
return selectedIds.filter((id) =>
|
|
9
|
+
return [...new Set(selectedIds.filter((id) => available.has(id)))];
|
|
4
10
|
const selected = [];
|
|
5
11
|
for (const item of items) {
|
|
6
12
|
const response = (await answer(`Install ${item.id}? [Y/n] `)).trim().toLowerCase();
|
package/dist/workflow.js
CHANGED
|
@@ -290,7 +290,7 @@ export async function runCommand(args, root, fs, services = {}) {
|
|
|
290
290
|
const evidence = profile.facts
|
|
291
291
|
.map((fact) => ` - key: ${fact.key}\n value: ${fact.value}\n state: ${fact.state}\n method: ${fact.method}\n confidence: ${fact.confidence}${fact.requiresConfirmation === undefined ? "" : `\n requires_confirmation: ${fact.requiresConfirmation}`}\n evidence: [${fact.evidence.map((item) => `"${item.source}"`).join(", ")}]`)
|
|
292
292
|
.join("\n");
|
|
293
|
-
fs.write(join(aiw, "profile.yml"), `schema: 1\nstatus: scanned\nfacts:\n${evidence}\nruntime:\n languages: [${profile.runtime.languages.join(", ")}]\nframeworks: [${profile.frameworks.join(", ")}]\npackage_manager: ${profile.packageManager}\nquality:\n linter: ${profile.quality.linter?.name ?? "unknown"}\n linter_command: ${profile.quality.linter?.command ?? "unknown"}\n formatter: ${profile.quality.formatter?.name ?? "unknown"}\n formatter_command: ${profile.quality.formatter?.command ?? "unknown"}\n typecheck: ${profile.quality.typecheck?.name ?? "unknown"}\n typecheck_command: ${profile.quality.typecheck?.command ?? "unknown"}\ntesting: ${profile.testing?.name ?? "unknown"}\ntesting_command: ${profile.testing?.command ?? "unknown"}\nci: [${profile.ci.join(", ")}]\nworkspaces: [${profile.workspaces.join(", ")}]\npolicies:\n artifact_language: en\n`);
|
|
293
|
+
fs.write(join(aiw, "profile.yml"), `schema: 1\nstatus: scanned\nfacts:\n${evidence}\nruntime:\n languages: [${profile.runtime.languages.join(", ")}]\nframeworks: [${profile.frameworks.join(", ")}]\npackage_manager: ${profile.packageManager}\nquality:\n linter: ${profile.quality.linter?.name ?? "unknown"}\n linter_command: ${profile.quality.linter?.command ?? "unknown"}\n formatter: ${profile.quality.formatter?.name ?? "unknown"}\n formatter_command: ${profile.quality.formatter?.command ?? "unknown"}\n typecheck: ${profile.quality.typecheck?.name ?? "unknown"}\n typecheck_command: ${profile.quality.typecheck?.command ?? "unknown"}\ntesting: ${profile.testing?.name ?? "unknown"}\ntesting_command: ${profile.testing?.command ?? "unknown"}\nci: [${profile.ci.join(", ")}]\nworkspaces: [${profile.workspaces.join(", ")}]\nproject_modules: ${JSON.stringify(profile.modules ?? [])}\npolicies:\n artifact_language: en\n`);
|
|
294
294
|
return { exitCode: 0, output: "Project profile generated." };
|
|
295
295
|
}
|
|
296
296
|
if (command === "target") {
|
|
@@ -1630,7 +1630,19 @@ function syncSelectedResources(fs, root, aiw, profile, selected) {
|
|
|
1630
1630
|
};
|
|
1631
1631
|
}
|
|
1632
1632
|
function personalizeBundledResource(type, id, content, profile) {
|
|
1633
|
-
|
|
1633
|
+
const contextual = (type === "rules" &&
|
|
1634
|
+
[
|
|
1635
|
+
"quality-gates",
|
|
1636
|
+
"tdd-policy",
|
|
1637
|
+
"dependency-policy",
|
|
1638
|
+
"architecture-policy",
|
|
1639
|
+
"data-access-policy",
|
|
1640
|
+
].includes(id)) ||
|
|
1641
|
+
(type === "skills" &&
|
|
1642
|
+
["nestjs-development", "prisma-data-access", "vite-frontend"].includes(id)) ||
|
|
1643
|
+
(type === "agents" &&
|
|
1644
|
+
["technical-architect", "typescript-engineer", "nestjs-engineer", "prisma-engineer"].includes(id));
|
|
1645
|
+
if (!contextual)
|
|
1634
1646
|
return content;
|
|
1635
1647
|
const signals = [
|
|
1636
1648
|
profile.runtime.languages.length ? `- Languages: ${profile.runtime.languages.join(", ")}` : "",
|
|
@@ -1647,6 +1659,24 @@ function personalizeBundledResource(type, id, content, profile) {
|
|
|
1647
1659
|
profile.quality.formatter?.command
|
|
1648
1660
|
? `- Format command: \`${safeProjectSignal(profile.quality.formatter.command)}\``
|
|
1649
1661
|
: "",
|
|
1662
|
+
...(profile.modules ?? []).map((module) => {
|
|
1663
|
+
const parts = [
|
|
1664
|
+
module.name
|
|
1665
|
+
? `${safeProjectSignal(module.name)} (${safeProjectSignal(module.path)})`
|
|
1666
|
+
: safeProjectSignal(module.path),
|
|
1667
|
+
module.frameworks.length ? `frameworks: ${module.frameworks.join(", ")}` : "",
|
|
1668
|
+
...(module.architecture ?? []).map((item) => `architecture: ${safeProjectSignal(item)}`),
|
|
1669
|
+
...(module.patterns ?? []).map((item) => `patterns: ${safeProjectSignal(item)}`),
|
|
1670
|
+
module.testing
|
|
1671
|
+
? `tests: ${module.testing.name}${module.testing.command ? ` (${safeProjectSignal(module.testing.command)})` : ""}`
|
|
1672
|
+
: "",
|
|
1673
|
+
module.quality.linter
|
|
1674
|
+
? `linter: ${module.quality.linter.name}${module.quality.linter.command ? ` (${safeProjectSignal(module.quality.linter.command)})` : ""}`
|
|
1675
|
+
: "",
|
|
1676
|
+
].filter(Boolean);
|
|
1677
|
+
const evidence = (module.evidence ?? []).slice(0, 8).map(safeProjectSignal);
|
|
1678
|
+
return `- Module ${parts.join("; ")}${evidence.length ? `; evidence: ${evidence.join(", ")}` : ""}`;
|
|
1679
|
+
}),
|
|
1650
1680
|
].filter(Boolean);
|
|
1651
1681
|
return signals.length
|
|
1652
1682
|
? `${content.trimEnd()}\n\n## Detected project context\n\n${signals.join("\n")}\n`
|
|
@@ -1666,11 +1696,20 @@ function readSelectedRecommendations(fs, aiw) {
|
|
|
1666
1696
|
if (!fs.exists(join(aiw, "recommendations.yml")))
|
|
1667
1697
|
return [];
|
|
1668
1698
|
const content = fs.read(join(aiw, "recommendations.yml"));
|
|
1669
|
-
|
|
1699
|
+
const selections = content
|
|
1670
1700
|
.split(/^\s{2}- id: /m)
|
|
1671
1701
|
.slice(1)
|
|
1672
|
-
.
|
|
1673
|
-
|
|
1702
|
+
.flatMap((block) => {
|
|
1703
|
+
const id = block.split("\n", 1)[0].trim();
|
|
1704
|
+
const selectedCapability = /^\s{4}selected: true$/m.test(block) ? [id] : [];
|
|
1705
|
+
const selectedResources = block
|
|
1706
|
+
.match(/^\s{4}selected_resources: \[([^\]]*)\]$/m)?.[1]
|
|
1707
|
+
?.split(",")
|
|
1708
|
+
.map((resource) => resource.trim())
|
|
1709
|
+
.filter(Boolean) ?? [];
|
|
1710
|
+
return [...selectedCapability, ...selectedResources];
|
|
1711
|
+
});
|
|
1712
|
+
return [...new Set(selections)];
|
|
1674
1713
|
}
|
|
1675
1714
|
function parseApprovedPermissions(args) {
|
|
1676
1715
|
return args
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# AI Init Project Onboarding Specification
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Make `/ai-init` a guided, evidence-backed onboarding flow. The initial `npx` install remains a small bootstrap; `/ai-init` analyzes the repository, recommends project-matched capabilities, gets an explicit selection, installs only that selection, and reports any restart needed to load it.
|
|
6
|
+
|
|
7
|
+
## User flow
|
|
8
|
+
|
|
9
|
+
1. **Model and consent.** Before repository analysis, recommend switching to a lower-cost model. Do not change model settings. Ask whether the user switched or wants to continue with the current model. Explain that repository analysis is read-only until resource selection.
|
|
10
|
+
2. **Parallel project analysis.** Use the host's native subagent mechanism to start independent read-only scans in parallel. Assign separate work to (a) technologies and package/workspace topology, (b) architecture, modules, and code patterns, and (c) tests, quality tools, and CI. The initiating agent is the orchestrator: it consolidates findings, resolves conflicts using repository evidence, and records paths for every material conclusion. If the host cannot start subagents, do the same scans directly and say so.
|
|
11
|
+
3. **Monorepo coverage.** Discover root and nested project manifests, workspace definitions, package boundaries, framework configs, tests, and module entry points. Analyze each workspace separately, then summarize shared tooling and cross-workspace relationships. Never infer that root dependencies apply to every workspace.
|
|
12
|
+
4. **Scan record.** Run the deterministic AI Workflow scan and combine it with the parallel analysis. Present technologies, architecture, patterns, modules, tests, quality commands, workspace topology, confidence, and source evidence. Do not include secret values. Ask for confirmation where conflicting or uncertain findings would affect installed guidance.
|
|
13
|
+
5. **Recommendations.** Match catalog capabilities to the consolidated project profile. Show each recommendation with its purpose, evidence, resource types (skills, rules, agents, hooks, templates), required permissions, MCP server/source, and project-specific configuration steps. Recommend an MCP only when repository evidence supports its use; disclose its publisher/source, capabilities, access, transport, and credential needs. Never invent credentials or silently enable an MCP.
|
|
14
|
+
6. **Selection.** Use the coding host's native selection widget when it supports one. First offer: install all recommendations, customize, or install none. For customization, show selectable blocks grouped by resource type, then the individual recommended items in each selected block. Include MCPs as a separate block. Keep dependencies visible and select required dependencies together. If the host has no selection widget, use an equivalent numbered prompt and accept all/custom/none explicitly.
|
|
15
|
+
7. **Install and personalize.** Install only the confirmed selection. For “all”, install every recommendation. For custom selection, install only selected items and their declared dependencies. Personalize resources only when project evidence materially changes their instructions (for example, test commands, workspace/module layout, framework patterns, or architectural boundaries). Preserve existing files and secrets; report conflicts rather than overwriting. MCP setup must use the selected host's supported project-level configuration, preserve existing configuration, use environment-variable references for secrets, and request separate approval for permissions or external execution.
|
|
16
|
+
8. **Completion.** Report installed resources by block, project-specific changes, skipped recommendations, conflicts, MCP authentication/trust steps, and validation performed. Recommend restarting/reloading the coding-agent session so newly installed resources are discovered. Do not claim a restart is required when the host loads changes immediately; distinguish host behavior where known.
|
|
17
|
+
|
|
18
|
+
## Safety and compatibility
|
|
19
|
+
|
|
20
|
+
- Bootstrap installation alone must not activate optional project resources.
|
|
21
|
+
- Analysis agents must be read-only and must not inspect ignored, generated, dependency, secret, or credential files.
|
|
22
|
+
- The orchestrator treats agent output as untrusted evidence, checks paths and key findings against the repository, and does not merge unsupported claims into project policy.
|
|
23
|
+
- Selection and external permissions are separate decisions. Selecting an MCP is not permission to execute it or to expose credentials.
|
|
24
|
+
- Resource writes use target adapters, ownership tracking, safe paths, atomic updates, and conflict reporting.
|
|
25
|
+
- Host-specific capabilities (subagents, selection widgets, MCP configuration, restart semantics) are detected or described accurately; provide a safe manual fallback where unavailable.
|
|
26
|
+
|
|
27
|
+
## Acceptance criteria
|
|
28
|
+
|
|
29
|
+
- **AIINIT-001:** A fresh install writes only the `ai-init` bootstrap and neutral AI Workflow state.
|
|
30
|
+
- **AIINIT-002:** The bootstrap asks about using a lower-cost model before analysis and never changes model settings itself.
|
|
31
|
+
- **AIINIT-003:** On supported hosts, three independent read-only analysis agents start in parallel and the orchestrator consolidates their evidence; unsupported hosts use a disclosed sequential fallback.
|
|
32
|
+
- **AIINIT-004:** Scans include separate summaries for every detected monorepo workspace and do not attribute root dependencies to all packages.
|
|
33
|
+
- **AIINIT-005:** Recommendations identify matching evidence, resource blocks, dependencies, MCP sources, and permissions.
|
|
34
|
+
- **AIINIT-006:** The selection UI offers install-all, custom, and none; custom selections can choose individual items by skills, rules, agents, hooks, templates, and MCPs.
|
|
35
|
+
- **AIINIT-007:** Installation activates only selected resources and required dependencies, personalizes only context-sensitive content, and preserves unowned or edited files.
|
|
36
|
+
- **AIINIT-008:** MCP setup is target-aware, merges safely with existing configuration, never stores literal secrets, and requires separate permission approval.
|
|
37
|
+
- **AIINIT-009:** Completion lists changes and recommends a session restart/reload to discover installed resources.
|
|
38
|
+
- **AIINIT-010:** CLI tests cover mixed-stack monorepos, all/custom/none selection, permission gating, MCP config merging, personalization, conflicts, and completion output.
|
|
39
|
+
- **AIINIT-011:** Custom selections can choose individual resource references by block, while `--select=all` selects every recommended capability and `--select=` selects none.
|
|
40
|
+
|
|
41
|
+
## Delivery slices
|
|
42
|
+
|
|
43
|
+
1. Bootstrap orchestration, host-native selection, model-cost prompt, and completion/reload guidance.
|
|
44
|
+
2. Deterministic monorepo profiles, stack-specific capability recommendations, resource-level selection, and context-aware sync.
|
|
45
|
+
3. MCP catalog provenance, per-target project configuration/merge support, credential placeholders, ownership, and config validation.
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
- Selected resources must be customized using confirmed project facts.
|
|
25
25
|
- `/ai-init` must orchestrate scan → recommendations → user selection → selected-resource sync and summarize what it added.
|
|
26
26
|
- The bootstrap must invoke CLI operations through the package runner; it cannot assume `aiw` is installed globally or in the consuming project's dependencies.
|
|
27
|
+
- The complete onboarding behavior and acceptance criteria are defined in [`ai-init-workflow-spec.md`](./ai-init-workflow-spec.md).
|
|
27
28
|
|
|
28
29
|
### Development workflow
|
|
29
30
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# NestJS Engineer
|
|
2
|
+
|
|
3
|
+
Analyze the target NestJS module and its tests before proposing changes. Report relevant controllers, providers, dependency-injection relationships, DTO validation, and module boundaries with file evidence. Implement only within the assigned scope, preserve existing patterns, and return changed paths plus the exact workspace checks run. Do not access secrets or run migrations.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# Prisma Engineer
|
|
2
|
+
|
|
3
|
+
Inspect the active Prisma schema, generated-client usage, migrations, and data-access tests. Explain the impact of proposed schema/query changes and preserve the project's transaction and migration conventions. Never reset, migrate, or access a database without explicit approval. Return evidence paths, changed paths, and workspace-scoped validation results; do not include connection strings or credentials.
|
package/resources/package.yaml
CHANGED
|
@@ -28,6 +28,9 @@ resources:
|
|
|
28
28
|
- { id: documentation-maintenance, version: 0.1.0, path: skills/documentation-maintenance.md }
|
|
29
29
|
- { id: adapter-development, version: 0.1.0, path: skills/adapter-development.md }
|
|
30
30
|
- { id: skill-authoring, version: 0.1.0, path: skills/skill-authoring.md }
|
|
31
|
+
- { id: nestjs-development, version: 0.1.0, path: skills/nestjs-development.md }
|
|
32
|
+
- { id: prisma-data-access, version: 0.1.0, path: skills/prisma-data-access.md }
|
|
33
|
+
- { id: vite-frontend, version: 0.1.0, path: skills/vite-frontend.md }
|
|
31
34
|
rules:
|
|
32
35
|
- { id: language-policy, version: 0.1.0, path: rules/language-policy.md }
|
|
33
36
|
- { id: tdd-policy, version: 0.1.0, path: rules/tdd-policy.md }
|
|
@@ -39,6 +42,7 @@ resources:
|
|
|
39
42
|
- { id: documentation-policy, version: 0.1.0, path: rules/documentation-policy.md }
|
|
40
43
|
- { id: self-validation, version: 0.1.0, path: rules/self-validation.md }
|
|
41
44
|
- { id: completion-validation, version: 0.1.0, path: rules/completion-validation.md }
|
|
45
|
+
- { id: data-access-policy, version: 0.1.0, path: rules/data-access-policy.md }
|
|
42
46
|
agents:
|
|
43
47
|
- { id: requirements-analyst, version: 0.1.0, path: agents/requirements-analyst.md }
|
|
44
48
|
- { id: technical-architect, version: 0.1.0, path: agents/technical-architect.md }
|
|
@@ -48,6 +52,8 @@ resources:
|
|
|
48
52
|
- { id: security-reviewer, version: 0.1.0, path: agents/security-reviewer.md }
|
|
49
53
|
- { id: adapter-engineer, version: 0.1.0, path: agents/adapter-engineer.md }
|
|
50
54
|
- { id: documentation-writer, version: 0.1.0, path: agents/documentation-writer.md }
|
|
55
|
+
- { id: nestjs-engineer, version: 0.1.0, path: agents/nestjs-engineer.md }
|
|
56
|
+
- { id: prisma-engineer, version: 0.1.0, path: agents/prisma-engineer.md }
|
|
51
57
|
hooks:
|
|
52
58
|
- { id: pre-implementation, version: 0.1.0, path: hooks/pre-implementation.md }
|
|
53
59
|
- { id: post-implementation, version: 0.1.0, path: hooks/post-implementation.md }
|
|
@@ -66,8 +72,62 @@ resources:
|
|
|
66
72
|
- { id: security-review, version: 0.1.0, path: templates/security-review.md }
|
|
67
73
|
- { id: migration-report, version: 0.1.0, path: templates/migration-report.md }
|
|
68
74
|
provides:
|
|
69
|
-
skills:
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
skills:
|
|
76
|
+
[
|
|
77
|
+
project-init,
|
|
78
|
+
brainstorming,
|
|
79
|
+
requirements-specification,
|
|
80
|
+
technical-design,
|
|
81
|
+
implementation-planning,
|
|
82
|
+
tdd-development,
|
|
83
|
+
verification,
|
|
84
|
+
code-review,
|
|
85
|
+
security-review,
|
|
86
|
+
documentation-maintenance,
|
|
87
|
+
adapter-development,
|
|
88
|
+
skill-authoring,
|
|
89
|
+
nestjs-development,
|
|
90
|
+
prisma-data-access,
|
|
91
|
+
vite-frontend,
|
|
92
|
+
]
|
|
93
|
+
rules:
|
|
94
|
+
[
|
|
95
|
+
language-policy,
|
|
96
|
+
tdd-policy,
|
|
97
|
+
architecture-policy,
|
|
98
|
+
quality-gates,
|
|
99
|
+
security-policy,
|
|
100
|
+
dependency-policy,
|
|
101
|
+
generated-files,
|
|
102
|
+
documentation-policy,
|
|
103
|
+
self-validation,
|
|
104
|
+
completion-validation,
|
|
105
|
+
data-access-policy,
|
|
106
|
+
]
|
|
107
|
+
agents:
|
|
108
|
+
[
|
|
109
|
+
requirements-analyst,
|
|
110
|
+
technical-architect,
|
|
111
|
+
typescript-engineer,
|
|
112
|
+
test-engineer,
|
|
113
|
+
quality-reviewer,
|
|
114
|
+
security-reviewer,
|
|
115
|
+
adapter-engineer,
|
|
116
|
+
documentation-writer,
|
|
117
|
+
nestjs-engineer,
|
|
118
|
+
prisma-engineer,
|
|
119
|
+
]
|
|
72
120
|
hooks: [pre-implementation, post-implementation, pre-commit, post-change, pre-release]
|
|
73
|
-
templates:
|
|
121
|
+
templates:
|
|
122
|
+
[
|
|
123
|
+
brainstorm,
|
|
124
|
+
specification,
|
|
125
|
+
technical-design,
|
|
126
|
+
decision-record,
|
|
127
|
+
implementation-plan,
|
|
128
|
+
task,
|
|
129
|
+
test-plan,
|
|
130
|
+
verification-report,
|
|
131
|
+
security-review,
|
|
132
|
+
migration-report,
|
|
133
|
+
]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Data Access Policy
|
|
2
|
+
|
|
3
|
+
- Follow the repository's existing data-access and transaction boundaries.
|
|
4
|
+
- Scope commands and tests to the workspace that owns the database schema or query.
|
|
5
|
+
- Review migration contents before applying them; never run destructive database commands without explicit approval.
|
|
6
|
+
- Keep credentials in the project's approved secret store or environment, never in source, logs, or generated AI instructions.
|
|
7
|
+
- Validate data-access changes with relevant tests and report any migration or compatibility impact.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nestjs-development
|
|
3
|
+
description: Implement and review NestJS services using the repository's module boundaries and conventions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# NestJS Development
|
|
7
|
+
|
|
8
|
+
Inspect the relevant NestJS module before editing. Follow the project's established controller, provider, dependency-injection, guard, pipe, interceptor, and DTO patterns; do not introduce a new architectural layer without evidence or approval. Keep transport concerns in controllers and business behavior in the existing service/domain boundaries. Validate external input with the project's configured validation approach. Add focused unit or integration tests using the test runner configured for this workspace, and run that workspace's declared quality commands.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prisma-data-access
|
|
3
|
+
description: Change Prisma schemas and data access safely within the repository's database conventions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Prisma Data Access
|
|
7
|
+
|
|
8
|
+
Read the active Prisma schema, client setup, existing migrations, and callers before changing data access. Preserve naming, relation, transaction, and repository patterns already used by the project. Do not apply migrations, reset databases, or run destructive commands without explicit approval. When schema changes are selected, explain migration impact, add or update tests around affected queries, and use the exact Prisma and test commands declared by the relevant workspace. Never copy connection strings or credentials into generated instructions.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vite-frontend
|
|
3
|
+
description: Build and verify Vite frontends using the repository's framework, module, and test conventions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Vite Frontend
|
|
7
|
+
|
|
8
|
+
Inspect the Vite entry point, framework plugins, route/component structure, and workspace-local scripts before making changes. Follow the existing component, state, styling, accessibility, and API-client patterns. Keep frontend configuration scoped to the correct workspace. Add focused tests with the configured runner and verify the workspace's declared lint, typecheck, and build commands. Do not assume that a root-level command applies to every app in a monorepo.
|