@birdapi/velinstyle 1.2.1 → 1.3.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.
Files changed (156) hide show
  1. package/README.de.md +148 -11
  2. package/README.md +174 -16
  3. package/cli/__fixtures__/atelier-library/showcases/01-login/app.css +1 -0
  4. package/cli/__fixtures__/atelier-library/showcases/01-login/app.js +2 -0
  5. package/cli/__fixtures__/atelier-library/showcases/01-login/index.html +5 -0
  6. package/cli/__fixtures__/atelier-library/showcases/04-pricing/app.css +1 -0
  7. package/cli/__fixtures__/atelier-library/showcases/04-pricing/app.js +2 -0
  8. package/cli/__fixtures__/atelier-library/showcases/04-pricing/index.html +5 -0
  9. package/cli/__fixtures__/atelier-library/showcases/36-calendar/app.css +1 -0
  10. package/cli/__fixtures__/atelier-library/showcases/36-calendar/app.js +2 -0
  11. package/cli/__fixtures__/atelier-library/showcases/36-calendar/index.html +5 -0
  12. package/cli/atelier-catalog.json +1267 -0
  13. package/cli/atelier-formats.js +159 -0
  14. package/cli/atelier.js +382 -0
  15. package/cli/blueprints/empty-state.html +3 -5
  16. package/cli/cli-manifest.json +441 -161
  17. package/cli/docgen/extract-cli.js +2 -1
  18. package/cli/docs-generate.js +9 -0
  19. package/cli/experience.js +173 -0
  20. package/cli/index.js +1384 -361
  21. package/cli/motion.js +738 -0
  22. package/cli/production/component-graph.json +166 -0
  23. package/cli/production/explain.js +52 -0
  24. package/cli/production/extract.js +278 -0
  25. package/cli/production/graph.js +102 -0
  26. package/cli/production/report.js +168 -0
  27. package/cli/production/run.js +355 -0
  28. package/cli/production/trim-css.js +177 -0
  29. package/cli/production/trim-fonts.js +23 -0
  30. package/cli/production/trim-icons.js +38 -0
  31. package/cli/production/trim-js.js +48 -0
  32. package/cli/production/trim-motion.js +22 -0
  33. package/cli/production/trim-themes.js +50 -0
  34. package/cli/production/watch.js +38 -0
  35. package/cli/review.js +48 -1
  36. package/cli/scripts/write-atelier-fixtures.mjs +33 -0
  37. package/cli/security.js +190 -0
  38. package/cli/skills.js +53 -10
  39. package/cli/transparency.js +140 -41
  40. package/cli/workflow.js +32 -17
  41. package/components/index.js +3 -0
  42. package/components/runtime/component-loaders.js +3 -0
  43. package/components/velin-drawer.js +43 -4
  44. package/components/velin-empty-state.js +83 -0
  45. package/components/velin-modal.js +76 -15
  46. package/components/velin-otp-input.js +220 -0
  47. package/components/velin-password-strength.js +147 -0
  48. package/components/velin-sheet.js +49 -4
  49. package/components/velin-theme-toggle.js +15 -1
  50. package/core/a11y/component-contracts.json +391 -255
  51. package/core/attributes/registry.js +1 -1
  52. package/core/meta/knowledge/components.json +75 -3
  53. package/core/motion/analyze.js +38 -0
  54. package/core/motion/blueprint.js +51 -0
  55. package/core/motion/composer.js +114 -0
  56. package/core/motion/data/registry.json +352 -0
  57. package/core/motion/effects.js +17 -1
  58. package/core/motion/index.js +112 -9
  59. package/core/motion/optimize.js +44 -0
  60. package/core/motion/policy.js +77 -0
  61. package/core/motion/registry.js +131 -0
  62. package/core/motion/review.js +59 -0
  63. package/core/motion/scan.js +150 -0
  64. package/core/motion/timeline.js +68 -0
  65. package/core/motion/transitions.js +49 -0
  66. package/core/motion/triggers.js +168 -0
  67. package/core/security/checks/fs.js +92 -0
  68. package/core/security/detect.js +35 -0
  69. package/core/security/engine.js +116 -0
  70. package/core/security/index.js +18 -0
  71. package/core/security/registry.js +85 -0
  72. package/core/security/report/report.js +113 -0
  73. package/core/security/rules/ci/index.js +146 -0
  74. package/core/security/rules/consumer/index.js +174 -0
  75. package/core/security/rules/deps/index.js +182 -0
  76. package/core/security/rules/index.js +24 -0
  77. package/core/security/rules/publish/index.js +230 -0
  78. package/core/security/rules/repo/index.js +143 -0
  79. package/core/security/rules/secrets/index.js +133 -0
  80. package/core/security/score/dependency-health.js +53 -0
  81. package/core/security/score/release-health.js +66 -0
  82. package/core/security/score/score.js +102 -0
  83. package/core/security/timeline/timeline.js +76 -0
  84. package/dist/chunks/attributes-2ORR27KA.js +404 -0
  85. package/dist/chunks/attributes-HK7VIOLA.js +1080 -0
  86. package/dist/chunks/attributes-VRHHVNDO.js +1080 -0
  87. package/dist/chunks/chunk-MYKUI364.js +116 -0
  88. package/dist/chunks/chunk-NEVBPD5T.js +116 -0
  89. package/dist/chunks/chunk-Y6HN7HWX.js +116 -0
  90. package/dist/chunks/runtime-entry.js +1 -1
  91. package/dist/chunks/velin-drawer-A7Q7EBOS.js +162 -0
  92. package/dist/chunks/velin-empty-state-3NTUH2RF.js +81 -0
  93. package/dist/chunks/velin-modal-3MV4FYBK.js +231 -0
  94. package/dist/chunks/velin-otp-input-PSK42MM6.js +207 -0
  95. package/dist/chunks/velin-password-strength-TAXMLSED.js +136 -0
  96. package/dist/chunks/velin-sheet-N2VVYXFP.js +157 -0
  97. package/dist/chunks/velin-theme-toggle-J2NHC7IM.js +304 -0
  98. package/dist/llms.txt +4 -4
  99. package/dist/search-index.json +232 -5
  100. package/dist/velin-agent.json +433 -46
  101. package/dist/velinstyle-components.iife.js +3695 -2272
  102. package/dist/velinstyle-components.js +3698 -2271
  103. package/dist/velinstyle-components.min.js +262 -120
  104. package/dist/velinstyle.css +96 -13
  105. package/dist/velinstyle.d.ts +3 -0
  106. package/dist/velinstyle.min.css +1 -1
  107. package/package.json +19 -4
  108. package/packages/velinstyle-cli-core/package.json +11 -0
  109. package/packages/velinstyle-cli-core/src/config.js +84 -0
  110. package/packages/velinstyle-cli-core/src/contract.js +56 -0
  111. package/packages/velinstyle-cli-core/src/event-bus.js +50 -0
  112. package/packages/velinstyle-cli-core/src/index.js +18 -0
  113. package/packages/velinstyle-cli-core/src/lifecycle.js +33 -0
  114. package/packages/velinstyle-cli-core/src/runner.js +129 -0
  115. package/packages/velinstyle-cli-registry/data/commands.json +1216 -0
  116. package/packages/velinstyle-cli-registry/data/layouts.json +33 -0
  117. package/packages/velinstyle-cli-registry/data/widgets.json +35 -0
  118. package/packages/velinstyle-cli-registry/package.json +13 -0
  119. package/packages/velinstyle-cli-registry/src/index.js +88 -0
  120. package/packages/velinstyle-cli-renderer/package.json +8 -0
  121. package/packages/velinstyle-cli-renderer/src/index.js +286 -0
  122. package/packages/velinstyle-cli-theme/package.json +8 -0
  123. package/packages/velinstyle-cli-theme/src/index.js +84 -0
  124. package/packages/velinstyle-cli-ui/package.json +8 -0
  125. package/packages/velinstyle-cli-ui/src/index.js +34 -0
  126. package/packages/velinstyle-motion/README.md +81 -0
  127. package/packages/velinstyle-motion/package.json +37 -0
  128. package/packages/velinstyle-motion/src/ai/mini-prompt.js +119 -0
  129. package/packages/velinstyle-motion/src/ai/provider.js +59 -0
  130. package/packages/velinstyle-motion/src/ai/providers/pixverse-setup.js +402 -0
  131. package/packages/velinstyle-motion/src/ai/providers/pixverse.js +245 -0
  132. package/packages/velinstyle-motion/src/config.js +257 -0
  133. package/packages/velinstyle-motion/src/export/html-clip.js +141 -0
  134. package/packages/velinstyle-motion/src/index.js +304 -0
  135. package/packages/velinstyle-motion/src/jobs/cache.js +30 -0
  136. package/packages/velinstyle-motion/src/jobs/queue.js +69 -0
  137. package/packages/velinstyle-motion/src/jobs/usage.js +53 -0
  138. package/packages/velinstyle-motion/src/local/effects.js +139 -0
  139. package/packages/velinstyle-motion/src/local/engine.js +96 -0
  140. package/packages/velinstyle-motion/src/providers-catalog.js +44 -0
  141. package/packages/velinstyle-motion/src/registry/presets.js +25 -0
  142. package/packages/velinstyle-motion/src/registry/presets.json +85 -0
  143. package/packages/velinstyle-motion/src/ux/cost-gate.js +37 -0
  144. package/packages/velinstyle-motion/src/ux/pixverse-gate.js +153 -0
  145. package/packages/velinstyle-motion/src/ux/wizard.js +171 -0
  146. package/packages/velinstyle-motion/src/validate-image.js +91 -0
  147. package/packages/velinstyle-skills/catalog.json +1 -1
  148. package/packages/velinstyle-skills/registry.json +1 -142
  149. package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +1 -1
  150. package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +1 -1
  151. package/src/components/data-table.css +19 -0
  152. package/src/components/empty-auth.css +33 -0
  153. package/src/components/table.css +16 -6
  154. package/src/tokens/motion.css +7 -0
  155. package/src/utilities/scroll-animation.css +66 -0
  156. package/src/velinstyle.css +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@birdapi/velinstyle",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org"
