@atlassian/chippy 0.0.0-stage → 0.0.1-beta.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.
Files changed (126) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +188 -2
  3. package/bin/chippy.mjs +134 -0
  4. package/bin/pack.mjs +184 -0
  5. package/lib/avalanche.js +2570 -0
  6. package/lib/avalanche.js.map +7 -0
  7. package/lib/bootstrap.d.ts +1 -0
  8. package/lib/bootstrap.js +51 -0
  9. package/lib/bootstrap.js.map +7 -0
  10. package/lib/browser.js +15 -0
  11. package/lib/browser.js.map +7 -0
  12. package/lib/extension/avalanche.es.js +5396 -0
  13. package/lib/extension/avalanche.es.js.map +1 -0
  14. package/lib/extension/chippy-page-bridge.js +1833 -0
  15. package/lib/extension/inspect-react.js +216 -0
  16. package/lib/extension/layout-lab.js +1453 -0
  17. package/lib/extension/panel-dom.js +90 -0
  18. package/lib/extension/resolve-probes.js +342 -0
  19. package/lib/mcp-skill/LICENSE +201 -0
  20. package/lib/mcp-skill/README.md +150 -0
  21. package/lib/mcp-skill/SKILL.md +330 -0
  22. package/lib/mcp-skill/browser-scripts/playwright-probes.js +368 -0
  23. package/lib/mcp-skill/browser-scripts/playwright-session.js +114 -0
  24. package/lib/mcp-skill/extension/avalanche.es.js +5396 -0
  25. package/lib/mcp-skill/extension/avalanche.es.js.map +1 -0
  26. package/lib/mcp-skill/extension/avalanche.iife.js +5397 -0
  27. package/lib/mcp-skill/extension/avalanche.iife.js.map +1 -0
  28. package/lib/mcp-skill/extension/background.js +323 -0
  29. package/lib/mcp-skill/extension/chippy-page-bridge.js +1833 -0
  30. package/lib/mcp-skill/extension/content-script.js +167 -0
  31. package/lib/mcp-skill/extension/devtools-hook-shim.js +68 -0
  32. package/lib/mcp-skill/extension/early-bootstrap.js +47 -0
  33. package/lib/mcp-skill/extension/icon-128.png +0 -0
  34. package/lib/mcp-skill/extension/icon-16.png +0 -0
  35. package/lib/mcp-skill/extension/icon-32.png +0 -0
  36. package/lib/mcp-skill/extension/icon-48.png +0 -0
  37. package/lib/mcp-skill/extension/inspect-react.js +216 -0
  38. package/lib/mcp-skill/extension/layout-lab.js +1453 -0
  39. package/lib/mcp-skill/extension/logo.svg +14 -0
  40. package/lib/mcp-skill/extension/manifest.json +69 -0
  41. package/lib/mcp-skill/extension/panel-dom.js +90 -0
  42. package/lib/mcp-skill/extension/panel-settings.js +191 -0
  43. package/lib/mcp-skill/extension/popup.html +41 -0
  44. package/lib/mcp-skill/extension/popup.js +80 -0
  45. package/lib/mcp-skill/extension/probe-descriptor.js +241 -0
  46. package/lib/mcp-skill/extension/resolve-probes.js +342 -0
  47. package/lib/mcp-skill/package.json +1 -0
  48. package/lib/mcp-skill/references/README.md +56 -0
  49. package/lib/mcp-skill/references/active-stimulus.md +85 -0
  50. package/lib/mcp-skill/references/avalanche-detector.md +731 -0
  51. package/lib/mcp-skill/references/bdd-router.md +310 -0
  52. package/lib/mcp-skill/references/browser-runtime-api.md +193 -0
  53. package/lib/mcp-skill/references/command-output-formats.md +1075 -0
  54. package/lib/mcp-skill/references/command-surface.md +183 -0
  55. package/lib/mcp-skill/references/cynefin.md +99 -0
  56. package/lib/mcp-skill/references/debug-hook.md +161 -0
  57. package/lib/mcp-skill/references/fixing-performance.md +490 -0
  58. package/lib/mcp-skill/references/hook-metrics.md +199 -0
  59. package/lib/mcp-skill/references/hooks-inspection.md +61 -0
  60. package/lib/mcp-skill/references/jira-taxonomy.md +81 -0
  61. package/lib/mcp-skill/references/mcp.md +122 -0
  62. package/lib/mcp-skill/references/mode-a-injection.md +68 -0
  63. package/lib/mcp-skill/references/operating-model.md +128 -0
  64. package/lib/mcp-skill/references/playwright-cli-setup.md +105 -0
  65. package/lib/mcp-skill/references/playwright-probes.md +205 -0
  66. package/lib/mcp-skill/references/procedures.md +545 -0
  67. package/lib/mcp-skill/references/recipes.txt +114 -0
  68. package/lib/mcp-skill/references/stability-render.md +428 -0
  69. package/lib/mcp-skill/references/test-mode-playbook.md +193 -0
  70. package/lib/mcp-skill/references/troubleshooting.md +54 -0
  71. package/lib/mcp-skill/scripts/avalanche-analyze.cjs +495 -0
  72. package/lib/mcp-skill/scripts/build-avalanche-command.js +34 -0
  73. package/lib/mcp-skill/scripts/build-fiber-state-command.js +19 -0
  74. package/lib/mcp-skill/scripts/chippy-helper-server.js +1710 -0
  75. package/lib/mcp-skill/scripts/chippy-mcp.cjs +273 -0
  76. package/lib/mcp-skill/scripts/chippy.sh +1218 -0
  77. package/lib/mcp-skill/scripts/format-probe-result.js +34 -0
  78. package/lib/mcp-skill/scripts/helper-access.js +144 -0
  79. package/lib/mcp-skill/scripts/probe-annotation-channel.js +116 -0
  80. package/lib/mcp-skill/scripts/search-recipes.js +248 -0
  81. package/lib/mcp-skill/snippets/README.md +89 -0
  82. package/lib/mcp-skill/snippets/count-context-consumers.js +40 -0
  83. package/lib/mcp-skill/snippets/find-context-providers.js +38 -0
  84. package/lib/mcp-skill/snippets/find-legacy-context.js +47 -0
  85. package/lib/mcp-skill/snippets/find-prop.js +83 -0
  86. package/lib/mcp-skill/snippets/hook-metrics-sample.js +69 -0
  87. package/lib/mcp-skill/snippets/hook-metrics.js +60 -0
  88. package/lib/mcp-skill/snippets/hook-tracker.js +198 -0
  89. package/lib/mcp-skill/snippets/stability-nudge.js +101 -0
  90. package/lib/package.json +1 -0
  91. package/lib/plugin.js +119 -0
  92. package/lib/plugin.js.map +7 -0
  93. package/lib/storybook/avalanche-panel-model.js +79 -0
  94. package/lib/storybook/brand.jsx +6 -0
  95. package/lib/storybook/connection-fallback.jsx +47 -0
  96. package/lib/storybook/constants.js +15 -0
  97. package/lib/storybook/context-card.jsx +171 -0
  98. package/lib/storybook/manager.jsx +403 -0
  99. package/lib/storybook/preset.js +7 -0
  100. package/lib/storybook/preview.js +263 -0
  101. package/lib/storybook/probe-annotation-editor.jsx +52 -0
  102. package/lib/storybook/probe-intent.js +23 -0
  103. package/lib/storybook/setup.js +93 -0
  104. package/lib/types/avalanche/commit-metrics-adapter.d.ts +76 -0
  105. package/lib/types/avalanche/commit-stats-indices.d.ts +41 -0
  106. package/lib/types/avalanche/commit-surface-ownership.d.ts +9 -0
  107. package/lib/types/avalanche/detector.d.ts +183 -0
  108. package/lib/types/avalanche/interaction-capture.d.ts +25 -0
  109. package/lib/types/avalanche/internals.d.ts +189 -0
  110. package/lib/types/avalanche/report-serializer.d.ts +283 -0
  111. package/lib/types/avalanche/reporter.d.ts +610 -0
  112. package/lib/types/chippy-bridge/fiber-source.d.ts +37 -0
  113. package/lib/types/chippy-bridge/react-renderer.d.ts +49 -0
  114. package/lib/types/chippy-bridge/source-map.d.ts +30 -0
  115. package/lib/types/debug-render/core.d.ts +72 -0
  116. package/lib/types/layout-lab/fiber-boundaries.d.ts +51 -0
  117. package/lib/types/layout-lab/fiber-walk.d.ts +131 -0
  118. package/lib/types/layout-lab/marker-context.d.ts +94 -0
  119. package/lib/types/package/avalanche.d.ts +6 -0
  120. package/lib/types/package/browser.d.ts +10 -0
  121. package/lib/types/package/plugin.d.ts +15 -0
  122. package/lib/types/plugins/atl/commit-metrics-adapter.d.ts +65 -0
  123. package/lib/types/plugins/atl/metrics-slot.d.ts +37 -0
  124. package/lib/types/plugins/index.d.ts +49 -0
  125. package/lib/types/plugins/jira/context-provider.d.ts +36 -0
  126. package/package.json +104 -4
