atris 3.15.14 → 3.15.22
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/AGENTS.md +84 -8
- package/README.md +5 -1
- package/atris/AGENTS.md +46 -1
- package/atris/CLAUDE.md +36 -1
- package/atris/GEMINI.md +14 -1
- package/atris/atris.md +12 -1
- package/atris/atrisDev.md +3 -2
- package/atris/context/README.md +11 -0
- package/atris/features/company-brain-sync/validate.md +5 -5
- package/atris/learnings.jsonl +1 -0
- package/atris/policies/atris-design.md +2 -0
- package/atris/skills/aeo/SKILL.md +2 -2
- package/atris/skills/atris/SKILL.md +15 -62
- package/atris/skills/design/SKILL.md +2 -0
- package/atris/skills/imessage/SKILL.md +19 -2
- package/atris/skills/loop/SKILL.md +6 -5
- package/atris/skills/magic-inbox/SKILL.md +1 -1
- package/atris/team/_template/MEMBER.md +23 -1
- package/atris/team/brainstormer/START_HERE.md +6 -0
- package/atris/team/executor/MEMBER.md +13 -0
- package/atris/team/executor/START_HERE.md +6 -0
- package/atris/team/launcher/START_HERE.md +6 -0
- package/atris/team/mission-lead/MEMBER.md +39 -0
- package/atris/team/mission-lead/MISSION.md +33 -0
- package/atris/team/mission-lead/START_HERE.md +6 -0
- package/atris/team/navigator/MEMBER.md +11 -0
- package/atris/team/navigator/START_HERE.md +6 -0
- package/atris/team/opus-overnight/MEMBER.md +39 -0
- package/atris/team/opus-overnight/MISSION.md +61 -0
- package/atris/team/opus-overnight/START_HERE.md +6 -0
- package/atris/team/opus-overnight/STEERING.md +35 -0
- package/atris/team/researcher/START_HERE.md +6 -0
- package/atris/team/validator/MEMBER.md +26 -6
- package/atris/team/validator/START_HERE.md +6 -0
- package/atris/wiki/concepts/agent-activation-contract.md +79 -0
- package/atris/wiki/concepts/workspace-initialization-contract.md +73 -0
- package/atris/wiki/index.md +27 -0
- package/atris/wiki/sources/atris-labs-2026-05-10.txt +17 -0
- package/atris/wiki/sources/atris-labs-goals-2026-05-10.txt +15 -0
- package/atris/wiki/sources/atrisos-generative-ui-product-surface-2026-05-10.txt +10 -0
- package/atris/wiki/sources/jack-dorsey-2026-05-10.txt +12 -0
- package/atris.md +49 -13
- package/bin/atris.js +660 -22
- package/commands/activate.js +12 -3
- package/commands/aeo.js +1 -1
- package/commands/align.js +10 -10
- package/commands/analytics.js +9 -4
- package/commands/app.js +2 -0
- package/commands/apps.js +276 -0
- package/commands/auth.js +1 -1
- package/commands/autopilot.js +74 -5
- package/commands/brain.js +536 -61
- package/commands/brainstorm.js +12 -12
- package/commands/business-sync.js +142 -24
- package/commands/clean.js +9 -6
- package/commands/codex-goal.js +311 -0
- package/commands/errors.js +11 -1
- package/commands/feedback.js +55 -17
- package/commands/fork.js +2 -2
- package/commands/gm.js +376 -0
- package/commands/init.js +80 -3
- package/commands/integrations.js +524 -0
- package/commands/learn.js +25 -16
- package/commands/lesson.js +41 -0
- package/commands/lifecycle.js +2 -2
- package/commands/member.js +2416 -9
- package/commands/mission.js +1776 -0
- package/commands/now.js +48 -7
- package/commands/play.js +425 -0
- package/commands/publish.js +2 -1
- package/commands/pull.js +72 -29
- package/commands/push.js +163 -18
- package/commands/review.js +51 -13
- package/commands/skill.js +2 -2
- package/commands/soul.js +19 -13
- package/commands/status.js +6 -1
- package/commands/sync.js +5 -4
- package/commands/task.js +1041 -147
- package/commands/terminal.js +5 -5
- package/commands/verify.js +7 -5
- package/commands/visualize.js +7 -0
- package/commands/wiki.js +53 -16
- package/commands/workflow.js +298 -54
- package/commands/workspace-clean.js +1 -1
- package/commands/worktree.js +468 -0
- package/commands/xp.js +1608 -0
- package/lib/manifest.js +34 -4
- package/lib/scorecard.js +3 -2
- package/lib/task-db.js +408 -27
- package/lib/todo-fallback.js +28 -2
- package/lib/todo.js +5 -3
- package/package.json +23 -2
- package/utils/update-check.js +51 -1
package/commands/brainstorm.js
CHANGED
|
@@ -11,18 +11,7 @@ const pkg = require('../package.json');
|
|
|
11
11
|
|
|
12
12
|
async function brainstormAtris() {
|
|
13
13
|
const args = process.argv.slice(3);
|
|
14
|
-
|
|
15
|
-
if (!fs.existsSync(targetDir)) {
|
|
16
|
-
throw new Error('atris/ folder not found. Run "atris init" first.');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
ensureLogDirectory();
|
|
20
|
-
const { logFile, dateFormatted } = getLogPath();
|
|
21
|
-
if (!fs.existsSync(logFile)) {
|
|
22
|
-
createLogFile(logFile, dateFormatted);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
14
|
+
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
26
15
|
console.log('');
|
|
27
16
|
console.log('Usage: atris brainstorm [idea] [--cloud]');
|
|
28
17
|
console.log('');
|
|
@@ -37,6 +26,17 @@ async function brainstormAtris() {
|
|
|
37
26
|
return;
|
|
38
27
|
}
|
|
39
28
|
|
|
29
|
+
const targetDir = path.join(process.cwd(), 'atris');
|
|
30
|
+
if (!fs.existsSync(targetDir)) {
|
|
31
|
+
throw new Error('atris/ folder not found. Run "atris init" first.');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
ensureLogDirectory();
|
|
35
|
+
const { logFile, dateFormatted } = getLogPath();
|
|
36
|
+
if (!fs.existsSync(logFile)) {
|
|
37
|
+
createLogFile(logFile, dateFormatted);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
40
|
const useCloudJournal = args.includes('--cloud') && !args.includes('--no-cloud');
|
|
41
41
|
const topicFromArgs = args.filter((arg) => !arg.startsWith('-')).join(' ').trim() || null;
|
|
42
42
|
|
|
@@ -3,7 +3,11 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { loadManifest } = require('../lib/manifest');
|
|
5
5
|
|
|
6
|
-
const WATCH_IGNORED_DIRS = new Set([
|
|
6
|
+
const WATCH_IGNORED_DIRS = new Set([
|
|
7
|
+
'.git', '.atris', '.claude', '.cursor', '.next', '.cache',
|
|
8
|
+
'node_modules', '__pycache__', 'venv', '.venv', 'dist', 'build',
|
|
9
|
+
'coverage', 'tmp', 'temp',
|
|
10
|
+
]);
|
|
7
11
|
const WATCH_IGNORED_FILES = new Set(['.DS_Store']);
|
|
8
12
|
|
|
9
13
|
function commandLine(args) {
|
|
@@ -33,8 +37,9 @@ function parseBusinessSyncArgs(args = []) {
|
|
|
33
37
|
const watch = args.includes('--watch');
|
|
34
38
|
const intervalSec = Number.parseInt(parseFlagValue(args, '--interval', '60'), 10);
|
|
35
39
|
const debounceSec = Number.parseInt(parseFlagValue(args, '--debounce', '5'), 10);
|
|
40
|
+
const help = args.includes('--help') || args.includes('-h') || positional[0] === 'help';
|
|
36
41
|
|
|
37
|
-
return { slug, dryRun, timeout, allowDelete, watch, intervalSec, debounceSec, status, review, resolve };
|
|
42
|
+
return { slug, dryRun, timeout, allowDelete, watch, intervalSec, debounceSec, status, review, resolve, help };
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
function readBusinessSlug(cwd = process.cwd()) {
|
|
@@ -75,8 +80,7 @@ function shouldIgnoreWatchPath(relativePath) {
|
|
|
75
80
|
return WATCH_IGNORED_FILES.has(path.basename(relativePath));
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
function
|
|
79
|
-
const brainDir = path.join(root, 'atris');
|
|
83
|
+
function collectWorkspaceSnapshot(root) {
|
|
80
84
|
const snapshot = new Map();
|
|
81
85
|
|
|
82
86
|
function walk(dir) {
|
|
@@ -89,7 +93,7 @@ function collectBrainSnapshot(root) {
|
|
|
89
93
|
|
|
90
94
|
for (const entry of entries) {
|
|
91
95
|
const full = path.join(dir, entry.name);
|
|
92
|
-
const rel = path.relative(
|
|
96
|
+
const rel = path.relative(root, full);
|
|
93
97
|
if (shouldIgnoreWatchPath(rel)) continue;
|
|
94
98
|
if (entry.isDirectory()) {
|
|
95
99
|
walk(full);
|
|
@@ -104,10 +108,12 @@ function collectBrainSnapshot(root) {
|
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
walk(
|
|
111
|
+
walk(root);
|
|
108
112
|
return snapshot;
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
const collectBrainSnapshot = collectWorkspaceSnapshot;
|
|
116
|
+
|
|
111
117
|
function snapshotsDiffer(before, after) {
|
|
112
118
|
if (before.size !== after.size) return true;
|
|
113
119
|
for (const [key, value] of before.entries()) {
|
|
@@ -152,8 +158,7 @@ function writeSyncStatus(cwd, payload = {}) {
|
|
|
152
158
|
}, null, 2) + '\n', 'utf8');
|
|
153
159
|
}
|
|
154
160
|
|
|
155
|
-
function
|
|
156
|
-
const brainDir = path.join(cwd, 'atris');
|
|
161
|
+
function countWorkspaceFiles(cwd) {
|
|
157
162
|
let count = 0;
|
|
158
163
|
|
|
159
164
|
function walk(dir) {
|
|
@@ -165,17 +170,78 @@ function countBrainFiles(cwd) {
|
|
|
165
170
|
}
|
|
166
171
|
for (const entry of entries) {
|
|
167
172
|
const full = path.join(dir, entry.name);
|
|
168
|
-
const rel = path.relative(
|
|
173
|
+
const rel = path.relative(cwd, full);
|
|
169
174
|
if (shouldIgnoreWatchPath(rel)) continue;
|
|
170
175
|
if (entry.isDirectory()) walk(full);
|
|
171
176
|
else if (entry.isFile()) count += 1;
|
|
172
177
|
}
|
|
173
178
|
}
|
|
174
179
|
|
|
175
|
-
walk(
|
|
180
|
+
walk(cwd);
|
|
176
181
|
return count;
|
|
177
182
|
}
|
|
178
183
|
|
|
184
|
+
function countFilesUnder(dir) {
|
|
185
|
+
let count = 0;
|
|
186
|
+
|
|
187
|
+
function walk(current) {
|
|
188
|
+
let entries;
|
|
189
|
+
try {
|
|
190
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
191
|
+
} catch {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
for (const entry of entries) {
|
|
195
|
+
const full = path.join(current, entry.name);
|
|
196
|
+
if (entry.isDirectory()) walk(full);
|
|
197
|
+
else if (entry.isFile()) count += 1;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
walk(dir);
|
|
202
|
+
return count;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function collectWorkspaceWarnings(cwd, slug) {
|
|
206
|
+
const warnings = [];
|
|
207
|
+
if (slug) {
|
|
208
|
+
const nested = path.join(cwd, slug);
|
|
209
|
+
if (fs.existsSync(nested)) {
|
|
210
|
+
try {
|
|
211
|
+
if (fs.statSync(nested).isDirectory()) {
|
|
212
|
+
warnings.push(`nested workspace folder: ${slug}/ (${countFilesUnder(nested)} files)`);
|
|
213
|
+
}
|
|
214
|
+
} catch {
|
|
215
|
+
// Ignore races while editors or sync tools move folders.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const syncArtifacts = [];
|
|
221
|
+
function walk(dir) {
|
|
222
|
+
let entries;
|
|
223
|
+
try {
|
|
224
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
225
|
+
} catch {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
for (const entry of entries) {
|
|
229
|
+
const full = path.join(dir, entry.name);
|
|
230
|
+
const rel = path.relative(cwd, full);
|
|
231
|
+
if (shouldIgnoreWatchPath(rel)) continue;
|
|
232
|
+
if (entry.isDirectory()) walk(full);
|
|
233
|
+
else if (entry.isFile() && ['.remote', '.local', '.base', '.cloud'].some((suffix) => entry.name.endsWith(suffix))) {
|
|
234
|
+
syncArtifacts.push(rel.replace(/\\/g, '/'));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
walk(cwd);
|
|
239
|
+
if (syncArtifacts.length > 0) {
|
|
240
|
+
warnings.push(`sync review artifacts outside .atris/: ${syncArtifacts.length}`);
|
|
241
|
+
}
|
|
242
|
+
return warnings;
|
|
243
|
+
}
|
|
244
|
+
|
|
179
245
|
function listConflictSummaries(cwd) {
|
|
180
246
|
const conflictsDir = path.join(cwd, '.atris', 'sync', 'conflicts');
|
|
181
247
|
const summaries = [];
|
|
@@ -206,30 +272,36 @@ function collectLocalSyncStatus(cwd = process.cwd(), options = {}) {
|
|
|
206
272
|
const heartbeat = readJsonFile(syncStatusPath(cwd));
|
|
207
273
|
const conflictSummaries = listConflictSummaries(cwd);
|
|
208
274
|
const brainDir = path.join(cwd, 'atris');
|
|
275
|
+
const workspaceFileCount = countWorkspaceFiles(cwd);
|
|
209
276
|
const manifestRootMatches = !manifest || !manifest.workspace_root || sameRealPath(manifest.workspace_root, cwd);
|
|
277
|
+
const warnings = collectWorkspaceWarnings(cwd, slug);
|
|
210
278
|
|
|
211
279
|
return {
|
|
212
280
|
slug,
|
|
213
281
|
cwd,
|
|
214
282
|
brainDir,
|
|
215
283
|
brainExists: fs.existsSync(brainDir),
|
|
216
|
-
brainFileCount:
|
|
284
|
+
brainFileCount: workspaceFileCount,
|
|
285
|
+
workspaceFileCount,
|
|
217
286
|
conflictCount: conflictSummaries.length,
|
|
218
287
|
latestConflict: conflictSummaries[conflictSummaries.length - 1] || null,
|
|
219
288
|
lastSync: manifest && manifest.last_sync ? manifest.last_sync : null,
|
|
220
289
|
manifestRoot: manifest && manifest.workspace_root ? manifest.workspace_root : null,
|
|
221
290
|
manifestRootMatches,
|
|
222
291
|
heartbeat,
|
|
292
|
+
warnings,
|
|
223
293
|
};
|
|
224
294
|
}
|
|
225
295
|
|
|
226
296
|
function renderLocalSyncStatus(status) {
|
|
227
297
|
const lines = [];
|
|
228
|
-
const fileLabel = status.
|
|
229
|
-
lines.push('
|
|
298
|
+
const fileLabel = status.workspaceFileCount === 1 ? 'file' : 'files';
|
|
299
|
+
lines.push('Business workspace sync status');
|
|
230
300
|
lines.push(` business: ${status.slug || 'not detected'}`);
|
|
231
301
|
lines.push(` folder: ${status.cwd}`);
|
|
232
|
-
lines.push(`
|
|
302
|
+
lines.push(` workspace: ${status.workspaceFileCount} ${fileLabel} (${status.brainExists ? 'atris/ present' : 'missing atris/'})`);
|
|
303
|
+
lines.push(' loop: Pull -> Review -> Publish');
|
|
304
|
+
lines.push(' quest gate: exact files beat broad pushes');
|
|
233
305
|
lines.push(` last cloud sync: ${status.lastSync || 'never on this machine'}`);
|
|
234
306
|
if (status.manifestRoot && !status.manifestRootMatches) {
|
|
235
307
|
lines.push(` manifest: from another folder (${status.manifestRoot})`);
|
|
@@ -242,16 +314,43 @@ function renderLocalSyncStatus(status) {
|
|
|
242
314
|
} else {
|
|
243
315
|
lines.push(' conflicts: none');
|
|
244
316
|
}
|
|
317
|
+
if (status.warnings && status.warnings.length > 0) {
|
|
318
|
+
lines.push(` warnings: ${status.warnings.length}`);
|
|
319
|
+
status.warnings.slice(0, 3).forEach((warning) => lines.push(` - ${warning}`));
|
|
320
|
+
} else {
|
|
321
|
+
lines.push(' warnings: none');
|
|
322
|
+
}
|
|
245
323
|
if (status.heartbeat && status.heartbeat.updated_at) {
|
|
246
324
|
lines.push(` watcher: last heartbeat ${status.heartbeat.updated_at} (${status.heartbeat.state || 'unknown'})`);
|
|
247
325
|
} else {
|
|
248
326
|
lines.push(' watcher: no heartbeat yet');
|
|
249
327
|
}
|
|
250
328
|
lines.push('');
|
|
251
|
-
lines.push('Next: run `atris sync --dry-run` to preview, or `atris sync --
|
|
329
|
+
lines.push('Next: run `atris sync --dry-run` to preview the quest, or `atris sync --review` if conflicts exist.');
|
|
252
330
|
return `${lines.join('\n')}\n`;
|
|
253
331
|
}
|
|
254
332
|
|
|
333
|
+
function renderBusinessSyncHelp() {
|
|
334
|
+
return [
|
|
335
|
+
'Usage: atris sync [business] [--dry-run] [--watch] [--status] [--review] [--resolve local|cloud|both|merge] [--timeout 120]',
|
|
336
|
+
'',
|
|
337
|
+
'Safe loop:',
|
|
338
|
+
' Pull -> Review -> Publish',
|
|
339
|
+
'',
|
|
340
|
+
'Commands:',
|
|
341
|
+
' atris sync --status Show local sync health',
|
|
342
|
+
' atris sync --dry-run Preview pull and publish plans without writing cloud',
|
|
343
|
+
' atris sync --review Read the latest conflict packet',
|
|
344
|
+
' atris sync --resolve cloud|local|merge',
|
|
345
|
+
' atris sync --watch Keep the workspace live with the same safety gates',
|
|
346
|
+
'',
|
|
347
|
+
'Publish safety:',
|
|
348
|
+
' Large unscoped pushes, nested workspace folders, and *.remote artifacts are blocked.',
|
|
349
|
+
' Use exact --only paths for repairs, or explicit push override flags after review.',
|
|
350
|
+
'',
|
|
351
|
+
].join('\n');
|
|
352
|
+
}
|
|
353
|
+
|
|
255
354
|
function renderLatestConflictReview(cwd = process.cwd()) {
|
|
256
355
|
const summaries = listConflictSummaries(cwd);
|
|
257
356
|
if (summaries.length === 0) {
|
|
@@ -297,7 +396,6 @@ function collectConflictResolutionEntries(cwd = process.cwd()) {
|
|
|
297
396
|
const localPath = full;
|
|
298
397
|
const remotePath = full.replace(/\.local$/, '.remote');
|
|
299
398
|
const targetRel = path.relative(dir, full).replace(/\\/g, '/').replace(/\.local$/, '');
|
|
300
|
-
if (!targetRel.startsWith('atris/')) continue;
|
|
301
399
|
entries.push({
|
|
302
400
|
targetRel,
|
|
303
401
|
basePath: full.replace(/\.local$/, '.base'),
|
|
@@ -321,6 +419,13 @@ function assertWorkspaceTarget(cwd, targetRel) {
|
|
|
321
419
|
return targetPath;
|
|
322
420
|
}
|
|
323
421
|
|
|
422
|
+
function cleanupResolvedConflictSidecars(cwd, targetRel, { keepCloud = false } = {}) {
|
|
423
|
+
const suffixes = ['.base', '.local', '.remote', ...(keepCloud ? [] : ['.cloud'])];
|
|
424
|
+
for (const suffix of suffixes) {
|
|
425
|
+
fs.rmSync(assertWorkspaceTarget(cwd, `${targetRel}${suffix}`), { force: true });
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
324
429
|
function changedRange(baseLines, changedLines) {
|
|
325
430
|
let start = 0;
|
|
326
431
|
while (start < baseLines.length && start < changedLines.length && baseLines[start] === changedLines[start]) {
|
|
@@ -485,6 +590,7 @@ function resolveLatestConflict(cwd = process.cwd(), strategy = 'local') {
|
|
|
485
590
|
fs.mkdirSync(path.dirname(remoteCopyPath), { recursive: true });
|
|
486
591
|
fs.copyFileSync(entry.remotePath, remoteCopyPath);
|
|
487
592
|
}
|
|
593
|
+
cleanupResolvedConflictSidecars(cwd, entry.targetRel, { keepCloud: true });
|
|
488
594
|
resolved.push(entry.targetRel);
|
|
489
595
|
continue;
|
|
490
596
|
}
|
|
@@ -505,6 +611,7 @@ function resolveLatestConflict(cwd = process.cwd(), strategy = 'local') {
|
|
|
505
611
|
}
|
|
506
612
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
507
613
|
fs.writeFileSync(targetPath, merged.content, 'utf8');
|
|
614
|
+
cleanupResolvedConflictSidecars(cwd, entry.targetRel);
|
|
508
615
|
resolved.push(entry.targetRel);
|
|
509
616
|
continue;
|
|
510
617
|
}
|
|
@@ -513,6 +620,7 @@ function resolveLatestConflict(cwd = process.cwd(), strategy = 'local') {
|
|
|
513
620
|
if (!fs.existsSync(sourcePath)) continue;
|
|
514
621
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
515
622
|
fs.copyFileSync(sourcePath, targetPath);
|
|
623
|
+
cleanupResolvedConflictSidecars(cwd, entry.targetRel);
|
|
516
624
|
resolved.push(entry.targetRel);
|
|
517
625
|
}
|
|
518
626
|
|
|
@@ -593,6 +701,11 @@ async function runSyncCycle(plan, cwd, options = {}) {
|
|
|
593
701
|
async function businessSync(args = process.argv.slice(3), cwd = process.cwd()) {
|
|
594
702
|
const options = resolveBusinessSyncOptions(args, cwd);
|
|
595
703
|
|
|
704
|
+
if (options.help) {
|
|
705
|
+
process.stdout.write(renderBusinessSyncHelp());
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
|
|
596
709
|
if (options.status) {
|
|
597
710
|
process.stdout.write(renderLocalSyncStatus(collectLocalSyncStatus(cwd, options)));
|
|
598
711
|
return;
|
|
@@ -611,14 +724,16 @@ async function businessSync(args = process.argv.slice(3), cwd = process.cwd()) {
|
|
|
611
724
|
const plan = buildBusinessSyncPlan(options);
|
|
612
725
|
|
|
613
726
|
if (!plan) {
|
|
614
|
-
console.error(
|
|
727
|
+
console.error(renderBusinessSyncHelp().trimEnd());
|
|
615
728
|
console.error('Run inside a business workspace or pass a business slug.');
|
|
616
729
|
process.exit(1);
|
|
617
730
|
}
|
|
618
731
|
|
|
619
732
|
console.log('');
|
|
620
|
-
console.log(`Syncing ${options.slug}
|
|
621
|
-
console.log(' scope:
|
|
733
|
+
console.log(`Syncing ${options.slug} business workspace...`);
|
|
734
|
+
console.log(' scope: full workspace');
|
|
735
|
+
console.log(' loop: Pull -> Review -> Publish');
|
|
736
|
+
console.log(' quest gate: exact files beat broad pushes');
|
|
622
737
|
if (options.watch) {
|
|
623
738
|
console.log(` watch: on (${options.intervalSec}s interval, ${options.debounceSec}s debounce)`);
|
|
624
739
|
}
|
|
@@ -632,18 +747,18 @@ async function businessSync(args = process.argv.slice(3), cwd = process.cwd()) {
|
|
|
632
747
|
});
|
|
633
748
|
if (!options.watch) return;
|
|
634
749
|
|
|
635
|
-
let lastSnapshot =
|
|
750
|
+
let lastSnapshot = collectWorkspaceSnapshot(cwd);
|
|
636
751
|
let running = false;
|
|
637
752
|
let quietTicks = 0;
|
|
638
753
|
let pendingLocal = false;
|
|
639
754
|
|
|
640
755
|
console.log('');
|
|
641
|
-
console.log('
|
|
756
|
+
console.log('Business workspace sync is watching this folder. Press Ctrl+C to stop.');
|
|
642
757
|
|
|
643
758
|
const tickMs = 1000;
|
|
644
759
|
setInterval(async () => {
|
|
645
760
|
if (running) return;
|
|
646
|
-
const current =
|
|
761
|
+
const current = collectWorkspaceSnapshot(cwd);
|
|
647
762
|
if (snapshotsDiffer(lastSnapshot, current)) {
|
|
648
763
|
pendingLocal = true;
|
|
649
764
|
quietTicks = 0;
|
|
@@ -664,14 +779,14 @@ async function businessSync(args = process.argv.slice(3), cwd = process.cwd()) {
|
|
|
664
779
|
running = true;
|
|
665
780
|
try {
|
|
666
781
|
console.log('');
|
|
667
|
-
console.log(shouldLocalSync ? 'Local
|
|
782
|
+
console.log(shouldLocalSync ? 'Local workspace changed. Syncing...' : 'Checking cloud workspace...');
|
|
668
783
|
await runSyncCycle(plan, cwd, {
|
|
669
784
|
dryRun: options.dryRun,
|
|
670
785
|
slug: options.slug,
|
|
671
786
|
writeStatus: !options.dryRun,
|
|
672
787
|
watch: true,
|
|
673
788
|
});
|
|
674
|
-
lastSnapshot =
|
|
789
|
+
lastSnapshot = collectWorkspaceSnapshot(cwd);
|
|
675
790
|
pendingLocal = false;
|
|
676
791
|
quietTicks = 0;
|
|
677
792
|
} catch (err) {
|
|
@@ -699,6 +814,8 @@ module.exports = {
|
|
|
699
814
|
buildBusinessSyncPlan,
|
|
700
815
|
canPreviewPush,
|
|
701
816
|
collectBrainSnapshot,
|
|
817
|
+
collectWorkspaceSnapshot,
|
|
818
|
+
collectWorkspaceWarnings,
|
|
702
819
|
collectLocalSyncStatus,
|
|
703
820
|
collectConflictResolutionEntries,
|
|
704
821
|
describeWatchFailure,
|
|
@@ -706,6 +823,7 @@ module.exports = {
|
|
|
706
823
|
readBusinessSlug,
|
|
707
824
|
renderLatestConflictReview,
|
|
708
825
|
renderLocalSyncStatus,
|
|
826
|
+
renderBusinessSyncHelp,
|
|
709
827
|
resolveLatestConflict,
|
|
710
828
|
resolveBusinessSyncOptions,
|
|
711
829
|
safeLineMerge,
|
package/commands/clean.js
CHANGED
|
@@ -60,7 +60,7 @@ function cleanAtris(options = {}) {
|
|
|
60
60
|
|
|
61
61
|
// Stale tasks
|
|
62
62
|
if (staleTasks.length > 0) {
|
|
63
|
-
console.log(`⚠ ${staleTasks.length} stale task
|
|
63
|
+
console.log(`⚠ ${staleTasks.length} stale ${staleTasks.length === 1 ? 'task' : 'tasks'} (claimed >3 days, not completed):`);
|
|
64
64
|
staleTasks.forEach(task => {
|
|
65
65
|
console.log(` • ${task.title.substring(0, 50)}${task.title.length > 50 ? '...' : ''}`);
|
|
66
66
|
});
|
|
@@ -71,12 +71,13 @@ function cleanAtris(options = {}) {
|
|
|
71
71
|
|
|
72
72
|
// Healed refs
|
|
73
73
|
if (healed > 0) {
|
|
74
|
-
|
|
74
|
+
const verb = options.dryRun ? 'Would heal' : 'Healed';
|
|
75
|
+
console.log(`✓ ${verb} ${healed} MAP.md ${healed === 1 ? 'reference' : 'references'}`);
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
// Unhealable refs
|
|
78
79
|
if (unhealable.length > 0) {
|
|
79
|
-
console.log(`⚠ ${unhealable.length} MAP.md ref
|
|
80
|
+
console.log(`⚠ ${unhealable.length} MAP.md ${unhealable.length === 1 ? 'ref' : 'refs'} couldn't be healed:`);
|
|
80
81
|
unhealable.slice(0, 3).forEach(ref => {
|
|
81
82
|
console.log(` • ${ref.file}:${ref.line} — ${ref.reason}`);
|
|
82
83
|
});
|
|
@@ -90,14 +91,16 @@ function cleanAtris(options = {}) {
|
|
|
90
91
|
|
|
91
92
|
// Archived journals
|
|
92
93
|
if (archived > 0) {
|
|
93
|
-
|
|
94
|
+
const verb = options.dryRun ? 'Would archive' : 'Archived';
|
|
95
|
+
console.log(`✓ ${verb} ${archived} old journal(s)`);
|
|
94
96
|
} else {
|
|
95
97
|
console.log('✓ No journals need archiving');
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
// Cleaned sections
|
|
99
101
|
if (cleaned > 0) {
|
|
100
|
-
|
|
102
|
+
const verb = options.dryRun ? 'Would clean' : 'Cleaned';
|
|
103
|
+
console.log(`✓ ${verb} ${cleaned} empty section(s)`);
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
console.log('');
|
|
@@ -105,7 +108,7 @@ function cleanAtris(options = {}) {
|
|
|
105
108
|
|
|
106
109
|
// Stale pages
|
|
107
110
|
if (stalePages.length > 0) {
|
|
108
|
-
console.log(`⚠ ${stalePages.length} stale page
|
|
111
|
+
console.log(`⚠ ${stalePages.length} stale ${stalePages.length === 1 ? 'page' : 'pages'} (source changed since last compiled):`);
|
|
109
112
|
stalePages.forEach(sp => {
|
|
110
113
|
console.log(` • ${path.relative(cwd, sp.page)} — stale source: ${sp.staleSource}`);
|
|
111
114
|
});
|