@deneb-ui/cli 2.0.40 → 2.0.42

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.
@@ -0,0 +1,158 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Deneb ARC — Component Registry
5
+ *
6
+ * Master lookup of all known editable components in @deneb-ui/ui.
7
+ * Used by the AI agent to classify "known vs unknown" components
8
+ * during `deneb init --ai`.
9
+ */
10
+
11
+ const KNOWN_EDITABLE_COMPONENTS = new Set([
12
+ // ─── Core Text & Primitives ────────────────────────────────────
13
+ 'EditableText',
14
+ 'EditableHeading',
15
+ 'EditableParagraph',
16
+ 'EditableBadge',
17
+ 'EditableQuote',
18
+ 'EditableButton',
19
+ 'EditableImage',
20
+ 'EditableMap',
21
+ 'EditableList',
22
+ 'EditableBox',
23
+ 'EditableGrid',
24
+ 'EditableSection',
25
+ 'EditableDialog',
26
+
27
+ // ─── Product & Commerce ────────────────────────────────────────
28
+ 'EditableProductCard',
29
+ 'EditableProductGrid',
30
+ 'EditableProductDetail',
31
+ 'EditableCartDrawer',
32
+ 'EditableFilterSidebar',
33
+ 'EditablePricingCard',
34
+ 'ProductQuickView',
35
+
36
+ // ─── Content & Social Proof ────────────────────────────────────
37
+ 'EditableServiceCard',
38
+ 'EditableCard',
39
+ 'EditableTestimonialCard',
40
+ 'EditableTestimonialSection',
41
+ 'EditableCustomerReviews',
42
+ 'EditableGoogleFeedback',
43
+ 'EditableFAQAccordion',
44
+ 'EditableContactForm',
45
+
46
+ // ─── Layout & Navigation ───────────────────────────────────────
47
+ 'EditableNavbar',
48
+ 'EditableFooter',
49
+ 'EditableHero',
50
+ 'EditableHeroCentered',
51
+ 'EditableHeroSplit',
52
+ 'EditableAnnouncementBar',
53
+ 'EditableCategoryPills',
54
+ 'StickyMobileBar',
55
+ 'TrustBadges',
56
+ 'CookieConsentBanner',
57
+
58
+ // ─── Infrastructure ────────────────────────────────────────────
59
+ 'SiteDataProvider',
60
+ 'ThemeStyles',
61
+ 'ResponsiveBaseStyles',
62
+ 'DenebComponentStyles',
63
+ 'FontLoader',
64
+ 'PreviewField',
65
+ ]);
66
+
67
+ /**
68
+ * Canonical short-name aliases that Shadcn/HeroUI developers might use.
69
+ * Maps common short names to their Deneb equivalents.
70
+ */
71
+ const ALIAS_MAP = {
72
+ 'Button': 'EditableButton',
73
+ 'Card': 'EditableCard',
74
+ 'Dialog': 'EditableDialog',
75
+ 'Text': 'EditableText',
76
+ 'Heading': 'EditableHeading',
77
+ 'Paragraph': 'EditableParagraph',
78
+ 'Badge': 'EditableBadge',
79
+ 'Quote': 'EditableQuote',
80
+ 'Image': 'EditableImage',
81
+ 'Map': 'EditableMap',
82
+ 'Grid': 'EditableGrid',
83
+ 'Section': 'EditableSection',
84
+ 'Box': 'EditableBox',
85
+ 'List': 'EditableList',
86
+ 'ProductCard': 'EditableProductCard',
87
+ 'ProductGrid': 'EditableProductGrid',
88
+ 'ProductDetail': 'EditableProductDetail',
89
+ 'CustomerReviews': 'EditableCustomerReviews',
90
+ 'GoogleFeedback': 'EditableGoogleFeedback',
91
+ 'ServiceCard': 'EditableServiceCard',
92
+ 'PricingCard': 'EditablePricingCard',
93
+ 'TestimonialCard': 'EditableTestimonialCard',
94
+ 'TestimonialSection': 'EditableTestimonialSection',
95
+ 'Testimonials': 'EditableTestimonialSection',
96
+ 'FAQ': 'EditableFAQAccordion',
97
+ 'Accordion': 'EditableFAQAccordion',
98
+ 'ContactForm': 'EditableContactForm',
99
+ 'Navbar': 'EditableNavbar',
100
+ 'Header': 'EditableNavbar',
101
+ 'Footer': 'EditableFooter',
102
+ 'Hero': 'EditableHeroCentered',
103
+ 'HeroSplit': 'EditableHeroSplit',
104
+ 'AnnouncementBar': 'EditableAnnouncementBar',
105
+ 'CategoryPills': 'EditableCategoryPills',
106
+ 'CartDrawer': 'EditableCartDrawer',
107
+ 'FilterSidebar': 'EditableFilterSidebar',
108
+ };
109
+
110
+ /**
111
+ * Check if a component name is already covered by @deneb-ui/ui.
112
+ * Checks both the full Editable* name and common aliases.
113
+ */
114
+ function isKnownComponent(name) {
115
+ if (!name || typeof name !== 'string') return false;
116
+ if (KNOWN_EDITABLE_COMPONENTS.has(name)) return true;
117
+ if (ALIAS_MAP[name]) return true;
118
+ // Also check if user passed the Editable-prefixed version
119
+ if (KNOWN_EDITABLE_COMPONENTS.has(`Editable${name}`)) return true;
120
+ return false;
121
+ }
122
+
123
+ /**
124
+ * Returns the list of all known editable component names.
125
+ */
126
+ function getKnownComponentNames() {
127
+ return [...KNOWN_EDITABLE_COMPONENTS];
128
+ }
129
+
130
+ /**
131
+ * Given a list of discovered component names from a project scan,
132
+ * returns { known: string[], unknown: string[] }.
133
+ */
134
+ function classifyComponents(componentNames) {
135
+ const known = [];
136
+ const unknown = [];
137
+ const seen = new Set();
138
+
139
+ for (const name of componentNames) {
140
+ if (seen.has(name)) continue;
141
+ seen.add(name);
142
+ if (isKnownComponent(name)) {
143
+ known.push(name);
144
+ } else {
145
+ unknown.push(name);
146
+ }
147
+ }
148
+
149
+ return { known, unknown };
150
+ }
151
+
152
+ module.exports = {
153
+ KNOWN_EDITABLE_COMPONENTS,
154
+ ALIAS_MAP,
155
+ isKnownComponent,
156
+ getKnownComponentNames,
157
+ classifyComponents,
158
+ };
package/src/arc/index.cjs CHANGED
@@ -36,6 +36,9 @@ const {
36
36
  findUncoveredVisibleText,
37
37
  } = require('./fivora-contract.cjs');
