@memberjunction/cli 6.1.2 → 6.2.0-edge.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/README.md +18 -7
- package/dist/commands/agent/init.d.ts +23 -0
- package/dist/commands/agent/init.d.ts.map +1 -0
- package/dist/commands/agent/init.js +222 -0
- package/dist/commands/agent/init.js.map +1 -0
- package/dist/commands/doctor/index.d.ts +6 -4
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +43 -18
- package/dist/commands/doctor/index.js.map +1 -1
- package/dist/commands/test/scripts.d.ts +13 -0
- package/dist/commands/test/scripts.d.ts.map +1 -0
- package/dist/commands/test/scripts.js +50 -0
- package/dist/commands/test/scripts.js.map +1 -0
- package/dist/init-templates/citizen-builder/.agents/skills/new-agent/SKILL.md +66 -0
- package/dist/init-templates/citizen-builder/.agents/skills/package-agent/SKILL.md +69 -0
- package/dist/init-templates/citizen-builder/.agents/skills/test-agent/SKILL.md +61 -0
- package/dist/init-templates/citizen-builder/.claude/skills/new-agent/SKILL.md +66 -0
- package/dist/init-templates/citizen-builder/.claude/skills/package-agent/SKILL.md +69 -0
- package/dist/init-templates/citizen-builder/.claude/skills/test-agent/SKILL.md +61 -0
- package/dist/init-templates/citizen-builder/.env.example +30 -0
- package/dist/init-templates/citizen-builder/AGENTS.md +331 -0
- package/dist/init-templates/citizen-builder/README.md +259 -0
- package/dist/init-templates/citizen-builder/docker/Dockerfile +16 -0
- package/dist/init-templates/citizen-builder/docker-compose.yml +59 -0
- package/dist/init-templates/citizen-builder/docs/ENTERPRISE_ORG_SETUP.md +73 -0
- package/dist/init-templates/citizen-builder/docs/PROMOTION_GUIDE.md +70 -0
- package/dist/init-templates/citizen-builder/metadata/.mj-sync.json +7 -0
- package/dist/init-templates/citizen-builder/metadata/agents/.customer-insight-agent.json +55 -0
- package/dist/init-templates/citizen-builder/metadata/agents/.mj-sync.json +45 -0
- package/dist/init-templates/citizen-builder/metadata/prompts/.customer-insight-prompt.json +15 -0
- package/dist/init-templates/citizen-builder/metadata/prompts/.mj-sync.json +45 -0
- package/dist/init-templates/citizen-builder/metadata/prompts/templates/customer-insight.template.md +18 -0
- package/dist/init-templates/citizen-builder/scripts/bootstrap.sh +47 -0
- package/dist/init-templates/citizen-builder/scripts/docker-entrypoint.sh +184 -0
- package/dist/init-templates/citizen-builder/scripts/generate-capabilities.sh +74 -0
- package/dist/init-templates/citizen-builder/scripts/query-run-history.sh +34 -0
- package/dist/init-templates/citizen-builder/scripts/sync-metadata.sh +14 -0
- package/dist/init-templates/remote-mj/README.md +4 -4
- package/oclif.manifest.json +271 -98
- package/package.json +22 -22
package/README.md
CHANGED
|
@@ -526,13 +526,24 @@ mj app check-updates
|
|
|
526
526
|
|
|
527
527
|
#### Internal / dangerous flags
|
|
528
528
|
|
|
529
|
-
Intentionally omitted from `--help`.
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
`
|
|
535
|
-
`
|
|
529
|
+
Intentionally omitted from `--help`. Almost never needed: MJ's own Open Apps use the
|
|
530
|
+
`__mj_<AppName>` schema namespace (`__mj_BizAppsCommon`, `__mj_BizAppsForms`, …), which
|
|
531
|
+
installs on the default path with no flag at all.
|
|
532
|
+
|
|
533
|
+
- `--dangerously-ignore-dbl-underscore-schema-rule` — available on `mj app install`,
|
|
534
|
+
`mj app upgrade` and `mj app remove`. Allows a `__`-prefixed schema name *outside* the
|
|
535
|
+
`__mj_` app namespace (e.g. `__bcsaas`). Reserved schemas remain hard-blocked regardless,
|
|
536
|
+
on both supported platforms: every schema the **database** owns — `dbo`, `sys`, `guest`,
|
|
537
|
+
SQL Server's nine `db_*` fixed database-role schemas, `public`, anything starting `pg_`,
|
|
538
|
+
and `information_schema` — plus every schema **MJ** owns: `__mj` and `__mj_UDT`. Each of
|
|
539
|
+
these already exists in a stock database, so naming one would not create it, it would
|
|
540
|
+
*adopt* it — and `mj app remove` would then drop it.
|
|
541
|
+
|
|
542
|
+
Removing an app installed under one of those `__`-outside-namespace names (e.g. `__bcsaas`)
|
|
543
|
+
needs the same flag again: `mj app remove <app> --dangerously-ignore-dbl-underscore-schema-rule`
|
|
544
|
+
drops the schema. `--keep-data` is the exit for the platform- and MJ-reserved names above, which MJ
|
|
545
|
+
must never drop: `mj app remove <app> --keep-data` unregisters the app and leaves the
|
|
546
|
+
schema in place.
|
|
536
547
|
|
|
537
548
|
```bash
|
|
538
549
|
mj app install https://github.com/BlueCypress/SaaS \
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class AgentInit extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static aliases: string[];
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static args: {
|
|
7
|
+
dir: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
start: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
app: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
'skip-docker-check': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
private resolveTemplateDir;
|
|
17
|
+
private ensureDockerRunning;
|
|
18
|
+
private startEnvironment;
|
|
19
|
+
private printDockerInstallInstructions;
|
|
20
|
+
private printNextSteps;
|
|
21
|
+
private printCodingAgentInstructions;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,OAAO,EAAS,MAAM,aAAa,CAAC;AAUnD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,OAAO;IAC5C,MAAM,CAAC,WAAW,SAC+G;IAEjI,MAAM,CAAC,OAAO,WAAsB;IAEpC,MAAM,CAAC,QAAQ,WAKb;IAEF,MAAM,CAAC,IAAI;;MAMT;IAEF,MAAM,CAAC,KAAK;;;;;MAmBV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA0E1B,OAAO,CAAC,kBAAkB;IAsB1B,OAAO,CAAC,mBAAmB;IAmC3B,OAAO,CAAC,gBAAgB;IAiCxB,OAAO,CAAC,8BAA8B;IActC,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,4BAA4B;CAcrC"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { existsSync, mkdirSync, cpSync, copyFileSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { execSync } from 'node:child_process';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import ora from 'ora-classic';
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
export default class AgentInit extends Command {
|
|
10
|
+
static { this.description = 'Initialize a MemberJunction Citizen Agent Builder environment in the current or specified directory, including Docker setup.'; }
|
|
11
|
+
static { this.aliases = ['ai:agents:init']; }
|
|
12
|
+
static { this.examples = [
|
|
13
|
+
'<%= config.bin %> <%= command.id %>',
|
|
14
|
+
'<%= config.bin %> <%= command.id %> ./my-agent-workspace',
|
|
15
|
+
'<%= config.bin %> <%= command.id %> --no-start',
|
|
16
|
+
'<%= config.bin %> <%= command.id %> --app https://github.com/BlueCypress/bc-sampledata',
|
|
17
|
+
]; }
|
|
18
|
+
static { this.args = {
|
|
19
|
+
dir: Args.string({
|
|
20
|
+
description: 'Target directory for the agent builder environment (default: current directory).',
|
|
21
|
+
required: false,
|
|
22
|
+
default: '.',
|
|
23
|
+
}),
|
|
24
|
+
}; }
|
|
25
|
+
static { this.flags = {
|
|
26
|
+
force: Flags.boolean({
|
|
27
|
+
char: 'f',
|
|
28
|
+
description: 'Overwrite existing files in target directory if they already exist.',
|
|
29
|
+
default: false,
|
|
30
|
+
}),
|
|
31
|
+
start: Flags.boolean({
|
|
32
|
+
char: 's',
|
|
33
|
+
description: 'Start Docker containers and run bootstrap immediately after initialization (pass --no-start to skip).',
|
|
34
|
+
default: true,
|
|
35
|
+
allowNo: true,
|
|
36
|
+
}),
|
|
37
|
+
app: Flags.string({
|
|
38
|
+
description: 'Open App Git URL to install for business context (default: More Cheese).',
|
|
39
|
+
}),
|
|
40
|
+
'skip-docker-check': Flags.boolean({
|
|
41
|
+
description: 'Skip verifying whether Docker Desktop is currently running.',
|
|
42
|
+
default: false,
|
|
43
|
+
}),
|
|
44
|
+
}; }
|
|
45
|
+
async run() {
|
|
46
|
+
const { args, flags } = await this.parse(AgentInit);
|
|
47
|
+
const targetDir = path.resolve(process.cwd(), args.dir);
|
|
48
|
+
this.log(chalk.bold.cyan('\n🚀 MemberJunction Citizen Agent Builder\n'));
|
|
49
|
+
// 1. Resolve template directory
|
|
50
|
+
const templateDir = this.resolveTemplateDir();
|
|
51
|
+
if (!templateDir) {
|
|
52
|
+
this.error(chalk.red('✗ Citizen Agent Builder template assets could not be located.\n') +
|
|
53
|
+
'Please ensure @memberjunction/cli is properly installed.');
|
|
54
|
+
}
|
|
55
|
+
// 2. Check Docker availability & attempt auto-launch if stopped on macOS
|
|
56
|
+
const dockerAvailable = flags['skip-docker-check'] ? false : this.ensureDockerRunning();
|
|
57
|
+
// 3. Ensure target directory exists
|
|
58
|
+
if (!existsSync(targetDir)) {
|
|
59
|
+
mkdirSync(targetDir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
// 4. Check for existing files
|
|
62
|
+
const agentsMdPath = path.join(targetDir, 'AGENTS.md');
|
|
63
|
+
if (existsSync(agentsMdPath) && !flags.force) {
|
|
64
|
+
this.log(chalk.yellow(`Target directory '${targetDir}' already contains AGENTS.md.\nUse --force to overwrite existing files.\n`));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// 5. Scaffold files
|
|
68
|
+
const spinner = ora('Scaffolding agent builder workspace...').start();
|
|
69
|
+
try {
|
|
70
|
+
cpSync(templateDir, targetDir, { recursive: true });
|
|
71
|
+
// Create .env from .env.example if missing
|
|
72
|
+
const envExamplePath = path.join(targetDir, '.env.example');
|
|
73
|
+
const envPath = path.join(targetDir, '.env');
|
|
74
|
+
if (existsSync(envExamplePath) && !existsSync(envPath)) {
|
|
75
|
+
copyFileSync(envExamplePath, envPath);
|
|
76
|
+
}
|
|
77
|
+
// If custom app URL was supplied, update .env
|
|
78
|
+
if (flags.app && existsSync(envPath)) {
|
|
79
|
+
let envContent = readFileSync(envPath, 'utf8');
|
|
80
|
+
envContent = envContent.replace(/OPEN_APP_INSTALL_URL=.*/, `OPEN_APP_INSTALL_URL=${flags.app}`);
|
|
81
|
+
writeFileSync(envPath, envContent, 'utf8');
|
|
82
|
+
}
|
|
83
|
+
spinner.succeed(chalk.green('Workspace scaffolded successfully!'));
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
spinner.fail(chalk.red('Failed to scaffold workspace.'));
|
|
87
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
88
|
+
this.error(errorMsg);
|
|
89
|
+
}
|
|
90
|
+
// 6. Start Docker and bootstrap if requested and available
|
|
91
|
+
if (flags.start && dockerAvailable) {
|
|
92
|
+
this.startEnvironment(targetDir);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
if (!dockerAvailable && !flags['skip-docker-check']) {
|
|
96
|
+
this.printDockerInstallInstructions();
|
|
97
|
+
}
|
|
98
|
+
this.printNextSteps(targetDir, dockerAvailable);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
resolveTemplateDir() {
|
|
102
|
+
// 1. Check bundled template ({src,dist}/init-templates/citizen-builder)
|
|
103
|
+
const bundledPath = path.join(__dirname, '..', '..', 'init-templates', 'citizen-builder');
|
|
104
|
+
if (existsSync(bundledPath)) {
|
|
105
|
+
return bundledPath;
|
|
106
|
+
}
|
|
107
|
+
// 2. Check src template when running from dist in dev mode
|
|
108
|
+
const bundledSrcPath = path.join(__dirname, '..', '..', '..', 'src', 'init-templates', 'citizen-builder');
|
|
109
|
+
if (existsSync(bundledSrcPath)) {
|
|
110
|
+
return bundledSrcPath;
|
|
111
|
+
}
|
|
112
|
+
// 3. Check monorepo root fallback (5 levels up from commands/agent)
|
|
113
|
+
const monorepoRootPath = path.resolve(__dirname, '../../../../../citizen-builder');
|
|
114
|
+
if (existsSync(monorepoRootPath)) {
|
|
115
|
+
return monorepoRootPath;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
ensureDockerRunning() {
|
|
120
|
+
// Step 1: Check if docker daemon is already responsive
|
|
121
|
+
try {
|
|
122
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Daemon not answering
|
|
127
|
+
}
|
|
128
|
+
// Step 2: If macOS and Docker.app exists, try launching it automatically
|
|
129
|
+
if (process.platform === 'darwin' && existsSync('/Applications/Docker.app')) {
|
|
130
|
+
const launchSpinner = ora('Docker Desktop is installed but not running. Launching Docker Desktop...').start();
|
|
131
|
+
try {
|
|
132
|
+
execSync('open -a Docker', { stdio: 'ignore' });
|
|
133
|
+
// Poll for up to 20 seconds for the daemon socket
|
|
134
|
+
for (let i = 0; i < 20; i++) {
|
|
135
|
+
try {
|
|
136
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
137
|
+
launchSpinner.succeed(chalk.green('Docker Desktop started!'));
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
execSync('sleep 1');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
launchSpinner.warn(chalk.yellow('Docker Desktop is still initializing in the background.'));
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
launchSpinner.fail(chalk.yellow('Could not launch Docker Desktop automatically.'));
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
startEnvironment(targetDir) {
|
|
155
|
+
const spinner = ora('Starting MemberJunction container stack (docker compose up -d)...').start();
|
|
156
|
+
try {
|
|
157
|
+
execSync('docker compose up -d', { cwd: targetDir, stdio: 'pipe' });
|
|
158
|
+
spinner.succeed(chalk.green('Containers started!'));
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
spinner.fail(chalk.red('Failed to start containers.'));
|
|
162
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
163
|
+
this.error(errorMsg);
|
|
164
|
+
}
|
|
165
|
+
let apiPort = '4000';
|
|
166
|
+
let dbPort = '1433';
|
|
167
|
+
let explorerPort = '4202';
|
|
168
|
+
const envPath = path.join(targetDir, '.env');
|
|
169
|
+
if (existsSync(envPath)) {
|
|
170
|
+
const content = readFileSync(envPath, 'utf8');
|
|
171
|
+
const apiMatch = content.match(/^API_PORT=(\d+)/m);
|
|
172
|
+
if (apiMatch)
|
|
173
|
+
apiPort = apiMatch[1];
|
|
174
|
+
const dbMatch = content.match(/^DB_PORT=(\d+)/m);
|
|
175
|
+
if (dbMatch)
|
|
176
|
+
dbPort = dbMatch[1];
|
|
177
|
+
const explorerMatch = content.match(/^EXPLORER_PORT=(\d+)/m);
|
|
178
|
+
if (explorerMatch)
|
|
179
|
+
explorerPort = explorerMatch[1];
|
|
180
|
+
}
|
|
181
|
+
this.log(chalk.bold.cyan('\n🎉 Your MemberJunction local instance is running!'));
|
|
182
|
+
this.log(` API Endpoint: ${chalk.green(`http://localhost:${apiPort}`)}`);
|
|
183
|
+
this.log(` Explorer UI: ${chalk.green(`http://localhost:${explorerPort}`)}`);
|
|
184
|
+
this.log(` Database: ${chalk.green(`localhost:${dbPort}`)} (DB: MemberJunction)`);
|
|
185
|
+
this.log(` Healthcheck: ${chalk.gray(`http://localhost:${apiPort}/healthcheck`)}\n`);
|
|
186
|
+
this.printCodingAgentInstructions();
|
|
187
|
+
}
|
|
188
|
+
printDockerInstallInstructions() {
|
|
189
|
+
this.log(chalk.yellow('\n⚠️ Docker Desktop is not installed or not running.'));
|
|
190
|
+
this.log('MemberJunction Agent Builder requires Docker Desktop to run your local database and API.\n');
|
|
191
|
+
this.log(chalk.bold('To install Docker Desktop:'));
|
|
192
|
+
if (process.platform === 'darwin') {
|
|
193
|
+
this.log(' • Download: ' + chalk.cyan('https://www.docker.com/products/docker-desktop'));
|
|
194
|
+
this.log(' • Or install via Homebrew: ' + chalk.cyan('brew install --cask docker'));
|
|
195
|
+
}
|
|
196
|
+
else if (process.platform === 'win32') {
|
|
197
|
+
this.log(' • Download: ' + chalk.cyan('https://www.docker.com/products/docker-desktop'));
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
this.log(' • Install Docker Engine: ' + chalk.cyan('https://docs.docker.com/engine/install/'));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
printNextSteps(targetDir, dockerAvailable) {
|
|
204
|
+
this.log(chalk.bold.white('\n📋 Next Steps:'));
|
|
205
|
+
this.log(`1. Navigate to your workspace:\n ${chalk.cyan(`cd ${path.relative(process.cwd(), targetDir) || '.'}`)}`);
|
|
206
|
+
this.log(`2. Add your LLM API keys in ${chalk.yellow('.env')}`);
|
|
207
|
+
if (dockerAvailable) {
|
|
208
|
+
this.log(`3. Start your local environment:\n ${chalk.cyan('docker compose up -d')}`);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
this.log(`3. Launch Docker Desktop and run:\n ${chalk.cyan('docker compose up -d')}`);
|
|
212
|
+
}
|
|
213
|
+
this.printCodingAgentInstructions();
|
|
214
|
+
}
|
|
215
|
+
printCodingAgentInstructions() {
|
|
216
|
+
this.log(chalk.bold.green('\n🤖 Ready to Build with your Coding Agent:'));
|
|
217
|
+
this.log('Open your coding agent (Claude Code, Antigravity, Cursor, etc.) in this folder and simply say:');
|
|
218
|
+
this.log(chalk.italic.white(' "Build me an agent that scans for overdue invoices and prepares reminder emails."'));
|
|
219
|
+
this.log(chalk.gray('Your coding agent will read AGENTS.md, author the declarative metadata, and test it against your local database!\n'));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/agent/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,10 +3,12 @@ export default class Doctor extends Command {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
-
dir: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
dir: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
scope: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
format: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
|
+
verbose: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
|
+
report: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
11
|
+
report_extended: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
12
|
};
|
|
11
13
|
run(): Promise<void>;
|
|
12
14
|
private formatStatusIcon;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAY7C,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,OAAO;IACzC,MAAM,CAAC,WAAW,SAA4C;IAE9D,MAAM,CAAC,QAAQ,WAQb;IAEF,MAAM,CAAC,KAAK;;;;;;;MAqBV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAuF1B,OAAO,CAAC,gBAAgB;CASzB"}
|
|
@@ -2,11 +2,15 @@ import { Command, Flags } from '@oclif/core';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { InstallerEngine, } from '@memberjunction/installer';
|
|
5
|
+
import { ResolveOutputFormat } from '@memberjunction/cli-core';
|
|
6
|
+
import { CANONICAL_FORMAT_FLAG } from '../../lib/format-compat.js';
|
|
5
7
|
export default class Doctor extends Command {
|
|
6
8
|
static { this.description = 'Diagnose a MemberJunction installation'; }
|
|
7
9
|
static { this.examples = [
|
|
8
10
|
'<%= config.bin %> <%= command.id %>',
|
|
9
11
|
'<%= config.bin %> <%= command.id %> --dir ./my-mj-project',
|
|
12
|
+
'<%= config.bin %> <%= command.id %> --format json',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> --scope ai',
|
|
10
14
|
'<%= config.bin %> <%= command.id %> --verbose',
|
|
11
15
|
'<%= config.bin %> <%= command.id %> --report',
|
|
12
16
|
'<%= config.bin %> <%= command.id %> --report_extended',
|
|
@@ -16,6 +20,11 @@ export default class Doctor extends Command {
|
|
|
16
20
|
description: 'Target directory to diagnose',
|
|
17
21
|
default: '.',
|
|
18
22
|
}),
|
|
23
|
+
scope: Flags.string({
|
|
24
|
+
description: 'Diagnostic scope to filter by (install, runtime, ai, metadata, agent)',
|
|
25
|
+
options: ['install', 'runtime', 'ai', 'metadata', 'agent'],
|
|
26
|
+
}),
|
|
27
|
+
format: CANONICAL_FORMAT_FLAG,
|
|
19
28
|
verbose: Flags.boolean({
|
|
20
29
|
char: 'v',
|
|
21
30
|
description: 'Show detailed output including suggested fixes inline',
|
|
@@ -32,29 +41,42 @@ export default class Doctor extends Command {
|
|
|
32
41
|
const { flags } = await this.parse(Doctor);
|
|
33
42
|
const engine = new InstallerEngine();
|
|
34
43
|
const targetDir = path.resolve(flags.dir);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.log(
|
|
41
|
-
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
const { format } = ResolveOutputFormat({ formatFlag: flags.format });
|
|
45
|
+
const isJson = format === 'json';
|
|
46
|
+
if (!isJson) {
|
|
47
|
+
this.log('');
|
|
48
|
+
this.log(chalk.bold('MJ Doctor'));
|
|
49
|
+
this.log('─────────');
|
|
50
|
+
engine.On('diagnostic', (event) => {
|
|
51
|
+
const icon = this.formatStatusIcon(event.Status);
|
|
52
|
+
this.log(`${icon} ${event.Message}`);
|
|
53
|
+
if (event.SuggestedFix && flags.verbose) {
|
|
54
|
+
this.log(chalk.dim(` Fix: ${event.SuggestedFix}`));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
// Capture log events for report path notification
|
|
58
|
+
engine.On('log', (event) => {
|
|
59
|
+
if (event.Message.startsWith('Diagnostic report saved to:')) {
|
|
60
|
+
this.log('');
|
|
61
|
+
this.log(chalk.cyan(event.Message));
|
|
62
|
+
this.log(chalk.dim('Share this file when requesting installation support. Passwords are redacted.'));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
53
66
|
const result = await engine.Doctor(targetDir, {
|
|
54
67
|
Verbose: flags.verbose,
|
|
55
68
|
Report: flags.report,
|
|
56
69
|
ReportExtended: flags.report_extended,
|
|
70
|
+
Scope: flags.scope,
|
|
71
|
+
Format: format,
|
|
57
72
|
});
|
|
73
|
+
if (isJson) {
|
|
74
|
+
this.log(JSON.stringify(result.toJSON(), null, 2));
|
|
75
|
+
if (result.HasFailures) {
|
|
76
|
+
this.exit(1);
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
58
80
|
this.log('');
|
|
59
81
|
if (result.HasFailures) {
|
|
60
82
|
this.log(chalk.red.bold('Issues found:'));
|
|
@@ -88,6 +110,9 @@ export default class Doctor extends Command {
|
|
|
88
110
|
this.log(chalk.dim('Extended report includes config file snapshots and service startup logs.'));
|
|
89
111
|
}
|
|
90
112
|
}
|
|
113
|
+
if (result.HasFailures) {
|
|
114
|
+
this.exit(1);
|
|
115
|
+
}
|
|
91
116
|
}
|
|
92
117
|
formatStatusIcon(status) {
|
|
93
118
|
switch (status) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,eAAe,GAIhB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class TestScripts extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
test: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
promote: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
discard: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=scripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scripts.d.ts","sourceRoot":"","sources":["../../../src/commands/test/scripts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAE7C,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,MAAM,CAAC,WAAW,SAGkE;IAEpF,MAAM,CAAC,QAAQ,WAMb;IAEF,MAAM,CAAC,KAAK;;;;;MAmBV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAgB3B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
export default class TestScripts extends Command {
|
|
3
|
+
static { this.description = 'Review and promote recorded replay scripts. A run that re-derives a test which already had a ' +
|
|
4
|
+
'script stores the new one as pending rather than replacing what replay uses, so a UI change ' +
|
|
5
|
+
'never rewrites the suite unnoticed. This is where you see the drift and decide.'; }
|
|
6
|
+
static { this.examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %>',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --test "T042 - Filter Grid"',
|
|
9
|
+
'<%= config.bin %> <%= command.id %> --promote --test "T042 - Filter Grid"',
|
|
10
|
+
'<%= config.bin %> <%= command.id %> --promote --yes',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> --discard --test "T042 - Filter Grid"',
|
|
12
|
+
]; }
|
|
13
|
+
static { this.flags = {
|
|
14
|
+
test: Flags.string({
|
|
15
|
+
description: 'Limit to one test, by name or ID',
|
|
16
|
+
}),
|
|
17
|
+
promote: Flags.boolean({
|
|
18
|
+
description: 'Promote the pending script(s) into the slot replay uses',
|
|
19
|
+
default: false,
|
|
20
|
+
exclusive: ['discard'],
|
|
21
|
+
}),
|
|
22
|
+
discard: Flags.boolean({
|
|
23
|
+
description: 'Drop the pending script(s), keeping the promoted one',
|
|
24
|
+
default: false,
|
|
25
|
+
exclusive: ['promote'],
|
|
26
|
+
}),
|
|
27
|
+
yes: Flags.boolean({
|
|
28
|
+
char: 'y',
|
|
29
|
+
description: 'Confirm a bulk --promote/--discard that was not narrowed with --test',
|
|
30
|
+
default: false,
|
|
31
|
+
}),
|
|
32
|
+
}; }
|
|
33
|
+
async run() {
|
|
34
|
+
const { flags } = await this.parse(TestScripts);
|
|
35
|
+
const { ScriptsCommand } = await import('@memberjunction/testing-cli');
|
|
36
|
+
try {
|
|
37
|
+
const scriptsCommand = new ScriptsCommand();
|
|
38
|
+
await scriptsCommand.execute({
|
|
39
|
+
test: flags.test,
|
|
40
|
+
promote: flags.promote,
|
|
41
|
+
discard: flags.discard,
|
|
42
|
+
yes: flags.yes,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
this.error(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=scripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../../src/commands/test/scripts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;aACvC,gBAAW,GAChB,+FAA+F;QAC/F,8FAA8F;QAC9F,iFAAiF,CAAC;aAE7E,aAAQ,GAAG;QAChB,qCAAqC;QACrC,iEAAiE;QACjE,2EAA2E;QAC3E,qDAAqD;QACrD,2EAA2E;KAC5E,CAAC;aAEK,UAAK,GAAG;QACb,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC;YACjB,WAAW,EAAE,kCAAkC;SAChD,CAAC;QACF,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC;YACrB,WAAW,EAAE,yDAAyD;YACtE,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB,CAAC;QACF,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC;YACrB,WAAW,EAAE,sDAAsD;YACnE,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB,CAAC;QACF,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;YACjB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,sEAAsE;YACnF,OAAO,EAAE,KAAK;SACf,CAAC;KACH,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAChD,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;QAEvE,IAAI,CAAC;YACH,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;YAC5C,MAAM,cAAc,CAAC,OAAO,CAAC;gBAC3B,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,GAAG,EAAE,KAAK,CAAC,GAAG;aACf,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,KAAc,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: new-agent
|
|
3
|
+
description: Guides the coding agent to author a new MemberJunction agent and prompt metadata from user business intent.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: `new-agent` (Authoring Declarative Agents)
|
|
7
|
+
|
|
8
|
+
Use this skill whenever the user asks to build, create, or modify a MemberJunction AI Agent.
|
|
9
|
+
|
|
10
|
+
## Step 1: Elicit & Scope Business Intent
|
|
11
|
+
Ask clarifying questions if the requirement is ambiguous:
|
|
12
|
+
1. **Goal**: What specific business problem does the agent solve?
|
|
13
|
+
2. **Inputs & Subjects**: What input does the user provide (e.g. an account ID, order number, or topic)?
|
|
14
|
+
3. **Data Sources**: Which entities does it read or check? (Cross-reference against `CAPABILITIES.md`).
|
|
15
|
+
4. **Output / Action**: Does it produce an answer, a formatted summary, a notification, or trigger a downstream action?
|
|
16
|
+
|
|
17
|
+
## Step 2: Determine Tier & Agent Type
|
|
18
|
+
- **Tier 1 (Preferred)**:
|
|
19
|
+
- If the agent follows a predictable sequence of steps, use **`TypeID: "@lookup:MJ: AI Agent Types.Name=Flow"`**.
|
|
20
|
+
- If the agent needs dynamic problem solving or open-ended tool calling, use **`TypeID: "@lookup:MJ: AI Agent Types.Name=Loop"`**.
|
|
21
|
+
- Always keep **`ModelSelectionMode: "Agent Type"`** and **`ExposeAsAction: false`**.
|
|
22
|
+
- **Tier 2 (Only if custom code is strictly necessary)**:
|
|
23
|
+
- If custom JavaScript formatting or computation is required that no existing action supports, create a sandboxed Runtime Action.
|
|
24
|
+
- Define an explicit `allowedEntities` list (never wildcards).
|
|
25
|
+
|
|
26
|
+
## Step 3: Author Prompt Template & Metadata
|
|
27
|
+
1. **Markdown Prompt Template**:
|
|
28
|
+
Save to: `metadata/prompts/templates/<agent-slug>.template.md`
|
|
29
|
+
Structure:
|
|
30
|
+
- Clear persona and objective.
|
|
31
|
+
- Exact instructions and step sequence.
|
|
32
|
+
- Business rules and edge-case handling.
|
|
33
|
+
- Explicit Markdown output formatting guidelines.
|
|
34
|
+
|
|
35
|
+
2. **Prompt Metadata JSON**:
|
|
36
|
+
Save to: `metadata/prompts/.<agent-slug>-prompt.json`
|
|
37
|
+
```json
|
|
38
|
+
[
|
|
39
|
+
{
|
|
40
|
+
"fields": {
|
|
41
|
+
"Name": "<Agent Name> Prompt",
|
|
42
|
+
"Description": "...",
|
|
43
|
+
"CategoryID": "@lookup:MJ: AI Prompt Categories.Name=Agent Type System Prompts",
|
|
44
|
+
"TypeID": "@lookup:MJ: AI Prompt Types.Name=Chat",
|
|
45
|
+
"Status": "Active",
|
|
46
|
+
"TemplateText": "@file:templates/<agent-slug>.template.md"
|
|
47
|
+
},
|
|
48
|
+
"primaryKey": { "ID": "<GENERATE-UUID-UPPERCASE>" }
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
3. **Agent Metadata JSON**:
|
|
54
|
+
Save to: `metadata/agents/.<agent-slug>-agent.json`
|
|
55
|
+
Wire `MJ: AI Agent Prompts`, `MJ: AI Agent Steps`, and optional `MJ: AI Agent Actions`.
|
|
56
|
+
|
|
57
|
+
## Step 4: Sync to Database
|
|
58
|
+
Run:
|
|
59
|
+
```bash
|
|
60
|
+
./scripts/sync-metadata.sh
|
|
61
|
+
```
|
|
62
|
+
Verify that the sync command outputs success.
|
|
63
|
+
|
|
64
|
+
## Step 5: Hand off to Testing
|
|
65
|
+
Inform the user that the agent has been created and synced:
|
|
66
|
+
*"I've created your agent '**<Agent Name>**' and synced it to your local environment. You can test it now in Explorer or ask me to run a test execution."*
|