@givolt/i3-composer-agent 2.3.2 → 2.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.agents/plugins/marketplace.json +1 -1
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/README.md +5 -1
  6. package/bin/agent-targets.json +4 -2
  7. package/bin/i3-composer-agent.cjs +48 -9
  8. package/bin/install.cjs +2 -1
  9. package/bin/native-layout.cjs +1 -1
  10. package/bin/tool-selector.cjs +24 -7
  11. package/bin/wizard.cjs +49 -15
  12. package/docs/codebuddy-setup.md +43 -4
  13. package/docs/codex-setup.md +3 -1
  14. package/docs/other-hosts.md +1 -1
  15. package/docs/ui-prototype-native/USAGE.md +4 -2
  16. package/docs/workbuddy-setup.md +20 -2
  17. package/hooks/codebuddy-upstream.cjs +93 -0
  18. package/hooks/i3-prototype.cjs +12 -0
  19. package/hooks/manage.cjs +170 -0
  20. package/package.json +35 -1
  21. package/plugin.json +1 -1
  22. package/scripts/prototype-cache-test.js +55 -0
  23. package/scripts/prototype-generation-entry-test.js +6 -0
  24. package/scripts/prototype-generation-evidence-test.js +10 -0
  25. package/scripts/prototype-generation-native-test.js +78 -0
  26. package/scripts/prototype-generation-test.js +99 -0
  27. package/scripts/prototype-inventory-test.js +43 -0
  28. package/scripts/prototype-locations-test.js +21 -0
  29. package/scripts/prototype-project-test.js +183 -0
  30. package/scripts/ui-prototype-buddy-lifecycle-test.js +89 -0
  31. package/scripts/ui-prototype-codebuddy-hook-test.js +106 -0
  32. package/scripts/ui-prototype-dependencies-test.js +13 -0
  33. package/scripts/ui-prototype-execution-evidence-test.js +62 -0
  34. package/scripts/ui-prototype-execution-gates-test.js +90 -0
  35. package/scripts/ui-prototype-execution-policy-test.js +53 -0
  36. package/scripts/ui-prototype-execution-runtime-test.js +27 -0
  37. package/scripts/ui-prototype-ide-source-test.js +73 -0
  38. package/scripts/ui-prototype-recovery-test.js +72 -0
  39. package/scripts/ui-prototype-registration-test.js +53 -0
  40. package/scripts/ui-prototype-transcript-auto-test.js +67 -0
  41. package/skills/i3-frontend-ui-engineering/SKILL.md +1 -1
  42. package/skills/i3-incremental-implementation/SKILL.md +1 -1
  43. package/skills/i3-ui-prototype/SKILL.md +29 -7
  44. package/skills/i3-ui-prototype/assets/capabilities/generation.json +833 -0
  45. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/generation-component-map.json +300 -0
  46. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/App.vue +14 -3
  47. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/PrototypeShell.vue +59 -0
  48. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/config.ts +6 -1
  49. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/domain/presentation.ts +29 -0
  50. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/style.css +13 -0
  51. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/tests/presentation.test.ts +23 -0
  52. package/skills/i3-ui-prototype/assets/hosts/codebuddy/profile.json +17 -0
  53. package/skills/i3-ui-prototype/assets/optional-skills/host-targets.json +4 -2
  54. package/skills/i3-ui-prototype/assets/templates/catalog.json +2 -1
  55. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/component-inventory.json +707 -0
  56. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/generation.json +49 -0
  57. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/template-inventory.json +28791 -0
  58. package/skills/i3-ui-prototype/assets/templates/gallery/index.html +1 -1
  59. package/skills/i3-ui-prototype/assets/templates/generation-options.js +42 -0
  60. package/skills/i3-ui-prototype/assets/templates/presentation.html +17 -0
  61. package/skills/i3-ui-prototype/references/artifact-contract.md +1 -1
  62. package/skills/i3-ui-prototype/references/content-template.md +9 -4
  63. package/skills/i3-ui-prototype/references/entry-contract.md +1 -1
  64. package/skills/i3-ui-prototype/references/execution-evidence.md +83 -0
  65. package/skills/i3-ui-prototype/references/generation-contract.md +122 -0
  66. package/skills/i3-ui-prototype/references/interaction-acceptance.md +5 -1
  67. package/skills/i3-ui-prototype/references/optional-skills.md +4 -0
  68. package/skills/i3-ui-prototype/references/project-layout.md +92 -0
  69. package/skills/i3-ui-prototype/references/template-adapters.md +6 -0
  70. package/skills/i3-ui-prototype/references/workflow-contract.md +4 -0
  71. package/skills/i3-ui-prototype/scripts/browser-workspace.cjs +72 -0
  72. package/skills/i3-ui-prototype/scripts/buddy-host.cjs +40 -0
  73. package/skills/i3-ui-prototype/scripts/codebuddy-context.cjs +75 -0
  74. package/skills/i3-ui-prototype/scripts/codebuddy-hook.cjs +196 -0
  75. package/skills/i3-ui-prototype/scripts/codebuddy-ide-source.cjs +69 -0
  76. package/skills/i3-ui-prototype/scripts/codebuddy-registration.cjs +125 -0
  77. package/skills/i3-ui-prototype/scripts/content-template.cjs +2 -2
  78. package/skills/i3-ui-prototype/scripts/execution-assertions.cjs +43 -0
  79. package/skills/i3-ui-prototype/scripts/execution-evidence.cjs +146 -0
  80. package/skills/i3-ui-prototype/scripts/execution-gates.cjs +168 -0
  81. package/skills/i3-ui-prototype/scripts/execution-policy.cjs +62 -0
  82. package/skills/i3-ui-prototype/scripts/execution-recovery.cjs +52 -0
  83. package/skills/i3-ui-prototype/scripts/execution-runtime.cjs +60 -0
  84. package/skills/i3-ui-prototype/scripts/generation-assets.cjs +18 -0
  85. package/skills/i3-ui-prototype/scripts/generation-contract.cjs +214 -0
  86. package/skills/i3-ui-prototype/scripts/generation-evidence.cjs +50 -0
  87. package/skills/i3-ui-prototype/scripts/generation-replan.cjs +34 -0
  88. package/skills/i3-ui-prototype/scripts/hosts/codebuddy.cjs +20 -0
  89. package/skills/i3-ui-prototype/scripts/native-command.cjs +18 -0
  90. package/skills/i3-ui-prototype/scripts/native-delivery.cjs +17 -5
  91. package/skills/i3-ui-prototype/scripts/native-devtools.cjs +1 -0
  92. package/skills/i3-ui-prototype/scripts/native-pilot-lib.cjs +64 -18
  93. package/skills/i3-ui-prototype/scripts/native-pilot.cjs +23 -3
  94. package/skills/i3-ui-prototype/scripts/project-registry.cjs +126 -0
  95. package/skills/i3-ui-prototype/scripts/prototype-dependencies.cjs +28 -6
  96. package/skills/i3-ui-prototype/scripts/prototype-hooks.cjs +87 -0
  97. package/skills/i3-ui-prototype/scripts/prototype-lib.cjs +5 -7
  98. package/skills/i3-ui-prototype/scripts/prototype-locations.cjs +5 -1
  99. package/skills/i3-ui-prototype/scripts/template-contract.cjs +11 -2
  100. package/skills/i3-ui-prototype/scripts/template-inventory.cjs +78 -0
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+ const assert = require('node:assert/strict');
2
3
  const fs = require('node:fs');