@@ -24,12 +24,20 @@
24
24
  "./email": "./components/velin-email.js",
25
25
  "./search": "./core/search/index.js",
26
26
  "./motion": "./core/motion/index.js",
27
+ "./motion-clip": "./packages/velinstyle-motion/src/index.js",
27
28
  "./attributes": "./core/attributes/index.js",
28
29
  "./highlight": "./core/highlight/index.js",
29
30
  "./meta": "./core/meta/index.js",
30
31
  "./transparency": "./core/transparency/index.js",
32
+ "./security": "./core/security/index.js",
31
33
  "./a11y": "./components/a11y-entry.js",
32
- "./skills-registry": "./packages/velinstyle-skills/registry.json"
34
+ "./skills-registry": "./packages/velinstyle-skills/registry.json",
35
+ "./experience": "./cli/experience.js",
36
+ "./cli-core": "./packages/velinstyle-cli-core/src/index.js",
37
+ "./cli-registry": "./packages/velinstyle-cli-registry/src/index.js",
38
+ "./cli-theme": "./packages/velinstyle-cli-theme/src/index.js",
39
+ "./cli-ui": "./packages/velinstyle-cli-ui/src/index.js",
40
+ "./cli-renderer": "./packages/velinstyle-cli-renderer/src/index.js"
33
41
  },
