atris 3.0.1 → 3.2.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/README.md +31 -0
- package/atris/skills/endgame/SKILL.md +19 -1
- package/atris/skills/improve/SKILL.md +65 -62
- package/atris/skills/launch/SKILL.md +62 -0
- package/atris/skills/tidy/SKILL.md +84 -0
- package/bin/atris.js +9 -2
- package/commands/autopilot.js +847 -43
- package/commands/business.js +157 -35
- package/commands/experiments.js +1 -1
- package/commands/release.js +183 -0
- package/commands/research.js +52 -0
- package/commands/sync.js +108 -15
- package/commands/verify.js +3 -3
- package/commands/wiki.js +45 -25
- package/lib/reward-config.js +24 -0
- package/lib/scorecard.js +301 -0
- package/lib/todo.js +12 -2
- package/lib/wiki.js +87 -56
- package/package.json +3 -2
package/commands/business.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
3
4
|
const { loadCredentials } = require('../utils/auth');
|
|
4
5
|
const { apiRequestJson } = require('../utils/api');
|
|
6
|
+
const { syncBusinessCanonical } = require('./sync');
|
|
5
7
|
|
|
6
8
|
function getBusinessConfigPath() {
|
|
7
9
|
const home = require('os').homedir();
|
|
@@ -20,6 +22,88 @@ function saveBusinesses(data) {
|
|
|
20
22
|
fs.writeFileSync(getBusinessConfigPath(), JSON.stringify(data, null, 2));
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
function parseCreateBusinessFlags(flags, cwd = process.cwd()) {
|
|
26
|
+
const options = {
|
|
27
|
+
description: '',
|
|
28
|
+
template: null,
|
|
29
|
+
ownerEmail: '',
|
|
30
|
+
noLocal: false,
|
|
31
|
+
workspace: false,
|
|
32
|
+
here: false,
|
|
33
|
+
root: null,
|
|
34
|
+
cwd,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < flags.length; i++) {
|
|
38
|
+
const flag = flags[i];
|
|
39
|
+
const next = flags[i + 1];
|
|
40
|
+
|
|
41
|
+
if ((flag === '--description' || flag === '-d') && next) {
|
|
42
|
+
options.description = next;
|
|
43
|
+
i++;
|
|
44
|
+
} else if ((flag === '--template' || flag === '-t') && next) {
|
|
45
|
+
options.template = next;
|
|
46
|
+
i++;
|
|
47
|
+
} else if (flag === '--owner-email' && next) {
|
|
48
|
+
options.ownerEmail = next;
|
|
49
|
+
i++;
|
|
50
|
+
} else if ((flag === '--root' || flag === '--workspace-root') && next) {
|
|
51
|
+
options.root = path.resolve(cwd, next);
|
|
52
|
+
options.workspace = true;
|
|
53
|
+
i++;
|
|
54
|
+
} else if (flag === '--here') {
|
|
55
|
+
options.here = true;
|
|
56
|
+
options.workspace = true;
|
|
57
|
+
} else if (flag === '--workspace' || flag === '--local-workspace') {
|
|
58
|
+
options.workspace = true;
|
|
59
|
+
} else if (flag === '--no-local') {
|
|
60
|
+
options.noLocal = true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return options;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function resolveWorkspaceRoot(slug, options = {}) {
|
|
68
|
+
if (options.noLocal) return null;
|
|
69
|
+
if (options.here) return options.cwd || process.cwd();
|
|
70
|
+
if (options.root) return path.join(options.root, slug);
|
|
71
|
+
return path.join(os.homedir(), 'arena', 'atris-business', slug);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createCanonicalBusinessWorkspace(targetRoot, bizMeta, options = {}) {
|
|
75
|
+
if (!targetRoot) {
|
|
76
|
+
throw new Error('No target directory provided for business workspace.');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (options.here !== true && fs.existsSync(targetRoot) && !fs.statSync(targetRoot).isDirectory()) {
|
|
80
|
+
throw new Error(`Target path is not a directory: ${targetRoot}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fs.mkdirSync(targetRoot, { recursive: true });
|
|
84
|
+
|
|
85
|
+
const atrisMetaDir = path.join(targetRoot, '.atris');
|
|
86
|
+
const businessJsonPath = path.join(atrisMetaDir, 'business.json');
|
|
87
|
+
if (fs.existsSync(businessJsonPath)) {
|
|
88
|
+
throw new Error(`Target already contains .atris/business.json: ${targetRoot}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const workspaceTemplate = options.templateName || bizMeta.workspace_template || 'business';
|
|
92
|
+
fs.mkdirSync(atrisMetaDir, { recursive: true });
|
|
93
|
+
fs.writeFileSync(businessJsonPath, JSON.stringify({
|
|
94
|
+
business_id: bizMeta.business_id,
|
|
95
|
+
workspace_id: bizMeta.workspace_id,
|
|
96
|
+
name: bizMeta.name,
|
|
97
|
+
slug: bizMeta.slug,
|
|
98
|
+
owner_email: bizMeta.owner_email || '',
|
|
99
|
+
workspace_template: workspaceTemplate,
|
|
100
|
+
created_at: new Date().toISOString(),
|
|
101
|
+
}, null, 2));
|
|
102
|
+
|
|
103
|
+
syncBusinessCanonical(targetRoot, bizMeta, { force: false, dryRun: false, templateName: workspaceTemplate });
|
|
104
|
+
return { targetRoot, businessJsonPath, workspaceTemplate };
|
|
105
|
+
}
|
|
106
|
+
|
|
23
107
|
function detectBusinessSlug(explicitSlug) {
|
|
24
108
|
if (explicitSlug) return explicitSlug;
|
|
25
109
|
const bizFile = path.join(process.cwd(), '.atris', 'business.json');
|
|
@@ -116,7 +200,7 @@ async function listBusinesses(opts = {}) {
|
|
|
116
200
|
* Walk ~/arena/atris-business/ and print a fleet status table for every
|
|
117
201
|
* customer workspace. Pure local — no API calls, no rate-limit risk.
|
|
118
202
|
*
|
|
119
|
-
* Classifies each dir as:
|
|
203
|
+
* Classifies each dir as: ready, flat, unbound, nested, bare, or superseded.
|
|
120
204
|
*
|
|
121
205
|
* Discovered the need for this during overnight loop tick #3 when we hand-wrote
|
|
122
206
|
* /tmp/customer_fleet.md. Now any team member can run `atris business list --local`
|
|
@@ -171,7 +255,7 @@ function listBusinessesLocal(opts = {}) {
|
|
|
171
255
|
|
|
172
256
|
let state, action, icon;
|
|
173
257
|
if (hasBizJson && hasAtris) {
|
|
174
|
-
state = '
|
|
258
|
+
state = 'ready'; action = 'none'; icon = '🟢';
|
|
175
259
|
} else if (hasBizJson && !hasAtris) {
|
|
176
260
|
state = 'flat'; action = 'migrate to atris/ wrapper'; icon = '🟡';
|
|
177
261
|
} else if (!hasBizJson && hasAtris) {
|
|
@@ -181,7 +265,7 @@ function listBusinessesLocal(opts = {}) {
|
|
|
181
265
|
} else if (total < 5) {
|
|
182
266
|
state = 'bare'; action = 'not yet onboarded'; icon = '⚪';
|
|
183
267
|
} else {
|
|
184
|
-
state = 'flat-unbound'; action = 'needs
|
|
268
|
+
state = 'flat-unbound'; action = 'needs business init'; icon = '🟡';
|
|
185
269
|
}
|
|
186
270
|
|
|
187
271
|
let bizName = name;
|
|
@@ -227,7 +311,7 @@ function listBusinessesLocal(opts = {}) {
|
|
|
227
311
|
console.log(' CUSTOMER STATE FILES BIZ.JSON ATRIS/ ACTION');
|
|
228
312
|
console.log(' ' + '─'.repeat(83));
|
|
229
313
|
|
|
230
|
-
const order = ['
|
|
314
|
+
const order = ['ready', 'flat', 'unbound', 'flat-unbound', 'bare', 'nested', 'superseded'];
|
|
231
315
|
const grouped = {};
|
|
232
316
|
for (const c of customers) {
|
|
233
317
|
if (!grouped[c.state]) grouped[c.state] = [];
|
|
@@ -519,9 +603,9 @@ async function businessAudit() {
|
|
|
519
603
|
console.log('');
|
|
520
604
|
}
|
|
521
605
|
|
|
522
|
-
async function
|
|
606
|
+
async function createBusinessInternal(name, flags = [], mode = 'auto') {
|
|
523
607
|
if (!name) {
|
|
524
|
-
console.error('Usage: atris business create <name> [--description "..."]');
|
|
608
|
+
console.error('Usage: atris business create <name> [--description "..."] [--workspace] [--here|--root <dir>]');
|
|
525
609
|
process.exit(1);
|
|
526
610
|
}
|
|
527
611
|
|
|
@@ -531,14 +615,8 @@ async function createBusiness(name, ...flags) {
|
|
|
531
615
|
process.exit(1);
|
|
532
616
|
}
|
|
533
617
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
for (let i = 0; i < flags.length; i++) {
|
|
537
|
-
if ((flags[i] === '--description' || flags[i] === '-d') && flags[i + 1]) {
|
|
538
|
-
description = flags[i + 1];
|
|
539
|
-
i++;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
618
|
+
const options = parseCreateBusinessFlags(flags);
|
|
619
|
+
const description = options.description;
|
|
542
620
|
|
|
543
621
|
console.log(`Creating business: ${name}...`);
|
|
544
622
|
|
|
@@ -567,8 +645,15 @@ async function createBusiness(name, ...flags) {
|
|
|
567
645
|
};
|
|
568
646
|
saveBusinesses(businesses);
|
|
569
647
|
|
|
570
|
-
|
|
571
|
-
|
|
648
|
+
const shouldCreateCanonicalWorkspace = !options.noLocal && (
|
|
649
|
+
mode === 'canonical' ||
|
|
650
|
+
options.workspace ||
|
|
651
|
+
options.here ||
|
|
652
|
+
Boolean(options.root)
|
|
653
|
+
);
|
|
654
|
+
|
|
655
|
+
// Scaffold legacy local directory if in an atris project
|
|
656
|
+
const atrisDir = !shouldCreateCanonicalWorkspace ? findAtrisDir() : null;
|
|
572
657
|
if (atrisDir) {
|
|
573
658
|
const bizDir = path.join(atrisDir, 'business', biz.slug);
|
|
574
659
|
if (!fs.existsSync(bizDir)) {
|
|
@@ -584,16 +669,21 @@ async function createBusiness(name, ...flags) {
|
|
|
584
669
|
].join(''));
|
|
585
670
|
console.log(` Local scaffold: ${bizDir}/`);
|
|
586
671
|
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
}
|
|
596
|
-
}
|
|
672
|
+
} else if (shouldCreateCanonicalWorkspace) {
|
|
673
|
+
const workspaceRoot = resolveWorkspaceRoot(biz.slug, options);
|
|
674
|
+
const scaffold = createCanonicalBusinessWorkspace(workspaceRoot, {
|
|
675
|
+
business_id: biz.id,
|
|
676
|
+
workspace_id: biz.workspace_id,
|
|
677
|
+
name: biz.name,
|
|
678
|
+
slug: biz.slug,
|
|
679
|
+
owner_email: options.ownerEmail,
|
|
680
|
+
}, { here: options.here });
|
|
681
|
+
console.log(` Local workspace: ${scaffold.targetRoot}/`);
|
|
682
|
+
} else if (!options.noLocal) {
|
|
683
|
+
console.log(' Tip: run `atris business init "<name>"` or add `--workspace` for a local business environment.');
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const template = options.template;
|
|
597
687
|
|
|
598
688
|
if (template) {
|
|
599
689
|
const templates = {
|
|
@@ -620,9 +710,22 @@ async function createBusiness(name, ...flags) {
|
|
|
620
710
|
console.log(` Slug: ${biz.slug}`);
|
|
621
711
|
console.log(` Agent: ${biz.agent_id || '(none)'}`);
|
|
622
712
|
console.log(` Dashboard: https://atris.ai/dashboard/gm/${biz.id}`);
|
|
713
|
+
if (shouldCreateCanonicalWorkspace) {
|
|
714
|
+
const workspaceRoot = resolveWorkspaceRoot(biz.slug, options);
|
|
715
|
+
console.log(` Next: cd ${workspaceRoot}`);
|
|
716
|
+
console.log(' atris align --fix');
|
|
717
|
+
}
|
|
623
718
|
console.log('');
|
|
624
719
|
}
|
|
625
720
|
|
|
721
|
+
async function createBusiness(name, ...flags) {
|
|
722
|
+
return createBusinessInternal(name, flags, 'auto');
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
async function initBusinessWorkspace(name, ...flags) {
|
|
726
|
+
return createBusinessInternal(name, flags, 'canonical');
|
|
727
|
+
}
|
|
728
|
+
|
|
626
729
|
|
|
627
730
|
async function businessStatus(slug) {
|
|
628
731
|
const creds = loadCredentials();
|
|
@@ -1046,18 +1149,21 @@ async function quickstart() {
|
|
|
1046
1149
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1047
1150
|
|
|
1048
1151
|
1. Create:
|
|
1049
|
-
atris business
|
|
1152
|
+
atris business init "My Company" --template saas
|
|
1050
1153
|
|
|
1051
|
-
2.
|
|
1052
|
-
atris
|
|
1053
|
-
atris business connect github --business my-company
|
|
1154
|
+
2. Open the local workspace:
|
|
1155
|
+
cd ~/arena/atris-business/my-company
|
|
1054
1156
|
|
|
1055
|
-
3.
|
|
1056
|
-
atris
|
|
1157
|
+
3. Push local state to cloud:
|
|
1158
|
+
atris align --fix
|
|
1057
1159
|
|
|
1058
|
-
|
|
1160
|
+
Then open atris/TODO.md and work the starter queue:
|
|
1161
|
+
define the first loop -> add named humans -> write the first recap
|
|
1059
1162
|
|
|
1060
1163
|
Optional:
|
|
1164
|
+
atris business connect slack --business my-company
|
|
1165
|
+
atris business connect github --business my-company
|
|
1166
|
+
|
|
1061
1167
|
atris business notify digest --business my-company
|
|
1062
1168
|
(get 1 email/day instead of every notification)
|
|
1063
1169
|
|
|
@@ -1076,6 +1182,10 @@ async function businessCommand(subcommand, ...args) {
|
|
|
1076
1182
|
case 'new':
|
|
1077
1183
|
await createBusiness(args[0], ...args.slice(1));
|
|
1078
1184
|
break;
|
|
1185
|
+
case 'init':
|
|
1186
|
+
case 'workspace':
|
|
1187
|
+
await initBusinessWorkspace(args[0], ...args.slice(1));
|
|
1188
|
+
break;
|
|
1079
1189
|
case 'list':
|
|
1080
1190
|
case 'ls': {
|
|
1081
1191
|
const opts = {};
|
|
@@ -1130,7 +1240,9 @@ async function businessCommand(subcommand, ...args) {
|
|
|
1130
1240
|
console.log('');
|
|
1131
1241
|
console.log(' quickstart ← Start here! 3-command guide');
|
|
1132
1242
|
console.log('');
|
|
1133
|
-
console.log('
|
|
1243
|
+
console.log(' init <name> Create a business environment (cloud + local)');
|
|
1244
|
+
console.log(' workspace <name> Alias for init');
|
|
1245
|
+
console.log(' create <name> Create the cloud business; add --workspace for a local business environment');
|
|
1134
1246
|
console.log(' add <slug> Register an existing cloud business');
|
|
1135
1247
|
console.log(' list Show registered businesses');
|
|
1136
1248
|
console.log(' team [slug] Show members, roles, and admin access');
|
|
@@ -1144,4 +1256,14 @@ async function businessCommand(subcommand, ...args) {
|
|
|
1144
1256
|
}
|
|
1145
1257
|
}
|
|
1146
1258
|
|
|
1147
|
-
module.exports = {
|
|
1259
|
+
module.exports = {
|
|
1260
|
+
businessCommand,
|
|
1261
|
+
businessHealth,
|
|
1262
|
+
businessAudit,
|
|
1263
|
+
businessTeam,
|
|
1264
|
+
loadBusinesses,
|
|
1265
|
+
saveBusinesses,
|
|
1266
|
+
getBusinessConfigPath,
|
|
1267
|
+
createCanonicalBusinessWorkspace,
|
|
1268
|
+
initBusinessWorkspace,
|
|
1269
|
+
};
|
package/commands/experiments.js
CHANGED
|
@@ -351,7 +351,7 @@ function buildBenchmarkArtifact(name, packDir, options) {
|
|
|
351
351
|
}
|
|
352
352
|
);
|
|
353
353
|
|
|
354
|
-
reviewStatus = execution.
|
|
354
|
+
reviewStatus = execution.verifyPass ? 'pass' : 'fail';
|
|
355
355
|
reviewSummary = summarizeReview(execution.reviewOutput);
|
|
356
356
|
tests = inferTestResults(execution.reviewOutput);
|
|
357
357
|
} catch (error) {
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execFileSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* atris release [--dry-run] - Tag a release, create GitHub release, draft /launch post
|
|
7
|
+
*
|
|
8
|
+
* - Reads git log since last tag
|
|
9
|
+
* - Determines bump type (minor if any scorecard has reward>=5, else patch)
|
|
10
|
+
* - Bumps package.json version
|
|
11
|
+
* - Commits, tags, pushes
|
|
12
|
+
* - Creates GitHub release via `gh`
|
|
13
|
+
* - Drafts a /launch post (3 emoji bullets)
|
|
14
|
+
*/
|
|
15
|
+
async function releaseAtris({ dryRun = false } = {}) {
|
|
16
|
+
const cwd = process.cwd();
|
|
17
|
+
|
|
18
|
+
// 1. Get last tag
|
|
19
|
+
let lastTag;
|
|
20
|
+
try {
|
|
21
|
+
lastTag = execFileSync('git', ['describe', '--tags', '--abbrev=0'], { cwd, encoding: 'utf8' }).trim();
|
|
22
|
+
} catch {
|
|
23
|
+
lastTag = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 2. Get git log since last tag
|
|
27
|
+
let logArgs = ['log', '--oneline'];
|
|
28
|
+
if (lastTag) {
|
|
29
|
+
logArgs = ['log', `${lastTag}..HEAD`, '--oneline'];
|
|
30
|
+
}
|
|
31
|
+
let commits;
|
|
32
|
+
try {
|
|
33
|
+
commits = execFileSync('git', logArgs, { cwd, encoding: 'utf8' }).trim();
|
|
34
|
+
} catch {
|
|
35
|
+
commits = '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!commits) {
|
|
39
|
+
console.log('no new commits since last tag' + (lastTag ? ` (${lastTag})` : '') + '. nothing to release.');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const commitLines = commits.split('\n').filter(Boolean);
|
|
44
|
+
console.log('');
|
|
45
|
+
console.log(`commits since ${lastTag || 'beginning'}: ${commitLines.length}`);
|
|
46
|
+
|
|
47
|
+
// 3. Build changelog
|
|
48
|
+
const changelog = commitLines.map(l => `- ${l}`).join('\n');
|
|
49
|
+
|
|
50
|
+
// 4. Determine bump type — minor if any scorecard has reward >= 5, else patch
|
|
51
|
+
const bumpType = determineBumpType(cwd);
|
|
52
|
+
console.log(`bump type: ${bumpType}`);
|
|
53
|
+
|
|
54
|
+
// 5. Read current version and compute next
|
|
55
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
56
|
+
if (!fs.existsSync(pkgPath)) {
|
|
57
|
+
console.log('no package.json found. cannot bump version.');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const pkgRaw = fs.readFileSync(pkgPath, 'utf8');
|
|
61
|
+
const pkg = JSON.parse(pkgRaw);
|
|
62
|
+
const currentVersion = pkg.version;
|
|
63
|
+
const nextVersion = bumpVersion(currentVersion, bumpType);
|
|
64
|
+
|
|
65
|
+
console.log(`version: ${currentVersion} → ${nextVersion}`);
|
|
66
|
+
console.log('');
|
|
67
|
+
|
|
68
|
+
// 6. Show changelog
|
|
69
|
+
console.log('changelog:');
|
|
70
|
+
console.log(changelog);
|
|
71
|
+
console.log('');
|
|
72
|
+
|
|
73
|
+
if (dryRun) {
|
|
74
|
+
console.log('--- dry-run: draft release ---');
|
|
75
|
+
console.log(`tag: v${nextVersion}`);
|
|
76
|
+
console.log(`title: v${nextVersion}`);
|
|
77
|
+
console.log('');
|
|
78
|
+
printLaunchPost(nextVersion, commitLines);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 7. Bump package.json
|
|
83
|
+
const updatedPkgRaw = pkgRaw.replace(
|
|
84
|
+
`"version": "${currentVersion}"`,
|
|
85
|
+
`"version": "${nextVersion}"`
|
|
86
|
+
);
|
|
87
|
+
fs.writeFileSync(pkgPath, updatedPkgRaw);
|
|
88
|
+
console.log(`bumped package.json to ${nextVersion}`);
|
|
89
|
+
|
|
90
|
+
// 8. Commit
|
|
91
|
+
execFileSync('git', ['add', 'package.json'], { cwd });
|
|
92
|
+
execFileSync('git', ['commit', '-m', `v${nextVersion}`], { cwd });
|
|
93
|
+
console.log('committed');
|
|
94
|
+
|
|
95
|
+
// 9. Tag
|
|
96
|
+
execFileSync('git', ['tag', `v${nextVersion}`], { cwd });
|
|
97
|
+
console.log(`tagged v${nextVersion}`);
|
|
98
|
+
|
|
99
|
+
// 10. Push + push tags
|
|
100
|
+
try {
|
|
101
|
+
execFileSync('git', ['push'], { cwd });
|
|
102
|
+
execFileSync('git', ['push', '--tags'], { cwd });
|
|
103
|
+
console.log('pushed');
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.log('push failed — you may need to push manually');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 11. Create GitHub release via gh
|
|
109
|
+
try {
|
|
110
|
+
execFileSync('gh', ['release', 'create', `v${nextVersion}`,
|
|
111
|
+
'--title', `v${nextVersion}`,
|
|
112
|
+
'--notes', changelog
|
|
113
|
+
], { cwd });
|
|
114
|
+
console.log(`github release created: v${nextVersion}`);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.log('gh release create failed — install gh or create release manually');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 12. Draft launch post
|
|
120
|
+
console.log('');
|
|
121
|
+
printLaunchPost(nextVersion, commitLines);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check scorecards for reward >= 5 → minor bump. Otherwise patch.
|
|
126
|
+
*/
|
|
127
|
+
function determineBumpType(cwd) {
|
|
128
|
+
const scorecardsDir = path.join(cwd, 'atris', 'scorecards');
|
|
129
|
+
if (!fs.existsSync(scorecardsDir)) return 'patch';
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const files = fs.readdirSync(scorecardsDir).filter(f => f.endsWith('.md'));
|
|
133
|
+
for (const file of files) {
|
|
134
|
+
const content = fs.readFileSync(path.join(scorecardsDir, file), 'utf8');
|
|
135
|
+
const rewardMatch = content.match(/reward[:\s]+(\d+)/i);
|
|
136
|
+
if (rewardMatch && parseInt(rewardMatch[1], 10) >= 5) {
|
|
137
|
+
return 'minor';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
// ignore
|
|
142
|
+
}
|
|
143
|
+
return 'patch';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Bump a semver string: "1.2.3" + "patch" → "1.2.4", "minor" → "1.3.0"
|
|
148
|
+
*/
|
|
149
|
+
function bumpVersion(version, type) {
|
|
150
|
+
const parts = version.split('.').map(Number);
|
|
151
|
+
if (type === 'minor') {
|
|
152
|
+
parts[1]++;
|
|
153
|
+
parts[2] = 0;
|
|
154
|
+
} else {
|
|
155
|
+
parts[2]++;
|
|
156
|
+
}
|
|
157
|
+
return parts.join('.');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Print a 3-emoji-bullet launch post (Twitter + LinkedIn format)
|
|
162
|
+
*/
|
|
163
|
+
function printLaunchPost(version, commitLines) {
|
|
164
|
+
// Pick top 3 changes (dedupe, trim hashes)
|
|
165
|
+
const topChanges = commitLines
|
|
166
|
+
.slice(0, 3)
|
|
167
|
+
.map(l => l.replace(/^[a-f0-9]+\s+/, ''));
|
|
168
|
+
|
|
169
|
+
const emojis = ['🚀', '⚡', '🔧'];
|
|
170
|
+
|
|
171
|
+
console.log('--- launch post draft ---');
|
|
172
|
+
console.log('');
|
|
173
|
+
console.log(`Atris v${version} is out.`);
|
|
174
|
+
console.log('');
|
|
175
|
+
topChanges.forEach((change, i) => {
|
|
176
|
+
console.log(`${emojis[i] || '•'} ${change}`);
|
|
177
|
+
});
|
|
178
|
+
console.log('');
|
|
179
|
+
console.log('npm i -g atris');
|
|
180
|
+
console.log('--- end launch post ---');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = { releaseAtris };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const { initResearchWorkspace } = require('./business');
|
|
2
|
+
|
|
3
|
+
async function researchQuickstart() {
|
|
4
|
+
console.log(`
|
|
5
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
6
|
+
Start a Research Lab in 3 Commands
|
|
7
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
8
|
+
|
|
9
|
+
1. Create:
|
|
10
|
+
atris research init "Frontier Lab"
|
|
11
|
+
|
|
12
|
+
2. Open the local workspace:
|
|
13
|
+
cd ~/arena/atris-business/frontier-lab
|
|
14
|
+
|
|
15
|
+
3. Push local state to cloud:
|
|
16
|
+
atris align --fix
|
|
17
|
+
|
|
18
|
+
The research template starts with:
|
|
19
|
+
hypotheses + experiment lanes
|
|
20
|
+
eval-first reward policy
|
|
21
|
+
literature + findings workflow
|
|
22
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
23
|
+
`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function researchCommand(subcommand, ...args) {
|
|
27
|
+
switch (subcommand) {
|
|
28
|
+
case 'init':
|
|
29
|
+
case 'workspace':
|
|
30
|
+
case 'create':
|
|
31
|
+
await initResearchWorkspace(args[0], ...args.slice(1));
|
|
32
|
+
break;
|
|
33
|
+
case 'quickstart':
|
|
34
|
+
case 'start':
|
|
35
|
+
case 'guide':
|
|
36
|
+
await researchQuickstart();
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
console.log('Usage: atris research <command> [args]');
|
|
40
|
+
console.log('');
|
|
41
|
+
console.log(' quickstart ← Start here! 3-command guide');
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log(' init <name> Create a research lab workspace (cloud + local)');
|
|
44
|
+
console.log(' workspace <name> Alias for init');
|
|
45
|
+
console.log(' create <name> Alias for init');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
researchCommand,
|
|
51
|
+
researchQuickstart,
|
|
52
|
+
};
|