atris 3.80.1 → 3.82.1
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/LICENSE +21 -0
- package/README.md +10 -0
- package/atris/atris.md +14 -0
- package/atris/features/README.md +15 -0
- package/atris/policies/README.md +24 -0
- package/atris/skills/README.md +61 -45
- package/atris/skills/design/SKILL.md +6 -5
- package/atris/skills/taste/SKILL.md +39 -0
- package/atris/skills/taste/notes.md +29 -0
- package/atris/skills/taste/principles.md +23 -0
- package/atris/skills/wiki/SKILL.md +14 -2
- package/atris/skills/wiki/index-lines.md +30 -0
- package/atris/skills/wiki/measure.md +20 -0
- package/atris/skills/wiki/note-shape.md +16 -0
- package/atris/skills/wiki/upkeep.md +16 -0
- package/atris/team/README.md +38 -0
- package/atris.md +14 -0
- package/bin/atris.js +1 -1
- package/commands/activate.js +1 -1
- package/commands/autoland.js +25 -26
- package/commands/autopilot-front.js +46 -1
- package/commands/autopilot.js +55 -25
- package/commands/clean.js +22 -11
- package/commands/computer.js +1 -1
- package/commands/doc-health.js +48 -1
- package/commands/improve.js +16 -3
- package/commands/init.js +4 -0
- package/commands/integrations.js +69 -1616
- package/commands/mission.js +109 -13
- package/commands/release.js +41 -10
- package/commands/status.js +1 -1
- package/commands/sync.js +12 -8
- package/commands/task.js +144 -118
- package/commands/unknowns.js +9 -5
- package/commands/visualize.js +1 -12
- package/commands/workflow.js +4 -3
- package/commands/worktree.js +68 -1602
- package/commands/xp.js +23 -2113
- package/commands/youtube.js +192 -3110
- package/lib/business-core.js +3 -4
- package/lib/campaign-run-lock.js +12 -1
- package/lib/engine-ask.js +1 -1
- package/lib/engine-receipt-sweep.js +64 -0
- package/lib/file-lock.js +175 -0
- package/lib/fleet.js +1 -1
- package/lib/integrations-core.js +1695 -0
- package/lib/json-file.js +22 -1
- package/lib/loop-doctor.js +36 -2
- package/lib/map-refs.js +68 -8
- package/lib/member-engine.js +1 -1
- package/lib/member-map-fold.js +49 -11
- package/lib/member-map-inbox.js +2 -1
- package/lib/mission-core.js +9 -2
- package/lib/recall-exam.js +171 -0
- package/lib/roster-markdown.js +2 -2
- package/lib/runner-command.js +5 -5
- package/lib/task-db.js +105 -37
- package/lib/taste-notes.js +35 -0
- package/lib/team-lineup.js +3 -3
- package/lib/todo.js +2 -0
- package/lib/wiki.js +11 -3
- package/lib/worktree-core.js +1517 -0
- package/lib/xp-core.js +2172 -0
- package/lib/youtube-core.js +3178 -0
- package/lib/zen-apply.js +3 -1
- package/package.json +2 -2
package/lib/task-db.js
CHANGED
|
@@ -826,6 +826,13 @@ function pidAlive(pid) {
|
|
|
826
826
|
try { process.kill(pid, 0); return true; } catch (err) { return err.code === 'EPERM'; }
|
|
827
827
|
}
|
|
828
828
|
|
|
829
|
+
// A claim made by hand (`atris task claim` with no --lease-minutes) has no lease, so before this nothing ever let
|
|
830
|
+
// it go: a session that died holding one kept the job off the board for good. Such a claim is let go once its job
|
|
831
|
+
// has seen no activity (no event of any kind) for this many days, whatever --stale-days says. It is measured by
|
|
832
|
+
// activity, not by a clock set at claim time, so a claim someone is still working on is never taken away, and it
|
|
833
|
+
// is long enough that the narrower --stale-days rule (fleet claims, or one named claimer) still decides first.
|
|
834
|
+
const UNLEASED_CLAIM_STALE_DAYS = 30;
|
|
835
|
+
|
|
829
836
|
function expireLeases(db, { now = Date.now(), staleDays, claimer, dryRun = false, isAlive = pidAlive } = {}) {
|
|
830
837
|
const timestamp = now instanceof Date ? now.getTime() : typeof now === 'string' ? Date.parse(now) : Number(now);
|
|
831
838
|
if (!Number.isFinite(timestamp)) throw new Error('invalid now');
|
|
@@ -846,6 +853,9 @@ function expireLeases(db, { now = Date.now(), staleDays, claimer, dryRun = false
|
|
|
846
853
|
} else if (!lease && staleDays != null && row.last_seen_at != null && row.last_seen_at < timestamp - Number(staleDays) * 86400000 && (claimer ? row.claimed_by === claimer : String(row.claimed_by || '').startsWith('fleet-'))) {
|
|
847
854
|
reason = `stranded with no activity for over ${staleDays} days`;
|
|
848
855
|
leaseUntil = new Date(row.last_seen_at + 3600000).toISOString();
|
|
856
|
+
} else if (!lease && row.last_seen_at != null && row.last_seen_at < timestamp - UNLEASED_CLAIM_STALE_DAYS * 86400000) {
|
|
857
|
+
reason = `stranded with no activity for over ${UNLEASED_CLAIM_STALE_DAYS} days`;
|
|
858
|
+
leaseUntil = new Date(row.last_seen_at + 3600000).toISOString();
|
|
849
859
|
} else continue;
|
|
850
860
|
const lastSeen = lease && Number.isFinite(until) && Number.isFinite(Number(lease.minutes))
|
|
851
861
|
? until - Number(lease.minutes) * 60000 : row.last_seen_at;
|
|
@@ -1130,7 +1140,7 @@ function reopenTask(db, { id, actor, reason, metadata: metadataPatch, fromReview
|
|
|
1130
1140
|
return { reopened: true, event, row: updated };
|
|
1131
1141
|
}
|
|
1132
1142
|
|
|
1133
|
-
function doneTask(db, { id, status, actor, allowReview = false, action, proof, autoAccepted = false } = {}) {
|
|
1143
|
+
function doneTask(db, { id, status, actor, allowReview = false, action, proof, autoAccepted = false, deferLogs = false } = {}) {
|
|
1134
1144
|
if (!id) throw new Error('id required');
|
|
1135
1145
|
const final = status || 'done';
|
|
1136
1146
|
if (!['done', 'failed'].includes(final)) throw new Error('status must be done|failed');
|
|
@@ -1139,18 +1149,22 @@ function doneTask(db, { id, status, actor, allowReview = false, action, proof, a
|
|
|
1139
1149
|
// read as a real failure. See archiveTask() below.
|
|
1140
1150
|
const now = Date.now();
|
|
1141
1151
|
const allowedStatuses = allowReview ? "'open', 'claimed', 'review'" : "'open', 'claimed'";
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1152
|
+
const eventActor = actor || process.env.ATRIS_AGENT_ID || process.env.USER || null;
|
|
1153
|
+
// The status change and its event are one write: a crash between them used to leave a done job with no
|
|
1154
|
+
// completed event. The daily log is a file, written only after the write is saved.
|
|
1155
|
+
const row = inWriteTransaction(db, () => {
|
|
1156
|
+
const result = withBusyRetry(() => db.prepare(`
|
|
1157
|
+
UPDATE tasks
|
|
1158
|
+
SET status = ?, done_at = ?, updated_at = ?
|
|
1159
|
+
WHERE id = ?
|
|
1160
|
+
AND status IN (${allowedStatuses})
|
|
1161
|
+
`).run(final, now, now, id));
|
|
1162
|
+
if (result.changes !== 1) return null;
|
|
1163
|
+
const updated = getTask(db, id);
|
|
1150
1164
|
appendTaskEvent(db, {
|
|
1151
1165
|
taskId: id,
|
|
1152
|
-
workspaceRoot:
|
|
1153
|
-
actor:
|
|
1166
|
+
workspaceRoot: updated.workspace_root,
|
|
1167
|
+
actor: eventActor,
|
|
1154
1168
|
eventType: final === 'done' ? 'completed' : 'blocked',
|
|
1155
1169
|
payload: {
|
|
1156
1170
|
status: final,
|
|
@@ -1158,17 +1172,40 @@ function doneTask(db, { id, status, actor, allowReview = false, action, proof, a
|
|
|
1158
1172
|
...(autoAccepted ? { auto_accepted: true } : {}),
|
|
1159
1173
|
},
|
|
1160
1174
|
});
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
}
|
|
1175
|
+
return updated;
|
|
1176
|
+
});
|
|
1177
|
+
if (row) {
|
|
1178
|
+
const logInput = { status: final, actor: eventActor, action: action || final, proof };
|
|
1179
|
+
// A caller that holds its own transaction (acceptTask) writes the log once that is saved.
|
|
1180
|
+
if (deferLogs) return { updated: true, row, logInput };
|
|
1181
|
+
const logs = appendTaskCompletionLogs(db, row, logInput);
|
|
1167
1182
|
return { updated: true, logs };
|
|
1168
1183
|
}
|
|
1169
1184
|
return { updated: false };
|
|
1170
1185
|
}
|
|
1171
1186
|
|
|
1187
|
+
// Accepting a job is two writes, the job marked done and the review that records it as accepted with its
|
|
1188
|
+
// reward. They are one transaction: a crash between them used to leave a done job with approval still
|
|
1189
|
+
// pending, and nothing scans done jobs to finish the accept (autoland reads only review). The daily log
|
|
1190
|
+
// is a file and is written once the transaction is saved.
|
|
1191
|
+
function acceptTask(db, { id, actor, proof, reward, lesson = '', nextTask = '', autoAccepted = false, careerXpEligible = true, clearedFields = [] } = {}) {
|
|
1192
|
+
if (!id) throw new Error('id required');
|
|
1193
|
+
const out = inWriteTransaction(db, () => {
|
|
1194
|
+
const done = doneTask(db, { id, status: 'done', actor, allowReview: true, action: 'accepted', proof, autoAccepted, deferLogs: true });
|
|
1195
|
+
if (!done.updated) return { updated: false };
|
|
1196
|
+
const reviewed = reviewTask(db, { id, actor, reward, lesson, nextTask, proof, careerXpEligible, clearedFields, autoAccepted });
|
|
1197
|
+
return { updated: true, reviewed, row: done.row, logInput: done.logInput };
|
|
1198
|
+
});
|
|
1199
|
+
if (!out.updated) return { updated: false };
|
|
1200
|
+
let logs = {};
|
|
1201
|
+
try {
|
|
1202
|
+
logs = appendTaskCompletionLogs(db, out.row, out.logInput);
|
|
1203
|
+
} catch (err) {
|
|
1204
|
+
if (process.env.ATRIS_DEBUG) process.stderr.write(`accept log failed: ${String(err?.message ?? err).replace(/[\r\n]+/g, ' ')}\n`);
|
|
1205
|
+
}
|
|
1206
|
+
return { updated: true, reviewed: out.reviewed, logs };
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1172
1209
|
function reapMissionBlockerTasks(db, { workspaceRoot: ws, missions = [], actor, rows = null, skipLogs = false } = {}) {
|
|
1173
1210
|
const missionById = new Map((Array.isArray(missions) ? missions : [])
|
|
1174
1211
|
.filter(mission => mission && mission.id)
|
|
@@ -1825,32 +1862,55 @@ function releaseClosedPullRequest(db, { id, actor, workspaceRoot: ws, expected,
|
|
|
1825
1862
|
return { released: true, row: out.row, event: out.event };
|
|
1826
1863
|
}
|
|
1827
1864
|
|
|
1865
|
+
// Read a job's metadata, change it, and write it back with the write lock held, so two writers that each
|
|
1866
|
+
// read the whole JSON cannot drop each other's keys. The mutator gets a copy and changes it in place or
|
|
1867
|
+
// returns a new object; returning false writes nothing. Keep the mutator fast and free of I/O: it runs
|
|
1868
|
+
// inside BEGIN IMMEDIATE. touch: false leaves updated_at as it was.
|
|
1869
|
+
// Returns null when the job does not exist, else { row, metadata, changed }.
|
|
1870
|
+
function updateTaskMetadata(db, id, mutator, { touch = true, now = Date.now() } = {}) {
|
|
1871
|
+
if (!id) throw new Error('id required');
|
|
1872
|
+
if (typeof mutator !== 'function') throw new Error('mutator required');
|
|
1873
|
+
return inWriteTransaction(db, () => {
|
|
1874
|
+
const row = getTask(db, id);
|
|
1875
|
+
if (!row) return null;
|
|
1876
|
+
const metadata = row.metadata && typeof row.metadata === 'object' ? { ...row.metadata } : {};
|
|
1877
|
+
const out = mutator(metadata, row);
|
|
1878
|
+
if (out === false) return { row, metadata, changed: false };
|
|
1879
|
+
const next = out && typeof out === 'object' ? out : metadata;
|
|
1880
|
+
const text = JSON.stringify(next);
|
|
1881
|
+
withBusyRetry(() => (touch
|
|
1882
|
+
? db.prepare('UPDATE tasks SET metadata = ?, updated_at = ? WHERE id = ?').run(text, now, id)
|
|
1883
|
+
: db.prepare('UPDATE tasks SET metadata = ? WHERE id = ?').run(text, id)));
|
|
1884
|
+
return { row, metadata: next, changed: true };
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1828
1888
|
function setTaskResult(db, { id, actor, result }) {
|
|
1829
1889
|
if (!id) throw new Error('id required');
|
|
1830
1890
|
const text = cleanLandingText(result);
|
|
1831
1891
|
if (!text) throw new Error('result required');
|
|
1832
|
-
const row = getTask(db, id);
|
|
1833
|
-
if (!row) return { saved: false, reason: 'not_found' };
|
|
1834
1892
|
const now = Date.now();
|
|
1835
|
-
const
|
|
1836
|
-
metadata
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1893
|
+
const resultBy = actor || process.env.ATRIS_AGENT_ID || process.env.USER || null;
|
|
1894
|
+
// The metadata write and its event are one transaction, and the metadata is read under the lock.
|
|
1895
|
+
const out = inWriteTransaction(db, () => {
|
|
1896
|
+
const saved = updateTaskMetadata(db, id, (metadata) => {
|
|
1897
|
+
metadata.result = text;
|
|
1898
|
+
metadata.result_at = new Date(now).toISOString();
|
|
1899
|
+
metadata.result_by = resultBy;
|
|
1900
|
+
}, { now });
|
|
1901
|
+
if (!saved) return null;
|
|
1902
|
+
const row = getTask(db, id);
|
|
1903
|
+
const written = appendTaskEvent(db, {
|
|
1904
|
+
taskId: id,
|
|
1905
|
+
workspaceRoot: row.workspace_root,
|
|
1906
|
+
actor: resultBy,
|
|
1907
|
+
eventType: 'result_set',
|
|
1908
|
+
payload: { result: text },
|
|
1909
|
+
});
|
|
1910
|
+
return { latest: row, event: written };
|
|
1853
1911
|
});
|
|
1912
|
+
if (!out) return { saved: false, reason: 'not_found' };
|
|
1913
|
+
const { latest, event } = out;
|
|
1854
1914
|
return { saved: true, event, row: latest };
|
|
1855
1915
|
}
|
|
1856
1916
|
|
|
@@ -3053,6 +3113,11 @@ function taskProjection(db, {
|
|
|
3053
3113
|
};
|
|
3054
3114
|
}
|
|
3055
3115
|
|
|
3116
|
+
// Status names: the database keeps `failed` (doneTask writes it, its event is `blocked`), and
|
|
3117
|
+
// TODO.md shows those rows under "## Blocked". The words differ on purpose and the stored value
|
|
3118
|
+
// is not renamed: every reader, filter, and old database already says `failed`, and a person
|
|
3119
|
+
// reads "blocked" as "needs someone", which is what a failed job is. `atris task list --status
|
|
3120
|
+
// failed` is how to list them.
|
|
3056
3121
|
function renderTodoMarkdown(rows, { title = 'TODO.md', doneLimit = TODO_RENDER_DONE_LIMIT, failedLimit = TODO_RENDER_FAILED_LIMIT, refRows = rows, preservedSections = [] } = {}) {
|
|
3057
3122
|
const displayRows = withTaskDisplayRefs(rows, refRows);
|
|
3058
3123
|
const buckets = {
|
|
@@ -3192,6 +3257,7 @@ module.exports = {
|
|
|
3192
3257
|
renewLease,
|
|
3193
3258
|
clearLease,
|
|
3194
3259
|
expireLeases,
|
|
3260
|
+
UNLEASED_CLAIM_STALE_DAYS,
|
|
3195
3261
|
previewExpiredLeases,
|
|
3196
3262
|
sweepExpiredLeases,
|
|
3197
3263
|
releaseTask,
|
|
@@ -3201,6 +3267,7 @@ module.exports = {
|
|
|
3201
3267
|
backlogTask,
|
|
3202
3268
|
clearPlanTasks,
|
|
3203
3269
|
doneTask,
|
|
3270
|
+
acceptTask,
|
|
3204
3271
|
reapMissionBlockerTasks,
|
|
3205
3272
|
archiveTask,
|
|
3206
3273
|
archiveNotNeeded,
|
|
@@ -3212,6 +3279,7 @@ module.exports = {
|
|
|
3212
3279
|
landHeldMerge,
|
|
3213
3280
|
releaseClosedPullRequest,
|
|
3214
3281
|
setTaskResult,
|
|
3282
|
+
updateTaskMetadata,
|
|
3215
3283
|
reviseTask,
|
|
3216
3284
|
stageTask,
|
|
3217
3285
|
noteTask,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Taste is personal, so it lives outside the shipped skill: the project's
|
|
4
|
+
// atris/taste/TASTE.md and the person's ~/.atris/taste/TASTE.md. Sync replaces
|
|
5
|
+
// atris/skills/ on every update and never touches either file. Init and update
|
|
6
|
+
// only create the project starter when it is missing.
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const PROJECT_TASTE_FILE = path.join('taste', 'TASTE.md');
|
|
12
|
+
|
|
13
|
+
const STARTER = `# Taste
|
|
14
|
+
|
|
15
|
+
How this project should look and read: its brand and its voice. Agents read this before making anything a person will see. The person's own taste in \`~/.atris/taste/TASTE.md\` comes second.
|
|
16
|
+
|
|
17
|
+
Each line gives the rule in plain words and links to a short note with the reason and real examples. \`atris/skills/taste/notes.md\` shows the shape.
|
|
18
|
+
|
|
19
|
+
## The list
|
|
20
|
+
|
|
21
|
+
- (Nothing recorded yet. When someone corrects how something looks or reads, add the rule here.)
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
// Returns true when the starter was written (or would be, in a dry run).
|
|
25
|
+
function ensureProjectTasteFile(atrisDir, { dryRun = false } = {}) {
|
|
26
|
+
const file = path.join(atrisDir, PROJECT_TASTE_FILE);
|
|
27
|
+
if (fs.existsSync(file)) return false;
|
|
28
|
+
if (!dryRun) {
|
|
29
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
30
|
+
fs.writeFileSync(file, STARTER, { flag: 'wx' });
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { ensureProjectTasteFile };
|
package/lib/team-lineup.js
CHANGED
|
@@ -19,15 +19,15 @@ function orderJobs(rows) {
|
|
|
19
19
|
return [...builtIn, ...list.filter((row) => !builtIn.includes(row))];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
//
|
|
22
|
+
// Friendly label for a claude model, "<tool> <model>" for anything else, and
|
|
23
23
|
// just the tool when it runs its own default.
|
|
24
24
|
function shortRuns(view) {
|
|
25
25
|
if (!view || !view.engine) return '';
|
|
26
26
|
const tool = rosterToolLabel(view.engine);
|
|
27
27
|
if (!view.model) return tool;
|
|
28
28
|
const label = modelLabel(view.model);
|
|
29
|
-
// "atris:fast" on atris fast says the tool twice;
|
|
30
|
-
// already names it.
|
|
29
|
+
// "atris:fast" on atris fast says the tool twice; a label starting with the
|
|
30
|
+
// tool name already names it.
|
|
31
31
|
const plain = (text) => String(text).toLowerCase().replace(/[^a-z0-9.]+/g, ' ').trim();
|
|
32
32
|
if (plain(label) === plain(tool)) return tool;
|
|
33
33
|
if (CLAUDE_FAMILY.includes(view.engine) || plain(label).startsWith(`${plain(tool)} `)) return label;
|
package/lib/todo.js
CHANGED
|
@@ -61,6 +61,8 @@ function dbBuckets(workspaceRoot) {
|
|
|
61
61
|
if (r.status === 'open') backlog.push(shaped);
|
|
62
62
|
else if (r.status === 'claimed') inProgress.push(shaped);
|
|
63
63
|
else if (r.status === 'review') review.push(shaped);
|
|
64
|
+
// `failed` is the stored name for a job TODO.md shows under "Blocked" (see renderTodoMarkdown
|
|
65
|
+
// in lib/task-db.js); this shim has no blocked bucket, so it files them with the finished rows.
|
|
64
66
|
else if (r.status === 'done' || r.status === 'failed') completed.push(shaped);
|
|
65
67
|
}
|
|
66
68
|
return { backlog, inProgress, review, completed, sourceKeys, sourceRefs };
|
package/lib/wiki.js
CHANGED
|
@@ -71,12 +71,17 @@ Turn raw project context into a living memory the next agent can pick up cold.
|
|
|
71
71
|
- Add cross-references with \`[[${wikiLinkRoot}/...]]\` links.
|
|
72
72
|
- Keep \`index.md\`, \`log.md\`, and \`STATUS.md\` in sync with page changes.
|
|
73
73
|
- If something is unclear or contradictory, say so directly.
|
|
74
|
+
- One idea per page, under about 80 lines, with real examples right under the rule they prove.
|
|
75
|
+
- Each \`index.md\` line gives the rule or answer and says when to open the page. Never a bare topic name.
|
|
76
|
+
- Keep \`index.md\` to one screen. Move text instead of rewriting it, and list anything deleted in \`log.md\`.
|
|
74
77
|
`;
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
function indexMarkdown() {
|
|
78
81
|
return `# Atris Wiki Index
|
|
79
82
|
|
|
83
|
+
Each line gives the rule or answer and says when to open the page behind it.
|
|
84
|
+
|
|
80
85
|
## People
|
|
81
86
|
|
|
82
87
|
## Systems
|
|
@@ -874,7 +879,9 @@ Rules:
|
|
|
874
879
|
- Merge new info into existing pages instead of replacing them
|
|
875
880
|
- Keep index.md, log.md, and STATUS.md current
|
|
876
881
|
- Flag contradictions directly instead of smoothing them over
|
|
877
|
-
- Never modify the raw source documents you ingested
|
|
882
|
+
- Never modify the raw source documents you ingested
|
|
883
|
+
- One idea per page, under about 80 lines, with examples under the rule they prove
|
|
884
|
+
- An index line gives the rule or answer, then says when to open the page`;
|
|
878
885
|
}
|
|
879
886
|
|
|
880
887
|
function buildIngestPrompt(sourceValue, mode = 'public') {
|
|
@@ -889,7 +896,7 @@ Workflow:
|
|
|
889
896
|
3. Extract people, systems, and concepts worth preserving
|
|
890
897
|
4. Create or update pages under ${wikiRoot}/, merging with existing facts instead of replacing them
|
|
891
898
|
5. Add cross-references using [[${wikiLinkRoot}/...]] links
|
|
892
|
-
6. Update ${wikiRoot}/index.md with one
|
|
899
|
+
6. Update ${wikiRoot}/index.md with one line per touched page: the rule or answer, then when to open it
|
|
893
900
|
7. Append an INGEST entry to ${wikiRoot}/log.md under today's date
|
|
894
901
|
8. Refresh ${wikiRoot}/STATUS.md in plain English for a non-technical reader
|
|
895
902
|
|
|
@@ -945,7 +952,8 @@ Keep rules:
|
|
|
945
952
|
- Entries marked as user-stated conventions or explicit remember-this survive.
|
|
946
953
|
- Every kept entry keeps its provenance as a source link or date.
|
|
947
954
|
|
|
948
|
-
Consolidate overlapping entries, preserve the strongest source-backed version, and leave index.md and page links consistent
|
|
955
|
+
Consolidate overlapping entries, preserve the strongest source-backed version, and leave index.md and page links consistent.
|
|
956
|
+
Split any page over about 80 lines or holding two ideas. Rewrite bare topic lines in index.md so each says when to open the page.`;
|
|
949
957
|
}
|
|
950
958
|
|
|
951
959
|
module.exports = {
|