atris 3.1.0 → 3.5.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.
- package/GETTING_STARTED.md +65 -131
- package/README.md +29 -4
- package/atris/GETTING_STARTED.md +65 -131
- package/atris/PERSONA.md +5 -1
- package/atris/atris.md +122 -153
- package/atris/skills/aeo/SKILL.md +117 -0
- package/atris/skills/atris/SKILL.md +49 -25
- package/atris/skills/create-member/SKILL.md +29 -9
- package/atris/skills/endgame/SKILL.md +9 -0
- package/atris/skills/improve/SKILL.md +2 -2
- package/atris/skills/research-search/SKILL.md +167 -0
- package/atris/skills/research-search/arxiv_search.py +157 -0
- package/atris/skills/research-search/program.md +48 -0
- package/atris/skills/research-search/results.tsv +6 -0
- package/atris/skills/research-search/scholar_search.py +154 -0
- package/atris/skills/tidy/SKILL.md +36 -21
- package/atris/team/_template/MEMBER.md +2 -0
- package/atris/team/validator/MEMBER.md +35 -1
- package/atris.md +118 -178
- package/bin/atris.js +37 -6
- package/cli/__pycache__/atris_code.cpython-314.pyc +0 -0
- package/cli/__pycache__/runtime_guard.cpython-312.pyc +0 -0
- package/cli/__pycache__/runtime_guard.cpython-314.pyc +0 -0
- package/cli/atris_code.py +889 -0
- package/cli/runtime_guard.py +693 -0
- package/commands/align.js +15 -0
- package/commands/app.js +316 -0
- package/commands/autopilot.js +948 -42
- package/commands/business.js +691 -11
- package/commands/computer.js +1979 -43
- package/commands/context-sync.js +5 -0
- package/commands/experiments.js +1 -1
- package/commands/lifecycle.js +12 -0
- package/commands/plugin.js +24 -0
- package/commands/pull.js +40 -1
- package/commands/push.js +44 -0
- package/commands/release.js +183 -0
- package/commands/research.js +52 -0
- package/commands/serve.js +1 -0
- package/commands/sync.js +372 -87
- package/commands/verify.js +53 -4
- package/commands/wiki.js +71 -26
- package/lib/file-ops.js +13 -1
- package/lib/journal.js +23 -0
- package/lib/reward-config.js +24 -0
- package/lib/scorecard.js +58 -6
- package/lib/sync-telemetry.js +59 -0
- package/lib/todo.js +6 -0
- package/lib/wiki.js +235 -60
- package/package.json +4 -2
- package/utils/api.js +19 -0
- package/utils/auth.js +25 -1
- package/utils/config.js +24 -0
- package/utils/update-check.js +16 -0
package/commands/business.js
CHANGED
|
@@ -3,7 +3,8 @@ const path = require('path');
|
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const { loadCredentials } = require('../utils/auth');
|
|
5
5
|
const { apiRequestJson } = require('../utils/api');
|
|
6
|
-
const { syncBusinessCanonical } = require('./sync');
|
|
6
|
+
const { syncBusinessCanonical, ensureWorkspaceStateFiles } = require('./sync');
|
|
7
|
+
const { ensureContextScaffold, writeWikiStatus, appendWikiLog } = require('../lib/wiki');
|
|
7
8
|
|
|
8
9
|
function getBusinessConfigPath() {
|
|
9
10
|
const home = require('os').homedir();
|
|
@@ -88,6 +89,7 @@ function createCanonicalBusinessWorkspace(targetRoot, bizMeta, options = {}) {
|
|
|
88
89
|
throw new Error(`Target already contains .atris/business.json: ${targetRoot}`);
|
|
89
90
|
}
|
|
90
91
|
|
|
92
|
+
const workspaceTemplate = options.templateName || bizMeta.workspace_template || 'business';
|
|
91
93
|
fs.mkdirSync(atrisMetaDir, { recursive: true });
|
|
92
94
|
fs.writeFileSync(businessJsonPath, JSON.stringify({
|
|
93
95
|
business_id: bizMeta.business_id,
|
|
@@ -95,11 +97,669 @@ function createCanonicalBusinessWorkspace(targetRoot, bizMeta, options = {}) {
|
|
|
95
97
|
name: bizMeta.name,
|
|
96
98
|
slug: bizMeta.slug,
|
|
97
99
|
owner_email: bizMeta.owner_email || '',
|
|
100
|
+
workspace_template: workspaceTemplate,
|
|
98
101
|
created_at: new Date().toISOString(),
|
|
99
102
|
}, null, 2));
|
|
100
103
|
|
|
101
|
-
syncBusinessCanonical(targetRoot, bizMeta, { force: false, dryRun: false });
|
|
102
|
-
return { targetRoot, businessJsonPath };
|
|
104
|
+
syncBusinessCanonical(targetRoot, bizMeta, { force: false, dryRun: false, templateName: workspaceTemplate });
|
|
105
|
+
return { targetRoot, businessJsonPath, workspaceTemplate };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function parseRecordFlags(args, cwd = process.cwd()) {
|
|
109
|
+
const options = {
|
|
110
|
+
cwd,
|
|
111
|
+
reportPath: null,
|
|
112
|
+
summary: '',
|
|
113
|
+
metric: '',
|
|
114
|
+
outcome: 'recorded',
|
|
115
|
+
reward: null,
|
|
116
|
+
loop: 'manual',
|
|
117
|
+
actor: 'operator',
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
for (let i = 0; i < args.length; i++) {
|
|
121
|
+
const arg = args[i];
|
|
122
|
+
const next = args[i + 1];
|
|
123
|
+
|
|
124
|
+
if ((arg === '--summary' || arg === '-s') && next) {
|
|
125
|
+
options.summary = next;
|
|
126
|
+
i++;
|
|
127
|
+
} else if ((arg === '--metric' || arg === '-m') && next) {
|
|
128
|
+
options.metric = next;
|
|
129
|
+
i++;
|
|
130
|
+
} else if ((arg === '--outcome' || arg === '-o') && next) {
|
|
131
|
+
options.outcome = next;
|
|
132
|
+
i++;
|
|
133
|
+
} else if ((arg === '--reward' || arg === '-r') && next) {
|
|
134
|
+
options.reward = next;
|
|
135
|
+
i++;
|
|
136
|
+
} else if (arg === '--loop' && next) {
|
|
137
|
+
options.loop = next;
|
|
138
|
+
i++;
|
|
139
|
+
} else if (arg === '--actor' && next) {
|
|
140
|
+
options.actor = next;
|
|
141
|
+
i++;
|
|
142
|
+
} else if (!arg.startsWith('-') && !options.reportPath) {
|
|
143
|
+
options.reportPath = arg;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return options;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function parseOnboardFlags(args, cwd = process.cwd()) {
|
|
151
|
+
const options = {
|
|
152
|
+
cwd,
|
|
153
|
+
name: '',
|
|
154
|
+
website: '',
|
|
155
|
+
links: [],
|
|
156
|
+
notes: [],
|
|
157
|
+
sources: [],
|
|
158
|
+
contactName: '',
|
|
159
|
+
contactEmail: '',
|
|
160
|
+
contactRole: '',
|
|
161
|
+
};
|
|
162
|
+
const freeform = [];
|
|
163
|
+
|
|
164
|
+
for (let i = 0; i < args.length; i++) {
|
|
165
|
+
const arg = args[i];
|
|
166
|
+
const next = args[i + 1];
|
|
167
|
+
|
|
168
|
+
if ((arg === '--name' || arg === '--business') && next) {
|
|
169
|
+
options.name = next;
|
|
170
|
+
i++;
|
|
171
|
+
} else if ((arg === '--website' || arg === '--site') && next) {
|
|
172
|
+
options.website = next;
|
|
173
|
+
i++;
|
|
174
|
+
} else if ((arg === '--link' || arg === '--url') && next) {
|
|
175
|
+
options.links.push(next);
|
|
176
|
+
i++;
|
|
177
|
+
} else if ((arg === '--from' || arg === '--source') && next) {
|
|
178
|
+
options.sources.push(next);
|
|
179
|
+
i++;
|
|
180
|
+
} else if ((arg === '--note' || arg === '--notes') && next) {
|
|
181
|
+
options.notes.push(next);
|
|
182
|
+
i++;
|
|
183
|
+
} else if ((arg === '--contact' || arg === '--person') && next) {
|
|
184
|
+
options.contactName = next;
|
|
185
|
+
i++;
|
|
186
|
+
} else if (arg === '--email' && next) {
|
|
187
|
+
options.contactEmail = next;
|
|
188
|
+
i++;
|
|
189
|
+
} else if (arg === '--role' && next) {
|
|
190
|
+
options.contactRole = next;
|
|
191
|
+
i++;
|
|
192
|
+
} else if (!arg.startsWith('-')) {
|
|
193
|
+
const resolved = path.resolve(cwd, arg);
|
|
194
|
+
if (/^https?:\/\//i.test(arg)) {
|
|
195
|
+
if (!options.website) options.website = arg;
|
|
196
|
+
else options.links.push(arg);
|
|
197
|
+
} else if (fs.existsSync(resolved)) {
|
|
198
|
+
options.sources.push(arg);
|
|
199
|
+
} else {
|
|
200
|
+
freeform.push(arg);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (freeform.length > 0) {
|
|
206
|
+
options.notes.push(freeform.join(' '));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return options;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function readWorkspaceBusinessMeta(cwd = process.cwd()) {
|
|
213
|
+
const bizFile = path.join(cwd, '.atris', 'business.json');
|
|
214
|
+
if (!fs.existsSync(bizFile)) {
|
|
215
|
+
throw new Error('Run this command inside a business environment with .atris/business.json.');
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
return JSON.parse(fs.readFileSync(bizFile, 'utf8'));
|
|
219
|
+
} catch (error) {
|
|
220
|
+
throw new Error(`Failed to read .atris/business.json: ${error.message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function resolveWorkspaceReport(cwd, reportPath) {
|
|
225
|
+
if (!reportPath) {
|
|
226
|
+
throw new Error('Usage: atris business record <report-path> [--summary "text"] [--metric name] [--outcome positive|mixed|negative] [--reward N]');
|
|
227
|
+
}
|
|
228
|
+
const absPath = path.resolve(cwd, reportPath);
|
|
229
|
+
if (!fs.existsSync(absPath) || !fs.statSync(absPath).isFile()) {
|
|
230
|
+
throw new Error(`Report not found: ${reportPath}`);
|
|
231
|
+
}
|
|
232
|
+
const relPath = path.relative(cwd, absPath).replace(/\\/g, '/');
|
|
233
|
+
return { absPath, relPath };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function extractReportTitle(content, absPath) {
|
|
237
|
+
const heading = String(content || '').match(/^#\s+(.+)$/m);
|
|
238
|
+
if (heading) return heading[1].trim();
|
|
239
|
+
return path.basename(absPath, path.extname(absPath));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function normalizeOutcome(value) {
|
|
243
|
+
const normalized = String(value || 'recorded').trim().toLowerCase();
|
|
244
|
+
if (['positive', 'win', 'success', 'improved'].includes(normalized)) return 'positive';
|
|
245
|
+
if (['negative', 'loss', 'failed', 'regressed'].includes(normalized)) return 'negative';
|
|
246
|
+
if (['mixed', 'partial', 'unclear'].includes(normalized)) return 'mixed';
|
|
247
|
+
return 'recorded';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function defaultRewardForOutcome(outcome) {
|
|
251
|
+
if (outcome === 'positive') return 5;
|
|
252
|
+
if (outcome === 'negative') return -3;
|
|
253
|
+
if (outcome === 'mixed') return 1;
|
|
254
|
+
return 0;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function appendJsonl(filePath, record) {
|
|
258
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
259
|
+
fs.appendFileSync(filePath, `${JSON.stringify(record)}\n`, 'utf8');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function slugifyName(value) {
|
|
263
|
+
return String(value || '')
|
|
264
|
+
.toLowerCase()
|
|
265
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
266
|
+
.replace(/^-+|-+$/g, '') || 'item';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function upsertIndexEntry(indexPath, sectionName, relativePath, description) {
|
|
270
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
271
|
+
const entryLine = `- [[${normalizedPath}]] - ${description}`;
|
|
272
|
+
let lines = fs.readFileSync(indexPath, 'utf8').split('\n');
|
|
273
|
+
const existingIndex = lines.findIndex((line) => line.includes(`[[${normalizedPath}]]`));
|
|
274
|
+
if (existingIndex >= 0) {
|
|
275
|
+
lines[existingIndex] = entryLine;
|
|
276
|
+
fs.writeFileSync(indexPath, `${lines.join('\n').replace(/\n*$/, '\n')}`, 'utf8');
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const header = `## ${sectionName}`;
|
|
281
|
+
const sectionIndex = lines.findIndex((line) => line.trim() === header);
|
|
282
|
+
if (sectionIndex === -1) return;
|
|
283
|
+
|
|
284
|
+
let insertAt = sectionIndex + 1;
|
|
285
|
+
while (insertAt < lines.length && !/^##\s+/.test(lines[insertAt])) {
|
|
286
|
+
insertAt++;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
lines.splice(insertAt, 0, entryLine);
|
|
290
|
+
fs.writeFileSync(indexPath, `${lines.join('\n').replace(/\n*$/, '\n')}`, 'utf8');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function writeMarkdownWithFrontmatter(filePath, frontmatter, body) {
|
|
294
|
+
const yaml = Object.entries(frontmatter).map(([key, value]) => {
|
|
295
|
+
if (Array.isArray(value)) {
|
|
296
|
+
return `${key}:\n${value.map((item) => ` - ${item}`).join('\n')}`;
|
|
297
|
+
}
|
|
298
|
+
return `${key}: ${value}`;
|
|
299
|
+
}).join('\n');
|
|
300
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
301
|
+
fs.writeFileSync(filePath, `---\n${yaml}\n---\n\n${body.trim()}\n`, 'utf8');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function walkOnboardingFiles(dir, options = {}) {
|
|
305
|
+
const skipDirs = new Set(['.git', '.atris', 'atris', '_ingest', 'node_modules', 'dist', 'build', 'coverage', '.next']);
|
|
306
|
+
const allowedExt = new Set(['.md', '.txt', '.pdf', '.csv', '.json', '.html', '.htm', '.docx', '.xlsx', '.png', '.jpg', '.jpeg']);
|
|
307
|
+
const maxFiles = options.maxFiles || 25;
|
|
308
|
+
const output = [];
|
|
309
|
+
|
|
310
|
+
function walk(currentDir) {
|
|
311
|
+
if (!fs.existsSync(currentDir) || output.length >= maxFiles) return;
|
|
312
|
+
for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
|
|
313
|
+
if (output.length >= maxFiles) break;
|
|
314
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
315
|
+
if (entry.isDirectory()) {
|
|
316
|
+
if (skipDirs.has(entry.name)) continue;
|
|
317
|
+
walk(fullPath);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
if (!entry.isFile()) continue;
|
|
321
|
+
if (entry.name.startsWith('.')) continue;
|
|
322
|
+
if (!allowedExt.has(path.extname(entry.name).toLowerCase())) continue;
|
|
323
|
+
output.push(fullPath);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
walk(dir);
|
|
328
|
+
return output;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function extractUrlsFromText(text) {
|
|
332
|
+
return Array.from(new Set((String(text || '').match(/https?:\/\/[^\s)<>"']+/g) || []).map((item) => item.replace(/[.,]$/, ''))));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function isTextLike(filePath) {
|
|
336
|
+
return new Set(['.md', '.txt', '.json', '.csv', '.html', '.htm']).has(path.extname(filePath).toLowerCase());
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function readSmallText(filePath, maxBytes = 200000) {
|
|
340
|
+
try {
|
|
341
|
+
const stat = fs.statSync(filePath);
|
|
342
|
+
if (stat.size > maxBytes || !isTextLike(filePath)) return null;
|
|
343
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
344
|
+
} catch {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function discoverOnboardingSignals(cwd, options = {}) {
|
|
350
|
+
const explicitSourcePaths = (options.sources || [])
|
|
351
|
+
.map((value) => path.resolve(cwd, value))
|
|
352
|
+
.filter((fullPath) => fs.existsSync(fullPath));
|
|
353
|
+
|
|
354
|
+
const rootCandidates = walkOnboardingFiles(cwd, { maxFiles: 20 })
|
|
355
|
+
.filter((fullPath) => {
|
|
356
|
+
const relative = path.relative(cwd, fullPath).replace(/\\/g, '/');
|
|
357
|
+
return !relative.startsWith('atris/') && !relative.startsWith('.atris/');
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const contextDir = path.join(cwd, 'atris', 'context');
|
|
361
|
+
const contextCandidates = walkOnboardingFiles(contextDir, { maxFiles: 20 })
|
|
362
|
+
.filter((fullPath) => {
|
|
363
|
+
const relative = path.relative(contextDir, fullPath).replace(/\\/g, '/');
|
|
364
|
+
return !relative.startsWith('_ingest/') && relative !== 'README.md' && relative !== 'live-workspace.md';
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const sourcePaths = Array.from(new Set([...explicitSourcePaths, ...rootCandidates, ...contextCandidates]));
|
|
368
|
+
const urls = new Set([options.website, ...(options.links || [])].filter(Boolean));
|
|
369
|
+
|
|
370
|
+
for (const note of options.notes || []) {
|
|
371
|
+
for (const url of extractUrlsFromText(note)) urls.add(url);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
for (const sourcePath of sourcePaths) {
|
|
375
|
+
const text = readSmallText(sourcePath);
|
|
376
|
+
if (!text) continue;
|
|
377
|
+
for (const url of extractUrlsFromText(text)) urls.add(url);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
website: options.website || Array.from(urls)[0] || '',
|
|
382
|
+
urls: Array.from(urls),
|
|
383
|
+
sourcePaths,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function stageOnboardingSources(cwd, packDir, sourcePaths = []) {
|
|
388
|
+
const stagedDir = path.join(packDir, 'sources');
|
|
389
|
+
fs.mkdirSync(stagedDir, { recursive: true });
|
|
390
|
+
const stagedEntries = [];
|
|
391
|
+
let counter = 0;
|
|
392
|
+
|
|
393
|
+
for (const sourcePath of sourcePaths) {
|
|
394
|
+
if (!fs.existsSync(sourcePath)) continue;
|
|
395
|
+
counter += 1;
|
|
396
|
+
const baseName = path.basename(sourcePath);
|
|
397
|
+
const targetPath = path.join(stagedDir, `${String(counter).padStart(2, '0')}-${baseName}`);
|
|
398
|
+
const stat = fs.statSync(sourcePath);
|
|
399
|
+
if (stat.isDirectory()) {
|
|
400
|
+
fs.cpSync(sourcePath, targetPath, { recursive: true });
|
|
401
|
+
} else {
|
|
402
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
403
|
+
}
|
|
404
|
+
stagedEntries.push({
|
|
405
|
+
original: path.relative(cwd, sourcePath).replace(/\\/g, '/'),
|
|
406
|
+
staged: path.relative(cwd, targetPath).replace(/\\/g, '/'),
|
|
407
|
+
kind: stat.isDirectory() ? 'directory' : 'file',
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return stagedEntries;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function suggestStarterAction(signals) {
|
|
415
|
+
if (signals.contactEmail && signals.website) {
|
|
416
|
+
return {
|
|
417
|
+
title: 'Draft a founder-context note',
|
|
418
|
+
action: `Write a short note to ${signals.contactName || 'the contact'} that reflects the website, asks for the current priority, and proposes one concrete first loop.`,
|
|
419
|
+
why: 'This is the shortest safe path to real feedback from a named human.',
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
if (signals.website) {
|
|
423
|
+
return {
|
|
424
|
+
title: 'Map the offer into one loop',
|
|
425
|
+
action: 'Read the website and turn it into one measurable workflow with a clear reward signal.',
|
|
426
|
+
why: 'A website is enough to define a first useful business loop without waiting for perfect intake.',
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
if ((signals.sourceEntries || []).length > 0) {
|
|
430
|
+
return {
|
|
431
|
+
title: 'Extract the first workflow from local evidence',
|
|
432
|
+
action: 'Read the strongest local source, summarize what the company does, and choose one workflow worth operationalizing first.',
|
|
433
|
+
why: 'Local evidence is already better than a blank template and can anchor the first action.',
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
title: 'Collect one anchor signal',
|
|
438
|
+
action: 'Get one website, one named human, or one source doc so the environment can stop guessing.',
|
|
439
|
+
why: 'The system can work from partial input, but it still needs one concrete anchor.',
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
async function onboardBusiness(...flags) {
|
|
444
|
+
const options = parseOnboardFlags(flags, process.cwd());
|
|
445
|
+
const cwd = options.cwd || process.cwd();
|
|
446
|
+
|
|
447
|
+
const bizFile = path.join(cwd, '.atris', 'business.json');
|
|
448
|
+
if (!fs.existsSync(bizFile) && options.name) {
|
|
449
|
+
const slug = slugifyName(options.name);
|
|
450
|
+
createCanonicalBusinessWorkspace(cwd, {
|
|
451
|
+
business_id: '',
|
|
452
|
+
workspace_id: '',
|
|
453
|
+
name: options.name,
|
|
454
|
+
slug,
|
|
455
|
+
owner_email: '',
|
|
456
|
+
workspace_template: 'business',
|
|
457
|
+
}, { here: true });
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const bizMeta = readWorkspaceBusinessMeta(cwd);
|
|
461
|
+
|
|
462
|
+
ensureWorkspaceStateFiles(cwd, {
|
|
463
|
+
slug: bizMeta.slug || 'business',
|
|
464
|
+
business_id: bizMeta.business_id || '',
|
|
465
|
+
workspace_id: bizMeta.workspace_id || '',
|
|
466
|
+
workspace_template: bizMeta.workspace_template || 'business',
|
|
467
|
+
}, { dryRun: false });
|
|
468
|
+
|
|
469
|
+
const contextDir = ensureContextScaffold(cwd, 'public');
|
|
470
|
+
const stamp = new Date().toISOString().replace(/[:]/g, '-').slice(0, 16);
|
|
471
|
+
const packDir = path.join(contextDir, '_ingest', `${stamp}-onboarding`);
|
|
472
|
+
fs.mkdirSync(packDir, { recursive: true });
|
|
473
|
+
|
|
474
|
+
const discovered = discoverOnboardingSignals(cwd, options);
|
|
475
|
+
const stagedSources = stageOnboardingSources(cwd, packDir, discovered.sourcePaths);
|
|
476
|
+
const links = Array.from(new Set([discovered.website, ...discovered.urls].filter(Boolean)));
|
|
477
|
+
const starterAction = suggestStarterAction({
|
|
478
|
+
website: discovered.website,
|
|
479
|
+
contactName: options.contactName,
|
|
480
|
+
contactEmail: options.contactEmail,
|
|
481
|
+
sourceEntries: stagedSources,
|
|
482
|
+
});
|
|
483
|
+
const intakeLines = [
|
|
484
|
+
`# ${bizMeta.name} Onboarding Intake`,
|
|
485
|
+
'',
|
|
486
|
+
`- Business: ${bizMeta.name}`,
|
|
487
|
+
`- Slug: ${bizMeta.slug}`,
|
|
488
|
+
discovered.website ? `- Website: ${discovered.website}` : null,
|
|
489
|
+
options.contactName ? `- Contact: ${options.contactName}` : null,
|
|
490
|
+
options.contactRole ? `- Contact role: ${options.contactRole}` : null,
|
|
491
|
+
options.contactEmail ? `- Contact email: ${options.contactEmail}` : null,
|
|
492
|
+
'',
|
|
493
|
+
'## Notes',
|
|
494
|
+
...(options.notes.length > 0 ? options.notes.map((note) => `- ${note}`) : ['- No notes captured yet.']),
|
|
495
|
+
'',
|
|
496
|
+
'## Discovered Sources',
|
|
497
|
+
...(stagedSources.length > 0 ? stagedSources.map((entry) => `- ${entry.original} -> ${entry.staged}`) : ['- No local files discovered yet.']),
|
|
498
|
+
'',
|
|
499
|
+
'## Links',
|
|
500
|
+
...(links.length > 0 ? links.map((link) => `- ${link}`) : ['- No links captured yet.']),
|
|
501
|
+
].filter(Boolean);
|
|
502
|
+
const intakePath = path.join(packDir, 'intake.md');
|
|
503
|
+
fs.writeFileSync(intakePath, `${intakeLines.join('\n')}\n`, 'utf8');
|
|
504
|
+
|
|
505
|
+
const linksPath = path.join(packDir, 'links.txt');
|
|
506
|
+
fs.writeFileSync(linksPath, `${links.join('\n')}${links.length > 0 ? '\n' : ''}`, 'utf8');
|
|
507
|
+
const sourcesPath = path.join(packDir, 'sources.txt');
|
|
508
|
+
fs.writeFileSync(
|
|
509
|
+
sourcesPath,
|
|
510
|
+
`${stagedSources.map((entry) => `${entry.original} -> ${entry.staged}`).join('\n')}${stagedSources.length > 0 ? '\n' : ''}`,
|
|
511
|
+
'utf8'
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
const intakeRel = path.relative(cwd, intakePath).replace(/\\/g, '/');
|
|
515
|
+
const linksRel = path.relative(cwd, linksPath).replace(/\\/g, '/');
|
|
516
|
+
const sourcesRel = path.relative(cwd, sourcesPath).replace(/\\/g, '/');
|
|
517
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
518
|
+
|
|
519
|
+
const briefSlug = `${bizMeta.slug}-starter-brief`;
|
|
520
|
+
const briefPath = path.join(cwd, 'atris', 'wiki', 'briefs', `${briefSlug}.md`);
|
|
521
|
+
writeMarkdownWithFrontmatter(briefPath, {
|
|
522
|
+
type: 'brief',
|
|
523
|
+
slug: briefSlug,
|
|
524
|
+
title: `${bizMeta.name} Starter Brief`,
|
|
525
|
+
sources: [intakeRel, linksRel, sourcesRel],
|
|
526
|
+
last_compiled: today,
|
|
527
|
+
created: today,
|
|
528
|
+
updated: today,
|
|
529
|
+
tags: ['business', 'onboarding', 'starter'],
|
|
530
|
+
}, `
|
|
531
|
+
# ${bizMeta.name} Starter Brief
|
|
532
|
+
|
|
533
|
+
## What We Know
|
|
534
|
+
|
|
535
|
+
- Website: ${discovered.website || 'unknown'}
|
|
536
|
+
- Contact: ${options.contactName || 'unknown'}
|
|
537
|
+
- Contact role: ${options.contactRole || 'unknown'}
|
|
538
|
+
- Contact email: ${options.contactEmail || 'unknown'}
|
|
539
|
+
${options.notes.map((note) => `- Note: ${note}`).join('\n') || '- Notes: none captured yet'}
|
|
540
|
+
${stagedSources.length > 0 ? `- Local sources discovered: ${stagedSources.length}` : '- Local sources discovered: 0'}
|
|
541
|
+
|
|
542
|
+
## Unknowns
|
|
543
|
+
|
|
544
|
+
- Primary customer or audience
|
|
545
|
+
- Revenue model and buying motion
|
|
546
|
+
- Main operator inside the business
|
|
547
|
+
- Tool stack and source systems
|
|
548
|
+
- First measurable operating loop
|
|
549
|
+
|
|
550
|
+
## Next Moves
|
|
551
|
+
|
|
552
|
+
- Read the staged intake in \`${intakeRel}\`
|
|
553
|
+
- ${starterAction.action}
|
|
554
|
+
- Turn the first real interaction into a recap, then run \`atris business record ...\`
|
|
555
|
+
`);
|
|
556
|
+
upsertIndexEntry(path.join(cwd, 'atris', 'wiki', 'index.md'), 'Briefs', path.relative(cwd, briefPath), 'Starter business brief from onboarding intake');
|
|
557
|
+
|
|
558
|
+
let personRelativePath = null;
|
|
559
|
+
if (options.contactName) {
|
|
560
|
+
const personSlug = slugifyName(options.contactName);
|
|
561
|
+
const personPath = path.join(cwd, 'atris', 'wiki', 'people', `${personSlug}.md`);
|
|
562
|
+
writeMarkdownWithFrontmatter(personPath, {
|
|
563
|
+
type: 'person',
|
|
564
|
+
slug: personSlug,
|
|
565
|
+
title: options.contactName,
|
|
566
|
+
sources: [intakeRel, sourcesRel],
|
|
567
|
+
last_compiled: today,
|
|
568
|
+
created: today,
|
|
569
|
+
updated: today,
|
|
570
|
+
tags: ['person', 'contact', 'onboarding'],
|
|
571
|
+
}, `
|
|
572
|
+
# ${options.contactName}
|
|
573
|
+
|
|
574
|
+
## Known
|
|
575
|
+
|
|
576
|
+
- Business: ${bizMeta.name}
|
|
577
|
+
- Role: ${options.contactRole || 'unknown'}
|
|
578
|
+
- Email: ${options.contactEmail || 'unknown'}
|
|
579
|
+
|
|
580
|
+
## Unknown
|
|
581
|
+
|
|
582
|
+
- Decision authority
|
|
583
|
+
- Preferred communication rhythm
|
|
584
|
+
- Main business pain
|
|
585
|
+
|
|
586
|
+
## Cross-References
|
|
587
|
+
|
|
588
|
+
- [[atris/wiki/briefs/${path.basename(briefPath)}]] - starter brief
|
|
589
|
+
`);
|
|
590
|
+
personRelativePath = path.relative(cwd, personPath).replace(/\\/g, '/');
|
|
591
|
+
upsertIndexEntry(path.join(cwd, 'atris', 'wiki', 'index.md'), 'People', personRelativePath, `Seed contact for ${bizMeta.name}`);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const conceptSlug = `${bizMeta.slug}-first-loop`;
|
|
595
|
+
const conceptPath = path.join(cwd, 'atris', 'wiki', 'concepts', `${conceptSlug}.md`);
|
|
596
|
+
writeMarkdownWithFrontmatter(conceptPath, {
|
|
597
|
+
type: 'concept',
|
|
598
|
+
slug: conceptSlug,
|
|
599
|
+
title: `${bizMeta.name} First Loop`,
|
|
600
|
+
sources: [intakeRel, linksRel, sourcesRel],
|
|
601
|
+
last_compiled: today,
|
|
602
|
+
created: today,
|
|
603
|
+
updated: today,
|
|
604
|
+
tags: ['concept', 'loop', 'onboarding'],
|
|
605
|
+
}, `
|
|
606
|
+
# ${bizMeta.name} First Loop
|
|
607
|
+
|
|
608
|
+
## Candidate Loop
|
|
609
|
+
|
|
610
|
+
- Trigger: a new lead, meeting, client request, or operator handoff
|
|
611
|
+
- Action: summarize context, propose the next move, and draft one concrete output
|
|
612
|
+
- Reward: operator approval, reply, booked meeting, or visible pipeline progress
|
|
613
|
+
|
|
614
|
+
## Known Signals
|
|
615
|
+
|
|
616
|
+
${links.map((link) => `- ${link}`).join('\n') || '- No external links captured yet'}
|
|
617
|
+
${stagedSources.length > 0 ? `- Local evidence files: ${stagedSources.length}` : ''}
|
|
618
|
+
|
|
619
|
+
## Unknowns
|
|
620
|
+
|
|
621
|
+
- Best first workflow to automate
|
|
622
|
+
- Exact reward signal
|
|
623
|
+
- Required integrations
|
|
624
|
+
`);
|
|
625
|
+
upsertIndexEntry(path.join(cwd, 'atris', 'wiki', 'index.md'), 'Concepts', path.relative(cwd, conceptPath), 'Seed first-loop hypothesis from onboarding intake');
|
|
626
|
+
|
|
627
|
+
const cheatSheetPath = path.join(cwd, 'atris', 'reports', `${today}-${bizMeta.slug}-onboarding-cheat-sheet.md`);
|
|
628
|
+
const onePagerPath = path.join(cwd, 'atris', 'reports', `${today}-${bizMeta.slug}-operator-one-pager.md`);
|
|
629
|
+
const operatorSummary = [
|
|
630
|
+
`# ${bizMeta.name} Onboarding Cheat Sheet`,
|
|
631
|
+
'',
|
|
632
|
+
'## What Exists',
|
|
633
|
+
`- Starter brief: ${path.relative(cwd, briefPath).replace(/\\/g, '/')}`,
|
|
634
|
+
personRelativePath ? `- Contact page: ${personRelativePath}` : null,
|
|
635
|
+
`- First loop page: ${path.relative(cwd, conceptPath).replace(/\\/g, '/')}`,
|
|
636
|
+
`- Raw intake: ${intakeRel}`,
|
|
637
|
+
`- Source list: ${sourcesRel}`,
|
|
638
|
+
stagedSources.length > 0 ? `- Staged sources: ${stagedSources.length}` : '- Staged sources: 0',
|
|
639
|
+
'',
|
|
640
|
+
'## Best Next Action',
|
|
641
|
+
`- ${starterAction.title}`,
|
|
642
|
+
`- Action: ${starterAction.action}`,
|
|
643
|
+
`- Why: ${starterAction.why}`,
|
|
644
|
+
'- Swarlo join: placeholder preserved for the next live join step.',
|
|
645
|
+
'',
|
|
646
|
+
'## Next 3 Moves',
|
|
647
|
+
'- Open the starter brief and correct anything false.',
|
|
648
|
+
`- ${starterAction.action}`,
|
|
649
|
+
'- After the first real run, write a recap and record it with `atris business record ...`.',
|
|
650
|
+
].filter(Boolean).join('\n') + '\n';
|
|
651
|
+
fs.writeFileSync(cheatSheetPath, operatorSummary, 'utf8');
|
|
652
|
+
fs.writeFileSync(onePagerPath, operatorSummary.replace('# ', '# One Pager — '), 'utf8');
|
|
653
|
+
|
|
654
|
+
const todoPath = path.join(cwd, 'atris', 'TODO.md');
|
|
655
|
+
if (fs.existsSync(todoPath)) {
|
|
656
|
+
let todoContent = fs.readFileSync(todoPath, 'utf8');
|
|
657
|
+
const taskLine = `- **Onboard:** ${starterAction.title} — ${starterAction.action} [execute]\n`;
|
|
658
|
+
const backlogMatch = todoContent.match(/^## Backlog\s*$/m);
|
|
659
|
+
if (backlogMatch) {
|
|
660
|
+
const insertAt = backlogMatch.index + backlogMatch[0].length;
|
|
661
|
+
todoContent = todoContent.slice(0, insertAt) + '\n' + taskLine + todoContent.slice(insertAt);
|
|
662
|
+
} else {
|
|
663
|
+
todoContent += '\n## Backlog\n\n' + taskLine;
|
|
664
|
+
}
|
|
665
|
+
fs.writeFileSync(todoPath, todoContent, 'utf8');
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
writeWikiStatus(cwd, {
|
|
669
|
+
health: `starter onboarding compiled from ${intakeRel}`,
|
|
670
|
+
nextMove: `review ${path.relative(cwd, briefPath).replace(/\\/g, '/')} and tighten the first loop`,
|
|
671
|
+
}, 'public', { lastIngest: `${today} ${new Date().toTimeString().slice(0, 5)}` });
|
|
672
|
+
appendWikiLog(cwd, `starter onboarding compiled for ${bizMeta.slug}`, [
|
|
673
|
+
`intake ${intakeRel}`,
|
|
674
|
+
`sources ${sourcesRel}`,
|
|
675
|
+
`brief ${path.relative(cwd, briefPath).replace(/\\/g, '/')}`,
|
|
676
|
+
personRelativePath ? `person ${personRelativePath}` : null,
|
|
677
|
+
`concept ${path.relative(cwd, conceptPath).replace(/\\/g, '/')}`,
|
|
678
|
+
`cheat sheet ${path.relative(cwd, cheatSheetPath).replace(/\\/g, '/')}`,
|
|
679
|
+
`one pager ${path.relative(cwd, onePagerPath).replace(/\\/g, '/')}`,
|
|
680
|
+
].filter(Boolean), 'public', 'ONBOARD');
|
|
681
|
+
|
|
682
|
+
console.log('');
|
|
683
|
+
console.log(`Onboarded ${bizMeta.name}.`);
|
|
684
|
+
console.log(` Intake: ${intakeRel}`);
|
|
685
|
+
console.log(` Sources: ${sourcesRel}`);
|
|
686
|
+
console.log(` Brief: ${path.relative(cwd, briefPath).replace(/\\/g, '/')}`);
|
|
687
|
+
if (personRelativePath) console.log(` Contact: ${personRelativePath}`);
|
|
688
|
+
console.log(` First loop: ${path.relative(cwd, conceptPath).replace(/\\/g, '/')}`);
|
|
689
|
+
console.log(` Cheat sheet: ${path.relative(cwd, cheatSheetPath).replace(/\\/g, '/')}`);
|
|
690
|
+
console.log(` One pager: ${path.relative(cwd, onePagerPath).replace(/\\/g, '/')}`);
|
|
691
|
+
console.log(` Next action: ${starterAction.title}`);
|
|
692
|
+
console.log('');
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async function recordBusinessRun(reportArg, ...flags) {
|
|
696
|
+
const options = parseRecordFlags([reportArg, ...flags], process.cwd());
|
|
697
|
+
const cwd = options.cwd || process.cwd();
|
|
698
|
+
const bizMeta = readWorkspaceBusinessMeta(cwd);
|
|
699
|
+
const { absPath, relPath } = resolveWorkspaceReport(cwd, options.reportPath);
|
|
700
|
+
|
|
701
|
+
ensureWorkspaceStateFiles(cwd, {
|
|
702
|
+
slug: bizMeta.slug || 'business',
|
|
703
|
+
business_id: bizMeta.business_id || '',
|
|
704
|
+
workspace_id: bizMeta.workspace_id || '',
|
|
705
|
+
workspace_template: bizMeta.workspace_template || 'business',
|
|
706
|
+
}, { dryRun: false });
|
|
707
|
+
|
|
708
|
+
const reportContent = fs.readFileSync(absPath, 'utf8');
|
|
709
|
+
const title = extractReportTitle(reportContent, absPath);
|
|
710
|
+
const outcome = normalizeOutcome(options.outcome);
|
|
711
|
+
const reward = options.reward != null ? Number(options.reward) : defaultRewardForOutcome(outcome);
|
|
712
|
+
if (!Number.isFinite(reward)) {
|
|
713
|
+
throw new Error(`Invalid reward: ${options.reward}`);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const recordedAt = new Date().toISOString();
|
|
717
|
+
const summary = options.summary || title;
|
|
718
|
+
const metric = options.metric || null;
|
|
719
|
+
const loop = options.loop || 'manual';
|
|
720
|
+
const actor = options.actor || 'operator';
|
|
721
|
+
const stateDir = path.join(cwd, '.atris', 'state');
|
|
722
|
+
|
|
723
|
+
const shared = {
|
|
724
|
+
recorded_at: recordedAt,
|
|
725
|
+
business_slug: bizMeta.slug || null,
|
|
726
|
+
business_name: bizMeta.name || null,
|
|
727
|
+
business_id: bizMeta.business_id || null,
|
|
728
|
+
workspace_id: bizMeta.workspace_id || null,
|
|
729
|
+
workspace_template: bizMeta.workspace_template || 'business',
|
|
730
|
+
report_path: relPath,
|
|
731
|
+
report_title: title,
|
|
732
|
+
summary,
|
|
733
|
+
metric,
|
|
734
|
+
outcome,
|
|
735
|
+
reward,
|
|
736
|
+
loop,
|
|
737
|
+
actor,
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
appendJsonl(path.join(stateDir, 'events.jsonl'), {
|
|
741
|
+
...shared,
|
|
742
|
+
type: 'report_recorded',
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
appendJsonl(path.join(stateDir, 'episodes.jsonl'), {
|
|
746
|
+
...shared,
|
|
747
|
+
type: 'episode',
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
appendJsonl(path.join(stateDir, 'scorecards.jsonl'), {
|
|
751
|
+
...shared,
|
|
752
|
+
type: 'scorecard',
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
console.log('');
|
|
756
|
+
console.log(`Recorded recap for ${bizMeta.name || bizMeta.slug || 'workspace'}.`);
|
|
757
|
+
console.log(` Report: ${relPath}`);
|
|
758
|
+
console.log(` Outcome: ${outcome}`);
|
|
759
|
+
console.log(` Reward: ${reward}`);
|
|
760
|
+
if (metric) console.log(` Metric: ${metric}`);
|
|
761
|
+
console.log(' State: .atris/state/events.jsonl, episodes.jsonl, scorecards.jsonl');
|
|
762
|
+
console.log('');
|
|
103
763
|
}
|
|
104
764
|
|
|
105
765
|
function detectBusinessSlug(explicitSlug) {
|
|
@@ -198,7 +858,7 @@ async function listBusinesses(opts = {}) {
|
|
|
198
858
|
* Walk ~/arena/atris-business/ and print a fleet status table for every
|
|
199
859
|
* customer workspace. Pure local — no API calls, no rate-limit risk.
|
|
200
860
|
*
|
|
201
|
-
* Classifies each dir as:
|
|
861
|
+
* Classifies each dir as: ready, flat, unbound, nested, bare, or superseded.
|
|
202
862
|
*
|
|
203
863
|
* Discovered the need for this during overnight loop tick #3 when we hand-wrote
|
|
204
864
|
* /tmp/customer_fleet.md. Now any team member can run `atris business list --local`
|
|
@@ -253,7 +913,7 @@ function listBusinessesLocal(opts = {}) {
|
|
|
253
913
|
|
|
254
914
|
let state, action, icon;
|
|
255
915
|
if (hasBizJson && hasAtris) {
|
|
256
|
-
state = '
|
|
916
|
+
state = 'ready'; action = 'none'; icon = '🟢';
|
|
257
917
|
} else if (hasBizJson && !hasAtris) {
|
|
258
918
|
state = 'flat'; action = 'migrate to atris/ wrapper'; icon = '🟡';
|
|
259
919
|
} else if (!hasBizJson && hasAtris) {
|
|
@@ -263,7 +923,7 @@ function listBusinessesLocal(opts = {}) {
|
|
|
263
923
|
} else if (total < 5) {
|
|
264
924
|
state = 'bare'; action = 'not yet onboarded'; icon = '⚪';
|
|
265
925
|
} else {
|
|
266
|
-
state = 'flat-unbound'; action = 'needs
|
|
926
|
+
state = 'flat-unbound'; action = 'needs business init'; icon = '🟡';
|
|
267
927
|
}
|
|
268
928
|
|
|
269
929
|
let bizName = name;
|
|
@@ -309,7 +969,7 @@ function listBusinessesLocal(opts = {}) {
|
|
|
309
969
|
console.log(' CUSTOMER STATE FILES BIZ.JSON ATRIS/ ACTION');
|
|
310
970
|
console.log(' ' + '─'.repeat(83));
|
|
311
971
|
|
|
312
|
-
const order = ['
|
|
972
|
+
const order = ['ready', 'flat', 'unbound', 'flat-unbound', 'bare', 'nested', 'superseded'];
|
|
313
973
|
const grouped = {};
|
|
314
974
|
for (const c of customers) {
|
|
315
975
|
if (!grouped[c.state]) grouped[c.state] = [];
|
|
@@ -678,7 +1338,7 @@ async function createBusinessInternal(name, flags = [], mode = 'auto') {
|
|
|
678
1338
|
}, { here: options.here });
|
|
679
1339
|
console.log(` Local workspace: ${scaffold.targetRoot}/`);
|
|
680
1340
|
} else if (!options.noLocal) {
|
|
681
|
-
console.log(' Tip: run `atris business init "<name>"` or add `--workspace` for a local
|
|
1341
|
+
console.log(' Tip: run `atris business init "<name>"` or add `--workspace` for a local business environment.');
|
|
682
1342
|
}
|
|
683
1343
|
|
|
684
1344
|
const template = options.template;
|
|
@@ -1152,9 +1812,18 @@ async function quickstart() {
|
|
|
1152
1812
|
2. Open the local workspace:
|
|
1153
1813
|
cd ~/arena/atris-business/my-company
|
|
1154
1814
|
|
|
1155
|
-
3.
|
|
1815
|
+
3. Seed onboarding context:
|
|
1816
|
+
atris business onboard --website https://example.com --contact "Founder Name" --note "what they do"
|
|
1817
|
+
|
|
1818
|
+
4. Push local state to cloud:
|
|
1156
1819
|
atris align --fix
|
|
1157
1820
|
|
|
1821
|
+
Then open atris/TODO.md and work the starter queue:
|
|
1822
|
+
define the first loop -> add named humans -> write the first recap
|
|
1823
|
+
|
|
1824
|
+
After the first recap lands:
|
|
1825
|
+
atris business record atris/reports/YYYY-MM-DD-your-recap.md --outcome mixed --metric "operator speed"
|
|
1826
|
+
|
|
1158
1827
|
Optional:
|
|
1159
1828
|
atris business connect slack --business my-company
|
|
1160
1829
|
atris business connect github --business my-company
|
|
@@ -1225,6 +1894,13 @@ async function businessCommand(subcommand, ...args) {
|
|
|
1225
1894
|
case 'push':
|
|
1226
1895
|
await deployBusiness(args[0]);
|
|
1227
1896
|
break;
|
|
1897
|
+
case 'record':
|
|
1898
|
+
case 'record-recap':
|
|
1899
|
+
await recordBusinessRun(args[0], ...args.slice(1));
|
|
1900
|
+
break;
|
|
1901
|
+
case 'onboard':
|
|
1902
|
+
await onboardBusiness(...args);
|
|
1903
|
+
break;
|
|
1228
1904
|
case 'quickstart':
|
|
1229
1905
|
case 'start':
|
|
1230
1906
|
case 'guide':
|
|
@@ -1235,9 +1911,9 @@ async function businessCommand(subcommand, ...args) {
|
|
|
1235
1911
|
console.log('');
|
|
1236
1912
|
console.log(' quickstart ← Start here! 3-command guide');
|
|
1237
1913
|
console.log('');
|
|
1238
|
-
console.log(' init <name> Create a
|
|
1914
|
+
console.log(' init <name> Create a business environment (cloud + local)');
|
|
1239
1915
|
console.log(' workspace <name> Alias for init');
|
|
1240
|
-
console.log(' create <name> Create the cloud business; add --workspace for local
|
|
1916
|
+
console.log(' create <name> Create the cloud business; add --workspace for a local business environment');
|
|
1241
1917
|
console.log(' add <slug> Register an existing cloud business');
|
|
1242
1918
|
console.log(' list Show registered businesses');
|
|
1243
1919
|
console.log(' team [slug] Show members, roles, and admin access');
|
|
@@ -1247,6 +1923,8 @@ async function businessCommand(subcommand, ...args) {
|
|
|
1247
1923
|
console.log(' connect <service> Connect a skill/integration');
|
|
1248
1924
|
console.log(' notify <mode> Set notification mode (digest/silent/push)');
|
|
1249
1925
|
console.log(' deploy <slug> Push local business to cloud');
|
|
1926
|
+
console.log(' onboard Seed brief, person, first loop, safe next action, and one-pager from sparse input');
|
|
1927
|
+
console.log(' record <report> Append recap state into events, episodes, and scorecards');
|
|
1250
1928
|
console.log(' remove <slug> Unregister locally');
|
|
1251
1929
|
}
|
|
1252
1930
|
}
|
|
@@ -1261,4 +1939,6 @@ module.exports = {
|
|
|
1261
1939
|
getBusinessConfigPath,
|
|
1262
1940
|
createCanonicalBusinessWorkspace,
|
|
1263
1941
|
initBusinessWorkspace,
|
|
1942
|
+
onboardBusiness,
|
|
1943
|
+
recordBusinessRun,
|
|
1264
1944
|
};
|