34
42
  "bin": {
35
43
  "velinstyle": "cli/index.js"
@@ -52,7 +60,13 @@
52
60
  "packages/velinstyle-skills/",
53
61
  "schemas/",
54
62
  "examples/search-index.schema.json",
55
- "icons/velin-icons.svg"
63
+ "icons/velin-icons.svg",
64
+ "packages/velinstyle-cli-core/",
65
+ "packages/velinstyle-cli-registry/",
66
+ "packages/velinstyle-cli-theme/",
67
+ "packages/velinstyle-cli-ui/",
68
+ "packages/velinstyle-cli-renderer/",
69
+ "packages/velinstyle-motion/"
56
70
  ],
57
71
  "scripts": {
58
72
  "build": "npm run build:css:min && npm run build:css:dev && npm run build:js:min && npm run build:js:dev && npm run build:js:iife && npm run build:js:chunks && npm run build:loaders && npm run build:react:src && npm run build:types && npm run build:themes && npm run build:icons",
@@ -75,7 +89,8 @@
75
89
  "test:a11y": "node tests/a11y/run.js",
76
90
  "test:a11y:coverage": "node tests/a11y/coverage.js",
77
91
  "test:contrast": "node scripts/check-contrast.js",
78
- "test:security": "vitest run tests/sanitize.test.js tests/cli-security-scanner.test.js",
92
+ "test:motion-clip": "vitest run tests/motion-clip.test.js",
93
+ "test:security": "vitest run tests/sanitize.test.js tests/cli-security-scanner.test.js tests/security.test.js",
79
94
  "test:perf": "vitest run tests/perf-audit.test.js",
80
95
  "test:search:bench": "vitest run tests/search-benchmark.test.js",
81
96
  "test:e2e": "playwright test",
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@birdapi/velinstyle-cli-core",
3
+ "version": "1.3.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js"
9
+ },
10
+ "description": "Velin Experience core — lifecycle, event bus, config, output contract"
11
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Velin Experience config — velinstyle-cli.json (+ flags/env).
3
+ */
4
+ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
5
+ import { join, resolve } from 'path';
6
+
7
+ export const DEFAULT_EXPERIENCE_CONFIG = {
8
+ theme: 'modern',
9
+ output: 'standard',
10
+ icons: true,
11
+ unicode: true,
12
+ animations: true,
13
+ progress: true,
14
+ compact: false,
15
+ analytics: false,
16
+ record: false,
17
+ };
18
+
19
+ const CONFIG_NAMES = ['velinstyle-cli.json', 'velin-experience.json'];
20
+
21
+ export function loadExperienceConfig(cwd = process.cwd(), overrides = {}) {
22
+ const root = resolve(cwd);
23
+ let fileConfig = {};
24
+ for (const name of CONFIG_NAMES) {
25
+ const path = join(root, name);
26
+ if (existsSync(path)) {
27
+ try {
28
+ fileConfig = JSON.parse(readFileSync(path, 'utf8'));
29
+ } catch {
30
+ fileConfig = {};
31
+ }
32
+ break;
33
+ }
34
+ }
35
+
36
+ const envTheme = process.env.VELIN_EXPERIENCE_THEME;
37
+ const envOutput = process.env.VELIN_EXPERIENCE_OUTPUT;
38
+
39
+ return {
40
+ ...DEFAULT_EXPERIENCE_CONFIG,
41
+ ...fileConfig,
42
+ ...(envTheme ? { theme: envTheme } : {}),
43
+ ...(envOutput ? { output: envOutput } : {}),
44
+ ...overrides,
45
+ };
46
+ }
47
+
48
+ export function analyticsPath(cwd = process.cwd()) {
49
+ return join(resolve(cwd), '.velin', 'experience-analytics.json');
50
+ }
51
+
52
+ export function recordUsage(commandId, cwd = process.cwd()) {
53
+ const path = analyticsPath(cwd);
54
+ mkdirSync(join(resolve(cwd), '.velin'), { recursive: true });
55
+ let data = { commands: {} };
56
+ if (existsSync(path)) {
57
+ try {
58
+ data = JSON.parse(readFileSync(path, 'utf8'));
59
+ } catch {
60
+ data = { commands: {} };
61
+ }
62
+ }
63
+ data.commands[commandId] = (data.commands[commandId] || 0) + 1;
64
+ data.updatedAt = new Date().toISOString();
65
+ writeFileSync(path, JSON.stringify(data, null, 2));
66
+ return data;
67
+ }
68
+
69
+ export function suggestFromAnalytics(cwd = process.cwd()) {
70
+ const path = analyticsPath(cwd);
71
+ if (!existsSync(path)) return null;
72
+ let data;
73
+ try {
74
+ data = JSON.parse(readFileSync(path, 'utf8'));
75
+ } catch {
76
+ return null;
77
+ }
78
+ const cmds = data.commands || {};
79
+ const hot = ['production', 'review', 'doctor'].filter((id) => (cmds[id] || 0) >= 2);
80
+ if (hot.length >= 2 && !(cmds.dashboard > 0)) {
81
+ return 'You often run production / review / doctor — try `velinstyle dashboard`.';
82
+ }
83
+ return null;
84
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Output Contract — every command fills the same shape (no free-form text).
3
+ */
4
+
5
+ export function createEmptyContract(partial = {}) {
6
+ return {
7
+ project: partial.project || {},
8
+ lifecycle: partial.lifecycle || { stage: 'init', stages: [] },
9
+ result: partial.result || { status: 'PASS', message: '' },
10
+ stats: partial.stats || {},
11
+ widgets: Array.isArray(partial.widgets) ? [...partial.widgets] : [],
12
+ suggestions: Array.isArray(partial.suggestions) ? [...partial.suggestions] : [],
13
+ diagnostics: Array.isArray(partial.diagnostics) ? [...partial.diagnostics] : [],
14
+ notifications: Array.isArray(partial.notifications) ? [...partial.notifications] : [],
15
+ meta: partial.meta || {},
16
+ };
17
+ }
18
+
19
+ export function validateContract(contract) {
20
+ const problems = [];
21
+ if (!contract || typeof contract !== 'object') {
22
+ return { ok: false, problems: ['contract must be an object'] };
23
+ }
24
+ for (const key of ['project', 'lifecycle', 'result', 'stats']) {
25
+ if (contract[key] == null || typeof contract[key] !== 'object') {
26
+ problems.push(`missing or invalid "${key}"`);
27
+ }
28
+ }
29
+ for (const key of ['widgets', 'suggestions', 'diagnostics']) {
30
+ if (!Array.isArray(contract[key])) problems.push(`"${key}" must be an array`);
31
+ }
32
+ if (contract.result && !['PASS', 'WARNING', 'ERROR', 'SUGGESTION'].includes(contract.result.status)) {
33
+ problems.push('result.status must be PASS|WARNING|ERROR|SUGGESTION');
34
+ }
35
+ return { ok: problems.length === 0, problems };
36
+ }
37
+
38
+ export function pushWidget(contract, id, props = {}) {
39
+ contract.widgets.push({ id, props });
40
+ return contract;
41
+ }
42
+
43
+ export function pushSuggestion(contract, text, opts = {}) {
44
+ contract.suggestions.push({ text, ...opts });
45
+ return contract;
46
+ }
47
+
48
+ export function pushDiagnostic(contract, diagnostic) {
49
+ contract.diagnostics.push(diagnostic);
50
+ return contract;
51
+ }
52
+
53
+ export function setResult(contract, status, message = '') {
54
+ contract.result = { status, message };
55
+ return contract;
56
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Velin Experience event bus — commands emit; renderers/plugins subscribe.
3
+ */
4
+ export function createEventBus() {
5
+ /** @type {Map<string, Set<Function>>} */
6
+ const listeners = new Map();
7
+
8
+ function on(event, handler) {
9
+ if (!listeners.has(event)) listeners.set(event, new Set());
10
+ listeners.get(event).add(handler);
11
+ return () => off(event, handler);
12
+ }
13
+
14
+ function off(event, handler) {
15
+ listeners.get(event)?.delete(handler);
16
+ }
17
+
18
+ function emit(event, payload) {
19
+ const set = listeners.get(event);
20
+ if (!set) return;
21
+ for (const handler of set) {
22
+ try {
23
+ handler(payload);
24
+ } catch (err) {
25
+ if (event !== 'onError') emit('onError', { error: err, source: event });
26
+ }
27
+ }
28
+ }
29
+
30
+ function once(event, handler) {
31
+ const wrap = (payload) => {
32
+ off(event, wrap);
33
+ handler(payload);
34
+ };
35
+ return on(event, wrap);
36
+ }
37
+
38
+ return { on, off, once, emit };
39
+ }
40
+
41
+ export const EXPERIENCE_EVENTS = [
42
+ 'onStart',
43
+ 'onProgress',
44
+ 'onWarning',
45
+ 'onSuggestion',
46
+ 'onResult',
47
+ 'onFinish',
48
+ 'onError',
49
+ 'onNotification',
50
+ ];
@@ -0,0 +1,18 @@
1
+ export { createEventBus, EXPERIENCE_EVENTS } from './event-bus.js';
2
+ export {
3
+ createEmptyContract,
4
+ validateContract,
5
+ pushWidget,
6
+ pushSuggestion,
7
+ pushDiagnostic,
8
+ setResult,
9
+ } from './contract.js';
10
+ export { LIFECYCLE_STAGES, createLifecycleTracker } from './lifecycle.js';
11
+ export {
12
+ DEFAULT_EXPERIENCE_CONFIG,
13
+ loadExperienceConfig,
14
+ recordUsage,
15
+ suggestFromAnalytics,
16
+ analyticsPath,
17
+ } from './config.js';
18
+ export { runExperienceCommand } from './runner.js';
@@ -0,0 +1,33 @@
1
+ /** Fixed lifecycle stages for every Experience command. */
2
+ export const LIFECYCLE_STAGES = [
3
+ 'init',
4
+ 'loadConfig',
5
+ 'environment',
6
+ 'input',
7
+ 'analysis',
8
+ 'processing',
9
+ 'validation',
10
+ 'result',
11
+ 'hints',
12
+ 'summary',
13
+ ];
14
+
15
+ export function createLifecycleTracker() {
16
+ const stages = [];
17
+ let current = 'init';
18
+
19
+ function enter(stage) {
20
+ if (!LIFECYCLE_STAGES.includes(stage)) {
21
+ throw new Error(`Unknown lifecycle stage: ${stage}`);
22
+ }
23
+ current = stage;
24
+ stages.push({ stage, at: new Date().toISOString() });
25
+ return current;
26
+ }
27
+
28
+ function snapshot() {
29
+ return { stage: current, stages: [...stages] };
30
+ }
31
+
32
+ return { enter, snapshot, get stage() { return current; } };
33
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Experience command runner — lifecycle + bus + contract collection.
3
+ */
4
+ import { createEventBus } from './event-bus.js';
5
+ import { createEmptyContract, validateContract } from './contract.js';
6
+ import { createLifecycleTracker } from './lifecycle.js';
7
+ import { loadExperienceConfig, recordUsage, suggestFromAnalytics } from './config.js';
8
+
9
+ /**
10
+ * @param {object} opts
11
+ * @param {string} opts.commandId
12
+ * @param {object} [opts.configOverrides]
13
+ * @param {string} [opts.cwd]
14
+ * @param {(ctx: object) => Promise<object>|object} opts.run — returns contract partial or full contract
15
+ */
16
+ export async function runExperienceCommand(opts) {
17
+ const {
18
+ commandId,
19
+ configOverrides = {},
20
+ cwd = process.cwd(),
21
+ run,
22
+ layout = 'standard',
23
+ meta = {},
24
+ } = opts;
25
+
26
+ const config = loadExperienceConfig(cwd, configOverrides);
27
+ const bus = createEventBus();
28
+ const lifecycle = createLifecycleTracker();
29
+ const startedAt = Date.now();
30
+
31
+ const events = [];
32
+ const recordEvent = (type, payload) => {
33
+ events.push({ type, payload, at: new Date().toISOString() });
34
+ };
35
+
36
+ for (const ev of ['onStart', 'onProgress', 'onWarning', 'onSuggestion', 'onResult', 'onFinish', 'onNotification']) {
37
+ bus.on(ev, (payload) => recordEvent(ev, payload));
38
+ }
39
+
40
+ lifecycle.enter('init');
41
+ bus.emit('onStart', { commandId, config });
42
+
43
+ lifecycle.enter('loadConfig');
44
+ lifecycle.enter('environment');
45
+
46
+ if (config.analytics) {
47
+ try {
48
+ recordUsage(commandId, cwd);
49
+ } catch {
50
+ /* ignore analytics failures */
51
+ }
52
+ }
53
+
54
+ let contract = createEmptyContract({
55
+ project: { cwd, commandId },
56
+ meta: { ...meta, layout, commandId, version: meta.version || null },
57
+ });
58
+
59
+ lifecycle.enter('input');
60
+ lifecycle.enter('analysis');
61
+ lifecycle.enter('processing');
62
+
63
+ const ctx = {
64
+ bus,
65
+ config,
66
+ cwd,
67
+ commandId,
68
+ lifecycle,
69
+ contract,
70
+ emitProgress: (label, percent) => bus.emit('onProgress', { label, percent }),
71
+ notify: (message, tone = 'info') => {
72
+ bus.emit('onNotification', { message, tone });
73
+ contract.notifications.push({ message, tone });
74
+ },
75
+ };
76
+
77
+ const result = await run(ctx);
78
+ if (result && typeof result === 'object') {
79
+ contract = {
80
+ ...contract,
81
+ ...result,
82
+ widgets: result.widgets || contract.widgets,
83
+ suggestions: result.suggestions || contract.suggestions,
84
+ diagnostics: result.diagnostics || contract.diagnostics,
85
+ notifications: result.notifications || contract.notifications,
86
+ project: { ...contract.project, ...(result.project || {}) },
87
+ stats: { ...contract.stats, ...(result.stats || {}) },
88
+ result: result.result || contract.result,
89
+ meta: { ...contract.meta, ...(result.meta || {}) },
90
+ };
91
+ }
92
+
93
+ lifecycle.enter('validation');
94
+ const validation = validateContract(contract);
95
+ if (!validation.ok) {
96
+ contract.diagnostics.push({
97
+ severity: 'warning',
98
+ message: `Contract validation: ${validation.problems.join('; ')}`,
99
+ });
100
+ }
101
+
102
+ lifecycle.enter('hints');
103
+ if (config.analytics) {
104
+ const tip = suggestFromAnalytics(cwd);
105
+ if (tip) {
106
+ contract.suggestions.push({ text: tip, source: 'analytics' });
107
+ bus.emit('onSuggestion', { text: tip });
108
+ }
109
+ }
110
+
111
+ lifecycle.enter('result');
112
+ contract.lifecycle = lifecycle.snapshot();
113
+ bus.emit('onResult', { result: contract.result });
114
+
115
+ lifecycle.enter('summary');
116
+ contract.meta = {
117
+ ...contract.meta,
118
+ durationMs: Date.now() - startedAt,
119
+ layout: contract.meta.layout || layout,
120
+ };
121
+ bus.emit('onFinish', { contract, durationMs: contract.meta.durationMs });
122
+
123
+ return {
124
+ contract,
125
+ config,
126
+ events,
127
+ bus,
128
+ };
129
+ }