atris 3.37.1 → 3.40.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 (164) hide show
  1. package/AGENTS.md +25 -6
  2. package/README.md +2 -0
  3. package/atris/PERSONA.md +11 -11
  4. package/atris.md +17 -2
  5. package/ax +160 -34
  6. package/bin/atris.js +78 -6
  7. package/commands/activate.js +16 -3
  8. package/commands/agent-spawn.js +13 -18
  9. package/commands/autoland.js +76 -47
  10. package/commands/autopilot-front.js +0 -4
  11. package/commands/autopilot.js +0 -12
  12. package/commands/bench.js +10 -12
  13. package/commands/brain.js +0 -7
  14. package/commands/brief.js +0 -2
  15. package/commands/business-sync.js +0 -1
  16. package/commands/business.js +452 -12
  17. package/commands/chat-scan.js +5 -7
  18. package/commands/clean.js +31 -2
  19. package/commands/close.js +46 -11
  20. package/commands/cloud.js +0 -1
  21. package/commands/codex-goal.js +8 -12
  22. package/commands/computer.js +6 -5
  23. package/commands/console.js +64 -11
  24. package/commands/decide.js +166 -0
  25. package/commands/deck.js +30 -12
  26. package/commands/dream.js +0 -7
  27. package/commands/drill.js +14 -25
  28. package/commands/engine.js +206 -26
  29. package/commands/experiments.js +0 -4
  30. package/commands/fleet-report.js +12 -4
  31. package/commands/game.js +261 -0
  32. package/commands/gm.js +17 -7
  33. package/commands/goal.js +0 -6
  34. package/commands/harvest.js +1 -5
  35. package/commands/improve.js +29 -8
  36. package/commands/init.js +41 -37
  37. package/commands/integrations.js +0 -7
  38. package/commands/land.js +77 -20
  39. package/commands/launchpad.js +1 -16
  40. package/commands/lesson.js +17 -7
  41. package/commands/lifecycle.js +5 -5
  42. package/commands/log.js +55 -5
  43. package/commands/loop-front.js +0 -2
  44. package/commands/loops.js +6 -5
  45. package/commands/meet.js +0 -2
  46. package/commands/member.js +622 -573
  47. package/commands/mission.js +790 -314
  48. package/commands/next.js +0 -1
  49. package/commands/now.js +0 -12
  50. package/commands/one-lap.js +8 -8
  51. package/commands/orb.js +0 -4
  52. package/commands/pack-craft.js +0 -1
  53. package/commands/pack.js +3599 -424
  54. package/commands/play.js +6 -6
  55. package/commands/probe.js +12 -6
  56. package/commands/pulse.js +88 -64
  57. package/commands/push.js +0 -4
  58. package/commands/radar.js +14 -11
  59. package/commands/recover.js +340 -0
  60. package/commands/release.js +10 -9
  61. package/commands/report.js +0 -1
  62. package/commands/router.js +320 -0
  63. package/commands/run-front.js +0 -2
  64. package/commands/search.js +0 -3
  65. package/commands/site-deploy.js +870 -0
  66. package/commands/site.js +11 -2
  67. package/commands/skill.js +7 -2
  68. package/commands/slop.js +237 -7
  69. package/commands/status.js +6 -0
  70. package/commands/stream.js +4 -20
  71. package/commands/study.js +0 -3
  72. package/commands/sync-checkout.js +51 -0
  73. package/commands/sync.js +11 -40
  74. package/commands/task.js +999 -595
  75. package/commands/taste.js +101 -0
  76. package/commands/teach.js +86 -0
  77. package/commands/team.js +83 -3
  78. package/commands/unknowns.js +0 -10
  79. package/commands/update.js +0 -1
  80. package/commands/vercel.js +4 -2
  81. package/commands/visualize.js +0 -5
  82. package/commands/voice.js +195 -0
  83. package/commands/watch.js +1 -28
  84. package/commands/wiki.js +42 -15
  85. package/commands/wish.js +0 -1
  86. package/commands/workflow.js +2 -2
  87. package/commands/worktree.js +88 -28
  88. package/commands/xp.js +28 -30
  89. package/commands/youtube.js +0 -2
  90. package/lib/accept-verify-gate.js +126 -0
  91. package/lib/activity-stream.js +0 -2
  92. package/lib/arg-parser.js +41 -0
  93. package/lib/auto-accept-certified.js +261 -3
  94. package/lib/autoland.js +66 -3
  95. package/lib/ax-auto-lane.js +68 -7
  96. package/lib/bench/context.js +0 -2
  97. package/lib/bench/report.js +0 -1
  98. package/lib/bench/runner.js +18 -11
  99. package/lib/brief-ledger.js +0 -6
  100. package/lib/business-simulate.js +382 -0
  101. package/lib/chat-log-scan.js +0 -5
  102. package/lib/checkout-sync.js +52 -0
  103. package/lib/claude-boot-block.js +48 -0
  104. package/lib/cloud-mission.js +0 -6
  105. package/lib/codex-flight.js +0 -1
  106. package/lib/conductor-artifacts.js +25 -0
  107. package/lib/context-gatherer.js +7 -2
  108. package/lib/deck-compose.js +0 -1
  109. package/lib/deck-history.js +0 -1
  110. package/lib/deck-layout.js +0 -5
  111. package/lib/deck-review.js +0 -7
  112. package/lib/deck-schema.js +0 -1
  113. package/lib/default-verifier.js +0 -2
  114. package/lib/engine-registry.js +157 -28
  115. package/lib/experiments/daily.js +0 -6
  116. package/lib/falsifier-probe.js +84 -0
  117. package/lib/fleet.js +275 -42
  118. package/lib/functional-owner.js +0 -3
  119. package/lib/git-spawn.js +15 -0
  120. package/lib/inspect-fields.js +0 -1
  121. package/lib/json-file.js +37 -0
  122. package/lib/known-commands.js +2 -2
  123. package/lib/learnings.js +0 -2
  124. package/lib/lesson-preflight.js +146 -0
  125. package/lib/loop-doctor.js +11 -19
  126. package/lib/manifest.js +0 -2
  127. package/lib/member-alive.js +0 -1
  128. package/lib/member-switches.js +0 -5
  129. package/lib/mission-artifact.js +0 -1
  130. package/lib/mission-human-asks.js +28 -0
  131. package/lib/mission-protected-lane.js +296 -0
  132. package/lib/mission-room.js +0 -2
  133. package/lib/mission-runtime-loop.js +0 -1
  134. package/lib/next-moves.js +0 -18
  135. package/lib/official-cli-integration.js +47 -6
  136. package/lib/orb-context.js +14 -4
  137. package/lib/orb-scorecard.js +0 -1
  138. package/lib/outbound-send-gate.js +0 -6
  139. package/lib/pack-capabilities.js +685 -0
  140. package/lib/permission-grants.js +0 -6
  141. package/lib/policy-lessons.js +0 -2
  142. package/lib/pulse.js +68 -15
  143. package/lib/review-integrity.js +0 -2
  144. package/lib/router-brain.js +51 -3
  145. package/lib/runner-command.js +0 -10
  146. package/lib/runtime-bootstrap.js +0 -1
  147. package/lib/scorecard.js +0 -1
  148. package/lib/self-drive.js +45 -14
  149. package/lib/state-detection.js +0 -6
  150. package/lib/task-db.js +137 -7
  151. package/lib/task-decision.js +50 -0
  152. package/lib/taste-lessons.js +153 -0
  153. package/lib/team-presence.js +0 -1
  154. package/lib/tool-result-encode.js +17 -1
  155. package/lib/voice-gate.js +129 -2
  156. package/lib/wiki.js +152 -9
  157. package/lib/wish-audit.js +21 -7
  158. package/lib/wish-delegate.js +27 -21
  159. package/lib/wish-stats.js +0 -5
  160. package/lib/wish-store.js +0 -1
  161. package/lib/workspace-safety.js +0 -2
  162. package/lib/zip.js +95 -8
  163. package/package.json +2 -1
  164. package/templates/business-starter/persona.md +9 -0
