feat(cli): add prdy, quality-rails, and wizard subcommands (#104)
Some checks failed
ci/woodpecker/push/ci Pipeline failed
Some checks failed
ci/woodpecker/push/ci Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #104.
This commit is contained in:
@@ -21,6 +21,9 @@
|
|||||||
"test": "vitest run --passWithNoTests"
|
"test": "vitest run --passWithNoTests"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mosaic/mosaic": "workspace:^",
|
||||||
|
"@mosaic/prdy": "workspace:^",
|
||||||
|
"@mosaic/quality-rails": "workspace:^",
|
||||||
"ink": "^5.0.0",
|
"ink": "^5.0.0",
|
||||||
"ink-text-input": "^6.0.0",
|
"ink-text-input": "^6.0.0",
|
||||||
"ink-spinner": "^5.0.0",
|
"ink-spinner": "^5.0.0",
|
||||||
@@ -29,6 +32,7 @@
|
|||||||
"commander": "^13.0.0"
|
"commander": "^13.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.0.0",
|
||||||
"@types/react": "^18.3.0",
|
"@types/react": "^18.3.0",
|
||||||
"tsx": "^4.0.0",
|
"tsx": "^4.0.0",
|
||||||
"typescript": "^5.8.0",
|
"typescript": "^5.8.0",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { Command } from 'commander';
|
import { Command } from 'commander';
|
||||||
|
import { buildPrdyCli } from '@mosaic/prdy';
|
||||||
|
import { createQualityRailsCli } from '@mosaic/quality-rails';
|
||||||
|
|
||||||
const program = new Command();
|
const program = new Command();
|
||||||
|
|
||||||
@@ -25,4 +27,85 @@ program
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// prdy subcommand
|
||||||
|
// buildPrdyCli() returns a wrapper Command; extract the 'prdy' subcommand from it.
|
||||||
|
// Type cast is required because @mosaic/prdy uses commander@12 while @mosaic/cli uses commander@13.
|
||||||
|
const prdyWrapper = buildPrdyCli();
|
||||||
|
const prdyCmd = prdyWrapper.commands.find((c) => c.name() === 'prdy');
|
||||||
|
if (prdyCmd !== undefined) {
|
||||||
|
program.addCommand(prdyCmd as unknown as Command);
|
||||||
|
}
|
||||||
|
|
||||||
|
// quality-rails subcommand
|
||||||
|
// createQualityRailsCli() returns a wrapper Command; extract the 'quality-rails' subcommand.
|
||||||
|
const qrWrapper = createQualityRailsCli();
|
||||||
|
const qrCmd = qrWrapper.commands.find((c) => c.name() === 'quality-rails');
|
||||||
|
if (qrCmd !== undefined) {
|
||||||
|
program.addCommand(qrCmd as unknown as Command);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wizard subcommand — wraps @mosaic/mosaic installation wizard
|
||||||
|
program
|
||||||
|
.command('wizard')
|
||||||
|
.description('Run the Mosaic installation wizard')
|
||||||
|
.option('--non-interactive', 'Run without prompts (uses defaults + flags)')
|
||||||
|
.option('--source-dir <path>', 'Source directory for framework files')
|
||||||
|
.option('--mosaic-home <path>', 'Target config directory')
|
||||||
|
.option('--name <name>', 'Agent name')
|
||||||
|
.option('--role <description>', 'Agent role description')
|
||||||
|
.option('--style <style>', 'Communication style: direct|friendly|formal')
|
||||||
|
.option('--accessibility <prefs>', 'Accessibility preferences')
|
||||||
|
.option('--guardrails <rules>', 'Custom guardrails')
|
||||||
|
.option('--user-name <name>', 'Your name')
|
||||||
|
.option('--pronouns <pronouns>', 'Your pronouns')
|
||||||
|
.option('--timezone <tz>', 'Your timezone')
|
||||||
|
.action(async (opts: Record<string, string | boolean | undefined>) => {
|
||||||
|
// Dynamic import to avoid loading wizard deps for other commands
|
||||||
|
const {
|
||||||
|
runWizard,
|
||||||
|
ClackPrompter,
|
||||||
|
HeadlessPrompter,
|
||||||
|
createConfigService,
|
||||||
|
WizardCancelledError,
|
||||||
|
DEFAULT_MOSAIC_HOME,
|
||||||
|
} = await import('@mosaic/mosaic');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const mosaicHome = (opts['mosaicHome'] as string | undefined) ?? DEFAULT_MOSAIC_HOME;
|
||||||
|
const sourceDir = (opts['sourceDir'] as string | undefined) ?? mosaicHome;
|
||||||
|
|
||||||
|
const prompter = opts['nonInteractive'] ? new HeadlessPrompter() : new ClackPrompter();
|
||||||
|
|
||||||
|
const configService = createConfigService(mosaicHome, sourceDir);
|
||||||
|
|
||||||
|
await runWizard({
|
||||||
|
mosaicHome,
|
||||||
|
sourceDir,
|
||||||
|
prompter,
|
||||||
|
configService,
|
||||||
|
cliOverrides: {
|
||||||
|
soul: {
|
||||||
|
agentName: opts['name'] as string | undefined,
|
||||||
|
roleDescription: opts['role'] as string | undefined,
|
||||||
|
communicationStyle: opts['style'] as 'direct' | 'friendly' | 'formal' | undefined,
|
||||||
|
accessibility: opts['accessibility'] as string | undefined,
|
||||||
|
customGuardrails: opts['guardrails'] as string | undefined,
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
userName: opts['userName'] as string | undefined,
|
||||||
|
pronouns: opts['pronouns'] as string | undefined,
|
||||||
|
timezone: opts['timezone'] as string | undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof WizardCancelledError) {
|
||||||
|
console.log('\nWizard cancelled.');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
console.error('Wizard failed:', err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
program.parse();
|
program.parse();
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
import { Command } from 'commander';
|
import { Command } from 'commander';
|
||||||
import { ClackPrompter } from './prompter/clack-prompter.js';
|
import { ClackPrompter } from './prompter/clack-prompter.js';
|
||||||
import { HeadlessPrompter } from './prompter/headless-prompter.js';
|
import { HeadlessPrompter } from './prompter/headless-prompter.js';
|
||||||
@@ -8,7 +11,12 @@ import { WizardCancelledError } from './errors.js';
|
|||||||
import { VERSION, DEFAULT_MOSAIC_HOME } from './constants.js';
|
import { VERSION, DEFAULT_MOSAIC_HOME } from './constants.js';
|
||||||
import type { CommunicationStyle } from './types.js';
|
import type { CommunicationStyle } from './types.js';
|
||||||
|
|
||||||
export { VERSION };
|
export { VERSION, DEFAULT_MOSAIC_HOME };
|
||||||
|
export { runWizard } from './wizard.js';
|
||||||
|
export { ClackPrompter } from './prompter/clack-prompter.js';
|
||||||
|
export { HeadlessPrompter } from './prompter/headless-prompter.js';
|
||||||
|
export { createConfigService } from './config/config-service.js';
|
||||||
|
export { WizardCancelledError } from './errors.js';
|
||||||
|
|
||||||
const program = new Command()
|
const program = new Command()
|
||||||
.name('mosaic-wizard')
|
.name('mosaic-wizard')
|
||||||
@@ -70,4 +78,7 @@ program
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
program.parse();
|
const entryPath = process.argv[1] ? resolve(process.argv[1]) : '';
|
||||||
|
if (entryPath.length > 0 && entryPath === fileURLToPath(import.meta.url)) {
|
||||||
|
program.parse();
|
||||||
|
}
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -268,6 +268,15 @@ importers:
|
|||||||
|
|
||||||
packages/cli:
|
packages/cli:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@mosaic/mosaic':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../mosaic
|
||||||
|
'@mosaic/prdy':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../prdy
|
||||||
|
'@mosaic/quality-rails':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../quality-rails
|
||||||
commander:
|
commander:
|
||||||
specifier: ^13.0.0
|
specifier: ^13.0.0
|
||||||
version: 13.1.0
|
version: 13.1.0
|
||||||
@@ -287,6 +296,9 @@ importers:
|
|||||||
specifier: ^4.8.0
|
specifier: ^4.8.0
|
||||||
version: 4.8.3
|
version: 4.8.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^22.0.0
|
||||||
|
version: 22.19.15
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.3.0
|
specifier: ^18.3.0
|
||||||
version: 18.3.28
|
version: 18.3.28
|
||||||
|
|||||||
Reference in New Issue
Block a user