3
4
  const path = require('node:path');
4
5
  const crypto = require('node:crypto');
@@ -300,6 +301,7 @@ function checkCapabilities({ definition, bindings = [], regions = [], scope = {}
300
301
  }
301
302
  function syncTemplate(directory, { write = true } = {}) {
302
303
  directory = path.resolve(directory);
304
+ if (write && fs.existsSync(path.join(directory, 'component-inventory.json'))) fs.writeFileSync(path.join(directory, 'template-inventory.json'), JSON.stringify(require('./template-inventory.cjs').scan(directory), null, 2) + '\n');
303
305
  const files = filesUnder(directory);
304
306
  const manifest = readJson(localFile(directory, 'template.json'));
305
307
  identifier(manifest.id, 'template');
@@ -345,8 +347,15 @@ function loadTemplate(directory) {
345
347
  if (!stored || canonical(stored) !== canonical(expected)) throw new Error('Template index is stale / 源和索引不同步; run sync-template.cjs');
346
348
  return templateSnapshot(directory, expected);
347
349
  }
348
- function loadBundledTemplate() {
349
- const directory = path.join(ASSETS, 'templates/enterprise-admin');
350
+ function loadBundledTemplate(templateId, { templatesRoot = path.join(ASSETS, 'templates') } = {}) {
351
+ const catalog = readJson(localFile(templatesRoot, 'catalog.json'));
352
+ templateId ||= catalog.defaultTemplate; identifier(templateId, 'template');
353
+ const selected = catalog.templates.find(item => item.id === templateId);
354
+ assert.ok(selected?.availability === 'available' && selected.contentRoot, `Unsupported or not available native template: ${templateId}`);
355
+ const directory = localFile(templatesRoot, selected.contentRoot);
356
+ const manifest = readJson(localFile(directory, 'template.json'));
357
+ assert.equal(manifest.kind, 'content-reference', 'Native templates require an explicit content-reference manifest');
358
+ assert.equal(manifest.id, templateId, 'Template catalog identity differs from its manifest');
350
359
  return templateSnapshot(directory, syncTemplate(directory, { write: false }));
351
360
  }
352
361
  function prepareTemplate({ project, id = 'enterprise-admin' }) {
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+ const fs = require('node:fs'), path = require('node:path'), crypto = require('node:crypto'), assert = require('node:assert/strict');
3
+ const digest = value => crypto.createHash('sha256').update(value).digest('hex');
4
+ const ignored = new Set(['template-inventory.json', 'capabilities.generated.json']);
5
+ function files(directory, prefix = '') {
6
+ return fs.readdirSync(path.join(directory, prefix), { withFileTypes: true }).sort((a,b) => a.name.localeCompare(b.name, 'en')).flatMap(entry => {
7
+ const name = prefix ? `${prefix}/${entry.name}` : entry.name;
8
+ assert.ok(!entry.isSymbolicLink(), `Refusing template symlink: ${name}`);
9
+ if (['.git', 'node_modules'].includes(entry.name) || ignored.has(name)) return [];
10
+ return entry.isDirectory() ? files(directory, name) : [name];
11
+ });
12
+ }
13
+ function attributes(text) {
14
+ const value = {};
15
+ for (const match of text.matchAll(/([^\s=<>/'"`]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g)) {
16
+ assert.ok(!Object.hasOwn(value, match[1]), `Duplicate HTML attribute: ${match[1]}`);
17
+ value[match[1]] = match[2] ?? match[3] ?? match[4] ?? '';
18
+ }
19
+ return value;
20
+ }
21
+ function scan(directory) {
22
+ directory = fs.realpathSync(directory);
23
+ const sourceFiles = files(directory), hashes = Object.fromEntries(sourceFiles.map(file => [file, digest(fs.readFileSync(path.join(directory, file)))]));
24
+ const html = fs.readFileSync(path.join(directory, 'index.html'), 'utf8');
25
+ const policy = JSON.parse(fs.readFileSync(path.join(directory, 'component-inventory.json'), 'utf8'));
26
+ return scanResources(hashes, html, policy);
27
+ }
28
+ function scanResources(hashes, html, policy) {
29
+ const routesMatch = html.match(/id="prototype-offline-routes">([\s\S]*?)<\/script>/);
30
+ assert.ok(routesMatch, 'Template inventory requires explicit offline routes');
31
+ const routes = JSON.parse(routesMatch[1]);
32
+ assert.equal(policy.schemaVersion, 1, 'Unsupported component inventory classification');
33
+ const fragments = [], components = {}, unknownComponents = new Set(); let eventCount = 0;
34
+ for (const match of html.matchAll(/^<template data-source="([^"]+)">\n([\s\S]*?)^<\/template>/gm)) {
35
+ const file = match[1], content = match[2], nodes = [];
36
+ assert.ok(!fragments.some(fragment => fragment.file === file), `Duplicate HTML fragment: ${file}`);
37
+ const cleaned = content.replace(/<!--[\s\S]*?-->/g, text => ' '.repeat(text.length));
38
+ for (const token of cleaned.matchAll(/<([a-zA-Z][\w:.-]*)(\s(?:[^"'<>]|"[^"]*"|'[^']*')*)?\s*\/?\s*>/g)) {
39
+ const attrs = attributes(token[2] || ''), node = { id: `${file}#${nodes.length + 1}`, tag: token[1], attributes: attrs };
40
+ nodes.push(node);
41
+ eventCount += Object.keys(attrs).filter(key => key.startsWith('data-on-')).length;
42
+ const component = attrs['data-component'];
43
+ if (!component) continue;
44
+ const classification = policy.components?.[component];
45
+ if (!classification) unknownComponents.add(component);
46
+ else {
47
+ assert.ok(['framework', 'business-reference', 'icon', 'router', 'dynamic'].includes(classification.kind), `Unknown classification: ${component}`);
48
+ assert.ok(Array.isArray(classification.capabilities), `Missing semantic classification: ${component}`);
49
+ if (classification.source) assert.ok(fragments.some(item => item.file === classification.source) || html.includes(`<template data-source="${classification.source}">`), `Missing component source: ${component}`);
50
+ }
51
+ components[component] ||= { classification: classification || null, occurrences: [] };
52
+ components[component].occurrences.push(node.id);
53
+ }
54
+ fragments.push({ file, digest: digest(content), nodes });
55
+ }
56
+ assert.ok(fragments.length, 'Template inventory requires editable fragments');
57
+ assert.equal(unknownComponents.size, 0, `Unclassified template components: ${[...unknownComponents].join(', ')}`);
58
+ const pages = [...new Set(routes.map(route => route.component).filter(Boolean))].map(component => {
59
+ const fragment = 'reference/' + component.replace(/^\.\//, '').replace(/\.js$/, '.html');
60
+ assert.ok(fragments.some(item => item.file === fragment), `Route has no editable HTML: ${component}`);
61
+ return { component, fragment, routes: routes.filter(route => route.component === component).map(route => route.path) };
62
+ });
63
+ return { schemaVersion: 1, files: hashes, routes, pages, fragments, components, eventCount, unknownComponents: [] };
64
+ }
65
+ function verify(directory, inventory) {
66
+ assert.deepEqual(scan(directory), inventory, 'Template inventory changed; review every source/component/interaction difference and regenerate the inventory');
67
+ return { status: 'passed', files: Object.keys(inventory.files).length, routes: inventory.routes.length, pages: inventory.pages.length, fragments: inventory.fragments.length, components: Object.keys(inventory.components).length, events: inventory.eventCount };
68
+ }
69
+ function verifySnapshot(snapshot) {
70
+ const resources = new Map(snapshot.files.map(file => [file.path, Buffer.from(file.content, 'base64')]));
71
+ assert.equal(resources.size, snapshot.files.length, 'Duplicate template snapshot resource');
72
+ const hashes = Object.fromEntries([...resources].filter(([file]) => !ignored.has(file)).map(([file, content]) => [file, digest(content)]));
73
+ const expected = JSON.parse(resources.get('template-inventory.json').toString('utf8'));
74
+ const actual = scanResources(hashes, resources.get('index.html').toString('utf8'), JSON.parse(resources.get('component-inventory.json').toString('utf8')));
75
+ assert.deepEqual(actual, expected, 'Template snapshot inventory differs from actual sources/components/events');
76
+ return actual;
77
+ }
78
+ module.exports = { scan, verify, verifySnapshot };