kracked-core 1.0.0 → 1.4.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 +41 -3
- package/bin/kracked-core.mjs +29 -2
- package/package.json +1 -1
- package/src/detect.mjs +67 -12
- package/src/prompt.mjs +339 -0
- package/src/scaffold.mjs +99 -20
- package/src/uninstall.mjs +214 -0
- package/src/update.mjs +150 -0
- package/src/wizard.mjs +167 -82
- package/templates/loaders/AGENTS.md +2 -0
- package/templates/loaders/CLAUDE.md +1 -0
- package/templates/loaders/antigravity-rules.md +1 -0
- package/templates/project/sdd/README.md +46 -0
- package/templates/project/sdd/architecture/.gitkeep +0 -0
- package/templates/project/sdd/architecture/_TEMPLATE.md +51 -0
- package/templates/project/sdd/architecture/decisions/_TEMPLATE.md +40 -0
- package/templates/project/sdd/epics/.gitkeep +0 -0
- package/templates/project/sdd/epics/_TEMPLATE.md +34 -0
- package/templates/project/sdd/specs/_TEMPLATE.md +44 -0
- package/templates/project/sdd/stories/.gitkeep +0 -0
- package/templates/project/sdd/stories/_TEMPLATE.md +33 -0
- package/templates/skills/kracked-boot/SKILL.md +13 -5
- package/templates/skills/kracked-identity/SKILL.md +70 -0
- package/templates/skills/kracked-sdd/SKILL.md +36 -1
package/src/wizard.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Interactive prompt flow for `npx kracked-core init`.
|
|
2
2
|
// Follows the wizard flow in CONTRACT.md exactly — question order matters.
|
|
3
3
|
|
|
4
|
-
import readline from 'node:readline/promises';
|
|
5
4
|
import { stdin, stdout } from 'node:process';
|
|
6
5
|
import fs from 'node:fs';
|
|
7
6
|
import path from 'node:path';
|
|
@@ -13,7 +12,10 @@ import {
|
|
|
13
12
|
classifyProjectDir,
|
|
14
13
|
scanExistingProject,
|
|
15
14
|
stackSummaryLine,
|
|
15
|
+
detectExistingAgentSetup,
|
|
16
|
+
sanitizeScanned,
|
|
16
17
|
} from './detect.mjs';
|
|
18
|
+
import { select, checkbox, confirm, input } from './prompt.mjs';
|
|
17
19
|
import {
|
|
18
20
|
writeGlobalMemory,
|
|
19
21
|
registerProject,
|
|
@@ -37,6 +39,31 @@ function isoDate() {
|
|
|
37
39
|
return new Date().toISOString().slice(0, 10);
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
// Installing into these scatters loader files across the filesystem, and
|
|
43
|
+
// installing into the home dir makes `<project>/.kracked` collide with global
|
|
44
|
+
// memory. A typo like `/` should not silently create a tree.
|
|
45
|
+
const FORBIDDEN_DIRS = ['/', '/etc', '/usr', '/bin', '/sbin', '/var', '/System', '/Library'];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Refuse obviously wrong install targets before anything is written.
|
|
49
|
+
* Returns an error string, or null when the path is fine.
|
|
50
|
+
*/
|
|
51
|
+
function rejectUnsafeProjectDir(dir) {
|
|
52
|
+
const resolved = path.resolve(dir);
|
|
53
|
+
|
|
54
|
+
if (FORBIDDEN_DIRS.includes(resolved)) {
|
|
55
|
+
return `${resolved} is a system directory — pick a project folder instead.`;
|
|
56
|
+
}
|
|
57
|
+
if (resolved === path.resolve(os.homedir())) {
|
|
58
|
+
return (
|
|
59
|
+
'That\'s your home directory. Installing here would put loader files loose in\n' +
|
|
60
|
+
' your home folder and collide with global memory at ~/.kracked.\n' +
|
|
61
|
+
' Make a project folder first, then run this inside it.'
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
40
67
|
/**
|
|
41
68
|
* Turn detected package.json scripts into a ready-to-read "how to run it" block.
|
|
42
69
|
* The scan already knows these commands — writing them into project.md is the
|
|
@@ -68,87 +95,91 @@ export async function runInit() {
|
|
|
68
95
|
return;
|
|
69
96
|
}
|
|
70
97
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
let interrupted = false;
|
|
98
|
+
// Ctrl+C / Ctrl+D are handled inside prompt.mjs, which owns stdin in raw
|
|
99
|
+
// mode. Keep a SIGINT guard for interrupts arriving between prompts.
|
|
74
100
|
const onSigint = () => {
|
|
75
|
-
interrupted = true;
|
|
76
101
|
stdout.write('\n\nCancelled. Nothing after this point was written.\n');
|
|
77
|
-
rl.close();
|
|
78
102
|
process.exit(130);
|
|
79
103
|
};
|
|
80
104
|
process.on('SIGINT', onSigint);
|
|
81
105
|
|
|
82
106
|
try {
|
|
83
|
-
await wizardFlow(
|
|
84
|
-
} catch (err) {
|
|
85
|
-
// Ctrl+D (EOF) mid-question surfaces as readline's AbortError. Treat it
|
|
86
|
-
// the same as Ctrl+C — a clean cancel, not a crash with a scary stack.
|
|
87
|
-
if (err.code === 'ABORT_ERR' || /ctrl\+d/i.test(err.message || '')) {
|
|
88
|
-
stdout.write('\n\nCancelled. Nothing after this point was written.\n');
|
|
89
|
-
process.exitCode = 130;
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
throw err;
|
|
107
|
+
await wizardFlow();
|
|
93
108
|
} finally {
|
|
94
109
|
process.off('SIGINT', onSigint);
|
|
95
|
-
|
|
110
|
+
stdin.pause();
|
|
96
111
|
}
|
|
97
112
|
}
|
|
98
113
|
|
|
99
|
-
/**
|
|
100
|
-
async function
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
/** Prompt for how to resolve a file-already-exists conflict. */
|
|
115
|
+
async function askConflict(destPath) {
|
|
116
|
+
stdout.write(`\n Already exists: ${destPath}\n`);
|
|
117
|
+
return select(
|
|
118
|
+
' What should we do?',
|
|
119
|
+
[
|
|
120
|
+
{ label: 'Skip', value: 'skip', hint: 'keep the existing file' },
|
|
121
|
+
{ label: 'Overwrite', value: 'overwrite', hint: 'replace it' },
|
|
122
|
+
{ label: 'Write alongside', value: 'alongside', hint: 'save as .kracked-new' },
|
|
123
|
+
],
|
|
124
|
+
0
|
|
125
|
+
);
|
|
104
126
|
}
|
|
105
127
|
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Warn before writing into a directory another agent system already governs.
|
|
130
|
+
* A CLAUDE.md we didn't write belongs to someone else's setup — overwriting it
|
|
131
|
+
* silently replaces that agent's identity. Ask first, every time.
|
|
132
|
+
*/
|
|
133
|
+
async function warnOnExistingSetup(projectDir) {
|
|
134
|
+
const found = detectExistingAgentSetup(projectDir).filter((f) => !f.ours);
|
|
135
|
+
if (found.length === 0) return true;
|
|
136
|
+
|
|
137
|
+
stdout.write('\n Heads up — this looks like it already has an agent setup:\n');
|
|
138
|
+
for (const f of found) {
|
|
139
|
+
const where = f.scope === 'global' ? 'global' : 'this project';
|
|
140
|
+
stdout.write(` • ${f.file} ${c_dim(`(${where})`)}\n`);
|
|
141
|
+
}
|
|
142
|
+
stdout.write(
|
|
143
|
+
'\n Installing here would overwrite those loaders and change who your\n' +
|
|
144
|
+
' agent is in this directory. Existing files are never replaced without\n' +
|
|
145
|
+
' asking, but it is easy to end up with two systems giving different\n' +
|
|
146
|
+
' instructions.\n\n'
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return confirm('Continue installing here?', false);
|
|
112
150
|
}
|
|
113
151
|
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const answer = (
|
|
118
|
-
await rl.question(' skip / overwrite / write alongside as .kracked-new? [skip] ')
|
|
119
|
-
).trim().toLowerCase();
|
|
120
|
-
|
|
121
|
-
if (answer === 'o' || answer === 'overwrite') return 'overwrite';
|
|
122
|
-
if (answer === 'a' || answer === 'alongside' || answer === 'write alongside') return 'alongside';
|
|
123
|
-
return 'skip';
|
|
152
|
+
/** Minimal dim helper for wizard-level notices. */
|
|
153
|
+
function c_dim(s) {
|
|
154
|
+
return `[2m${s}[0m`;
|
|
124
155
|
}
|
|
125
156
|
|
|
126
|
-
async function wizardFlow(
|
|
157
|
+
async function wizardFlow() {
|
|
127
158
|
const created = [];
|
|
128
159
|
const reporter = (entry) => created.push(entry);
|
|
129
|
-
const conflictAsk = (destPath) => askConflict(
|
|
160
|
+
const conflictAsk = (destPath) => askConflict(destPath);
|
|
130
161
|
|
|
131
162
|
stdout.write('kracked-core — set up memory for your AI coding agent\n\n');
|
|
132
163
|
|
|
133
164
|
// 1. Agent name
|
|
134
|
-
const agentName = (await
|
|
165
|
+
const agentName = sanitizeScanned(await input('What should we call your agent?', 'KC'), 40) || 'KC';
|
|
135
166
|
|
|
136
167
|
// 2. User name
|
|
137
168
|
const systemUser = os.userInfo().username || 'you';
|
|
138
|
-
const userName = (await
|
|
169
|
+
const userName = sanitizeScanned(await input('Your name?', systemUser), 40) || systemUser;
|
|
139
170
|
|
|
140
|
-
// 3. Global memory
|
|
171
|
+
// 3. Global memory — always installed. It holds identity, preferences and
|
|
172
|
+
// cross-project lessons, and boot depends on it; making it optional only
|
|
173
|
+
// created a way to end up with a half-installed system that can't boot.
|
|
174
|
+
// Existing files are still never overwritten without asking.
|
|
141
175
|
const hasGlobal = globalMemoryExists();
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
} else {
|
|
146
|
-
const reuse = await askYesNo(rl, 'Global memory found. Reuse it?', true);
|
|
147
|
-
setUpGlobal = !reuse; // if not reusing, we still (re)write it below
|
|
176
|
+
const setUpGlobal = true;
|
|
177
|
+
if (hasGlobal) {
|
|
178
|
+
stdout.write(`\n Global memory found at ${globalMemoryDir()} — reusing it.\n`);
|
|
148
179
|
}
|
|
149
180
|
|
|
150
181
|
// 4. Project setup
|
|
151
|
-
const setUpProject = await
|
|
182
|
+
const setUpProject = await confirm('Set up this project?', true);
|
|
152
183
|
|
|
153
184
|
let projectDir = process.cwd();
|
|
154
185
|
let projectMode = null; // 'new' | 'existing'
|
|
@@ -156,24 +187,58 @@ async function wizardFlow(rl) {
|
|
|
156
187
|
let projectSummary = null;
|
|
157
188
|
|
|
158
189
|
if (setUpProject) {
|
|
159
|
-
const pathAnswer = await
|
|
190
|
+
const pathAnswer = await input('Project directory?', '.');
|
|
160
191
|
const expanded = expandTilde(pathAnswer);
|
|
161
192
|
projectDir = path.resolve(expanded === '' ? '.' : expanded);
|
|
162
193
|
|
|
163
|
-
const
|
|
194
|
+
const unsafe = rejectUnsafeProjectDir(projectDir);
|
|
195
|
+
if (unsafe) {
|
|
196
|
+
stdout.write(`\n ${unsafe}\n\nStopped — nothing was written.\n`);
|
|
197
|
+
process.exitCode = 1;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Already installed here? 27 individual conflict prompts is not a sane
|
|
202
|
+
// default — offer the three real intents up front instead.
|
|
203
|
+
if (fs.existsSync(path.join(projectDir, '.kracked'))) {
|
|
204
|
+
stdout.write('\n kracked-core is already set up in this project.\n\n');
|
|
205
|
+
const action = await select('What would you like to do?', [
|
|
206
|
+
{ label: 'Refresh package files', value: 'refresh', hint: 'skills + loaders, keeps all your memory' },
|
|
207
|
+
{ label: 'Reinstall everything', value: 'reinstall', hint: 'asks per file before replacing' },
|
|
208
|
+
{ label: 'Cancel', value: 'cancel' },
|
|
209
|
+
], 0);
|
|
210
|
+
|
|
211
|
+
if (action === 'cancel') {
|
|
212
|
+
stdout.write('\nCancelled — nothing changed.\n');
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (action === 'refresh') {
|
|
216
|
+
const { runUpdate } = await import('./update.mjs');
|
|
217
|
+
process.chdir(projectDir);
|
|
218
|
+
await runUpdate();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
164
222
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
await ask(rl, 'New project or existing codebase? (new/existing)', 'new')
|
|
173
|
-
).toLowerCase();
|
|
174
|
-
projectMode = modeAnswer.startsWith('e') ? 'existing' : 'new';
|
|
223
|
+
// Another agent system already here? Ask before touching its loaders.
|
|
224
|
+
if (fs.existsSync(projectDir)) {
|
|
225
|
+
const proceed = await warnOnExistingSetup(projectDir);
|
|
226
|
+
if (!proceed) {
|
|
227
|
+
stdout.write('\nStopped — nothing was written to this project.\n');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
175
230
|
}
|
|
176
231
|
|
|
232
|
+
const classification = classifyProjectDir(projectDir);
|
|
233
|
+
projectMode = await select(
|
|
234
|
+
'Is this a new project or an existing codebase?',
|
|
235
|
+
[
|
|
236
|
+
{ label: 'Existing codebase', value: 'existing', hint: 'scan it and draft the memory' },
|
|
237
|
+
{ label: 'New project', value: 'new', hint: 'start with a blank scaffold' },
|
|
238
|
+
],
|
|
239
|
+
classification === 'existing' ? 0 : 1
|
|
240
|
+
);
|
|
241
|
+
|
|
177
242
|
if (projectMode === 'existing') {
|
|
178
243
|
if (!fs.existsSync(projectDir)) {
|
|
179
244
|
stdout.write(`\n ${projectDir} doesn't exist yet — nothing to scan. Treating as new.\n`);
|
|
@@ -192,10 +257,10 @@ async function wizardFlow(rl) {
|
|
|
192
257
|
}
|
|
193
258
|
stdout.write('\n');
|
|
194
259
|
|
|
195
|
-
const confirmed = await
|
|
260
|
+
const confirmed = await confirm('Does this look right?', true);
|
|
196
261
|
if (!confirmed) {
|
|
197
|
-
const manualStack = await
|
|
198
|
-
stackLine = manualStack;
|
|
262
|
+
const manualStack = await input('Describe the stack yourself:', stackLine);
|
|
263
|
+
stackLine = sanitizeScanned(manualStack, 80) || stackLine;
|
|
199
264
|
}
|
|
200
265
|
}
|
|
201
266
|
}
|
|
@@ -208,22 +273,42 @@ async function wizardFlow(rl) {
|
|
|
208
273
|
}
|
|
209
274
|
}
|
|
210
275
|
|
|
211
|
-
// 5. Editors
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
276
|
+
// 5. Editors — a checkbox, so anyone already running another agent system in
|
|
277
|
+
// one harness can install for the other and leave that one alone.
|
|
278
|
+
const existing = setUpProject ? detectExistingAgentSetup(projectDir) : [];
|
|
279
|
+
const claudeTaken = existing.some((f) => f.file === 'CLAUDE.md' && !f.ours);
|
|
280
|
+
|
|
281
|
+
const editors = await checkbox('Which editor(s) do you use?', [
|
|
282
|
+
{
|
|
283
|
+
label: 'Antigravity',
|
|
284
|
+
value: 'antigravity',
|
|
285
|
+
checked: true,
|
|
286
|
+
hint: 'writes .agents/',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
label: 'Claude Code',
|
|
290
|
+
value: 'claude',
|
|
291
|
+
checked: !claudeTaken,
|
|
292
|
+
hint: claudeTaken ? 'CLAUDE.md already in use here' : 'writes CLAUDE.md + .claude/',
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
label: 'Kilo Code (VS Code)',
|
|
296
|
+
value: 'kilo',
|
|
297
|
+
checked: false,
|
|
298
|
+
hint: 'reads AGENTS.md + .agents/skills; adds kilo.jsonc',
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
label: 'Roo Code (VS Code)',
|
|
302
|
+
value: 'roo',
|
|
303
|
+
checked: false,
|
|
304
|
+
hint: 'reads AGENTS.md + .agents/skills; adds .roo/rules',
|
|
305
|
+
},
|
|
306
|
+
]);
|
|
307
|
+
|
|
308
|
+
// Kilo and Roo both read AGENTS.md and .agents/skills/ natively, so picking
|
|
309
|
+
// either implies the shared .agents/ footprint.
|
|
310
|
+
if ((editors.includes('kilo') || editors.includes('roo')) && !editors.includes('antigravity')) {
|
|
311
|
+
editors.push('antigravity');
|
|
227
312
|
}
|
|
228
313
|
|
|
229
314
|
// 6. Write files
|
|
@@ -254,7 +339,7 @@ async function wizardFlow(rl) {
|
|
|
254
339
|
|
|
255
340
|
if (setUpProject) {
|
|
256
341
|
await writeProjectMemory({ projectDir, tokens, ask: conflictAsk, report: reporter });
|
|
257
|
-
await writeLoaders({ projectDir, tokens, ask: conflictAsk, report: reporter });
|
|
342
|
+
await writeLoaders({ projectDir, tokens, ask: conflictAsk, report: reporter, editors });
|
|
258
343
|
await writeSkills({ projectDir, tokens, editors, ask: conflictAsk, report: reporter });
|
|
259
344
|
}
|
|
260
345
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<!-- kracked-core:owned -->
|
|
1
2
|
# AGENTS.md — {{PROJECT_NAME}}
|
|
2
3
|
|
|
3
4
|
This is the canonical loader. Every harness (Antigravity, Claude Code, others) reads this file
|
|
@@ -50,6 +51,7 @@ the signup flow, next step is wiring the email step. Ready."
|
|
|
50
51
|
| `/kracked-sdd` | Runs the SDD flow: idea → spec → docs → build → review |
|
|
51
52
|
| `/kracked-wrap` | Writes lessons + updates session state, closes the session cleanly |
|
|
52
53
|
| `/kracked-explain` | Walks {{USER_NAME}} through what kracked-core installed and why |
|
|
54
|
+
| `/kracked-identity` | Change who you are, your tone, or how {{USER_NAME}} likes to work |
|
|
53
55
|
|
|
54
56
|
## Writing memory (wrap)
|
|
55
57
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# SDD — {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
Spec-driven development artifacts. Run `/kracked-sdd` to work through the flow; these folders are
|
|
4
|
+
where it puts things.
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
idea → spec → epic → stories → build → review
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
| Folder | Holds | Written when |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| `specs/` | What to build and why | Start of any non-trivial job |
|
|
13
|
+
| `epics/` | A group of related stories | When a spec is too big for one story |
|
|
14
|
+
| `stories/` | One independently shippable slice | Broken out of a spec or epic |
|
|
15
|
+
| `architecture/` | How the system is put together | Large or risky work only |
|
|
16
|
+
| `tracker.md` | Status of every story + evidence | Continuously |
|
|
17
|
+
|
|
18
|
+
## Naming
|
|
19
|
+
|
|
20
|
+
Numbers first so files sort in the order you built them.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
specs/login.md
|
|
24
|
+
epics/1-authentication.md
|
|
25
|
+
stories/1.1-email-login.md
|
|
26
|
+
stories/1.2-password-reset.md
|
|
27
|
+
architecture/data-model.md
|
|
28
|
+
architecture/decisions/0001-use-postgres.md
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Story IDs are `<epic>.<story>` and must match the ID in `tracker.md`. That's the link between a
|
|
32
|
+
story's detail and its status — if they drift, the tracker stops being trustworthy.
|
|
33
|
+
|
|
34
|
+
## The two rules that matter
|
|
35
|
+
|
|
36
|
+
**1. Not everything needs the full flow.** A typo fix needs no spec. `/kracked-sdd` sizes the job
|
|
37
|
+
first and skips straight to the build when ceremony would be waste. Over-documenting small work is
|
|
38
|
+
the fastest way to abandon the system entirely.
|
|
39
|
+
|
|
40
|
+
**2. A story isn't `done` without evidence.** The tracker's Evidence column must say what was
|
|
41
|
+
actually verified — and what wasn't. "Tests pass" is not evidence of a working feature.
|
|
42
|
+
|
|
43
|
+
## Commit these
|
|
44
|
+
|
|
45
|
+
These docs belong in git. They're how the next person — including you in three months, and your
|
|
46
|
+
agent on its next boot — finds out why the code looks the way it does.
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Architecture — <area>
|
|
2
|
+
|
|
3
|
+
**Date:** {{DATE}}
|
|
4
|
+
**Status:** current | superseded by `<file>`
|
|
5
|
+
|
|
6
|
+
Write one of these only for work that's **large or risky** — new services, data model changes,
|
|
7
|
+
auth, anything touching money or user data. A small feature does not need an architecture doc,
|
|
8
|
+
and writing one anyway is how the system becomes ceremony people skip.
|
|
9
|
+
|
|
10
|
+
## The shape
|
|
11
|
+
|
|
12
|
+
What the pieces are and how they talk to each other. A diagram in text is fine:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
browser → api route → service → database
|
|
16
|
+
↘ queue → worker
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Data model
|
|
20
|
+
|
|
21
|
+
Tables/collections, key fields, and the relationships that matter. Not every column — the ones a
|
|
22
|
+
newcomer would get wrong.
|
|
23
|
+
|
|
24
|
+
| Entity | Key fields | Related to |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| | | |
|
|
27
|
+
|
|
28
|
+
## Key decisions
|
|
29
|
+
|
|
30
|
+
The choices someone would otherwise re-litigate. Link to an ADR in `decisions/` for the big ones.
|
|
31
|
+
|
|
32
|
+
| Decision | Why | Alternative rejected |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| | | |
|
|
35
|
+
|
|
36
|
+
## Failure modes
|
|
37
|
+
|
|
38
|
+
What breaks, and what happens when it does. Answer for each: does it fail loudly or silently?
|
|
39
|
+
|
|
40
|
+
| What fails | Effect | Handling |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| Database unreachable | | |
|
|
43
|
+
| Third-party API down | | |
|
|
44
|
+
| Duplicate/retried request | | |
|
|
45
|
+
|
|
46
|
+
## What this does NOT do
|
|
47
|
+
|
|
48
|
+
Scope boundaries and deliberate limitations. Prevents someone assuming a capability that isn't
|
|
49
|
+
there.
|
|
50
|
+
|
|
51
|
+
- ...
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# <NNNN> — <the decision, as a statement>
|
|
2
|
+
|
|
3
|
+
**Date:** {{DATE}}
|
|
4
|
+
**Status:** accepted | superseded by `<NNNN>` | reversed
|
|
5
|
+
|
|
6
|
+
> Example filename: `0001-use-postgres-not-mongo.md`
|
|
7
|
+
> Number them in order. Never renumber or delete one — a reversed decision is still history worth
|
|
8
|
+
> keeping, and the reasoning is often more valuable than the outcome.
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
What forced a choice here? The constraints, the pressures, what was true at the time. Someone
|
|
13
|
+
reading this in six months has none of that context — this section is the whole point.
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
|
|
17
|
+
What was decided, stated plainly and in the present tense. "We use X." Not "we should" or
|
|
18
|
+
"we might."
|
|
19
|
+
|
|
20
|
+
## Alternatives considered
|
|
21
|
+
|
|
22
|
+
| Option | Why not |
|
|
23
|
+
|---|---|
|
|
24
|
+
| | |
|
|
25
|
+
|
|
26
|
+
## Consequences
|
|
27
|
+
|
|
28
|
+
Both directions — honest about the costs.
|
|
29
|
+
|
|
30
|
+
**Good:**
|
|
31
|
+
- ...
|
|
32
|
+
|
|
33
|
+
**Costs / things we now have to live with:**
|
|
34
|
+
- ...
|
|
35
|
+
|
|
36
|
+
## Revisit if
|
|
37
|
+
|
|
38
|
+
The conditions that would make this decision wrong. If none, say "no expected trigger."
|
|
39
|
+
|
|
40
|
+
- ...
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Epic <N> — <name>
|
|
2
|
+
|
|
3
|
+
**Spec:** `../specs/<name>.md`
|
|
4
|
+
**Status:** planning | in-progress | done
|
|
5
|
+
**Date:** {{DATE}}
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
What is true when this whole epic is finished? One or two sentences.
|
|
10
|
+
|
|
11
|
+
## Stories
|
|
12
|
+
|
|
13
|
+
Every story must be **independently shippable** — mergeable on its own, leaving the app working.
|
|
14
|
+
If a story can't ship alone, split it.
|
|
15
|
+
|
|
16
|
+
| ID | Story | File |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| <N>.1 | | `../stories/<N>.1-<slug>.md` |
|
|
19
|
+
| <N>.2 | | `../stories/<N>.2-<slug>.md` |
|
|
20
|
+
|
|
21
|
+
Status lives in `../tracker.md`, not here. One source of truth — don't track status in two places
|
|
22
|
+
or they drift.
|
|
23
|
+
|
|
24
|
+
## Order
|
|
25
|
+
|
|
26
|
+
Which stories block others, and why. If they're all independent, say so explicitly.
|
|
27
|
+
|
|
28
|
+
1. ...
|
|
29
|
+
|
|
30
|
+
## Out of scope
|
|
31
|
+
|
|
32
|
+
What belongs to a future epic, not this one.
|
|
33
|
+
|
|
34
|
+
- ...
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Spec — <name>
|
|
2
|
+
|
|
3
|
+
**Status:** draft | agreed | building | shipped
|
|
4
|
+
**Date:** {{DATE}}
|
|
5
|
+
|
|
6
|
+
## What
|
|
7
|
+
|
|
8
|
+
One paragraph. What is being built, in plain language — no implementation detail.
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
The problem this solves, and for whom. If you can't name who is worse off without it, the spec
|
|
13
|
+
isn't ready.
|
|
14
|
+
|
|
15
|
+
## Acceptance criteria
|
|
16
|
+
|
|
17
|
+
Concrete and checkable. Someone else must be able to read these and tell whether the thing works.
|
|
18
|
+
|
|
19
|
+
- [ ] A user can ...
|
|
20
|
+
- [ ] When X happens, the system ...
|
|
21
|
+
- [ ] If the input is empty/invalid, it ...
|
|
22
|
+
|
|
23
|
+
## Out of scope
|
|
24
|
+
|
|
25
|
+
**Not optional.** Scope creep starts exactly where this section is skipped. List what this spec
|
|
26
|
+
deliberately does NOT cover.
|
|
27
|
+
|
|
28
|
+
- ...
|
|
29
|
+
|
|
30
|
+
## Edge cases
|
|
31
|
+
|
|
32
|
+
For every input involved, answer: what if it's empty, missing, or unexpected?
|
|
33
|
+
|
|
34
|
+
| Case | Expected behaviour |
|
|
35
|
+
|---|---|
|
|
36
|
+
| Empty input | |
|
|
37
|
+
| Already exists | |
|
|
38
|
+
| Not authorised | |
|
|
39
|
+
|
|
40
|
+
## Open questions
|
|
41
|
+
|
|
42
|
+
Things that must be answered before or during the build. Delete this section once it's empty.
|
|
43
|
+
|
|
44
|
+
- [ ] ...
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Story <N>.<M> — <name>
|
|
2
|
+
|
|
3
|
+
**Epic:** `../epics/<N>-<slug>.md`
|
|
4
|
+
**Status:** backlog | in-progress | review | done ← mirror this in `../tracker.md`
|
|
5
|
+
**Date:** {{DATE}}
|
|
6
|
+
|
|
7
|
+
## What
|
|
8
|
+
|
|
9
|
+
One slice of the epic. Small enough to build and verify in one sitting.
|
|
10
|
+
|
|
11
|
+
## Acceptance criteria
|
|
12
|
+
|
|
13
|
+
- [ ] ...
|
|
14
|
+
- [ ] ...
|
|
15
|
+
|
|
16
|
+
## Notes for the build
|
|
17
|
+
|
|
18
|
+
Files likely to change, existing code to reuse, anything that would trip someone up.
|
|
19
|
+
|
|
20
|
+
> Verify these claims before building on them. A story written from memory can name a file,
|
|
21
|
+
> function, or column that doesn't exist — grep first, then build.
|
|
22
|
+
|
|
23
|
+
- ...
|
|
24
|
+
|
|
25
|
+
## Evidence
|
|
26
|
+
|
|
27
|
+
**Fill this in before marking `done`.** Copy the summary line into `tracker.md`.
|
|
28
|
+
|
|
29
|
+
- **Verified:** what you actually ran or checked, and what you saw
|
|
30
|
+
- **Not verified:** what you did NOT check — untested paths, edge cases, anything left open
|
|
31
|
+
|
|
32
|
+
A story marked `done` with an empty Evidence section is worse than one left at `review`: it hides
|
|
33
|
+
risk instead of surfacing it.
|
|
@@ -5,14 +5,21 @@ description: Use at the start of every session, before any other work, to load g
|
|
|
5
5
|
|
|
6
6
|
Run this before doing anything else in the session. Do not skip steps or merge reads together.
|
|
7
7
|
|
|
8
|
-
## 1. Check
|
|
8
|
+
## 1. Check what memory exists
|
|
9
9
|
|
|
10
|
-
Look for `~/.kracked/identity.md
|
|
11
|
-
|
|
10
|
+
Look for `~/.kracked/identity.md` (global) and `.kracked/` in the current directory (project).
|
|
11
|
+
At least one must exist. Boot works with either layer — never halt just because one is missing.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- **Both present** — normal boot. Continue to step 2.
|
|
14
|
+
- **Project only** (no `~/.kracked/`) — boot from project memory alone. Behave as a careful,
|
|
15
|
+
direct coding agent, and say in your orientation line that global memory isn't set up, so
|
|
16
|
+
cross-project lessons and preferences aren't loaded. Mention `npx kracked-core init` once.
|
|
17
|
+
Do NOT invent preferences or lessons you haven't read.
|
|
18
|
+
- **Global only** (no `.kracked/` here) — see step 3.
|
|
19
|
+
- **Neither** — nothing is installed. Say so and point at `npx kracked-core init`. This is the
|
|
20
|
+
only case where there's nothing to load.
|
|
14
21
|
|
|
15
|
-
|
|
22
|
+
Never fabricate identity, preferences, project state, or lessons. Missing is missing — say it.
|
|
16
23
|
|
|
17
24
|
## 2. Load global memory, in this exact order
|
|
18
25
|
|
|
@@ -36,6 +43,7 @@ If the current directory has a `.kracked/` folder, read:
|
|
|
36
43
|
1. `.kracked/project.md` — what this project is (stack, conventions)
|
|
37
44
|
2. `.kracked/session.md` — working memory: state + next steps
|
|
38
45
|
3. `.kracked/decisions.md` — why things are the way they are
|
|
46
|
+
4. `.kracked/sdd/tracker.md` — story status, if this project uses SDD (skip if absent)
|
|
39
47
|
|
|
40
48
|
If there is no `.kracked/` folder here, this is a project that hasn't been set up yet. Say so
|
|
41
49
|
plainly and suggest `npx kracked-core init` if the user wants project memory for it. Continue
|