knight-os 0.1.1 β 0.1.2
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/bin/knight.js +57 -0
- package/package.json +1 -1
- package/src/migrate.js +306 -0
- package/src/setup.js +4 -0
- package/templates/AGENTS.md +24 -1
- package/templates/PROJECTS.md +38 -19
package/bin/knight.js
CHANGED
|
@@ -8,6 +8,13 @@ const readline = require('readline');
|
|
|
8
8
|
const { loadConfig, resolveWorkspace } = require('../src/config');
|
|
9
9
|
const { chat } = require('../src/chat');
|
|
10
10
|
const { setup } = require('../src/setup');
|
|
11
|
+
const {
|
|
12
|
+
runMigrations,
|
|
13
|
+
checkVersion,
|
|
14
|
+
refreshTemplates,
|
|
15
|
+
backupWorkspace,
|
|
16
|
+
CURRENT_DATA_VERSION,
|
|
17
|
+
} = require('../src/migrate');
|
|
11
18
|
|
|
12
19
|
const VERSION = '0.1.0';
|
|
13
20
|
const DEFAULT_WORKSPACE = path.join(process.env.HOME || '~', '.openclaw', 'workspace');
|
|
@@ -213,6 +220,52 @@ function commandVersion() {
|
|
|
213
220
|
console.log(`knight-os v${VERSION}`);
|
|
214
221
|
}
|
|
215
222
|
|
|
223
|
+
async function commandUpgrade() {
|
|
224
|
+
const workspace = DEFAULT_WORKSPACE;
|
|
225
|
+
|
|
226
|
+
console.log(`\nπ Knight OS β Upgrade Check`);
|
|
227
|
+
console.log(` Workspace: ${workspace}\n`);
|
|
228
|
+
|
|
229
|
+
if (!fs.existsSync(workspace)) {
|
|
230
|
+
console.log(' β Workspace not found. Run "knight setup" first.\n');
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 1. Run data migrations
|
|
235
|
+
const { migrated, backupPath, error } = runMigrations(workspace);
|
|
236
|
+
if (error) {
|
|
237
|
+
console.error(`\nβ Upgrade failed: ${error.message}\n`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (!migrated) {
|
|
242
|
+
const { currentVersion } = checkVersion(workspace);
|
|
243
|
+
console.log(` β
Already up to date (data v${currentVersion}).\n`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 2. Refresh non-protected template files (add new ones, skip existing)
|
|
247
|
+
console.log(' Checking for new template filesβ¦');
|
|
248
|
+
const { added, skipped } = refreshTemplates(workspace, TEMPLATES_DIR);
|
|
249
|
+
if (added.length > 0) {
|
|
250
|
+
console.log(` β
Added ${added.length} new file(s):`);
|
|
251
|
+
added.forEach((f) => console.log(` + ${f}`));
|
|
252
|
+
} else {
|
|
253
|
+
console.log(' β
No new template files.');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const protectedSkipped = skipped.filter((s) => s.includes('(protected)'));
|
|
257
|
+
if (protectedSkipped.length > 0) {
|
|
258
|
+
console.log(`\n π Protected files untouched (your personal data is safe):`);
|
|
259
|
+
protectedSkipped.forEach((f) => console.log(` ${f}`));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (backupPath) {
|
|
263
|
+
console.log(`\n π¦ Backup kept at:\n ${backupPath}`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
console.log(`\nβ
Upgrade complete. Workspace is at data v${CURRENT_DATA_VERSION}.\n`);
|
|
267
|
+
}
|
|
268
|
+
|
|
216
269
|
async function commandChat() {
|
|
217
270
|
const config = loadConfig();
|
|
218
271
|
const workspace = resolveWorkspace(config);
|
|
@@ -234,6 +287,9 @@ switch (command) {
|
|
|
234
287
|
case 'status':
|
|
235
288
|
commandStatus();
|
|
236
289
|
break;
|
|
290
|
+
case 'upgrade':
|
|
291
|
+
commandUpgrade();
|
|
292
|
+
break;
|
|
237
293
|
case 'version':
|
|
238
294
|
case '--version':
|
|
239
295
|
case '-v':
|
|
@@ -247,6 +303,7 @@ switch (command) {
|
|
|
247
303
|
console.log(' init Initialize a new workspace (standalone, no OpenClaw required)');
|
|
248
304
|
console.log(' chat Start interactive AI chat session');
|
|
249
305
|
console.log(' status Check workspace file status');
|
|
306
|
+
console.log(' upgrade Migrate workspace data + refresh template files safely');
|
|
250
307
|
console.log(' version Show version number');
|
|
251
308
|
console.log('');
|
|
252
309
|
break;
|
package/package.json
CHANGED
package/src/migrate.js
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* migrate.js β Safe upgrade framework for knight-os
|
|
5
|
+
*
|
|
6
|
+
* Design principles:
|
|
7
|
+
* 1. Data and code live in different places β npm upgrades never touch user data
|
|
8
|
+
* 2. Version file (.knight-version) tracks the data format version
|
|
9
|
+
* 3. Before any migration: full backup to .knight-backups/<timestamp>/
|
|
10
|
+
* 4. Migrations only ADD or TRANSFORM β never delete user content
|
|
11
|
+
* 5. Protected files (SOUL/MEMORY/USER/REDLINES) are never touched
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
18
|
+
// Constants
|
|
19
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
20
|
+
|
|
21
|
+
/** Current data format version expected by this version of knight-os */
|
|
22
|
+
const CURRENT_DATA_VERSION = 1;
|
|
23
|
+
|
|
24
|
+
/** Version file stored in the workspace root */
|
|
25
|
+
const VERSION_FILE = '.knight-version';
|
|
26
|
+
|
|
27
|
+
/** Backup directory inside the workspace */
|
|
28
|
+
const BACKUP_DIR = '.knight-backups';
|
|
29
|
+
|
|
30
|
+
/** Files that must never be overwritten during migration */
|
|
31
|
+
const PROTECTED_FILES = ['SOUL.md', 'MEMORY.md', 'USER.md', 'REDLINES.md'];
|
|
32
|
+
|
|
33
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
34
|
+
// Version helpers
|
|
35
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read the data version from the workspace.
|
|
39
|
+
* Returns 0 if the file doesn't exist (pre-versioning install).
|
|
40
|
+
*/
|
|
41
|
+
function readDataVersion(workspace) {
|
|
42
|
+
const versionPath = path.join(workspace, VERSION_FILE);
|
|
43
|
+
if (!fs.existsSync(versionPath)) return 0;
|
|
44
|
+
const raw = fs.readFileSync(versionPath, 'utf8').trim();
|
|
45
|
+
const parsed = parseInt(raw, 10);
|
|
46
|
+
return isNaN(parsed) ? 0 : parsed;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Write the data version to the workspace.
|
|
51
|
+
*/
|
|
52
|
+
function writeDataVersion(workspace, version) {
|
|
53
|
+
const versionPath = path.join(workspace, VERSION_FILE);
|
|
54
|
+
fs.writeFileSync(versionPath, String(version) + '\n', 'utf8');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
58
|
+
// Backup
|
|
59
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Recursively copy files from src to dst, skipping .knight-backups itself.
|
|
63
|
+
*/
|
|
64
|
+
function copyDirRecursive(src, dst) {
|
|
65
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
66
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
if (entry.name === BACKUP_DIR) continue; // don't backup backups
|
|
69
|
+
const srcPath = path.join(src, entry.name);
|
|
70
|
+
const dstPath = path.join(dst, entry.name);
|
|
71
|
+
if (entry.isDirectory()) {
|
|
72
|
+
copyDirRecursive(srcPath, dstPath);
|
|
73
|
+
} else {
|
|
74
|
+
fs.copyFileSync(srcPath, dstPath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a timestamped backup of the entire workspace.
|
|
81
|
+
* Returns the backup path so callers can report it to the user.
|
|
82
|
+
*/
|
|
83
|
+
function backupWorkspace(workspace) {
|
|
84
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
85
|
+
const backupPath = path.join(workspace, BACKUP_DIR, timestamp);
|
|
86
|
+
console.log(` π¦ Backing up workspace to ${backupPath} β¦`);
|
|
87
|
+
copyDirRecursive(workspace, backupPath);
|
|
88
|
+
console.log(` β
Backup complete.`);
|
|
89
|
+
return backupPath;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
93
|
+
// Migration registry
|
|
94
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Each migration:
|
|
98
|
+
* from β data version before this migration
|
|
99
|
+
* to β data version after this migration
|
|
100
|
+
* desc β human-readable description
|
|
101
|
+
* run(workspace) β the actual migration function; must not throw on clean workspaces
|
|
102
|
+
*/
|
|
103
|
+
const MIGRATIONS = [
|
|
104
|
+
{
|
|
105
|
+
from: 0,
|
|
106
|
+
to: 1,
|
|
107
|
+
desc: 'Bootstrap versioning β record baseline data version for existing installs',
|
|
108
|
+
run(workspace) {
|
|
109
|
+
// Ensure memory subdirectories exist (previously optional)
|
|
110
|
+
const memoryDirs = [
|
|
111
|
+
'memory/logs',
|
|
112
|
+
'memory/projects',
|
|
113
|
+
'memory/templates',
|
|
114
|
+
'memory/references',
|
|
115
|
+
];
|
|
116
|
+
for (const dir of memoryDirs) {
|
|
117
|
+
const fullPath = path.join(workspace, dir);
|
|
118
|
+
if (!fs.existsSync(fullPath)) {
|
|
119
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
120
|
+
console.log(` π Created missing directory: ${dir}/`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Add UPGRADE.md so users know migration ran
|
|
125
|
+
const upgradePath = path.join(workspace, 'UPGRADE.md');
|
|
126
|
+
if (!fs.existsSync(upgradePath)) {
|
|
127
|
+
fs.writeFileSync(
|
|
128
|
+
upgradePath,
|
|
129
|
+
[
|
|
130
|
+
'# Upgrade Log',
|
|
131
|
+
'',
|
|
132
|
+
'knight-os upgrade history for this workspace.',
|
|
133
|
+
'This file is auto-maintained β do not edit.',
|
|
134
|
+
'',
|
|
135
|
+
].join('\n'),
|
|
136
|
+
'utf8'
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
// Append an entry
|
|
140
|
+
const entry = `\n## v1 β ${new Date().toISOString().slice(0, 10)}\n- Baseline version established\n- memory/ subdirectories ensured\n`;
|
|
141
|
+
fs.appendFileSync(upgradePath, entry, 'utf8');
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// ββ Future migrations go here ββββββββββββββββββββββββββββββ
|
|
146
|
+
//
|
|
147
|
+
// Example v1 β v2:
|
|
148
|
+
// {
|
|
149
|
+
// from: 1,
|
|
150
|
+
// to: 2,
|
|
151
|
+
// desc: 'Rename ai-patterns.md β noa-patterns.md',
|
|
152
|
+
// run(workspace) {
|
|
153
|
+
// const oldPath = path.join(workspace, 'memory', 'ai-patterns.md');
|
|
154
|
+
// const newPath = path.join(workspace, 'memory', 'noa-patterns.md');
|
|
155
|
+
// if (fs.existsSync(oldPath) && !fs.existsSync(newPath)) {
|
|
156
|
+
// fs.renameSync(oldPath, newPath);
|
|
157
|
+
// }
|
|
158
|
+
// },
|
|
159
|
+
// },
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
163
|
+
// Migration runner
|
|
164
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if the workspace data is up to date.
|
|
168
|
+
* Returns { needsMigration: bool, currentVersion: number, targetVersion: number }
|
|
169
|
+
*/
|
|
170
|
+
function checkVersion(workspace) {
|
|
171
|
+
const currentVersion = readDataVersion(workspace);
|
|
172
|
+
return {
|
|
173
|
+
needsMigration: currentVersion < CURRENT_DATA_VERSION,
|
|
174
|
+
currentVersion,
|
|
175
|
+
targetVersion: CURRENT_DATA_VERSION,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Run all pending migrations for the workspace.
|
|
181
|
+
*
|
|
182
|
+
* - Skips silently if already up to date.
|
|
183
|
+
* - Creates a backup before running any migrations.
|
|
184
|
+
* - Runs migrations in order, updating the version file after each one.
|
|
185
|
+
* - If a migration throws, stops immediately (version file reflects last successful step).
|
|
186
|
+
*
|
|
187
|
+
* Returns { migrated: bool, backupPath: string|null, error: Error|null }
|
|
188
|
+
*/
|
|
189
|
+
function runMigrations(workspace) {
|
|
190
|
+
if (!fs.existsSync(workspace)) {
|
|
191
|
+
return { migrated: false, backupPath: null, error: null };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const { needsMigration, currentVersion, targetVersion } = checkVersion(workspace);
|
|
195
|
+
|
|
196
|
+
if (!needsMigration) {
|
|
197
|
+
return { migrated: false, backupPath: null, error: null };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const pending = MIGRATIONS.filter(
|
|
201
|
+
(m) => m.from >= currentVersion && m.to <= targetVersion
|
|
202
|
+
).sort((a, b) => a.from - b.from);
|
|
203
|
+
|
|
204
|
+
if (pending.length === 0) {
|
|
205
|
+
// No migration steps defined yet β just bump the version
|
|
206
|
+
writeDataVersion(workspace, targetVersion);
|
|
207
|
+
return { migrated: true, backupPath: null, error: null };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.log(`\nπ knight-os: workspace needs upgrade (v${currentVersion} β v${targetVersion})`);
|
|
211
|
+
|
|
212
|
+
// Backup before touching anything
|
|
213
|
+
let backupPath = null;
|
|
214
|
+
try {
|
|
215
|
+
backupPath = backupWorkspace(workspace);
|
|
216
|
+
} catch (err) {
|
|
217
|
+
return {
|
|
218
|
+
migrated: false,
|
|
219
|
+
backupPath: null,
|
|
220
|
+
error: new Error(`Backup failed, aborting migration: ${err.message}`),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Run each pending migration
|
|
225
|
+
for (const migration of pending) {
|
|
226
|
+
console.log(` βοΈ Migration ${migration.from}β${migration.to}: ${migration.desc}`);
|
|
227
|
+
try {
|
|
228
|
+
migration.run(workspace);
|
|
229
|
+
writeDataVersion(workspace, migration.to);
|
|
230
|
+
console.log(` β
Done.`);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
return {
|
|
233
|
+
migrated: false,
|
|
234
|
+
backupPath,
|
|
235
|
+
error: new Error(
|
|
236
|
+
`Migration ${migration.from}β${migration.to} failed: ${err.message}\n` +
|
|
237
|
+
`Your data is backed up at: ${backupPath}`
|
|
238
|
+
),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
console.log(`\nβ
Workspace upgraded to v${targetVersion}. Backup kept at:\n ${backupPath}\n`);
|
|
244
|
+
return { migrated: true, backupPath, error: null };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
248
|
+
// Template refresh (for `knight upgrade` command)
|
|
249
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Refresh non-protected template files in the workspace.
|
|
253
|
+
* Protected files (SOUL/MEMORY/USER/REDLINES) are always skipped.
|
|
254
|
+
* For all other files: only write if the file doesn't exist yet (safe default).
|
|
255
|
+
* Pass { force: true } to overwrite non-protected existing files.
|
|
256
|
+
*
|
|
257
|
+
* Returns { added: string[], skipped: string[] }
|
|
258
|
+
*/
|
|
259
|
+
function refreshTemplates(workspace, templatesDir, opts) {
|
|
260
|
+
opts = opts || {};
|
|
261
|
+
const added = [];
|
|
262
|
+
const skipped = [];
|
|
263
|
+
|
|
264
|
+
function walk(dir, base) {
|
|
265
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
266
|
+
for (const entry of entries) {
|
|
267
|
+
const relPath = path.relative(base, path.join(dir, entry.name));
|
|
268
|
+
if (entry.isDirectory()) {
|
|
269
|
+
walk(path.join(dir, entry.name), base);
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
const isRoot = !relPath.includes(path.sep);
|
|
273
|
+
const isProtected = isRoot && PROTECTED_FILES.includes(entry.name);
|
|
274
|
+
if (isProtected) {
|
|
275
|
+
skipped.push(relPath + ' (protected)');
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const dest = path.join(workspace, relPath);
|
|
279
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
280
|
+
if (!fs.existsSync(dest) || opts.force) {
|
|
281
|
+
fs.copyFileSync(path.join(dir, entry.name), dest);
|
|
282
|
+
added.push(relPath);
|
|
283
|
+
} else {
|
|
284
|
+
skipped.push(relPath);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
walk(templatesDir, templatesDir);
|
|
290
|
+
return { added, skipped };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
294
|
+
// Exports
|
|
295
|
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
296
|
+
|
|
297
|
+
module.exports = {
|
|
298
|
+
CURRENT_DATA_VERSION,
|
|
299
|
+
PROTECTED_FILES,
|
|
300
|
+
readDataVersion,
|
|
301
|
+
writeDataVersion,
|
|
302
|
+
backupWorkspace,
|
|
303
|
+
checkVersion,
|
|
304
|
+
runMigrations,
|
|
305
|
+
refreshTemplates,
|
|
306
|
+
};
|
package/src/setup.js
CHANGED
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const readline = require('readline');
|
|
7
7
|
const { execSync, spawnSync } = require('child_process');
|
|
8
|
+
const { runMigrations, writeDataVersion, CURRENT_DATA_VERSION } = require('./migrate');
|
|
8
9
|
|
|
9
10
|
const DEFAULT_WORKSPACE = path.join(os.homedir(), '.openclaw', 'workspace');
|
|
10
11
|
|
|
@@ -447,6 +448,9 @@ async function setup() {
|
|
|
447
448
|
}
|
|
448
449
|
}
|
|
449
450
|
|
|
451
|
+
// Record the data version so future upgrades know where to start
|
|
452
|
+
writeDataVersion(workspace, CURRENT_DATA_VERSION);
|
|
453
|
+
|
|
450
454
|
console.log(`\n${separator}`);
|
|
451
455
|
console.log('β
Knight OS setup complete!\n');
|
|
452
456
|
console.log(`Workspace: ${workspace}`);
|
package/templates/AGENTS.md
CHANGED
|
@@ -35,7 +35,30 @@ On session start, read files in this order:
|
|
|
35
35
|
6. `memory/ai-patterns.md` (load own behavior rules)
|
|
36
36
|
7. `USER.md` (load user profile)
|
|
37
37
|
8. `TOOLS.md` (load available tools)
|
|
38
|
-
9. `
|
|
38
|
+
9. `memory/YYYY-MM-DD.md` for today + yesterday (load recent context; skip if file doesn't exist)
|
|
39
|
+
10. `PROJECTS.md` (load project index β on-demand per project)
|
|
40
|
+
|
|
41
|
+
> **Why daily logs?** Without reading recent logs, the AI starts each session with no memory of what happened yesterday. Always load today + yesterday at boot.
|
|
42
|
+
|
|
43
|
+
## On-Demand Loading Trigger Table
|
|
44
|
+
|
|
45
|
+
Do NOT load everything at boot. Load these files only when the matching situation arises:
|
|
46
|
+
|
|
47
|
+
| Trigger | Load |
|
|
48
|
+
|---------|------|
|
|
49
|
+
| Replying to a message / adjusting tone | `memory/ai-patterns.md` chat section |
|
|
50
|
+
| Before executing a task | `memory/ai-patterns.md` exec section |
|
|
51
|
+
| User mentions a project by name | `memory/projects/<name>/main.md` |
|
|
52
|
+
| Executing a task tied to a project | `memory/projects/<name>/main.md` + latest log |
|
|
53
|
+
| Heartbeat / daily review | `PROJECTS.md` index only (no main.md) |
|
|
54
|
+
| Writing daily report | Update main.md β Current Sprint with today's progress |
|
|
55
|
+
| Writing to memory / log / daily report | Check `memory/ai-patterns.md` memory section |
|
|
56
|
+
| Received group message / someone @-mentioned | group handling rules |
|
|
57
|
+
| Involves code / development / PR | `memory/ai-patterns.md` code section |
|
|
58
|
+
| Using scripts / external tools | `memory/ai-patterns.md` tool section |
|
|
59
|
+
| Writing copy / articles / presentations | `memory/user-patterns.md` writing style section |}
|
|
60
|
+
|
|
61
|
+
> **Principle:** Static identity + rules β system prompt (always present). Long-term memory β load at session start. Project details + situational rules β lazy-load on demand. Per-turn context β conversation history only.
|
|
39
62
|
|
|
40
63
|
## Memory Structure Quick Reference
|
|
41
64
|
|
package/templates/PROJECTS.md
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
# PROJECTS.md β {{AI_NAME}} Project Overview
|
|
2
2
|
|
|
3
|
-
> Active projects index.
|
|
4
|
-
>
|
|
3
|
+
> Active projects index. Load this file at every session start β keep it short (target: under 40 lines).
|
|
4
|
+
> Full context lives in `memory/projects/<name>/main.md` β load on demand when the project is discussed.
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## Active Projects
|
|
9
9
|
|
|
10
|
-
|
|
|
11
|
-
|
|
12
|
-
|
|
|
10
|
+
| Name | Status | Priority | One-liner |
|
|
11
|
+
|------|--------|----------|-----------|
|
|
12
|
+
| _(add your first project)_ | π’ | β | _(what is this?)_ |
|
|
13
|
+
|
|
14
|
+
Status: π’ Active / π‘ On Hold / π΄ Blocked / β
Done
|
|
13
15
|
|
|
14
16
|
---
|
|
15
17
|
|
|
16
|
-
##
|
|
18
|
+
## Loading Rules
|
|
19
|
+
|
|
20
|
+
{{AI_NAME}} follows these rules for project context:
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
| When | Do |
|
|
23
|
+
|------|----|
|
|
24
|
+
| User mentions a project name | Load `memory/projects/<name>/main.md` |
|
|
25
|
+
| Executing a task related to a project | Load main.md + most recent project log |
|
|
26
|
+
| Heartbeat / daily review | Scan PROJECTS.md index only (no main.md) |
|
|
27
|
+
| Writing daily report | Update main.md β Current Sprint section with today's progress |
|
|
28
|
+
| Project not mentioned in session | Do NOT load main.md (save tokens) |
|
|
21
29
|
|
|
22
30
|
---
|
|
23
31
|
|
|
@@ -32,8 +40,8 @@ _(Move completed or abandoned projects here)_
|
|
|
32
40
|
```
|
|
33
41
|
memory/projects/
|
|
34
42
|
βββ <project-name>/
|
|
35
|
-
β βββ main.md #
|
|
36
|
-
β βββ logs/ #
|
|
43
|
+
β βββ main.md # Project "workbench" β goals, current sprint, blockers, decisions
|
|
44
|
+
β βββ logs/ # Deep history β load only when reviewing past decisions
|
|
37
45
|
```
|
|
38
46
|
|
|
39
47
|
### main.md template
|
|
@@ -43,18 +51,29 @@ memory/projects/
|
|
|
43
51
|
|
|
44
52
|
**Status:** π’ Active
|
|
45
53
|
**Started:** YYYY-MM-DD
|
|
46
|
-
**Goal:** One sentence.
|
|
54
|
+
**Goal:** One sentence. What does success look like?
|
|
47
55
|
|
|
48
|
-
##
|
|
49
|
-
[
|
|
56
|
+
## Current Sprint / This Week
|
|
57
|
+
- [ ] Task 1
|
|
58
|
+
- [ ] Task 2
|
|
59
|
+
_(Update this section at the end of each working session)_
|
|
60
|
+
|
|
61
|
+
## Open Questions / Blockers
|
|
62
|
+
- [YYYY-MM-DD] Question or blocker β owner or resolution
|
|
63
|
+
|
|
64
|
+
## Next Actions (Top 3)
|
|
65
|
+
1.
|
|
66
|
+
2.
|
|
67
|
+
3.
|
|
50
68
|
|
|
51
69
|
## Key Decisions
|
|
52
70
|
- YYYY-MM-DD: [Decision and rationale]
|
|
53
71
|
|
|
54
|
-
##
|
|
55
|
-
|
|
56
|
-
- [ ] M2: [Description]
|
|
72
|
+
## Context
|
|
73
|
+
[What is this project? Why does it matter? Who is it for?]
|
|
57
74
|
|
|
58
|
-
## Notes
|
|
59
|
-
[Anything
|
|
75
|
+
## Notes for {{AI_NAME}}
|
|
76
|
+
[Anything the AI must remember between sessions β constraints, preferences, gotchas]
|
|
60
77
|
```
|
|
78
|
+
|
|
79
|
+
> Keep main.md under 100 lines. If it grows longer, move older decisions/context to `logs/archive.md`.
|