38
38
  const printer = require('./printer.cjs');
39
+ const { classifyComponents } = require('./component-registry.cjs');
40
+ const { checkAiReady, adaptComponent, generateDocsPage, loadEnv } = require('./ai-agent.cjs');
41
+ const { checkGithubReady, createComponentPR } = require('./pr-agent.cjs');
39
42
 
40
43
  function parseArcOptions(raw = {}) {
41
44
  return {
@@ -46,6 +49,8 @@ function parseArcOptions(raw = {}) {
46
49
  skipInstall: Boolean(raw.skipInstall),
47
50
  detectedPages: raw.detectedPages || null,
48
51
  json: Boolean(raw.json),
52
+ aiEnabled: Boolean(raw.aiEnabled),
53
+ aiDryRun: Boolean(raw.aiDryRun),
49
54
  };
50
55
  }
51
56
 
@@ -224,7 +229,7 @@ function analyzeProjectFiles(profile, graph) {
224
229
  return analyses;
225
230
  }
226
231
 
227
- function runDenebArc(projectDir, projectName, options = {}) {
232
+ async function runDenebArcAsync(projectDir, projectName, options = {}) {
228
233
  const opts = parseArcOptions(options);
229
234
  const runId = createRunId();
230
235
  const startedAt = new Date().toISOString();
@@ -255,6 +260,133 @@ function runDenebArc(projectDir, projectName, options = {}) {
255
260
  );
256
261
  printer.printScan(profile, graph, candidateCount, actionCount);
257
262
 
263
+ // ─── AI Agent: Classify & Adapt Unknown Components ──────────
264
+ let aiResults = [];
265
+ loadEnv(projectDir);
266
+ const aiCheck = checkAiReady();
267
+ if (!aiCheck.ready) {
268
+ printer.warn(`AI Agent disabled: ${aiCheck.reason}`);
269
+ } else {
270
+ const componentNames = (profile.components || []).map((c) => c.name || c.tag).filter(Boolean);
271
+ const { unknown } = classifyComponents(componentNames);
272
+
273
+ if (unknown.length > 0) {
274
+ printer.printAiDetected(unknown.length);
275
+
276
+ for (const componentName of unknown) {
277
+ const meta = (profile.components || []).find((c) => (c.name || c.tag) === componentName);
278
+ const absFile = meta?.file ? path.join(projectDir, meta.file) : null;
279
+ let sourceCode = '';
280
+ if (absFile && fs.existsSync(absFile)) {
281
+ try { sourceCode = fs.readFileSync(absFile, 'utf8'); } catch { /* skip */ }
282
+ }
283
+ if (!sourceCode) {
284
+ printer.printAiSkipped(componentName, 'could not read source file');
285
+ continue;
286
+ }
287
+
288
+ try {
289
+ const aiResult = await adaptComponent(sourceCode, componentName, profile, {
290
+ onAttempt: (attempt, max) => printer.printAiAttempt(componentName, attempt, max),
291
+ onValidationFail: (errors, attempt) => printer.printAiValidationFail(errors, attempt),
292
+ onSuccess: (attempt) => printer.printAiSuccess(componentName, attempt, 0),
293
+ });
294
+
295
+ if (aiResult.success) {
296
+ aiResults.push({ componentName, ...aiResult });
297
+ printer.printAiSuccess(componentName, aiResult.attempts, aiResult.fields.length);
298
+ } else {
299
+ printer.printAiSkipped(componentName, aiResult.error || 'failed after max retries');
300
+ }
301
+ } catch (err) {
302
+ printer.printAiSkipped(componentName, err.message);
303
+ }
304
+ }
305
+
306
+ // Background: Open PRs for successfully adapted components
307
+ const ghCheck = checkGithubReady();
308
+ if (ghCheck.ready && aiResults.length > 0) {
309
+ for (const aiResult of aiResults) {
310
+ try {
311
+ const docsResult = await generateDocsPage(aiResult.componentName, aiResult.code, aiResult.fields);
312
+ const prResult = await createComponentPR({
313
+ componentName: aiResult.componentName,
314
+ componentCode: aiResult.code,
315
+ docsCode: docsResult.success ? docsResult.code : null,
316
+ editableFields: aiResult.fields,
317
+ attempts: aiResult.attempts,
318
+ totalInputTokens: aiResult.totalInputTokens,
319
+ totalOutputTokens: aiResult.totalOutputTokens,
320
+ dryRun: opts.aiDryRun,
321
+ });
322
+ if (prResult.corePr) {
323
+ printer.printAiPr('chamikathereal/core → deneb-ui/core', `feat/ai-editable-${aiResult.componentName.toLowerCase()}`, prResult.corePr.number);
324
+ }
325
+ if (prResult.uiPr) {
326
+ printer.printAiPr('chamikathereal/ui → deneb-ui/ui', `feat/ai-docs-${aiResult.componentName.toLowerCase()}`, prResult.uiPr.number);
327
+ }
328
+ for (const err of prResult.errors) {
329
+ printer.warn(`PR: ${err}`);
330
+ }
331
+ } catch (err) {
332
+ printer.warn(`PR creation failed for ${aiResult.componentName}: ${err.message}`);
333
+ }
334
+ }
335
+ } else if (!ghCheck.ready && aiResults.length > 0) {
336
+ printer.warn(`GitHub PRs skipped: ${ghCheck.reason}`);
337
+ }
338
+
339
+ const totalTokens = aiResults.reduce((n, r) => n + r.totalInputTokens + r.totalOutputTokens, 0);
340
+ printer.printAiSummary(aiResults.length, unknown.length - aiResults.length, totalTokens);
341
+ }
342
+ }
343
+
344
+ return runArcTransformations(projectDir, projectName, opts, profile, graph, analyses, runId, startedAt);
345
+ }
346
+
347
+ function runDenebArcSync(projectDir, projectName, options = {}) {
348
+ const opts = parseArcOptions(options);
349
+ const runId = createRunId();
350
+ const startedAt = new Date().toISOString();
351
+
352
+ printer.printBanner(opts.dryRun ? 'dry-run' : opts.explain ? 'explain' : 'run');
353
+
354
+ const profile = scanProject(projectDir);
355
+ if (opts.detectedPages && Array.isArray(opts.detectedPages) && opts.detectedPages.length) {
356
+ const scannedIds = new Set(profile.routes.map((r) => r.id));
357
+ for (const page of opts.detectedPages) {
358
+ if (page && page.id && !scannedIds.has(page.id)) {
359
+ profile.routes.push(page);
360
+ }
361
+ }
362
+ }
363
+
364
+ printer.printProfile(profile);
365
+ const graph = buildDependencyGraph(profile);
366
+ const analyses = analyzeProjectFiles(profile, graph);
367
+
368
+ const candidateCount = analyses.reduce(
369
+ (n, a) => n + (a.candidates || []).filter((c) => c.kind !== 'decoration' && c.kind !== 'already-editable' && !c.skip).length,
370
+ 0
371
+ );
372
+ const actionCount = analyses.reduce(
373
+ (n, a) => n + (a.candidates || []).filter((c) => c.operation === 'split-action-contract' || c.kind === 'url').length,
374
+ 0
375
+ );
376
+ printer.printScan(profile, graph, candidateCount, actionCount);
377
+
378
+ return runArcTransformations(projectDir, projectName, opts, profile, graph, analyses, runId, startedAt);
379
+ }
380
+
381
+ function runDenebArc(projectDir, projectName, options = {}) {
382
+ const opts = parseArcOptions(options);
383
+ if (opts.aiEnabled) {
384
+ return runDenebArcAsync(projectDir, projectName, options);
385
+ }
386
+ return runDenebArcSync(projectDir, projectName, options);
387
+ }
388
+
389
+ function runArcTransformations(projectDir, projectName, opts, profile, graph, analyses, runId, startedAt) {
258
390
  const sourceAbs = profile.jsxFiles.map((f) => path.join(projectDir, f));
259
391
  const recipeMatch = matchRecipeV2(projectDir, profile, sourceAbs, opts.recipeName);
260
392
  if (recipeMatch.recipe) {
@@ -0,0 +1,295 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Deneb ARC — PR Agent
5
+ *
6
+ * GitHub API integration for automated Pull Request creation.
7
+ * Creates branches, commits files, and opens PRs from the user's
8
+ * personal fork into the upstream deneb-ui organization repos.
9
+ *
10
+ * All commits are authored under the configured GITHUB_USERNAME
11
+ * and GITHUB_EMAIL for full contribution credit.
12
+ */
13
+
14
+ const { loadEnv } = require('./ai-agent.cjs');
15
+
16
+ /**
17
+ * Get GitHub configuration from environment variables.
18
+ */
19
+ function getGithubConfig() {
20
+ return {
21
+ pat: process.env.GITHUB_PAT || '',
22
+ username: process.env.GITHUB_USERNAME || 'chamikathereal',
23
+ email: process.env.GITHUB_EMAIL || 'dmforceeg@gmail.com',
24
+ org: process.env.GITHUB_ORG || 'deneb-ui',
25
+ coreRepo: process.env.GITHUB_CORE_REPO || 'core',
26
+ uiRepo: process.env.GITHUB_UI_REPO || 'ui',
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Check if GitHub integration is ready.
32
+ * @returns {{ ready: boolean, reason?: string }}
33
+ */
34
+ function checkGithubReady() {
35
+ loadEnv();
36
+ const config = getGithubConfig();
37
+ if (!config.pat) {
38
+ return { ready: false, reason: 'GITHUB_PAT not set. Generate a Personal Access Token at https://github.com/settings/tokens with "repo" scope.' };
39
+ }
40
+ if (!config.pat.startsWith('ghp_') && !config.pat.startsWith('github_pat_')) {
41
+ return { ready: false, reason: 'GITHUB_PAT does not look valid (should start with ghp_ or github_pat_).' };
42
+ }
43
+ return { ready: true };
44
+ }
45
+
46
+ /**
47
+ * Create a GitHub API client using @octokit/rest.
48
+ * Returns null if the PAT is not configured.
49
+ */
50
+ function createOctokitClient() {
51
+ const config = getGithubConfig();
52
+ if (!config.pat) return null;
53
+
54
+ try {
55
+ const { Octokit } = require('@octokit/rest');
56
+ return new Octokit({ auth: config.pat });
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Get the SHA of the latest commit on a branch.
64
+ */
65
+ async function getLatestCommitSha(octokit, owner, repo, branch = 'main') {
66
+ const { data } = await octokit.repos.getBranch({
67
+ owner,
68
+ repo,
69
+ branch,
70
+ });
71
+ return data.commit.sha;
72
+ }
73
+
74
+ /**
75
+ * Create a new branch from the latest main.
76
+ */
77
+ async function createBranch(octokit, owner, repo, branchName, baseSha) {
78
+ await octokit.git.createRef({
79
+ owner,
80
+ repo,
81
+ ref: `refs/heads/${branchName}`,
82
+ sha: baseSha,
83
+ });
84
+ }
85
+
86
+ /**
87
+ * Create or update a file in a repository on a specific branch.
88
+ */
89
+ async function createOrUpdateFile(octokit, owner, repo, branch, filePath, content, commitMessage) {
90
+ const config = getGithubConfig();
91
+
92
+ // Check if file already exists to get its SHA
93
+ let existingSha;
94
+ try {
95
+ const { data } = await octokit.repos.getContent({
96
+ owner,
97
+ repo,
98
+ path: filePath,
99
+ ref: branch,
100
+ });
101
+ existingSha = data.sha;
102
+ } catch {
103
+ // File does not exist — will create
104
+ }
105
+
106
+ const params = {
107
+ owner,
108
+ repo,
109
+ path: filePath,
110
+ message: commitMessage,
111
+ content: Buffer.from(content, 'utf8').toString('base64'),
112
+ branch,
113
+ committer: {
114
+ name: config.username,
115
+ email: config.email,
116
+ },
117
+ author: {
118
+ name: config.username,
119
+ email: config.email,
120
+ },
121
+ };
122
+ if (existingSha) params.sha = existingSha;
123
+
124
+ await octokit.repos.createOrUpdateFileContents(params);
125
+ }
126
+
127
+ /**
128
+ * Open a Pull Request from the user's fork to the upstream repo.
129
+ */
130
+ async function openPullRequest(octokit, { upstreamOwner, upstreamRepo, forkOwner, branch, title, body }) {
131
+ const { data } = await octokit.pulls.create({
132
+ owner: upstreamOwner,
133
+ repo: upstreamRepo,
134
+ title,
135
+ body,
136
+ head: `${forkOwner}:${branch}`,
137
+ base: 'main',
138
+ });
139
+ return { number: data.number, url: data.html_url };
140
+ }
141
+
142
+ /**
143
+ * Build a PR body with validation results and component details.
144
+ */
145
+ function buildPrBody({ componentName, editableFields, attempts, totalInputTokens, totalOutputTokens, fingerprint }) {
146
+ const fieldsTable = editableFields.map((f) => `| \`${f}\` |`).join('\n');
147
+
148
+ return `## 🤖 AI-Generated Editable Component: \`Editable${componentName}\`
149
+
150
+ This component was automatically generated by **Deneb ARC AI Agent** and passed all validation checks.
151
+
152
+ ### Editable Fields Discovered
153
+ | Field Path |
154
+ |---|
155
+ ${fieldsTable}
156
+
157
+ ### Validation Results
158
+ - ✅ AST Syntax: Passed
159
+ - ✅ Fivora Contract Markers: Passed
160
+ - ✅ TypeScript Interface: Passed
161
+ - ✅ Design Preservation: Intact
162
+
163
+ ### Generation Stats
164
+ - **Model:** ${process.env.OPENAI_CONTENT_MODEL || 'gpt-4o-mini'}
165
+ - **Attempts:** ${attempts}/3
166
+ - **Tokens:** ${totalInputTokens.toLocaleString()} input / ${totalOutputTokens.toLocaleString()} output
167
+ - **Fingerprint:** \`${fingerprint || 'n/a'}\`
168
+
169
+ ---
170
+ *Generated by Deneb ARC AI Agent — [deneb.fivora.site](https://deneb.fivora.site)*`;
171
+ }
172
+
173
+ /**
174
+ * Full PR workflow: create branch → commit files → open PR.
175
+ *
176
+ * Opens PRs for both the core repo (component) and the ui repo (docs).
177
+ *
178
+ * @param {object} params
179
+ * @param {string} params.componentName - e.g. "Carousel"
180
+ * @param {string} params.componentCode - The validated EditableCarousel.tsx code
181
+ * @param {string} params.docsCode - The generated docs page code (optional)
182
+ * @param {string[]} params.editableFields - List of editable field paths
183
+ * @param {number} params.attempts - Number of AI attempts taken
184
+ * @param {number} params.totalInputTokens - Total tokens used
185
+ * @param {number} params.totalOutputTokens - Total tokens used
186
+ * @param {string} [params.fingerprint] - Structural fingerprint
187
+ * @param {boolean} [params.dryRun=false] - If true, skip actual API calls
188
+ * @returns {Promise<{ corePr?: { number: number, url: string }, uiPr?: { number: number, url: string }, errors: string[] }>}
189
+ */
190
+ async function createComponentPR(params) {
191
+ const {
192
+ componentName,
193
+ componentCode,
194
+ docsCode,
195
+ editableFields,
196
+ attempts,
197
+ totalInputTokens,
198
+ totalOutputTokens,
199
+ fingerprint,
200
+ dryRun = false,
201
+ } = params;
202
+
203
+ const errors = [];
204
+ let corePr = null;
205
+ let uiPr = null;
206
+
207
+ if (dryRun) {
208
+ return {
209
+ corePr: { number: 0, url: '(dry-run — PR not created)' },
210
+ uiPr: docsCode ? { number: 0, url: '(dry-run — PR not created)' } : null,
211
+ errors: [],
212
+ };
213
+ }
214
+
215
+ const octokit = createOctokitClient();
216
+ if (!octokit) {
217
+ return { errors: ['GitHub client not available — check GITHUB_PAT and @octokit/rest installation.'] };
218
+ }
219
+
220
+ const config = getGithubConfig();
221
+ const slugName = componentName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
222
+ const timestamp = Date.now().toString(36);
223
+ const prBody = buildPrBody({ componentName, editableFields, attempts, totalInputTokens, totalOutputTokens, fingerprint });
224
+
225
+ // ── Core Repo PR ───────────────────────────────────────────────
226
+ try {
227
+ const coreBranch = `feat/ai-editable-${slugName}-${timestamp}`;
228
+ const baseSha = await getLatestCommitSha(octokit, config.username, config.coreRepo);
229
+ await createBranch(octokit, config.username, config.coreRepo, coreBranch, baseSha);
230
+
231
+ // Commit the component file
232
+ await createOrUpdateFile(
233
+ octokit,
234
+ config.username,
235
+ config.coreRepo,
236
+ coreBranch,
237
+ `packages/deneb-ui/src/Editable${componentName}.tsx`,
238
+ componentCode,
239
+ `feat(ui): add Editable${componentName} component [AI-generated]`
240
+ );
241
+
242
+ // Open PR to upstream
243
+ corePr = await openPullRequest(octokit, {
244
+ upstreamOwner: config.org,
245
+ upstreamRepo: config.coreRepo,
246
+ forkOwner: config.username,
247
+ branch: coreBranch,
248
+ title: `feat(ui): add Editable${componentName} component [AI-generated]`,
249
+ body: prBody,
250
+ });
251
+ } catch (err) {
252
+ errors.push(`Core PR failed: ${err.message}`);
253
+ }
254
+
255
+ // ── UI Repo PR (docs page) ─────────────────────────────────────
256
+ if (docsCode) {
257
+ try {
258
+ const uiBranch = `feat/ai-docs-${slugName}-${timestamp}`;
259
+ const uiBaseSha = await getLatestCommitSha(octokit, config.username, config.uiRepo);
260
+ await createBranch(octokit, config.username, config.uiRepo, uiBranch, uiBaseSha);
261
+
262
+ // Commit the docs page
263
+ await createOrUpdateFile(
264
+ octokit,
265
+ config.username,
266
+ config.uiRepo,
267
+ uiBranch,
268
+ `src/app/docs/components/editable-${slugName}/page.tsx`,
269
+ docsCode,
270
+ `docs: add Editable${componentName} documentation [AI-generated]`
271
+ );
272
+
273
+ // Open PR to upstream
274
+ uiPr = await openPullRequest(octokit, {
275
+ upstreamOwner: config.org,
276
+ upstreamRepo: config.uiRepo,
277
+ forkOwner: config.username,
278
+ branch: uiBranch,
279
+ title: `docs: add Editable${componentName} documentation [AI-generated]`,
280
+ body: prBody,
281
+ });
282
+ } catch (err) {
283
+ errors.push(`UI docs PR failed: ${err.message}`);
284
+ }
285
+ }
286
+
287
+ return { corePr, uiPr, errors };
288
+ }
289
+
290
+ module.exports = {
291
+ getGithubConfig,
292
+ checkGithubReady,
293
+ createComponentPR,
294
+ buildPrBody,
295
+ };
@@ -163,6 +163,45 @@ function printRollback(reason) {
163
163
  console.log(`${C.dim}${reason}${C.reset}\n`);
164
164
  }
165
165
 
166
+ // ─── AI Agent Progress Messages ──────────────────────────────────
167
+
168
+ function printAiDetected(unknownCount) {
169
+ console.log(`\n ${C.cyan}⚡${C.reset} ${C.bold}AI Agent:${C.reset} Detected ${unknownCount} unknown component${unknownCount > 1 ? 's' : ''}`);
170
+ }
171
+
172
+ function printAiAttempt(componentName, attempt, maxRetries) {
173
+ console.log(` ${C.cyan}⚡${C.reset} Adapting ${C.bold}<${componentName} />${C.reset}... attempt ${attempt}/${maxRetries}`);
174
+ }
175
+
176
+ function printAiValidationFail(errors, attempt) {
177
+ for (const err of errors.slice(0, 3)) {
178
+ console.log(` ${C.red}❌${C.reset} ${err}`);
179
+ }
180
+ if (errors.length > 3) {
181
+ console.log(` ${C.dim}... ${errors.length - 3} more errors${C.reset}`);
182
+ }
183
+ }
184
+
185
+ function printAiSuccess(componentName, attempt, fieldsCount) {
186
+ console.log(` ${C.green}✅${C.reset} All checks passed! (${fieldsCount} editable field${fieldsCount !== 1 ? 's' : ''})`);
187
+ }
188
+
189
+ function printAiSkipped(componentName, reason) {
190
+ console.log(` ${C.yellow}⚠${C.reset} Skipped ${C.bold}<${componentName} />${C.reset}: ${reason}`);
191
+ }
192
+
193
+ function printAiPr(repoName, branchName, prNumber) {
194
+ if (prNumber === 0) {
195
+ console.log(` ${C.cyan}📦${C.reset} ${C.dim}(dry-run)${C.reset} ${repoName}: ${branchName}`);
196
+ } else {
197
+ console.log(` ${C.cyan}📦${C.reset} PR opened: ${C.bold}${repoName}${C.reset} (#${prNumber})`);
198
+ }
199
+ }
200
+
201
+ function printAiSummary(adapted, skipped, totalTokens) {
202
+ console.log(`\n ${C.cyan}⚡${C.reset} AI Agent Summary: ${C.green}${adapted} adapted${C.reset}, ${C.yellow}${skipped} skipped${C.reset}, ~${totalTokens.toLocaleString()} tokens used`);
203
+ }
204
+
166
205
  module.exports = {
167
206
  printBanner,
168
207
  printProfile,
@@ -177,6 +216,13 @@ module.exports = {
177
216
  printUncoveredText,
178
217
  printDeveloperNextSteps,
179
218
  printRollback,
219
+ printAiDetected,
220
+ printAiAttempt,
221
+ printAiValidationFail,
222
+ printAiSuccess,
223
+ printAiSkipped,
224
+ printAiPr,
225
+ printAiSummary,
180
226
  ok,
181
227
  warn,
182
228
  info,
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const ARC_NAME = 'Deneb ARC';
4
- const ARC_FULL_NAME = 'Deneb Adaptive Refactoring Compiler';
5
- const ARC_VERSION = '1.1.0';
4
+ const ARC_FULL_NAME = 'Deneb Adaptive Refactoring Compiler — AI-Augmented';
5
+ const ARC_VERSION = '2.0.0';
6
6
  const SCHEMA_VERSION = 2;
7
7
  const ENGINE_ID = 'deneb-arc';
8
8