package/bin/pack.mjs ADDED
@@ -0,0 +1,184 @@
1
+ // Builds a custom Chippy skill and extension from this package: bundles the
2
+ // configured plugins into the runtime, sets the build's default plugins and
3
+ // extension identity, and applies a documentation overlay.
4
+ // npx @atlassian/chippy pack [--config chippy.config.mjs]
5
+ import { execFileSync } from 'node:child_process';
6
+ import {
7
+ appendFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync,
8
+ } from 'node:fs';
9
+ import { dirname, join, relative, resolve } from 'node:path';
10
+ import { fileURLToPath, pathToFileURL } from 'node:url';
11
+
12
+ const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
13
+ const BUILTIN_PLUGINS = [
14
+ { id: 'jira', label: 'Jira ownership markers' },
15
+ { id: 'atl', label: 'ATL commit metrics' },
16
+ ];
17
+ const RUNTIME_BUNDLES = ['extension/avalanche.iife.js', 'extension/avalanche.es.js'];
18
+ const BUILD_CONFIG_CONSUMERS = ['extension/background.js', 'extension/panel-settings.js'];
19
+ const PLUGIN_ID = /^[a-z][a-z0-9-]*$/;
20
+ const CHROME_VERSION = /^\d+(\.\d+){0,3}$/;
21
+
22
+ function fail(message) {
23
+ throw new Error(`chippy pack: ${message}`);
24
+ }
25
+
26
+ /** Validates a pack config and resolves its paths against `baseDir`. */
27
+ export function normalizePackConfig(config, baseDir) {
28
+ if (!config || typeof config !== 'object') fail('the config must export an object');
29
+ const plugins = (config.plugins ?? []).map((plugin, index) => {
30
+ if (!plugin || typeof plugin !== 'object') fail(`plugins[${index}] must be { id, entry, label? }`);
31
+ if (!PLUGIN_ID.test(plugin.id ?? '')) fail(`plugins[${index}].id must match ${PLUGIN_ID}`);
32
+ if (BUILTIN_PLUGINS.some(({ id }) => id === plugin.id)) fail(`plugin id ${plugin.id} is built in`);
33
+ if (typeof plugin.entry !== 'string') fail(`plugins[${index}].entry must be a path`);
34
+ const entry = resolve(baseDir, plugin.entry);
35
+ if (!existsSync(entry)) fail(`plugin entry not found: ${entry}`);
36
+ return { id: plugin.id, label: plugin.label ?? plugin.id, entry };
37
+ });
38
+ const ids = new Set(plugins.map(({ id }) => id));
39
+ if (ids.size !== plugins.length) fail('plugin ids must be unique');
40
+ const known = new Set([...BUILTIN_PLUGINS.map(({ id }) => id), ...ids]);
41
+ const defaultPlugins = config.defaultPlugins ?? [];
42
+ for (const id of defaultPlugins) if (!known.has(id)) fail(`defaultPlugins names an unknown plugin: ${id}`);
43
+ const extension = config.extension ?? {};
44
+ if (extension.version !== undefined && !CHROME_VERSION.test(extension.version)) {
45
+ fail('extension.version must be one to four dot-separated integers');
46
+ }
47
+ const overlay = config.skill?.overlay ? resolve(baseDir, config.skill.overlay) : null;
48
+ if (overlay && !existsSync(overlay)) fail(`skill overlay not found: ${overlay}`);
49
+ return {
50
+ plugins,
51
+ defaultPlugins: [...defaultPlugins],
52
+ extension,
53
+ overlay,
54
+ outDir: resolve(baseDir, config.outDir ?? 'chippy-build'),
55
+ };
56
+ }
57
+
58
+ /** Imports a pack config module and normalizes it against its own directory. */
59
+ export async function loadPackConfig(file) {
60
+ if (!existsSync(file)) fail(`config not found: ${file}`);
61
+ const { default: config } = await import(pathToFileURL(file).href);
62
+ return normalizePackConfig(config, dirname(file));
63
+ }
64
+
65
+ async function loadEsbuild() {
66
+ try {
67
+ return await import('esbuild');
68
+ } catch (error) {
69
+ if (error?.code !== 'ERR_MODULE_NOT_FOUND') throw error;
70
+ fail('bundling plugins needs esbuild; add it to your devDependencies');
71
+ }
72
+ }
73
+
74
+ /** Bundles the plugin registrations and build defaults into one page script. */
75
+ export async function bundlePlugins(plugins, defaultPlugins) {
76
+ if (plugins.length === 0 && defaultPlugins.length === 0) return '';
77
+ const lines = plugins.map(({ entry }, index) => `import plugin${index} from ${JSON.stringify(entry)};`);
78
+ lines.push(
79
+ 'const api = globalThis.__AVALANCHE__;',
80
+ "if (!api || typeof api.registerPlugin !== 'function') {",
81
+ " console.warn('[chippy] this build\\'s plugins need a Chippy runtime with registerPlugin()');",
82
+ '} else {',
83
+ ...plugins.map(({ id, label }, index) =>
84
+ ` api.registerPlugin({ ...plugin${index}, id: ${JSON.stringify(id)}, label: ${JSON.stringify(label)} });`),
85
+ ` api.enableDefaultPlugins(${JSON.stringify(defaultPlugins)});`,
86
+ '}',
87
+ );
88
+ const { build } = await loadEsbuild();
89
+ const result = await build({
90
+ stdin: { contents: lines.join('\n'), resolveDir: process.cwd(), sourcefile: 'chippy-plugins.js', loader: 'js' },
91
+ bundle: true, format: 'iife', platform: 'browser', target: 'es2022', write: false, legalComments: 'none',
92
+ });
93
+ return result.outputFiles[0].text;
94
+ }
95
+
96
+ // Inserts `code` before the bundle's sourceMappingURL comment, so the existing
97
+ // map still lines up with the core bundle.
98
+ function appendToBundle(file, code) {
99
+ const source = readFileSync(file, 'utf8');
100
+ const marker = source.lastIndexOf('\n//# sourceMappingURL=');
101
+ const block = `\n;/* chippy pack: plugins */\n${code}`;
102
+ writeFileSync(file, marker < 0 ? source + block : source.slice(0, marker) + block + source.slice(marker));
103
+ }
104
+
105
+ function prependBuildConfig(file, buildConfig) {
106
+ const source = readFileSync(file, 'utf8');
107
+ writeFileSync(file, `globalThis.__CHIPPY_BUILD = ${JSON.stringify(buildConfig)};\n${source}`);
108
+ }
109
+
110
+ function updateManifest(file, extension) {
111
+ const manifest = JSON.parse(readFileSync(file, 'utf8'));
112
+ if (extension.name) {
113
+ manifest.name = extension.name;
114
+ if (manifest.action) manifest.action.default_title = extension.name;
115
+ }
116
+ for (const field of ['version', 'description', 'key']) {
117
+ if (extension[field] !== undefined) manifest[field] = extension[field];
118
+ }
119
+ writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\n`);
120
+ }
121
+
122
+ // Copies the overlay onto the skill. `<name>.append.md` appends to `<name>.md`.
123
+ function applyOverlay(overlay, skillDir) {
124
+ for (const entry of readdirSync(overlay, { recursive: true })) {
125
+ const source = join(overlay, entry);
126
+ if (statSync(source).isDirectory()) continue;
127
+ const appendTarget = entry.match(/^(.*)\.append\.md$/);
128
+ if (appendTarget) {
129
+ const target = join(skillDir, `${appendTarget[1]}.md`);
130
+ if (!existsSync(target)) fail(`overlay ${entry} appends to a missing file: ${relative(skillDir, target)}`);
131
+ appendFileSync(target, `\n${readFileSync(source, 'utf8')}`);
132
+ } else {
133
+ mkdirSync(dirname(join(skillDir, entry)), { recursive: true });
134
+ cpSync(source, join(skillDir, entry));
135
+ }
136
+ }
137
+ }
138
+
139
+ function zip(archive, cwd) {
140
+ rmSync(archive, { force: true });
141
+ execFileSync('zip', ['-qr', archive, '.'], { cwd });
142
+ }
143
+
144
+ /**
145
+ * Writes `<outDir>/chippy` (the skill, with the extension in `extension/`),
146
+ * `<outDir>/chippy-skill.zip`, and `<outDir>/chippy-extension.zip`.
147
+ */
148
+ export async function packChippy(config, { source = join(PACKAGE_ROOT, 'lib', 'mcp-skill'), zips = true } = {}) {
149
+ if (!existsSync(join(source, 'SKILL.md'))) fail(`skill not found at ${source}`);
150
+ const { plugins, defaultPlugins, extension, overlay, outDir } = config;
151
+ const code = await bundlePlugins(plugins, defaultPlugins);
152
+ const skillDir = join(outDir, 'chippy');
153
+ rmSync(skillDir, { recursive: true, force: true });
154
+ mkdirSync(outDir, { recursive: true });
155
+ cpSync(source, skillDir, {
156
+ recursive: true,
157
+ filter: (path) => !['.DS_Store', '.gaps', '.provenance', '__MACOSX'].includes(path.split('/').at(-1)),
158
+ });
159
+ if (code) for (const bundle of RUNTIME_BUNDLES) appendToBundle(join(skillDir, bundle), code);
160
+ const buildConfig = {
161
+ plugins: [...BUILTIN_PLUGINS, ...plugins.map(({ id, label }) => ({ id, label }))],
162
+ defaultPlugins,
163
+ };
164
+ for (const file of BUILD_CONFIG_CONSUMERS) prependBuildConfig(join(skillDir, file), buildConfig);
165
+ updateManifest(join(skillDir, 'extension', 'manifest.json'), extension);
166
+ if (overlay) applyOverlay(overlay, skillDir);
167
+ const license = join(PACKAGE_ROOT, 'LICENSE');
168
+ for (const target of [skillDir, join(skillDir, 'extension')]) {
169
+ if (existsSync(license) && !existsSync(join(target, 'LICENSE'))) cpSync(license, join(target, 'LICENSE'));
170
+ }
171
+ const result = { skillDir };
172
+ if (zips) {
173
+ const stage = join(outDir, '.stage');
174
+ rmSync(stage, { recursive: true, force: true });
175
+ mkdirSync(stage);
176
+ cpSync(skillDir, join(stage, 'chippy'), { recursive: true });
177
+ result.skillZip = join(outDir, 'chippy-skill.zip');
178
+ result.extensionZip = join(outDir, 'chippy-extension.zip');
179
+ zip(result.skillZip, stage);
180
+ zip(result.extensionZip, join(skillDir, 'extension'));
181
+ rmSync(stage, { recursive: true, force: true });
182
+ }
183
+ return result;
184
+ }