@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.
@@ -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', 'revenuecat'],
55
- objective: 'Only investigate critical production blockers and business anomalies: Sentry/GlitchTip production errors, crashes, very low users, conversion, purchases, or other urgent drops.',
56
- instructions: 'Do exact root-cause analysis with connected production data, memory/state, release context, and recent code changes. Produce the fix or next debugging step; avoid generic growth ideas.',
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 conversion, traffic, and RevenueCat review',
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: 'Analyze total conversion, traffic quality, activation, retention, RevenueCat trials/subscriptions/revenue/churn, source mix, reviews, releases, and stability.',
66
- instructions: 'Pick one to three high-confidence growth bets with evidence, expected KPI movement, likely code/store surfaces, and verification plan.',
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 product review',
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, and crash totals month-over-month.',
76
- instructions: 'Decide what should be built, changed, or deleted next and explain why it should move revenue, activation, retention, or acquisition quality.',
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) Full setup: project, schedule, outputs, and sources\n');
2969
- process.stdout.write(' 2) Connectors: credentials and provider health checks\n');
2970
- process.stdout.write(' 3) Intervals: growth cadence and connector health check interval\n');
2971
- process.stdout.write(' 4) Output: summary, GitHub issues, draft PRs, and notifications\n');
2972
- const answer = await ask(rl, 'Setup area (1/2/3/4)', '1');
2973
- if (answer.trim() === '2')
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() === '3')
2976
- return 'intervals';
2977
+ if (answer.trim() === '2')
2978
+ return 'outputs_intervals';
2977
2979
  if (answer.trim() === '4')
2978
- return 'output';
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: false,
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: 1440,
3023
- connectorHealthCheckIntervalMinutes: 720,
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) Summary only: OpenClaw chat handoff and notifications\n');
3153
- process.stdout.write(' 2) GitHub issue drafts: generate issue-ready handoffs, no auto-create by default\n');
3154
- process.stdout.write(' 3) GitHub pull request drafts: generate PR-oriented proposal branches when enabled\n');
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 || 1440)), 10) || 1440;
3244
- const connectorHealthCheckIntervalMinutes = Number.parseInt(await ask(rl, 'Connector health check interval in minutes', String(currentSchedule.connectorHealthCheckIntervalMinutes || 720)), 10) || 720;
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 || 1440));
3265
- const connectorHealthCheckIntervalMinutes = Math.max(1, Number(config?.schedule?.connectorHealthCheckIntervalMinutes || 720));
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 === 'output') {
3340
- const config = await askOutputConfig(rl, await loadEditableConfig(configPath));
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('Connector-health alerts are deduped per unhealthy incident and sent through configured channels.\n');
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, 'Check interval in minutes', '1440'), 10) || 1440;
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: 720,
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,