paqad-ai 0.0.1
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/LICENSE +21 -0
- package/README.md +27 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +2699 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +1195 -0
- package/dist/index.js +4205 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
- package/runtime/domains/_shared/agents/adversarial-reviewer.md +19 -0
- package/runtime/domains/_shared/agents/context-curator.md +19 -0
- package/runtime/domains/_shared/agents/doc-maintainer.md +19 -0
- package/runtime/domains/_shared/agents/final-reviewer.md +19 -0
- package/runtime/domains/_shared/agents/gap-detector.md +19 -0
- package/runtime/domains/_shared/agents/market-researcher.md +19 -0
- package/runtime/domains/_shared/agents/product-owner.md +19 -0
- package/runtime/domains/_shared/agents/requirement-analyst.md +19 -0
- package/runtime/domains/_shared/agents/router.md +18 -0
- package/runtime/domains/_shared/agents/story-designer.md +18 -0
- package/runtime/domains/_shared/agents/test-planner.md +19 -0
- package/runtime/domains/_shared/agents/verifier.md +19 -0
- package/runtime/domains/_shared/checklists/definition-of-done.md +5 -0
- package/runtime/domains/_shared/checklists/definition-of-ready.md +5 -0
- package/runtime/domains/_shared/checklists/human-escalation.md +5 -0
- package/runtime/domains/_shared/rules/canonical-docs.md +11 -0
- package/runtime/domains/_shared/rules/constitution.md +11 -0
- package/runtime/domains/_shared/rules/documentation.md +11 -0
- package/runtime/domains/_shared/rules/observability.md +11 -0
- package/runtime/domains/_shared/rules/performance.md +11 -0
- package/runtime/domains/_shared/rules/pipeline.md +11 -0
- package/runtime/domains/_shared/rules/security.md +11 -0
- package/runtime/domains/_shared/rules/testing.md +11 -0
- package/runtime/domains/_shared/skills/acceptance-criteria-gen/SKILL.md +21 -0
- package/runtime/domains/_shared/skills/adversarial-review/SKILL.md +21 -0
- package/runtime/domains/_shared/skills/api-doc-maintainer/SKILL.md +38 -0
- package/runtime/domains/_shared/skills/canonical-doc-sync/SKILL.md +23 -0
- package/runtime/domains/_shared/skills/edge-case-detection/SKILL.md +21 -0
- package/runtime/domains/_shared/skills/error-catalog-maintainer/SKILL.md +39 -0
- package/runtime/domains/_shared/skills/existing-doc-checker/SKILL.md +23 -0
- package/runtime/domains/_shared/skills/glossary-maintainer/SKILL.md +23 -0
- package/runtime/domains/_shared/skills/integration-doc-maintainer/SKILL.md +38 -0
- package/runtime/domains/_shared/skills/request-classifier/SKILL.md +21 -0
- package/runtime/domains/_shared/skills/requirement-enrichment/SKILL.md +21 -0
- package/runtime/domains/_shared/skills/sequence-planner/SKILL.md +21 -0
- package/runtime/domains/_shared/skills/story-and-solution-writer/SKILL.md +21 -0
- package/runtime/domains/_shared/skills/test-per-ac-planner/SKILL.md +21 -0
- package/runtime/domains/coding/agents/database-expert.md +19 -0
- package/runtime/domains/coding/agents/solution-architect.md +20 -0
- package/runtime/domains/coding/agents/ux-ui-analyst.md +19 -0
- package/runtime/domains/coding/benchmarks/patterns/authentication-flows.md +9 -0
- package/runtime/domains/coding/benchmarks/patterns/empty-states.md +9 -0
- package/runtime/domains/coding/benchmarks/patterns/error-handling.md +9 -0
- package/runtime/domains/coding/benchmarks/patterns/form-patterns.md +9 -0
- package/runtime/domains/coding/benchmarks/patterns/loading-patterns.md +9 -0
- package/runtime/domains/coding/benchmarks/patterns/navigation-patterns.md +9 -0
- package/runtime/domains/coding/benchmarks/patterns/search-patterns.md +9 -0
- package/runtime/domains/coding/benchmarks/patterns/table-data-display.md +9 -0
- package/runtime/domains/coding/checklists/database-review-20pt.md +5 -0
- package/runtime/domains/coding/checklists/edge-cases-coding.md +5 -0
- package/runtime/domains/coding/rules/architecture.md +11 -0
- package/runtime/domains/coding/rules/code-quality.md +11 -0
- package/runtime/domains/coding/rules/code-review.md +11 -0
- package/runtime/domains/coding/skills/database-design-review/SKILL.md +24 -0
- package/runtime/domains/coding/skills/index-optimization/SKILL.md +24 -0
- package/runtime/domains/coding/skills/query-pattern-analysis/SKILL.md +23 -0
- package/runtime/domains/coding/skills/ui-doc-maintainer/SKILL.md +23 -0
- package/runtime/domains/coding/skills/user-flow-generation/SKILL.md +23 -0
- package/runtime/domains/coding/skills/ux-design-research/SKILL.md +22 -0
- package/runtime/domains/coding/skills/ux-heuristic-evaluation/SKILL.md +23 -0
- package/runtime/domains/coding/skills/ux-state-machine/SKILL.md +23 -0
- package/runtime/domains/coding/stacks/_shared/rules/api-design.md +10 -0
- package/runtime/domains/coding/stacks/_shared/rules/ci-cd.md +10 -0
- package/runtime/domains/coding/stacks/_shared/rules/environment.md +10 -0
- package/runtime/domains/coding/stacks/_shared/rules/git.md +10 -0
- package/runtime/domains/coding/stacks/_shared/skills/regression-test-gen/SKILL.md +23 -0
- package/runtime/domains/coding/stacks/flutter/mcp/dart-mcp-config.md +3 -0
- package/runtime/domains/coding/stacks/flutter/references/tools/easy-localization.md +15 -0
- package/runtime/domains/coding/stacks/flutter/references/tools/environment-loading.md +15 -0
- package/runtime/domains/coding/stacks/flutter/references/tools/flutter-quality-gate.md +24 -0
- package/runtime/domains/coding/stacks/flutter/references/tools/playwright.md +16 -0
- package/runtime/domains/coding/stacks/flutter/references/tools/security-review-checklist.md +18 -0
- package/runtime/domains/coding/stacks/flutter/references/tools-catalog.md +11 -0
- package/runtime/domains/coding/stacks/flutter/rules/architecture.md +7 -0
- package/runtime/domains/coding/stacks/flutter/rules/conventions/guide.md +4 -0
- package/runtime/domains/coding/stacks/flutter/rules/database/guide.md +4 -0
- package/runtime/domains/coding/stacks/flutter/rules/documentation/guide.md +4 -0
- package/runtime/domains/coding/stacks/flutter/rules/environment.md +7 -0
- package/runtime/domains/coding/stacks/flutter/rules/foundation/guide.md +4 -0
- package/runtime/domains/coding/stacks/flutter/rules/localization.md +7 -0
- package/runtime/domains/coding/stacks/flutter/rules/performance/guide.md +4 -0
- package/runtime/domains/coding/stacks/flutter/rules/security/guide.md +4 -0
- package/runtime/domains/coding/stacks/flutter/rules/testing/guide.md +4 -0
- package/runtime/domains/coding/stacks/flutter/rules/theming.md +7 -0
- package/runtime/domains/coding/stacks/flutter/rules/ui-platform.md +7 -0
- package/runtime/domains/coding/stacks/laravel/mcp/boost-mcp-config.md +3 -0
- package/runtime/domains/coding/stacks/laravel/references/tools/artisan.md +16 -0
- package/runtime/domains/coding/stacks/laravel/references/tools/boost.md +20 -0
- package/runtime/domains/coding/stacks/laravel/references/tools/code-quality.md +21 -0
- package/runtime/domains/coding/stacks/laravel/references/tools/playwright.md +16 -0
- package/runtime/domains/coding/stacks/laravel/references/tools/sail.md +19 -0
- package/runtime/domains/coding/stacks/laravel/references/tools/testing.md +22 -0
- package/runtime/domains/coding/stacks/laravel/references/tools-catalog.md +12 -0
- package/runtime/domains/coding/stacks/laravel/rules/api.md +7 -0
- package/runtime/domains/coding/stacks/laravel/rules/conventions/guide.md +4 -0
- package/runtime/domains/coding/stacks/laravel/rules/database/guide.md +4 -0
- package/runtime/domains/coding/stacks/laravel/rules/documentation/guide.md +4 -0
- package/runtime/domains/coding/stacks/laravel/rules/foundation/guide.md +4 -0
- package/runtime/domains/coding/stacks/laravel/rules/laravel.md +7 -0
- package/runtime/domains/coding/stacks/laravel/rules/localization.md +7 -0
- package/runtime/domains/coding/stacks/laravel/rules/modules.md +7 -0
- package/runtime/domains/coding/stacks/laravel/rules/performance/guide.md +4 -0
- package/runtime/domains/coding/stacks/laravel/rules/security/guide.md +4 -0
- package/runtime/domains/coding/stacks/laravel/rules/testing/guide.md +4 -0
- package/runtime/domains/coding/stacks/laravel/rules/ui-safety.md +7 -0
- package/runtime/hooks/block-destructive.sh +12 -0
- package/runtime/hooks/context-hit-tracker.sh +12 -0
- package/runtime/hooks/pre-commit-check.sh +18 -0
- package/runtime/hooks/pre-compact-preserve.sh +9 -0
- package/runtime/hooks/pre-migrate-check.sh +12 -0
- package/runtime/hooks/pre-write-check-spec.sh +10 -0
- package/runtime/hooks/skill-cache-check.mjs +45 -0
- package/runtime/hooks/skill-cache-check.sh +3 -0
- package/runtime/hooks/track-file-changes.sh +9 -0
- package/runtime/templates/adversarial-review-report.md.hbs +2 -0
- package/runtime/templates/agent-configs/agents.md.hbs +6 -0
- package/runtime/templates/agent-configs/claude.md.hbs +6 -0
- package/runtime/templates/agent-configs/gemini.md.hbs +6 -0
- package/runtime/templates/design-system/accessibility.md.hbs +1 -0
- package/runtime/templates/design-system/components.md.hbs +1 -0
- package/runtime/templates/design-system/motion.md.hbs +1 -0
- package/runtime/templates/design-system/patterns.md.hbs +1 -0
- package/runtime/templates/design-system/responsive.md.hbs +1 -0
- package/runtime/templates/design-system/tokens.md.hbs +1 -0
- package/runtime/templates/module-scaffold/api-endpoints.md.hbs +26 -0
- package/runtime/templates/module-scaffold/api-error-codes.md.hbs +8 -0
- package/runtime/templates/module-scaffold/api-schemas.md.hbs +17 -0
- package/runtime/templates/module-scaffold/components.md.hbs +1 -0
- package/runtime/templates/module-scaffold/data-volumes.md.hbs +1 -0
- package/runtime/templates/module-scaffold/error-catalog.md.hbs +29 -0
- package/runtime/templates/module-scaffold/indexes.md.hbs +1 -0
- package/runtime/templates/module-scaffold/integration-contracts.md.hbs +34 -0
- package/runtime/templates/module-scaffold/integration-events.md.hbs +37 -0
- package/runtime/templates/module-scaffold/queries.md.hbs +1 -0
- package/runtime/templates/module-scaffold/schema.md.hbs +1 -0
- package/runtime/templates/module-scaffold/screens.md.hbs +1 -0
- package/runtime/templates/module-scaffold/states.md.hbs +1 -0
- package/runtime/templates/module-scaffold/summary.md.hbs +1 -0
- package/runtime/templates/phase-handoff.md.hbs +2 -0
- package/runtime/templates/registry-file.md.hbs +1 -0
- package/runtime/templates/runner-scripts/analyze-context-hits.sh.hbs +14 -0
- package/runtime/templates/runner-scripts/extract-error-codes.sh.hbs +5 -0
- package/runtime/templates/runner-scripts/extract-events.sh.hbs +7 -0
- package/runtime/templates/runner-scripts/extract-routes.sh.hbs +3 -0
- package/runtime/templates/runner-scripts/health-check.sh.hbs +3 -0
- package/runtime/templates/runner-scripts/implement.sh.hbs +3 -0
- package/runtime/templates/runner-scripts/onboard-framework.sh.hbs +3 -0
- package/runtime/templates/runner-scripts/refresh-registry-diff.sh.hbs +11 -0
- package/runtime/templates/runner-scripts/refresh-registry.sh.hbs +3 -0
- package/runtime/templates/runner-scripts/spec-only.sh.hbs +3 -0
- package/runtime/templates/runner-scripts/update-framework.sh.hbs +3 -0
- package/runtime/templates/runner-scripts/verify.sh.hbs +8 -0
- package/runtime/templates/sequence-plan.md.hbs +2 -0
- package/runtime/templates/spec-document.md.hbs +8 -0
- package/runtime/templates/user-flow.md.hbs +8 -0
|
@@ -0,0 +1,2699 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/index.ts
|
|
4
|
+
import { realpathSync } from "fs";
|
|
5
|
+
import { pathToFileURL } from "url";
|
|
6
|
+
|
|
7
|
+
// src/cli/program.ts
|
|
8
|
+
import { Command as Command6 } from "commander";
|
|
9
|
+
|
|
10
|
+
// src/adapters/shared/base-adapter.ts
|
|
11
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
12
|
+
import { basename, join as join2 } from "pathe";
|
|
13
|
+
|
|
14
|
+
// src/core/runtime-paths.ts
|
|
15
|
+
import { dirname, join } from "path";
|
|
16
|
+
import { fileURLToPath } from "url";
|
|
17
|
+
var PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
18
|
+
function getRuntimeRoot() {
|
|
19
|
+
return join(PACKAGE_ROOT, "runtime");
|
|
20
|
+
}
|
|
21
|
+
function getRuntimeTemplatesRoot() {
|
|
22
|
+
return join(getRuntimeRoot(), "templates");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/mcp/server-registry.ts
|
|
26
|
+
var MCP_SERVERS = [
|
|
27
|
+
{
|
|
28
|
+
name: "laravel-boost",
|
|
29
|
+
stacks: ["laravel"],
|
|
30
|
+
capabilities: ["boost"],
|
|
31
|
+
provides: ["routes", "models", "services", "middleware", "config"],
|
|
32
|
+
replaces: ["route-scanning", "model-scanning", "service-scanning"]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "dart-mcp",
|
|
36
|
+
stacks: ["flutter"],
|
|
37
|
+
capabilities: [],
|
|
38
|
+
provides: ["widgets", "dependencies", "assets", "platforms"],
|
|
39
|
+
replaces: ["widget-scanning", "pubspec-parsing"]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "database-inspector",
|
|
43
|
+
stacks: ["laravel", "flutter"],
|
|
44
|
+
capabilities: [],
|
|
45
|
+
provides: ["schema", "indexes", "foreign-keys", "query-plans"],
|
|
46
|
+
replaces: ["migration-scanning"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "figma",
|
|
50
|
+
stacks: ["laravel", "flutter"],
|
|
51
|
+
capabilities: [],
|
|
52
|
+
provides: ["design-specs", "components", "layouts"],
|
|
53
|
+
replaces: ["web-search-design-research"]
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
function getServersForStack(stack, capabilities) {
|
|
57
|
+
return MCP_SERVERS.filter((server) => {
|
|
58
|
+
const stackMatch = server.stacks.includes(stack);
|
|
59
|
+
const capabilityMatch = server.capabilities.length === 0 || server.capabilities.some((capability) => capabilities.includes(capability));
|
|
60
|
+
return stackMatch && capabilityMatch;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
var McpServerRegistry = class {
|
|
64
|
+
list() {
|
|
65
|
+
return [...MCP_SERVERS];
|
|
66
|
+
}
|
|
67
|
+
forProfile(profile) {
|
|
68
|
+
const enabledNames = new Set(
|
|
69
|
+
profile.mcp.servers.filter((server) => server.enabled).map((server) => server.name)
|
|
70
|
+
);
|
|
71
|
+
return MCP_SERVERS.filter(
|
|
72
|
+
(server) => isStackSupported(server.stacks, profile.routing.stack) && isCapabilitySatisfied(server.capabilities, profile.routing.capabilities) && (enabledNames.has(server.name) || isDefaultServer(profile.routing.stack, server.name, profile.routing.capabilities))
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
function isStackSupported(stacks, stack) {
|
|
77
|
+
return stacks.includes(stack);
|
|
78
|
+
}
|
|
79
|
+
function isCapabilitySatisfied(required, capabilities) {
|
|
80
|
+
return required.length === 0 || required.every((capability) => capabilities.includes(capability));
|
|
81
|
+
}
|
|
82
|
+
function isDefaultServer(stack, name, capabilities) {
|
|
83
|
+
if (name === "database-inspector") {
|
|
84
|
+
return stack === "laravel" || stack === "flutter";
|
|
85
|
+
}
|
|
86
|
+
if (name === "laravel-boost") {
|
|
87
|
+
return stack === "laravel" && capabilities.includes("boost");
|
|
88
|
+
}
|
|
89
|
+
if (name === "dart-mcp") {
|
|
90
|
+
return stack === "flutter";
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/mcp/config-manager.ts
|
|
96
|
+
var ADAPTER_OUTPUT_PATHS = {
|
|
97
|
+
"claude-code": ".claude/settings.mcp.json",
|
|
98
|
+
"codex-cli": ".codex/mcp.json",
|
|
99
|
+
"gemini-cli": ".gemini/mcp.json"
|
|
100
|
+
};
|
|
101
|
+
var McpConfigManager = class {
|
|
102
|
+
registry = new McpServerRegistry();
|
|
103
|
+
generate(profile, adapter) {
|
|
104
|
+
const servers = this.registry.forProfile(profile);
|
|
105
|
+
return {
|
|
106
|
+
path: ADAPTER_OUTPUT_PATHS[adapter],
|
|
107
|
+
content: JSON.stringify(
|
|
108
|
+
{
|
|
109
|
+
mcpServers: Object.fromEntries(
|
|
110
|
+
servers.map((server) => [server.name, buildServerConfig(server, profile)])
|
|
111
|
+
)
|
|
112
|
+
},
|
|
113
|
+
null,
|
|
114
|
+
2
|
|
115
|
+
)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
function buildServerConfig(server, profile) {
|
|
120
|
+
const fromProfile = profile.mcp.servers.find((entry) => entry.name === server.name)?.config ?? {};
|
|
121
|
+
return {
|
|
122
|
+
enabled: true,
|
|
123
|
+
stack: profile.routing.stack,
|
|
124
|
+
provides: server.provides,
|
|
125
|
+
replaces: server.replaces,
|
|
126
|
+
...fromProfile
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/templates/engine.ts
|
|
131
|
+
import { readFile } from "fs/promises";
|
|
132
|
+
import Handlebars from "handlebars";
|
|
133
|
+
var TemplateEngine = class {
|
|
134
|
+
handlebars;
|
|
135
|
+
constructor() {
|
|
136
|
+
this.handlebars = Handlebars.create();
|
|
137
|
+
this.registerHelpers();
|
|
138
|
+
}
|
|
139
|
+
async render(templatePath, context) {
|
|
140
|
+
const templateSource = await readFile(templatePath, "utf8");
|
|
141
|
+
const template = this.handlebars.compile(templateSource);
|
|
142
|
+
return template(context);
|
|
143
|
+
}
|
|
144
|
+
registerHelpers() {
|
|
145
|
+
this.handlebars.registerHelper(
|
|
146
|
+
"if_eq",
|
|
147
|
+
function ifEq(left, right, options) {
|
|
148
|
+
return left === right ? options.fn(this) : options.inverse(this);
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
this.handlebars.registerHelper(
|
|
152
|
+
"join",
|
|
153
|
+
(value, separator = ", ") => Array.isArray(value) ? value.join(separator) : ""
|
|
154
|
+
);
|
|
155
|
+
this.handlebars.registerHelper("uppercase", (value) => value.toUpperCase());
|
|
156
|
+
this.handlebars.registerHelper("lowercase", (value) => value.toLowerCase());
|
|
157
|
+
this.handlebars.registerHelper(
|
|
158
|
+
"slugify",
|
|
159
|
+
(value) => value.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "")
|
|
160
|
+
);
|
|
161
|
+
this.handlebars.registerHelper("date", () => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// src/adapters/shared/base-adapter.ts
|
|
166
|
+
var BaseAdapter = class {
|
|
167
|
+
engine = new TemplateEngine();
|
|
168
|
+
async generateConfig(context) {
|
|
169
|
+
return [
|
|
170
|
+
{
|
|
171
|
+
path: this.configOutputPath(),
|
|
172
|
+
content: await this.engine.render(
|
|
173
|
+
join2(getRuntimeTemplatesRoot(), "agent-configs", this.configTemplateName()),
|
|
174
|
+
{
|
|
175
|
+
adapter: this.type,
|
|
176
|
+
frameworkPath: context.frameworkPath,
|
|
177
|
+
rulesPath: context.rulesPath
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
autoUpdate: true
|
|
181
|
+
}
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
async generateSkills(skills) {
|
|
185
|
+
return Promise.all(
|
|
186
|
+
skills.map(async (skill) => ({
|
|
187
|
+
path: join2(this.skillsRoot(), basename(skill.path)),
|
|
188
|
+
content: await readFile2(skill.path, "utf8"),
|
|
189
|
+
autoUpdate: true
|
|
190
|
+
}))
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
async generateAgents(agents) {
|
|
194
|
+
return Promise.all(
|
|
195
|
+
agents.map(async (agent) => ({
|
|
196
|
+
path: join2(this.agentsRoot(), basename(agent.path)),
|
|
197
|
+
content: await readFile2(agent.path, "utf8"),
|
|
198
|
+
autoUpdate: true
|
|
199
|
+
}))
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
async installHooks(hooks) {
|
|
203
|
+
return [
|
|
204
|
+
{
|
|
205
|
+
path: this.hooksOutputPath(),
|
|
206
|
+
content: JSON.stringify(
|
|
207
|
+
hooks.map((hook) => ({ source: hook.source, path: hook.path })),
|
|
208
|
+
null,
|
|
209
|
+
2
|
|
210
|
+
),
|
|
211
|
+
autoUpdate: true
|
|
212
|
+
}
|
|
213
|
+
];
|
|
214
|
+
}
|
|
215
|
+
async installMcp(mcpConfigs, profile) {
|
|
216
|
+
const config = new McpConfigManager().generate(profile, this.type);
|
|
217
|
+
return [
|
|
218
|
+
{
|
|
219
|
+
path: config.path,
|
|
220
|
+
content: JSON.stringify(
|
|
221
|
+
{
|
|
222
|
+
...JSON.parse(config.content),
|
|
223
|
+
resolved_artifacts: mcpConfigs.map((artifact) => artifact.source)
|
|
224
|
+
},
|
|
225
|
+
null,
|
|
226
|
+
2
|
|
227
|
+
),
|
|
228
|
+
autoUpdate: true
|
|
229
|
+
}
|
|
230
|
+
];
|
|
231
|
+
}
|
|
232
|
+
async configureCaching(profile) {
|
|
233
|
+
return [
|
|
234
|
+
{
|
|
235
|
+
path: this.cacheOutputPath(),
|
|
236
|
+
content: JSON.stringify(
|
|
237
|
+
{
|
|
238
|
+
enabled: profile.efficiency.skill_caching,
|
|
239
|
+
differential_refresh: profile.efficiency.differential_refresh
|
|
240
|
+
},
|
|
241
|
+
null,
|
|
242
|
+
2
|
|
243
|
+
),
|
|
244
|
+
autoUpdate: true
|
|
245
|
+
}
|
|
246
|
+
];
|
|
247
|
+
}
|
|
248
|
+
async configureMemory(profile) {
|
|
249
|
+
return [
|
|
250
|
+
{
|
|
251
|
+
path: this.memoryOutputPath(),
|
|
252
|
+
content: JSON.stringify(
|
|
253
|
+
{
|
|
254
|
+
context_hit_rate_target: profile.efficiency.context_hit_rate_target,
|
|
255
|
+
mcp_first: profile.efficiency.mcp_first
|
|
256
|
+
},
|
|
257
|
+
null,
|
|
258
|
+
2
|
|
259
|
+
),
|
|
260
|
+
autoUpdate: true
|
|
261
|
+
}
|
|
262
|
+
];
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// src/adapters/claude/claude-adapter.ts
|
|
267
|
+
var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
268
|
+
type = "claude-code";
|
|
269
|
+
configTemplateName() {
|
|
270
|
+
return "claude.md.hbs";
|
|
271
|
+
}
|
|
272
|
+
configOutputPath() {
|
|
273
|
+
return "CLAUDE.md";
|
|
274
|
+
}
|
|
275
|
+
skillsRoot() {
|
|
276
|
+
return ".claude/skills";
|
|
277
|
+
}
|
|
278
|
+
agentsRoot() {
|
|
279
|
+
return ".claude/agents";
|
|
280
|
+
}
|
|
281
|
+
hooksOutputPath() {
|
|
282
|
+
return ".claude/settings.hooks.json";
|
|
283
|
+
}
|
|
284
|
+
mcpOutputPath() {
|
|
285
|
+
return ".claude/settings.mcp.json";
|
|
286
|
+
}
|
|
287
|
+
cacheOutputPath() {
|
|
288
|
+
return ".claude/cache.json";
|
|
289
|
+
}
|
|
290
|
+
memoryOutputPath() {
|
|
291
|
+
return ".claude/memory.json";
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
// src/adapters/codex/codex-adapter.ts
|
|
296
|
+
var CodexCliAdapter = class extends BaseAdapter {
|
|
297
|
+
type = "codex-cli";
|
|
298
|
+
configTemplateName() {
|
|
299
|
+
return "agents.md.hbs";
|
|
300
|
+
}
|
|
301
|
+
configOutputPath() {
|
|
302
|
+
return "AGENTS.md";
|
|
303
|
+
}
|
|
304
|
+
skillsRoot() {
|
|
305
|
+
return ".codex/skills";
|
|
306
|
+
}
|
|
307
|
+
agentsRoot() {
|
|
308
|
+
return ".codex/agents";
|
|
309
|
+
}
|
|
310
|
+
hooksOutputPath() {
|
|
311
|
+
return ".codex/hooks.json";
|
|
312
|
+
}
|
|
313
|
+
mcpOutputPath() {
|
|
314
|
+
return ".codex/mcp.json";
|
|
315
|
+
}
|
|
316
|
+
cacheOutputPath() {
|
|
317
|
+
return ".codex/cache.json";
|
|
318
|
+
}
|
|
319
|
+
memoryOutputPath() {
|
|
320
|
+
return ".codex/memory.json";
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/adapters/gemini/gemini-adapter.ts
|
|
325
|
+
var GeminiCliAdapter = class extends BaseAdapter {
|
|
326
|
+
type = "gemini-cli";
|
|
327
|
+
configTemplateName() {
|
|
328
|
+
return "gemini.md.hbs";
|
|
329
|
+
}
|
|
330
|
+
configOutputPath() {
|
|
331
|
+
return "GEMINI.md";
|
|
332
|
+
}
|
|
333
|
+
skillsRoot() {
|
|
334
|
+
return ".gemini/skills";
|
|
335
|
+
}
|
|
336
|
+
agentsRoot() {
|
|
337
|
+
return ".gemini/agents";
|
|
338
|
+
}
|
|
339
|
+
hooksOutputPath() {
|
|
340
|
+
return ".gemini/hooks.json";
|
|
341
|
+
}
|
|
342
|
+
mcpOutputPath() {
|
|
343
|
+
return ".gemini/mcp.json";
|
|
344
|
+
}
|
|
345
|
+
cacheOutputPath() {
|
|
346
|
+
return ".gemini/cache.json";
|
|
347
|
+
}
|
|
348
|
+
memoryOutputPath() {
|
|
349
|
+
return ".gemini/memory.json";
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// src/adapters/factory.ts
|
|
354
|
+
var AdapterFactory = class {
|
|
355
|
+
static create(type) {
|
|
356
|
+
switch (type) {
|
|
357
|
+
case "claude-code":
|
|
358
|
+
return new ClaudeCodeAdapter();
|
|
359
|
+
case "codex-cli":
|
|
360
|
+
return new CodexCliAdapter();
|
|
361
|
+
case "gemini-cli":
|
|
362
|
+
return new GeminiCliAdapter();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// src/core/constants/paths.ts
|
|
368
|
+
var PATHS = {
|
|
369
|
+
AGENCY_DIR: ".agency",
|
|
370
|
+
PROJECT_PROFILE: ".agency/project-profile.yaml",
|
|
371
|
+
DETECTION_REPORT: ".agency/detection-report.json",
|
|
372
|
+
ONBOARDING_MANIFEST: ".agency/onboarding-manifest.json",
|
|
373
|
+
FRAMEWORK_VERSION: ".agency/framework-version.txt",
|
|
374
|
+
FRAMEWORK_PATH: ".agency/framework-path.txt",
|
|
375
|
+
GLOSSARY: ".agency/glossary.md",
|
|
376
|
+
HANDOFF: ".agency/session/handoff.md",
|
|
377
|
+
CHANGED_FILES: ".agency/session/changed-files.json",
|
|
378
|
+
CONTEXT_HIT_LOG: ".agency/session/context-hit-log.json",
|
|
379
|
+
SKILL_CACHE_DIR: ".agency/cache/skill-results",
|
|
380
|
+
INDEXES_DIR: ".agency/indexes",
|
|
381
|
+
DOCS_DIR: "docs",
|
|
382
|
+
RULES_DIR: "docs/rules",
|
|
383
|
+
ARCHITECTURE_DIR: "docs/architecture",
|
|
384
|
+
DESIGN_SYSTEM_DIR: "docs/design-system",
|
|
385
|
+
MODULES_DIR: "docs/modules",
|
|
386
|
+
BENCHMARKS_DIR: "docs/benchmarks",
|
|
387
|
+
TECH_DEBT_DIR: "docs/tech-debt",
|
|
388
|
+
MODULE_DB_DIR: "database",
|
|
389
|
+
MODULE_API_DIR: "api",
|
|
390
|
+
MODULE_INTEGRATION_DIR: "integration",
|
|
391
|
+
MODULE_UI_DIR: "ui",
|
|
392
|
+
MODULE_FEATURES_DIR: "features",
|
|
393
|
+
MODULE_USER_FLOWS_DIR: "user-flows",
|
|
394
|
+
MODULE_RESEARCH_DIR: "research",
|
|
395
|
+
MODULE_DECISIONS_DIR: "decisions",
|
|
396
|
+
MODULE_ERROR_CATALOG: "error-catalog.md",
|
|
397
|
+
CLAUDE_MD: "CLAUDE.md",
|
|
398
|
+
AGENTS_MD: "AGENTS.md",
|
|
399
|
+
GEMINI_MD: "GEMINI.md",
|
|
400
|
+
SCRIPTS_DIR: "scripts"
|
|
401
|
+
};
|
|
402
|
+
var REGISTRIES = [
|
|
403
|
+
"module-registry.md",
|
|
404
|
+
"feature-registry.md",
|
|
405
|
+
"model-registry.md",
|
|
406
|
+
"api-registry.md",
|
|
407
|
+
"job-event-registry.md",
|
|
408
|
+
"component-registry.md",
|
|
409
|
+
"screen-registry.md",
|
|
410
|
+
"table-registry.md",
|
|
411
|
+
"query-registry.md",
|
|
412
|
+
"test-registry.md",
|
|
413
|
+
"error-code-registry.md",
|
|
414
|
+
"integration-registry.md",
|
|
415
|
+
"reuse-catalog.md"
|
|
416
|
+
];
|
|
417
|
+
|
|
418
|
+
// src/detection/report.ts
|
|
419
|
+
function buildDetectionReport(input) {
|
|
420
|
+
return {
|
|
421
|
+
detected_domain: input.domain,
|
|
422
|
+
detected_stack: input.stack,
|
|
423
|
+
detected_capabilities: input.capabilities ?? [],
|
|
424
|
+
confidence: input.confidence,
|
|
425
|
+
signals: input.signals,
|
|
426
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/detection/signals/flutter.ts
|
|
431
|
+
import { existsSync, readFileSync } from "fs";
|
|
432
|
+
import { join as join3 } from "path";
|
|
433
|
+
import { parse } from "yaml";
|
|
434
|
+
function detectFlutterSignals(projectRoot) {
|
|
435
|
+
const pubspecPath = join3(projectRoot, "pubspec.yaml");
|
|
436
|
+
if (!existsSync(pubspecPath)) {
|
|
437
|
+
return [];
|
|
438
|
+
}
|
|
439
|
+
const pubspec = parse(readFileSync(pubspecPath, "utf8"));
|
|
440
|
+
if (pubspec.dependencies?.flutter === void 0) {
|
|
441
|
+
return [];
|
|
442
|
+
}
|
|
443
|
+
return [
|
|
444
|
+
{
|
|
445
|
+
signal: "pubspec.yaml declares flutter dependency",
|
|
446
|
+
file: "pubspec.yaml",
|
|
447
|
+
implies: "flutter",
|
|
448
|
+
confidence: "high"
|
|
449
|
+
}
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/detection/signals/laravel.ts
|
|
454
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
455
|
+
import { join as join4 } from "path";
|
|
456
|
+
function detectLaravelSignals(projectRoot) {
|
|
457
|
+
const signals = [];
|
|
458
|
+
const capabilities = /* @__PURE__ */ new Set();
|
|
459
|
+
const composerPath = join4(projectRoot, "composer.json");
|
|
460
|
+
const packageJsonPath = join4(projectRoot, "package.json");
|
|
461
|
+
if (existsSync2(join4(projectRoot, "artisan"))) {
|
|
462
|
+
signals.push(signal("artisan file exists", "artisan", "laravel", "high"));
|
|
463
|
+
}
|
|
464
|
+
if (existsSync2(composerPath)) {
|
|
465
|
+
const composer = readJsonFile(composerPath);
|
|
466
|
+
const dependencies = {
|
|
467
|
+
...composer.dependencies,
|
|
468
|
+
...composer.require
|
|
469
|
+
};
|
|
470
|
+
if (dependencies["laravel/framework"] !== void 0) {
|
|
471
|
+
signals.push(
|
|
472
|
+
signal("composer dependency laravel/framework", "composer.json", "laravel", "high")
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
if (dependencies["inertiajs/inertia-laravel"] !== void 0) {
|
|
476
|
+
capabilities.add("inertia");
|
|
477
|
+
signals.push(
|
|
478
|
+
signal("composer dependency inertiajs/inertia-laravel", "composer.json", "inertia", "high")
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
if (dependencies["laravel/boost"] !== void 0) {
|
|
482
|
+
capabilities.add("boost");
|
|
483
|
+
signals.push(signal("composer dependency laravel/boost", "composer.json", "boost", "high"));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if (existsSync2(join4(projectRoot, "app"))) {
|
|
487
|
+
signals.push(signal("app directory exists", "app", "laravel", "medium"));
|
|
488
|
+
}
|
|
489
|
+
if (existsSync2(join4(projectRoot, "routes"))) {
|
|
490
|
+
signals.push(signal("routes directory exists", "routes", "laravel", "medium"));
|
|
491
|
+
}
|
|
492
|
+
if (existsSync2(packageJsonPath)) {
|
|
493
|
+
const packageJson = readJsonFile(packageJsonPath);
|
|
494
|
+
const dependencies = {
|
|
495
|
+
...packageJson.dependencies,
|
|
496
|
+
...packageJson.devDependencies
|
|
497
|
+
};
|
|
498
|
+
if (dependencies.react !== void 0) {
|
|
499
|
+
capabilities.add("react");
|
|
500
|
+
signals.push(signal("package dependency react", "package.json", "react", "high"));
|
|
501
|
+
}
|
|
502
|
+
if (dependencies.vue !== void 0) {
|
|
503
|
+
capabilities.add("vue");
|
|
504
|
+
signals.push(signal("package dependency vue", "package.json", "vue", "high"));
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (existsSync2(join4(projectRoot, "resources", "js", "App.tsx"))) {
|
|
508
|
+
capabilities.add("react");
|
|
509
|
+
signals.push(signal("React entrypoint found", "resources/js/App.tsx", "react", "high"));
|
|
510
|
+
}
|
|
511
|
+
if (existsSync2(join4(projectRoot, "config", "boost.php"))) {
|
|
512
|
+
capabilities.add("boost");
|
|
513
|
+
signals.push(signal("boost config exists", "config/boost.php", "boost", "high"));
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
signals,
|
|
517
|
+
capabilities: Array.from(capabilities)
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
function readJsonFile(path) {
|
|
521
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
522
|
+
}
|
|
523
|
+
function signal(description, file, implies, confidence) {
|
|
524
|
+
return {
|
|
525
|
+
signal: description,
|
|
526
|
+
file,
|
|
527
|
+
implies,
|
|
528
|
+
confidence
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/detection/signals/short-video.ts
|
|
533
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
534
|
+
import { join as join5 } from "path";
|
|
535
|
+
import { parse as parse2 } from "yaml";
|
|
536
|
+
function detectShortVideoSignals(projectRoot) {
|
|
537
|
+
const profilePath = join5(projectRoot, ".agency", "project-profile.yaml");
|
|
538
|
+
const markerPath = join5(projectRoot, ".short-video-project");
|
|
539
|
+
const signals = [];
|
|
540
|
+
if (existsSync3(profilePath)) {
|
|
541
|
+
const profile = parse2(readFileSync3(profilePath, "utf8"));
|
|
542
|
+
if (profile.routing?.domain === "content" && profile.routing.stack === "short-video") {
|
|
543
|
+
signals.push({
|
|
544
|
+
signal: "project profile declares content/short-video",
|
|
545
|
+
file: ".agency/project-profile.yaml",
|
|
546
|
+
implies: "short-video",
|
|
547
|
+
confidence: "high"
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (existsSync3(markerPath)) {
|
|
552
|
+
signals.push({
|
|
553
|
+
signal: "short-video marker file exists",
|
|
554
|
+
file: ".short-video-project",
|
|
555
|
+
implies: "short-video",
|
|
556
|
+
confidence: "medium"
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
return signals;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// src/detection/detector.ts
|
|
563
|
+
var Detector = class {
|
|
564
|
+
async detect(projectRoot) {
|
|
565
|
+
const laravel = detectLaravelSignals(projectRoot);
|
|
566
|
+
const flutter = detectFlutterSignals(projectRoot);
|
|
567
|
+
const shortVideo = detectShortVideoSignals(projectRoot);
|
|
568
|
+
const scoredStacks = [
|
|
569
|
+
{
|
|
570
|
+
domain: "coding",
|
|
571
|
+
stack: "laravel",
|
|
572
|
+
score: laravel.signals.filter((signal2) => signal2.implies === "laravel").length,
|
|
573
|
+
capabilities: laravel.capabilities,
|
|
574
|
+
signals: laravel.signals
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
domain: "coding",
|
|
578
|
+
stack: "flutter",
|
|
579
|
+
score: flutter.length,
|
|
580
|
+
capabilities: [],
|
|
581
|
+
signals: flutter
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
domain: "content",
|
|
585
|
+
stack: "short-video",
|
|
586
|
+
score: shortVideo.length,
|
|
587
|
+
capabilities: [],
|
|
588
|
+
signals: shortVideo
|
|
589
|
+
}
|
|
590
|
+
].filter((candidate) => candidate.score > 0);
|
|
591
|
+
if (scoredStacks.length === 0) {
|
|
592
|
+
return buildDetectionReport({
|
|
593
|
+
domain: null,
|
|
594
|
+
stack: null,
|
|
595
|
+
signals: [],
|
|
596
|
+
confidence: "low"
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
const topScore = Math.max(...scoredStacks.map((candidate) => candidate.score));
|
|
600
|
+
const winners = scoredStacks.filter((candidate) => candidate.score === topScore);
|
|
601
|
+
if (winners.length > 1) {
|
|
602
|
+
return buildDetectionReport({
|
|
603
|
+
domain: null,
|
|
604
|
+
stack: null,
|
|
605
|
+
signals: winners.flatMap((winner2) => winner2.signals),
|
|
606
|
+
confidence: "low"
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
const winner = winners[0];
|
|
610
|
+
const confidence = winner.score >= 3 ? "high" : winner.score >= 2 ? "medium" : "low";
|
|
611
|
+
return buildDetectionReport({
|
|
612
|
+
domain: winner.domain,
|
|
613
|
+
stack: winner.stack,
|
|
614
|
+
capabilities: winner.capabilities,
|
|
615
|
+
signals: winner.signals,
|
|
616
|
+
confidence
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
// src/health/checker.ts
|
|
622
|
+
import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync4, statSync } from "fs";
|
|
623
|
+
import { access } from "fs/promises";
|
|
624
|
+
import { constants as fsConstants } from "fs";
|
|
625
|
+
import { join as join6 } from "path";
|
|
626
|
+
import YAML from "yaml";
|
|
627
|
+
|
|
628
|
+
// src/validators/validator.ts
|
|
629
|
+
import Ajv from "ajv";
|
|
630
|
+
|
|
631
|
+
// src/validators/schemas/adversarial-review-report.schema.json
|
|
632
|
+
var adversarial_review_report_schema_default = {
|
|
633
|
+
$id: "adversarial-review-report",
|
|
634
|
+
type: "object",
|
|
635
|
+
additionalProperties: false,
|
|
636
|
+
required: [
|
|
637
|
+
"point",
|
|
638
|
+
"tier",
|
|
639
|
+
"mode",
|
|
640
|
+
"verdict",
|
|
641
|
+
"summary",
|
|
642
|
+
"findings",
|
|
643
|
+
"dimensions_passed_clean",
|
|
644
|
+
"dimensions_deferred"
|
|
645
|
+
],
|
|
646
|
+
properties: {
|
|
647
|
+
point: { type: "string", enum: ["after-spec", "after-implementation"] },
|
|
648
|
+
tier: { type: "string", enum: ["full", "standard", "spot-check"] },
|
|
649
|
+
mode: { type: "string", enum: ["fresh", "diff"] },
|
|
650
|
+
verdict: { type: "string", enum: ["pass", "fail"] },
|
|
651
|
+
summary: { type: "string" },
|
|
652
|
+
findings: {
|
|
653
|
+
type: "array",
|
|
654
|
+
items: {
|
|
655
|
+
type: "object",
|
|
656
|
+
additionalProperties: false,
|
|
657
|
+
required: ["id", "dimension", "severity", "finding", "impact", "required_action"],
|
|
658
|
+
properties: {
|
|
659
|
+
id: { type: "string" },
|
|
660
|
+
dimension: { type: "string" },
|
|
661
|
+
severity: { type: "string", enum: ["critical", "high", "medium", "low"] },
|
|
662
|
+
finding: { type: "string" },
|
|
663
|
+
impact: { type: "string" },
|
|
664
|
+
required_action: { type: "string" }
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
dimensions_passed_clean: { type: "array", items: { type: "string" } },
|
|
669
|
+
dimensions_deferred: { type: "array", items: { type: "string" } }
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
// src/validators/schemas/api-endpoint-doc.schema.json
|
|
674
|
+
var api_endpoint_doc_schema_default = {
|
|
675
|
+
$id: "api-endpoint-doc",
|
|
676
|
+
type: "object",
|
|
677
|
+
additionalProperties: false,
|
|
678
|
+
required: [
|
|
679
|
+
"method",
|
|
680
|
+
"route",
|
|
681
|
+
"auth",
|
|
682
|
+
"description",
|
|
683
|
+
"request_schema_ref",
|
|
684
|
+
"response_schema_ref"
|
|
685
|
+
],
|
|
686
|
+
properties: {
|
|
687
|
+
method: { type: "string", enum: ["GET", "POST", "PUT", "PATCH", "DELETE"] },
|
|
688
|
+
route: { type: "string", pattern: "^/" },
|
|
689
|
+
auth: { type: "string", enum: ["required", "public"] },
|
|
690
|
+
permissions: { type: "array", items: { type: "string" } },
|
|
691
|
+
rate_limit: { type: "string" },
|
|
692
|
+
description: { type: "string", minLength: 1 },
|
|
693
|
+
request_schema_ref: { type: "string" },
|
|
694
|
+
response_schema_ref: { type: "string" },
|
|
695
|
+
error_codes_ref: { type: "string" },
|
|
696
|
+
added_in: { type: "string" },
|
|
697
|
+
last_updated: { type: "string" }
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
// src/validators/schemas/context-hit-log.schema.json
|
|
702
|
+
var context_hit_log_schema_default = {
|
|
703
|
+
$id: "context-hit-log",
|
|
704
|
+
type: "object",
|
|
705
|
+
additionalProperties: false,
|
|
706
|
+
required: [
|
|
707
|
+
"session_id",
|
|
708
|
+
"phase",
|
|
709
|
+
"files_loaded",
|
|
710
|
+
"files_referenced",
|
|
711
|
+
"hit_rate",
|
|
712
|
+
"unreferenced_files",
|
|
713
|
+
"timestamp"
|
|
714
|
+
],
|
|
715
|
+
properties: {
|
|
716
|
+
session_id: { type: "string" },
|
|
717
|
+
phase: { type: "string" },
|
|
718
|
+
story: { type: "string" },
|
|
719
|
+
files_loaded: { type: "integer", minimum: 0 },
|
|
720
|
+
files_referenced: { type: "integer", minimum: 0 },
|
|
721
|
+
hit_rate: { type: "number", minimum: 0, maximum: 1 },
|
|
722
|
+
unreferenced_files: { type: "array", items: { type: "string" } },
|
|
723
|
+
timestamp: { type: "string", format: "date-time" }
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
// src/validators/schemas/detection-report.schema.json
|
|
728
|
+
var detection_report_schema_default = {
|
|
729
|
+
$id: "detection-report",
|
|
730
|
+
type: "object",
|
|
731
|
+
additionalProperties: false,
|
|
732
|
+
required: [
|
|
733
|
+
"detected_domain",
|
|
734
|
+
"detected_stack",
|
|
735
|
+
"detected_capabilities",
|
|
736
|
+
"confidence",
|
|
737
|
+
"signals",
|
|
738
|
+
"timestamp"
|
|
739
|
+
],
|
|
740
|
+
properties: {
|
|
741
|
+
detected_domain: { type: ["string", "null"], enum: ["coding", "content", null] },
|
|
742
|
+
detected_stack: {
|
|
743
|
+
type: ["string", "null"],
|
|
744
|
+
enum: ["laravel", "flutter", "short-video", null]
|
|
745
|
+
},
|
|
746
|
+
detected_capabilities: {
|
|
747
|
+
type: "array",
|
|
748
|
+
items: { type: "string", enum: ["inertia", "vue", "react", "boost"] }
|
|
749
|
+
},
|
|
750
|
+
confidence: { type: "string", enum: ["high", "medium", "low"] },
|
|
751
|
+
signals: {
|
|
752
|
+
type: "array",
|
|
753
|
+
items: {
|
|
754
|
+
type: "object",
|
|
755
|
+
additionalProperties: false,
|
|
756
|
+
required: ["signal", "file", "implies", "confidence"],
|
|
757
|
+
properties: {
|
|
758
|
+
signal: { type: "string" },
|
|
759
|
+
file: { type: "string" },
|
|
760
|
+
implies: { type: "string" },
|
|
761
|
+
confidence: { type: "string", enum: ["high", "medium", "low"] }
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
timestamp: { type: "string" }
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
// src/validators/schemas/error-catalog.schema.json
|
|
770
|
+
var error_catalog_schema_default = {
|
|
771
|
+
$id: "error-catalog",
|
|
772
|
+
type: "object",
|
|
773
|
+
additionalProperties: false,
|
|
774
|
+
required: ["code", "user_message", "trigger", "recovery_path", "retry_safe"],
|
|
775
|
+
properties: {
|
|
776
|
+
code: { type: "string", pattern: "^[A-Z]{2,5}-[0-9]{3}$" },
|
|
777
|
+
http_status: { type: "integer", minimum: 400, maximum: 599 },
|
|
778
|
+
user_message: { type: "string", minLength: 1 },
|
|
779
|
+
internal_message: { type: "string" },
|
|
780
|
+
trigger: { type: "string", minLength: 1 },
|
|
781
|
+
recovery_path: { type: "string", minLength: 1 },
|
|
782
|
+
retry_safe: { type: "boolean" },
|
|
783
|
+
logged: { type: "boolean" },
|
|
784
|
+
alerted: { type: "boolean" },
|
|
785
|
+
added_in: { type: "string" },
|
|
786
|
+
last_updated: { type: "string" }
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
// src/validators/schemas/gate-result.schema.json
|
|
791
|
+
var gate_result_schema_default = {
|
|
792
|
+
$id: "gate-result",
|
|
793
|
+
type: "object",
|
|
794
|
+
additionalProperties: false,
|
|
795
|
+
required: ["gate", "passed", "detail"],
|
|
796
|
+
properties: {
|
|
797
|
+
gate: { type: "string" },
|
|
798
|
+
passed: { type: "boolean" },
|
|
799
|
+
detail: { type: "string" },
|
|
800
|
+
remediation: { type: "string" }
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
// src/validators/schemas/handoff-artifact.schema.json
|
|
805
|
+
var handoff_artifact_schema_default = {
|
|
806
|
+
$id: "handoff-artifact",
|
|
807
|
+
type: "object",
|
|
808
|
+
additionalProperties: false,
|
|
809
|
+
required: [
|
|
810
|
+
"framework_version",
|
|
811
|
+
"current_phase",
|
|
812
|
+
"current_story",
|
|
813
|
+
"completed_stories",
|
|
814
|
+
"key_decisions",
|
|
815
|
+
"verification_results",
|
|
816
|
+
"changed_files",
|
|
817
|
+
"context_hit_rate",
|
|
818
|
+
"warnings",
|
|
819
|
+
"unresolved_items",
|
|
820
|
+
"references"
|
|
821
|
+
],
|
|
822
|
+
properties: {
|
|
823
|
+
framework_version: { type: "string" },
|
|
824
|
+
current_phase: { type: "string" },
|
|
825
|
+
current_story: {
|
|
826
|
+
anyOf: [
|
|
827
|
+
{ type: "null" },
|
|
828
|
+
{
|
|
829
|
+
type: "object",
|
|
830
|
+
additionalProperties: false,
|
|
831
|
+
required: ["id", "title"],
|
|
832
|
+
properties: { id: { type: "string" }, title: { type: "string" } }
|
|
833
|
+
}
|
|
834
|
+
]
|
|
835
|
+
},
|
|
836
|
+
completed_stories: {
|
|
837
|
+
type: "array",
|
|
838
|
+
items: {
|
|
839
|
+
type: "object",
|
|
840
|
+
additionalProperties: false,
|
|
841
|
+
required: ["id", "title", "verification_status"],
|
|
842
|
+
properties: {
|
|
843
|
+
id: { type: "string" },
|
|
844
|
+
title: { type: "string" },
|
|
845
|
+
verification_status: { type: "string", enum: ["passed", "failed", "partial"] }
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
},
|
|
849
|
+
key_decisions: { type: "array", items: { type: "string" } },
|
|
850
|
+
verification_results: { type: "array", items: { $ref: "gate-result" } },
|
|
851
|
+
changed_files: { type: "array", items: { type: "string" } },
|
|
852
|
+
context_hit_rate: { type: "number" },
|
|
853
|
+
warnings: { type: "array", items: { type: "string" } },
|
|
854
|
+
unresolved_items: { type: "array", items: { type: "string" } },
|
|
855
|
+
references: {
|
|
856
|
+
type: "object",
|
|
857
|
+
additionalProperties: false,
|
|
858
|
+
required: ["spec", "flow", "review_report"],
|
|
859
|
+
properties: {
|
|
860
|
+
spec: { type: "string" },
|
|
861
|
+
flow: { type: "string" },
|
|
862
|
+
review_report: { type: "string" }
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
// src/validators/schemas/integration-doc.schema.json
|
|
869
|
+
var integration_doc_schema_default = {
|
|
870
|
+
$id: "integration-doc",
|
|
871
|
+
type: "object",
|
|
872
|
+
additionalProperties: false,
|
|
873
|
+
required: ["event_class", "published_by", "subscribers", "async"],
|
|
874
|
+
properties: {
|
|
875
|
+
event_class: { type: "string", minLength: 1 },
|
|
876
|
+
published_by: { type: "string", minLength: 1 },
|
|
877
|
+
payload_fields: {
|
|
878
|
+
type: "array",
|
|
879
|
+
items: {
|
|
880
|
+
type: "object",
|
|
881
|
+
additionalProperties: false,
|
|
882
|
+
required: ["name", "type", "description"],
|
|
883
|
+
properties: {
|
|
884
|
+
name: { type: "string" },
|
|
885
|
+
type: { type: "string" },
|
|
886
|
+
description: { type: "string" }
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
},
|
|
890
|
+
subscribers: { type: "array", items: { type: "string" } },
|
|
891
|
+
async: { type: "boolean" },
|
|
892
|
+
added_in: { type: "string" }
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
// src/validators/schemas/onboarding-manifest.schema.json
|
|
897
|
+
var onboarding_manifest_schema_default = {
|
|
898
|
+
$id: "onboarding-manifest",
|
|
899
|
+
type: "object",
|
|
900
|
+
additionalProperties: false,
|
|
901
|
+
required: [
|
|
902
|
+
"framework_version",
|
|
903
|
+
"adapter",
|
|
904
|
+
"project_root",
|
|
905
|
+
"profile",
|
|
906
|
+
"detected",
|
|
907
|
+
"generated_at",
|
|
908
|
+
"generated_artifacts"
|
|
909
|
+
],
|
|
910
|
+
properties: {
|
|
911
|
+
framework_version: { type: "string" },
|
|
912
|
+
adapter: { type: "string", enum: ["claude-code", "codex-cli", "gemini-cli"] },
|
|
913
|
+
project_root: { type: "string" },
|
|
914
|
+
profile: { $ref: "project-profile" },
|
|
915
|
+
detected: { anyOf: [{ $ref: "detection-report" }, { type: "null" }] },
|
|
916
|
+
generated_at: { type: "string" },
|
|
917
|
+
generated_artifacts: {
|
|
918
|
+
type: "array",
|
|
919
|
+
items: {
|
|
920
|
+
type: "object",
|
|
921
|
+
additionalProperties: false,
|
|
922
|
+
required: ["path", "auto_update"],
|
|
923
|
+
properties: {
|
|
924
|
+
path: { type: "string" },
|
|
925
|
+
auto_update: { type: "boolean" },
|
|
926
|
+
executable: { type: "boolean" }
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
// src/validators/schemas/project-profile.schema.json
|
|
934
|
+
var project_profile_schema_default = {
|
|
935
|
+
$id: "project-profile",
|
|
936
|
+
type: "object",
|
|
937
|
+
additionalProperties: false,
|
|
938
|
+
required: [
|
|
939
|
+
"project",
|
|
940
|
+
"routing",
|
|
941
|
+
"commands",
|
|
942
|
+
"strictness",
|
|
943
|
+
"compliance_packs",
|
|
944
|
+
"features",
|
|
945
|
+
"mcp",
|
|
946
|
+
"model_routing",
|
|
947
|
+
"research",
|
|
948
|
+
"efficiency",
|
|
949
|
+
"escalation",
|
|
950
|
+
"custom"
|
|
951
|
+
],
|
|
952
|
+
properties: {
|
|
953
|
+
project: {
|
|
954
|
+
type: "object",
|
|
955
|
+
additionalProperties: false,
|
|
956
|
+
required: ["name", "id", "description"],
|
|
957
|
+
properties: {
|
|
958
|
+
name: { type: "string" },
|
|
959
|
+
id: { type: "string" },
|
|
960
|
+
description: { type: "string" }
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
routing: {
|
|
964
|
+
type: "object",
|
|
965
|
+
additionalProperties: false,
|
|
966
|
+
required: ["domain", "stack", "capabilities"],
|
|
967
|
+
properties: {
|
|
968
|
+
domain: { type: "string", enum: ["coding", "content"] },
|
|
969
|
+
stack: { type: "string", enum: ["laravel", "flutter", "short-video"] },
|
|
970
|
+
capabilities: {
|
|
971
|
+
type: "array",
|
|
972
|
+
items: { type: "string", enum: ["inertia", "vue", "react", "boost"] }
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
},
|
|
976
|
+
commands: {
|
|
977
|
+
type: "object",
|
|
978
|
+
additionalProperties: false,
|
|
979
|
+
required: ["install", "dev", "test", "test_single", "lint", "format", "migrate", "build"],
|
|
980
|
+
properties: {
|
|
981
|
+
install: { type: "string" },
|
|
982
|
+
dev: { type: "string" },
|
|
983
|
+
test: { type: "string" },
|
|
984
|
+
test_single: { type: "string" },
|
|
985
|
+
lint: { type: "string" },
|
|
986
|
+
format: { type: "string" },
|
|
987
|
+
migrate: { type: "string" },
|
|
988
|
+
build: { type: "string" }
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
strictness: {
|
|
992
|
+
type: "object",
|
|
993
|
+
additionalProperties: false,
|
|
994
|
+
required: [
|
|
995
|
+
"full_lane_default",
|
|
996
|
+
"require_adversarial_review",
|
|
997
|
+
"block_on_stale_docs",
|
|
998
|
+
"require_db_review_for_migrations"
|
|
999
|
+
],
|
|
1000
|
+
properties: {
|
|
1001
|
+
full_lane_default: { type: "boolean" },
|
|
1002
|
+
require_adversarial_review: { type: "boolean" },
|
|
1003
|
+
block_on_stale_docs: { type: "boolean" },
|
|
1004
|
+
require_db_review_for_migrations: { type: "boolean" }
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
compliance_packs: {
|
|
1008
|
+
type: "array",
|
|
1009
|
+
items: {
|
|
1010
|
+
type: "object",
|
|
1011
|
+
additionalProperties: false,
|
|
1012
|
+
required: ["name", "enabled"],
|
|
1013
|
+
properties: {
|
|
1014
|
+
name: { type: "string" },
|
|
1015
|
+
enabled: { type: "boolean" }
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
features: {
|
|
1020
|
+
type: "object",
|
|
1021
|
+
additionalProperties: false,
|
|
1022
|
+
required: [
|
|
1023
|
+
"spec_only_mode",
|
|
1024
|
+
"market_research",
|
|
1025
|
+
"design_research",
|
|
1026
|
+
"team_agents",
|
|
1027
|
+
"supply_chain_governance",
|
|
1028
|
+
"ai_governance"
|
|
1029
|
+
],
|
|
1030
|
+
properties: {
|
|
1031
|
+
spec_only_mode: { type: "boolean" },
|
|
1032
|
+
market_research: { type: "boolean" },
|
|
1033
|
+
design_research: { type: "boolean" },
|
|
1034
|
+
team_agents: { type: "boolean" },
|
|
1035
|
+
supply_chain_governance: { type: "boolean" },
|
|
1036
|
+
ai_governance: { type: "boolean" }
|
|
1037
|
+
}
|
|
1038
|
+
},
|
|
1039
|
+
mcp: {
|
|
1040
|
+
type: "object",
|
|
1041
|
+
additionalProperties: false,
|
|
1042
|
+
required: ["servers"],
|
|
1043
|
+
properties: {
|
|
1044
|
+
servers: {
|
|
1045
|
+
type: "array",
|
|
1046
|
+
items: {
|
|
1047
|
+
type: "object",
|
|
1048
|
+
additionalProperties: false,
|
|
1049
|
+
required: ["name", "enabled"],
|
|
1050
|
+
properties: {
|
|
1051
|
+
name: { type: "string" },
|
|
1052
|
+
enabled: { type: "boolean" },
|
|
1053
|
+
config: { type: "object" }
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
model_routing: {
|
|
1060
|
+
type: "object",
|
|
1061
|
+
additionalProperties: false,
|
|
1062
|
+
required: ["default_model", "reasoning_model", "fast_model"],
|
|
1063
|
+
properties: {
|
|
1064
|
+
default_model: { type: "string" },
|
|
1065
|
+
reasoning_model: { type: "string" },
|
|
1066
|
+
fast_model: { type: "string" }
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
research: {
|
|
1070
|
+
type: "object",
|
|
1071
|
+
additionalProperties: false,
|
|
1072
|
+
required: ["depth"],
|
|
1073
|
+
properties: {
|
|
1074
|
+
depth: { type: "string", enum: ["cutting-edge", "standard", "conservative"] }
|
|
1075
|
+
}
|
|
1076
|
+
},
|
|
1077
|
+
efficiency: {
|
|
1078
|
+
type: "object",
|
|
1079
|
+
additionalProperties: false,
|
|
1080
|
+
properties: {
|
|
1081
|
+
context_hit_rate_target: { type: "number" },
|
|
1082
|
+
skill_caching: { type: "boolean" },
|
|
1083
|
+
differential_refresh: { type: "boolean" },
|
|
1084
|
+
mcp_first: { type: "boolean" }
|
|
1085
|
+
}
|
|
1086
|
+
},
|
|
1087
|
+
escalation: {
|
|
1088
|
+
type: "object",
|
|
1089
|
+
additionalProperties: false,
|
|
1090
|
+
required: [
|
|
1091
|
+
"destructive_operations",
|
|
1092
|
+
"risky_migrations",
|
|
1093
|
+
"security_findings",
|
|
1094
|
+
"db_row_threshold"
|
|
1095
|
+
],
|
|
1096
|
+
properties: {
|
|
1097
|
+
destructive_operations: {
|
|
1098
|
+
type: "string",
|
|
1099
|
+
enum: ["block", "require_approval", "warn"]
|
|
1100
|
+
},
|
|
1101
|
+
risky_migrations: { type: "string", enum: ["block", "require_approval", "warn"] },
|
|
1102
|
+
security_findings: { type: "string", enum: ["block", "require_approval", "warn"] },
|
|
1103
|
+
db_row_threshold: { type: "integer" }
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
custom: {
|
|
1107
|
+
type: "object",
|
|
1108
|
+
additionalProperties: false,
|
|
1109
|
+
required: ["classification_dimensions", "verification_plugins", "escalation_rules"],
|
|
1110
|
+
properties: {
|
|
1111
|
+
classification_dimensions: { type: "array", items: { type: "object" } },
|
|
1112
|
+
verification_plugins: { type: "array", items: { type: "object" } },
|
|
1113
|
+
escalation_rules: { type: "array", items: { type: "object" } }
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
// src/validators/schemas/skill-frontmatter.schema.json
|
|
1120
|
+
var skill_frontmatter_schema_default = {
|
|
1121
|
+
$id: "skill-frontmatter",
|
|
1122
|
+
type: "object",
|
|
1123
|
+
additionalProperties: false,
|
|
1124
|
+
required: ["name", "description", "model_tier"],
|
|
1125
|
+
properties: {
|
|
1126
|
+
name: { type: "string", minLength: 1 },
|
|
1127
|
+
description: { type: "string", minLength: 1 },
|
|
1128
|
+
model_tier: { type: "string", enum: ["fast", "medium", "deep"] },
|
|
1129
|
+
triggers: { type: "array" },
|
|
1130
|
+
max_lines: { type: "integer", maximum: 300 },
|
|
1131
|
+
cacheable: { type: "boolean" },
|
|
1132
|
+
cache_key_inputs: { type: "array", items: { type: "string" } }
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
// src/validators/validator.ts
|
|
1137
|
+
var SCHEMAS = [
|
|
1138
|
+
project_profile_schema_default,
|
|
1139
|
+
detection_report_schema_default,
|
|
1140
|
+
onboarding_manifest_schema_default,
|
|
1141
|
+
handoff_artifact_schema_default,
|
|
1142
|
+
adversarial_review_report_schema_default,
|
|
1143
|
+
context_hit_log_schema_default,
|
|
1144
|
+
api_endpoint_doc_schema_default,
|
|
1145
|
+
integration_doc_schema_default,
|
|
1146
|
+
error_catalog_schema_default,
|
|
1147
|
+
skill_frontmatter_schema_default,
|
|
1148
|
+
gate_result_schema_default
|
|
1149
|
+
];
|
|
1150
|
+
var SchemaValidator = class {
|
|
1151
|
+
ajv;
|
|
1152
|
+
constructor() {
|
|
1153
|
+
this.ajv = new Ajv({ allErrors: true, verbose: true });
|
|
1154
|
+
this.ajv.addFormat(
|
|
1155
|
+
"date-time",
|
|
1156
|
+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/
|
|
1157
|
+
);
|
|
1158
|
+
this.registerSchemas();
|
|
1159
|
+
}
|
|
1160
|
+
validate(schemaId, data) {
|
|
1161
|
+
const valid = this.ajv.validate(schemaId, data);
|
|
1162
|
+
return {
|
|
1163
|
+
valid,
|
|
1164
|
+
errors: valid ? [] : this.formatErrors(this.ajv.errors)
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
registerSchemas() {
|
|
1168
|
+
for (const schema of SCHEMAS) {
|
|
1169
|
+
this.ajv.addSchema(schema);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
formatErrors(errors) {
|
|
1173
|
+
if (errors === null || errors === void 0) {
|
|
1174
|
+
return [];
|
|
1175
|
+
}
|
|
1176
|
+
return errors.map((error) => ({
|
|
1177
|
+
path: error.instancePath || "/",
|
|
1178
|
+
message: error.message ?? "Validation failed",
|
|
1179
|
+
expected: error.schemaPath,
|
|
1180
|
+
actual: error.data === void 0 ? void 0 : JSON.stringify(error.data)
|
|
1181
|
+
}));
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1185
|
+
// src/health/checker.ts
|
|
1186
|
+
var STALENESS_WINDOW_MS = 1e3 * 60 * 60 * 24 * 7;
|
|
1187
|
+
var HealthChecker = class {
|
|
1188
|
+
validator = new SchemaValidator();
|
|
1189
|
+
async run(projectRoot) {
|
|
1190
|
+
const profile = this.readProfile(projectRoot);
|
|
1191
|
+
const modules = this.detectModules(projectRoot);
|
|
1192
|
+
const checks = [
|
|
1193
|
+
this.checkFrameworkArtifacts(projectRoot),
|
|
1194
|
+
this.checkProfile(profile),
|
|
1195
|
+
this.checkDocsScaffold(projectRoot),
|
|
1196
|
+
this.checkIndexesCurrent(projectRoot),
|
|
1197
|
+
await this.checkScripts(projectRoot),
|
|
1198
|
+
this.checkHooks(projectRoot),
|
|
1199
|
+
this.checkAdapterConfig(projectRoot),
|
|
1200
|
+
this.checkStackCommands(profile),
|
|
1201
|
+
this.checkStableFrameworkPaths(projectRoot),
|
|
1202
|
+
this.checkBrokenScaffold(projectRoot),
|
|
1203
|
+
this.checkUiDocs(projectRoot, modules),
|
|
1204
|
+
this.checkApiDocs(projectRoot, modules),
|
|
1205
|
+
this.checkIntegrationDocs(projectRoot, modules),
|
|
1206
|
+
this.checkErrorCatalog(projectRoot, modules),
|
|
1207
|
+
this.checkMcp(projectRoot, profile),
|
|
1208
|
+
this.checkSkillCache(projectRoot),
|
|
1209
|
+
this.checkContextHitRate(projectRoot, profile)
|
|
1210
|
+
];
|
|
1211
|
+
const overallStatus = deriveOverallStatus(checks);
|
|
1212
|
+
return {
|
|
1213
|
+
overall_status: overallStatus,
|
|
1214
|
+
checks,
|
|
1215
|
+
efficiency: this.buildEfficiencySummary(projectRoot, profile)
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
readProfile(projectRoot) {
|
|
1219
|
+
const path = join6(projectRoot, PATHS.PROJECT_PROFILE);
|
|
1220
|
+
if (!existsSync4(path)) {
|
|
1221
|
+
return null;
|
|
1222
|
+
}
|
|
1223
|
+
try {
|
|
1224
|
+
return YAML.parse(readFileSync4(path, "utf8"));
|
|
1225
|
+
} catch {
|
|
1226
|
+
return null;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
detectModules(projectRoot) {
|
|
1230
|
+
const modulesRoot = join6(projectRoot, "docs/modules");
|
|
1231
|
+
if (!existsSync4(modulesRoot)) {
|
|
1232
|
+
return [];
|
|
1233
|
+
}
|
|
1234
|
+
return readdirSync(modulesRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
1235
|
+
}
|
|
1236
|
+
checkFrameworkArtifacts(projectRoot) {
|
|
1237
|
+
const required = [
|
|
1238
|
+
PATHS.PROJECT_PROFILE,
|
|
1239
|
+
PATHS.FRAMEWORK_VERSION,
|
|
1240
|
+
PATHS.FRAMEWORK_PATH,
|
|
1241
|
+
"scripts",
|
|
1242
|
+
"docs/modules/core"
|
|
1243
|
+
];
|
|
1244
|
+
const missing = required.filter((relative6) => !existsSync4(join6(projectRoot, relative6)));
|
|
1245
|
+
return missing.length === 0 ? pass("Framework artifacts exist", "Framework artifacts are present") : fail(
|
|
1246
|
+
"Framework artifacts exist",
|
|
1247
|
+
`Missing framework artifacts: ${missing.join(", ")}`,
|
|
1248
|
+
"Re-run onboarding to regenerate the missing framework artifacts."
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
checkProfile(profile) {
|
|
1252
|
+
if (profile === null) {
|
|
1253
|
+
return fail(
|
|
1254
|
+
"Profile is valid",
|
|
1255
|
+
"Project profile is missing or unreadable",
|
|
1256
|
+
"Regenerate the project profile."
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
const validation = this.validator.validate("project-profile", profile);
|
|
1260
|
+
return validation.valid ? pass("Profile is valid", "Project profile schema is valid") : fail(
|
|
1261
|
+
"Profile is valid",
|
|
1262
|
+
validation.errors.map((error) => error.message).join("; "),
|
|
1263
|
+
"Fix the invalid project profile fields."
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
checkDocsScaffold(projectRoot) {
|
|
1267
|
+
const required = [
|
|
1268
|
+
"docs/modules/core/api/endpoints.md",
|
|
1269
|
+
"docs/modules/core/api/schemas.md",
|
|
1270
|
+
"docs/modules/core/api/error-codes.md",
|
|
1271
|
+
"docs/modules/core/integration/events.md",
|
|
1272
|
+
"docs/modules/core/integration/contracts.md",
|
|
1273
|
+
"docs/modules/core/error-catalog.md"
|
|
1274
|
+
];
|
|
1275
|
+
const missing = required.filter((relative6) => !existsSync4(join6(projectRoot, relative6)));
|
|
1276
|
+
return missing.length === 0 ? pass("Docs scaffold exists", "Documentation scaffold is complete") : fail(
|
|
1277
|
+
"Docs scaffold exists",
|
|
1278
|
+
`Missing scaffold files: ${missing.join(", ")}`,
|
|
1279
|
+
"Regenerate the documentation scaffold."
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
checkIndexesCurrent(projectRoot) {
|
|
1283
|
+
const missing = REGISTRIES.filter(
|
|
1284
|
+
(registry) => !existsSync4(join6(projectRoot, "docs", registry))
|
|
1285
|
+
);
|
|
1286
|
+
const statusPath = join6(projectRoot, ".agency/indexes/registry-status.json");
|
|
1287
|
+
if (missing.length > 0 || !existsSync4(statusPath)) {
|
|
1288
|
+
return fail(
|
|
1289
|
+
"Indexes are current",
|
|
1290
|
+
"Registry scaffold is incomplete",
|
|
1291
|
+
"Run the registry refresh scripts."
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
const stale = isStale(statSync(statusPath).mtimeMs);
|
|
1295
|
+
return stale ? fail("Indexes are current", "Registries are stale", "Run the registry refresh scripts.") : pass("Indexes are current", "Registries are present and current");
|
|
1296
|
+
}
|
|
1297
|
+
async checkScripts(projectRoot) {
|
|
1298
|
+
const required = ["verify.sh", "refresh-registry-diff.sh", "extract-routes.sh"];
|
|
1299
|
+
const missing = [];
|
|
1300
|
+
const nonExecutable = [];
|
|
1301
|
+
for (const script of required) {
|
|
1302
|
+
const path = join6(projectRoot, "scripts", script);
|
|
1303
|
+
if (!existsSync4(path)) {
|
|
1304
|
+
missing.push(script);
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1307
|
+
try {
|
|
1308
|
+
await access(path, fsConstants.X_OK);
|
|
1309
|
+
} catch {
|
|
1310
|
+
nonExecutable.push(script);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
return missing.length === 0 && nonExecutable.length === 0 ? pass("Scripts are runnable", "Required scripts exist and are executable") : fail(
|
|
1314
|
+
"Scripts are runnable",
|
|
1315
|
+
`Missing: ${missing.join(", ") || "none"}; non-executable: ${nonExecutable.join(", ") || "none"}`,
|
|
1316
|
+
"Regenerate the scripts and ensure they are executable."
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
checkHooks(projectRoot) {
|
|
1320
|
+
const candidates = [".claude/settings.hooks.json", ".codex/hooks.json", ".gemini/hooks.json"];
|
|
1321
|
+
return candidates.some((candidate) => existsSync4(join6(projectRoot, candidate))) ? pass("Hooks are active", "Hook registration files are present") : fail(
|
|
1322
|
+
"Hooks are active",
|
|
1323
|
+
"No hook registration files were found",
|
|
1324
|
+
"Regenerate adapter hook configuration."
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
checkAdapterConfig(projectRoot) {
|
|
1328
|
+
const configs = [PATHS.CLAUDE_MD, PATHS.AGENTS_MD, PATHS.GEMINI_MD];
|
|
1329
|
+
const existing = configs.filter((config) => existsSync4(join6(projectRoot, config)));
|
|
1330
|
+
return existing.length > 0 ? pass("Adapter config is present", "Adapter config files are present") : fail(
|
|
1331
|
+
"Adapter config is present",
|
|
1332
|
+
"No adapter config files were found",
|
|
1333
|
+
"Regenerate the adapter configuration."
|
|
1334
|
+
);
|
|
1335
|
+
}
|
|
1336
|
+
checkStackCommands(profile) {
|
|
1337
|
+
if (profile === null) {
|
|
1338
|
+
return fail(
|
|
1339
|
+
"Stack commands configured",
|
|
1340
|
+
"Project profile is missing",
|
|
1341
|
+
"Restore a valid project profile."
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
if (!profile.commands || typeof profile.commands !== "object") {
|
|
1345
|
+
return fail(
|
|
1346
|
+
"Stack commands configured",
|
|
1347
|
+
"Project commands are missing from the profile",
|
|
1348
|
+
"Restore the commands block in the project profile."
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
const missing = Object.entries(profile.commands).filter(([, value]) => typeof value !== "string" || value.trim() === "").map(([key]) => key);
|
|
1352
|
+
return missing.length === 0 ? pass("Stack commands configured", "Required stack commands are populated") : fail(
|
|
1353
|
+
"Stack commands configured",
|
|
1354
|
+
`Missing command definitions: ${missing.join(", ")}`,
|
|
1355
|
+
"Populate the missing command entries in the project profile."
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
checkStableFrameworkPaths(projectRoot) {
|
|
1359
|
+
const frameworkPath = join6(projectRoot, PATHS.FRAMEWORK_PATH);
|
|
1360
|
+
if (!existsSync4(frameworkPath)) {
|
|
1361
|
+
return fail(
|
|
1362
|
+
"Stable framework paths only",
|
|
1363
|
+
"Framework path file is missing",
|
|
1364
|
+
"Re-run onboarding to write the framework path."
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
const value = readFileSync4(frameworkPath, "utf8").trim();
|
|
1368
|
+
const unstable = value.includes("npx") || value.includes("npm-global") || value.includes(".npm/_npx");
|
|
1369
|
+
return unstable ? fail(
|
|
1370
|
+
"Stable framework paths only",
|
|
1371
|
+
"Framework path points to an ephemeral install location",
|
|
1372
|
+
"Reinstall the framework to a stable user-level path and re-run onboarding."
|
|
1373
|
+
) : pass("Stable framework paths only", "Framework path is stable");
|
|
1374
|
+
}
|
|
1375
|
+
checkBrokenScaffold(projectRoot) {
|
|
1376
|
+
const broken = walk(projectRoot).filter(
|
|
1377
|
+
(path) => path.endsWith(".partial") || path.endsWith(".tmp")
|
|
1378
|
+
);
|
|
1379
|
+
return broken.length === 0 ? pass("No broken scaffold state", "No partial scaffold artifacts detected") : fail(
|
|
1380
|
+
"No broken scaffold state",
|
|
1381
|
+
`Partial scaffold artifacts detected: ${broken.join(", ")}`,
|
|
1382
|
+
"Remove the partial files and re-run onboarding or update."
|
|
1383
|
+
);
|
|
1384
|
+
}
|
|
1385
|
+
checkUiDocs(projectRoot, modules) {
|
|
1386
|
+
const missing = modules.filter(
|
|
1387
|
+
(moduleName) => !existsSync4(join6(projectRoot, "docs/modules", moduleName, "ui/screens.md")) || !existsSync4(join6(projectRoot, "docs/modules", moduleName, "ui/components.md")) || !existsSync4(join6(projectRoot, "docs/modules", moduleName, "ui/states.md"))
|
|
1388
|
+
);
|
|
1389
|
+
return missing.length === 0 ? pass("UI docs present", "UI docs are present for all modules with UI docs") : fail(
|
|
1390
|
+
"UI docs present",
|
|
1391
|
+
`Missing UI docs for: ${missing.join(", ")}`,
|
|
1392
|
+
"Add the missing UI documentation files."
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
checkApiDocs(projectRoot, modules) {
|
|
1396
|
+
const missing = modules.filter(
|
|
1397
|
+
(moduleName) => !existsSync4(join6(projectRoot, "docs/modules", moduleName, "api/endpoints.md")) || !existsSync4(join6(projectRoot, "docs/modules", moduleName, "api/schemas.md")) || !existsSync4(join6(projectRoot, "docs/modules", moduleName, "api/error-codes.md"))
|
|
1398
|
+
);
|
|
1399
|
+
return missing.length === 0 ? pass("API docs present", "API docs are present for all modules with APIs") : fail(
|
|
1400
|
+
"API docs present",
|
|
1401
|
+
`Missing API docs for: ${missing.join(", ")}`,
|
|
1402
|
+
"Add the missing API documentation files."
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1405
|
+
checkIntegrationDocs(projectRoot, modules) {
|
|
1406
|
+
const missing = modules.filter(
|
|
1407
|
+
(moduleName) => !existsSync4(join6(projectRoot, "docs/modules", moduleName, "integration/events.md")) || !existsSync4(join6(projectRoot, "docs/modules", moduleName, "integration/contracts.md"))
|
|
1408
|
+
);
|
|
1409
|
+
return missing.length === 0 ? pass("Integration docs present", "Integration docs are present for all modules") : fail(
|
|
1410
|
+
"Integration docs present",
|
|
1411
|
+
`Missing integration docs for: ${missing.join(", ")}`,
|
|
1412
|
+
"Add the missing integration documentation files."
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
checkErrorCatalog(projectRoot, modules) {
|
|
1416
|
+
const missing = modules.filter(
|
|
1417
|
+
(moduleName) => !existsSync4(join6(projectRoot, "docs/modules", moduleName, "error-catalog.md"))
|
|
1418
|
+
);
|
|
1419
|
+
return missing.length === 0 ? pass("Error catalog present", "Error catalogs are present for all modules") : fail(
|
|
1420
|
+
"Error catalog present",
|
|
1421
|
+
`Missing error catalogs for: ${missing.join(", ")}`,
|
|
1422
|
+
"Add the missing module error catalogs."
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
checkMcp(projectRoot, profile) {
|
|
1426
|
+
if (profile === null || !profile.routing || !Array.isArray(profile.routing.capabilities)) {
|
|
1427
|
+
return warn(
|
|
1428
|
+
"MCP servers configured",
|
|
1429
|
+
"Project profile is unavailable, MCP expectations could not be derived",
|
|
1430
|
+
"Restore the project profile, then configure stack-appropriate MCP servers."
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
const candidates = [".claude/settings.mcp.json", ".codex/mcp.json", ".gemini/mcp.json"];
|
|
1434
|
+
const existing = candidates.filter((candidate) => existsSync4(join6(projectRoot, candidate)));
|
|
1435
|
+
const expectedServers = getServersForStack(profile.routing.stack, profile.routing.capabilities).filter((server) => server.name !== "figma").map((server) => server.name);
|
|
1436
|
+
if (existing.length === 0) {
|
|
1437
|
+
return warn(
|
|
1438
|
+
"MCP servers configured",
|
|
1439
|
+
"No MCP configuration files were found",
|
|
1440
|
+
"Configure MCP servers in project profile for improved efficiency"
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
const configured = /* @__PURE__ */ new Set();
|
|
1444
|
+
for (const path of existing) {
|
|
1445
|
+
try {
|
|
1446
|
+
const parsed = JSON.parse(readFileSync4(join6(projectRoot, path), "utf8"));
|
|
1447
|
+
Object.keys(parsed.mcpServers ?? {}).forEach((server) => configured.add(server));
|
|
1448
|
+
} catch {
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
const missingExpected = expectedServers.filter((server) => !configured.has(server));
|
|
1453
|
+
return missingExpected.length === 0 ? pass("MCP servers configured", "Stack-appropriate MCP servers are configured") : warn(
|
|
1454
|
+
"MCP servers configured",
|
|
1455
|
+
`Missing recommended MCP servers: ${missingExpected.join(", ")}`,
|
|
1456
|
+
"Configure MCP servers in project profile for improved efficiency"
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
checkSkillCache(projectRoot) {
|
|
1460
|
+
const cacheDir = join6(projectRoot, PATHS.SKILL_CACHE_DIR);
|
|
1461
|
+
if (!existsSync4(cacheDir)) {
|
|
1462
|
+
return warn(
|
|
1463
|
+
"Skill cache healthy",
|
|
1464
|
+
"Skill cache directory is missing",
|
|
1465
|
+
"Run skill-cache-manager.sh --clear-all to reset cache"
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
const corrupt = readdirSync(cacheDir).filter((entry) => entry.endsWith(".json")).filter((entry) => {
|
|
1469
|
+
try {
|
|
1470
|
+
JSON.parse(readFileSync4(join6(cacheDir, entry), "utf8"));
|
|
1471
|
+
return false;
|
|
1472
|
+
} catch {
|
|
1473
|
+
return true;
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
return corrupt.length === 0 ? pass("Skill cache healthy", "Skill cache directory is healthy") : warn(
|
|
1477
|
+
"Skill cache healthy",
|
|
1478
|
+
`Corrupt cache files detected: ${corrupt.join(", ")}`,
|
|
1479
|
+
"Run skill-cache-manager.sh --clear-all to reset cache"
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
checkContextHitRate(projectRoot, profile) {
|
|
1483
|
+
const path = join6(projectRoot, PATHS.CONTEXT_HIT_LOG);
|
|
1484
|
+
if (!existsSync4(path) || profile === null) {
|
|
1485
|
+
return pass("Context hit rate acceptable", "No recent context logs yet");
|
|
1486
|
+
}
|
|
1487
|
+
try {
|
|
1488
|
+
const entry = JSON.parse(readFileSync4(path, "utf8"));
|
|
1489
|
+
const hitRate = entry.hit_rate ?? 1;
|
|
1490
|
+
const target = profile.efficiency.context_hit_rate_target ?? 0.7;
|
|
1491
|
+
return hitRate >= target ? pass("Context hit rate acceptable", "Context hit rate meets the configured target") : warn(
|
|
1492
|
+
"Context hit rate acceptable",
|
|
1493
|
+
"Context hit rate is below the configured target",
|
|
1494
|
+
"Reduce unnecessary context loading or improve context selection."
|
|
1495
|
+
);
|
|
1496
|
+
} catch {
|
|
1497
|
+
return warn(
|
|
1498
|
+
"Context hit rate acceptable",
|
|
1499
|
+
"Context hit log is unreadable",
|
|
1500
|
+
"Regenerate the context hit log with the tracking hook."
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
buildEfficiencySummary(projectRoot, profile) {
|
|
1505
|
+
const path = join6(projectRoot, PATHS.CONTEXT_HIT_LOG);
|
|
1506
|
+
let contextHitRate = 1;
|
|
1507
|
+
if (existsSync4(path)) {
|
|
1508
|
+
try {
|
|
1509
|
+
const entry = JSON.parse(readFileSync4(path, "utf8"));
|
|
1510
|
+
contextHitRate = entry.hit_rate ?? 1;
|
|
1511
|
+
} catch {
|
|
1512
|
+
contextHitRate = 0;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
return {
|
|
1516
|
+
context_hit_rate: contextHitRate,
|
|
1517
|
+
skill_cache_hit_rate: existsSync4(join6(projectRoot, PATHS.SKILL_CACHE_DIR)) ? 1 : 0,
|
|
1518
|
+
mcp_usage_rate: profile?.efficiency?.mcp_first ? 1 : 0
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
function pass(name, detail) {
|
|
1523
|
+
return { name, status: "pass", detail };
|
|
1524
|
+
}
|
|
1525
|
+
function fail(name, detail, remediation) {
|
|
1526
|
+
return { name, status: "fail", detail, remediation };
|
|
1527
|
+
}
|
|
1528
|
+
function warn(name, detail, remediation) {
|
|
1529
|
+
return { name, status: "warning", detail, remediation };
|
|
1530
|
+
}
|
|
1531
|
+
function deriveOverallStatus(checks) {
|
|
1532
|
+
if (checks.some((check) => check.status === "fail")) {
|
|
1533
|
+
return "fail";
|
|
1534
|
+
}
|
|
1535
|
+
if (checks.some((check) => check.status === "warning")) {
|
|
1536
|
+
return "warning";
|
|
1537
|
+
}
|
|
1538
|
+
return "pass";
|
|
1539
|
+
}
|
|
1540
|
+
function isStale(timestampMs) {
|
|
1541
|
+
return Date.now() - timestampMs > STALENESS_WINDOW_MS;
|
|
1542
|
+
}
|
|
1543
|
+
function walk(root) {
|
|
1544
|
+
const results = [];
|
|
1545
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
1546
|
+
const path = join6(root, entry.name);
|
|
1547
|
+
if (entry.isDirectory()) {
|
|
1548
|
+
results.push(...walk(path));
|
|
1549
|
+
continue;
|
|
1550
|
+
}
|
|
1551
|
+
results.push(path);
|
|
1552
|
+
}
|
|
1553
|
+
return results;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// src/install/bootstrap.ts
|
|
1557
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
1558
|
+
|
|
1559
|
+
// src/onboarding/manifest-writer.ts
|
|
1560
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
1561
|
+
import { homedir } from "os";
|
|
1562
|
+
import { dirname as dirname2, join as join7 } from "path";
|
|
1563
|
+
import YAML2 from "yaml";
|
|
1564
|
+
function writeProjectProfile(projectRoot, profile) {
|
|
1565
|
+
const path = join7(projectRoot, PATHS.PROJECT_PROFILE);
|
|
1566
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
1567
|
+
writeFileSync(path, YAML2.stringify(profile));
|
|
1568
|
+
return path;
|
|
1569
|
+
}
|
|
1570
|
+
function writeDetectionReport(projectRoot, report) {
|
|
1571
|
+
const path = join7(projectRoot, PATHS.DETECTION_REPORT);
|
|
1572
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
1573
|
+
writeFileSync(path, JSON.stringify(report, null, 2));
|
|
1574
|
+
return path;
|
|
1575
|
+
}
|
|
1576
|
+
function writeFrameworkMetadata(projectRoot, version) {
|
|
1577
|
+
mkdirSync(dirname2(join7(projectRoot, PATHS.FRAMEWORK_VERSION)), {
|
|
1578
|
+
recursive: true
|
|
1579
|
+
});
|
|
1580
|
+
writeFileSync(join7(projectRoot, PATHS.FRAMEWORK_VERSION), `${version}
|
|
1581
|
+
`);
|
|
1582
|
+
writeFileSync(join7(projectRoot, PATHS.FRAMEWORK_PATH), `${resolveFrameworkInstallPath()}
|
|
1583
|
+
`);
|
|
1584
|
+
}
|
|
1585
|
+
function writeOnboardingManifest(projectRoot, manifest) {
|
|
1586
|
+
const path = join7(projectRoot, PATHS.ONBOARDING_MANIFEST);
|
|
1587
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
1588
|
+
writeFileSync(path, JSON.stringify(manifest, null, 2));
|
|
1589
|
+
return path;
|
|
1590
|
+
}
|
|
1591
|
+
function resolveFrameworkInstallPath() {
|
|
1592
|
+
return process.env.PAQAD_FRAMEWORK_HOME ?? join7(homedir(), ".paqad-ai/current");
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// src/install/bootstrap.ts
|
|
1596
|
+
function bootstrapFramework(projectRoot) {
|
|
1597
|
+
const frameworkHome = resolveFrameworkInstallPath();
|
|
1598
|
+
mkdirSync2(frameworkHome, { recursive: true });
|
|
1599
|
+
writeFrameworkMetadata(projectRoot, VERSION);
|
|
1600
|
+
return {
|
|
1601
|
+
framework_home: frameworkHome,
|
|
1602
|
+
project_root: projectRoot,
|
|
1603
|
+
version: VERSION
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// src/onboarding/file-writer.ts
|
|
1608
|
+
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1609
|
+
import { dirname as dirname3, join as join8 } from "path";
|
|
1610
|
+
function writeGeneratedFiles(projectRoot, files) {
|
|
1611
|
+
const written = [];
|
|
1612
|
+
const skipped = [];
|
|
1613
|
+
for (const file of files) {
|
|
1614
|
+
const target = join8(projectRoot, file.path);
|
|
1615
|
+
mkdirSync3(dirname3(target), { recursive: true });
|
|
1616
|
+
if (!file.autoUpdate && existsSync5(target)) {
|
|
1617
|
+
skipped.push(file.path);
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
writeFileSync2(target, file.content);
|
|
1621
|
+
if (file.executable === true) {
|
|
1622
|
+
chmodSync(target, 493);
|
|
1623
|
+
}
|
|
1624
|
+
written.push(file.path);
|
|
1625
|
+
}
|
|
1626
|
+
return { written, skipped };
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// src/resolver/resolver.ts
|
|
1630
|
+
import fg from "fast-glob";
|
|
1631
|
+
import { basename as basename2, extname, relative as relative2 } from "pathe";
|
|
1632
|
+
|
|
1633
|
+
// src/resolver/artifact-types.ts
|
|
1634
|
+
var ARTIFACT_TYPES = [
|
|
1635
|
+
"rules",
|
|
1636
|
+
"skills",
|
|
1637
|
+
"agents",
|
|
1638
|
+
"hooks",
|
|
1639
|
+
"templates",
|
|
1640
|
+
"patterns",
|
|
1641
|
+
"anti-patterns",
|
|
1642
|
+
"checklists",
|
|
1643
|
+
"mcp-configs"
|
|
1644
|
+
];
|
|
1645
|
+
var COLLISION_MAP = {
|
|
1646
|
+
rules: "most-specific-wins",
|
|
1647
|
+
skills: "most-specific-wins",
|
|
1648
|
+
agents: "most-specific-wins",
|
|
1649
|
+
hooks: "most-specific-wins",
|
|
1650
|
+
templates: "most-specific-wins",
|
|
1651
|
+
patterns: "additive-merge",
|
|
1652
|
+
"anti-patterns": "additive-merge",
|
|
1653
|
+
checklists: "additive-merge",
|
|
1654
|
+
"mcp-configs": "additive-merge"
|
|
1655
|
+
};
|
|
1656
|
+
var ARTIFACT_OUTPUT_KEYS = {
|
|
1657
|
+
rules: "rules",
|
|
1658
|
+
skills: "skills",
|
|
1659
|
+
agents: "agents",
|
|
1660
|
+
hooks: "hooks",
|
|
1661
|
+
templates: "templates",
|
|
1662
|
+
patterns: "patterns",
|
|
1663
|
+
"anti-patterns": "antiPatterns",
|
|
1664
|
+
checklists: "checklists",
|
|
1665
|
+
"mcp-configs": "mcpConfigs"
|
|
1666
|
+
};
|
|
1667
|
+
|
|
1668
|
+
// src/resolver/inheritance.ts
|
|
1669
|
+
import { join as join10, relative } from "pathe";
|
|
1670
|
+
|
|
1671
|
+
// src/resolver/capability-resolver.ts
|
|
1672
|
+
import { join as join9 } from "pathe";
|
|
1673
|
+
function resolveCapabilityDirectories(runtimeRoot, domain, stack, capabilities, artifactType) {
|
|
1674
|
+
const directoryName = resolveCapabilityArtifactDirectory(artifactType);
|
|
1675
|
+
return capabilities.map(
|
|
1676
|
+
(capability) => join9(
|
|
1677
|
+
runtimeRoot,
|
|
1678
|
+
"domains",
|
|
1679
|
+
domain,
|
|
1680
|
+
"stacks",
|
|
1681
|
+
stack,
|
|
1682
|
+
"capabilities",
|
|
1683
|
+
capability,
|
|
1684
|
+
directoryName
|
|
1685
|
+
)
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
function resolveCapabilityArtifactDirectory(artifactType) {
|
|
1689
|
+
switch (artifactType) {
|
|
1690
|
+
case "mcp-configs":
|
|
1691
|
+
return "mcp";
|
|
1692
|
+
case "patterns":
|
|
1693
|
+
return join9("benchmarks", "patterns");
|
|
1694
|
+
case "anti-patterns":
|
|
1695
|
+
return join9("benchmarks", "anti-patterns");
|
|
1696
|
+
default:
|
|
1697
|
+
return artifactType;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// src/resolver/inheritance.ts
|
|
1702
|
+
function getInheritanceDirectories(runtimeRoot, routing, artifactType) {
|
|
1703
|
+
if (artifactType === "hooks") {
|
|
1704
|
+
return [
|
|
1705
|
+
{
|
|
1706
|
+
path: join10(runtimeRoot, "hooks"),
|
|
1707
|
+
level: 0,
|
|
1708
|
+
source: relative(runtimeRoot, join10(runtimeRoot, "hooks"))
|
|
1709
|
+
}
|
|
1710
|
+
];
|
|
1711
|
+
}
|
|
1712
|
+
if (artifactType === "templates") {
|
|
1713
|
+
return [
|
|
1714
|
+
{
|
|
1715
|
+
path: join10(runtimeRoot, "templates"),
|
|
1716
|
+
level: 0,
|
|
1717
|
+
source: relative(runtimeRoot, join10(runtimeRoot, "templates"))
|
|
1718
|
+
}
|
|
1719
|
+
];
|
|
1720
|
+
}
|
|
1721
|
+
const basePath = join10(runtimeRoot, "domains");
|
|
1722
|
+
const directoryName = resolveArtifactDirectoryName(artifactType);
|
|
1723
|
+
const directories = [
|
|
1724
|
+
{
|
|
1725
|
+
path: join10(basePath, "_shared", directoryName),
|
|
1726
|
+
level: 0,
|
|
1727
|
+
source: relative(runtimeRoot, join10(basePath, "_shared", directoryName))
|
|
1728
|
+
},
|
|
1729
|
+
{
|
|
1730
|
+
path: join10(basePath, routing.domain, directoryName),
|
|
1731
|
+
level: 1,
|
|
1732
|
+
source: relative(runtimeRoot, join10(basePath, routing.domain, directoryName))
|
|
1733
|
+
},
|
|
1734
|
+
{
|
|
1735
|
+
path: join10(basePath, routing.domain, "stacks", "_shared", directoryName),
|
|
1736
|
+
level: 2,
|
|
1737
|
+
source: relative(
|
|
1738
|
+
runtimeRoot,
|
|
1739
|
+
join10(basePath, routing.domain, "stacks", "_shared", directoryName)
|
|
1740
|
+
)
|
|
1741
|
+
},
|
|
1742
|
+
{
|
|
1743
|
+
path: join10(basePath, routing.domain, "stacks", routing.stack, directoryName),
|
|
1744
|
+
level: 2,
|
|
1745
|
+
source: relative(
|
|
1746
|
+
runtimeRoot,
|
|
1747
|
+
join10(basePath, routing.domain, "stacks", routing.stack, directoryName)
|
|
1748
|
+
)
|
|
1749
|
+
}
|
|
1750
|
+
];
|
|
1751
|
+
const capabilityDirectories = resolveCapabilityDirectories(
|
|
1752
|
+
runtimeRoot,
|
|
1753
|
+
routing.domain,
|
|
1754
|
+
routing.stack,
|
|
1755
|
+
routing.capabilities,
|
|
1756
|
+
artifactType
|
|
1757
|
+
).map((path) => ({
|
|
1758
|
+
path,
|
|
1759
|
+
level: 3,
|
|
1760
|
+
source: relative(runtimeRoot, path)
|
|
1761
|
+
}));
|
|
1762
|
+
return [...directories, ...capabilityDirectories];
|
|
1763
|
+
}
|
|
1764
|
+
function resolveArtifactDirectoryName(artifactType) {
|
|
1765
|
+
switch (artifactType) {
|
|
1766
|
+
case "mcp-configs":
|
|
1767
|
+
return "mcp";
|
|
1768
|
+
case "patterns":
|
|
1769
|
+
return join10("benchmarks", "patterns");
|
|
1770
|
+
case "anti-patterns":
|
|
1771
|
+
return join10("benchmarks", "anti-patterns");
|
|
1772
|
+
default:
|
|
1773
|
+
return artifactType;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
// src/resolver/resolver.ts
|
|
1778
|
+
var RULE_SEED_PRIORITY = [
|
|
1779
|
+
"constitution",
|
|
1780
|
+
"security",
|
|
1781
|
+
"testing",
|
|
1782
|
+
"documentation",
|
|
1783
|
+
"performance",
|
|
1784
|
+
"pipeline",
|
|
1785
|
+
"canonical-docs",
|
|
1786
|
+
"architecture",
|
|
1787
|
+
"code-quality",
|
|
1788
|
+
"git",
|
|
1789
|
+
"api-design",
|
|
1790
|
+
"environment",
|
|
1791
|
+
"foundation",
|
|
1792
|
+
"conventions",
|
|
1793
|
+
"inertia",
|
|
1794
|
+
"react",
|
|
1795
|
+
"boost"
|
|
1796
|
+
];
|
|
1797
|
+
var Resolver = class {
|
|
1798
|
+
runtimeRoot;
|
|
1799
|
+
constructor(options) {
|
|
1800
|
+
this.runtimeRoot = options.runtimeRoot;
|
|
1801
|
+
}
|
|
1802
|
+
async resolve(routing) {
|
|
1803
|
+
const result = {
|
|
1804
|
+
rules: [],
|
|
1805
|
+
skills: [],
|
|
1806
|
+
agents: [],
|
|
1807
|
+
hooks: [],
|
|
1808
|
+
templates: [],
|
|
1809
|
+
patterns: [],
|
|
1810
|
+
antiPatterns: [],
|
|
1811
|
+
checklists: [],
|
|
1812
|
+
mcpConfigs: []
|
|
1813
|
+
};
|
|
1814
|
+
for (const artifactType of ARTIFACT_TYPES) {
|
|
1815
|
+
const outputKey = ARTIFACT_OUTPUT_KEYS[artifactType];
|
|
1816
|
+
result[outputKey] = await this.resolveArtifactType(routing, artifactType);
|
|
1817
|
+
}
|
|
1818
|
+
return result;
|
|
1819
|
+
}
|
|
1820
|
+
async resolveArtifactType(routing, artifactType) {
|
|
1821
|
+
const directories = getInheritanceDirectories(this.runtimeRoot, routing, artifactType);
|
|
1822
|
+
const entries = [];
|
|
1823
|
+
const overrides = /* @__PURE__ */ new Map();
|
|
1824
|
+
for (const directory of directories) {
|
|
1825
|
+
const files = await fg("**/*", {
|
|
1826
|
+
cwd: directory.path,
|
|
1827
|
+
onlyFiles: true,
|
|
1828
|
+
absolute: true,
|
|
1829
|
+
dot: false
|
|
1830
|
+
});
|
|
1831
|
+
for (const file of files.sort()) {
|
|
1832
|
+
const artifact = {
|
|
1833
|
+
path: file,
|
|
1834
|
+
level: directory.level,
|
|
1835
|
+
source: relative2(this.runtimeRoot, file)
|
|
1836
|
+
};
|
|
1837
|
+
if (COLLISION_MAP[artifactType] === "additive-merge") {
|
|
1838
|
+
entries.push(artifact);
|
|
1839
|
+
continue;
|
|
1840
|
+
}
|
|
1841
|
+
overrides.set(relative2(directory.path, file), artifact);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
const resolved = COLLISION_MAP[artifactType] === "additive-merge" ? entries : Array.from(overrides.values());
|
|
1845
|
+
if (artifactType === "rules") {
|
|
1846
|
+
return resolved.sort(compareRuleSeedOrder);
|
|
1847
|
+
}
|
|
1848
|
+
return resolved;
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
function compareRuleSeedOrder(left, right) {
|
|
1852
|
+
const leftPriority = getRulePriority(left.path);
|
|
1853
|
+
const rightPriority = getRulePriority(right.path);
|
|
1854
|
+
if (leftPriority !== rightPriority) {
|
|
1855
|
+
return leftPriority - rightPriority;
|
|
1856
|
+
}
|
|
1857
|
+
return left.source.localeCompare(right.source);
|
|
1858
|
+
}
|
|
1859
|
+
function getRulePriority(filePath) {
|
|
1860
|
+
const name = basename2(filePath, extname(filePath));
|
|
1861
|
+
const index = RULE_SEED_PRIORITY.indexOf(name);
|
|
1862
|
+
return index === -1 ? RULE_SEED_PRIORITY.length : index;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
// src/scripts/generator.ts
|
|
1866
|
+
import { chmodSync as chmodSync2 } from "fs";
|
|
1867
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
1868
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
1869
|
+
|
|
1870
|
+
// src/templates/context-builders/runner-scripts.ts
|
|
1871
|
+
function buildRunnerScriptContext(input) {
|
|
1872
|
+
return input;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
// src/templates/registry.ts
|
|
1876
|
+
import fg2 from "fast-glob";
|
|
1877
|
+
import { basename as basename3, relative as relative3 } from "pathe";
|
|
1878
|
+
var TemplateRegistry = class {
|
|
1879
|
+
constructor(templatesRoot) {
|
|
1880
|
+
this.templatesRoot = templatesRoot;
|
|
1881
|
+
}
|
|
1882
|
+
async discover() {
|
|
1883
|
+
const files = await fg2("**/*.hbs", {
|
|
1884
|
+
cwd: this.templatesRoot,
|
|
1885
|
+
absolute: true,
|
|
1886
|
+
onlyFiles: true
|
|
1887
|
+
});
|
|
1888
|
+
return files.sort().map((path) => ({
|
|
1889
|
+
name: basename3(path),
|
|
1890
|
+
path,
|
|
1891
|
+
relativePath: relative3(this.templatesRoot, path)
|
|
1892
|
+
}));
|
|
1893
|
+
}
|
|
1894
|
+
};
|
|
1895
|
+
|
|
1896
|
+
// src/scripts/generator.ts
|
|
1897
|
+
var RunnerScriptGenerator = class {
|
|
1898
|
+
engine = new TemplateEngine();
|
|
1899
|
+
async generate(profile) {
|
|
1900
|
+
const registry = new TemplateRegistry(join11(getRuntimeTemplatesRoot(), "runner-scripts"));
|
|
1901
|
+
const templates = await registry.discover();
|
|
1902
|
+
return Promise.all(
|
|
1903
|
+
templates.map(async (template) => ({
|
|
1904
|
+
path: join11("scripts", template.relativePath.replace(/\.hbs$/, "")),
|
|
1905
|
+
content: await this.engine.render(template.path, buildScriptContext(profile)),
|
|
1906
|
+
autoUpdate: true,
|
|
1907
|
+
executable: true
|
|
1908
|
+
}))
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
async write(projectRoot, profile) {
|
|
1912
|
+
const generated = await this.generate(profile);
|
|
1913
|
+
const written = [];
|
|
1914
|
+
for (const file of generated) {
|
|
1915
|
+
const target = join11(projectRoot, file.path);
|
|
1916
|
+
await mkdir(dirname4(target), { recursive: true });
|
|
1917
|
+
await writeFile(target, file.content);
|
|
1918
|
+
chmodSync2(target, 493);
|
|
1919
|
+
written.push(file.path);
|
|
1920
|
+
}
|
|
1921
|
+
return written;
|
|
1922
|
+
}
|
|
1923
|
+
};
|
|
1924
|
+
function buildScriptContext(profile) {
|
|
1925
|
+
return buildRunnerScriptContext({
|
|
1926
|
+
projectName: profile.project.name,
|
|
1927
|
+
commands: {
|
|
1928
|
+
test: profile.commands.test,
|
|
1929
|
+
lint: profile.commands.lint,
|
|
1930
|
+
format: profile.commands.format
|
|
1931
|
+
},
|
|
1932
|
+
routing: {
|
|
1933
|
+
stack: profile.routing.stack
|
|
1934
|
+
}
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
// src/onboarding/prompts.ts
|
|
1939
|
+
async function resolveSelections(detection, overrides) {
|
|
1940
|
+
return {
|
|
1941
|
+
domain: overrides?.domain ?? detection.detected_domain ?? "coding",
|
|
1942
|
+
stack: overrides?.stack ?? detection.detected_stack ?? "laravel",
|
|
1943
|
+
capabilities: overrides?.capabilities ?? detection.detected_capabilities
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
// src/onboarding/registry-generator.ts
|
|
1948
|
+
import { readdir } from "fs/promises";
|
|
1949
|
+
import { join as join12 } from "path";
|
|
1950
|
+
async function generateInitialRegistries(projectRoot) {
|
|
1951
|
+
const modules = await discoverModules(projectRoot);
|
|
1952
|
+
return [
|
|
1953
|
+
{
|
|
1954
|
+
path: ".agency/indexes/registry-status.json",
|
|
1955
|
+
content: JSON.stringify(
|
|
1956
|
+
{
|
|
1957
|
+
generated: true,
|
|
1958
|
+
modules,
|
|
1959
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1960
|
+
},
|
|
1961
|
+
null,
|
|
1962
|
+
2
|
|
1963
|
+
),
|
|
1964
|
+
autoUpdate: true
|
|
1965
|
+
},
|
|
1966
|
+
{
|
|
1967
|
+
path: ".agency/cache/skill-results/.gitkeep",
|
|
1968
|
+
content: "",
|
|
1969
|
+
autoUpdate: true
|
|
1970
|
+
},
|
|
1971
|
+
{
|
|
1972
|
+
path: PATHS.GLOSSARY,
|
|
1973
|
+
content: "# Glossary\n\n",
|
|
1974
|
+
autoUpdate: true
|
|
1975
|
+
},
|
|
1976
|
+
...REGISTRIES.map((registry) => ({
|
|
1977
|
+
path: join12("docs", registry),
|
|
1978
|
+
content: registry === "module-registry.md" ? buildModuleRegistry(modules) : `# ${registry}
|
|
1979
|
+
|
|
1980
|
+
`,
|
|
1981
|
+
autoUpdate: true
|
|
1982
|
+
}))
|
|
1983
|
+
];
|
|
1984
|
+
}
|
|
1985
|
+
async function discoverModules(projectRoot) {
|
|
1986
|
+
const candidates = [
|
|
1987
|
+
join12(projectRoot, "docs/modules"),
|
|
1988
|
+
join12(projectRoot, "app"),
|
|
1989
|
+
join12(projectRoot, "lib")
|
|
1990
|
+
];
|
|
1991
|
+
const modules = /* @__PURE__ */ new Set(["core"]);
|
|
1992
|
+
for (const root of candidates) {
|
|
1993
|
+
try {
|
|
1994
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
1995
|
+
for (const entry of entries) {
|
|
1996
|
+
if (!entry.isDirectory()) {
|
|
1997
|
+
continue;
|
|
1998
|
+
}
|
|
1999
|
+
const name = entry.name;
|
|
2000
|
+
if (name !== "" && !name.startsWith(".")) {
|
|
2001
|
+
modules.add(name);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
} catch {
|
|
2005
|
+
continue;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
return Array.from(modules).sort();
|
|
2009
|
+
}
|
|
2010
|
+
function buildModuleRegistry(modules) {
|
|
2011
|
+
return ["# module-registry.md", "", ...modules.map((module) => `- ${module}`), ""].join("\n");
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
// src/onboarding/reference-generator.ts
|
|
2015
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
2016
|
+
import { join as join13, relative as relative4 } from "path";
|
|
2017
|
+
import fg3 from "fast-glob";
|
|
2018
|
+
async function generateReferenceGuides(runtimeRoot, routing) {
|
|
2019
|
+
const stack = routing.stack;
|
|
2020
|
+
if (routing.domain !== "coding" || stack !== "laravel" && stack !== "flutter") {
|
|
2021
|
+
return [];
|
|
2022
|
+
}
|
|
2023
|
+
const referencesRoot = join13(runtimeRoot, "domains", "coding", "stacks", stack, "references");
|
|
2024
|
+
const entries = await fg3(["tools/*.md", "tools-catalog.md"], {
|
|
2025
|
+
cwd: referencesRoot,
|
|
2026
|
+
onlyFiles: true,
|
|
2027
|
+
absolute: true
|
|
2028
|
+
});
|
|
2029
|
+
return Promise.all(
|
|
2030
|
+
entries.sort().map(async (entry) => ({
|
|
2031
|
+
path: toProjectReferencePath(stack, relative4(referencesRoot, entry)),
|
|
2032
|
+
content: await readFile3(entry, "utf8"),
|
|
2033
|
+
autoUpdate: false
|
|
2034
|
+
}))
|
|
2035
|
+
);
|
|
2036
|
+
}
|
|
2037
|
+
function toProjectReferencePath(stack, relativePath) {
|
|
2038
|
+
const normalized = relativePath.replaceAll("\\", "/");
|
|
2039
|
+
if (normalized === "tools-catalog.md") {
|
|
2040
|
+
return join13("docs/tools", stack, "README.md");
|
|
2041
|
+
}
|
|
2042
|
+
return join13("docs/tools", stack, normalized.replace(/^tools\//, ""));
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
// src/onboarding/rule-generator.ts
|
|
2046
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
2047
|
+
import { join as join14 } from "path";
|
|
2048
|
+
async function generateProjectRules(rules) {
|
|
2049
|
+
return Promise.all(
|
|
2050
|
+
rules.map(async (rule) => ({
|
|
2051
|
+
path: toProjectRulePath(rule.source),
|
|
2052
|
+
content: await readFile4(rule.path, "utf8"),
|
|
2053
|
+
autoUpdate: false
|
|
2054
|
+
}))
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
function toProjectRulePath(source) {
|
|
2058
|
+
const normalized = source.replace(/^domains\//, "");
|
|
2059
|
+
const [prefix, suffix] = normalized.split("/rules/");
|
|
2060
|
+
if (prefix === void 0 || suffix === void 0) {
|
|
2061
|
+
return join14("docs/rules", normalized);
|
|
2062
|
+
}
|
|
2063
|
+
const target = suffix.endsWith("/guide.md") ? suffix.replace("/guide.md", ".md") : suffix;
|
|
2064
|
+
return join14("docs/rules", prefix, target);
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
// src/onboarding/scaffold-generator.ts
|
|
2068
|
+
import { join as join15 } from "path";
|
|
2069
|
+
|
|
2070
|
+
// src/templates/context-builders/design-system.ts
|
|
2071
|
+
function buildDesignSystemContext(projectName) {
|
|
2072
|
+
return { projectName };
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// src/templates/context-builders/module-scaffold.ts
|
|
2076
|
+
function buildModuleScaffoldContext(moduleName) {
|
|
2077
|
+
return {
|
|
2078
|
+
moduleName,
|
|
2079
|
+
title: moduleName.replace(/-/g, " ")
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
// src/onboarding/scaffold-generator.ts
|
|
2084
|
+
var MODULE_TEMPLATE_TARGETS = [
|
|
2085
|
+
["summary.md.hbs", "index/summary.md"],
|
|
2086
|
+
["schema.md.hbs", "database/schema.md"],
|
|
2087
|
+
["indexes.md.hbs", "database/indexes.md"],
|
|
2088
|
+
["queries.md.hbs", "database/queries.md"],
|
|
2089
|
+
["data-volumes.md.hbs", "database/data-volumes.md"],
|
|
2090
|
+
["api-endpoints.md.hbs", "api/endpoints.md"],
|
|
2091
|
+
["api-schemas.md.hbs", "api/schemas.md"],
|
|
2092
|
+
["api-error-codes.md.hbs", "api/error-codes.md"],
|
|
2093
|
+
["integration-events.md.hbs", "integration/events.md"],
|
|
2094
|
+
["integration-contracts.md.hbs", "integration/contracts.md"],
|
|
2095
|
+
["error-catalog.md.hbs", "error-catalog.md"],
|
|
2096
|
+
["screens.md.hbs", "ui/screens.md"],
|
|
2097
|
+
["components.md.hbs", "ui/components.md"],
|
|
2098
|
+
["states.md.hbs", "ui/states.md"]
|
|
2099
|
+
];
|
|
2100
|
+
async function generateDocumentationScaffold(moduleNames = ["core"]) {
|
|
2101
|
+
const files = [];
|
|
2102
|
+
for (const moduleName of Array.from(new Set(moduleNames)).sort()) {
|
|
2103
|
+
files.push(...await generateModuleScaffold(moduleName));
|
|
2104
|
+
}
|
|
2105
|
+
const engine = new TemplateEngine();
|
|
2106
|
+
for (const [templateName, target] of DESIGN_SYSTEM_TEMPLATE_TARGETS) {
|
|
2107
|
+
files.push({
|
|
2108
|
+
path: target,
|
|
2109
|
+
content: await engine.render(
|
|
2110
|
+
join15(getRuntimeTemplatesRoot(), "design-system", templateName),
|
|
2111
|
+
buildDesignSystemContext("Paqad")
|
|
2112
|
+
),
|
|
2113
|
+
autoUpdate: true
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
files.push(
|
|
2117
|
+
{
|
|
2118
|
+
path: join15(PATHS.ARCHITECTURE_DIR, "overview.md"),
|
|
2119
|
+
content: "# Architecture Overview\n\n",
|
|
2120
|
+
autoUpdate: true
|
|
2121
|
+
},
|
|
2122
|
+
{
|
|
2123
|
+
path: join15(PATHS.ARCHITECTURE_DIR, "decisions.md"),
|
|
2124
|
+
content: "# Architecture Decisions\n\n",
|
|
2125
|
+
autoUpdate: true
|
|
2126
|
+
},
|
|
2127
|
+
{
|
|
2128
|
+
path: join15(PATHS.ARCHITECTURE_DIR, "patterns.md"),
|
|
2129
|
+
content: "# Architecture Patterns\n\n",
|
|
2130
|
+
autoUpdate: true
|
|
2131
|
+
},
|
|
2132
|
+
{
|
|
2133
|
+
path: join15(PATHS.BENCHMARKS_DIR, "index.md"),
|
|
2134
|
+
content: "# Benchmarks\n\n",
|
|
2135
|
+
autoUpdate: true
|
|
2136
|
+
},
|
|
2137
|
+
{
|
|
2138
|
+
path: join15(PATHS.TECH_DEBT_DIR, "index.md"),
|
|
2139
|
+
content: "# Technical Debt\n\n",
|
|
2140
|
+
autoUpdate: true
|
|
2141
|
+
}
|
|
2142
|
+
);
|
|
2143
|
+
for (const registry of REGISTRIES) {
|
|
2144
|
+
files.push({
|
|
2145
|
+
path: `docs/${registry}`,
|
|
2146
|
+
content: `# ${registry}
|
|
2147
|
+
`,
|
|
2148
|
+
autoUpdate: true
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
return files;
|
|
2152
|
+
}
|
|
2153
|
+
async function generateModuleScaffold(moduleName) {
|
|
2154
|
+
const engine = new TemplateEngine();
|
|
2155
|
+
const context = buildModuleScaffoldContext(moduleName);
|
|
2156
|
+
const files = [];
|
|
2157
|
+
for (const [templateName, relativeTarget] of MODULE_TEMPLATE_TARGETS) {
|
|
2158
|
+
files.push({
|
|
2159
|
+
path: join15(PATHS.MODULES_DIR, moduleName, relativeTarget),
|
|
2160
|
+
content: await engine.render(
|
|
2161
|
+
join15(getRuntimeTemplatesRoot(), "module-scaffold", templateName),
|
|
2162
|
+
context
|
|
2163
|
+
),
|
|
2164
|
+
autoUpdate: false
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
return files;
|
|
2168
|
+
}
|
|
2169
|
+
var DESIGN_SYSTEM_TEMPLATE_TARGETS = [
|
|
2170
|
+
["tokens.md.hbs", "docs/design-system/tokens.md"],
|
|
2171
|
+
["components.md.hbs", "docs/design-system/components.md"],
|
|
2172
|
+
["motion.md.hbs", "docs/design-system/motion.md"],
|
|
2173
|
+
["accessibility.md.hbs", "docs/design-system/accessibility.md"],
|
|
2174
|
+
["responsive.md.hbs", "docs/design-system/responsive.md"],
|
|
2175
|
+
["patterns.md.hbs", "docs/design-system/patterns.md"]
|
|
2176
|
+
];
|
|
2177
|
+
|
|
2178
|
+
// src/onboarding/orchestrator.ts
|
|
2179
|
+
var OnboardingOrchestrator = class {
|
|
2180
|
+
async run(options) {
|
|
2181
|
+
const detector = new Detector();
|
|
2182
|
+
const detection = await detector.detect(options.projectRoot);
|
|
2183
|
+
const selections = await resolveSelections(detection, options.selections);
|
|
2184
|
+
const runtimeRoot = options.runtimeRoot ?? getRuntimeRoot();
|
|
2185
|
+
const resolver = new Resolver({ runtimeRoot });
|
|
2186
|
+
const resolved = await resolver.resolve(selections);
|
|
2187
|
+
const adapters = options.adapters ?? ["claude-code", "codex-cli", "gemini-cli"];
|
|
2188
|
+
const profile = buildProjectProfile(selections, options.profileOverrides);
|
|
2189
|
+
const validator = new SchemaValidator();
|
|
2190
|
+
const validation = validator.validate("project-profile", profile);
|
|
2191
|
+
const modules = await discoverModules(options.projectRoot);
|
|
2192
|
+
if (!validation.valid) {
|
|
2193
|
+
throw new Error(validation.errors.map((error) => error.message).join("; "));
|
|
2194
|
+
}
|
|
2195
|
+
const generatedFiles = [];
|
|
2196
|
+
for (const adapterType of adapters) {
|
|
2197
|
+
const adapter = AdapterFactory.create(adapterType);
|
|
2198
|
+
generatedFiles.push(
|
|
2199
|
+
...await adapter.generateConfig({
|
|
2200
|
+
frameworkPath: ".agency/framework-path.txt",
|
|
2201
|
+
rulesPath: "docs/rules",
|
|
2202
|
+
projectRoot: options.projectRoot
|
|
2203
|
+
}),
|
|
2204
|
+
...await adapter.generateSkills(resolved.skills),
|
|
2205
|
+
...await adapter.generateAgents(resolved.agents),
|
|
2206
|
+
...await adapter.installHooks(resolved.hooks),
|
|
2207
|
+
...await adapter.installMcp(resolved.mcpConfigs, profile),
|
|
2208
|
+
...await adapter.configureCaching(profile),
|
|
2209
|
+
...await adapter.configureMemory(profile)
|
|
2210
|
+
);
|
|
2211
|
+
}
|
|
2212
|
+
generatedFiles.push(...await generateProjectRules(resolved.rules));
|
|
2213
|
+
generatedFiles.push(...await generateReferenceGuides(runtimeRoot, selections));
|
|
2214
|
+
generatedFiles.push(...await generateDocumentationScaffold(modules));
|
|
2215
|
+
generatedFiles.push(...await generateInitialRegistries(options.projectRoot));
|
|
2216
|
+
generatedFiles.push(...await new RunnerScriptGenerator().generate(profile));
|
|
2217
|
+
const writeResult = writeGeneratedFiles(options.projectRoot, generatedFiles);
|
|
2218
|
+
writeProjectProfile(options.projectRoot, profile);
|
|
2219
|
+
writeDetectionReport(options.projectRoot, detection);
|
|
2220
|
+
writeFrameworkMetadata(options.projectRoot, VERSION);
|
|
2221
|
+
const manifestPath = writeOnboardingManifest(options.projectRoot, {
|
|
2222
|
+
framework_version: VERSION,
|
|
2223
|
+
adapter: adapters[0],
|
|
2224
|
+
project_root: options.projectRoot,
|
|
2225
|
+
profile,
|
|
2226
|
+
detected: detection,
|
|
2227
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2228
|
+
generated_artifacts: generatedFiles.map((file) => ({
|
|
2229
|
+
path: file.path,
|
|
2230
|
+
auto_update: file.autoUpdate,
|
|
2231
|
+
executable: file.executable
|
|
2232
|
+
}))
|
|
2233
|
+
});
|
|
2234
|
+
return {
|
|
2235
|
+
adapter: adapters[0],
|
|
2236
|
+
generated_files: writeResult.written,
|
|
2237
|
+
detected_modules: modules,
|
|
2238
|
+
runtime_root: runtimeRoot,
|
|
2239
|
+
manifest_path: manifestPath,
|
|
2240
|
+
warnings: writeResult.skipped
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
};
|
|
2244
|
+
function buildProjectProfile(selections, overrides) {
|
|
2245
|
+
return {
|
|
2246
|
+
project: {
|
|
2247
|
+
name: overrides?.project?.name ?? "paqad project",
|
|
2248
|
+
id: overrides?.project?.id ?? "paqad-project",
|
|
2249
|
+
description: overrides?.project?.description ?? "Generated by paqad-ai"
|
|
2250
|
+
},
|
|
2251
|
+
routing: selections,
|
|
2252
|
+
commands: overrides?.commands ?? {
|
|
2253
|
+
install: "pnpm install",
|
|
2254
|
+
dev: "pnpm dev",
|
|
2255
|
+
test: "pnpm test",
|
|
2256
|
+
test_single: "pnpm test -- <pattern>",
|
|
2257
|
+
lint: "pnpm lint",
|
|
2258
|
+
format: "pnpm format",
|
|
2259
|
+
migrate: 'echo "configure migrate command"',
|
|
2260
|
+
build: "pnpm build"
|
|
2261
|
+
},
|
|
2262
|
+
strictness: overrides?.strictness ?? {
|
|
2263
|
+
full_lane_default: false,
|
|
2264
|
+
require_adversarial_review: true,
|
|
2265
|
+
block_on_stale_docs: true,
|
|
2266
|
+
require_db_review_for_migrations: true
|
|
2267
|
+
},
|
|
2268
|
+
compliance_packs: overrides?.compliance_packs ?? [],
|
|
2269
|
+
features: overrides?.features ?? {
|
|
2270
|
+
spec_only_mode: false,
|
|
2271
|
+
market_research: false,
|
|
2272
|
+
design_research: false,
|
|
2273
|
+
team_agents: true,
|
|
2274
|
+
supply_chain_governance: false,
|
|
2275
|
+
ai_governance: false
|
|
2276
|
+
},
|
|
2277
|
+
mcp: overrides?.mcp ?? { servers: [] },
|
|
2278
|
+
model_routing: overrides?.model_routing ?? {
|
|
2279
|
+
default_model: "gpt-5",
|
|
2280
|
+
reasoning_model: "gpt-5",
|
|
2281
|
+
fast_model: "gpt-5-mini"
|
|
2282
|
+
},
|
|
2283
|
+
research: overrides?.research ?? { depth: "standard" },
|
|
2284
|
+
efficiency: overrides?.efficiency ?? {
|
|
2285
|
+
context_hit_rate_target: 0.7,
|
|
2286
|
+
skill_caching: true,
|
|
2287
|
+
differential_refresh: true,
|
|
2288
|
+
mcp_first: true
|
|
2289
|
+
},
|
|
2290
|
+
escalation: overrides?.escalation ?? {
|
|
2291
|
+
destructive_operations: "block",
|
|
2292
|
+
risky_migrations: "warn",
|
|
2293
|
+
security_findings: "block",
|
|
2294
|
+
db_row_threshold: 1e4
|
|
2295
|
+
},
|
|
2296
|
+
custom: overrides?.custom ?? {
|
|
2297
|
+
classification_dimensions: [],
|
|
2298
|
+
verification_plugins: [],
|
|
2299
|
+
escalation_rules: []
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
// src/pipeline/lane-runner.ts
|
|
2305
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
2306
|
+
import { dirname as dirname5, join as join16 } from "path";
|
|
2307
|
+
|
|
2308
|
+
// src/pipeline/phases/shared.ts
|
|
2309
|
+
function createPassResult(phase, summary, context) {
|
|
2310
|
+
return {
|
|
2311
|
+
phase,
|
|
2312
|
+
status: "pass",
|
|
2313
|
+
summary,
|
|
2314
|
+
artifacts: [`handoff:${context.phases.length + 1}`]
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
// src/pipeline/phases/classify.ts
|
|
2319
|
+
var ClassifyPhase = class {
|
|
2320
|
+
phase = "request-classification";
|
|
2321
|
+
async execute(context) {
|
|
2322
|
+
return createPassResult(this.phase, "Classification confirmed", context);
|
|
2323
|
+
}
|
|
2324
|
+
};
|
|
2325
|
+
|
|
2326
|
+
// src/pipeline/phases/analysis.ts
|
|
2327
|
+
var AnalysisPhase = class {
|
|
2328
|
+
phase = "analysis";
|
|
2329
|
+
async execute(context) {
|
|
2330
|
+
return createPassResult(this.phase, "Analysis roles completed", context);
|
|
2331
|
+
}
|
|
2332
|
+
};
|
|
2333
|
+
|
|
2334
|
+
// src/pipeline/phases/doc-update.ts
|
|
2335
|
+
var DocumentationUpdatePhase = class {
|
|
2336
|
+
phase = "documentation-update";
|
|
2337
|
+
async execute(context) {
|
|
2338
|
+
return createPassResult(this.phase, "Canonical docs updated", context);
|
|
2339
|
+
}
|
|
2340
|
+
};
|
|
2341
|
+
|
|
2342
|
+
// src/pipeline/phases/flow-writing.ts
|
|
2343
|
+
var FlowWritingPhase = class {
|
|
2344
|
+
phase = "user-flow";
|
|
2345
|
+
async execute(context) {
|
|
2346
|
+
return createPassResult(this.phase, "User flows documented", context);
|
|
2347
|
+
}
|
|
2348
|
+
};
|
|
2349
|
+
|
|
2350
|
+
// src/pipeline/phases/impl-review.ts
|
|
2351
|
+
function selectReviewTier(_classification, lane) {
|
|
2352
|
+
if (lane === "fast") {
|
|
2353
|
+
return "spot-check";
|
|
2354
|
+
}
|
|
2355
|
+
if (lane === "graduated") {
|
|
2356
|
+
return "standard";
|
|
2357
|
+
}
|
|
2358
|
+
return "full";
|
|
2359
|
+
}
|
|
2360
|
+
function selectReviewMode(isReReview, changePercentage) {
|
|
2361
|
+
if (!isReReview) {
|
|
2362
|
+
return "fresh";
|
|
2363
|
+
}
|
|
2364
|
+
if (changePercentage > 0.6) {
|
|
2365
|
+
return "fresh";
|
|
2366
|
+
}
|
|
2367
|
+
return "diff";
|
|
2368
|
+
}
|
|
2369
|
+
var ImplementationReviewPhase = class {
|
|
2370
|
+
phase = "implementation-review";
|
|
2371
|
+
async execute(context) {
|
|
2372
|
+
const tier = selectReviewTier(context.classification, context.lane);
|
|
2373
|
+
const mode = selectReviewMode(false, 0);
|
|
2374
|
+
return createPassResult(this.phase, `Implementation review passed (${tier}, ${mode})`, context);
|
|
2375
|
+
}
|
|
2376
|
+
};
|
|
2377
|
+
|
|
2378
|
+
// src/pipeline/phases/implementation.ts
|
|
2379
|
+
var ImplementationPhase = class {
|
|
2380
|
+
phase = "implementation";
|
|
2381
|
+
async execute(context) {
|
|
2382
|
+
return createPassResult(this.phase, "Implementation completed", context);
|
|
2383
|
+
}
|
|
2384
|
+
};
|
|
2385
|
+
|
|
2386
|
+
// src/pipeline/phases/load-docs.ts
|
|
2387
|
+
var LoadDocsPhase = class {
|
|
2388
|
+
phase = "docs-first-load";
|
|
2389
|
+
async execute(context) {
|
|
2390
|
+
return createPassResult(this.phase, "Docs-first context prepared", context);
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2393
|
+
|
|
2394
|
+
// src/pipeline/phases/spec-review.ts
|
|
2395
|
+
function selectReviewTier2(_classification, lane) {
|
|
2396
|
+
if (lane === "fast") {
|
|
2397
|
+
return "spot-check";
|
|
2398
|
+
}
|
|
2399
|
+
if (lane === "graduated") {
|
|
2400
|
+
return "standard";
|
|
2401
|
+
}
|
|
2402
|
+
return "full";
|
|
2403
|
+
}
|
|
2404
|
+
function selectReviewMode2(isReReview, changePercentage) {
|
|
2405
|
+
if (!isReReview) {
|
|
2406
|
+
return "fresh";
|
|
2407
|
+
}
|
|
2408
|
+
if (changePercentage > 0.6) {
|
|
2409
|
+
return "fresh";
|
|
2410
|
+
}
|
|
2411
|
+
return "diff";
|
|
2412
|
+
}
|
|
2413
|
+
var SpecReviewPhase = class {
|
|
2414
|
+
phase = "spec-review";
|
|
2415
|
+
async execute(context) {
|
|
2416
|
+
const tier = selectReviewTier2(context.classification, context.lane);
|
|
2417
|
+
const mode = selectReviewMode2(false, 0);
|
|
2418
|
+
return createPassResult(this.phase, `Spec review passed (${tier}, ${mode})`, context);
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
|
|
2422
|
+
// src/pipeline/phases/spec-writing.ts
|
|
2423
|
+
var SpecWritingPhase = class {
|
|
2424
|
+
phase = "specification";
|
|
2425
|
+
async execute(context) {
|
|
2426
|
+
return createPassResult(this.phase, "Specification written", context);
|
|
2427
|
+
}
|
|
2428
|
+
};
|
|
2429
|
+
|
|
2430
|
+
// src/pipeline/phases/story-planning.ts
|
|
2431
|
+
var StoryPlanningPhase = class {
|
|
2432
|
+
phase = "sequence-planning";
|
|
2433
|
+
async execute(context) {
|
|
2434
|
+
return createPassResult(this.phase, "Story sequence planned", context);
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
|
|
2438
|
+
// src/pipeline/phases/verification.ts
|
|
2439
|
+
var VerificationPhase = class {
|
|
2440
|
+
phase = "verification-gates";
|
|
2441
|
+
async execute(context) {
|
|
2442
|
+
return createPassResult(this.phase, "Verification gates passed", context);
|
|
2443
|
+
}
|
|
2444
|
+
};
|
|
2445
|
+
|
|
2446
|
+
// src/pipeline/lane-runner.ts
|
|
2447
|
+
var DEFAULT_PHASES = {
|
|
2448
|
+
"request-classification": new ClassifyPhase(),
|
|
2449
|
+
"docs-first-load": new LoadDocsPhase(),
|
|
2450
|
+
analysis: new AnalysisPhase(),
|
|
2451
|
+
"sequence-planning": new StoryPlanningPhase(),
|
|
2452
|
+
specification: new SpecWritingPhase(),
|
|
2453
|
+
"user-flow": new FlowWritingPhase(),
|
|
2454
|
+
"spec-review": new SpecReviewPhase(),
|
|
2455
|
+
implementation: new ImplementationPhase(),
|
|
2456
|
+
"implementation-review": new ImplementationReviewPhase(),
|
|
2457
|
+
"verification-gates": new VerificationPhase(),
|
|
2458
|
+
"documentation-update": new DocumentationUpdatePhase()
|
|
2459
|
+
};
|
|
2460
|
+
|
|
2461
|
+
// src/skills/cache-manager.ts
|
|
2462
|
+
import { createHash } from "crypto";
|
|
2463
|
+
import { mkdir as mkdir3, readFile as readFile5, readdir as readdir2, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
2464
|
+
import { join as join17 } from "path";
|
|
2465
|
+
import fg4 from "fast-glob";
|
|
2466
|
+
|
|
2467
|
+
// src/skills/frontmatter-parser.ts
|
|
2468
|
+
import YAML3 from "yaml";
|
|
2469
|
+
|
|
2470
|
+
// src/skills/loader.ts
|
|
2471
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
2472
|
+
|
|
2473
|
+
// src/update/updater.ts
|
|
2474
|
+
import { chmodSync as chmodSync3, existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
2475
|
+
import { mkdtemp, readdir as readdir3, readFile as readFile7, rm as rm2 } from "fs/promises";
|
|
2476
|
+
import { tmpdir } from "os";
|
|
2477
|
+
import { dirname as dirname6, join as join18, relative as relative5 } from "path";
|
|
2478
|
+
import YAML4 from "yaml";
|
|
2479
|
+
var FrameworkUpdater = class {
|
|
2480
|
+
constructor(options = {}) {
|
|
2481
|
+
this.options = options;
|
|
2482
|
+
}
|
|
2483
|
+
async run(projectRoot) {
|
|
2484
|
+
const previousVersion = readText(join18(projectRoot, PATHS.FRAMEWORK_VERSION));
|
|
2485
|
+
const manifest = readManifest(projectRoot);
|
|
2486
|
+
const artifactPolicy = new Map(
|
|
2487
|
+
manifest?.generated_artifacts.map((artifact) => [artifact.path, artifact.auto_update]) ?? []
|
|
2488
|
+
);
|
|
2489
|
+
const candidates = await this.getCandidates(projectRoot);
|
|
2490
|
+
const regenerated = [];
|
|
2491
|
+
const skipped = [];
|
|
2492
|
+
const newScripts = [];
|
|
2493
|
+
for (const candidate of candidates) {
|
|
2494
|
+
const target = join18(projectRoot, candidate.path);
|
|
2495
|
+
const existed = existsSync6(target);
|
|
2496
|
+
const autoUpdate = artifactPolicy.get(candidate.path) ?? candidate.autoUpdate;
|
|
2497
|
+
if (existed && autoUpdate === false) {
|
|
2498
|
+
skipped.push({
|
|
2499
|
+
path: candidate.path,
|
|
2500
|
+
before: readFileSync5(target, "utf8"),
|
|
2501
|
+
after: candidate.content
|
|
2502
|
+
});
|
|
2503
|
+
continue;
|
|
2504
|
+
}
|
|
2505
|
+
mkdirSync4(dirname6(target), { recursive: true });
|
|
2506
|
+
writeFileSync3(target, candidate.content);
|
|
2507
|
+
if (candidate.executable === true) {
|
|
2508
|
+
chmodSync3(target, 493);
|
|
2509
|
+
}
|
|
2510
|
+
regenerated.push(candidate.path);
|
|
2511
|
+
if (!existed && candidate.path.startsWith("scripts/")) {
|
|
2512
|
+
newScripts.push(candidate.path);
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
mkdirSync4(dirname6(join18(projectRoot, PATHS.FRAMEWORK_VERSION)), { recursive: true });
|
|
2516
|
+
writeFileSync3(join18(projectRoot, PATHS.FRAMEWORK_VERSION), `${VERSION}
|
|
2517
|
+
`);
|
|
2518
|
+
return {
|
|
2519
|
+
previous_version: previousVersion,
|
|
2520
|
+
target_version: VERSION,
|
|
2521
|
+
regenerated,
|
|
2522
|
+
skipped,
|
|
2523
|
+
deprecated: [],
|
|
2524
|
+
new_mcp_servers: [],
|
|
2525
|
+
new_scripts: newScripts.sort()
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
async getCandidates(projectRoot) {
|
|
2529
|
+
if (this.options.generateCandidates) {
|
|
2530
|
+
return this.options.generateCandidates(projectRoot);
|
|
2531
|
+
}
|
|
2532
|
+
const profile = readProfile(projectRoot);
|
|
2533
|
+
if (profile === null) {
|
|
2534
|
+
throw new Error("Cannot update framework-managed artifacts without a project profile");
|
|
2535
|
+
}
|
|
2536
|
+
const tempRoot = await mkdtemp(join18(tmpdir(), "paqad-ai-update-"));
|
|
2537
|
+
try {
|
|
2538
|
+
const result = await new OnboardingOrchestrator().run({
|
|
2539
|
+
projectRoot: tempRoot,
|
|
2540
|
+
selections: profile.routing,
|
|
2541
|
+
profileOverrides: profile
|
|
2542
|
+
});
|
|
2543
|
+
const files = await collectFiles(tempRoot, result.generated_files);
|
|
2544
|
+
return files;
|
|
2545
|
+
} finally {
|
|
2546
|
+
await rm2(tempRoot, { recursive: true, force: true });
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
};
|
|
2550
|
+
function readManifest(projectRoot) {
|
|
2551
|
+
const path = join18(projectRoot, PATHS.ONBOARDING_MANIFEST);
|
|
2552
|
+
if (!existsSync6(path)) {
|
|
2553
|
+
return null;
|
|
2554
|
+
}
|
|
2555
|
+
return JSON.parse(readFileSync5(path, "utf8"));
|
|
2556
|
+
}
|
|
2557
|
+
function readProfile(projectRoot) {
|
|
2558
|
+
const path = join18(projectRoot, PATHS.PROJECT_PROFILE);
|
|
2559
|
+
if (!existsSync6(path)) {
|
|
2560
|
+
return null;
|
|
2561
|
+
}
|
|
2562
|
+
return YAML4.parse(readFileSync5(path, "utf8"));
|
|
2563
|
+
}
|
|
2564
|
+
function readText(path) {
|
|
2565
|
+
if (!existsSync6(path)) {
|
|
2566
|
+
return null;
|
|
2567
|
+
}
|
|
2568
|
+
return readFileSync5(path, "utf8").trim();
|
|
2569
|
+
}
|
|
2570
|
+
async function collectFiles(root, generated) {
|
|
2571
|
+
const paths = generated.length > 0 ? generated : await walk2(root);
|
|
2572
|
+
return Promise.all(
|
|
2573
|
+
paths.map(async (file) => ({
|
|
2574
|
+
path: file,
|
|
2575
|
+
content: await readFile7(join18(root, file), "utf8"),
|
|
2576
|
+
autoUpdate: true,
|
|
2577
|
+
executable: file.startsWith("scripts/")
|
|
2578
|
+
}))
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
async function walk2(root, current = root) {
|
|
2582
|
+
const entries = await readdir3(current, { withFileTypes: true });
|
|
2583
|
+
const files = [];
|
|
2584
|
+
for (const entry of entries) {
|
|
2585
|
+
const absolute = join18(current, entry.name);
|
|
2586
|
+
if (entry.isDirectory()) {
|
|
2587
|
+
files.push(...await walk2(root, absolute));
|
|
2588
|
+
continue;
|
|
2589
|
+
}
|
|
2590
|
+
files.push(relative5(root, absolute));
|
|
2591
|
+
}
|
|
2592
|
+
return files.sort();
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
// src/verification/gates/documentation-freshness.ts
|
|
2596
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2597
|
+
import { join as join19 } from "path";
|
|
2598
|
+
var STALENESS_WINDOW_MS2 = 1e3 * 60 * 60 * 24 * 7;
|
|
2599
|
+
|
|
2600
|
+
// src/index.ts
|
|
2601
|
+
var VERSION = "0.0.1";
|
|
2602
|
+
|
|
2603
|
+
// src/cli/commands/doctor.ts
|
|
2604
|
+
import { Command } from "commander";
|
|
2605
|
+
function createDoctorCommand() {
|
|
2606
|
+
return new Command("doctor").description("Check framework health and suggest fixes").option("--project-root <path>", "Project root", process.cwd()).action(async (options) => {
|
|
2607
|
+
const report = await new HealthChecker().run(options.projectRoot);
|
|
2608
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2609
|
+
process.exitCode = report.overall_status === "fail" ? 1 : 0;
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
// src/cli/commands/install.ts
|
|
2614
|
+
import { Command as Command2 } from "commander";
|
|
2615
|
+
function createInstallCommand() {
|
|
2616
|
+
return new Command2("install").description("Bootstrap the framework into the current project").option("--project-root <path>", "Project root", process.cwd()).action((options) => {
|
|
2617
|
+
const result = bootstrapFramework(options.projectRoot);
|
|
2618
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
// src/cli/commands/onboard.ts
|
|
2623
|
+
import { Command as Command3 } from "commander";
|
|
2624
|
+
function createOnboardCommand() {
|
|
2625
|
+
return new Command3("onboard").description("Full project onboarding").option("--project-root <path>", "Project root", process.cwd()).option("--domain <domain>", "Force the target domain").option("--stack <stack>", "Force the target stack").option("--capability <capability...>", "Add one or more capabilities").action(
|
|
2626
|
+
async (options) => {
|
|
2627
|
+
const orchestrator = new OnboardingOrchestrator();
|
|
2628
|
+
await orchestrator.run({
|
|
2629
|
+
projectRoot: options.projectRoot,
|
|
2630
|
+
selections: options.domain || options.stack || options.capability ? {
|
|
2631
|
+
domain: options.domain,
|
|
2632
|
+
stack: options.stack,
|
|
2633
|
+
capabilities: options.capability
|
|
2634
|
+
} : void 0
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
);
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
// src/cli/commands/refresh.ts
|
|
2641
|
+
import { Command as Command4 } from "commander";
|
|
2642
|
+
function createRefreshCommand() {
|
|
2643
|
+
return new Command4("refresh").description("Refresh indexes and registries").action(() => {
|
|
2644
|
+
});
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
// src/cli/commands/update.ts
|
|
2648
|
+
import { Command as Command5 } from "commander";
|
|
2649
|
+
function createUpdateCommand() {
|
|
2650
|
+
return new Command5("update").description("Update framework-managed artifacts").option("--project-root <path>", "Project root", process.cwd()).action(async (options) => {
|
|
2651
|
+
const report = await new FrameworkUpdater().run(options.projectRoot);
|
|
2652
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2656
|
+
// src/cli/program.ts
|
|
2657
|
+
function createProgram() {
|
|
2658
|
+
const program = new Command6();
|
|
2659
|
+
program.name("paqad-ai").description("AI Agency Framework for software agencies").version(VERSION).showSuggestionAfterError(true);
|
|
2660
|
+
program.addCommand(createInstallCommand());
|
|
2661
|
+
program.addCommand(createDoctorCommand());
|
|
2662
|
+
program.addCommand(createOnboardCommand());
|
|
2663
|
+
program.addCommand(createRefreshCommand());
|
|
2664
|
+
program.addCommand(createUpdateCommand());
|
|
2665
|
+
return program;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
// src/cli/index.ts
|
|
2669
|
+
function getCliBanner() {
|
|
2670
|
+
return "paqad-ai";
|
|
2671
|
+
}
|
|
2672
|
+
async function runCli(argv = process.argv) {
|
|
2673
|
+
const program = createProgram();
|
|
2674
|
+
await program.parseAsync(argv);
|
|
2675
|
+
}
|
|
2676
|
+
function shouldRunFromCommandLine(importMetaUrl, argvEntry) {
|
|
2677
|
+
const entrypoint = argvToEntrypoint(argvEntry);
|
|
2678
|
+
return entrypoint !== void 0 && importMetaUrl === entrypoint;
|
|
2679
|
+
}
|
|
2680
|
+
if (shouldRunFromCommandLine(import.meta.url, process.argv[1])) {
|
|
2681
|
+
await runCli();
|
|
2682
|
+
}
|
|
2683
|
+
function argvToEntrypoint(value) {
|
|
2684
|
+
if (value === void 0) {
|
|
2685
|
+
return void 0;
|
|
2686
|
+
}
|
|
2687
|
+
try {
|
|
2688
|
+
return pathToFileURL(realpathSync(value)).href;
|
|
2689
|
+
} catch {
|
|
2690
|
+
return pathToFileURL(value).href;
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
export {
|
|
2694
|
+
argvToEntrypoint,
|
|
2695
|
+
getCliBanner,
|
|
2696
|
+
runCli,
|
|
2697
|
+
shouldRunFromCommandLine
|
|
2698
|
+
};
|
|
2699
|
+
//# sourceMappingURL=index.js.map
|