popilot 0.5.0 → 0.7.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.
Files changed (171) hide show
  1. package/adapters/codex/.codex/commands/_domain.md.hbs +33 -0
  2. package/adapters/codex/.codex/commands/analytics.md.hbs +55 -0
  3. package/adapters/codex/.codex/commands/daily.md.hbs +301 -0
  4. package/adapters/codex/.codex/commands/dev.md.hbs +62 -0
  5. package/adapters/codex/.codex/commands/gtm.md +82 -0
  6. package/adapters/codex/.codex/commands/handoff.md +259 -0
  7. package/adapters/codex/.codex/commands/market.md +120 -0
  8. package/adapters/codex/.codex/commands/metrics.md +123 -0
  9. package/adapters/codex/.codex/commands/oscar-loop.md +436 -0
  10. package/adapters/codex/.codex/commands/party.md +85 -0
  11. package/adapters/codex/.codex/commands/plan.md +43 -0
  12. package/adapters/codex/.codex/commands/research.md +203 -0
  13. package/adapters/codex/.codex/commands/retro.md +68 -0
  14. package/adapters/codex/.codex/commands/save.md +440 -0
  15. package/adapters/codex/.codex/commands/sessions.md +139 -0
  16. package/adapters/codex/.codex/commands/sprint.md +106 -0
  17. package/adapters/codex/.codex/commands/start.md +396 -0
  18. package/adapters/codex/.codex/commands/strategy.md +41 -0
  19. package/adapters/codex/.codex/commands/task.md +220 -0
  20. package/adapters/codex/.codex/commands/tracking.md +116 -0
  21. package/adapters/codex/.codex/commands/validate.md +58 -0
  22. package/adapters/codex/AGENTS.md.hbs +210 -0
  23. package/adapters/codex/manifest.yaml +36 -0
  24. package/adapters/gemini/.gemini/commands/_domain.md.hbs +33 -0
  25. package/adapters/gemini/.gemini/commands/analytics.md.hbs +55 -0
  26. package/adapters/gemini/.gemini/commands/daily.md.hbs +301 -0
  27. package/adapters/gemini/.gemini/commands/dev.md.hbs +62 -0
  28. package/adapters/gemini/.gemini/commands/gtm.md +82 -0
  29. package/adapters/gemini/.gemini/commands/handoff.md +259 -0
  30. package/adapters/gemini/.gemini/commands/market.md +120 -0
  31. package/adapters/gemini/.gemini/commands/metrics.md +123 -0
  32. package/adapters/gemini/.gemini/commands/oscar-loop.md +436 -0
  33. package/adapters/gemini/.gemini/commands/party.md +85 -0
  34. package/adapters/gemini/.gemini/commands/plan.md +43 -0
  35. package/adapters/gemini/.gemini/commands/research.md +203 -0
  36. package/adapters/gemini/.gemini/commands/retro.md +68 -0
  37. package/adapters/gemini/.gemini/commands/save.md +440 -0
  38. package/adapters/gemini/.gemini/commands/sessions.md +139 -0
  39. package/adapters/gemini/.gemini/commands/sprint.md +106 -0
  40. package/adapters/gemini/.gemini/commands/start.md +396 -0
  41. package/adapters/gemini/.gemini/commands/strategy.md +41 -0
  42. package/adapters/gemini/.gemini/commands/task.md +220 -0
  43. package/adapters/gemini/.gemini/commands/tracking.md +116 -0
  44. package/adapters/gemini/.gemini/commands/validate.md +58 -0
  45. package/adapters/gemini/GEMINI.md.hbs +210 -0
  46. package/adapters/gemini/manifest.yaml +36 -0
  47. package/bin/cli.mjs +215 -4
  48. package/lib/doctor.mjs +38 -1
  49. package/lib/hydrate.mjs +15 -0
  50. package/lib/industry-presets.mjs +135 -0
  51. package/lib/scaffold.mjs +5 -0
  52. package/lib/setup-wizard.mjs +71 -2
  53. package/package.json +1 -1
  54. package/scaffold/.context/agents/TEMPLATE.md +14 -0
  55. package/scaffold/.context/agents/analyst.md.hbs +3 -3
  56. package/scaffold/.context/agents/developer.md.hbs +5 -5
  57. package/scaffold/.context/agents/gtm-strategist.md.hbs +3 -3
  58. package/scaffold/.context/agents/handoff-specialist.md.hbs +18 -18
  59. package/scaffold/.context/agents/market-researcher.md.hbs +6 -6
  60. package/scaffold/.context/agents/orchestrator.md.hbs +8 -8
  61. package/scaffold/.context/agents/planner.md.hbs +6 -6
  62. package/scaffold/.context/agents/qa.md.hbs +5 -5
  63. package/scaffold/.context/agents/researcher.md.hbs +33 -6
  64. package/scaffold/.context/agents/strategist.md.hbs +8 -8
  65. package/scaffold/.context/agents/tracking-governor.md.hbs +2 -2
  66. package/scaffold/.context/project.yaml.example +25 -0
  67. package/scaffold/mcp-pm/package.json +19 -0
  68. package/scaffold/mcp-pm/src/api-client.ts +69 -0
  69. package/scaffold/mcp-pm/src/index.ts +660 -0
  70. package/scaffold/mcp-pm/tsconfig.json +14 -0
  71. package/scaffold/pm-api/package.json +21 -0
  72. package/scaffold/pm-api/sql/schema-core.sql +331 -0
  73. package/scaffold/pm-api/sql/schema-docs.sql +25 -0
  74. package/scaffold/pm-api/sql/schema-meetings.sql +17 -0
  75. package/scaffold/pm-api/sql/schema-rewards.sql +16 -0
  76. package/scaffold/pm-api/src/auth.ts +28 -0
  77. package/scaffold/pm-api/src/blockchain/adapter.ts +20 -0
  78. package/scaffold/pm-api/src/blockchain/tron.ts +62 -0
  79. package/scaffold/pm-api/src/db/adapter.ts +36 -0
  80. package/scaffold/pm-api/src/db/turso.ts +147 -0
  81. package/scaffold/pm-api/src/index.ts +114 -0
  82. package/scaffold/pm-api/src/mcp-tools/dashboard.ts +40 -0
  83. package/scaffold/pm-api/src/mcp-tools/epic.ts +67 -0
  84. package/scaffold/pm-api/src/mcp-tools/event.ts +89 -0
  85. package/scaffold/pm-api/src/mcp-tools/index.ts +11 -0
  86. package/scaffold/pm-api/src/mcp-tools/initiative.ts +51 -0
  87. package/scaffold/pm-api/src/mcp-tools/memo.ts +164 -0
  88. package/scaffold/pm-api/src/mcp-tools/notification.ts +37 -0
  89. package/scaffold/pm-api/src/mcp-tools/retro.ts +183 -0
  90. package/scaffold/pm-api/src/mcp-tools/sprint.ts +204 -0
  91. package/scaffold/pm-api/src/mcp-tools/standup.ts +136 -0
  92. package/scaffold/pm-api/src/mcp-tools/story.ts +230 -0
  93. package/scaffold/pm-api/src/mcp-tools/task.ts +187 -0
  94. package/scaffold/pm-api/src/mcp-tools/utils.ts +83 -0
  95. package/scaffold/pm-api/src/mcp.ts +871 -0
  96. package/scaffold/pm-api/src/nudge.ts +283 -0
  97. package/scaffold/pm-api/src/routes/auth.ts +32 -0
  98. package/scaffold/pm-api/src/routes/v2-activity.ts +27 -0
  99. package/scaffold/pm-api/src/routes/v2-admin.ts +165 -0
  100. package/scaffold/pm-api/src/routes/v2-dashboard.ts +189 -0
  101. package/scaffold/pm-api/src/routes/v2-docs.ts +34 -0
  102. package/scaffold/pm-api/src/routes/v2-initiatives.ts +118 -0
  103. package/scaffold/pm-api/src/routes/v2-kickoff.ts +265 -0
  104. package/scaffold/pm-api/src/routes/v2-meetings.ts +324 -0
  105. package/scaffold/pm-api/src/routes/v2-memos.ts +257 -0
  106. package/scaffold/pm-api/src/routes/v2-nav.ts +260 -0
  107. package/scaffold/pm-api/src/routes/v2-notifications.ts +79 -0
  108. package/scaffold/pm-api/src/routes/v2-page-content.ts +35 -0
  109. package/scaffold/pm-api/src/routes/v2-pm.ts +380 -0
  110. package/scaffold/pm-api/src/routes/v2-policy.ts +58 -0
  111. package/scaffold/pm-api/src/routes/v2-retro.ts +221 -0
  112. package/scaffold/pm-api/src/routes/v2-rewards.ts +132 -0
  113. package/scaffold/pm-api/src/routes/v2-scenarios.ts +48 -0
  114. package/scaffold/pm-api/src/routes/v2-search.ts +32 -0
  115. package/scaffold/pm-api/src/routes/v2-standup.ts +127 -0
  116. package/scaffold/pm-api/src/routes/v2-user.ts +38 -0
  117. package/scaffold/pm-api/src/types.ts +11 -0
  118. package/scaffold/pm-api/src/utils/activity.ts +22 -0
  119. package/scaffold/pm-api/src/utils/admin.ts +9 -0
  120. package/scaffold/pm-api/src/utils/agent-notify.ts +62 -0
  121. package/scaffold/pm-api/src/utils/assignee.ts +69 -0
  122. package/scaffold/pm-api/src/utils/db.ts +45 -0
  123. package/scaffold/pm-api/src/utils/initiative.ts +23 -0
  124. package/scaffold/pm-api/src/utils/sprint-lifecycle.ts +96 -0
  125. package/scaffold/pm-api/tsconfig.json +15 -0
  126. package/scaffold/pm-api/wrangler.toml.hbs +11 -0
  127. package/scaffold/spec-site/package-lock.json +40 -0
  128. package/scaffold/spec-site/package.json +4 -1
  129. package/scaffold/spec-site/src/api/types.ts +6 -0
  130. package/scaffold/spec-site/src/components/AppHeader.vue +429 -55
  131. package/scaffold/spec-site/src/components/MemberSelect.vue +48 -0
  132. package/scaffold/spec-site/src/components/NotificationDropdown.vue +116 -0
  133. package/scaffold/spec-site/src/components/SearchModal.vue +102 -0
  134. package/scaffold/spec-site/src/components/VelocityChart.vue +77 -0
  135. package/scaffold/spec-site/src/composables/pmTypes.ts +15 -2
  136. package/scaffold/spec-site/src/composables/useDashboard.ts +221 -0
  137. package/scaffold/spec-site/src/composables/useMediaQuery.ts +28 -0
  138. package/scaffold/spec-site/src/composables/useNotification.ts +200 -0
  139. package/scaffold/spec-site/src/composables/usePmStore.ts +48 -1
  140. package/scaffold/spec-site/src/composables/useRetro.ts +6 -0
  141. package/scaffold/spec-site/src/composables/useStandup.ts +201 -0
  142. package/scaffold/spec-site/src/composables/useTheme.ts +37 -0
  143. package/scaffold/spec-site/src/composables/useUser.ts +19 -1
  144. package/scaffold/spec-site/src/features.ts +108 -0
  145. package/scaffold/spec-site/src/pages/AdminPage.vue +299 -0
  146. package/scaffold/spec-site/src/pages/DashboardPage.vue +650 -0
  147. package/scaffold/spec-site/src/pages/DocsHub.vue +157 -0
  148. package/scaffold/spec-site/src/pages/InboxPage.vue +156 -0
  149. package/scaffold/spec-site/src/pages/MeetingsPage.vue +294 -0
  150. package/scaffold/spec-site/src/pages/MyPage.vue +343 -0
  151. package/scaffold/spec-site/src/pages/RewardsPage.vue +266 -0
  152. package/scaffold/spec-site/src/pages/board/BoardAdmin.vue +422 -0
  153. package/scaffold/spec-site/src/pages/board/BoardEpicSection.vue +54 -0
  154. package/scaffold/spec-site/src/pages/board/BoardPage.vue +884 -0
  155. package/scaffold/spec-site/src/pages/board/BoardStoryCard.vue +67 -0
  156. package/scaffold/spec-site/src/pages/board/BoardTaskItem.vue +52 -0
  157. package/scaffold/spec-site/src/pages/board/MyTasksPage.vue +202 -0
  158. package/scaffold/spec-site/src/pages/board/SprintClose.vue +167 -0
  159. package/scaffold/spec-site/src/pages/board/SprintColumn.vue +49 -0
  160. package/scaffold/spec-site/src/pages/board/SprintKickoff.vue +389 -0
  161. package/scaffold/spec-site/src/pages/board/StatusBadge.vue +52 -0
  162. package/scaffold/spec-site/src/pages/board/StoryDetailPanel.vue +495 -0
  163. package/scaffold/spec-site/src/pages/board/TaskCard.vue +42 -0
  164. package/scaffold/spec-site/src/pages/retro/RetroCard.vue +36 -2
  165. package/scaffold/spec-site/src/pages/retro/RetroHeader.vue +82 -66
  166. package/scaffold/spec-site/src/pages/retro/RetroPage.vue +47 -18
  167. package/scaffold/spec-site/src/pages/standup/StandupEntryCard.vue +551 -0
  168. package/scaffold/spec-site/src/pages/standup/StandupForm.vue +68 -0
  169. package/scaffold/spec-site/src/pages/standup/StandupList.vue +71 -0
  170. package/scaffold/spec-site/src/pages/standup/StandupPage.vue +225 -0
  171. package/scaffold/spec-site/src/router.ts +141 -0
