@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.
- package/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +5 -1
- package/bin/agent-targets.json +4 -2
- package/bin/i3-composer-agent.cjs +48 -9
- package/bin/install.cjs +2 -1
- package/bin/native-layout.cjs +1 -1
- package/bin/tool-selector.cjs +24 -7
- package/bin/wizard.cjs +49 -15
- package/docs/codebuddy-setup.md +43 -4
- package/docs/codex-setup.md +3 -1
- package/docs/other-hosts.md +1 -1
- package/docs/ui-prototype-native/USAGE.md +4 -2
- package/docs/workbuddy-setup.md +20 -2
- package/hooks/codebuddy-upstream.cjs +93 -0
- package/hooks/i3-prototype.cjs +12 -0
- package/hooks/manage.cjs +170 -0
- package/package.json +35 -1
- package/plugin.json +1 -1
- package/scripts/prototype-cache-test.js +55 -0
- package/scripts/prototype-generation-entry-test.js +6 -0
- package/scripts/prototype-generation-evidence-test.js +10 -0
- package/scripts/prototype-generation-native-test.js +78 -0
- package/scripts/prototype-generation-test.js +99 -0
- package/scripts/prototype-inventory-test.js +43 -0
- package/scripts/prototype-locations-test.js +21 -0
- package/scripts/prototype-project-test.js +183 -0
- package/scripts/ui-prototype-buddy-lifecycle-test.js +89 -0
- package/scripts/ui-prototype-codebuddy-hook-test.js +106 -0
- package/scripts/ui-prototype-dependencies-test.js +13 -0
- package/scripts/ui-prototype-execution-evidence-test.js +62 -0
- package/scripts/ui-prototype-execution-gates-test.js +90 -0
- package/scripts/ui-prototype-execution-policy-test.js +53 -0
- package/scripts/ui-prototype-execution-runtime-test.js +27 -0
- package/scripts/ui-prototype-ide-source-test.js +73 -0
- package/scripts/ui-prototype-recovery-test.js +72 -0
- package/scripts/ui-prototype-registration-test.js +53 -0
- package/scripts/ui-prototype-transcript-auto-test.js +67 -0
- package/skills/i3-frontend-ui-engineering/SKILL.md +1 -1
- package/skills/i3-incremental-implementation/SKILL.md +1 -1
- package/skills/i3-ui-prototype/SKILL.md +29 -7
- package/skills/i3-ui-prototype/assets/capabilities/generation.json +833 -0
- package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/generation-component-map.json +300 -0
- package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/App.vue +14 -3
- package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/PrototypeShell.vue +59 -0
- package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/config.ts +6 -1
- package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/domain/presentation.ts +29 -0
- package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/style.css +13 -0
- package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/tests/presentation.test.ts +23 -0
- package/skills/i3-ui-prototype/assets/hosts/codebuddy/profile.json +17 -0
- package/skills/i3-ui-prototype/assets/optional-skills/host-targets.json +4 -2
- package/skills/i3-ui-prototype/assets/templates/catalog.json +2 -1
- package/skills/i3-ui-prototype/assets/templates/enterprise-admin/component-inventory.json +707 -0
- package/skills/i3-ui-prototype/assets/templates/enterprise-admin/generation.json +49 -0
- package/skills/i3-ui-prototype/assets/templates/enterprise-admin/template-inventory.json +28791 -0
- package/skills/i3-ui-prototype/assets/templates/gallery/index.html +1 -1
- package/skills/i3-ui-prototype/assets/templates/generation-options.js +42 -0
- package/skills/i3-ui-prototype/assets/templates/presentation.html +17 -0
- package/skills/i3-ui-prototype/references/artifact-contract.md +1 -1
- package/skills/i3-ui-prototype/references/content-template.md +9 -4
- package/skills/i3-ui-prototype/references/entry-contract.md +1 -1
- package/skills/i3-ui-prototype/references/execution-evidence.md +83 -0
- package/skills/i3-ui-prototype/references/generation-contract.md +122 -0
- package/skills/i3-ui-prototype/references/interaction-acceptance.md +5 -1
- package/skills/i3-ui-prototype/references/optional-skills.md +4 -0
- package/skills/i3-ui-prototype/references/project-layout.md +92 -0
- package/skills/i3-ui-prototype/references/template-adapters.md +6 -0
- package/skills/i3-ui-prototype/references/workflow-contract.md +4 -0
- package/skills/i3-ui-prototype/scripts/browser-workspace.cjs +72 -0
- package/skills/i3-ui-prototype/scripts/buddy-host.cjs +40 -0
- package/skills/i3-ui-prototype/scripts/codebuddy-context.cjs +75 -0
- package/skills/i3-ui-prototype/scripts/codebuddy-hook.cjs +196 -0
- package/skills/i3-ui-prototype/scripts/codebuddy-ide-source.cjs +69 -0
- package/skills/i3-ui-prototype/scripts/codebuddy-registration.cjs +125 -0
- package/skills/i3-ui-prototype/scripts/content-template.cjs +2 -2
- package/skills/i3-ui-prototype/scripts/execution-assertions.cjs +43 -0
- package/skills/i3-ui-prototype/scripts/execution-evidence.cjs +146 -0
- package/skills/i3-ui-prototype/scripts/execution-gates.cjs +168 -0
- package/skills/i3-ui-prototype/scripts/execution-policy.cjs +62 -0
- package/skills/i3-ui-prototype/scripts/execution-recovery.cjs +52 -0
- package/skills/i3-ui-prototype/scripts/execution-runtime.cjs +60 -0
- package/skills/i3-ui-prototype/scripts/generation-assets.cjs +18 -0
- package/skills/i3-ui-prototype/scripts/generation-contract.cjs +214 -0
- package/skills/i3-ui-prototype/scripts/generation-evidence.cjs +50 -0
- package/skills/i3-ui-prototype/scripts/generation-replan.cjs +34 -0
- package/skills/i3-ui-prototype/scripts/hosts/codebuddy.cjs +20 -0
- package/skills/i3-ui-prototype/scripts/native-command.cjs +18 -0
- package/skills/i3-ui-prototype/scripts/native-delivery.cjs +17 -5
- package/skills/i3-ui-prototype/scripts/native-devtools.cjs +1 -0
- package/skills/i3-ui-prototype/scripts/native-pilot-lib.cjs +64 -18
- package/skills/i3-ui-prototype/scripts/native-pilot.cjs +23 -3
- package/skills/i3-ui-prototype/scripts/project-registry.cjs +126 -0
- package/skills/i3-ui-prototype/scripts/prototype-dependencies.cjs +28 -6
- package/skills/i3-ui-prototype/scripts/prototype-hooks.cjs +87 -0
- package/skills/i3-ui-prototype/scripts/prototype-lib.cjs +5 -7
- package/skills/i3-ui-prototype/scripts/prototype-locations.cjs +5 -1
- package/skills/i3-ui-prototype/scripts/template-contract.cjs +11 -2
- 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
|
|
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 };
|