tevra-design-kit 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/PROVENANCE.md +18 -0
- package/README.md +251 -2
- package/SKILL.md +20 -0
- package/THIRD_PARTY_NOTICES.md +23 -0
- package/adapters/claude-code/CLAUDE.md +14 -0
- package/adapters/codex/AGENTS.md +5 -0
- package/adapters/cursor/README.md +5 -0
- package/adapters/gemini-cli/README.md +5 -0
- package/adapters/github-copilot/README.md +5 -0
- package/adapters/kiro/README.md +5 -0
- package/adapters/opencode/README.md +5 -0
- package/adapters/windsurf/README.md +5 -0
- package/dist/packages/cli/src/arguments.d.ts +9 -0
- package/dist/packages/cli/src/arguments.js +58 -0
- package/dist/packages/cli/src/arguments.js.map +1 -0
- package/dist/packages/cli/src/index.d.ts +2 -0
- package/dist/packages/cli/src/index.js +132 -0
- package/dist/packages/cli/src/index.js.map +1 -0
- package/dist/packages/cli/src/project-commands.d.ts +4 -0
- package/dist/packages/cli/src/project-commands.js +137 -0
- package/dist/packages/cli/src/project-commands.js.map +1 -0
- package/dist/packages/cli/src/run-command.d.ts +2 -0
- package/dist/packages/cli/src/run-command.js +35 -0
- package/dist/packages/cli/src/run-command.js.map +1 -0
- package/dist/packages/cli/src/visual-commands.d.ts +5 -0
- package/dist/packages/cli/src/visual-commands.js +73 -0
- package/dist/packages/cli/src/visual-commands.js.map +1 -0
- package/dist/packages/cli/src/workflow-commands.d.ts +2 -0
- package/dist/packages/cli/src/workflow-commands.js +73 -0
- package/dist/packages/cli/src/workflow-commands.js.map +1 -0
- package/dist/packages/contracts/src/index.d.ts +181 -0
- package/dist/packages/contracts/src/index.js +3 -0
- package/dist/packages/contracts/src/index.js.map +1 -0
- package/dist/packages/core/src/analyze.d.ts +6 -0
- package/dist/packages/core/src/analyze.js +46 -0
- package/dist/packages/core/src/analyze.js.map +1 -0
- package/dist/packages/core/src/config.d.ts +4 -0
- package/dist/packages/core/src/config.js +39 -0
- package/dist/packages/core/src/config.js.map +1 -0
- package/dist/packages/core/src/install.d.ts +13 -0
- package/dist/packages/core/src/install.js +69 -0
- package/dist/packages/core/src/install.js.map +1 -0
- package/dist/packages/core/src/router.d.ts +12 -0
- package/dist/packages/core/src/router.js +90 -0
- package/dist/packages/core/src/router.js.map +1 -0
- package/dist/packages/core/src/search.d.ts +12 -0
- package/dist/packages/core/src/search.js +31 -0
- package/dist/packages/core/src/search.js.map +1 -0
- package/dist/packages/core/src/skills.d.ts +7 -0
- package/dist/packages/core/src/skills.js +36 -0
- package/dist/packages/core/src/skills.js.map +1 -0
- package/dist/packages/core/src/workflow.d.ts +3 -0
- package/dist/packages/core/src/workflow.js +73 -0
- package/dist/packages/core/src/workflow.js.map +1 -0
- package/dist/packages/design-context/src/index.d.ts +5 -0
- package/dist/packages/design-context/src/index.js +41 -0
- package/dist/packages/design-context/src/index.js.map +1 -0
- package/dist/packages/design-context/src/migrate.d.ts +2 -0
- package/dist/packages/design-context/src/migrate.js +27 -0
- package/dist/packages/design-context/src/migrate.js.map +1 -0
- package/dist/packages/design-context/src/tokens.d.ts +8 -0
- package/dist/packages/design-context/src/tokens.js +65 -0
- package/dist/packages/design-context/src/tokens.js.map +1 -0
- package/dist/packages/design-context/src/validation.d.ts +1 -0
- package/dist/packages/design-context/src/validation.js +22 -0
- package/dist/packages/design-context/src/validation.js.map +1 -0
- package/dist/packages/design-memory/src/index.d.ts +16 -0
- package/dist/packages/design-memory/src/index.js +47 -0
- package/dist/packages/design-memory/src/index.js.map +1 -0
- package/dist/packages/filesystem/src/index.d.ts +8 -0
- package/dist/packages/filesystem/src/index.js +83 -0
- package/dist/packages/filesystem/src/index.js.map +1 -0
- package/dist/packages/filesystem/src/resources.d.ts +1 -0
- package/dist/packages/filesystem/src/resources.js +13 -0
- package/dist/packages/filesystem/src/resources.js.map +1 -0
- package/dist/packages/performance-qa/src/index.d.ts +24 -0
- package/dist/packages/performance-qa/src/index.js +77 -0
- package/dist/packages/performance-qa/src/index.js.map +1 -0
- package/dist/packages/quality-engine/src/html.d.ts +2 -0
- package/dist/packages/quality-engine/src/html.js +15 -0
- package/dist/packages/quality-engine/src/html.js.map +1 -0
- package/dist/packages/quality-engine/src/index.d.ts +3 -0
- package/dist/packages/quality-engine/src/index.js +45 -0
- package/dist/packages/quality-engine/src/index.js.map +1 -0
- package/dist/packages/registry/src/index.d.ts +49 -0
- package/dist/packages/registry/src/index.js +132 -0
- package/dist/packages/registry/src/index.js.map +1 -0
- package/dist/packages/visual-testing/src/capture.d.ts +12 -0
- package/dist/packages/visual-testing/src/capture.js +174 -0
- package/dist/packages/visual-testing/src/capture.js.map +1 -0
- package/dist/packages/visual-testing/src/checks.d.ts +3 -0
- package/dist/packages/visual-testing/src/checks.js +17 -0
- package/dist/packages/visual-testing/src/checks.js.map +1 -0
- package/dist/packages/visual-testing/src/compare.d.ts +12 -0
- package/dist/packages/visual-testing/src/compare.js +85 -0
- package/dist/packages/visual-testing/src/compare.js.map +1 -0
- package/dist/packages/visual-testing/src/types.d.ts +49 -0
- package/dist/packages/visual-testing/src/types.js +2 -0
- package/dist/packages/visual-testing/src/types.js.map +1 -0
- package/dist/packages/workflow-engine/src/index.d.ts +11 -0
- package/dist/packages/workflow-engine/src/index.js +51 -0
- package/dist/packages/workflow-engine/src/index.js.map +1 -0
- package/docs/agents.md +36 -0
- package/docs/architecture.md +86 -0
- package/docs/configuration.md +109 -0
- package/docs/contributing.md +75 -0
- package/docs/security.md +42 -0
- package/docs/usage.md +142 -0
- package/package.json +85 -4
- package/references/accessibility/manual-checklist.md +3 -0
- package/references/color-systems/semantic-color.md +3 -0
- package/references/design-principles/decision-making.md +3 -0
- package/references/frameworks/nextjs.md +3 -0
- package/references/frameworks/react-native-expo.md +3 -0
- package/references/frameworks/react.md +3 -0
- package/references/frameworks/sveltekit.md +3 -0
- package/references/frameworks/vue.md +3 -0
- package/references/typography/scales.md +3 -0
- package/references/ux-patterns/README.md +7 -0
- package/references/ux-patterns/UPSTREAM-LICENSE.txt +21 -0
- package/references/ux-patterns/catalog.json +221 -0
- package/registry/capability-ownership.json +191 -0
- package/registry/skills.json +764 -0
- package/registry/sources.json +159 -0
- package/rubrics/quality-rubric.md +24 -0
- package/schemas/capability-ownership.schema.json +69 -0
- package/schemas/capture-manifest.schema.json +209 -0
- package/schemas/checks.schema.json +83 -0
- package/schemas/comparison.schema.json +154 -0
- package/schemas/config.schema.json +297 -0
- package/schemas/design-context.schema.json +651 -0
- package/schemas/design-context.v2.schema.json +395 -0
- package/schemas/handoff.schema.json +135 -0
- package/schemas/legacy/design-context.master-v1.schema.json +167 -0
- package/schemas/legacy/design-context.v1.schema.json +36 -0
- package/schemas/memory.schema.json +69 -0
- package/schemas/plan.schema.json +139 -0
- package/schemas/quality-report.schema.json +151 -0
- package/schemas/skill-registry.schema.json +222 -0
- package/schemas/sources.schema.json +129 -0
- package/skills/accessibility-qa/LICENSE +21 -0
- package/skills/accessibility-qa/SKILL.md +56 -0
- package/skills/accessibility-qa/references/guide.md +3 -0
- package/skills/browser-testing/LICENSE +21 -0
- package/skills/browser-testing/SKILL.md +35 -0
- package/skills/browser-testing/references/guide.md +11 -0
- package/skills/design-direction/LICENSE +21 -0
- package/skills/design-direction/SKILL.md +56 -0
- package/skills/design-direction/references/guide.md +3 -0
- package/skills/design-research/LICENSE +21 -0
- package/skills/design-research/SKILL.md +56 -0
- package/skills/design-research/references/guide.md +3 -0
- package/skills/design-system/LICENSE +21 -0
- package/skills/design-system/SKILL.md +56 -0
- package/skills/design-system/references/guide.md +3 -0
- package/skills/frontend-review/LICENSE +21 -0
- package/skills/frontend-review/SKILL.md +35 -0
- package/skills/frontend-review/references/guide.md +11 -0
- package/skills/interaction-motion/LICENSE +21 -0
- package/skills/interaction-motion/SKILL.md +56 -0
- package/skills/interaction-motion/references/guide.md +3 -0
- package/skills/performance-qa/LICENSE +21 -0
- package/skills/performance-qa/SKILL.md +35 -0
- package/skills/performance-qa/references/guide.md +9 -0
- package/skills/responsive-design/LICENSE +21 -0
- package/skills/responsive-design/SKILL.md +56 -0
- package/skills/responsive-design/references/guide.md +3 -0
- package/skills/ui-builder/LICENSE +21 -0
- package/skills/ui-builder/SKILL.md +58 -0
- package/skills/ui-builder/references/guide.md +3 -0
- package/skills/ui-builder/references/nextjs.md +3 -0
- package/skills/ui-builder/references/react-native-expo.md +3 -0
- package/skills/ui-builder/references/react.md +3 -0
- package/skills/ui-builder/references/sveltekit.md +3 -0
- package/skills/ui-builder/references/vue.md +3 -0
- package/skills/ui-redesign/LICENSE +21 -0
- package/skills/ui-redesign/SKILL.md +56 -0
- package/skills/ui-redesign/references/guide.md +3 -0
- package/skills/visual-review/LICENSE +21 -0
- package/skills/visual-review/SKILL.md +56 -0
- package/skills/visual-review/references/guide.md +7 -0
- package/templates/direction-decision.md +45 -0
- package/templates/research-brief.md +56 -0
- package/templates/visual-review.md +46 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { isMissing, safePath } from '../../filesystem/src/index.js';
|
|
4
|
+
export async function analyzeProject(root) {
|
|
5
|
+
let manifest = {};
|
|
6
|
+
try {
|
|
7
|
+
manifest = JSON.parse(await readFile(await safePath(root, 'package.json'), 'utf8'));
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
if (!isMissing(error))
|
|
11
|
+
throw error;
|
|
12
|
+
}
|
|
13
|
+
const dependencies = new Set();
|
|
14
|
+
if (manifest && typeof manifest === 'object')
|
|
15
|
+
for (const section of ['dependencies', 'devDependencies']) {
|
|
16
|
+
if (section in manifest) {
|
|
17
|
+
const value = Reflect.get(manifest, section);
|
|
18
|
+
if (value && typeof value === 'object')
|
|
19
|
+
for (const key of Object.keys(value))
|
|
20
|
+
dependencies.add(key);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const framework = ['next', '@sveltejs/kit', 'expo', 'react-native', 'vue', 'react'].find(name => dependencies.has(name)) ?? 'unknown';
|
|
24
|
+
const styling = ['tailwindcss', 'styled-components', '@emotion/react'].filter(name => dependencies.has(name));
|
|
25
|
+
const systemFiles = [];
|
|
26
|
+
const instructions = [];
|
|
27
|
+
async function scan(directory, depth) {
|
|
28
|
+
if (depth > 3 || systemFiles.length >= 200)
|
|
29
|
+
return;
|
|
30
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
31
|
+
if (entry.isSymbolicLink() || /^(node_modules|dist|build|\.git|\.next|\.svelte-kit)$/u.test(entry.name))
|
|
32
|
+
continue;
|
|
33
|
+
const full = resolve(directory, entry.name);
|
|
34
|
+
const local = full.slice(root.length + 1);
|
|
35
|
+
if (entry.isDirectory())
|
|
36
|
+
await scan(full, depth + 1);
|
|
37
|
+
else if (/^(CLAUDE|AGENTS)\.md$/u.test(entry.name))
|
|
38
|
+
instructions.push(local);
|
|
39
|
+
else if (/(token|theme|global|design.system|component).*\.(css|json|md|tsx|ts|vue|svelte)$/iu.test(entry.name))
|
|
40
|
+
systemFiles.push(local);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
await scan(root, 0);
|
|
44
|
+
return { framework: framework === 'next' ? 'nextjs' : framework === '@sveltejs/kit' ? 'sveltekit' : framework, styling: styling.length ? styling : ['inspect-css'], systemFiles, instructions };
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../../../packages/core/src/analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { validate } from '../../design-context/src/validation.js';
|
|
2
|
+
import { readJSON } from '../../filesystem/src/index.js';
|
|
3
|
+
export const defaultConfig = {
|
|
4
|
+
version: 1, baseURL: 'http://127.0.0.1:4173', permittedOrigins: ['http://127.0.0.1:4173'],
|
|
5
|
+
viewports: [{ name: 'mobile', width: 390, height: 844 }, { name: 'tablet', width: 768, height: 1024 }, { name: 'laptop', width: 1280, height: 800 }, { name: 'desktop', width: 1440, height: 900 }],
|
|
6
|
+
routes: [{ name: 'home', path: '/', states: [{ name: 'default', actions: [], masks: [] }] }],
|
|
7
|
+
browser: {}, timeoutMs: 15000, thresholds: { diffRatio: 0.01, pixelSensitivity: 0.1, minScore: 75 },
|
|
8
|
+
weights: { hierarchy: 15, typography: 10, layout: 15, consistency: 10, responsive: 15, accessibility: 15, interaction: 0, functional: 10, performance: 10 },
|
|
9
|
+
requiredChecks: ['visual-diff', 'axe', 'overflow', 'functional-tests'], maxRefinements: 2
|
|
10
|
+
};
|
|
11
|
+
export function validateConfig(value) {
|
|
12
|
+
const config = validate('config.schema.json', value);
|
|
13
|
+
const base = new URL(config.baseURL);
|
|
14
|
+
if (!['http:', 'https:'].includes(base.protocol) || base.username || base.password || base.search || base.hash)
|
|
15
|
+
throw new Error('baseURL must be an HTTP(S) URL without credentials, query or fragment');
|
|
16
|
+
for (const origin of config.permittedOrigins)
|
|
17
|
+
if (new URL(origin).origin !== origin)
|
|
18
|
+
throw new Error(`Expected exact permitted origin: ${origin}`);
|
|
19
|
+
if (!config.permittedOrigins.includes(base.origin))
|
|
20
|
+
throw new Error('baseURL origin is not explicitly permitted');
|
|
21
|
+
for (const route of config.routes)
|
|
22
|
+
if (!config.permittedOrigins.includes(new URL(route.path, config.baseURL).origin))
|
|
23
|
+
throw new Error(`Route escapes permitted origins: ${route.name}`);
|
|
24
|
+
for (const [label, names] of [['viewport', config.viewports.map(viewport => viewport.name)], ['route', config.routes.map(route => route.name)], ...config.routes.map(route => [`state in ${route.name}`, route.states.map(state => state.name)])]) {
|
|
25
|
+
if (new Set(names).size !== names.length)
|
|
26
|
+
throw new Error(`Duplicate ${label} names`);
|
|
27
|
+
}
|
|
28
|
+
for (const route of config.routes)
|
|
29
|
+
for (const state of route.states)
|
|
30
|
+
for (const action of state.actions) {
|
|
31
|
+
if ((action.type === 'fill' || action.type === 'press' || action.type === 'expect-text') && action.value === undefined)
|
|
32
|
+
throw new Error(`${action.type} action requires a value`);
|
|
33
|
+
}
|
|
34
|
+
if (Math.abs(Object.values(config.weights).reduce((sum, weight) => sum + weight, 0) - 100) > 0.001)
|
|
35
|
+
throw new Error('Quality weights must sum to 100');
|
|
36
|
+
return config;
|
|
37
|
+
}
|
|
38
|
+
export async function loadConfig(root) { return validateConfig(await readJSON(root, '.tevra/config.json')); }
|
|
39
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../packages/core/src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const agentDirectories: {
|
|
2
|
+
readonly codex: ".agents/skills";
|
|
3
|
+
readonly claude: ".claude/skills";
|
|
4
|
+
readonly cursor: ".cursor/skills";
|
|
5
|
+
readonly kiro: ".kiro/skills";
|
|
6
|
+
readonly gemini: ".gemini/skills";
|
|
7
|
+
readonly copilot: ".github/skills";
|
|
8
|
+
readonly opencode: ".opencode/skills";
|
|
9
|
+
readonly windsurf: ".windsurf/skills";
|
|
10
|
+
};
|
|
11
|
+
export type Agent = keyof typeof agentDirectories;
|
|
12
|
+
export declare function isAgent(name: string): name is Agent;
|
|
13
|
+
export declare function installSkills(root: string, agents: Agent[], dryRun?: boolean): Promise<string[]>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { lstat, readFile, readdir } from 'node:fs/promises';
|
|
2
|
+
import { relative, resolve } from 'node:path';
|
|
3
|
+
import { isMissing, safePath, withLock, writeAtomic } from '../../filesystem/src/index.js';
|
|
4
|
+
import { kitRoot } from '../../filesystem/src/resources.js';
|
|
5
|
+
import { discoverSkills } from './skills.js';
|
|
6
|
+
export const agentDirectories = {
|
|
7
|
+
codex: '.agents/skills', claude: '.claude/skills', cursor: '.cursor/skills', kiro: '.kiro/skills',
|
|
8
|
+
gemini: '.gemini/skills', copilot: '.github/skills', opencode: '.opencode/skills', windsurf: '.windsurf/skills'
|
|
9
|
+
};
|
|
10
|
+
export function isAgent(name) { return Object.hasOwn(agentDirectories, name); }
|
|
11
|
+
async function filesBelow(directory) {
|
|
12
|
+
const found = [];
|
|
13
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
14
|
+
if (entry.isSymbolicLink())
|
|
15
|
+
throw new Error(`Symlink in installation source: ${entry.name}`);
|
|
16
|
+
const full = resolve(directory, entry.name);
|
|
17
|
+
if (entry.isDirectory())
|
|
18
|
+
found.push(...await filesBelow(full));
|
|
19
|
+
else if (entry.isFile())
|
|
20
|
+
found.push(full);
|
|
21
|
+
}
|
|
22
|
+
return found;
|
|
23
|
+
}
|
|
24
|
+
export async function installSkills(root, agents, dryRun = false) {
|
|
25
|
+
const source = resolve(kitRoot(), 'skills');
|
|
26
|
+
const skills = await discoverSkills(source);
|
|
27
|
+
const writes = [];
|
|
28
|
+
for (const agent of new Set(agents)) {
|
|
29
|
+
const base = agentDirectories[agent];
|
|
30
|
+
if (!base)
|
|
31
|
+
throw new Error(`Unsupported agent: ${agent}`);
|
|
32
|
+
for (const skill of skills) {
|
|
33
|
+
const directory = resolve(source, skill.name);
|
|
34
|
+
for (const file of await filesBelow(directory)) {
|
|
35
|
+
const name = `${base}/${skill.name}/${relative(directory, file).split('\\').join('/')}`;
|
|
36
|
+
const destination = await safePath(root, name);
|
|
37
|
+
const bytes = await readFile(file);
|
|
38
|
+
try {
|
|
39
|
+
const existing = await lstat(destination);
|
|
40
|
+
if (!existing.isFile() || !(await readFile(destination)).equals(bytes))
|
|
41
|
+
throw new Error(`Installation conflict; preserve local file: ${name}`);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (!isMissing(error))
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
writes.push({ name, bytes });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!dryRun)
|
|
52
|
+
await withLock(root, '.tevra/install.lock', async () => {
|
|
53
|
+
for (const write of writes) {
|
|
54
|
+
const destination = await safePath(root, write.name);
|
|
55
|
+
try {
|
|
56
|
+
if (!(await readFile(destination)).equals(write.bytes))
|
|
57
|
+
throw new Error(`Installation conflict; preserve local file: ${write.name}`);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (!isMissing(error))
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const write of writes)
|
|
65
|
+
await writeAtomic(root, write.name, write.bytes);
|
|
66
|
+
});
|
|
67
|
+
return writes.map(write => write.name);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../../packages/core/src/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Registry } from '../../registry/src/index.js';
|
|
2
|
+
import type { Plan } from '../../contracts/src/index.js';
|
|
3
|
+
export interface RouteOptions {
|
|
4
|
+
mode: Plan['mode'];
|
|
5
|
+
maxSteps: number;
|
|
6
|
+
maxMinutes: number;
|
|
7
|
+
maxRefinements: number;
|
|
8
|
+
contextRevision: number;
|
|
9
|
+
framework?: string;
|
|
10
|
+
platform?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function planWorkflow(request: string, options: RouteOptions, extensionRegistry?: Registry): Plan;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { kitRoot } from '../../filesystem/src/resources.js';
|
|
4
|
+
import { selectCapabilities, validateRegistry } from '../../registry/src/index.js';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
export function planWorkflow(request, options, extensionRegistry) {
|
|
7
|
+
if (!request.trim())
|
|
8
|
+
throw new Error('A non-empty user request is required');
|
|
9
|
+
if (!Number.isInteger(options.maxSteps) || options.maxSteps < 1 || options.maxSteps > 100 || !Number.isInteger(options.contextRevision) || options.contextRevision < 0)
|
|
10
|
+
throw new Error('Invalid step budget or context revision');
|
|
11
|
+
const text = request.toLocaleLowerCase();
|
|
12
|
+
// Whole-word match so "form" does not fire on "performance" or "information".
|
|
13
|
+
const has = (terms) => new RegExp(`(?<!\\p{L})(?:${terms})(?!\\p{L})`, 'u').test(text);
|
|
14
|
+
const screenshot = has('screenshots?|ảnh chụp|reference images?');
|
|
15
|
+
const redesign = has('redesign|improve|fix|restyle|cải thiện|thiết kế lại|sửa');
|
|
16
|
+
const component = has('cards?|buttons?|headers?|navigation|forms?|tables?|components?|thẻ|nút|biểu mẫu');
|
|
17
|
+
let selected;
|
|
18
|
+
if (has('performance|lighthouse|web vitals|core web vitals|hiệu năng'))
|
|
19
|
+
selected = ['performance-qa'];
|
|
20
|
+
else if (has('browser tests?|browser testing|e2e|functional regression'))
|
|
21
|
+
selected = ['browser-testing'];
|
|
22
|
+
else if (has('source review|code review|frontend review'))
|
|
23
|
+
selected = ['frontend-review'];
|
|
24
|
+
else if (has('accessib\\p{L}*|wcag|a11y|truy cập') && !redesign)
|
|
25
|
+
selected = ['accessibility-qa'];
|
|
26
|
+
else if (has('motion|animations?|micro.interactions?|chuyển động'))
|
|
27
|
+
selected = ['interaction-motion', 'ui-builder', 'visual-review', 'accessibility-qa'];
|
|
28
|
+
else if (has('responsive|overflow|mobile layout|di động'))
|
|
29
|
+
selected = ['responsive-design', 'ui-builder', 'visual-review', 'accessibility-qa'];
|
|
30
|
+
else if (has('tokens?|design system|hệ thống thiết kế'))
|
|
31
|
+
selected = ['design-research', 'design-direction', 'design-system'];
|
|
32
|
+
else if (screenshot)
|
|
33
|
+
selected = ['design-direction', 'ui-builder', 'responsive-design', 'visual-review', 'accessibility-qa'];
|
|
34
|
+
else if (redesign && component)
|
|
35
|
+
selected = ['ui-redesign', 'ui-builder', 'visual-review'];
|
|
36
|
+
else if (redesign)
|
|
37
|
+
selected = ['ui-redesign', 'design-direction', 'ui-builder', 'responsive-design', 'visual-review', 'accessibility-qa'];
|
|
38
|
+
else if (has('review|compare|audit|kiểm tra'))
|
|
39
|
+
selected = ['visual-review', 'accessibility-qa'];
|
|
40
|
+
else
|
|
41
|
+
selected = ['design-research', 'design-direction', 'design-system', 'ui-builder', 'responsive-design', 'visual-review', 'accessibility-qa'];
|
|
42
|
+
// Quick mode trims supporting steps but keeps responsive-design when it owns the request.
|
|
43
|
+
if (options.mode === 'quick')
|
|
44
|
+
selected = selected.filter((skill, index) => skill !== 'design-research' && (skill !== 'responsive-design' || index === 0));
|
|
45
|
+
if (options.mode === 'full' && !component && !selected.includes('accessibility-qa'))
|
|
46
|
+
selected.push('accessibility-qa');
|
|
47
|
+
if (options.mode === 'full' && !['performance-qa', 'browser-testing', 'frontend-review'].some(name => selected.length === 1 && selected.includes(name))) {
|
|
48
|
+
for (const name of ['frontend-review', 'browser-testing', 'performance-qa'])
|
|
49
|
+
if (!selected.includes(name))
|
|
50
|
+
selected.push(name);
|
|
51
|
+
}
|
|
52
|
+
const registry = extensionRegistry ?? validateRegistry(JSON.parse(readFileSync(resolve(kitRoot(), 'registry/skills.json'), 'utf8')));
|
|
53
|
+
const builtinCapabilities = {
|
|
54
|
+
'design-research': 'ux.research', 'design-direction': 'visual.direction', 'design-system': 'design.tokens',
|
|
55
|
+
'ui-builder': 'frontend.implementation', 'ui-redesign': 'ui.redesign', 'responsive-design': 'responsive.design',
|
|
56
|
+
'interaction-motion': 'interaction.motion', 'visual-review': 'visual.regression', 'accessibility-qa': 'accessibility',
|
|
57
|
+
'performance-qa': 'performance', 'browser-testing': 'browser.testing', 'frontend-review': 'frontend.review'
|
|
58
|
+
};
|
|
59
|
+
const requested = selected.map(name => { const capability = builtinCapabilities[name]; if (!capability)
|
|
60
|
+
throw new Error(`Unknown capability for ${name}`); return capability; });
|
|
61
|
+
const resolution = selectCapabilities(registry, requested, { ...(options.framework ? { framework: options.framework } : {}), ...(options.platform ? { platform: options.platform } : {}) });
|
|
62
|
+
const ordered = [];
|
|
63
|
+
const added = new Set();
|
|
64
|
+
function add(id) {
|
|
65
|
+
if (added.has(id))
|
|
66
|
+
return;
|
|
67
|
+
const entry = resolution.skills.find(candidate => candidate.id === id);
|
|
68
|
+
if (!entry)
|
|
69
|
+
throw new Error(`Unresolved registry skill: ${id}`);
|
|
70
|
+
for (const dependency of entry.dependencies)
|
|
71
|
+
add(dependency);
|
|
72
|
+
added.add(id);
|
|
73
|
+
ordered.push(entry.name);
|
|
74
|
+
}
|
|
75
|
+
for (const capability of requested) {
|
|
76
|
+
const owner = resolution.owners[capability];
|
|
77
|
+
if (owner)
|
|
78
|
+
add(owner);
|
|
79
|
+
}
|
|
80
|
+
selected = ordered;
|
|
81
|
+
if (selected.length > options.maxSteps)
|
|
82
|
+
throw new Error(`Workflow needs ${selected.length} steps; budget permits ${options.maxSteps}. Narrow the task or increase the budget.`);
|
|
83
|
+
if (!Number.isInteger(options.maxMinutes) || options.maxMinutes < 1 || options.maxMinutes > 1440 || !Number.isInteger(options.maxRefinements) || options.maxRefinements < 0 || options.maxRefinements > 10)
|
|
84
|
+
throw new Error('Invalid execution budget');
|
|
85
|
+
return { version: 1, id: randomUUID(), request, mode: options.mode, contextVersion: '2.0', contextRevision: options.contextRevision, createdAt: new Date().toISOString(),
|
|
86
|
+
budget: { maxSteps: options.maxSteps, maxMinutes: options.maxMinutes, maxRefinements: options.mode === 'quick' ? 0 : options.maxRefinements },
|
|
87
|
+
steps: selected.map((skill, index) => ({ skill, dependsOn: index > 0 ? selected.slice(index - 1, index) : [], status: 'pending', attempts: 0, evidence: [] })),
|
|
88
|
+
approvalRequired: /transformative|shared tokens|brand migration|đổi thương hiệu/u.test(text), approvedBy: null };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../../../packages/core/src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEnF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,OAAqB,EAAE,iBAA4B;IAC/F,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC7E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,OAAO,CAAC,eAAe,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACnO,MAAM,IAAI,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IACzC,8EAA8E;IAC9E,MAAM,GAAG,GAAG,CAAC,KAAa,EAAW,EAAE,CAAC,IAAI,MAAM,CAAC,iBAAiB,KAAK,aAAa,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxG,MAAM,UAAU,GAAG,GAAG,CAAC,yCAAyC,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,GAAG,CAAC,yDAAyD,CAAC,CAAC;IAChF,MAAM,SAAS,GAAG,GAAG,CAAC,iFAAiF,CAAC,CAAC;IACzG,IAAI,QAAqB,CAAC;IAC1B,IAAI,GAAG,CAAC,6DAA6D,CAAC;QAAE,QAAQ,GAAG,CAAC,gBAAgB,CAAC,CAAC;SACjG,IAAI,GAAG,CAAC,0DAA0D,CAAC;QAAE,QAAQ,GAAG,CAAC,iBAAiB,CAAC,CAAC;SACpG,IAAI,GAAG,CAAC,2CAA2C,CAAC;QAAE,QAAQ,GAAG,CAAC,iBAAiB,CAAC,CAAC;SACrF,IAAI,GAAG,CAAC,oCAAoC,CAAC,IAAI,CAAC,QAAQ;QAAE,QAAQ,GAAG,CAAC,kBAAkB,CAAC,CAAC;SAC5F,IAAI,GAAG,CAAC,oDAAoD,CAAC;QAAE,QAAQ,GAAG,CAAC,oBAAoB,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,CAAC,CAAC;SACpJ,IAAI,GAAG,CAAC,2CAA2C,CAAC;QAAE,QAAQ,GAAG,CAAC,mBAAmB,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,CAAC,CAAC;SAC1I,IAAI,GAAG,CAAC,yCAAyC,CAAC;QAAE,QAAQ,GAAG,CAAC,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,CAAC,CAAC;SACxH,IAAI,UAAU;QAAE,QAAQ,GAAG,CAAC,kBAAkB,EAAE,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,kBAAkB,CAAC,CAAC;SACxH,IAAI,QAAQ,IAAI,SAAS;QAAE,QAAQ,GAAG,CAAC,aAAa,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;SACrF,IAAI,QAAQ;QAAE,QAAQ,GAAG,CAAC,aAAa,EAAE,kBAAkB,EAAE,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,kBAAkB,CAAC,CAAC;SACrI,IAAI,GAAG,CAAC,+BAA+B,CAAC;QAAE,QAAQ,GAAG,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;;QAC3F,QAAQ,GAAG,CAAC,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,kBAAkB,CAAC,CAAC;IACjJ,0FAA0F;IAC1F,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO;QAAE,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,iBAAiB,IAAI,CAAC,KAAK,KAAK,mBAAmB,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1J,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACvH,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACxJ,KAAK,MAAM,IAAI,IAAI,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,CAAC;YAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjI,CAAC;IACD,MAAM,QAAQ,GAAG,iBAAiB,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,sBAAsB,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACrI,MAAM,mBAAmB,GAA2B;QAClD,iBAAiB,EAAE,aAAa,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe;QAC1G,YAAY,EAAE,yBAAyB,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,mBAAmB;QAC/G,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,eAAe;QACrH,gBAAgB,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB;KAC5G,CAAC;IACF,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACjL,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5L,MAAM,OAAO,GAAa,EAAE,CAAC;IAAC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9D,SAAS,GAAG,CAAC,EAAU;QACrB,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO;QAC1B,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACvE,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,EAAE,EAAE,CAAC,CAAC;QAChE,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,YAAY;YAAE,GAAG,CAAC,UAAU,CAAC,CAAC;QAC7D,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IACD,KAAK,MAAM,UAAU,IAAI,SAAS,EAAE,CAAC;QAAC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAAC,IAAI,KAAK;YAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC;IAC3G,QAAQ,GAAG,OAAO,CAAC;IACnB,IAAI,QAAQ,CAAC,MAAM,GAAG,OAAO,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,CAAC,MAAM,0BAA0B,OAAO,CAAC,QAAQ,2CAA2C,CAAC,CAAC;IAChL,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,UAAU,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC,cAAc,GAAG,CAAC,IAAI,OAAO,CAAC,cAAc,GAAG,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACxP,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtK,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE;QAC7I,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9J,gBAAgB,EAAE,+DAA+D,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AACrH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface Pattern {
|
|
2
|
+
id: string;
|
|
3
|
+
category: string;
|
|
4
|
+
tags: string[];
|
|
5
|
+
recommendation: string;
|
|
6
|
+
source: string;
|
|
7
|
+
confidence: string;
|
|
8
|
+
license: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function searchPatterns(query: string, category?: string, limit?: number): Promise<(Pattern & {
|
|
11
|
+
score: number;
|
|
12
|
+
})[]>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { kitRoot } from '../../filesystem/src/resources.js';
|
|
4
|
+
function terms(text) { return text.toLocaleLowerCase().match(/[\p{L}\p{N}]+/gu) ?? []; }
|
|
5
|
+
export async function searchPatterns(query, category, limit = 5) {
|
|
6
|
+
if (!query.trim() || !Number.isInteger(limit) || limit < 1 || limit > 20)
|
|
7
|
+
throw new Error('Search needs a query and a result limit between 1 and 20');
|
|
8
|
+
const input = JSON.parse(await readFile(resolve(kitRoot(), 'references/ux-patterns/catalog.json'), 'utf8'));
|
|
9
|
+
if (!Array.isArray(input))
|
|
10
|
+
throw new Error('Invalid bundled pattern catalog');
|
|
11
|
+
const patterns = input.map((entry) => {
|
|
12
|
+
if (!entry || typeof entry !== 'object' || !('id' in entry) || !('category' in entry) || !('tags' in entry) || !('recommendation' in entry) || !('source' in entry) || !('confidence' in entry) || !('license' in entry) || ![entry.id, entry.category, entry.recommendation, entry.source, entry.confidence, entry.license].every(value => typeof value === 'string') || !Array.isArray(entry.tags) || !entry.tags.every(value => typeof value === 'string'))
|
|
13
|
+
throw new Error('Invalid pattern catalog record');
|
|
14
|
+
return entry;
|
|
15
|
+
}).filter(pattern => !category || pattern.category === category);
|
|
16
|
+
const documents = patterns.map(pattern => terms(`${pattern.category} ${pattern.tags.join(' ')} ${pattern.recommendation}`));
|
|
17
|
+
const averageLength = documents.reduce((sum, tokens) => sum + tokens.length, 0) / (documents.length || 1);
|
|
18
|
+
const queryTerms = [...new Set(terms(query))];
|
|
19
|
+
const frequencies = new Map(queryTerms.map(term => [term, documents.filter(tokens => tokens.includes(term)).length]));
|
|
20
|
+
return patterns.map((pattern, index) => {
|
|
21
|
+
const tokens = documents[index] ?? [];
|
|
22
|
+
const score = queryTerms.reduce((sum, term) => {
|
|
23
|
+
const frequency = tokens.filter(token => token === term).length;
|
|
24
|
+
const matched = frequencies.get(term) ?? 0;
|
|
25
|
+
const inverseFrequency = Math.log(1 + (documents.length - matched + 0.5) / (matched + 0.5));
|
|
26
|
+
return sum + inverseFrequency * frequency * 2.2 / (frequency + 1.2 * (0.25 + 0.75 * tokens.length / averageLength));
|
|
27
|
+
}, 0);
|
|
28
|
+
return { ...pattern, score };
|
|
29
|
+
}).filter(pattern => pattern.score > 0).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id)).slice(0, limit);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../../packages/core/src/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { parseDocument } from 'yaml';
|
|
4
|
+
import { safePath } from '../../filesystem/src/index.js';
|
|
5
|
+
export async function discoverSkills(directory) {
|
|
6
|
+
const discovered = [];
|
|
7
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
8
|
+
if (!entry.isDirectory())
|
|
9
|
+
continue;
|
|
10
|
+
const path = await safePath(directory, `${entry.name}/SKILL.md`);
|
|
11
|
+
const content = await readFile(path, 'utf8');
|
|
12
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u.exec(content);
|
|
13
|
+
if (!match?.[1])
|
|
14
|
+
throw new Error(`Missing YAML frontmatter: ${path}`);
|
|
15
|
+
const document = parseDocument(match[1], { uniqueKeys: true });
|
|
16
|
+
if (document.errors.length)
|
|
17
|
+
throw new Error(`Invalid YAML: ${path}`);
|
|
18
|
+
const metadata = document.toJS({ maxAliasCount: 0 });
|
|
19
|
+
if (!metadata || typeof metadata !== 'object' || !('name' in metadata) || !('description' in metadata))
|
|
20
|
+
throw new Error(`Missing skill metadata: ${path}`);
|
|
21
|
+
const name = typeof metadata.name === 'string' && /^[a-z][a-z0-9-]{0,63}$/u.test(metadata.name) ? metadata.name : undefined;
|
|
22
|
+
if (!name || name !== entry.name || typeof metadata.description !== 'string' || !metadata.description.trim() || metadata.description.length > 1024)
|
|
23
|
+
throw new Error(`Invalid skill identity: ${entry.name}`);
|
|
24
|
+
for (const link of content.matchAll(/\]\(([^)#]+)(?:#[^)]*)?\)/gu)) {
|
|
25
|
+
const target = link[1];
|
|
26
|
+
if (!target || /^[a-z]+:/iu.test(target))
|
|
27
|
+
continue;
|
|
28
|
+
await readFile(await safePath(resolve(directory, entry.name), target));
|
|
29
|
+
}
|
|
30
|
+
if (discovered.some(skill => skill.name === name))
|
|
31
|
+
throw new Error(`Duplicate skill: ${name}`);
|
|
32
|
+
discovered.push({ name, description: metadata.description, path });
|
|
33
|
+
}
|
|
34
|
+
return discovered.sort((a, b) => a.name.localeCompare(b.name));
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../../../packages/core/src/skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Handoff, Plan, SkillName, Status } from '../../contracts/src/index.js';
|
|
2
|
+
export declare function assertHandoff(value: unknown, contextRevision: number, active: Handoff[]): Handoff;
|
|
3
|
+
export declare function transitionPlan(root: string, path: string, skill: SkillName, status: Status, evidence: string[], currentRevision: number): Promise<Plan>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import { validate } from '../../design-context/src/validation.js';
|
|
3
|
+
import { readJSON, safePath, withLock, writeJSON } from '../../filesystem/src/index.js';
|
|
4
|
+
import { loadContext } from '../../design-context/src/index.js';
|
|
5
|
+
export function assertHandoff(value, contextRevision, active) {
|
|
6
|
+
const handoff = validate('handoff.schema.json', value);
|
|
7
|
+
const canonical = (file) => {
|
|
8
|
+
const normalized = file.replaceAll('\\', '/').replace(/\/$/u, '');
|
|
9
|
+
if (!normalized || normalized.startsWith('/') || /^[a-z]:/iu.test(normalized) || normalized.split('/').some(segment => segment === '..' || segment === '.' || !segment))
|
|
10
|
+
throw new Error(`Unsafe handoff path: ${file}`);
|
|
11
|
+
return normalized;
|
|
12
|
+
};
|
|
13
|
+
handoff.files = handoff.files.map(canonical);
|
|
14
|
+
handoff.inputs = handoff.inputs.map(canonical);
|
|
15
|
+
handoff.outputs = handoff.outputs.map(canonical);
|
|
16
|
+
handoff.evidence = handoff.evidence.map(canonical);
|
|
17
|
+
if (handoff.contextRevision !== contextRevision)
|
|
18
|
+
throw new Error('Handoff context revision is stale');
|
|
19
|
+
if (active.some(existing => existing.taskId === handoff.taskId && existing.owner !== handoff.owner))
|
|
20
|
+
throw new Error('Task ownership transfer requires explicit coordination');
|
|
21
|
+
for (const existing of active.filter(task => ['pending', 'running', 'awaiting-approval', 'blocked'].includes(task.status) && task.taskId !== handoff.taskId)) {
|
|
22
|
+
if (handoff.files.some(file => existing.files.map(canonical).some(other => file === other || file.startsWith(`${other}/`) || other.startsWith(`${file}/`))))
|
|
23
|
+
throw new Error(`Conflicting file ownership with ${existing.taskId}`);
|
|
24
|
+
}
|
|
25
|
+
if (handoff.status === 'passed' && !handoff.evidence.length)
|
|
26
|
+
throw new Error('Passed handoff requires validation evidence');
|
|
27
|
+
if (handoff.status === 'failed' && !handoff.error)
|
|
28
|
+
throw new Error('Failed handoff requires error information');
|
|
29
|
+
return handoff;
|
|
30
|
+
}
|
|
31
|
+
export async function transitionPlan(root, path, skill, status, evidence, currentRevision) {
|
|
32
|
+
return withLock(root, '.tevra/context.lock', () => withLock(root, '.tevra/workflow.lock', async () => {
|
|
33
|
+
if ((await loadContext(root)).revision !== currentRevision)
|
|
34
|
+
throw new Error('Workflow context revision is stale');
|
|
35
|
+
const plan = validate('plan.schema.json', await readJSON(root, path));
|
|
36
|
+
if (plan.contextRevision !== currentRevision)
|
|
37
|
+
throw new Error('Workflow context revision is stale');
|
|
38
|
+
const step = plan.steps.find(value => value.skill === skill);
|
|
39
|
+
if (!step)
|
|
40
|
+
throw new Error(`Skill is outside this workflow: ${skill}`);
|
|
41
|
+
if (Date.now() > Date.parse(plan.createdAt) + plan.budget.maxMinutes * 60000)
|
|
42
|
+
throw new Error('Workflow time budget exhausted');
|
|
43
|
+
if (status === 'running') {
|
|
44
|
+
if (!['pending', 'failed', 'awaiting-approval', 'blocked'].includes(step.status))
|
|
45
|
+
throw new Error('Only pending or failed work can start');
|
|
46
|
+
if (plan.approvalRequired && !plan.approvedBy)
|
|
47
|
+
throw new Error('Human approval required before starting this high-impact workflow');
|
|
48
|
+
if (step.dependsOn.some(dependency => plan.steps.find(candidate => candidate.skill === dependency)?.status !== 'passed'))
|
|
49
|
+
throw new Error('Workflow dependency has not passed');
|
|
50
|
+
if (step.attempts >= plan.budget.maxRefinements + 1)
|
|
51
|
+
throw new Error('Maximum refinement count reached');
|
|
52
|
+
step.attempts++;
|
|
53
|
+
}
|
|
54
|
+
else if (status === 'passed' || status === 'failed' || status === 'blocked' || status === 'skipped') {
|
|
55
|
+
if (step.status !== 'running')
|
|
56
|
+
throw new Error('Start the step before completing it');
|
|
57
|
+
if (!evidence.length)
|
|
58
|
+
throw new Error('Completion or failure requires evidence');
|
|
59
|
+
for (const artifact of evidence)
|
|
60
|
+
if (!(await stat(await safePath(root, artifact))).isFile())
|
|
61
|
+
throw new Error(`Evidence is not a file: ${artifact}`);
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
throw new Error('Supported transitions: running, passed, failed, blocked, skipped');
|
|
65
|
+
if (status === 'blocked')
|
|
66
|
+
step.attempts = Math.max(0, step.attempts - 1);
|
|
67
|
+
step.status = status;
|
|
68
|
+
step.evidence = evidence;
|
|
69
|
+
await writeJSON(root, path, plan);
|
|
70
|
+
return plan;
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../../../packages/core/src/workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DesignContext } from '../../contracts/src/index.js';
|
|
2
|
+
export declare const contextPath = ".tevra/design-context.json";
|
|
3
|
+
export declare function createContext(name: string, owner: string): DesignContext;
|
|
4
|
+
export declare function loadContext(root: string): Promise<DesignContext>;
|
|
5
|
+
export declare function updateContext(root: string, candidate: unknown, expectedRevision: number, approveConstraints?: boolean): Promise<DesignContext>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readJSON, withLock, writeJSON } from '../../filesystem/src/index.js';
|
|
2
|
+
import { validate } from './validation.js';
|
|
3
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
4
|
+
export const contextPath = '.tevra/design-context.json';
|
|
5
|
+
export function createContext(name, owner) {
|
|
6
|
+
return validate('design-context.v2.schema.json', {
|
|
7
|
+
version: '2.0', revision: 0, owner, project: { name, framework: 'unknown', styling: 'css' },
|
|
8
|
+
design: { variance: 5, motionIntensity: 3, visualDensity: 5, theme: 'system', brandPersonality: [] },
|
|
9
|
+
tokens: { primitive: {}, semantic: {}, component: {}, themes: {}, responsive: {} },
|
|
10
|
+
constraints: { preserveBrand: true, preserveFunctionality: true, accessibilityTarget: 'WCAG_2_2_AA', approvedScope: [] },
|
|
11
|
+
decisions: [], assumptions: ['Inspect the existing design system before proposing tokens.'], evidence: []
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export async function loadContext(root) { return validate('design-context.v2.schema.json', await readJSON(root, contextPath)); }
|
|
15
|
+
export async function updateContext(root, candidate, expectedRevision, approveConstraints = false) {
|
|
16
|
+
const next = validate('design-context.v2.schema.json', candidate);
|
|
17
|
+
return withLock(root, '.tevra/context.lock', async () => {
|
|
18
|
+
const current = await loadContext(root);
|
|
19
|
+
if (current.revision !== expectedRevision || next.revision !== expectedRevision)
|
|
20
|
+
throw new Error('Stale context revision; reload before writing');
|
|
21
|
+
if (current.owner !== next.owner)
|
|
22
|
+
throw new Error('Context ownership transfer requires an explicit migration');
|
|
23
|
+
if (!approveConstraints && !isDeepStrictEqual(current.constraints, next.constraints))
|
|
24
|
+
throw new Error('Constraint changes require explicit --approve-constraints authorization');
|
|
25
|
+
if (current.approvedDirection?.approved && !isDeepStrictEqual(current.approvedDirection, next.approvedDirection))
|
|
26
|
+
throw new Error('Approved visual direction is immutable; record a new explicit decision before migration');
|
|
27
|
+
if (current.brand && !isDeepStrictEqual(current.brand, next.brand) && !approveConstraints)
|
|
28
|
+
throw new Error('Brand changes require explicit --approve-constraints authorization');
|
|
29
|
+
for (const approved of current.decisions.filter(decision => decision.approved)) {
|
|
30
|
+
const replacement = next.decisions.find(decision => decision.id === approved.id);
|
|
31
|
+
if (!isDeepStrictEqual(replacement, approved))
|
|
32
|
+
throw new Error(`Approved decision is immutable: ${approved.id}`);
|
|
33
|
+
}
|
|
34
|
+
if (new Set(next.decisions.map(decision => decision.id)).size !== next.decisions.length)
|
|
35
|
+
throw new Error('Duplicate decision identifiers');
|
|
36
|
+
const updated = { ...next, revision: expectedRevision + 1 };
|
|
37
|
+
await writeJSON(root, contextPath, updated);
|
|
38
|
+
return updated;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/design-context/src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createContext } from './index.js';
|
|
2
|
+
import { validate } from './validation.js';
|
|
3
|
+
export function migrateContext(input, owner) {
|
|
4
|
+
if (!input || typeof input !== 'object')
|
|
5
|
+
throw new Error('Context must be an object');
|
|
6
|
+
if ('version' in input && input.version === '2.0')
|
|
7
|
+
return validate('design-context.v2.schema.json', input);
|
|
8
|
+
if ('version' in input && input.version === '1.0') {
|
|
9
|
+
const original = validate('legacy/design-context.master-v1.schema.json', input);
|
|
10
|
+
const migrated = createContext(original.project.name, owner);
|
|
11
|
+
migrated.project = original.project;
|
|
12
|
+
migrated.design = original.design;
|
|
13
|
+
migrated.tokens.primitive = Object.fromEntries(Object.entries(original.tokens).flatMap(([group, tokens]) => Object.entries(tokens).map(([key, value]) => [`${group}.${key}`, value])));
|
|
14
|
+
migrated.constraints = { ...original.constraints, approvedScope: [] };
|
|
15
|
+
migrated.decisions = original.decisions.map((description, index) => ({ id: `imported-${index + 1}`, description, owner, approved: false }));
|
|
16
|
+
migrated.assumptions = ['Imported version 1.0 without approval metadata; confirm imported decisions explicitly.'];
|
|
17
|
+
return validate('design-context.v2.schema.json', migrated);
|
|
18
|
+
}
|
|
19
|
+
const legacy = validate('legacy/design-context.v1.schema.json', input);
|
|
20
|
+
const migrated = createContext(legacy.project.name, owner);
|
|
21
|
+
migrated.tokens.primitive = Object.fromEntries(Object.entries(legacy.tokens).flatMap(([group, tokens]) => Object.entries(tokens).map(([key, value]) => [`${group}.${key}`, value])));
|
|
22
|
+
migrated.decisions = (legacy.decisions ?? []).map((description, index) => ({ id: `legacy-${index + 1}`, description, approved: false, owner }));
|
|
23
|
+
migrated.evidence = legacy.project.evidence ?? [];
|
|
24
|
+
migrated.assumptions = ['Migrated legacy constraints retained as notes; confirm structured scope before implementation.', ...(legacy.constraints ?? [])];
|
|
25
|
+
return validate('design-context.v2.schema.json', migrated);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=migrate.js.map
|