@girardmedia/bootspring 2.0.21 → 2.0.23

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 (159) hide show
  1. package/bin/bootspring.js +5 -0
  2. package/cli/org.js +474 -0
  3. package/cli/preseed/index.js +16 -0
  4. package/cli/preseed/interactive.js +143 -0
  5. package/cli/preseed/templates.js +227 -0
  6. package/cli/preseed.js +9 -301
  7. package/cli/seed/builders/ai-context-builder.js +85 -0
  8. package/cli/seed/builders/index.js +13 -0
  9. package/cli/seed/builders/seed-builder.js +272 -0
  10. package/cli/seed/extractors/content-extractors.js +383 -0
  11. package/cli/seed/extractors/index.js +47 -0
  12. package/cli/seed/extractors/metadata-extractors.js +167 -0
  13. package/cli/seed/extractors/section-extractor.js +54 -0
  14. package/cli/seed/extractors/stack-extractors.js +228 -0
  15. package/cli/seed/index.js +18 -0
  16. package/cli/seed/utils/folder-structure.js +84 -0
  17. package/cli/seed/utils/index.js +11 -0
  18. package/cli/seed.js +23 -1074
  19. package/core/api-client.js +77 -0
  20. package/core/entitlements.js +36 -0
  21. package/core/organizations.js +223 -0
  22. package/core/policies.js +51 -6
  23. package/core/policy-matrix.js +303 -0
  24. package/core/project-context.js +1 -0
  25. package/dist/cli/index.d.ts +3 -0
  26. package/dist/cli/index.js +3220 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/context-McpJQa_2.d.ts +5710 -0
  29. package/dist/core/index.d.ts +635 -0
  30. package/dist/core/index.js +2593 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/index-QqbeEiDm.d.ts +857 -0
  33. package/dist/index-UiYCgwiH.d.ts +174 -0
  34. package/dist/index.d.ts +453 -0
  35. package/dist/index.js +44228 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/mcp/index.d.ts +1 -0
  38. package/dist/mcp/index.js +41173 -0
  39. package/dist/mcp/index.js.map +1 -0
  40. package/generators/index.ts +82 -0
  41. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  42. package/intelligence/orchestrator/config/index.js +23 -0
  43. package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
  44. package/intelligence/orchestrator/config/phases.js +111 -0
  45. package/intelligence/orchestrator/config/remediation.js +150 -0
  46. package/intelligence/orchestrator/config/workflows.js +168 -0
  47. package/intelligence/orchestrator/core/index.js +16 -0
  48. package/intelligence/orchestrator/core/state-manager.js +88 -0
  49. package/intelligence/orchestrator/core/telemetry.js +24 -0
  50. package/intelligence/orchestrator/index.js +17 -0
  51. package/intelligence/orchestrator.js +17 -512
  52. package/mcp/contracts/mcp-contract.v1.json +1 -1
  53. package/package.json +16 -3
  54. package/src/cli/agent.ts +703 -0
  55. package/src/cli/analyze.ts +640 -0
  56. package/src/cli/audit.ts +707 -0
  57. package/src/cli/auth.ts +930 -0
  58. package/src/cli/billing.ts +364 -0
  59. package/src/cli/build.ts +1089 -0
  60. package/src/cli/business.ts +508 -0
  61. package/src/cli/checkpoint-utils.ts +236 -0
  62. package/src/cli/checkpoint.ts +757 -0
  63. package/src/cli/cloud-sync.ts +534 -0
  64. package/src/cli/content.ts +273 -0
  65. package/src/cli/context.ts +667 -0
  66. package/src/cli/dashboard.ts +133 -0
  67. package/src/cli/deploy.ts +704 -0
  68. package/src/cli/doctor.ts +480 -0
  69. package/src/cli/fundraise.ts +494 -0
  70. package/src/cli/generate.ts +346 -0
  71. package/src/cli/github-cmd.ts +566 -0
  72. package/src/cli/health.ts +599 -0
  73. package/src/cli/index.ts +113 -0
  74. package/src/cli/init.ts +838 -0
  75. package/src/cli/legal.ts +495 -0
  76. package/src/cli/log.ts +316 -0
  77. package/src/cli/loop.ts +1660 -0
  78. package/src/cli/manager.ts +878 -0
  79. package/src/cli/mcp.ts +275 -0
  80. package/src/cli/memory.ts +346 -0
  81. package/src/cli/metrics.ts +590 -0
  82. package/src/cli/monitor.ts +960 -0
  83. package/src/cli/mvp.ts +662 -0
  84. package/src/cli/onboard.ts +663 -0
  85. package/src/cli/orchestrator.ts +622 -0
  86. package/src/cli/plugin.ts +483 -0
  87. package/src/cli/prd.ts +671 -0
  88. package/src/cli/preseed-start.ts +1633 -0
  89. package/src/cli/preseed.ts +2434 -0
  90. package/src/cli/project.ts +526 -0
  91. package/src/cli/quality.ts +885 -0
  92. package/src/cli/security.ts +1079 -0
  93. package/src/cli/seed.ts +1224 -0
  94. package/src/cli/skill.ts +537 -0
  95. package/src/cli/suggest.ts +1225 -0
  96. package/src/cli/switch.ts +518 -0
  97. package/src/cli/task.ts +780 -0
  98. package/src/cli/telemetry.ts +172 -0
  99. package/src/cli/todo.ts +627 -0
  100. package/src/cli/types.ts +15 -0
  101. package/src/cli/update.ts +334 -0
  102. package/src/cli/visualize.ts +609 -0
  103. package/src/cli/watch.ts +895 -0
  104. package/src/cli/workspace.ts +709 -0
  105. package/src/core/action-recorder.ts +673 -0
  106. package/src/core/analyze-workflow.ts +1453 -0
  107. package/src/core/api-client.ts +1120 -0
  108. package/src/core/audit-workflow.ts +1681 -0
  109. package/src/core/auth.ts +471 -0
  110. package/src/core/build-orchestrator.ts +509 -0
  111. package/src/core/build-state.ts +621 -0
  112. package/src/core/checkpoint-engine.ts +482 -0
  113. package/src/core/config.ts +1285 -0
  114. package/src/core/context-loader.ts +694 -0
  115. package/src/core/context.ts +410 -0
  116. package/src/core/deploy-workflow.ts +1085 -0
  117. package/src/core/entitlements.ts +322 -0
  118. package/src/core/github-sync.ts +720 -0
  119. package/src/core/index.ts +981 -0
  120. package/src/core/ingest.ts +1186 -0
  121. package/src/core/metrics-engine.ts +886 -0
  122. package/src/core/mvp.ts +847 -0
  123. package/src/core/onboard-workflow.ts +1293 -0
  124. package/src/core/policies.ts +81 -0
  125. package/src/core/preseed-workflow.ts +1163 -0
  126. package/src/core/preseed.ts +1826 -0
  127. package/src/core/project-context.ts +380 -0
  128. package/src/core/project-state.ts +699 -0
  129. package/src/core/r2-sync.ts +691 -0
  130. package/src/core/scaffold.ts +1715 -0
  131. package/src/core/session.ts +286 -0
  132. package/src/core/task-extractor.ts +799 -0
  133. package/src/core/telemetry.ts +371 -0
  134. package/src/core/tier-enforcement.ts +737 -0
  135. package/src/core/utils.ts +437 -0
  136. package/src/index.ts +29 -0
  137. package/src/intelligence/agent-collab.ts +2376 -0
  138. package/src/intelligence/auto-suggest.ts +713 -0
  139. package/src/intelligence/content-gen.ts +1351 -0
  140. package/src/intelligence/cross-project.ts +1692 -0
  141. package/src/intelligence/git-memory.ts +529 -0
  142. package/src/intelligence/index.ts +318 -0
  143. package/src/intelligence/orchestrator.ts +534 -0
  144. package/src/intelligence/prd.ts +466 -0
  145. package/src/intelligence/recommendations.ts +982 -0
  146. package/src/intelligence/workflow-composer.ts +1472 -0
  147. package/src/mcp/capabilities.ts +233 -0
  148. package/src/mcp/index.ts +37 -0
  149. package/src/mcp/registry.ts +1268 -0
  150. package/src/mcp/response-formatter.ts +797 -0
  151. package/src/mcp/server.ts +240 -0
  152. package/src/types/agent.ts +69 -0
  153. package/src/types/config.ts +86 -0
  154. package/src/types/context.ts +77 -0
  155. package/src/types/index.ts +53 -0
  156. package/src/types/mcp.ts +91 -0
  157. package/src/types/skills.ts +47 -0
  158. package/src/types/workflow.ts +155 -0
  159. package/generators/index.js +0 -18