package/commands/bench.js CHANGED
@@ -8,12 +8,10 @@ const {
8
8
  taskMetadata,
9
9
  } = require('../lib/bench/runner');
10
10
  const { buildBenchReport, renderBenchReportText } = require('../lib/bench/report');
11
+ const { hasFlag } = require('../lib/arg-parser');
11
12
 
12
- function hasFlag(args, name) {
13
- return args.includes(name);
14
- }
15
-
16
- function readFlag(args, name) {
13
+ // Preserve the existing rule that any inline value wins over a split value.
14
+ function readInlineFirstFlag(args, name) {
17
15
  const prefix = `${name}=`;
18
16
  const inline = args.find((arg) => String(arg).startsWith(prefix));
19
17
  if (inline) return String(inline).slice(prefix.length);
@@ -58,11 +56,11 @@ async function runCommand(args) {
58
56
  const asJson = hasFlag(args, '--json');
59
57
  try {
60
58
  const result = await runBench({
61
- pack: readFlag(args, '--pack') || undefined,
62
- engine: readFlag(args, '--engine') || undefined,
59
+ pack: readInlineFirstFlag(args, '--pack') || undefined,
60
+ engine: readInlineFirstFlag(args, '--engine') || undefined,
63
61
  taskIds: readRepeatedFlag(args, '--task'),
64
- label: readFlag(args, '--label'),
65
- experiment: readFlag(args, '--experiment'),
62
+ label: readInlineFirstFlag(args, '--label'),
63
+ experiment: readInlineFirstFlag(args, '--experiment'),
66
64
  updateBaseline: hasFlag(args, '--update-baseline'),
67
65
  stateRoot: process.cwd(),
68
66
  });
@@ -87,7 +85,7 @@ async function runCommand(args) {
87
85
  }
88
86
 
89
87
  function tasksCommand(args) {
90
- const pack = readFlag(args, '--pack') || undefined;
88
+ const pack = readInlineFirstFlag(args, '--pack') || undefined;
91
89
  const tasks = taskMetadata({ pack });
92
90
  if (hasFlag(args, '--json')) {
93
91
  console.log(JSON.stringify({
@@ -121,7 +119,7 @@ function packsCommand(args) {
121
119
  }
122
120
 
123
121
  function resultsCommand(args) {
124
- const last = Number(readFlag(args, '--last') || 0);
122
+ const last = Number(readInlineFirstFlag(args, '--last') || 0);
125
123
  const results = readResultRecords({ stateRoot: process.cwd(), last });
126
124
  if (hasFlag(args, '--json')) {
127
125
  console.log(JSON.stringify({
@@ -143,7 +141,7 @@ function resultsCommand(args) {
143
141
  }
144
142
 
145
143
  function reportCommand(args) {
146
- const pack = readFlag(args, '--pack') || undefined;
144
+ const pack = readInlineFirstFlag(args, '--pack') || undefined;
147
145
  const asJson = hasFlag(args, '--json');
148
146
  try {
149
147
  const report = buildBenchReport({ pack, stateRoot: process.cwd() });
package/commands/brain.js CHANGED
@@ -1714,17 +1714,10 @@ function brainCommand(args = process.argv.slice(3)) {
1714
1714
  module.exports = {
1715
1715
  brainCommand,
1716
1716
  collectState,
1717
- prepareBrainState,
1718
1717
  renderStatus,
1719
- renderLedger,
1720
- renderActivationCard,
1721
- renderActivationGallery,
1722
1718
  recordTaskEpisodeScorecards,
1723
- recordFeedback,
1724
- recordApproval,
1725
1719
  verifyActivationCard,
1726
1720
  verifyActivationGallery,
1727
- verifyBrain,
1728
1721
  parseContributionCard,
1729
1722
  escapeRegExp,
1730
1723
  countTodoItems,
package/commands/brief.js CHANGED
@@ -642,10 +642,8 @@ module.exports = {
642
642
  buildBriefData,
643
643
  briefCommand,
644
644
  handleBriefAnswer,
645
- parsePromptAnswer,
646
645
  renderBrief,
647
646
  renderBriefHtml,
648
647
  run: briefCommand,
649
- runBriefLedgerCommand,
650
648
  shouldPromptBrief,
651
649
  };
@@ -1082,7 +1082,6 @@ module.exports = {
1082
1082
  buildConflictMarkers,
1083
1083
  canPreviewPush,
1084
1084
  collectBrainSnapshot,
1085
- collectWorkspaceSnapshot,
1086
1085
  collectWorkspaceWarnings,
1087
1086
  collectLocalSyncStatus,
1088
1087
  collectConflictResolutionEntries,
@@ -6,6 +6,12 @@ const { apiRequestJson } = require('../utils/api');
6
6
  const { syncBusinessCanonical, ensureWorkspaceStateFiles, ensureBusinessRootAgentAdapters } = require('./sync');
7
7
  const { ensureContextScaffold, writeWikiStatus, appendWikiLog } = require('../lib/wiki');
8
8
  const { writeRuntimeReceipt } = require('../lib/runtime-bootstrap');
9
+ const {
10
+ SIMULATE_USAGE,
11
+ buildSimulationPlan,
12
+ parseSimulationArgs,
13
+ renderDryRun,
14
+ } = require('../lib/business-simulate');
9
15
 
10
16
  function getBusinessConfigPath() {
11
17
  const home = require('os').homedir();
@@ -2022,6 +2028,328 @@ function activityBar(daysSinceActive, width = 10) {
2022
2028
  return '\u2501'.repeat(filled) + '\u2591'.repeat(width - filled);
2023
2029
  }
2024
2030
 
2031
+ function asRecord(value) {
2032
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
2033
+ }
2034
+
2035
+ function firstString(...values) {
2036
+ for (const value of values) {
2037
+ if (typeof value === 'string' && value.trim()) return value.trim();
2038
+ }
2039
+ return '';
2040
+ }
2041
+
2042
+ function firstNumber(...values) {
2043
+ for (const value of values) {
2044
+ const parsed = typeof value === 'number'
2045
+ ? value
2046
+ : (typeof value === 'string' && value.trim() ? Number(value) : Number.NaN);
2047
+ if (Number.isFinite(parsed)) return parsed;
2048
+ }
2049
+ return 0;
2050
+ }
2051
+
2052
+ function firstBoolean(...values) {
2053
+ for (const value of values) {
2054
+ if (typeof value === 'boolean') return value;
2055
+ if (value === 1 || value === '1' || value === 'true') return true;
2056
+ if (value === 0 || value === '0' || value === 'false') return false;
2057
+ }
2058
+ return false;
2059
+ }
2060
+
2061
+ function normalizeOrdersPayload(value) {
2062
+ const root = asRecord(value);
2063
+ const body = asRecord(root.data);
2064
+ const source = Object.keys(body).length > 0 ? body : root;
2065
+ const summary = asRecord(source.summary);
2066
+ const rawOrders = Array.isArray(value)
2067
+ ? value
2068
+ : (Array.isArray(source.orders) ? source.orders : (Array.isArray(root.orders) ? root.orders : []));
2069
+ const orders = rawOrders.map((rawOrder, index) => {
2070
+ const order = asRecord(rawOrder);
2071
+ const product = asRecord(order.product);
2072
+ const item = asRecord(order.item);
2073
+ const buyer = asRecord(order.buyer);
2074
+ const customer = asRecord(order.customer);
2075
+ const metadata = asRecord(order.metadata);
2076
+ const firstItem = Array.isArray(order.items) ? asRecord(order.items[0]) : {};
2077
+ const status = firstString(order.status, order.payment_status, order.order_type).toLowerCase();
2078
+ const createdAt = firstString(
2079
+ order.created_at,
2080
+ order.createdAt,
2081
+ order.order_date,
2082
+ order.date,
2083
+ order.paid_at,
2084
+ ) || null;
2085
+
2086
+ return {
2087
+ id: firstString(order.id, order.order_id, order.reference) || `order-${index + 1}`,
2088
+ reference: firstString(order.order_ref, order.reference, order.order_number, order.id) || `order ${index + 1}`,
2089
+ product: firstString(
2090
+ order.product_name,
2091
+ order.product_title,
2092
+ typeof order.product === 'string' ? order.product : null,
2093
+ product.name,
2094
+ product.title,
2095
+ item.name,
2096
+ firstItem.name,
2097
+ firstItem.product_name,
2098
+ metadata.product_name,
2099
+ ) || 'product',
2100
+ quantity: Math.max(1, Math.round(firstNumber(order.quantity, order.qty, firstItem.quantity, 1))),
2101
+ buyer: firstString(
2102
+ order.buyer_name,
2103
+ order.customer_name,
2104
+ buyer.name,
2105
+ customer.name,
2106
+ order.buyer_email,
2107
+ order.customer_email,
2108
+ buyer.email,
2109
+ customer.email,
2110
+ ) || 'guest',
2111
+ status: status || 'unknown',
2112
+ isPreorder: firstBoolean(order.is_preorder, order.preorder)
2113
+ || status.replace(/[_-]/g, '').includes('preorder')
2114
+ || status === 'pending',
2115
+ amountCents: firstNumber(order.amount_cents, order.price_cents, order.total_cents, firstItem.amount_cents),
2116
+ createdAt,
2117
+ };
2118
+ });
2119
+
2120
+ orders.sort((a, b) => {
2121
+ const aTime = a.createdAt ? Date.parse(a.createdAt) : 0;
2122
+ const bTime = b.createdAt ? Date.parse(b.createdAt) : 0;
2123
+ return (Number.isFinite(bTime) ? bTime : 0) - (Number.isFinite(aTime) ? aTime : 0);
2124
+ });
2125
+
2126
+ return {
2127
+ orders,
2128
+ revenueCents: firstNumber(summary.revenue_cents, source.revenue_cents, root.revenue_cents),
2129
+ paidOrders: Math.max(0, Math.round(firstNumber(summary.paid_orders, source.paid_orders, root.paid_orders))),
2130
+ pendingRevenueCents: firstNumber(
2131
+ summary.pending_revenue_cents,
2132
+ source.pending_revenue_cents,
2133
+ root.pending_revenue_cents,
2134
+ ),
2135
+ };
2136
+ }
2137
+
2138
+ function readStorefront(business) {
2139
+ const config = asRecord(asRecord(business).config);
2140
+ const storefront = asRecord(config.storefront);
2141
+ return {
2142
+ enabled: storefront.enabled === true,
2143
+ products: Array.isArray(storefront.products) ? storefront.products.filter(product => product && typeof product === 'object') : [],
2144
+ };
2145
+ }
2146
+
2147
+ function readWalletNet(business) {
2148
+ const config = asRecord(asRecord(business).config);
2149
+ for (const key of ['wallet_balance', 'credits_balance', 'credits', 'wallet_credits']) {
2150
+ const value = config[key];
2151
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
2152
+ }
2153
+ return null;
2154
+ }
2155
+
2156
+ function formatWalletNet(value) {
2157
+ if (value === null) return 'not available';
2158
+ return `$${value.toLocaleString('en-US', { maximumFractionDigits: 2 })}`;
2159
+ }
2160
+
2161
+ function formatCents(value, currency = 'usd') {
2162
+ const amount = firstNumber(value) / 100;
2163
+ const code = firstString(currency).toUpperCase() || 'USD';
2164
+ const formatted = amount.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
2165
+ return code === 'USD' ? `$${formatted}` : `${code} ${formatted}`;
2166
+ }
2167
+
2168
+ function businessOperationError(message) {
2169
+ console.error(message);
2170
+ process.exitCode = 1;
2171
+ return null;
2172
+ }
2173
+
2174
+ async function resolveBusinessOperation(slug, usage) {
2175
+ const requestedSlug = detectBusinessSlug(slug);
2176
+ if (!requestedSlug) return businessOperationError(`No business specified. Usage: ${usage}`);
2177
+
2178
+ const creds = loadCredentials();
2179
+ if (!creds || !creds.token) return businessOperationError('Not logged in. Run: atris login');
2180
+
2181
+ const resolved = await resolveSlug(requestedSlug, creds);
2182
+ if (!resolved) return businessOperationError(`Business "${requestedSlug}" not found.`);
2183
+ return { requestedSlug, resolved, token: creds.token };
2184
+ }
2185
+
2186
+ async function fetchBusinessDashboard(context) {
2187
+ const result = await apiRequestJson(`/business/${context.resolved.business_id}/dashboard`, {
2188
+ method: 'GET',
2189
+ token: context.token,
2190
+ timeoutMs: 120000,
2191
+ });
2192
+ if (!result.ok) {
2193
+ const detail = result.errorMessage || result.error || `HTTP ${result.status}`;
2194
+ return businessOperationError(`Could not load business room: ${detail}`);
2195
+ }
2196
+ return result.data || {};
2197
+ }
2198
+
2199
+ async function fetchBusinessOrders(context, limit = 50) {
2200
+ return apiRequestJson(`/storefront/${context.resolved.business_id}/orders?limit=${limit}`, {
2201
+ method: 'GET',
2202
+ token: context.token,
2203
+ timeoutMs: 120000,
2204
+ });
2205
+ }
2206
+
2207
+ function renderOrderLine(order) {
2208
+ const kind = order.isPreorder ? ', preorder' : '';
2209
+ const when = order.createdAt ? relativeTime(order.createdAt) : 'date unavailable';
2210
+ return ` ${order.reference}: ${order.product} x${order.quantity}, ${order.buyer}, ${when}${kind}`;
2211
+ }
2212
+
2213
+ async function businessRoom(slug) {
2214
+ const context = await resolveBusinessOperation(slug, 'atris business room <slug>');
2215
+ if (!context) return;
2216
+
2217
+ const [dashboard, ordersResult] = await Promise.all([
2218
+ fetchBusinessDashboard(context),
2219
+ fetchBusinessOrders(context, 5),
2220
+ ]);
2221
+ if (!dashboard) return;
2222
+
2223
+ const business = asRecord(dashboard.business);
2224
+ const roster = asRecord(dashboard.roster);
2225
+ const members = Array.isArray(roster.members)
2226
+ ? roster.members
2227
+ : (Array.isArray(business.members) ? business.members : []);
2228
+ const storefront = readStorefront(business);
2229
+ const orders = ordersResult.ok ? normalizeOrdersPayload(ordersResult.data).orders.slice(0, 5) : [];
2230
+ const name = business.name || context.resolved.name || context.requestedSlug;
2231
+ const appCount = Number.isFinite(business.app_count)
2232
+ ? business.app_count
2233
+ : (Array.isArray(business.apps) ? business.apps.length : 0);
2234
+
2235
+ console.log('');
2236
+ console.log(`business room: ${name}`);
2237
+ console.log(`wallet/net: ${formatWalletNet(readWalletNet(business))}`);
2238
+ console.log(`members: ${members.length}`);
2239
+ for (const member of members) {
2240
+ const agent = asRecord(member.atris);
2241
+ const memberName = firstString(member.display_name, agent.agent_name, member.name, member.email) || 'unknown';
2242
+ console.log(` ${memberName} (${member.role || 'member'})`);
2243
+ }
2244
+ console.log(`apps: ${appCount}`);
2245
+ console.log(`store: ${storefront.enabled ? 'enabled' : 'disabled'}, ${storefront.products.length} products`);
2246
+ console.log('recent orders:');
2247
+ if (!ordersResult.ok) {
2248
+ const detail = ordersResult.errorMessage || ordersResult.error || `HTTP ${ordersResult.status}`;
2249
+ console.log(` unavailable: ${detail}`);
2250
+ process.exitCode = 1;
2251
+ } else if (orders.length === 0) {
2252
+ console.log(' none');
2253
+ } else {
2254
+ orders.forEach(order => console.log(renderOrderLine(order)));
2255
+ }
2256
+ console.log('');
2257
+ }
2258
+
2259
+ async function businessProducts(slug) {
2260
+ const context = await resolveBusinessOperation(slug, 'atris business products <slug>');
2261
+ if (!context) return;
2262
+ const dashboard = await fetchBusinessDashboard(context);
2263
+ if (!dashboard) return;
2264
+
2265
+ const business = asRecord(dashboard.business);
2266
+ const storefront = readStorefront(business);
2267
+ const name = business.name || context.resolved.name || context.requestedSlug;
2268
+ console.log('');
2269
+ console.log(`products: ${name} (${storefront.products.length})`);
2270
+ if (storefront.products.length === 0) {
2271
+ console.log(' none');
2272
+ } else {
2273
+ for (const product of storefront.products) {
2274
+ const productName = firstString(product.name) || 'unnamed product';
2275
+ const productId = firstString(product.id) || 'no id';
2276
+ console.log(` ${productName} (${productId}): ${formatCents(product.price_cents, product.currency)}`);
2277
+ }
2278
+ }
2279
+ console.log('');
2280
+ }
2281
+
2282
+ async function businessOrders(slug) {
2283
+ const context = await resolveBusinessOperation(slug, 'atris business orders <slug>');
2284
+ if (!context) return;
2285
+ const result = await fetchBusinessOrders(context, 50);
2286
+ if (!result.ok) {
2287
+ const detail = result.errorMessage || result.error || `HTTP ${result.status}`;
2288
+ businessOperationError(`Could not load orders: ${detail}`);
2289
+ return;
2290
+ }
2291
+
2292
+ const data = normalizeOrdersPayload(result.data);
2293
+ const name = context.resolved.name || context.requestedSlug;
2294
+ console.log('');
2295
+ console.log(`orders: ${name} (${data.orders.length})`);
2296
+ console.log(`revenue: ${formatCents(data.revenueCents)}`);
2297
+ console.log(`paid orders: ${data.paidOrders}`);
2298
+ console.log(`pending revenue: ${formatCents(data.pendingRevenueCents)}`);
2299
+ if (data.orders.length === 0) {
2300
+ console.log(' none');
2301
+ } else {
2302
+ data.orders.forEach(order => console.log(renderOrderLine(order)));
2303
+ }
2304
+ console.log('');
2305
+ }
2306
+
2307
+ function parseBusinessStoreArgs(args = []) {
2308
+ const actions = new Set(['status', 'on', 'enable', 'toggle', 'off', 'disable']);
2309
+ if (actions.has(args[0])) return { action: args[0], slug: args[1] };
2310
+ if (actions.has(args[1])) return { action: args[1], slug: args[0] };
2311
+ return { action: 'status', slug: args[0] };
2312
+ }
2313
+
2314
+ async function businessStore(args = []) {
2315
+ const { action, slug } = parseBusinessStoreArgs(args);
2316
+ if (action === 'off' || action === 'disable') {
2317
+ businessOperationError('The production store API does not support disabling stores yet.');
2318
+ return;
2319
+ }
2320
+
2321
+ const context = await resolveBusinessOperation(slug, 'atris business store <on|status> [slug]');
2322
+ if (!context) return;
2323
+ const dashboard = await fetchBusinessDashboard(context);
2324
+ if (!dashboard) return;
2325
+
2326
+ const business = asRecord(dashboard.business);
2327
+ const storefront = readStorefront(business);
2328
+ const name = business.name || context.resolved.name || context.requestedSlug;
2329
+ if (action === 'status') {
2330
+ console.log(`store: ${storefront.enabled ? 'enabled' : 'disabled'} for ${name}, ${storefront.products.length} products`);
2331
+ return;
2332
+ }
2333
+ if (storefront.enabled) {
2334
+ console.log(`store: already enabled for ${name}, ${storefront.products.length} products`);
2335
+ return;
2336
+ }
2337
+
2338
+ const result = await apiRequestJson(`/storefront/${context.resolved.business_id}/products`, {
2339
+ method: 'PUT',
2340
+ token: context.token,
2341
+ body: { products: storefront.products },
2342
+ timeoutMs: 120000,
2343
+ });
2344
+ if (!result.ok) {
2345
+ const detail = result.errorMessage || result.error || `HTTP ${result.status}`;
2346
+ businessOperationError(`Could not enable store: ${detail}`);
2347
+ return;
2348
+ }
2349
+ const enabledProducts = Array.isArray(result.data?.products) ? result.data.products.length : storefront.products.length;
2350
+ console.log(`store: enabled for ${name}, ${enabledProducts} products`);
2351
+ }
2352
+
2025
2353
  // ---------------------------------------------------------------------------
2026
2354
  // atris business health <slug>
2027
2355
  // ---------------------------------------------------------------------------
@@ -2386,6 +2714,7 @@ async function createBusinessInternal(name, flags = [], mode = 'auto') {
2386
2714
  options.here ||
2387
2715
  Boolean(options.root)
2388
2716
  );
2717
+ let workspaceRoot = null;
2389
2718
 
2390
2719
  // Scaffold legacy local directory if in an atris project
2391
2720
  const atrisDir = !shouldCreateCanonicalWorkspace ? findAtrisDir() : null;
@@ -2405,7 +2734,7 @@ async function createBusinessInternal(name, flags = [], mode = 'auto') {
2405
2734
  console.log(` Local scaffold: ${bizDir}/`);
2406
2735
  }
2407
2736
  } else if (shouldCreateCanonicalWorkspace) {
2408
- const workspaceRoot = resolveWorkspaceRoot(biz.slug, options);
2737
+ workspaceRoot = resolveWorkspaceRoot(biz.slug, options);
2409
2738
  const scaffold = createCanonicalBusinessWorkspace(workspaceRoot, {
2410
2739
  business_id: biz.id,
2411
2740
  workspace_id: biz.workspace_id,
@@ -2446,10 +2775,10 @@ async function createBusinessInternal(name, flags = [], mode = 'auto') {
2446
2775
  console.log(` Agent: ${biz.agent_id || '(none)'}`);
2447
2776
  console.log(` Dashboard: https://atris.ai/dashboard/gm/${biz.id}`);
2448
2777
  if (shouldCreateCanonicalWorkspace) {
2449
- const workspaceRoot = resolveWorkspaceRoot(biz.slug, options);
2450
2778
  console.log(renderBusinessCreatedNextSteps(biz, workspaceRoot));
2451
2779
  }
2452
2780
  console.log('');
2781
+ return { business: biz, workspaceRoot };
2453
2782
  }
2454
2783
 
2455
2784
  async function createBusiness(name, ...flags) {
@@ -2875,6 +3204,96 @@ function findAtrisDir() {
2875
3204
  return null;
2876
3205
  }
2877
3206
 
3207
+ function printBusinessSimulateHelp() {
3208
+ console.log(SIMULATE_USAGE);
3209
+ console.log('');
3210
+ console.log('Creates a business workspace, four simulation members, their missions, and atris/ENDGAME.md.');
3211
+ console.log('Use --dry-run to preview every simulation file without cloud calls or writes.');
3212
+ }
3213
+
3214
+ async function simulateBusiness(args = [], dependencies = {}) {
3215
+ const options = parseSimulationArgs(args);
3216
+ const createdAt = new Date().toISOString();
3217
+ const preview = buildSimulationPlan({
3218
+ idea: options.idea,
3219
+ businessName: options.businessName,
3220
+ roleNames: options.roleNames,
3221
+ createdAt,
3222
+ });
3223
+
3224
+ if (options.dryRun) {
3225
+ console.log(renderDryRun(preview));
3226
+ return preview;
3227
+ }
3228
+
3229
+ const init = dependencies.initBusinessWorkspace || initBusinessWorkspace;
3230
+ const runMemberCommand = dependencies.memberCommand || require('./member').memberCommand;
3231
+ const initialized = await init(options.businessName);
3232
+ const business = initialized?.business || {};
3233
+ const workspaceRoot = initialized?.workspaceRoot
3234
+ || resolveWorkspaceRoot(business.slug || options.businessSlug, {});
3235
+ const plan = buildSimulationPlan({
3236
+ idea: options.idea,
3237
+ businessName: business.name || options.businessName,
3238
+ businessSlug: business.slug || options.businessSlug,
3239
+ roleNames: options.roleNames,
3240
+ createdAt,
3241
+ workspacePath: workspaceRoot,
3242
+ });
3243
+
3244
+ const businessJson = path.join(workspaceRoot, '.atris', 'business.json');
3245
+ if (!fs.existsSync(businessJson)) {
3246
+ throw new Error(`Business init did not create the expected workspace: ${workspaceRoot}`);
3247
+ }
3248
+
3249
+ const originalCwd = process.cwd();
3250
+ try {
3251
+ process.chdir(workspaceRoot);
3252
+
3253
+ const endgame = plan.files.find((file) => file.kind === 'endgame');
3254
+ fs.writeFileSync(path.join(workspaceRoot, endgame.path), endgame.content, 'utf8');
3255
+
3256
+ for (const role of plan.roles) {
3257
+ const memberDir = path.join(workspaceRoot, 'atris', 'team', role.name);
3258
+ const memberFile = path.join(memberDir, 'MEMBER.md');
3259
+ if (!fs.existsSync(memberFile)) {
3260
+ await runMemberCommand(
3261
+ 'create',
3262
+ role.name,
3263
+ `--role=${role.title}`,
3264
+ `--description=${role.description}`,
3265
+ );
3266
+ } else {
3267
+ console.log(`Using existing team/${role.name}/ scaffold from business init.`);
3268
+ }
3269
+
3270
+ for (const directory of ['skills', 'tools', 'context', 'logs']) {
3271
+ fs.mkdirSync(path.join(memberDir, directory), { recursive: true });
3272
+ }
3273
+
3274
+ const roleFiles = plan.files.filter((file) => file.role?.name === role.name);
3275
+ for (const file of roleFiles) {
3276
+ fs.writeFileSync(path.join(workspaceRoot, file.path), file.content, 'utf8');
3277
+ }
3278
+
3279
+ await runMemberCommand('push', role.name);
3280
+ }
3281
+ } finally {
3282
+ process.chdir(originalCwd);
3283
+ }
3284
+
3285
+ console.log('');
3286
+ console.log('business simulation ready');
3287
+ console.log(`workspace: ${workspaceRoot}`);
3288
+ console.log('roster:');
3289
+ for (const role of plan.roles) {
3290
+ console.log(` - ${role.name}: ${role.role}`);
3291
+ }
3292
+ console.log('next: open an agent session in the workspace and run the loop against atris/ENDGAME.md');
3293
+ console.log('');
3294
+ return plan;
3295
+ }
3296
+
2878
3297
 
2879
3298
  async function quickstart() {
2880
3299
  console.log(`
@@ -2945,8 +3364,13 @@ function printBusinessHelp() {
2945
3364
  console.log(' init <name> RECOMMENDED: create a business environment (cloud + local)');
2946
3365
  console.log(' workspace <name> Alias for init');
2947
3366
  console.log(' create <name> Cloud-only business record; add --workspace to also scaffold local');
3367
+ console.log(' simulate <idea> Create a business, four-role team, missions, and endgame loop');
2948
3368
  console.log(' add <slug> Register an existing cloud business');
2949
3369
  console.log(' list Show registered businesses');
3370
+ console.log(' room [slug] Show wallet, members, apps, store, and recent orders');
3371
+ console.log(' products [slug] List store products');
3372
+ console.log(' orders [slug] List store orders');
3373
+ console.log(' store on [slug] Enable the store with its current catalog');
2950
3374
  console.log(' team [slug] Show members, roles, and admin access');
2951
3375
  console.log(' status <slug> Quick status check');
2952
3376
  console.log(' health [slug] Full health dashboard');
@@ -2973,6 +3397,10 @@ async function businessCommand(subcommand, ...args) {
2973
3397
  printBusinessHelp();
2974
3398
  return;
2975
3399
  }
3400
+ if (subcommand === 'simulate' && args.some(isHelpToken)) {
3401
+ printBusinessSimulateHelp();
3402
+ return;
3403
+ }
2976
3404
  const nameTakingSubcommands = new Set(['create', 'new', 'init', 'workspace']);
2977
3405
  if (nameTakingSubcommands.has(subcommand) && args.some(isHelpToken)) {
2978
3406
  printBusinessHelp();
@@ -2995,6 +3423,9 @@ async function businessCommand(subcommand, ...args) {
2995
3423
  case 'workspace':
2996
3424
  await initBusinessWorkspace(args[0], ...args.slice(1));
2997
3425
  break;
3426
+ case 'simulate':
3427
+ await simulateBusiness(args);
3428
+ break;
2998
3429
  case 'list':
2999
3430
  case 'ls': {
3000
3431
  const opts = {};
@@ -3017,6 +3448,21 @@ async function businessCommand(subcommand, ...args) {
3017
3448
  case 'health':
3018
3449
  await businessHealth(args[0]);
3019
3450
  break;
3451
+ case 'room':
3452
+ await businessRoom(args[0]);
3453
+ break;
3454
+ case 'products':
3455
+ case 'product':
3456
+ await businessProducts(args[0]);
3457
+ break;
3458
+ case 'orders':
3459
+ case 'order':
3460
+ await businessOrders(args[0]);
3461
+ break;
3462
+ case 'store':
3463
+ case 'storefront':
3464
+ await businessStore(args);
3465
+ break;
3020
3466
  case 'team':
3021
3467
  case 'members':
3022
3468
  case 'roster':
@@ -3072,26 +3518,20 @@ async function businessCommand(subcommand, ...args) {
3072
3518
 
3073
3519
  module.exports = {
3074
3520
  businessCommand,
3075
- businessHealth,
3076
- businessAudit,
3077
- businessDoctor,
3078
- businessTeam,
3079
3521
  loadBusinesses,
3080
3522
  saveBusinesses,
3081
- getBusinessConfigPath,
3082
3523
  businessMatchesSlug,
3083
3524
  isAccidentalHelpBusiness,
3084
3525
  validateBusinessCreateName,
3085
3526
  analyzeBusinessDoctor,
3086
- readBusinessFolderBindings,
3087
3527
  createCanonicalBusinessWorkspace,
3088
- initBusinessWorkspace,
3089
3528
  onboardBusiness,
3090
- startBusinessWorkspace,
3091
3529
  shareBusinessWorkspace,
3092
3530
  collectBusinessShareState,
3093
3531
  renderBusinessCreatedNextSteps,
3094
- renderBusinessShareHandoff,
3095
- renderBusinessStartCard,
3096
3532
  recordBusinessRun,
3533
+ normalizeOrdersPayload,
3534
+ parseBusinessStoreArgs,
3535
+ readStorefront,
3536
+ readWalletNet,
3097
3537
  };
@@ -1,12 +1,10 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const { hasFlag } = require('../lib/arg-parser');
3
4
  const { scanChatLogs, writeLatestScan } = require('../lib/chat-log-scan');
4
5
 
5
- function hasFlag(args, name) {
6
- return args.includes(name);
7
- }
8
-
9
- function readFlag(args, name, fallback) {
6
+ // Preserve the existing rule that the next token is a value, even if it is a flag.
7
+ function readFollowingFlag(args, name, fallback) {
10
8
  const i = args.indexOf(name);
11
9
  if (i === -1 || i === args.length - 1) return fallback;
12
10
  return args[i + 1];
@@ -79,8 +77,8 @@ Options:
79
77
 
80
78
  const report = scanChatLogs({
81
79
  cwd: root,
82
- hours: readFlag(args, '--hours', 720),
83
- limit: readFlag(args, '--limit', 12),
80
+ hours: readFollowingFlag(args, '--hours', 720),
81
+ limit: readFollowingFlag(args, '--limit', 12),
84
82
  });
85
83
 
86
84
  let latestPath = null;