@analyticscli/growth-engineer 0.1.0-preview.0 → 0.1.0-preview.2
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/dist/config.js +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/openclaw-growth-runner.mjs +20 -20
- package/dist/runtime/openclaw-growth-runner.mjs.map +1 -1
- package/dist/runtime/openclaw-growth-start.mjs +1 -1
- package/dist/runtime/openclaw-growth-wizard.mjs +62 -45
- package/dist/runtime/openclaw-growth-wizard.mjs.map +1 -1
- package/package.json +2 -2
- package/templates/config.example.json +20 -20
|
@@ -11,6 +11,8 @@ import { loadOpenClawGrowthSecrets } from './openclaw-growth-env.mjs';
|
|
|
11
11
|
const DEFAULT_CONFIG_PATH = 'data/openclaw-growth-engineer/config.json';
|
|
12
12
|
const SELF_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
13
13
|
const ENABLE_ISOLATED_SECRET_RUNNER_WIZARD = false;
|
|
14
|
+
const DEFAULT_GROWTH_INTERVAL_MINUTES = 1440;
|
|
15
|
+
const DEFAULT_CONNECTOR_HEALTH_INTERVAL_MINUTES = 360;
|
|
14
16
|
const CONNECTOR_KEYS = ['analytics', 'github', 'revenuecat', 'sentry', 'asc'];
|
|
15
17
|
const CONNECTOR_DEFINITIONS = [
|
|
16
18
|
{
|
|
@@ -47,33 +49,33 @@ const CONNECTOR_DEFINITIONS = [
|
|
|
47
49
|
const DEFAULT_CADENCE_PLAN = [
|
|
48
50
|
{
|
|
49
51
|
key: 'daily',
|
|
50
|
-
title: 'Daily production guardrail',
|
|
52
|
+
title: 'Daily Sentry and production guardrail',
|
|
51
53
|
intervalDays: 1,
|
|
52
54
|
criticalOnly: true,
|
|
53
|
-
focusAreas: ['crash', 'conversion', 'paywall'],
|
|
54
|
-
sourcePriorities: ['sentry', 'glitchtip', 'analytics', 'asc_cli', '
|
|
55
|
-
objective: '
|
|
56
|
-
instructions: '
|
|
55
|
+
focusAreas: ['sentry_errors', 'crash', 'onboarding', 'conversion', 'paywall', 'purchase'],
|
|
56
|
+
sourcePriorities: ['sentry', 'glitchtip', 'analytics', 'revenuecat', 'asc_cli', 'feedback', 'github'],
|
|
57
|
+
objective: 'Analyze every configured project for critical production blockers: Sentry/GlitchTip errors, crashes, onboarding or purchase drop-offs, zero-conversion days, missing buyers, very low users, and other silent business anomalies.',
|
|
58
|
+
instructions: 'Compare against recent baselines across connected sources and code changes. If the finding is critical, produce the exact fix or next debugging step and prefer a GitHub issue or draft PR when GitHub write access is configured; otherwise hand off via OpenClaw chat. Avoid generic growth ideas.',
|
|
57
59
|
},
|
|
58
60
|
{
|
|
59
61
|
key: 'weekly',
|
|
60
|
-
title: 'Weekly
|
|
62
|
+
title: 'Weekly executive product and growth summary',
|
|
61
63
|
intervalDays: 7,
|
|
62
64
|
criticalOnly: false,
|
|
63
|
-
focusAreas: ['conversion', 'paywall', 'onboarding', 'marketing', 'retention'],
|
|
64
|
-
sourcePriorities: ['analytics', 'revenuecat', 'asc_cli', 'feedback', 'sentry'],
|
|
65
|
-
objective: '
|
|
66
|
-
instructions: 'Pick one to three high-confidence
|
|
65
|
+
focusAreas: ['conversion', 'paywall', 'onboarding', 'marketing', 'retention', 'stability'],
|
|
66
|
+
sourcePriorities: ['analytics', 'revenuecat', 'asc_cli', 'feedback', 'sentry', 'github'],
|
|
67
|
+
objective: 'Create an executive summary across all configured projects, connectors, recent releases, code changes, revenue, activation, retention, reviews, and production stability.',
|
|
68
|
+
instructions: 'Pick one to three high-confidence improvements with evidence, expected KPI movement, likely code/store surfaces, owner-ready next steps, and a verification plan. Create GitHub issues or draft PR proposals only when the evidence is specific enough.',
|
|
67
69
|
},
|
|
68
70
|
{
|
|
69
71
|
key: 'monthly',
|
|
70
|
-
title: 'Monthly business and
|
|
72
|
+
title: 'Monthly deep product, business, and code review',
|
|
71
73
|
intervalDays: 30,
|
|
72
74
|
criticalOnly: false,
|
|
73
|
-
focusAreas: ['conversion', 'paywall', 'retention', 'marketing', 'onboarding'],
|
|
74
|
-
sourcePriorities: ['analytics', 'revenuecat', 'asc_cli', 'feedback', 'sentry'],
|
|
75
|
-
objective: 'Compare MRR, trial conversion, churn, acquisition quality, store conversion, retention, review themes, feature usage,
|
|
76
|
-
instructions: 'Decide what should be built, changed, or
|
|
75
|
+
focusAreas: ['conversion', 'paywall', 'retention', 'marketing', 'onboarding', 'codebase'],
|
|
76
|
+
sourcePriorities: ['analytics', 'revenuecat', 'asc_cli', 'feedback', 'sentry', 'github'],
|
|
77
|
+
objective: 'Compare all configured projects month-over-month: MRR, trial conversion, churn, acquisition quality, store conversion, retention, review themes, feature usage, crash totals, and codebase changes.',
|
|
78
|
+
instructions: 'Decide what should be built, changed, deleted, or instrumented next. Tie conclusions to connector data plus codebase evidence and explain why each recommendation should move revenue, activation, retention, stability, or acquisition quality.',
|
|
77
79
|
},
|
|
78
80
|
{
|
|
79
81
|
key: 'quarterly',
|
|
@@ -81,8 +83,8 @@ const DEFAULT_CADENCE_PLAN = [
|
|
|
81
83
|
intervalDays: 91,
|
|
82
84
|
criticalOnly: false,
|
|
83
85
|
focusAreas: ['marketing', 'paywall', 'retention', 'conversion', 'onboarding'],
|
|
84
|
-
sourcePriorities: ['analytics', 'revenuecat', 'asc_cli', 'feedback'],
|
|
85
|
-
objective: 'Revisit positioning, pricing/packaging, onboarding architecture, roadmap assumptions, tracking quality, and major funnel bets.',
|
|
86
|
+
sourcePriorities: ['analytics', 'revenuecat', 'asc_cli', 'feedback', 'github', 'sentry'],
|
|
87
|
+
objective: 'Revisit positioning, pricing/packaging, onboarding architecture, roadmap assumptions, tracking quality, codebase constraints, and major funnel bets across every configured project.',
|
|
86
88
|
instructions: 'Find structural constraints and durable opportunities. Tie recommendations to cohort behavior, monetization, reviews, channel quality, and shipped changes.',
|
|
87
89
|
},
|
|
88
90
|
{
|
|
@@ -92,7 +94,7 @@ const DEFAULT_CADENCE_PLAN = [
|
|
|
92
94
|
criticalOnly: false,
|
|
93
95
|
focusAreas: ['retention', 'conversion', 'paywall', 'marketing', 'general'],
|
|
94
96
|
sourcePriorities: ['analytics', 'revenuecat', 'asc_cli', 'feedback', 'sentry'],
|
|
95
|
-
objective: 'Audit connector coverage, SDK instrumentation, event taxonomy, data reliability, memory, growth loops, and whether strategy still matches the best users.',
|
|
97
|
+
objective: 'Audit connector coverage, SDK instrumentation, event taxonomy, data reliability, memory, growth loops, and whether product/code strategy still matches the best users across configured projects.',
|
|
96
98
|
instructions: 'Prioritize measurement fixes and system changes that make future analysis more trustworthy. Identify stale events, missing attribution, weak identity, and misleading dashboards.',
|
|
97
99
|
},
|
|
98
100
|
{
|
|
@@ -102,7 +104,7 @@ const DEFAULT_CADENCE_PLAN = [
|
|
|
102
104
|
criticalOnly: false,
|
|
103
105
|
focusAreas: ['marketing', 'retention', 'paywall', 'conversion', 'general'],
|
|
104
106
|
sourcePriorities: ['analytics', 'revenuecat', 'asc_cli', 'feedback', 'sentry'],
|
|
105
|
-
objective: 'Reset strategy from evidence: market/channel fit, monetization model, retention ceiling, product scope, and whether to double down, reposition, rebuild, or sunset major surfaces/features.',
|
|
107
|
+
objective: 'Reset strategy from evidence across every configured project: market/channel fit, monetization model, retention ceiling, product scope, and whether to double down, reposition, rebuild, or sunset major surfaces/features.',
|
|
106
108
|
instructions: 'Use the full year of memory, releases, revenue, acquisition, reviews, code changes, and cohort behavior. Produce strategic experiments and stop-doing decisions.',
|
|
107
109
|
},
|
|
108
110
|
];
|
|
@@ -2965,17 +2967,17 @@ async function askCadencePlan(rl) {
|
|
|
2965
2967
|
}
|
|
2966
2968
|
async function askWizardGoal(rl) {
|
|
2967
2969
|
process.stdout.write('\nWhat do you want to configure?\n');
|
|
2968
|
-
process.stdout.write(' 1)
|
|
2969
|
-
process.stdout.write(' 2)
|
|
2970
|
-
process.stdout.write(' 3)
|
|
2971
|
-
process.stdout.write(' 4)
|
|
2972
|
-
const answer = await ask(rl, 'Setup area (1/2/3/4)', '
|
|
2973
|
-
if (answer.trim() === '
|
|
2970
|
+
process.stdout.write(' 1) Connectors: credentials, provider setup, and health checks\n');
|
|
2971
|
+
process.stdout.write(' 2) Outputs and intervals: daily/weekly/monthly jobs, GitHub issue/PR delivery, OpenClaw chat notifications\n');
|
|
2972
|
+
process.stdout.write(' 3) Full setup: project, connectors, outputs, intervals, and sources\n');
|
|
2973
|
+
process.stdout.write(' 4) Advanced intervals only: runner wake-up and connector health check cadence\n');
|
|
2974
|
+
const answer = await ask(rl, 'Setup area (1/2/3/4)', '3');
|
|
2975
|
+
if (answer.trim() === '1')
|
|
2974
2976
|
return 'connectors';
|
|
2975
|
-
if (answer.trim() === '
|
|
2976
|
-
return '
|
|
2977
|
+
if (answer.trim() === '2')
|
|
2978
|
+
return 'outputs_intervals';
|
|
2977
2979
|
if (answer.trim() === '4')
|
|
2978
|
-
return '
|
|
2980
|
+
return 'intervals';
|
|
2979
2981
|
return 'full';
|
|
2980
2982
|
}
|
|
2981
2983
|
async function buildDefaultWizardConfig() {
|
|
@@ -3003,7 +3005,7 @@ async function buildDefaultWizardConfig() {
|
|
|
3003
3005
|
command: getDefaultSourceCommand('revenuecat'),
|
|
3004
3006
|
},
|
|
3005
3007
|
sentry: {
|
|
3006
|
-
enabled:
|
|
3008
|
+
enabled: true,
|
|
3007
3009
|
mode: 'command',
|
|
3008
3010
|
command: getDefaultSourceCommand('sentry'),
|
|
3009
3011
|
},
|
|
@@ -3019,8 +3021,8 @@ async function buildDefaultWizardConfig() {
|
|
|
3019
3021
|
],
|
|
3020
3022
|
},
|
|
3021
3023
|
schedule: {
|
|
3022
|
-
intervalMinutes:
|
|
3023
|
-
connectorHealthCheckIntervalMinutes:
|
|
3024
|
+
intervalMinutes: DEFAULT_GROWTH_INTERVAL_MINUTES,
|
|
3025
|
+
connectorHealthCheckIntervalMinutes: DEFAULT_CONNECTOR_HEALTH_INTERVAL_MINUTES,
|
|
3024
3026
|
skipIfNoDataChange: true,
|
|
3025
3027
|
skipIfIssueSetUnchanged: true,
|
|
3026
3028
|
cadences: DEFAULT_CADENCE_PLAN.map((cadence) => ({ ...cadence })),
|
|
@@ -3028,6 +3030,8 @@ async function buildDefaultWizardConfig() {
|
|
|
3028
3030
|
actions: {
|
|
3029
3031
|
autoCreateIssues: false,
|
|
3030
3032
|
autoCreatePullRequests: false,
|
|
3033
|
+
autoCreateWhenGitHubWriteAccess: true,
|
|
3034
|
+
disableAutoCreateGitHubArtifacts: false,
|
|
3031
3035
|
mode: 'issue',
|
|
3032
3036
|
usageMode: 'production_autopilot',
|
|
3033
3037
|
draftPullRequests: true,
|
|
@@ -3149,9 +3153,9 @@ async function askNotificationChannels(rl, config) {
|
|
|
3149
3153
|
}
|
|
3150
3154
|
async function askOutputConfig(rl, config) {
|
|
3151
3155
|
process.stdout.write('\nOutput type\n');
|
|
3152
|
-
process.stdout.write(' 1)
|
|
3153
|
-
process.stdout.write(' 2) GitHub
|
|
3154
|
-
process.stdout.write(' 3) GitHub pull
|
|
3156
|
+
process.stdout.write(' 1) OpenClaw chat plus automatic GitHub issue fallback when repo + token allow it\n');
|
|
3157
|
+
process.stdout.write(' 2) GitHub issues: create issues automatically when new findings are found\n');
|
|
3158
|
+
process.stdout.write(' 3) GitHub pull requests: create draft PR-oriented proposal branches when enabled\n');
|
|
3155
3159
|
const currentMode = config?.actions?.mode || config?.deliveries?.github?.mode || 'issue';
|
|
3156
3160
|
const currentAutoCreate = Boolean(config?.actions?.autoCreateIssues || config?.actions?.autoCreatePullRequests || config?.deliveries?.github?.autoCreate);
|
|
3157
3161
|
const defaultChoice = currentAutoCreate ? (currentMode === 'pull_request' ? '3' : '2') : '1';
|
|
@@ -3186,6 +3190,8 @@ async function askOutputConfig(rl, config) {
|
|
|
3186
3190
|
mode,
|
|
3187
3191
|
autoCreateIssues: mode === 'issue' && autoCreate,
|
|
3188
3192
|
autoCreatePullRequests: mode === 'pull_request' && autoCreate,
|
|
3193
|
+
autoCreateWhenGitHubWriteAccess: config.actions?.autoCreateWhenGitHubWriteAccess !== false,
|
|
3194
|
+
disableAutoCreateGitHubArtifacts: config.actions?.disableAutoCreateGitHubArtifacts === true,
|
|
3189
3195
|
draftPullRequests: true,
|
|
3190
3196
|
proposalBranchPrefix: config?.actions?.proposalBranchPrefix || 'openclaw/proposals',
|
|
3191
3197
|
};
|
|
@@ -3240,8 +3246,8 @@ async function askOutputConfig(rl, config) {
|
|
|
3240
3246
|
}
|
|
3241
3247
|
async function askIntervalConfig(rl, config) {
|
|
3242
3248
|
const currentSchedule = config?.schedule || {};
|
|
3243
|
-
const intervalMinutes = Number.parseInt(await ask(rl, 'Growth runner wake-up interval in minutes', String(currentSchedule.intervalMinutes ||
|
|
3244
|
-
const connectorHealthCheckIntervalMinutes = Number.parseInt(await ask(rl, 'Connector health check interval in minutes', String(currentSchedule.connectorHealthCheckIntervalMinutes ||
|
|
3249
|
+
const intervalMinutes = Number.parseInt(await ask(rl, 'Growth runner wake-up interval in minutes', String(currentSchedule.intervalMinutes || DEFAULT_GROWTH_INTERVAL_MINUTES)), 10) || DEFAULT_GROWTH_INTERVAL_MINUTES;
|
|
3250
|
+
const connectorHealthCheckIntervalMinutes = Number.parseInt(await ask(rl, 'Connector health check interval in minutes', String(currentSchedule.connectorHealthCheckIntervalMinutes || DEFAULT_CONNECTOR_HEALTH_INTERVAL_MINUTES)), 10) || DEFAULT_CONNECTOR_HEALTH_INTERVAL_MINUTES;
|
|
3245
3251
|
const usageMode = await askToolUsage(rl);
|
|
3246
3252
|
const cadences = await askCadencePlan(rl);
|
|
3247
3253
|
config.schedule = {
|
|
@@ -3258,11 +3264,15 @@ async function askIntervalConfig(rl, config) {
|
|
|
3258
3264
|
};
|
|
3259
3265
|
return config;
|
|
3260
3266
|
}
|
|
3267
|
+
async function askOutputsAndIntervalsConfig(rl, config) {
|
|
3268
|
+
const withIntervals = await askIntervalConfig(rl, config);
|
|
3269
|
+
return await askOutputConfig(rl, withIntervals);
|
|
3270
|
+
}
|
|
3261
3271
|
async function writeOpenClawJobManifest(configPath, config) {
|
|
3262
3272
|
const manifestPath = path.resolve('.openclaw/jobs/openclaw-growth-engineer.json');
|
|
3263
3273
|
const displayConfigPath = path.relative(process.cwd(), configPath) || configPath;
|
|
3264
|
-
const intervalMinutes = Math.max(1, Number(config?.schedule?.intervalMinutes ||
|
|
3265
|
-
const connectorHealthCheckIntervalMinutes = Math.max(1, Number(config?.schedule?.connectorHealthCheckIntervalMinutes ||
|
|
3274
|
+
const intervalMinutes = Math.max(1, Number(config?.schedule?.intervalMinutes || DEFAULT_GROWTH_INTERVAL_MINUTES));
|
|
3275
|
+
const connectorHealthCheckIntervalMinutes = Math.max(1, Number(config?.schedule?.connectorHealthCheckIntervalMinutes || DEFAULT_CONNECTOR_HEALTH_INTERVAL_MINUTES));
|
|
3266
3276
|
const actionMode = config?.actions?.mode || config?.deliveries?.github?.mode || 'issue';
|
|
3267
3277
|
const growthRunCommand = getGrowthRunCommand(config, displayConfigPath);
|
|
3268
3278
|
const connectorHealthCommand = getConnectorHealthCommand(config, displayConfigPath);
|
|
@@ -3336,15 +3346,15 @@ async function main() {
|
|
|
3336
3346
|
process.stdout.write('OpenClaw can run and update growth jobs plus non-secret connector config from the manifest; connector API keys stay behind the connector wizard.\n');
|
|
3337
3347
|
return;
|
|
3338
3348
|
}
|
|
3339
|
-
if (goal === '
|
|
3340
|
-
const config = await
|
|
3349
|
+
if (goal === 'outputs_intervals') {
|
|
3350
|
+
const config = await askOutputsAndIntervalsConfig(rl, await loadEditableConfig(configPath));
|
|
3341
3351
|
const secretAccess = await askSecretAccessModel(rl, configPath, config);
|
|
3342
3352
|
await writeJsonFile(configPath, config);
|
|
3343
3353
|
const manifestPath = await writeOpenClawJobManifest(configPath, config);
|
|
3344
|
-
process.stdout.write(`\nSaved output config: ${configPath}\n`);
|
|
3354
|
+
process.stdout.write(`\nSaved output and interval config: ${configPath}\n`);
|
|
3345
3355
|
process.stdout.write(`Saved OpenClaw job manifest: ${manifestPath}\n`);
|
|
3346
3356
|
printSecretRunnerKitInstructions(secretAccess.kit);
|
|
3347
|
-
process.stdout.write('
|
|
3357
|
+
process.stdout.write('Daily checks prioritize Sentry and production anomalies; larger cadences analyze all configured projects and connectors.\n');
|
|
3348
3358
|
return;
|
|
3349
3359
|
}
|
|
3350
3360
|
const detectedRepo = await detectGitHubRepo();
|
|
@@ -3355,7 +3365,9 @@ async function main() {
|
|
|
3355
3365
|
.map((value) => value.trim())
|
|
3356
3366
|
.filter(Boolean);
|
|
3357
3367
|
const maxIssues = Number.parseInt(await ask(rl, 'Max issues per run', '4'), 10) || 4;
|
|
3358
|
-
const intervalMinutes = Number.parseInt(await ask(rl, '
|
|
3368
|
+
const intervalMinutes = Number.parseInt(await ask(rl, 'Growth runner wake-up interval in minutes', String(DEFAULT_GROWTH_INTERVAL_MINUTES)), 10) ||
|
|
3369
|
+
DEFAULT_GROWTH_INTERVAL_MINUTES;
|
|
3370
|
+
const connectorHealthCheckIntervalMinutes = Number.parseInt(await ask(rl, 'Connector health check interval in minutes', String(DEFAULT_CONNECTOR_HEALTH_INTERVAL_MINUTES)), 10) || DEFAULT_CONNECTOR_HEALTH_INTERVAL_MINUTES;
|
|
3359
3371
|
const usageMode = await askToolUsage(rl);
|
|
3360
3372
|
const cadences = await askCadencePlan(rl);
|
|
3361
3373
|
const actionMode = await askChoice(rl, 'Preferred GitHub artifact mode', ['issue', 'pull_request'], 'issue');
|
|
@@ -3364,7 +3376,10 @@ async function main() {
|
|
|
3364
3376
|
defaultCommand: getDefaultSourceCommand('analytics'),
|
|
3365
3377
|
});
|
|
3366
3378
|
const revenuecat = await askSourceConfig(rl, 'revenuecat', 'data/openclaw-growth-engineer/revenuecat_summary.example.json', getDefaultSourceHint('revenuecat'));
|
|
3367
|
-
const sentry = await askSourceConfig(rl, 'sentry', 'data/openclaw-growth-engineer/sentry_summary.example.json', getDefaultSourceHint('sentry')
|
|
3379
|
+
const sentry = await askSourceConfig(rl, 'sentry', 'data/openclaw-growth-engineer/sentry_summary.example.json', getDefaultSourceHint('sentry'), {
|
|
3380
|
+
defaultEnabled: true,
|
|
3381
|
+
defaultCommand: getDefaultSourceCommand('sentry'),
|
|
3382
|
+
});
|
|
3368
3383
|
const feedback = await askSourceConfig(rl, 'feedback', 'data/openclaw-growth-engineer/feedback_summary.example.json', getDefaultSourceHint('feedback'), {
|
|
3369
3384
|
defaultEnabled: true,
|
|
3370
3385
|
defaultCommand: getDefaultSourceCommand('feedback'),
|
|
@@ -3410,7 +3425,7 @@ async function main() {
|
|
|
3410
3425
|
},
|
|
3411
3426
|
schedule: {
|
|
3412
3427
|
intervalMinutes,
|
|
3413
|
-
connectorHealthCheckIntervalMinutes
|
|
3428
|
+
connectorHealthCheckIntervalMinutes,
|
|
3414
3429
|
skipIfNoDataChange: true,
|
|
3415
3430
|
skipIfIssueSetUnchanged: true,
|
|
3416
3431
|
cadences,
|
|
@@ -3418,6 +3433,8 @@ async function main() {
|
|
|
3418
3433
|
actions: {
|
|
3419
3434
|
autoCreateIssues,
|
|
3420
3435
|
autoCreatePullRequests,
|
|
3436
|
+
autoCreateWhenGitHubWriteAccess: true,
|
|
3437
|
+
disableAutoCreateGitHubArtifacts: false,
|
|
3421
3438
|
mode: actionMode,
|
|
3422
3439
|
usageMode,
|
|
3423
3440
|
draftPullRequests: true,
|