humanbehavior-js 0.7.0 → 0.8.0-beta.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.
- package/package.json +1 -1
- package/packages/browser/dist/cjs/index.js +6 -6
- package/packages/browser/dist/cjs/index.js.map +1 -1
- package/packages/browser/dist/esm/index.js +4 -4
- package/packages/browser/dist/esm/index.js.map +1 -1
- package/packages/browser/dist/index.min.js +1 -1
- package/packages/browser/dist/index.min.js.map +1 -1
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/index.js.map +1 -1
- package/packages/core/dist/index.mjs +1 -1
- package/packages/core/dist/index.mjs.map +1 -1
- package/packages/core/dist/tracker.d.ts +12 -0
- package/packages/core/dist/tracker.d.ts.map +1 -1
- package/packages/react/dist/index.js +1 -1
- package/packages/react/dist/index.js.map +1 -1
- package/packages/react/dist/index.mjs +1 -1
- package/packages/react/dist/index.mjs.map +1 -1
- package/packages/wizard/dist/agent/claude-agent-installer.d.ts +17 -0
- package/packages/wizard/dist/agent/claude-agent-installer.d.ts.map +1 -0
- package/packages/wizard/dist/ai/ai-install-wizard.d.ts +2 -2
- package/packages/wizard/dist/ai/ai-install-wizard.d.ts.map +1 -1
- package/packages/wizard/dist/ai/manual-framework-wizard.d.ts +2 -2
- package/packages/wizard/dist/ai/manual-framework-wizard.d.ts.map +1 -1
- package/packages/wizard/dist/cli/ai-auto-install.d.ts +6 -0
- package/packages/wizard/dist/cli/ai-auto-install.d.ts.map +1 -1
- package/packages/wizard/dist/cli/ai-auto-install.js +641 -81
- package/packages/wizard/dist/cli/ai-auto-install.js.map +1 -1
- package/packages/wizard/dist/cli/auto-install.d.ts +4 -0
- package/packages/wizard/dist/cli/auto-install.d.ts.map +1 -1
- package/packages/wizard/dist/cli/auto-install.js +314 -56
- package/packages/wizard/dist/cli/auto-install.js.map +1 -1
- package/packages/wizard/dist/core/install-wizard.d.ts +51 -3
- package/packages/wizard/dist/core/install-wizard.d.ts.map +1 -1
- package/packages/wizard/dist/index.d.ts +2 -0
- package/packages/wizard/dist/index.d.ts.map +1 -1
- package/packages/wizard/dist/index.js +1 -1
- package/packages/wizard/dist/index.js.map +1 -1
- package/packages/wizard/dist/index.mjs +1 -1
- package/packages/wizard/dist/index.mjs.map +1 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { execSync } from 'child_process';
|
|
5
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
5
6
|
import * as clack from '@clack/prompts';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -10,12 +11,18 @@ import * as clack from '@clack/prompts';
|
|
|
10
11
|
* This wizard automatically detects the user's framework and modifies their codebase
|
|
11
12
|
* to integrate the SDK with minimal user intervention.
|
|
12
13
|
*/
|
|
14
|
+
const MIN_SUPPORTED_SDK_VERSION = '0.7.0';
|
|
15
|
+
const TESTED_SDK_VERSION = '0.7.0';
|
|
13
16
|
class AutoInstallationWizard {
|
|
14
|
-
constructor(apiKey, projectRoot = process.cwd()) {
|
|
17
|
+
constructor(apiKey, projectRoot = process.cwd(), options = {}) {
|
|
15
18
|
this.framework = null;
|
|
16
19
|
this.manualNotes = [];
|
|
17
20
|
this.apiKey = apiKey;
|
|
18
|
-
this.projectRoot = projectRoot;
|
|
21
|
+
this.projectRoot = path.resolve(projectRoot);
|
|
22
|
+
this.options = options;
|
|
23
|
+
}
|
|
24
|
+
setAgentPlan(agentPlan) {
|
|
25
|
+
this.options.agentPlan = agentPlan;
|
|
19
26
|
}
|
|
20
27
|
/**
|
|
21
28
|
* Simple version comparison utility
|
|
@@ -46,11 +53,13 @@ class AutoInstallationWizard {
|
|
|
46
53
|
try {
|
|
47
54
|
// Step 1: Detect framework
|
|
48
55
|
this.framework = await this.detectFramework();
|
|
49
|
-
// Step 2:
|
|
50
|
-
await this.installPackage();
|
|
56
|
+
// Step 2: Plan and optionally install package
|
|
57
|
+
const installPlan = await this.installPackage();
|
|
51
58
|
// Step 3: Generate and apply code modifications
|
|
52
59
|
const modifications = await this.generateModifications();
|
|
53
|
-
|
|
60
|
+
if (!this.options.dryRun) {
|
|
61
|
+
await this.applyModifications(modifications);
|
|
62
|
+
}
|
|
54
63
|
// Step 4: Generate next steps
|
|
55
64
|
const nextSteps = this.generateNextSteps();
|
|
56
65
|
return {
|
|
@@ -58,7 +67,10 @@ class AutoInstallationWizard {
|
|
|
58
67
|
framework: this.framework,
|
|
59
68
|
modifications,
|
|
60
69
|
errors: [],
|
|
61
|
-
nextSteps
|
|
70
|
+
nextSteps,
|
|
71
|
+
dryRun: this.options.dryRun,
|
|
72
|
+
installPlan,
|
|
73
|
+
agentPlan: this.options.agentPlan
|
|
62
74
|
};
|
|
63
75
|
}
|
|
64
76
|
catch (error) {
|
|
@@ -67,7 +79,9 @@ class AutoInstallationWizard {
|
|
|
67
79
|
framework: this.framework || { name: 'unknown', type: 'vanilla' },
|
|
68
80
|
modifications: [],
|
|
69
81
|
errors: [error instanceof Error ? error.message : 'Unknown error'],
|
|
70
|
-
nextSteps: []
|
|
82
|
+
nextSteps: [],
|
|
83
|
+
dryRun: this.options.dryRun,
|
|
84
|
+
agentPlan: this.options.agentPlan
|
|
71
85
|
};
|
|
72
86
|
}
|
|
73
87
|
}
|
|
@@ -243,38 +257,212 @@ class AutoInstallationWizard {
|
|
|
243
257
|
else if (dependencies.rollup) {
|
|
244
258
|
framework.bundler = 'rollup';
|
|
245
259
|
}
|
|
246
|
-
|
|
247
|
-
if (fs.existsSync(path.join(this.projectRoot, 'yarn.lock'))) {
|
|
248
|
-
framework.packageManager = 'yarn';
|
|
249
|
-
}
|
|
250
|
-
else if (fs.existsSync(path.join(this.projectRoot, 'pnpm-lock.yaml'))) {
|
|
251
|
-
framework.packageManager = 'pnpm';
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
framework.packageManager = 'npm';
|
|
255
|
-
}
|
|
260
|
+
framework.packageManager = this.detectPackageManager(this.projectRoot);
|
|
256
261
|
return framework;
|
|
257
262
|
}
|
|
258
263
|
/**
|
|
259
264
|
* Install the SDK package with latest version range
|
|
260
265
|
*/
|
|
261
266
|
async installPackage() {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
: this.framework?.packageManager === 'pnpm'
|
|
266
|
-
? 'pnpm add humanbehavior-js@latest'
|
|
267
|
-
: 'npm install humanbehavior-js@latest';
|
|
268
|
-
// Add legacy peer deps flag for npm to handle dependency conflicts
|
|
269
|
-
if (this.framework?.packageManager !== 'yarn' && this.framework?.packageManager !== 'pnpm') {
|
|
270
|
-
command += ' --legacy-peer-deps';
|
|
267
|
+
const plan = this.planPackageInstall();
|
|
268
|
+
if (!plan.shouldInstall || this.options.skipInstall || this.options.dryRun) {
|
|
269
|
+
return plan;
|
|
271
270
|
}
|
|
272
271
|
try {
|
|
273
|
-
execSync(command, { cwd:
|
|
272
|
+
execSync(plan.command, { cwd: plan.cwd, stdio: 'inherit' });
|
|
274
273
|
}
|
|
275
274
|
catch (error) {
|
|
276
275
|
throw new Error(`Failed to install humanbehavior-js: ${error}`);
|
|
277
276
|
}
|
|
277
|
+
return plan;
|
|
278
|
+
}
|
|
279
|
+
planPackageInstall() {
|
|
280
|
+
const packageManager = this.framework?.packageManager || this.detectPackageManager(this.projectRoot);
|
|
281
|
+
const workspaceRoot = this.findWorkspaceRoot(this.projectRoot);
|
|
282
|
+
const packageJson = this.readPackageJson(this.projectRoot);
|
|
283
|
+
const packageName = packageJson?.name;
|
|
284
|
+
const installedVersion = this.getInstalledSDKVersion(packageJson);
|
|
285
|
+
const alreadyInstalled = installedVersion !== undefined;
|
|
286
|
+
const versionStatus = this.getSDKVersionStatus(installedVersion);
|
|
287
|
+
const isWorkspace = workspaceRoot !== this.projectRoot;
|
|
288
|
+
const targetRef = packageName || path.relative(workspaceRoot, this.projectRoot);
|
|
289
|
+
const sdkPackage = 'humanbehavior-js@latest';
|
|
290
|
+
const shouldInstall = !alreadyInstalled ||
|
|
291
|
+
(versionStatus === 'stale' && this.options.upgrade === true);
|
|
292
|
+
const command = this.buildInstallCommand(packageManager, sdkPackage, {
|
|
293
|
+
isWorkspace,
|
|
294
|
+
targetRef
|
|
295
|
+
});
|
|
296
|
+
return {
|
|
297
|
+
packageName: 'humanbehavior-js',
|
|
298
|
+
packageManager,
|
|
299
|
+
command,
|
|
300
|
+
cwd: isWorkspace ? workspaceRoot : this.projectRoot,
|
|
301
|
+
targetPackagePath: this.projectRoot,
|
|
302
|
+
workspaceRoot,
|
|
303
|
+
isWorkspace,
|
|
304
|
+
alreadyInstalled,
|
|
305
|
+
installedVersion,
|
|
306
|
+
minimumSupportedVersion: MIN_SUPPORTED_SDK_VERSION,
|
|
307
|
+
testedVersion: TESTED_SDK_VERSION,
|
|
308
|
+
versionStatus,
|
|
309
|
+
shouldInstall,
|
|
310
|
+
reason: this.getInstallPlanReason({
|
|
311
|
+
alreadyInstalled,
|
|
312
|
+
installedVersion,
|
|
313
|
+
versionStatus,
|
|
314
|
+
shouldInstall,
|
|
315
|
+
isWorkspace,
|
|
316
|
+
targetRef
|
|
317
|
+
})
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
getInstallPlanReason(options) {
|
|
321
|
+
if (options.alreadyInstalled) {
|
|
322
|
+
switch (options.versionStatus) {
|
|
323
|
+
case 'compatible':
|
|
324
|
+
return `humanbehavior-js is already compatible (${options.installedVersion})`;
|
|
325
|
+
case 'stale':
|
|
326
|
+
return options.shouldInstall
|
|
327
|
+
? `humanbehavior-js ${options.installedVersion} is below ${MIN_SUPPORTED_SDK_VERSION}; upgrade requested`
|
|
328
|
+
: `humanbehavior-js ${options.installedVersion} is below ${MIN_SUPPORTED_SDK_VERSION}; rerun with --upgrade to update`;
|
|
329
|
+
case 'newer':
|
|
330
|
+
return `humanbehavior-js ${options.installedVersion} is newer than tested ${TESTED_SDK_VERSION}; keeping existing version`;
|
|
331
|
+
case 'unknown':
|
|
332
|
+
return `humanbehavior-js is already declared with a non-semver range (${options.installedVersion}); keeping existing version`;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return options.isWorkspace
|
|
336
|
+
? `Install scoped to workspace package ${options.targetRef}`
|
|
337
|
+
: 'Install in selected project package';
|
|
338
|
+
}
|
|
339
|
+
buildInstallCommand(packageManager, sdkPackage, options) {
|
|
340
|
+
if (options.isWorkspace) {
|
|
341
|
+
switch (packageManager) {
|
|
342
|
+
case 'pnpm':
|
|
343
|
+
return `pnpm --filter ${this.shellQuote(options.targetRef)} add ${sdkPackage}`;
|
|
344
|
+
case 'yarn':
|
|
345
|
+
return `yarn workspace ${this.shellQuote(options.targetRef)} add ${sdkPackage}`;
|
|
346
|
+
case 'bun':
|
|
347
|
+
return `bun add ${sdkPackage} --cwd ${this.shellQuote(options.targetRef)}`;
|
|
348
|
+
case 'npm':
|
|
349
|
+
default:
|
|
350
|
+
return `npm install ${sdkPackage} --workspace ${this.shellQuote(options.targetRef)} --legacy-peer-deps`;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
switch (packageManager) {
|
|
354
|
+
case 'pnpm':
|
|
355
|
+
return `pnpm add ${sdkPackage}`;
|
|
356
|
+
case 'yarn':
|
|
357
|
+
return `yarn add ${sdkPackage}`;
|
|
358
|
+
case 'bun':
|
|
359
|
+
return `bun add ${sdkPackage}`;
|
|
360
|
+
case 'npm':
|
|
361
|
+
default:
|
|
362
|
+
return `npm install ${sdkPackage} --legacy-peer-deps`;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
detectPackageManager(startDir) {
|
|
366
|
+
const root = this.findWorkspaceRoot(startDir);
|
|
367
|
+
const lockfileChecks = [
|
|
368
|
+
['pnpm-lock.yaml', 'pnpm'],
|
|
369
|
+
['yarn.lock', 'yarn'],
|
|
370
|
+
['bun.lockb', 'bun'],
|
|
371
|
+
['bun.lock', 'bun'],
|
|
372
|
+
['package-lock.json', 'npm'],
|
|
373
|
+
['npm-shrinkwrap.json', 'npm']
|
|
374
|
+
];
|
|
375
|
+
for (const dir of [startDir, root]) {
|
|
376
|
+
for (const [lockfile, packageManager] of lockfileChecks) {
|
|
377
|
+
if (fs.existsSync(path.join(dir, lockfile))) {
|
|
378
|
+
return packageManager;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
const packageJson = this.readPackageJson(root) || this.readPackageJson(startDir);
|
|
383
|
+
const packageManager = packageJson?.packageManager;
|
|
384
|
+
if (typeof packageManager === 'string') {
|
|
385
|
+
if (packageManager.startsWith('pnpm@'))
|
|
386
|
+
return 'pnpm';
|
|
387
|
+
if (packageManager.startsWith('yarn@'))
|
|
388
|
+
return 'yarn';
|
|
389
|
+
if (packageManager.startsWith('bun@'))
|
|
390
|
+
return 'bun';
|
|
391
|
+
if (packageManager.startsWith('npm@'))
|
|
392
|
+
return 'npm';
|
|
393
|
+
}
|
|
394
|
+
return 'npm';
|
|
395
|
+
}
|
|
396
|
+
findWorkspaceRoot(startDir) {
|
|
397
|
+
let current = path.resolve(startDir);
|
|
398
|
+
let bestRoot = current;
|
|
399
|
+
while (true) {
|
|
400
|
+
const packageJson = this.readPackageJson(current);
|
|
401
|
+
const hasWorkspacePackageJson = !!packageJson &&
|
|
402
|
+
(Array.isArray(packageJson.workspaces) ||
|
|
403
|
+
(packageJson.workspaces && Array.isArray(packageJson.workspaces.packages)));
|
|
404
|
+
const hasPnpmWorkspace = fs.existsSync(path.join(current, 'pnpm-workspace.yaml'));
|
|
405
|
+
if (hasWorkspacePackageJson || hasPnpmWorkspace) {
|
|
406
|
+
bestRoot = current;
|
|
407
|
+
}
|
|
408
|
+
const parent = path.dirname(current);
|
|
409
|
+
if (parent === current)
|
|
410
|
+
break;
|
|
411
|
+
current = parent;
|
|
412
|
+
}
|
|
413
|
+
return bestRoot;
|
|
414
|
+
}
|
|
415
|
+
readPackageJson(dir) {
|
|
416
|
+
const packageJsonPath = path.join(dir, 'package.json');
|
|
417
|
+
if (!fs.existsSync(packageJsonPath))
|
|
418
|
+
return null;
|
|
419
|
+
try {
|
|
420
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
getInstalledSDKVersion(packageJson) {
|
|
427
|
+
if (!packageJson)
|
|
428
|
+
return undefined;
|
|
429
|
+
const dependencyFields = [
|
|
430
|
+
'dependencies',
|
|
431
|
+
'devDependencies',
|
|
432
|
+
'peerDependencies',
|
|
433
|
+
'optionalDependencies'
|
|
434
|
+
];
|
|
435
|
+
for (const field of dependencyFields) {
|
|
436
|
+
const version = packageJson[field]?.['humanbehavior-js'];
|
|
437
|
+
if (typeof version === 'string') {
|
|
438
|
+
return version;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
getSDKVersionStatus(installedVersion) {
|
|
444
|
+
if (!installedVersion)
|
|
445
|
+
return 'not-installed';
|
|
446
|
+
const normalized = this.extractSemver(installedVersion);
|
|
447
|
+
if (!normalized)
|
|
448
|
+
return 'unknown';
|
|
449
|
+
if (this.compareVersions(normalized, MIN_SUPPORTED_SDK_VERSION) < 0) {
|
|
450
|
+
return 'stale';
|
|
451
|
+
}
|
|
452
|
+
if (this.compareVersions(normalized, TESTED_SDK_VERSION) > 0) {
|
|
453
|
+
return 'newer';
|
|
454
|
+
}
|
|
455
|
+
return 'compatible';
|
|
456
|
+
}
|
|
457
|
+
extractSemver(versionRange) {
|
|
458
|
+
const match = versionRange.match(/\d+\.\d+\.\d+/);
|
|
459
|
+
return match ? match[0] : null;
|
|
460
|
+
}
|
|
461
|
+
shellQuote(value) {
|
|
462
|
+
if (/^[A-Za-z0-9_@./:-]+$/.test(value)) {
|
|
463
|
+
return value;
|
|
464
|
+
}
|
|
465
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
278
466
|
}
|
|
279
467
|
/**
|
|
280
468
|
* Generate code modifications based on framework
|
|
@@ -601,20 +789,14 @@ export function useHumanBehavior() {
|
|
|
601
789
|
return { tracker: null }
|
|
602
790
|
}
|
|
603
791
|
`;
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
action: 'create',
|
|
612
|
-
content: composableContent,
|
|
613
|
-
description: 'Created Vue composable useHumanBehavior'
|
|
614
|
-
});
|
|
615
|
-
}
|
|
792
|
+
if (!fs.existsSync(composablePath)) {
|
|
793
|
+
modifications.push({
|
|
794
|
+
filePath: composablePath,
|
|
795
|
+
action: 'create',
|
|
796
|
+
content: composableContent,
|
|
797
|
+
description: 'Created Vue composable useHumanBehavior'
|
|
798
|
+
});
|
|
616
799
|
}
|
|
617
|
-
catch { }
|
|
618
800
|
if (mainFile) {
|
|
619
801
|
const content = fs.readFileSync(mainFile, 'utf8');
|
|
620
802
|
const modifiedContent = this.injectVuePlugin(content);
|
|
@@ -667,9 +849,6 @@ export class HumanBehavior {
|
|
|
667
849
|
}
|
|
668
850
|
}
|
|
669
851
|
`;
|
|
670
|
-
if (!fs.existsSync(serviceDir)) {
|
|
671
|
-
fs.mkdirSync(serviceDir, { recursive: true });
|
|
672
|
-
}
|
|
673
852
|
if (!fs.existsSync(servicePath)) {
|
|
674
853
|
modifications.push({
|
|
675
854
|
filePath: servicePath,
|
|
@@ -681,11 +860,6 @@ export class HumanBehavior {
|
|
|
681
860
|
// Handle Angular environment files (proper Angular way)
|
|
682
861
|
const envFile = path.join(this.projectRoot, 'src', 'environments', 'environment.ts');
|
|
683
862
|
const envProdFile = path.join(this.projectRoot, 'src', 'environments', 'environment.prod.ts');
|
|
684
|
-
// Create environments directory if it doesn't exist
|
|
685
|
-
const envDir = path.dirname(envFile);
|
|
686
|
-
if (!fs.existsSync(envDir)) {
|
|
687
|
-
fs.mkdirSync(envDir, { recursive: true });
|
|
688
|
-
}
|
|
689
863
|
// Create or update development environment
|
|
690
864
|
if (fs.existsSync(envFile)) {
|
|
691
865
|
const content = fs.readFileSync(envFile, 'utf8');
|
|
@@ -868,8 +1042,15 @@ export const onClientEntry = () => {
|
|
|
868
1042
|
* Apply modifications to the codebase
|
|
869
1043
|
*/
|
|
870
1044
|
async applyModifications(modifications) {
|
|
871
|
-
|
|
872
|
-
|
|
1045
|
+
const snapshots = modifications.map((modification) => ({
|
|
1046
|
+
filePath: modification.filePath,
|
|
1047
|
+
existed: fs.existsSync(modification.filePath),
|
|
1048
|
+
content: fs.existsSync(modification.filePath)
|
|
1049
|
+
? fs.readFileSync(modification.filePath, 'utf8')
|
|
1050
|
+
: null
|
|
1051
|
+
}));
|
|
1052
|
+
try {
|
|
1053
|
+
for (const modification of modifications) {
|
|
873
1054
|
const dir = path.dirname(modification.filePath);
|
|
874
1055
|
if (!fs.existsSync(dir)) {
|
|
875
1056
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -886,8 +1067,24 @@ export const onClientEntry = () => {
|
|
|
886
1067
|
break;
|
|
887
1068
|
}
|
|
888
1069
|
}
|
|
889
|
-
|
|
890
|
-
|
|
1070
|
+
}
|
|
1071
|
+
catch (error) {
|
|
1072
|
+
this.rollbackModifications(snapshots);
|
|
1073
|
+
throw new Error(`Failed to apply modifications; rolled back file changes: ${error}`);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
rollbackModifications(snapshots) {
|
|
1077
|
+
for (const snapshot of snapshots.reverse()) {
|
|
1078
|
+
try {
|
|
1079
|
+
if (snapshot.existed && snapshot.content !== null) {
|
|
1080
|
+
fs.writeFileSync(snapshot.filePath, snapshot.content);
|
|
1081
|
+
}
|
|
1082
|
+
else if (!snapshot.existed && fs.existsSync(snapshot.filePath)) {
|
|
1083
|
+
fs.rmSync(snapshot.filePath, { force: true });
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
catch {
|
|
1087
|
+
// Best-effort rollback. The original apply error is more actionable.
|
|
891
1088
|
}
|
|
892
1089
|
}
|
|
893
1090
|
}
|
|
@@ -895,6 +1092,13 @@ export const onClientEntry = () => {
|
|
|
895
1092
|
* Generate next steps for the user
|
|
896
1093
|
*/
|
|
897
1094
|
generateNextSteps() {
|
|
1095
|
+
if (this.options.dryRun) {
|
|
1096
|
+
return [
|
|
1097
|
+
'Dry run complete. No packages were installed and no files were changed.',
|
|
1098
|
+
'Review the planned install command and file modifications.',
|
|
1099
|
+
'Run again without --dry-run to apply these changes.'
|
|
1100
|
+
];
|
|
1101
|
+
}
|
|
898
1102
|
const steps = [
|
|
899
1103
|
'✅ SDK installed and configured automatically!',
|
|
900
1104
|
'🚀 Your app is now tracking user behavior',
|
|
@@ -1638,8 +1842,8 @@ class RemoteAIService {
|
|
|
1638
1842
|
* Useful when auto-detection fails or users want more control.
|
|
1639
1843
|
*/
|
|
1640
1844
|
class ManualFrameworkInstallationWizard extends AutoInstallationWizard {
|
|
1641
|
-
constructor(apiKey, projectRoot = process.cwd(), framework) {
|
|
1642
|
-
super(apiKey, projectRoot);
|
|
1845
|
+
constructor(apiKey, projectRoot = process.cwd(), framework, options = {}) {
|
|
1846
|
+
super(apiKey, projectRoot, options);
|
|
1643
1847
|
this.selectedFramework = framework.toLowerCase();
|
|
1644
1848
|
this.framework = this.createFrameworkInfo(this.selectedFramework);
|
|
1645
1849
|
}
|
|
@@ -1669,10 +1873,12 @@ class ManualFrameworkInstallationWizard extends AutoInstallationWizard {
|
|
|
1669
1873
|
};
|
|
1670
1874
|
}
|
|
1671
1875
|
// Step 4: Install package
|
|
1672
|
-
await this.installPackage();
|
|
1876
|
+
const installPlan = await this.installPackage();
|
|
1673
1877
|
// Step 5: Generate and apply code modifications
|
|
1674
1878
|
const modifications = await this.generateModifications();
|
|
1675
|
-
|
|
1879
|
+
if (!this.options.dryRun) {
|
|
1880
|
+
await this.applyModifications(modifications);
|
|
1881
|
+
}
|
|
1676
1882
|
// Step 6: Generate next steps
|
|
1677
1883
|
const nextSteps = this.generateManualNextSteps();
|
|
1678
1884
|
return {
|
|
@@ -1681,6 +1887,8 @@ class ManualFrameworkInstallationWizard extends AutoInstallationWizard {
|
|
|
1681
1887
|
modifications,
|
|
1682
1888
|
errors: [],
|
|
1683
1889
|
nextSteps,
|
|
1890
|
+
dryRun: this.options.dryRun,
|
|
1891
|
+
installPlan,
|
|
1684
1892
|
selectedFramework: this.selectedFramework,
|
|
1685
1893
|
manualMode: true
|
|
1686
1894
|
};
|
|
@@ -1692,6 +1900,7 @@ class ManualFrameworkInstallationWizard extends AutoInstallationWizard {
|
|
|
1692
1900
|
modifications: [],
|
|
1693
1901
|
errors: [error instanceof Error ? error.message : 'Unknown error'],
|
|
1694
1902
|
nextSteps: [],
|
|
1903
|
+
dryRun: this.options.dryRun,
|
|
1695
1904
|
selectedFramework: this.selectedFramework,
|
|
1696
1905
|
manualMode: true
|
|
1697
1906
|
};
|
|
@@ -1812,6 +2021,14 @@ class ManualFrameworkInstallationWizard extends AutoInstallationWizard {
|
|
|
1812
2021
|
* Generate next steps with manual mode info
|
|
1813
2022
|
*/
|
|
1814
2023
|
generateManualNextSteps() {
|
|
2024
|
+
if (this.options.dryRun) {
|
|
2025
|
+
return [
|
|
2026
|
+
'Dry run complete. No packages were installed and no files were changed.',
|
|
2027
|
+
`Selected framework: ${this.framework?.name || 'unknown'}`,
|
|
2028
|
+
'Review the planned install command and file modifications.',
|
|
2029
|
+
'Run again without --dry-run to apply these changes.'
|
|
2030
|
+
];
|
|
2031
|
+
}
|
|
1815
2032
|
return [
|
|
1816
2033
|
'✅ Manual framework installation completed!',
|
|
1817
2034
|
`🎯 Selected framework: ${this.framework?.name || 'unknown'}`,
|
|
@@ -1840,6 +2057,232 @@ class ManualFrameworkInstallationWizard extends AutoInstallationWizard {
|
|
|
1840
2057
|
}
|
|
1841
2058
|
}
|
|
1842
2059
|
|
|
2060
|
+
const AGENT_PLAN_SCHEMA = {
|
|
2061
|
+
type: 'object',
|
|
2062
|
+
properties: {
|
|
2063
|
+
summary: { type: 'string' },
|
|
2064
|
+
targetPackagePath: { type: 'string' },
|
|
2065
|
+
framework: { type: 'string' },
|
|
2066
|
+
packageManager: { type: 'string' },
|
|
2067
|
+
recommendedChecks: {
|
|
2068
|
+
type: 'array',
|
|
2069
|
+
items: { type: 'string' }
|
|
2070
|
+
},
|
|
2071
|
+
risks: {
|
|
2072
|
+
type: 'array',
|
|
2073
|
+
items: { type: 'string' }
|
|
2074
|
+
},
|
|
2075
|
+
confidence: {
|
|
2076
|
+
type: 'number',
|
|
2077
|
+
minimum: 0,
|
|
2078
|
+
maximum: 1
|
|
2079
|
+
}
|
|
2080
|
+
},
|
|
2081
|
+
required: ['summary', 'recommendedChecks', 'risks', 'confidence'],
|
|
2082
|
+
additionalProperties: false
|
|
2083
|
+
};
|
|
2084
|
+
async function runClaudeAgentInstaller(options) {
|
|
2085
|
+
const prompt = `You are the HumanBehavior setup wizard agent.
|
|
2086
|
+
|
|
2087
|
+
Inspect this repository in read-only planning mode and produce a JSON setup plan.
|
|
2088
|
+
Do not edit files. Do not install packages. Do not include API keys or secrets.
|
|
2089
|
+
|
|
2090
|
+
Known deterministic install plan:
|
|
2091
|
+
- Target package path: ${options.installPlan.targetPackagePath}
|
|
2092
|
+
- Workspace root: ${options.installPlan.workspaceRoot}
|
|
2093
|
+
- Package manager: ${options.installPlan.packageManager}
|
|
2094
|
+
- Install command: ${options.installPlan.command}
|
|
2095
|
+
- Framework guess: ${options.framework}
|
|
2096
|
+
- Dry run: ${options.dryRun === true}
|
|
2097
|
+
|
|
2098
|
+
Return only the structured JSON matching the requested schema. Focus on:
|
|
2099
|
+
1. Whether the target package looks correct.
|
|
2100
|
+
2. Which files should be instrumented.
|
|
2101
|
+
3. Which checks should run after deterministic mutation.
|
|
2102
|
+
4. Risks or ambiguities the deterministic wizard should surface.`;
|
|
2103
|
+
let resultText = '';
|
|
2104
|
+
for await (const message of query({
|
|
2105
|
+
prompt,
|
|
2106
|
+
options: {
|
|
2107
|
+
cwd: options.projectRoot,
|
|
2108
|
+
permissionMode: 'plan',
|
|
2109
|
+
allowedTools: ['Read', 'Glob', 'Grep', 'LS'],
|
|
2110
|
+
disallowedTools: ['Edit', 'MultiEdit', 'Write', 'NotebookEdit', 'Bash'],
|
|
2111
|
+
maxTurns: 6,
|
|
2112
|
+
outputFormat: {
|
|
2113
|
+
type: 'json_schema',
|
|
2114
|
+
schema: AGENT_PLAN_SCHEMA
|
|
2115
|
+
},
|
|
2116
|
+
persistSession: false
|
|
2117
|
+
}
|
|
2118
|
+
})) {
|
|
2119
|
+
if (message.type === 'result' && message.subtype === 'success') {
|
|
2120
|
+
resultText = message.result || '';
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
return validateAgentPlan(resultText);
|
|
2124
|
+
}
|
|
2125
|
+
async function runClaudeAgentFullFlow(options) {
|
|
2126
|
+
const snapshot = snapshotProject(options.projectRoot);
|
|
2127
|
+
try {
|
|
2128
|
+
const prompt = `You are the HumanBehavior setup wizard agent.
|
|
2129
|
+
|
|
2130
|
+
Run the full setup flow for this app:
|
|
2131
|
+
1. Scan the codebase.
|
|
2132
|
+
2. Install the SDK using exactly this command if install is needed: ${options.installPlan.command}
|
|
2133
|
+
3. Instrument the client entrypoint using HumanBehavior SDK best practices.
|
|
2134
|
+
4. Use environment-variable based API key access only. Never write a raw API key.
|
|
2135
|
+
5. Run safe local checks if package scripts exist.
|
|
2136
|
+
6. Return a concise JSON summary matching the requested schema.
|
|
2137
|
+
|
|
2138
|
+
Hard constraints:
|
|
2139
|
+
- Work only in this target package: ${options.installPlan.targetPackagePath}
|
|
2140
|
+
- Do not edit files outside the target package.
|
|
2141
|
+
- Do not run destructive commands.
|
|
2142
|
+
- Do not include secrets in output.
|
|
2143
|
+
- If unsure, stop and report risks instead of guessing.
|
|
2144
|
+
|
|
2145
|
+
Framework guess: ${options.framework}
|
|
2146
|
+
Package manager: ${options.installPlan.packageManager}
|
|
2147
|
+
Workspace root: ${options.installPlan.workspaceRoot}`;
|
|
2148
|
+
let resultText = '';
|
|
2149
|
+
for await (const message of query({
|
|
2150
|
+
prompt,
|
|
2151
|
+
options: {
|
|
2152
|
+
cwd: options.installPlan.targetPackagePath,
|
|
2153
|
+
permissionMode: 'acceptEdits',
|
|
2154
|
+
allowedTools: ['Read', 'Glob', 'Grep', 'LS', 'Edit', 'Bash'],
|
|
2155
|
+
disallowedTools: ['Write', 'NotebookEdit', 'MultiEdit'],
|
|
2156
|
+
maxTurns: 12,
|
|
2157
|
+
outputFormat: {
|
|
2158
|
+
type: 'json_schema',
|
|
2159
|
+
schema: AGENT_PLAN_SCHEMA
|
|
2160
|
+
},
|
|
2161
|
+
persistSession: false,
|
|
2162
|
+
canUseTool: async (toolName, input) => {
|
|
2163
|
+
if (toolName === 'Bash') {
|
|
2164
|
+
const command = String(input.command || '');
|
|
2165
|
+
return isAllowedBash(command, options.installPlan.command)
|
|
2166
|
+
? { behavior: 'allow' }
|
|
2167
|
+
: { behavior: 'deny', message: `Command is outside setup wizard allowlist: ${command}` };
|
|
2168
|
+
}
|
|
2169
|
+
return { behavior: 'allow' };
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
})) {
|
|
2173
|
+
if (message.type === 'result') {
|
|
2174
|
+
if (message.subtype === 'success') {
|
|
2175
|
+
resultText = message.result || '';
|
|
2176
|
+
}
|
|
2177
|
+
else {
|
|
2178
|
+
throw new Error(message.subtype);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
const agentPlan = validateAgentPlan(resultText);
|
|
2183
|
+
return {
|
|
2184
|
+
success: true,
|
|
2185
|
+
agentPlan,
|
|
2186
|
+
result: resultText,
|
|
2187
|
+
rolledBack: false
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
catch (error) {
|
|
2191
|
+
restoreProject(options.projectRoot, snapshot);
|
|
2192
|
+
return {
|
|
2193
|
+
success: false,
|
|
2194
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2195
|
+
rolledBack: true
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
function validateAgentPlan(rawResult) {
|
|
2200
|
+
let parsed;
|
|
2201
|
+
try {
|
|
2202
|
+
parsed = JSON.parse(rawResult);
|
|
2203
|
+
}
|
|
2204
|
+
catch {
|
|
2205
|
+
throw new Error('Claude agent did not return valid JSON');
|
|
2206
|
+
}
|
|
2207
|
+
if (typeof parsed.summary !== 'string') {
|
|
2208
|
+
throw new Error('Claude agent plan missing summary');
|
|
2209
|
+
}
|
|
2210
|
+
if (!Array.isArray(parsed.recommendedChecks) || !parsed.recommendedChecks.every((item) => typeof item === 'string')) {
|
|
2211
|
+
throw new Error('Claude agent plan has invalid recommendedChecks');
|
|
2212
|
+
}
|
|
2213
|
+
if (!Array.isArray(parsed.risks) || !parsed.risks.every((item) => typeof item === 'string')) {
|
|
2214
|
+
throw new Error('Claude agent plan has invalid risks');
|
|
2215
|
+
}
|
|
2216
|
+
if (typeof parsed.confidence !== 'number' || parsed.confidence < 0 || parsed.confidence > 1) {
|
|
2217
|
+
throw new Error('Claude agent plan has invalid confidence');
|
|
2218
|
+
}
|
|
2219
|
+
return {
|
|
2220
|
+
summary: parsed.summary,
|
|
2221
|
+
targetPackagePath: typeof parsed.targetPackagePath === 'string' ? parsed.targetPackagePath : undefined,
|
|
2222
|
+
framework: typeof parsed.framework === 'string' ? parsed.framework : undefined,
|
|
2223
|
+
packageManager: typeof parsed.packageManager === 'string' ? parsed.packageManager : undefined,
|
|
2224
|
+
recommendedChecks: parsed.recommendedChecks,
|
|
2225
|
+
risks: parsed.risks,
|
|
2226
|
+
confidence: parsed.confidence,
|
|
2227
|
+
rawResult
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
function isAllowedBash(command, installCommand) {
|
|
2231
|
+
if (!command)
|
|
2232
|
+
return false;
|
|
2233
|
+
const trimmed = command.trim();
|
|
2234
|
+
if (trimmed === installCommand)
|
|
2235
|
+
return true;
|
|
2236
|
+
if (/^(npm|pnpm|yarn|bun) run (build|test|lint|typecheck|check)(\s|$)/.test(trimmed))
|
|
2237
|
+
return true;
|
|
2238
|
+
if (/^(npm|pnpm|yarn|bun) (test|lint)(\s|$)/.test(trimmed))
|
|
2239
|
+
return true;
|
|
2240
|
+
return false;
|
|
2241
|
+
}
|
|
2242
|
+
function snapshotProject(root) {
|
|
2243
|
+
const snapshot = new Map();
|
|
2244
|
+
for (const filePath of listProjectFiles(root)) {
|
|
2245
|
+
snapshot.set(filePath, fs.readFileSync(filePath, 'utf8'));
|
|
2246
|
+
}
|
|
2247
|
+
return snapshot;
|
|
2248
|
+
}
|
|
2249
|
+
function restoreProject(root, snapshot) {
|
|
2250
|
+
const currentFiles = new Set(listProjectFiles(root));
|
|
2251
|
+
for (const filePath of currentFiles) {
|
|
2252
|
+
if (!snapshot.has(filePath)) {
|
|
2253
|
+
fs.rmSync(filePath, { force: true });
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
for (const [filePath, content] of snapshot.entries()) {
|
|
2257
|
+
if (content !== null) {
|
|
2258
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
2259
|
+
fs.writeFileSync(filePath, content);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
function listProjectFiles(root) {
|
|
2264
|
+
const out = [];
|
|
2265
|
+
const ignored = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.nuxt', '.svelte-kit']);
|
|
2266
|
+
const walk = (dir) => {
|
|
2267
|
+
if (!fs.existsSync(dir))
|
|
2268
|
+
return;
|
|
2269
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
2270
|
+
if (ignored.has(entry))
|
|
2271
|
+
continue;
|
|
2272
|
+
const fullPath = path.join(dir, entry);
|
|
2273
|
+
const stat = fs.statSync(fullPath);
|
|
2274
|
+
if (stat.isDirectory()) {
|
|
2275
|
+
walk(fullPath);
|
|
2276
|
+
}
|
|
2277
|
+
else if (stat.isFile() && stat.size <= 1024 * 1024) {
|
|
2278
|
+
out.push(fullPath);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
2282
|
+
walk(root);
|
|
2283
|
+
return out;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
1843
2286
|
/**
|
|
1844
2287
|
* AI-Enhanced HumanBehavior SDK Auto-Installation CLI
|
|
1845
2288
|
*
|
|
@@ -1880,11 +2323,52 @@ class AIAutoInstallCLI {
|
|
|
1880
2323
|
// Run installation
|
|
1881
2324
|
const spinner = clack.spinner();
|
|
1882
2325
|
spinner.start('🔍 Analyzing your project with AI...');
|
|
1883
|
-
const wizard = new ManualFrameworkInstallationWizard(apiKey, projectPath, framework
|
|
2326
|
+
const wizard = new ManualFrameworkInstallationWizard(apiKey, projectPath, framework, {
|
|
2327
|
+
dryRun: this.options.dryRun,
|
|
2328
|
+
upgrade: this.options.upgrade
|
|
2329
|
+
});
|
|
2330
|
+
if (this.options.agent) {
|
|
2331
|
+
const detected = await wizard.detectFramework();
|
|
2332
|
+
const installPlan = wizard.planPackageInstall();
|
|
2333
|
+
const agentResult = this.options.agentApply && !this.options.dryRun
|
|
2334
|
+
? await runClaudeAgentFullFlow({
|
|
2335
|
+
projectRoot: projectPath,
|
|
2336
|
+
installPlan,
|
|
2337
|
+
framework: detected.name,
|
|
2338
|
+
dryRun: this.options.dryRun
|
|
2339
|
+
})
|
|
2340
|
+
: { success: true, agentPlan: await runClaudeAgentInstaller({
|
|
2341
|
+
projectRoot: projectPath,
|
|
2342
|
+
installPlan,
|
|
2343
|
+
framework: detected.name,
|
|
2344
|
+
dryRun: this.options.dryRun
|
|
2345
|
+
}), rolledBack: false };
|
|
2346
|
+
if (agentResult.success && agentResult.agentPlan) {
|
|
2347
|
+
wizard.setAgentPlan(agentResult.agentPlan);
|
|
2348
|
+
if (this.options.agentApply && !this.options.dryRun) {
|
|
2349
|
+
clack.outro('Claude agent completed setup successfully.');
|
|
2350
|
+
this.writeReport({
|
|
2351
|
+
success: true,
|
|
2352
|
+
framework: detected,
|
|
2353
|
+
modifications: [],
|
|
2354
|
+
errors: [],
|
|
2355
|
+
nextSteps: agentResult.agentPlan.recommendedChecks,
|
|
2356
|
+
dryRun: false,
|
|
2357
|
+
installPlan,
|
|
2358
|
+
agentPlan: agentResult.agentPlan
|
|
2359
|
+
}, framework);
|
|
2360
|
+
return;
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
else {
|
|
2364
|
+
clack.note(`${agentResult.error || 'Unknown agent error'}\nRolled back: ${agentResult.rolledBack ? 'yes' : 'no'}\nContinuing with deterministic fallback.`, 'Claude Agent Fallback');
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
1884
2367
|
const result = await wizard.install();
|
|
1885
|
-
spinner.stop('Analysis complete!');
|
|
2368
|
+
spinner.stop(this.options.dryRun ? 'Dry run complete!' : 'Analysis complete!');
|
|
1886
2369
|
// Display results
|
|
1887
2370
|
this.displayResults(result, framework);
|
|
2371
|
+
this.writeReport(result, framework);
|
|
1888
2372
|
}
|
|
1889
2373
|
catch (error) {
|
|
1890
2374
|
clack.cancel(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
@@ -1895,6 +2379,12 @@ class AIAutoInstallCLI {
|
|
|
1895
2379
|
if (this.options.apiKey) {
|
|
1896
2380
|
return this.options.apiKey;
|
|
1897
2381
|
}
|
|
2382
|
+
if (this.options.apiKeyFile) {
|
|
2383
|
+
return fs.readFileSync(this.options.apiKeyFile, 'utf8').trim();
|
|
2384
|
+
}
|
|
2385
|
+
if (process.env.HB_API_KEY) {
|
|
2386
|
+
return process.env.HB_API_KEY;
|
|
2387
|
+
}
|
|
1898
2388
|
const apiKey = await clack.text({
|
|
1899
2389
|
message: 'Enter your HumanBehavior API key:',
|
|
1900
2390
|
placeholder: 'hb_...',
|
|
@@ -1915,6 +2405,9 @@ class AIAutoInstallCLI {
|
|
|
1915
2405
|
return confirmed;
|
|
1916
2406
|
}
|
|
1917
2407
|
async chooseFramework() {
|
|
2408
|
+
if (this.options.framework) {
|
|
2409
|
+
return this.options.framework;
|
|
2410
|
+
}
|
|
1918
2411
|
const framework = await clack.select({
|
|
1919
2412
|
message: 'Select your framework:',
|
|
1920
2413
|
options: [
|
|
@@ -1934,13 +2427,39 @@ class AIAutoInstallCLI {
|
|
|
1934
2427
|
}
|
|
1935
2428
|
displayResults(result, framework) {
|
|
1936
2429
|
if (result.success) {
|
|
1937
|
-
clack.outro(
|
|
2430
|
+
clack.outro(result.dryRun
|
|
2431
|
+
? 'Dry run completed successfully. No packages were installed and no files were changed.'
|
|
2432
|
+
: '🎉 Installation completed successfully!');
|
|
1938
2433
|
// Display framework info
|
|
1939
2434
|
clack.note(`Framework detected: ${result.framework.name} (${result.framework.type})`, 'Framework Info');
|
|
1940
2435
|
// Display modifications
|
|
2436
|
+
if (result.installPlan) {
|
|
2437
|
+
clack.note([
|
|
2438
|
+
`Command: ${result.installPlan.command}`,
|
|
2439
|
+
`Run from: ${result.installPlan.cwd}`,
|
|
2440
|
+
`Target package: ${result.installPlan.targetPackagePath}`,
|
|
2441
|
+
`Workspace: ${result.installPlan.isWorkspace ? 'yes' : 'no'}`,
|
|
2442
|
+
`Version status: ${result.installPlan.versionStatus}`,
|
|
2443
|
+
result.installPlan.alreadyInstalled
|
|
2444
|
+
? `Already installed: ${result.installPlan.installedVersion}`
|
|
2445
|
+
: `Reason: ${result.installPlan.reason}`
|
|
2446
|
+
].join('\n'), result.dryRun ? 'Planned Package Install' : 'Package Install');
|
|
2447
|
+
}
|
|
1941
2448
|
if (result.modifications && result.modifications.length > 0) {
|
|
1942
2449
|
const modifications = result.modifications.map((mod) => `${mod.action}: ${mod.filePath} - ${mod.description}`);
|
|
1943
|
-
clack.note(modifications.join('\n'), 'Files Modified');
|
|
2450
|
+
clack.note(modifications.join('\n'), result.dryRun ? 'Planned File Changes' : 'Files Modified');
|
|
2451
|
+
}
|
|
2452
|
+
if (result.agentPlan) {
|
|
2453
|
+
clack.note([
|
|
2454
|
+
result.agentPlan.summary,
|
|
2455
|
+
`Confidence: ${Math.round(result.agentPlan.confidence * 100)}%`,
|
|
2456
|
+
result.agentPlan.recommendedChecks.length
|
|
2457
|
+
? `Checks: ${result.agentPlan.recommendedChecks.join(', ')}`
|
|
2458
|
+
: 'Checks: none',
|
|
2459
|
+
result.agentPlan.risks.length
|
|
2460
|
+
? `Risks: ${result.agentPlan.risks.join(', ')}`
|
|
2461
|
+
: 'Risks: none'
|
|
2462
|
+
].join('\n'), 'Claude Agent Plan');
|
|
1944
2463
|
}
|
|
1945
2464
|
// Display next steps
|
|
1946
2465
|
if (result.nextSteps && result.nextSteps.length > 0) {
|
|
@@ -1961,6 +2480,37 @@ class AIAutoInstallCLI {
|
|
|
1961
2480
|
}
|
|
1962
2481
|
}
|
|
1963
2482
|
}
|
|
2483
|
+
writeReport(result, selectedFramework) {
|
|
2484
|
+
if (!this.options.reportJson)
|
|
2485
|
+
return;
|
|
2486
|
+
const report = {
|
|
2487
|
+
schemaVersion: 1,
|
|
2488
|
+
status: result.success ? (result.dryRun ? 'dry-run' : 'completed') : 'failed',
|
|
2489
|
+
selectedFramework,
|
|
2490
|
+
framework: result.framework,
|
|
2491
|
+
dryRun: result.dryRun === true,
|
|
2492
|
+
installPlan: result.installPlan,
|
|
2493
|
+
agentPlan: result.agentPlan
|
|
2494
|
+
? {
|
|
2495
|
+
summary: result.agentPlan.summary,
|
|
2496
|
+
targetPackagePath: result.agentPlan.targetPackagePath,
|
|
2497
|
+
framework: result.agentPlan.framework,
|
|
2498
|
+
packageManager: result.agentPlan.packageManager,
|
|
2499
|
+
recommendedChecks: result.agentPlan.recommendedChecks,
|
|
2500
|
+
risks: result.agentPlan.risks,
|
|
2501
|
+
confidence: result.agentPlan.confidence
|
|
2502
|
+
}
|
|
2503
|
+
: undefined,
|
|
2504
|
+
modifications: (result.modifications || []).map((mod) => ({
|
|
2505
|
+
filePath: mod.filePath,
|
|
2506
|
+
action: mod.action,
|
|
2507
|
+
description: mod.description
|
|
2508
|
+
})),
|
|
2509
|
+
errors: result.errors || [],
|
|
2510
|
+
nextSteps: result.nextSteps || []
|
|
2511
|
+
};
|
|
2512
|
+
fs.writeFileSync(this.options.reportJson, JSON.stringify(report, null, 2));
|
|
2513
|
+
}
|
|
1964
2514
|
}
|
|
1965
2515
|
function parseArgs() {
|
|
1966
2516
|
const args = process.argv.slice(2);
|
|
@@ -1980,20 +2530,34 @@ function parseArgs() {
|
|
|
1980
2530
|
case '--dry-run':
|
|
1981
2531
|
options.dryRun = true;
|
|
1982
2532
|
break;
|
|
2533
|
+
case '--upgrade':
|
|
2534
|
+
options.upgrade = true;
|
|
2535
|
+
break;
|
|
2536
|
+
case '--agent':
|
|
2537
|
+
options.agent = true;
|
|
2538
|
+
break;
|
|
2539
|
+
case '--agent-apply':
|
|
2540
|
+
options.agent = true;
|
|
2541
|
+
options.agentApply = true;
|
|
2542
|
+
break;
|
|
1983
2543
|
case '--project':
|
|
1984
2544
|
case '-p':
|
|
1985
2545
|
options.projectPath = args[++i];
|
|
1986
2546
|
break;
|
|
2547
|
+
case '--api-key-file':
|
|
2548
|
+
options.apiKeyFile = args[++i];
|
|
2549
|
+
break;
|
|
2550
|
+
case '--report-json':
|
|
2551
|
+
options.reportJson = args[++i];
|
|
2552
|
+
break;
|
|
1987
2553
|
case '--framework':
|
|
1988
2554
|
case '-f':
|
|
1989
2555
|
options.framework = args[++i];
|
|
1990
2556
|
break;
|
|
1991
2557
|
case 'init':
|
|
1992
|
-
if (
|
|
1993
|
-
|
|
1994
|
-
process.exit(1);
|
|
2558
|
+
if (args[i + 1] && !args[i + 1].startsWith('-')) {
|
|
2559
|
+
options.apiKey = args[++i];
|
|
1995
2560
|
}
|
|
1996
|
-
options.apiKey = args[++i];
|
|
1997
2561
|
break;
|
|
1998
2562
|
default:
|
|
1999
2563
|
if (!options.apiKey && !arg.startsWith('-')) {
|
|
@@ -2014,7 +2578,12 @@ Options:
|
|
|
2014
2578
|
-h, --help Show this help message
|
|
2015
2579
|
-y, --yes Skip all prompts and use defaults
|
|
2016
2580
|
--dry-run Show what would be changed without making changes
|
|
2581
|
+
--upgrade Upgrade stale existing SDK versions
|
|
2582
|
+
--agent Use Claude Agent SDK to inspect and plan before deterministic changes
|
|
2583
|
+
--agent-apply Let Claude attempt guarded install/edit/check before deterministic fallback
|
|
2017
2584
|
|
|
2585
|
+
--api-key-file <path> Read API key from a local file
|
|
2586
|
+
--report-json <path> Write a structured install report without file contents
|
|
2018
2587
|
-p, --project <path> Specify project directory
|
|
2019
2588
|
-f, --framework <name> Specify framework manually
|
|
2020
2589
|
|
|
@@ -2025,24 +2594,15 @@ Examples:
|
|
|
2025
2594
|
npx humanbehavior-js ai-auto-install --framework react --yes
|
|
2026
2595
|
`);
|
|
2027
2596
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
npx humanbehavior-js init hb_your_api_key_here
|
|
2037
|
-
`);
|
|
2597
|
+
const isMain = import.meta.url === `file://${process.argv[1]}`;
|
|
2598
|
+
if (isMain) {
|
|
2599
|
+
const options = parseArgs();
|
|
2600
|
+
const cli = new AIAutoInstallCLI(options);
|
|
2601
|
+
cli.run().catch((error) => {
|
|
2602
|
+
clack.cancel(`Unexpected error: ${error.message}`);
|
|
2603
|
+
process.exit(1);
|
|
2604
|
+
});
|
|
2038
2605
|
}
|
|
2039
|
-
// Main execution
|
|
2040
|
-
const options = parseArgs();
|
|
2041
|
-
const cli = new AIAutoInstallCLI(options);
|
|
2042
|
-
cli.run().catch((error) => {
|
|
2043
|
-
clack.cancel(`Unexpected error: ${error.message}`);
|
|
2044
|
-
process.exit(1);
|
|
2045
|
-
});
|
|
2046
2606
|
|
|
2047
2607
|
export { AIAutoInstallCLI };
|
|
2048
2608
|
//# sourceMappingURL=ai-auto-install.js.map
|