kracked-core 1.5.0 → 1.6.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/README.md +5 -1
- package/bin/kracked-core.mjs +14 -5
- package/package.json +1 -1
- package/src/prompt.mjs +20 -0
- package/src/scaffold.mjs +21 -5
- package/src/update.mjs +12 -4
- package/src/wizard.mjs +22 -0
- package/templates/global/projects.md +1 -7
- package/templates/project/project.md +1 -1
package/README.md
CHANGED
|
@@ -47,7 +47,11 @@ It's plain markdown files. You can read them, edit them, commit them to git, and
|
|
|
47
47
|
npx kracked-core init
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
The wizard asks a few questions — name your agent,
|
|
50
|
+
The wizard asks a few questions — name your agent, new or existing project, which editor you use —
|
|
51
|
+
and writes the files. Pressing Enter through it gives you a working setup.
|
|
52
|
+
|
|
53
|
+
**Existing codebase?** It scans your repo and drafts the memory from what it finds.
|
|
54
|
+
**Starting fresh?** It asks what you're building and what stack, so your agent knows from day one.
|
|
51
55
|
|
|
52
56
|
**Full support** (memory + all five commands):
|
|
53
57
|
**Antigravity** · **Claude Code** · **Kilo Code** (VS Code) · **Roo Code** (VS Code)
|
package/bin/kracked-core.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { runInit } from '../src/wizard.mjs';
|
|
|
4
4
|
import { runUninstall } from '../src/uninstall.mjs';
|
|
5
5
|
import { runUpdate } from '../src/update.mjs';
|
|
6
6
|
import { runStatus } from '../src/status.mjs';
|
|
7
|
-
import { beginTypeAhead } from '../src/prompt.mjs';
|
|
7
|
+
import { beginTypeAhead, endTypeAhead } from '../src/prompt.mjs';
|
|
8
8
|
|
|
9
9
|
const USAGE = `kracked-core — memory & workflow installer for AI coding agents
|
|
10
10
|
|
|
@@ -63,7 +63,16 @@ async function main() {
|
|
|
63
63
|
process.exit(1);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
main()
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
main()
|
|
67
|
+
.catch((err) => {
|
|
68
|
+
process.stderr.write(`\nkracked-core failed: ${err.message}\n`);
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
})
|
|
71
|
+
// A raw-mode TTY stdin stays an ACTIVE HANDLE even after removeListener +
|
|
72
|
+
// pause(), so Node's event loop never drains and the terminal never returns —
|
|
73
|
+
// the command looks stuck even when it succeeded. Releasing stdin is not
|
|
74
|
+
// enough on its own; exit explicitly once the work is done.
|
|
75
|
+
.finally(() => {
|
|
76
|
+
endTypeAhead();
|
|
77
|
+
process.stdout.write('', () => process.exit(process.exitCode ?? 0));
|
|
78
|
+
});
|
package/package.json
CHANGED
package/src/prompt.mjs
CHANGED
|
@@ -110,6 +110,26 @@ export function beginTypeAhead() {
|
|
|
110
110
|
stdin.on('data', bufferEarlyKeys);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Release stdin so the process can exit.
|
|
115
|
+
*
|
|
116
|
+
* An open stdin listener keeps Node's event loop alive indefinitely, so any
|
|
117
|
+
* command that finishes WITHOUT going through a prompt's cleanup (an early
|
|
118
|
+
* return, an error path, a completed run) leaves the terminal hanging with no
|
|
119
|
+
* output and no prompt back. `process.exitCode` sets the code but does not
|
|
120
|
+
* exit. Every command path must end here.
|
|
121
|
+
*/
|
|
122
|
+
export function endTypeAhead() {
|
|
123
|
+
if (!stdin.isTTY) return;
|
|
124
|
+
stdin.removeListener('data', bufferEarlyKeys);
|
|
125
|
+
try {
|
|
126
|
+
if (stdin.isRaw) stdin.setRawMode(false);
|
|
127
|
+
} catch {
|
|
128
|
+
// Terminal already gone.
|
|
129
|
+
}
|
|
130
|
+
stdin.pause();
|
|
131
|
+
}
|
|
132
|
+
|
|
113
133
|
function bufferEarlyKeys(chunk) {
|
|
114
134
|
pendingKeys.push(...splitKeys(chunk));
|
|
115
135
|
}
|
package/src/scaffold.mjs
CHANGED
|
@@ -80,6 +80,19 @@ async function writeFileWithConflictCheck(destPath, content, ask, report) {
|
|
|
80
80
|
* 'skip' | 'overwrite' | 'alongside'.
|
|
81
81
|
* `report(entry)` is called once per file, for the wizard's final summary.
|
|
82
82
|
*/
|
|
83
|
+
/**
|
|
84
|
+
* Drop the illustrative example entries from a freshly-written memory file.
|
|
85
|
+
* They exist to show the format, but left in place they sit alongside the
|
|
86
|
+
* user's real lessons forever and make the index untrustworthy.
|
|
87
|
+
*/
|
|
88
|
+
function stripExamples(content) {
|
|
89
|
+
return content
|
|
90
|
+
.split('\n')
|
|
91
|
+
.filter((line) => !/^[-|]\s*\[?(YYYY-MM-DD|\d{4}-\d{2}-\d{2})\]?\s*(todo-app|my-blog|<project>)/.test(line))
|
|
92
|
+
.filter((line) => !/^\|\s*(todo-app|my-blog)\s*\|/.test(line))
|
|
93
|
+
.join('\n');
|
|
94
|
+
}
|
|
95
|
+
|
|
83
96
|
export async function writeGlobalMemory({ tokens, ask, report }) {
|
|
84
97
|
const globalDir = path.join(os.homedir(), '.kracked');
|
|
85
98
|
// projects.md is excluded here — it's a running registry appended to by
|
|
@@ -88,7 +101,7 @@ export async function writeGlobalMemory({ tokens, ask, report }) {
|
|
|
88
101
|
|
|
89
102
|
for (const file of files) {
|
|
90
103
|
const destPath = path.join(globalDir, file);
|
|
91
|
-
const content = applyTokens(readTemplate(`global/${file}`), tokens);
|
|
104
|
+
const content = stripExamples(applyTokens(readTemplate(`global/${file}`), tokens));
|
|
92
105
|
await writeFileWithConflictCheck(destPath, content, ask, report);
|
|
93
106
|
}
|
|
94
107
|
}
|
|
@@ -124,10 +137,13 @@ export function registerProject({ tokens }) {
|
|
|
124
137
|
const lines = content.split('\n');
|
|
125
138
|
const isDivider = (l) => /^\|[\s|:-]+\|?\s*$/.test(l);
|
|
126
139
|
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
const
|
|
140
|
+
// Anchor on the Registry heading first — a table-looking line elsewhere in the
|
|
141
|
+
// doc (an example, a fenced snippet) would otherwise capture the insert and
|
|
142
|
+
// produce a second, malformed table.
|
|
143
|
+
const registryIdx = lines.findIndex((l) => /^##\s+Registry\s*$/i.test(l));
|
|
144
|
+
const searchFrom = registryIdx >= 0 ? registryIdx : 0;
|
|
145
|
+
const rel = lines.slice(searchFrom).findIndex(isDivider);
|
|
146
|
+
const dividerIdx = rel >= 0 ? searchFrom + rel : -1;
|
|
131
147
|
|
|
132
148
|
if (dividerIdx >= 0) {
|
|
133
149
|
let insertAt = dividerIdx;
|
package/src/update.mjs
CHANGED
|
@@ -87,10 +87,18 @@ export async function runUpdate() {
|
|
|
87
87
|
stdout.write(`${bold('kracked-core update')} ${dim(`v${version}`)}\n\n`);
|
|
88
88
|
|
|
89
89
|
if (!fs.existsSync(path.join(projectDir, '.kracked'))) {
|
|
90
|
-
stdout.write(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
stdout.write(`No kracked-core install found in ${projectDir}\n\n`);
|
|
91
|
+
|
|
92
|
+
// Easy mistake: running this inside the package source rather than a
|
|
93
|
+
// project that uses it. Say so instead of just repeating the error.
|
|
94
|
+
if (fs.existsSync(path.join(projectDir, 'templates', 'loaders', 'AGENTS.md'))) {
|
|
95
|
+
stdout.write(
|
|
96
|
+
'This looks like the kracked-core package source, not a project that\n' +
|
|
97
|
+
'uses it. cd into your own project first, then run this again.\n'
|
|
98
|
+
);
|
|
99
|
+
} else {
|
|
100
|
+
stdout.write('Run `npx kracked-core@latest init` to set it up first.\n');
|
|
101
|
+
}
|
|
94
102
|
process.exitCode = 1;
|
|
95
103
|
return;
|
|
96
104
|
}
|
package/src/wizard.mjs
CHANGED
|
@@ -196,6 +196,7 @@ async function wizardFlow() {
|
|
|
196
196
|
let projectMode = null; // 'new' | 'existing'
|
|
197
197
|
let stackLine = 'unknown — not auto-detected';
|
|
198
198
|
let projectSummary = null;
|
|
199
|
+
let projectPitch = '';
|
|
199
200
|
|
|
200
201
|
if (setUpProject) {
|
|
201
202
|
const pathAnswer = await input('Project directory?', '.');
|
|
@@ -250,6 +251,24 @@ async function wizardFlow() {
|
|
|
250
251
|
classification === 'existing' ? 0 : 1
|
|
251
252
|
);
|
|
252
253
|
|
|
254
|
+
if (projectMode === 'new') {
|
|
255
|
+
// Nothing to scan, so ask instead — otherwise the agent boots knowing
|
|
256
|
+
// nothing about what's being built, which is the problem this package
|
|
257
|
+
// exists to solve. Both skippable with Enter.
|
|
258
|
+
stdout.write('\n Two quick questions so your agent knows what it\'s working on.\n');
|
|
259
|
+
stdout.write(` ${c_dim('Press Enter to skip either one.')}\n\n`);
|
|
260
|
+
|
|
261
|
+
projectPitch = sanitizeScanned(
|
|
262
|
+
await input('What are you building? (one line)', ''),
|
|
263
|
+
200
|
|
264
|
+
);
|
|
265
|
+
const statedStack = sanitizeScanned(
|
|
266
|
+
await input('What stack? (e.g. Next.js + Supabase)', ''),
|
|
267
|
+
80
|
|
268
|
+
);
|
|
269
|
+
if (statedStack) stackLine = statedStack;
|
|
270
|
+
}
|
|
271
|
+
|
|
253
272
|
if (projectMode === 'existing') {
|
|
254
273
|
if (!fs.existsSync(projectDir)) {
|
|
255
274
|
stdout.write(`\n ${projectDir} doesn't exist yet — nothing to scan. Treating as new.\n`);
|
|
@@ -333,6 +352,9 @@ async function wizardFlow() {
|
|
|
333
352
|
DATE: isoDate(),
|
|
334
353
|
STACK: stackLine,
|
|
335
354
|
RUN_COMMANDS: runCommandsBlock(projectSummary),
|
|
355
|
+
PITCH: projectPitch
|
|
356
|
+
? projectPitch
|
|
357
|
+
: '_(Add a sentence or two here on what this project actually does.)_',
|
|
336
358
|
};
|
|
337
359
|
|
|
338
360
|
stdout.write('\nWriting files...\n');
|
|
@@ -4,13 +4,7 @@ Registry of every project {{USER_NAME}} is working on. Each row points to the pr
|
|
|
4
4
|
`.kracked/` folder, where the real detail (stack, conventions, session state) lives. This file
|
|
5
5
|
only tracks what exists and where — never project-specific truth.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
| Project | Path | Stack | Status |
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
- **Status** is one of: `active`, `paused`, `done`.
|
|
7
|
+
**Status** is one of: `active`, `paused`, `done`.
|
|
14
8
|
|
|
15
9
|
## Registry
|
|
16
10
|
|