package/bin/bootspring.js CHANGED
@@ -139,6 +139,11 @@ const COMMANDS = {
139
139
  description: 'Manage subscription and view usage',
140
140
  usage: 'bootspring billing [status|usage|upgrade|portal]'
141
141
  },
142
+ org: {
143
+ script: '../cli/org.js',
144
+ description: 'Manage organization policies and members',
145
+ usage: 'bootspring org [list|info|policy|members]'
146
+ },
142
147
  manager: {
143
148
  script: '../cli/manager.js',
144
149
  description: 'Overview of all projects with health scores',
package/cli/org.js ADDED
@@ -0,0 +1,474 @@
1
+ /**
2
+ * Bootspring Organization Command
3
+ * Manage organization policies and members
4
+ *
5
+ * Commands:
6
+ * list List your organizations
7
+ * info [orgId] Show organization details
8
+ * policy get Get current policy settings
9
+ * policy set <profile> Set policy profile (startup/regulated/enterprise)
10
+ * policy scopes List available policy scopes
11
+ * members List organization members
12
+ * member <userId> policy View/set member policy overrides
13
+ *
14
+ * @package bootspring
15
+ * @command org
16
+ */
17
+
18
+ const utils = require('../core/utils');
19
+ const auth = require('../core/auth');
20
+ const apiClient = require('../core/api-client');
21
+ const policies = require('../core/policies');
22
+ const organizations = require('../core/organizations');
23
+ const policyMatrix = require('../core/policy-matrix');
24
+
25
+ /**
26
+ * Run the org command
27
+ */
28
+ async function run(args = []) {
29
+ const subcommand = args[0] || 'list';
30
+
31
+ switch (subcommand) {
32
+ case 'list':
33
+ return listOrganizations();
34
+ case 'info':
35
+ return showOrgInfo(args[1]);
36
+ case 'policy':
37
+ return handlePolicyCommand(args.slice(1));
38
+ case 'members':
39
+ return listMembers(args[1]);
40
+ case 'member':
41
+ return handleMemberCommand(args.slice(1));
42
+ case 'help':
43
+ case '--help':
44
+ case '-h':
45
+ return showHelp();
46
+ default:
47
+ console.log(`${utils.COLORS.red}Unknown command: ${subcommand}${utils.COLORS.reset}`);
48
+ showHelp();
49
+ return 1;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * List user's organizations
55
+ */
56
+ async function listOrganizations() {
57
+ if (!auth.isAuthenticated()) {
58
+ console.log(`${utils.COLORS.yellow}Not authenticated. Run: bootspring auth login${utils.COLORS.reset}`);
59
+ return 1;
60
+ }
61
+
62
+ const spinner = utils.createSpinner('Fetching organizations...').start();
63
+
64
+ try {
65
+ const orgs = await apiClient.listOrganizations();
66
+
67
+ if (!orgs || orgs.length === 0) {
68
+ spinner.info('No organizations found');
69
+ console.log(`\n${utils.COLORS.dim}Create an organization at bootspring.com/dashboard${utils.COLORS.reset}`);
70
+ return 0;
71
+ }
72
+
73
+ spinner.succeed(`Found ${orgs.length} organization(s)`);
74
+
75
+ console.log(`\n${utils.COLORS.bold}Your Organizations:${utils.COLORS.reset}\n`);
76
+
77
+ for (const org of orgs) {
78
+ const tierBadge = getTierBadge(org.tier);
79
+ const profileBadge = getProfileBadge(org.policyProfile);
80
+ console.log(` ${utils.COLORS.cyan}${org.name}${utils.COLORS.reset} ${tierBadge} ${profileBadge}`);
81
+ console.log(` ${utils.COLORS.dim}ID: ${org.id}${utils.COLORS.reset}`);
82
+ console.log(` ${utils.COLORS.dim}Members: ${org.memberCount || '?'}${utils.COLORS.reset}`);
83
+ console.log();
84
+ }
85
+
86
+ return 0;
87
+ } catch (error) {
88
+ spinner.fail(`Failed to fetch organizations: ${error.message}`);
89
+ return 1;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Show organization info
95
+ */
96
+ async function showOrgInfo(orgId) {
97
+ const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID || auth.getCredentials()?.orgId;
98
+
99
+ if (!resolvedOrgId) {
100
+ console.log(`${utils.COLORS.yellow}No organization specified.${utils.COLORS.reset}`);
101
+ console.log(`Usage: bootspring org info <orgId>`);
102
+ console.log(`Or set BOOTSPRING_ORG_ID environment variable`);
103
+ return 1;
104
+ }
105
+
106
+ const spinner = utils.createSpinner('Fetching organization...').start();
107
+
108
+ try {
109
+ const org = await apiClient.getOrganization(resolvedOrgId);
110
+ spinner.succeed('Organization loaded');
111
+
112
+ console.log(`
113
+ ${utils.COLORS.cyan}${utils.COLORS.bold}${org.name}${utils.COLORS.reset}
114
+ ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
115
+
116
+ ${utils.COLORS.bold}Details:${utils.COLORS.reset}
117
+ ID: ${org.id}
118
+ Tier: ${getTierBadge(org.tier)}
119
+ Profile: ${getProfileBadge(org.policyProfile)}
120
+ Created: ${new Date(org.createdAt).toLocaleDateString()}
121
+
122
+ ${utils.COLORS.bold}Policy:${utils.COLORS.reset}
123
+ External Skills: ${org.policy?.allowExternalSkills ? '✓ Allowed' : '✗ Blocked'}
124
+ Blocked Workflows: ${org.policy?.blockedWorkflows?.length || 0}
125
+
126
+ ${utils.COLORS.bold}Members:${utils.COLORS.reset} ${org.memberCount || org.members?.length || '?'}
127
+ `);
128
+
129
+ if (org.members && org.members.length > 0) {
130
+ console.log(`${utils.COLORS.bold}Member List:${utils.COLORS.reset}`);
131
+ for (const member of org.members.slice(0, 10)) {
132
+ const roleBadge = getRoleBadge(member.role);
133
+ console.log(` ${member.email || member.userId} ${roleBadge}`);
134
+ }
135
+ if (org.members.length > 10) {
136
+ console.log(` ${utils.COLORS.dim}... and ${org.members.length - 10} more${utils.COLORS.reset}`);
137
+ }
138
+ }
139
+
140
+ return 0;
141
+ } catch (error) {
142
+ spinner.fail(`Failed to fetch organization: ${error.message}`);
143
+ return 1;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Handle policy subcommands
149
+ */
150
+ async function handlePolicyCommand(args) {
151
+ const action = args[0] || 'get';
152
+
153
+ switch (action) {
154
+ case 'get':
155
+ return showPolicy(args[1]);
156
+ case 'set':
157
+ return setPolicy(args[1], args[2]);
158
+ case 'scopes':
159
+ return showScopes();
160
+ case 'profiles':
161
+ return showProfiles();
162
+ default:
163
+ console.log(`${utils.COLORS.red}Unknown policy action: ${action}${utils.COLORS.reset}`);
164
+ console.log('Available: get, set, scopes, profiles');
165
+ return 1;
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Show current policy
171
+ */
172
+ async function showPolicy(orgId) {
173
+ try {
174
+ const summary = await organizations.getOrgPolicySummary({ orgId });
175
+
176
+ if (!summary.hasOrg) {
177
+ // Show user-level policy
178
+ const profile = policies.resolvePolicyProfile();
179
+ const policy = policies.getPolicyProfile(profile);
180
+
181
+ console.log(`
182
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Current Policy (User-Level)${utils.COLORS.reset}
183
+ ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
184
+
185
+ Profile: ${getProfileBadge(profile)}
186
+ External Skills: ${policy.allowExternalSkills ? '✓ Allowed' : '✗ Blocked'}
187
+ Blocked Workflows: ${policy.blockedWorkflows.length > 0 ? policy.blockedWorkflows.join(', ') : 'None'}
188
+
189
+ ${utils.COLORS.dim}No organization context. Set BOOTSPRING_ORG_ID for org-level policy.${utils.COLORS.reset}
190
+ `);
191
+ return 0;
192
+ }
193
+
194
+ console.log(`
195
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Organization Policy${utils.COLORS.reset}
196
+ ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
197
+
198
+ Organization: ${summary.orgName || summary.orgId}
199
+ Tier: ${getTierBadge(summary.tier)}
200
+ Profile: ${getProfileBadge(summary.profile)}
201
+ Your Role: ${getRoleBadge(summary.role)}
202
+
203
+ ${utils.COLORS.bold}Scope Summary:${utils.COLORS.reset}
204
+ Allowed Scopes: ${utils.COLORS.green}${summary.allowedScopes}${utils.COLORS.reset}
205
+ Blocked Scopes: ${utils.COLORS.red}${summary.blockedScopes}${utils.COLORS.reset}
206
+
207
+ ${utils.COLORS.bold}Limits:${utils.COLORS.reset}
208
+ Skills/Day: ${formatLimit(summary.limits.skillsPerDay)}
209
+ Workflows/Day: ${formatLimit(summary.limits.workflowsPerDay)}
210
+ Agent Calls/Day: ${formatLimit(summary.limits.agentInvocationsPerDay)}
211
+ Team Members: ${formatLimit(summary.limits.teamMembers)}
212
+
213
+ ${summary.overrides.length > 0 ? `${utils.COLORS.bold}Active Overrides:${utils.COLORS.reset} ${summary.overrides.join(', ')}` : ''}
214
+ `);
215
+
216
+ return 0;
217
+ } catch (error) {
218
+ console.log(`${utils.COLORS.red}Failed to get policy: ${error.message}${utils.COLORS.reset}`);
219
+ return 1;
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Set policy profile
225
+ */
226
+ async function setPolicy(profile, orgId) {
227
+ if (!profile) {
228
+ console.log(`${utils.COLORS.yellow}Please specify a profile: startup, regulated, or enterprise${utils.COLORS.reset}`);
229
+ return 1;
230
+ }
231
+
232
+ const validProfiles = ['startup', 'regulated', 'enterprise'];
233
+ if (!validProfiles.includes(profile.toLowerCase())) {
234
+ console.log(`${utils.COLORS.red}Invalid profile: ${profile}${utils.COLORS.reset}`);
235
+ console.log(`Valid profiles: ${validProfiles.join(', ')}`);
236
+ return 1;
237
+ }
238
+
239
+ const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID;
240
+
241
+ if (!resolvedOrgId) {
242
+ // Set user-level via env var hint
243
+ console.log(`
244
+ ${utils.COLORS.yellow}No organization specified.${utils.COLORS.reset}
245
+
246
+ To set user-level policy, add to your environment:
247
+ ${utils.COLORS.cyan}export BOOTSPRING_POLICY_PROFILE=${profile}${utils.COLORS.reset}
248
+
249
+ To set org-level policy, specify an org ID:
250
+ ${utils.COLORS.cyan}bootspring org policy set ${profile} <orgId>${utils.COLORS.reset}
251
+ `);
252
+ return 0;
253
+ }
254
+
255
+ const spinner = utils.createSpinner(`Setting policy to "${profile}"...`).start();
256
+
257
+ try {
258
+ await apiClient.updateOrgPolicy(resolvedOrgId, { policyProfile: profile });
259
+ organizations.clearOrgCache(resolvedOrgId);
260
+ spinner.succeed(`Policy updated to "${profile}"`);
261
+ return 0;
262
+ } catch (error) {
263
+ spinner.fail(`Failed to update policy: ${error.message}`);
264
+ return 1;
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Show available scopes
270
+ */
271
+ function showScopes() {
272
+ const scopes = policies.getPolicyScopes();
273
+
274
+ console.log(`
275
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Policy Scopes${utils.COLORS.reset}
276
+ ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
277
+ `);
278
+
279
+ for (const [category, categoryScopes] of Object.entries(scopes)) {
280
+ console.log(`${utils.COLORS.bold}${category}:${utils.COLORS.reset}`);
281
+ for (const [name, scope] of Object.entries(categoryScopes)) {
282
+ console.log(` ${utils.COLORS.cyan}${scope}${utils.COLORS.reset} ${utils.COLORS.dim}(${name})${utils.COLORS.reset}`);
283
+ }
284
+ console.log();
285
+ }
286
+
287
+ return 0;
288
+ }
289
+
290
+ /**
291
+ * Show available profiles
292
+ */
293
+ function showProfiles() {
294
+ const profiles = policies.listPolicyProfiles();
295
+
296
+ console.log(`
297
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Policy Profiles${utils.COLORS.reset}
298
+ ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
299
+ `);
300
+
301
+ for (const profile of profiles) {
302
+ console.log(` ${getProfileBadge(profile.id)} ${utils.COLORS.dim}(${profile.tier} tier+)${utils.COLORS.reset}`);
303
+ console.log(` ${utils.COLORS.dim}${profile.description}${utils.COLORS.reset}`);
304
+ console.log();
305
+ }
306
+
307
+ return 0;
308
+ }
309
+
310
+ /**
311
+ * List organization members
312
+ */
313
+ async function listMembers(orgId) {
314
+ const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID;
315
+
316
+ if (!resolvedOrgId) {
317
+ console.log(`${utils.COLORS.yellow}No organization specified.${utils.COLORS.reset}`);
318
+ return 1;
319
+ }
320
+
321
+ const spinner = utils.createSpinner('Fetching members...').start();
322
+
323
+ try {
324
+ const members = await apiClient.listOrgMembers(resolvedOrgId);
325
+ spinner.succeed(`Found ${members.length} member(s)`);
326
+
327
+ console.log(`\n${utils.COLORS.bold}Organization Members:${utils.COLORS.reset}\n`);
328
+
329
+ for (const member of members) {
330
+ const roleBadge = getRoleBadge(member.role);
331
+ const hasOverrides = member.policyOverrides && Object.keys(member.policyOverrides).length > 0;
332
+ console.log(` ${member.email || member.userId} ${roleBadge}${hasOverrides ? ' (custom policy)' : ''}`);
333
+ }
334
+
335
+ return 0;
336
+ } catch (error) {
337
+ spinner.fail(`Failed to fetch members: ${error.message}`);
338
+ return 1;
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Handle member subcommands
344
+ */
345
+ async function handleMemberCommand(args) {
346
+ const userId = args[0];
347
+ const action = args[1] || 'info';
348
+
349
+ if (!userId) {
350
+ console.log(`${utils.COLORS.yellow}Please specify a user ID${utils.COLORS.reset}`);
351
+ return 1;
352
+ }
353
+
354
+ switch (action) {
355
+ case 'info':
356
+ case 'policy':
357
+ return showMemberPolicy(userId, args[2]);
358
+ default:
359
+ console.log(`${utils.COLORS.red}Unknown member action: ${action}${utils.COLORS.reset}`);
360
+ return 1;
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Show member policy
366
+ */
367
+ async function showMemberPolicy(userId, orgId) {
368
+ const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID;
369
+
370
+ if (!resolvedOrgId) {
371
+ console.log(`${utils.COLORS.yellow}No organization specified.${utils.COLORS.reset}`);
372
+ return 1;
373
+ }
374
+
375
+ const spinner = utils.createSpinner('Fetching member policy...').start();
376
+
377
+ try {
378
+ const policy = await apiClient.getMemberPolicy(resolvedOrgId, userId);
379
+ spinner.succeed('Member policy loaded');
380
+
381
+ console.log(`
382
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Member Policy${utils.COLORS.reset}
383
+ ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
384
+
385
+ User: ${userId}
386
+ Role: ${getRoleBadge(policy.role)}
387
+
388
+ ${utils.COLORS.bold}Policy Overrides:${utils.COLORS.reset}
389
+ ${policy.overrides && Object.keys(policy.overrides).length > 0
390
+ ? JSON.stringify(policy.overrides, null, 2).split('\n').join('\n ')
391
+ : 'No custom overrides (using org defaults)'}
392
+ `);
393
+
394
+ return 0;
395
+ } catch (error) {
396
+ spinner.fail(`Failed to fetch member policy: ${error.message}`);
397
+ return 1;
398
+ }
399
+ }
400
+
401
+ // Helper functions
402
+
403
+ function getTierBadge(tier) {
404
+ const badges = {
405
+ free: `${utils.COLORS.dim}[free]${utils.COLORS.reset}`,
406
+ pro: `${utils.COLORS.blue}[pro]${utils.COLORS.reset}`,
407
+ team: `${utils.COLORS.green}[team]${utils.COLORS.reset}`,
408
+ enterprise: `${utils.COLORS.magenta}[enterprise]${utils.COLORS.reset}`
409
+ };
410
+ return badges[tier] || `[${tier}]`;
411
+ }
412
+
413
+ function getProfileBadge(profile) {
414
+ const badges = {
415
+ startup: `${utils.COLORS.cyan}startup${utils.COLORS.reset}`,
416
+ regulated: `${utils.COLORS.yellow}regulated${utils.COLORS.reset}`,
417
+ enterprise: `${utils.COLORS.magenta}enterprise${utils.COLORS.reset}`
418
+ };
419
+ return badges[profile] || profile;
420
+ }
421
+
422
+ function getRoleBadge(role) {
423
+ const badges = {
424
+ owner: `${utils.COLORS.red}(owner)${utils.COLORS.reset}`,
425
+ admin: `${utils.COLORS.yellow}(admin)${utils.COLORS.reset}`,
426
+ member: `${utils.COLORS.dim}(member)${utils.COLORS.reset}`,
427
+ viewer: `${utils.COLORS.dim}(viewer)${utils.COLORS.reset}`
428
+ };
429
+ return badges[role] || `(${role})`;
430
+ }
431
+
432
+ function formatLimit(value) {
433
+ if (value === -1) return `${utils.COLORS.green}Unlimited${utils.COLORS.reset}`;
434
+ if (value === undefined) return `${utils.COLORS.dim}N/A${utils.COLORS.reset}`;
435
+ return value.toLocaleString();
436
+ }
437
+
438
+ function showHelp() {
439
+ console.log(`
440
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Organization${utils.COLORS.reset}
441
+ ${utils.COLORS.dim}Manage organization policies and members${utils.COLORS.reset}
442
+
443
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
444
+ bootspring org <command> [options]
445
+
446
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
447
+ ${utils.COLORS.cyan}list${utils.COLORS.reset} List your organizations
448
+ ${utils.COLORS.cyan}info${utils.COLORS.reset} [orgId] Show organization details
449
+ ${utils.COLORS.cyan}policy get${utils.COLORS.reset} Get current policy settings
450
+ ${utils.COLORS.cyan}policy set <profile>${utils.COLORS.reset} Set policy profile
451
+ ${utils.COLORS.cyan}policy scopes${utils.COLORS.reset} List available policy scopes
452
+ ${utils.COLORS.cyan}policy profiles${utils.COLORS.reset} List policy profiles
453
+ ${utils.COLORS.cyan}members${utils.COLORS.reset} [orgId] List organization members
454
+ ${utils.COLORS.cyan}member <userId> policy${utils.COLORS.reset} View member policy overrides
455
+
456
+ ${utils.COLORS.bold}Policy Profiles:${utils.COLORS.reset}
457
+ ${utils.COLORS.cyan}startup${utils.COLORS.reset} Permissive policy for fast iteration
458
+ ${utils.COLORS.cyan}regulated${utils.COLORS.reset} Compliance-focused, external skills blocked
459
+ ${utils.COLORS.cyan}enterprise${utils.COLORS.reset} Full control with SSO and audit
460
+
461
+ ${utils.COLORS.bold}Environment:${utils.COLORS.reset}
462
+ BOOTSPRING_ORG_ID Default organization ID
463
+ BOOTSPRING_POLICY_PROFILE User-level policy profile
464
+
465
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
466
+ bootspring org list
467
+ bootspring org info org_abc123
468
+ bootspring org policy get
469
+ bootspring org policy set regulated
470
+ bootspring org members
471
+ `);
472
+ }
473
+
474
+ module.exports = { run };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Preseed Module Index
3
+ * Re-exports all preseed submodules
4
+ * @package bootspring
5
+ */
6
+
7
+ const interactive = require('./interactive');
8
+ const templates = require('./templates');
9
+
10
+ module.exports = {
11
+ interactive,
12
+ templates,
13
+ // Re-export commonly used functions at top level
14
+ ...interactive,
15
+ ...templates
16
+ };
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Interactive Helpers
3
+ * Readline and prompting utilities for preseed wizard
4
+ * @package bootspring
5
+ */
6
+
7
+ const readline = require('readline');
8
+ const utils = require('../../core/utils');
9
+
10
+ /**
11
+ * Create readline interface
12
+ * @returns {readline.Interface}
13
+ */
14
+ function createInterface() {
15
+ return readline.createInterface({
16
+ input: process.stdin,
17
+ output: process.stdout
18
+ });
19
+ }
20
+
21
+ /**
22
+ * Ask a text question
23
+ * @param {readline.Interface} rl - readline interface
24
+ * @param {string} question - the question to ask
25
+ * @param {string} defaultValue - default value if empty
26
+ * @returns {Promise<string>}
27
+ */
28
+ function askText(rl, question, defaultValue = '') {
29
+ const prompt = defaultValue
30
+ ? `${question} ${utils.COLORS.dim}[${defaultValue}]${utils.COLORS.reset}: `
31
+ : `${question}: `;
32
+
33
+ return new Promise((resolve) => {
34
+ rl.question(prompt, (answer) => {
35
+ resolve(answer.trim() || defaultValue);
36
+ });
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Ask a multi-line text question
42
+ * @param {readline.Interface} rl - readline interface
43
+ * @param {string} question - the question to ask
44
+ * @param {string} hint - hint text
45
+ * @returns {Promise<string>}
46
+ */
47
+ function askMultiLine(rl, question, hint = 'Enter text (empty line to finish)') {
48
+ return new Promise((resolve) => {
49
+ console.log(`\n${utils.COLORS.bold}${question}${utils.COLORS.reset}`);
50
+ console.log(`${utils.COLORS.dim}${hint}${utils.COLORS.reset}`);
51
+
52
+ const lines = [];
53
+ const onLine = (line) => {
54
+ if (line.trim() === '') {
55
+ rl.removeListener('line', onLine);
56
+ resolve(lines.join('\n'));
57
+ } else {
58
+ lines.push(line);
59
+ }
60
+ };
61
+ rl.on('line', onLine);
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Ask a list question (comma-separated)
67
+ * @param {readline.Interface} rl - readline interface
68
+ * @param {string} question - the question to ask
69
+ * @param {string} hint - hint text
70
+ * @returns {Promise<string[]>}
71
+ */
72
+ function askList(rl, question, hint = 'Enter comma-separated values') {
73
+ return new Promise((resolve) => {
74
+ console.log(`\n${utils.COLORS.bold}${question}${utils.COLORS.reset}`);
75
+ console.log(`${utils.COLORS.dim}${hint}${utils.COLORS.reset}`);
76
+
77
+ rl.question('> ', (answer) => {
78
+ const items = answer
79
+ .split(',')
80
+ .map(s => s.trim())
81
+ .filter(s => s.length > 0);
82
+ resolve(items);
83
+ });
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Ask a choice question
89
+ * @param {readline.Interface} rl - readline interface
90
+ * @param {string} question - the question to ask
91
+ * @param {Array} options - array of options (strings or {label, value, description})
92
+ * @param {number} defaultIndex - default selected index
93
+ * @returns {Promise<string>}
94
+ */
95
+ function askChoice(rl, question, options, defaultIndex = 0) {
96
+ return new Promise((resolve) => {
97
+ console.log(`\n${utils.COLORS.bold}${question}${utils.COLORS.reset}`);
98
+
99
+ options.forEach((opt, i) => {
100
+ const marker = i === defaultIndex
101
+ ? `${utils.COLORS.cyan}>${utils.COLORS.reset}`
102
+ : ' ';
103
+ const label = typeof opt === 'object' ? opt.label : opt;
104
+ const desc = typeof opt === 'object' && opt.description
105
+ ? ` ${utils.COLORS.dim}- ${opt.description}${utils.COLORS.reset}`
106
+ : '';
107
+ console.log(` ${marker} ${i + 1}. ${label}${desc}`);
108
+ });
109
+
110
+ rl.question(`\n${utils.COLORS.dim}Select [1-${options.length}]${utils.COLORS.reset}: `, (answer) => {
111
+ const index = parseInt(answer, 10) - 1;
112
+ const selectedIndex = (index >= 0 && index < options.length) ? index : defaultIndex;
113
+ const selected = options[selectedIndex];
114
+ resolve(typeof selected === 'object' ? selected.value : selected);
115
+ });
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Display section header
121
+ * @param {number} number - current step number
122
+ * @param {number} total - total steps
123
+ * @param {string} title - section title
124
+ * @param {string} description - section description
125
+ */
126
+ function displaySection(number, total, title, description) {
127
+ console.log('\n');
128
+ console.log(`${utils.COLORS.cyan}${'━'.repeat(60)}${utils.COLORS.reset}`);
129
+ console.log(`${utils.COLORS.bold}Step ${number}/${total}: ${title}${utils.COLORS.reset}`);
130
+ if (description) {
131
+ console.log(`${utils.COLORS.dim}${description}${utils.COLORS.reset}`);
132
+ }
133
+ console.log(`${utils.COLORS.cyan}${'━'.repeat(60)}${utils.COLORS.reset}`);
134
+ }
135
+
136
+ module.exports = {
137
+ createInterface,
138
+ askText,
139
+ askMultiLine,
140
+ askList,
141
+ askChoice,
142
+ displaySection
143
+ };