package/bin/cli.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { resolve, basename } from 'node:path';
4
+ import { readFileSync, existsSync } from 'node:fs';
4
5
  import { execSync } from 'node:child_process';
5
6
  import { copyScaffold, appendToFile, detectExisting } from '../lib/scaffold.mjs';
6
7
  import { runSetupWizard } from '../lib/setup-wizard.mjs';
@@ -15,6 +16,8 @@ const USAGE = `
15
16
  init [dir] Scaffold + interactive setup + hydration (default)
16
17
  hydrate [dir] Sync latest scaffold templates + re-hydrate from project.yaml
17
18
  doctor [dir] Check installation health
19
+ deploy [dir] Deploy pm-api to Cloudflare Workers (Tier 2)
20
+ migrate [dir] Run SQL schema migrations on pm-api database (Tier 2)
18
21
  help Show this help
19
22
 
20
23
  Options:
@@ -28,10 +31,12 @@ const USAGE = `
28
31
  npx popilot hydrate
29
32
  npx popilot hydrate --force
30
33
  npx popilot doctor
34
+ npx popilot deploy
35
+ npx popilot migrate
31
36
  npx popilot my-project # same as: popilot init my-project
32
37
  `;
33
38
 
34
- const SUBCOMMANDS = new Set(['init', 'hydrate', 'doctor', 'help']);
39
+ const SUBCOMMANDS = new Set(['init', 'hydrate', 'doctor', 'deploy', 'migrate', 'help']);
35
40
 
36
41
  async function main() {
37
42
  const args = process.argv.slice(2);
@@ -71,6 +76,12 @@ async function main() {
71
76
  case 'doctor':
72
77
  await cmdDoctor(targetDir, { skipSpecSite, platform });
73
78
  break;
79
+ case 'deploy':
80
+ await cmdDeploy(targetDir);
81
+ break;
82
+ case 'migrate':
83
+ await cmdMigrate(targetDir);
84
+ break;
74
85
  }
75
86
  }
76
87
 
@@ -133,7 +144,34 @@ async function cmdInit(targetDir, { skipSpecSite, force, platform }) {
133
144
  console.log(` ${f} ✅ (domain)`);
134
145
  }
135
146
 
136
- // 4. Install spec-site dependencies
147
+ // 4a. Install pm-api dependencies (Tier 2)
148
+ const pmApiDir = resolve(targetDir, 'pm-api');
149
+ try {
150
+ const { access: fsAccess } = await import('node:fs/promises');
151
+ await fsAccess(resolve(pmApiDir, 'package.json'));
152
+ console.log();
153
+ console.log(' 📦 Installing pm-api dependencies...');
154
+ try {
155
+ execSync('npm install', { cwd: pmApiDir, stdio: 'pipe' });
156
+ console.log(' ✅ Done');
157
+ } catch {
158
+ console.log(' ⚠️ npm install failed. Run manually: cd pm-api && npm install');
159
+ }
160
+
161
+ // Also install mcp-pm
162
+ const mcpPmDir = resolve(targetDir, 'mcp-pm');
163
+ console.log(' 📦 Installing mcp-pm dependencies...');
164
+ try {
165
+ execSync('npm install', { cwd: mcpPmDir, stdio: 'pipe' });
166
+ console.log(' ✅ Done');
167
+ } catch {
168
+ console.log(' ⚠️ npm install failed. Run manually: cd mcp-pm && npm install');
169
+ }
170
+ } catch {
171
+ // pm-api not present (Tier 0/1) — skip
172
+ }
173
+
174
+ // 4b. Install spec-site dependencies
137
175
  if (!skipSpecSite) {
138
176
  const specSiteDir = resolve(targetDir, 'spec-site');
139
177
  console.log();
@@ -171,10 +209,19 @@ async function cmdInit(targetDir, { skipSpecSite, force, platform }) {
171
209
  }
172
210
  console.log(' 3. Oscar can run a deep interview to enrich your project context');
173
211
  console.log();
212
+ // Platform-aware file paths
213
+ let sysPromptName = 'CLAUDE.md';
214
+ let cmdDirName = '.claude/commands/';
215
+ try {
216
+ const m = await loadManifest(platform);
217
+ sysPromptName = m.system_prompt?.target || sysPromptName;
218
+ cmdDirName = m.commands?.target_dir || cmdDirName;
219
+ } catch {}
220
+
174
221
  console.log(' 📁 Created:');
175
- console.log(' CLAUDE.md → System instructions (hydrated)');
222
+ console.log(` ${sysPromptName.padEnd(20)} → System instructions (hydrated)`);
176
223
  console.log(' .context/project.yaml → Project configuration');
177
- console.log(' .claude/commands/ → Slash commands');
224
+ console.log(` ${(cmdDirName + '/').replace(/\/\/$/, '/').padEnd(20)} → Slash commands`);
178
225
  console.log(' .context/agents/ → Agent personas (hydrated)');
179
226
  if (!skipSpecSite) {
180
227
  console.log(' spec-site/ → Interactive spec viewer (Vue3 + Vite)');
@@ -232,6 +279,170 @@ async function cmdDoctor(targetDir, { skipSpecSite, platform }) {
232
279
  process.exit(failed.length > 0 ? 1 : 0);
233
280
  }
234
281
 
282
+ // ── deploy ───────────────────────────────────────────────
283
+
284
+ async function cmdDeploy(targetDir) {
285
+ console.log();
286
+ console.log(' 🚀 Popilot — Deploy pm-api');
287
+ console.log(' ══════════════════════════════════════');
288
+ console.log();
289
+
290
+ const pmApiDir = resolve(targetDir, 'pm-api');
291
+
292
+ if (!existsSync(pmApiDir)) {
293
+ console.log(' ❌ pm-api directory not found.');
294
+ console.log(` Expected at: ${pmApiDir}`);
295
+ console.log(' Deploy is only available for Tier 2 (interactive spec-site with backend).');
296
+ console.log();
297
+ process.exit(1);
298
+ }
299
+
300
+ const wranglerToml = resolve(pmApiDir, 'wrangler.toml');
301
+ if (!existsSync(wranglerToml)) {
302
+ console.log(' ❌ wrangler.toml not found in pm-api.');
303
+ console.log(' Found wrangler.toml.hbs — run `popilot hydrate` first to generate wrangler.toml.');
304
+ console.log();
305
+ process.exit(1);
306
+ }
307
+
308
+ console.log(` 📂 Deploying from: ${pmApiDir}`);
309
+ console.log();
310
+
311
+ try {
312
+ execSync('npx wrangler deploy', { cwd: pmApiDir, stdio: 'inherit' });
313
+ console.log();
314
+ console.log(' ✅ pm-api deployed successfully.');
315
+ console.log();
316
+ } catch {
317
+ console.log();
318
+ console.log(' ❌ Deploy failed. Check the wrangler output above for details.');
319
+ console.log();
320
+ process.exit(1);
321
+ }
322
+ }
323
+
324
+ // ── migrate ──────────────────────────────────────────────
325
+
326
+ async function cmdMigrate(targetDir) {
327
+ console.log();
328
+ console.log(' 🗄️ Popilot — Run SQL Migrations');
329
+ console.log(' ══════════════════════════════════════');
330
+ console.log();
331
+
332
+ const pmApiDir = resolve(targetDir, 'pm-api');
333
+
334
+ if (!existsSync(pmApiDir)) {
335
+ console.log(' ❌ pm-api directory not found.');
336
+ console.log(` Expected at: ${pmApiDir}`);
337
+ console.log(' Migrate is only available for Tier 2 (interactive spec-site with backend).');
338
+ console.log();
339
+ process.exit(1);
340
+ }
341
+
342
+ const sqlDir = resolve(pmApiDir, 'sql');
343
+ if (!existsSync(sqlDir)) {
344
+ console.log(' ❌ pm-api/sql/ directory not found.');
345
+ console.log(' No migration files available.');
346
+ console.log();
347
+ process.exit(1);
348
+ }
349
+
350
+ // Read D1 database name from wrangler.toml
351
+ const wranglerToml = resolve(pmApiDir, 'wrangler.toml');
352
+ if (!existsSync(wranglerToml)) {
353
+ console.log(' ❌ wrangler.toml not found in pm-api.');
354
+ console.log(' Run `popilot hydrate` first to generate wrangler.toml.');
355
+ console.log();
356
+ process.exit(1);
357
+ }
358
+
359
+ const wranglerContent = readFileSync(wranglerToml, 'utf-8');
360
+ const dbNameMatch = wranglerContent.match(/database_name\s*=\s*"([^"]+)"/);
361
+ const dbName = dbNameMatch ? dbNameMatch[1] : null;
362
+
363
+ if (!dbName) {
364
+ console.log(' ❌ Could not find D1 database_name in wrangler.toml.');
365
+ console.log(' Ensure [[d1_databases]] is configured with a database_name.');
366
+ console.log();
367
+ process.exit(1);
368
+ }
369
+
370
+ console.log(` 📂 SQL directory: ${sqlDir}`);
371
+ console.log(` 🗃️ D1 database: ${dbName}`);
372
+ console.log();
373
+
374
+ // Read project.yaml to determine enabled features
375
+ const projectYaml = resolve(targetDir, '.context', 'project.yaml');
376
+ let features = { rewards: false, meetings: true, docs: true };
377
+
378
+ if (existsSync(projectYaml)) {
379
+ try {
380
+ const yamlContent = readFileSync(projectYaml, 'utf-8');
381
+ // Parse feature flags from YAML (simple regex — avoids adding a YAML dep)
382
+ const rewardsMatch = yamlContent.match(/features:[\s\S]*?rewards:\s*(true|false)/);
383
+ const meetingsMatch = yamlContent.match(/features:[\s\S]*?meetings:\s*(true|false)/);
384
+ const docsMatch = yamlContent.match(/features:[\s\S]*?docs:\s*(true|false)/);
385
+
386
+ if (rewardsMatch) features.rewards = rewardsMatch[1] === 'true';
387
+ if (meetingsMatch) features.meetings = meetingsMatch[1] === 'true';
388
+ if (docsMatch) features.docs = docsMatch[1] === 'true';
389
+ } catch {
390
+ console.log(' ⚠️ Could not read project.yaml — using default feature flags.');
391
+ }
392
+ }
393
+
394
+ // Build list of schemas to apply
395
+ const schemas = [{ file: 'schema-core.sql', label: 'core (always)' }];
396
+
397
+ if (features.rewards) {
398
+ schemas.push({ file: 'schema-rewards.sql', label: 'rewards' });
399
+ }
400
+ if (features.meetings) {
401
+ schemas.push({ file: 'schema-meetings.sql', label: 'meetings' });
402
+ }
403
+ if (features.docs) {
404
+ schemas.push({ file: 'schema-docs.sql', label: 'docs' });
405
+ }
406
+
407
+ console.log(' 📋 Schemas to apply:');
408
+ for (const s of schemas) {
409
+ console.log(` - ${s.file} (${s.label})`);
410
+ }
411
+ console.log();
412
+
413
+ // Execute each schema
414
+ let applied = 0;
415
+ let failed = 0;
416
+
417
+ for (const s of schemas) {
418
+ const sqlFile = resolve(sqlDir, s.file);
419
+ if (!existsSync(sqlFile)) {
420
+ console.log(` ⚠️ ${s.file} not found — skipped`);
421
+ continue;
422
+ }
423
+
424
+ try {
425
+ execSync(
426
+ `npx wrangler d1 execute ${dbName} --file=sql/${s.file} --remote`,
427
+ { cwd: pmApiDir, stdio: 'pipe' }
428
+ );
429
+ console.log(` ✅ ${s.file} applied`);
430
+ applied++;
431
+ } catch (err) {
432
+ console.log(` ❌ ${s.file} failed: ${err.message}`);
433
+ failed++;
434
+ }
435
+ }
436
+
437
+ console.log();
438
+ if (failed === 0) {
439
+ console.log(` ✅ Migration complete — ${applied} schema(s) applied.`);
440
+ } else {
441
+ console.log(` ⚠️ Migration finished with errors — ${applied} applied, ${failed} failed.`);
442
+ }
443
+ console.log();
444
+ }
445
+
235
446
  // ── Run ─────────────────────────────────────────────────
236
447
 
237
448
  main().catch(err => {
package/lib/doctor.mjs CHANGED
@@ -97,7 +97,44 @@ export async function runDoctor(targetDir, opts = {}) {
97
97
  }, passed, failed, warnings);
98
98
  }
99
99
 
100
- // 7. .gitignore entries
100
+ // 7. Tier 2 checks (pm-api + mcp-pm)
101
+ const pmApiDir = join(targetDir, 'pm-api');
102
+ let hasPmApi = false;
103
+ try { await access(pmApiDir); hasPmApi = true; } catch {}
104
+
105
+ if (hasPmApi) {
106
+ await check('pm-api/package.json exists', async () => {
107
+ await access(join(pmApiDir, 'package.json'));
108
+ }, passed, failed);
109
+
110
+ await check('pm-api/wrangler.toml exists (hydrated)', async () => {
111
+ await access(join(pmApiDir, 'wrangler.toml'));
112
+ try {
113
+ await access(join(pmApiDir, 'wrangler.toml.hbs'));
114
+ throw new Error('wrangler.toml.hbs still exists — hydration incomplete');
115
+ } catch (e) {
116
+ if (e.message.includes('hydration')) throw e;
117
+ }
118
+ }, passed, failed);
119
+
120
+ await check('pm-api/sql/schema-core.sql exists', async () => {
121
+ await access(join(pmApiDir, 'sql', 'schema-core.sql'));
122
+ }, passed, failed);
123
+
124
+ await check('mcp-pm/package.json exists', async () => {
125
+ await access(join(targetDir, 'mcp-pm', 'package.json'));
126
+ }, passed, failed);
127
+
128
+ await check('pm-api/node_modules exists', async () => {
129
+ await access(join(pmApiDir, 'node_modules'));
130
+ }, passed, failed, warnings);
131
+
132
+ await check('mcp-pm/node_modules exists', async () => {
133
+ await access(join(targetDir, 'mcp-pm', 'node_modules'));
134
+ }, passed, failed, warnings);
135
+ }
136
+
137
+ // 8. .gitignore entries
101
138
  await check('.gitignore includes user-context.yaml', async () => {
102
139
  const content = await readFile(join(targetDir, '.gitignore'), 'utf-8');
103
140
  if (!content.includes('user-context.yaml')) {
package/lib/hydrate.mjs CHANGED
@@ -145,6 +145,21 @@ export async function hydrate(targetDir, opts = {}) {
145
145
  if (result) hydrated.push(relative(targetDir, result));
146
146
  }
147
147
 
148
+ // pm-api wrangler.toml.hbs hydration (Tier 2)
149
+ const pmApiWranglerPath = join(targetDir, 'pm-api', 'wrangler.toml.hbs');
150
+ try {
151
+ const wranglerResult = await hydrateFile(pmApiWranglerPath, {
152
+ ctx,
153
+ registry,
154
+ enabledProviders,
155
+ targetType: 'system',
156
+ targetName: 'wrangler.toml',
157
+ });
158
+ if (wranglerResult) hydrated.push(relative(targetDir, wranglerResult));
159
+ } catch {
160
+ // pm-api not present (Tier 0/1) — skip
161
+ }
162
+
148
163
  // Domain command generation
149
164
  const domains = projectYaml.operations?.domains || [];
150
165
  const domainResults = [];
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Industry presets — maps industry type to template variables
3
+ * used in agent .hbs persona generation.
4
+ *
5
+ * Each preset provides 21 project.* variables that agent templates
6
+ * reference via {{project.example_entity}}, {{project.key_metrics}}, etc.
7
+ */
8
+
9
+ const PRESETS = {
10
+ saas: {
11
+ industry_label: 'SaaS',
12
+ domain_expertise: 'SaaS product management, subscription lifecycle, activation & retention optimization',
13
+ key_metrics: 'MRR, Churn Rate, LTV, CAC, Trial-to-Paid Conversion',
14
+ example_entity: 'subscription plan',
15
+ example_entity_plural: 'subscription plans',
16
+ example_status_feature: 'plan health indicator',
17
+ example_status_states: '🟢 active, 🟡 at-risk, 🔴 churning',
18
+ example_metric_name: 'Trial-to-Paid Conversion Rate',
19
+ example_metric_before: '12%',
20
+ example_metric_target: '20%',
21
+ example_persona_primary: 'trial users in first 14 days',
22
+ example_icp: 'new SMB teams (5-20 seats)',
23
+ example_icp_ko: '신규 SMB 팀(5-20석)',
24
+ example_api_endpoint: '/api/subscriptions/{id}',
25
+ example_event_name: 'plan_status_viewed',
26
+ example_chart_name: 'MRR trend chart',
27
+ example_data_path: 'domains/subscriptions/database.md',
28
+ example_empty_state_msg: '구독 플랜을 등록해보세요',
29
+ example_few_shot_problem: 'Users discover plan issues 72h late',
30
+ example_few_shot_question: 'If we provide plan health indicators, will users take preventive action?',
31
+ example_action: 'upgrade to premium',
32
+ },
33
+
34
+ ecommerce: {
35
+ industry_label: 'E-commerce',
36
+ domain_expertise: 'E-commerce operations, product catalog management, order fulfillment & conversion optimization',
37
+ key_metrics: 'GMV, Conversion Rate, AOV, Cart Abandonment Rate, Repeat Purchase Rate',
38
+ example_entity: 'product listing',
39
+ example_entity_plural: 'product listings',
40
+ example_status_feature: 'listing performance indicator',
41
+ example_status_states: '🟢 top-seller, 🟡 underperforming, 🔴 stale',
42
+ example_metric_name: 'Cart-to-Purchase Conversion Rate',
43
+ example_metric_before: '18%',
44
+ example_metric_target: '28%',
45
+ example_persona_primary: 'first-time buyers within 7 days of signup',
46
+ example_icp: 'online shoppers aged 25-40 in metro areas',
47
+ example_icp_ko: '수도권 25-40세 온라인 쇼핑 고객',
48
+ example_api_endpoint: '/api/products/{id}',
49
+ example_event_name: 'product_detail_viewed',
50
+ example_chart_name: 'GMV trend chart',
51
+ example_data_path: 'domains/products/database.md',
52
+ example_empty_state_msg: '첫 번째 상품을 등록해보세요',
53
+ example_few_shot_problem: 'Sellers discover underperforming listings too late',
54
+ example_few_shot_question: 'If we show listing performance indicators, will sellers optimize faster?',
55
+ example_action: 'add to cart',
56
+ },
57
+
58
+ b2b_platform: {
59
+ industry_label: 'B2B Platform',
60
+ domain_expertise: 'B2B platform management, client onboarding, workflow automation & enterprise sales cycle',
61
+ key_metrics: 'ARR, Net Revenue Retention, Onboarding Completion Rate, Feature Adoption, Expansion Revenue',
62
+ example_entity: 'client workspace',
63
+ example_entity_plural: 'client workspaces',
64
+ example_status_feature: 'workspace health score',
65
+ example_status_states: '🟢 healthy, 🟡 needs-attention, 🔴 at-risk',
66
+ example_metric_name: 'Onboarding Completion Rate',
67
+ example_metric_before: '55%',
68
+ example_metric_target: '80%',
69
+ example_persona_primary: 'new enterprise clients in first 30 days',
70
+ example_icp: 'mid-market companies (50-500 employees)',
71
+ example_icp_ko: '중견기업(50-500명 규모)',
72
+ example_api_endpoint: '/api/workspaces/{id}',
73
+ example_event_name: 'workspace_setup_completed',
74
+ example_chart_name: 'ARR trend chart',
75
+ example_data_path: 'domains/workspaces/database.md',
76
+ example_empty_state_msg: '워크스페이스를 설정해보세요',
77
+ example_few_shot_problem: 'Clients get stuck during onboarding and churn before activation',
78
+ example_few_shot_question: 'If we provide guided onboarding with health scores, will completion rate improve?',
79
+ example_action: 'complete onboarding',
80
+ },
81
+
82
+ generic: {
83
+ industry_label: 'Digital Product',
84
+ domain_expertise: 'Product management, user experience optimization, growth and retention strategy',
85
+ key_metrics: 'DAU, Retention Rate, Activation Rate, NPS, Feature Adoption Rate',
86
+ example_entity: 'feature module',
87
+ example_entity_plural: 'feature modules',
88
+ example_status_feature: 'usage health indicator',
89
+ example_status_states: '🟢 active, 🟡 declining, 🔴 inactive',
90
+ example_metric_name: 'Feature Activation Rate',
91
+ example_metric_before: '25%',
92
+ example_metric_target: '45%',
93
+ example_persona_primary: 'new users in first 7 days',
94
+ example_icp: 'early adopters who complete onboarding',
95
+ example_icp_ko: '온보딩을 완료한 얼리어답터',
96
+ example_api_endpoint: '/api/resources/{id}',
97
+ example_event_name: 'feature_activated',
98
+ example_chart_name: 'DAU trend chart',
99
+ example_data_path: 'domains/core/database.md',
100
+ example_empty_state_msg: '첫 번째 항목을 만들어보세요',
101
+ example_few_shot_problem: 'Users sign up but never reach the activation moment',
102
+ example_few_shot_question: 'If we redesign the first-run experience, will activation rate improve?',
103
+ example_action: 'complete first task',
104
+ },
105
+ };
106
+
107
+ /** All required keys every preset must have */
108
+ export const REQUIRED_KEYS = Object.keys(PRESETS.saas);
109
+
110
+ /** List available industry IDs */
111
+ export function listIndustries() {
112
+ return Object.keys(PRESETS);
113
+ }
114
+
115
+ /**
116
+ * Get a preset by industry ID.
117
+ * @param {string} industry
118
+ * @returns {Record<string, string>|null}
119
+ */
120
+ export function getPreset(industry) {
121
+ return PRESETS[industry] || null;
122
+ }
123
+
124
+ /**
125
+ * Get a preset and allow overrides for specific fields.
126
+ * @param {string} industry
127
+ * @param {Record<string, string>} [overrides]
128
+ * @returns {Record<string, string>}
129
+ */
130
+ export function getPresetWithOverrides(industry, overrides = {}) {
131
+ const base = PRESETS[industry] || PRESETS.generic;
132
+ return { ...base, ...overrides };
133
+ }
134
+
135
+ export default PRESETS;
package/lib/scaffold.mjs CHANGED
@@ -57,6 +57,11 @@ async function walk(srcDir, destDir, targetDir, options, copied, overwritten, sk
57
57
  continue;
58
58
  }
59
59
 
60
+ // Skip pm-api and mcp-pm if not Tier 2
61
+ if (options.skipPmApi && (relPath.startsWith('pm-api') || relPath.startsWith('mcp-pm'))) {
62
+ continue;
63
+ }
64
+
60
65
  // Skip manifest.yaml (adapter metadata, not a project file)
61
66
  if (entry.name === 'manifest.yaml') {
62
67
  continue;
@@ -9,6 +9,7 @@ import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises';
9
9
  import { join } from 'node:path';
10
10
  import { ask, confirm, select, createPrompt } from './prompt.mjs';
11
11
  import { parse as parseYaml, stringify as stringifyYaml } from './yaml-lite.mjs';
12
+ import { listIndustries, getPresetWithOverrides } from './industry-presets.mjs';
12
13
 
13
14
  /**
14
15
  * Run the interactive setup wizard.
@@ -58,6 +59,39 @@ export async function runSetupWizard(targetDir, opts = {}) {
58
59
 
59
60
  console.log();
60
61
 
62
+ // ── Phase 1.5: Industry ──────────────────────────
63
+ console.log(' ──────────────────────────────────────');
64
+ console.log(' 🏭 Industry');
65
+ console.log(' ──────────────────────────────────────');
66
+ console.log();
67
+
68
+ const industryOptions = listIndustries().map(id => ({
69
+ label: id === 'saas' ? 'SaaS' :
70
+ id === 'ecommerce' ? 'E-commerce' :
71
+ id === 'b2b_platform' ? 'B2B Platform' :
72
+ 'Generic (any product)',
73
+ value: id,
74
+ }));
75
+
76
+ const industry = await select(rl, 'Industry type:', industryOptions, 3);
77
+ const preset = getPresetWithOverrides(industry);
78
+
79
+ // Offer override for key fields
80
+ console.log();
81
+ console.log(' You can customize key fields (Enter to keep defaults):');
82
+ const domainExpertiseOverride = await ask(rl, `Domain expertise [${preset.domain_expertise.slice(0, 50)}...]`);
83
+ const keyMetricsOverride = await ask(rl, `Key metrics [${preset.key_metrics.slice(0, 50)}...]`);
84
+ const exampleEntityOverride = await ask(rl, `Example entity [${preset.example_entity}]`);
85
+
86
+ const industryOverrides = {};
87
+ if (domainExpertiseOverride) industryOverrides.domain_expertise = domainExpertiseOverride;
88
+ if (keyMetricsOverride) industryOverrides.key_metrics = keyMetricsOverride;
89
+ if (exampleEntityOverride) industryOverrides.example_entity = exampleEntityOverride;
90
+
91
+ const industryPreset = getPresetWithOverrides(industry, industryOverrides);
92
+
93
+ console.log();
94
+
61
95
  // ── Phase 2: Domains ─────────────────────────────
62
96
  const hasDomains = await confirm(rl, 'Do you have work domains?', false);
63
97
  let domains = [];
@@ -96,6 +130,38 @@ export async function runSetupWizard(targetDir, opts = {}) {
96
130
  specSiteConfig.mode = specSiteTier;
97
131
  }
98
132
 
133
+ // ── Phase 6: Backend Setup (Tier 2) ──────────────
134
+ let pmApiConfig = { enabled: false, url: '', features: { rewards: false, meetings: true, docs: true, initiatives: true }, blockchain: { enabled: false, provider: '', token_name: '', contract_address: '', token_decimals: 8 } };
135
+
136
+ if (specSiteTier === 'interactive') {
137
+ console.log();
138
+ console.log(' ──────────────────────────────────────');
139
+ console.log(' 🖥️ Backend Setup (Tier 2)');
140
+ console.log(' ──────────────────────────────────────');
141
+ console.log();
142
+
143
+ pmApiConfig.enabled = true;
144
+ pmApiConfig.url = await ask(rl, 'PM API URL (leave empty to configure later)');
145
+
146
+ console.log();
147
+ console.log(' Feature modules:');
148
+ pmApiConfig.features.rewards = await confirm(rl, 'Enable rewards/penalties module?', false);
149
+ pmApiConfig.features.meetings = await confirm(rl, 'Enable meetings module?', true);
150
+ pmApiConfig.features.docs = await confirm(rl, 'Enable docs module?', true);
151
+ pmApiConfig.features.initiatives = await confirm(rl, 'Enable initiatives module?', true);
152
+
153
+ console.log();
154
+ const enableBlockchain = await confirm(rl, 'Enable blockchain integration?', false);
155
+ if (enableBlockchain) {
156
+ pmApiConfig.blockchain.enabled = true;
157
+ pmApiConfig.blockchain.provider = await select(rl, 'Blockchain provider:', [
158
+ { label: 'TRON', value: 'tron' },
159
+ ], 0);
160
+ pmApiConfig.blockchain.token_name = await ask(rl, 'Token name');
161
+ pmApiConfig.blockchain.contract_address = await ask(rl, 'Contract address');
162
+ }
163
+ }
164
+
99
165
  console.log();
100
166
 
101
167
  // ── Phase 5: Integrations ────────────────────────
@@ -107,8 +173,9 @@ export async function runSetupWizard(targetDir, opts = {}) {
107
173
  // project.yaml
108
174
  const projectYaml = buildProjectYaml({
109
175
  projectName, tagline, projectType,
110
- domains, devScope, integrations, specSiteConfig,
176
+ domains, devScope, integrations, specSiteConfig, pmApiConfig,
111
177
  platform: opts.platform || null,
178
+ industryPreset,
112
179
  });
113
180
  const contextDir = join(targetDir, '.context');
114
181
  await mkdir(contextDir, { recursive: true });
@@ -321,7 +388,7 @@ export const ALL_INTEGRATION_PROVIDERS = [
321
388
  'sqlite_lambda',
322
389
  ];
323
390
 
324
- function buildProjectYaml({ projectName, tagline, projectType, domains, devScope, integrations, specSiteConfig, platform }) {
391
+ function buildProjectYaml({ projectName, tagline, projectType, domains, devScope, integrations, specSiteConfig, pmApiConfig, platform, industryPreset }) {
325
392
  // Build the full integrations block with all known providers
326
393
  const integrationsBlock = {};
327
394
 
@@ -338,6 +405,7 @@ function buildProjectYaml({ projectName, tagline, projectType, domains, devScope
338
405
  name: projectName,
339
406
  tagline: tagline || '',
340
407
  type: projectType,
408
+ ...(industryPreset || {}),
341
409
  },
342
410
  problem: {
343
411
  core: '',
@@ -382,6 +450,7 @@ function buildProjectYaml({ projectName, tagline, projectType, domains, devScope
382
450
  deploy_url: '',
383
451
  backend: specSiteConfig.backend,
384
452
  },
453
+ pm_api: pmApiConfig || { enabled: false },
385
454
  },
386
455
  _meta: {
387
456
  created_at: new Date().toISOString(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "popilot",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Multi-agent PO/PM system scaffold for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -70,6 +70,18 @@ read_only: true | false # Whether this agent can modify files
70
70
  - Definition of evidence (file:line, data, URL, etc.)
71
71
  - BAD → GOOD transformation example
72
72
 
73
+ ### 13.5. Evidence Requirements (MANDATORY for data-consuming agents)
74
+ - All PASS/FAIL verdicts must include: data source, measurement period, sample size
75
+ - Structured evidence table format (Item | Verdict | Evidence)
76
+ - Numerical recording principle: both absolute values and change rates
77
+ - Anti-patterns: no subjective judgments, no conclusions without data sources, no one-sided measurements
78
+
79
+ ### 13.6. Surface → Hidden Need Pattern (MANDATORY for customer-facing agents)
80
+ - Surface complaint is never the real problem
81
+ - Every VOC must follow: Surface → Hidden Need → Root Cause → Hypothesis pipeline
82
+ - Hidden Need must have 2+ supporting quotes
83
+ - Output must end with IF/THEN/BECAUSE hypothesis proposal
84
+
73
85
  ### 14. Context Budget (MANDATORY)
74
86
  - Files to skip
75
87
  - Reading strategy by file size (200+ lines → partial read)
@@ -90,6 +102,8 @@ read_only: true | false # Whether this agent can modify files
90
102
  | Few-shot Examples | 2+ Good/Bad pairs | Bad must be a realistic failure, not a strawman |
91
103
  | Final Checklist | 5-7 Yes/No items | Answerable without additional context |
92
104
  | Evidence Principle | Declaration + 1 example | Must include BAD→GOOD transformation |
105
+ | Evidence Requirements | Structured table format | Data source + period + numbers for every verdict |
106
+ | Hidden Need Pattern | Surface → Hidden Need → Hypothesis | 2+ quotes supporting hidden need |
93
107
  | Context Budget | 3+ rules | Must include "what to skip" guidance |
94
108
 
95
109
  ---
@@ -326,7 +326,7 @@ GOOD: "M1 retention: 45% (Jul) → 35% (Oct). N=23→46 signups per month. Trend
326
326
  | Document | Path | Content |
327
327
  |----------|------|---------|
328
328
  | **Full DB Overview** | `global/database/index.md` | ERD, table categories, query guide |
329
- | **Domain Detail** | `domains/ads/database.md` | Domain-specific detailed schema, ETL flows |
329
+ | **Domain Detail** | `{{project.example_data_path}}` | Domain-specific detailed schema, ETL flows |
330
330
 
331
331
  ### Pre-Query Checklist
332
332
  ```
@@ -351,12 +351,12 @@ Files Danny **must** read upon activation:
351
351
 
352
352
  ### Domain-Specific Load (During Work)
353
353
  ```
354
- When working on domain analysis → domains/ads/database.md (domain-specific detailed schema)
354
+ When working on domain analysis → {{project.example_data_path}} (domain-specific detailed schema)
355
355
  ```
356
356
 
357
357
  ---
358
358
 
359
359
  *Reference Context*: `global/database/index.md`, `global/metrics.md`, `global/strategy.md`
360
- *Domain Context*: `domains/ads/database.md`
360
+ *Domain Context*: `{{project.example_data_path}}`
361
361
  *Tools*: {{INTEGRATION_TOOLS_FOOTER}}
362
362
  *Connected Agents*: 🎯 Simon (insight delivery), 📊 Vicky (deep analysis request), 📋 Penny (execution rationale), 📡 Tara (tracking ready notification + data quality issues)