@xxkeefer/mrkl 0.1.0 → 0.2.4
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 +232 -0
- package/dist/cli.mjs +205 -31
- package/package.json +6 -3
package/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">mrkl</h1>
|
|
3
|
+
<p align="center">
|
|
4
|
+
Lightweight CLI for structured markdown task tracking.
|
|
5
|
+
<br />
|
|
6
|
+
Track work in your repo, not in a separate app.
|
|
7
|
+
</p>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://www.npmjs.com/package/@xxkeefer/mrkl"><img src="https://img.shields.io/npm/v/@xxkeefer/mrkl" alt="npm version" /></a>
|
|
12
|
+
<a href="https://github.com/xxKeefer/mrkl/blob/main/LICENSE"><img src="https://img.shields.io/github/license/xxKeefer/mrkl" alt="license" /></a>
|
|
13
|
+
<a href="https://github.com/xxKeefer/mrkl"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome" /></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why mrkl?
|
|
19
|
+
|
|
20
|
+
Most task trackers live outside your codebase. mrkl keeps tasks as markdown files right alongside your code — version-controlled, greppable, and readable by both humans and AI agents.
|
|
21
|
+
|
|
22
|
+
- **No external service** — tasks live in `.tasks/` as structured markdown
|
|
23
|
+
- **Git-native** — commit, branch, and diff your tasks like any other file
|
|
24
|
+
- **AI-agent friendly** — consistent YAML frontmatter makes tasks easy to parse programmatically
|
|
25
|
+
- **Conventional commits vocabulary** — task types mirror what you already use (`feat`, `fix`, `chore`, etc.)
|
|
26
|
+
- **Zero config** — one command to set up, sensible defaults for everything
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm install -g @xxkeefer/mrkl
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or use without installing:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npx @xxkeefer/mrkl init MY_PROJECT
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
# Initialize in your project root
|
|
44
|
+
mrkl init PROJ
|
|
45
|
+
|
|
46
|
+
# Create tasks
|
|
47
|
+
mrkl create feat "user authentication"
|
|
48
|
+
mrkl create fix "login redirect loop" --desc "Users get stuck after OAuth callback"
|
|
49
|
+
mrkl create feat "dark mode" --ac "toggle in settings" --ac "persists across sessions"
|
|
50
|
+
|
|
51
|
+
# View active tasks
|
|
52
|
+
mrkl list
|
|
53
|
+
# PROJ-001 feat todo user authentication
|
|
54
|
+
# PROJ-002 fix todo login redirect loop
|
|
55
|
+
# PROJ-003 feat todo dark mode
|
|
56
|
+
|
|
57
|
+
# Filter by type or status
|
|
58
|
+
mrkl list --type fix
|
|
59
|
+
mrkl list --status todo
|
|
60
|
+
|
|
61
|
+
# Archive a completed task
|
|
62
|
+
mrkl done PROJ-001
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Commands
|
|
66
|
+
|
|
67
|
+
### `mrkl init <prefix>`
|
|
68
|
+
|
|
69
|
+
Initializes mrkl in the current directory.
|
|
70
|
+
|
|
71
|
+
| Argument | Description |
|
|
72
|
+
|----------|-------------|
|
|
73
|
+
| `prefix` | Project prefix for task IDs (e.g., `PROJ`, `API`, `WEB`) |
|
|
74
|
+
|
|
75
|
+
Creates:
|
|
76
|
+
- `.config/mrkl/mrkl.toml` — project configuration
|
|
77
|
+
- `.config/mrkl/mrkl_counter` — auto-incrementing ID tracker
|
|
78
|
+
- `.tasks/` — active task directory
|
|
79
|
+
- `.tasks/.archive/` — completed task storage
|
|
80
|
+
|
|
81
|
+
Safe to run multiple times — existing config and counter are preserved.
|
|
82
|
+
|
|
83
|
+
### `mrkl create <type> <title> [options]`
|
|
84
|
+
|
|
85
|
+
Creates a new task file.
|
|
86
|
+
|
|
87
|
+
| Argument | Description |
|
|
88
|
+
|----------|-------------|
|
|
89
|
+
| `type` | Task type (see [Task Types](#task-types)) |
|
|
90
|
+
| `title` | Short description of the task |
|
|
91
|
+
|
|
92
|
+
| Option | Description |
|
|
93
|
+
|--------|-------------|
|
|
94
|
+
| `--desc <text>` | Detailed description |
|
|
95
|
+
| `--ac <text>` | Acceptance criterion (repeatable) |
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
mrkl create feat "search functionality" \
|
|
99
|
+
--desc "Full-text search across all documents" \
|
|
100
|
+
--ac "search bar in header" \
|
|
101
|
+
--ac "results update as you type" \
|
|
102
|
+
--ac "highlights matching terms"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `mrkl list [options]`
|
|
106
|
+
|
|
107
|
+
Lists all active tasks.
|
|
108
|
+
|
|
109
|
+
| Option | Description |
|
|
110
|
+
|--------|-------------|
|
|
111
|
+
| `--type <type>` | Filter by task type |
|
|
112
|
+
| `--status <status>` | Filter by status (`todo`, `in-progress`, `done`) |
|
|
113
|
+
|
|
114
|
+
Non-conforming markdown files in the tasks directory are silently skipped.
|
|
115
|
+
|
|
116
|
+
### `mrkl done <id>`
|
|
117
|
+
|
|
118
|
+
Archives a completed task.
|
|
119
|
+
|
|
120
|
+
| Argument | Description |
|
|
121
|
+
|----------|-------------|
|
|
122
|
+
| `id` | Task ID to archive (e.g., `PROJ-001`) |
|
|
123
|
+
|
|
124
|
+
Moves the task file to `.tasks/.archive/` and sets its status to `done`.
|
|
125
|
+
|
|
126
|
+
## Task Types
|
|
127
|
+
|
|
128
|
+
mrkl uses [conventional commit](https://www.conventionalcommits.org/) types:
|
|
129
|
+
|
|
130
|
+
| Type | Purpose |
|
|
131
|
+
|------|---------|
|
|
132
|
+
| `feat` | New feature |
|
|
133
|
+
| `fix` | Bug fix |
|
|
134
|
+
| `chore` | Maintenance |
|
|
135
|
+
| `docs` | Documentation |
|
|
136
|
+
| `perf` | Performance improvement |
|
|
137
|
+
| `refactor` | Code restructuring |
|
|
138
|
+
| `test` | Testing |
|
|
139
|
+
| `ci` | CI/CD changes |
|
|
140
|
+
| `build` | Build system changes |
|
|
141
|
+
| `style` | Code style/formatting |
|
|
142
|
+
|
|
143
|
+
## Task File Format
|
|
144
|
+
|
|
145
|
+
Each task is a markdown file with YAML frontmatter:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
.tasks/PROJ-001 feat - user authentication.md
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```markdown
|
|
152
|
+
---
|
|
153
|
+
id: PROJ-001
|
|
154
|
+
type: feat
|
|
155
|
+
status: todo
|
|
156
|
+
created: '2026-03-01'
|
|
157
|
+
---
|
|
158
|
+
## Description
|
|
159
|
+
|
|
160
|
+
Implement user authentication with OAuth2.
|
|
161
|
+
|
|
162
|
+
## Acceptance Criteria
|
|
163
|
+
|
|
164
|
+
- [ ] login page renders
|
|
165
|
+
- [ ] OAuth flow completes
|
|
166
|
+
- [ ] session persists across refreshes
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The format is intentionally simple — edit task files directly when you need to update descriptions, change status, or check off criteria.
|
|
170
|
+
|
|
171
|
+
## Project Structure
|
|
172
|
+
|
|
173
|
+
After initialization, mrkl adds the following to your project:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
your-project/
|
|
177
|
+
.config/mrkl/
|
|
178
|
+
mrkl.toml # project configuration
|
|
179
|
+
mrkl_counter # current task number
|
|
180
|
+
.tasks/
|
|
181
|
+
PROJ-001 feat - first task.md
|
|
182
|
+
PROJ-002 fix - second task.md
|
|
183
|
+
.archive/
|
|
184
|
+
PROJ-000 chore - archived task.md
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Commit `.config/mrkl/` and `.tasks/` to version control. They're designed to be tracked alongside your code.
|
|
188
|
+
|
|
189
|
+
## Configuration
|
|
190
|
+
|
|
191
|
+
Configuration lives in `.config/mrkl/mrkl.toml` (or `mrkl.toml` at the project root):
|
|
192
|
+
|
|
193
|
+
```toml
|
|
194
|
+
prefix = "PROJ"
|
|
195
|
+
tasks_dir = ".tasks"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
| Key | Default | Description |
|
|
199
|
+
|-----|---------|-------------|
|
|
200
|
+
| `prefix` | *(required)* | Project prefix for task IDs |
|
|
201
|
+
| `tasks_dir` | `".tasks"` | Directory for task files |
|
|
202
|
+
|
|
203
|
+
## Development
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
git clone https://github.com/xxKeefer/mrkl.git
|
|
207
|
+
cd mrkl
|
|
208
|
+
npm install
|
|
209
|
+
|
|
210
|
+
# Run tests
|
|
211
|
+
npm test
|
|
212
|
+
|
|
213
|
+
# Run CLI in development
|
|
214
|
+
npx tsx src/cli.ts list
|
|
215
|
+
|
|
216
|
+
# Build
|
|
217
|
+
npm run build
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Contributing
|
|
221
|
+
|
|
222
|
+
Contributions are welcome. Please open an issue first to discuss what you'd like to change.
|
|
223
|
+
|
|
224
|
+
1. Fork the repo
|
|
225
|
+
2. Create your branch (`git checkout -b feat/my-feature`)
|
|
226
|
+
3. Commit your changes using [conventional commits](https://www.conventionalcommits.org/)
|
|
227
|
+
4. Push to your branch (`git push origin feat/my-feature`)
|
|
228
|
+
5. Open a Pull Request
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
[MIT](LICENSE)
|
package/dist/cli.mjs
CHANGED
|
@@ -1,9 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { defineCommand, runMain } from 'citty';
|
|
3
3
|
import consola from 'consola';
|
|
4
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, unlinkSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { stringify, parse as parse$1 } from 'smol-toml';
|
|
7
|
+
import matter from 'gray-matter';
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
const CONFIG_PATHS = [
|
|
10
|
+
join(".config", "mrkl", "mrkl.toml"),
|
|
11
|
+
"mrkl.toml"
|
|
12
|
+
];
|
|
13
|
+
function loadConfig(dir) {
|
|
14
|
+
const configPath = CONFIG_PATHS.map((p) => join(dir, p)).find((p) => existsSync(p));
|
|
15
|
+
if (!configPath) {
|
|
16
|
+
throw new Error(`mrkl.toml not found in ${dir}`);
|
|
17
|
+
}
|
|
18
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
19
|
+
const parsed = parse$1(raw);
|
|
20
|
+
return {
|
|
21
|
+
prefix: parsed.prefix,
|
|
22
|
+
tasks_dir: parsed.tasks_dir ?? ".tasks"
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function initConfig(dir, opts) {
|
|
26
|
+
const configDir = join(dir, ".config", "mrkl");
|
|
27
|
+
const configPath = join(configDir, "mrkl.toml");
|
|
28
|
+
if (!existsSync(configPath)) {
|
|
29
|
+
const config2 = {
|
|
30
|
+
prefix: opts?.prefix ?? "TASK",
|
|
31
|
+
tasks_dir: opts?.tasks_dir ?? ".tasks"
|
|
32
|
+
};
|
|
33
|
+
mkdirSync(configDir, { recursive: true });
|
|
34
|
+
writeFileSync(configPath, stringify(config2));
|
|
35
|
+
}
|
|
36
|
+
const config = loadConfig(dir);
|
|
37
|
+
const tasksDir = join(dir, config.tasks_dir);
|
|
38
|
+
mkdirSync(join(tasksDir, ".archive"), { recursive: true });
|
|
39
|
+
const counterPath = join(dir, ".config", "mrkl", "mrkl_counter");
|
|
40
|
+
if (!existsSync(counterPath)) {
|
|
41
|
+
writeFileSync(counterPath, "0");
|
|
42
|
+
}
|
|
7
43
|
}
|
|
8
44
|
|
|
9
45
|
const initCommand = defineCommand({
|
|
@@ -19,21 +55,138 @@ const initCommand = defineCommand({
|
|
|
19
55
|
},
|
|
20
56
|
run({ args }) {
|
|
21
57
|
const dir = process.cwd();
|
|
22
|
-
|
|
23
|
-
|
|
58
|
+
try {
|
|
59
|
+
initConfig(dir, { prefix: args.prefix });
|
|
60
|
+
consola.success("mrkl initialized");
|
|
61
|
+
} catch (err) {
|
|
62
|
+
consola.error(String(err.message));
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
24
65
|
}
|
|
25
66
|
});
|
|
26
67
|
|
|
27
|
-
|
|
28
|
-
|
|
68
|
+
const COUNTER_FILE = join(".config", "mrkl", "mrkl_counter");
|
|
69
|
+
function nextId(dir) {
|
|
70
|
+
const filePath = join(dir, COUNTER_FILE);
|
|
71
|
+
const current = existsSync(filePath) ? parseInt(readFileSync(filePath, "utf-8").trim(), 10) : 0;
|
|
72
|
+
const next = current + 1;
|
|
73
|
+
writeFileSync(filePath, String(next));
|
|
74
|
+
return next;
|
|
29
75
|
}
|
|
30
|
-
|
|
31
|
-
|
|
76
|
+
|
|
77
|
+
function render(task) {
|
|
78
|
+
const frontmatter = {
|
|
79
|
+
id: task.id,
|
|
80
|
+
type: task.type,
|
|
81
|
+
status: task.status,
|
|
82
|
+
created: task.created
|
|
83
|
+
};
|
|
84
|
+
const sections = [];
|
|
85
|
+
sections.push("## Description");
|
|
86
|
+
sections.push("");
|
|
87
|
+
sections.push(task.description || "");
|
|
88
|
+
sections.push("");
|
|
89
|
+
sections.push("## Acceptance Criteria");
|
|
90
|
+
sections.push("");
|
|
91
|
+
for (const item of task.acceptance_criteria) {
|
|
92
|
+
sections.push(`- [ ] ${item}`);
|
|
93
|
+
}
|
|
94
|
+
const body = sections.join("\n") + "\n";
|
|
95
|
+
return matter.stringify(body, frontmatter);
|
|
32
96
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
97
|
+
function parse(content, filename) {
|
|
98
|
+
const { data, content: body } = matter(content);
|
|
99
|
+
const titleMatch = filename.replace(/\.md$/, "").match(/^\S+\s+\S+\s+-\s+(.+)$/);
|
|
100
|
+
const title = titleMatch ? titleMatch[1] : "";
|
|
101
|
+
const acRegex = /^- \[[ x]\] (.+)$/gm;
|
|
102
|
+
const acceptance_criteria = [];
|
|
103
|
+
let match;
|
|
104
|
+
while ((match = acRegex.exec(body)) !== null) {
|
|
105
|
+
acceptance_criteria.push(match[1]);
|
|
106
|
+
}
|
|
107
|
+
const descMatch = body.match(/## Description\n\n([\s\S]*?)(?:\n\n## Acceptance Criteria|$)/);
|
|
108
|
+
const description = descMatch ? descMatch[1].trim() : "";
|
|
109
|
+
return {
|
|
110
|
+
id: data.id,
|
|
111
|
+
type: data.type,
|
|
112
|
+
status: data.status,
|
|
113
|
+
created: data.created,
|
|
114
|
+
title,
|
|
115
|
+
description,
|
|
116
|
+
acceptance_criteria
|
|
117
|
+
};
|
|
35
118
|
}
|
|
36
119
|
|
|
120
|
+
function createTask(opts) {
|
|
121
|
+
const config = loadConfig(opts.dir);
|
|
122
|
+
const num = nextId(opts.dir);
|
|
123
|
+
const id = `${config.prefix}-${String(num).padStart(3, "0")}`;
|
|
124
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
125
|
+
const task = {
|
|
126
|
+
id,
|
|
127
|
+
type: opts.type,
|
|
128
|
+
status: "todo",
|
|
129
|
+
created: today,
|
|
130
|
+
title: opts.title,
|
|
131
|
+
description: opts.description ?? "",
|
|
132
|
+
acceptance_criteria: opts.acceptance_criteria ?? []
|
|
133
|
+
};
|
|
134
|
+
const filename = `${id} ${task.type} - ${task.title}.md`;
|
|
135
|
+
const tasksDir = join(opts.dir, config.tasks_dir);
|
|
136
|
+
writeFileSync(join(tasksDir, filename), render(task));
|
|
137
|
+
return task;
|
|
138
|
+
}
|
|
139
|
+
function listTasks(filter) {
|
|
140
|
+
const config = loadConfig(filter.dir);
|
|
141
|
+
const tasksDir = join(filter.dir, config.tasks_dir);
|
|
142
|
+
const files = readdirSync(tasksDir).filter(
|
|
143
|
+
(f) => f.endsWith(".md") && !f.startsWith(".")
|
|
144
|
+
);
|
|
145
|
+
let tasks = files.flatMap((f) => {
|
|
146
|
+
try {
|
|
147
|
+
const content = readFileSync(join(tasksDir, f), "utf-8");
|
|
148
|
+
const task = parse(content, f);
|
|
149
|
+
if (!task.id || !task.type || !task.status) return [];
|
|
150
|
+
return [task];
|
|
151
|
+
} catch {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
if (filter.type) tasks = tasks.filter((t) => t.type === filter.type);
|
|
156
|
+
if (filter.status) tasks = tasks.filter((t) => t.status === filter.status);
|
|
157
|
+
return tasks;
|
|
158
|
+
}
|
|
159
|
+
function archiveTask(dir, id) {
|
|
160
|
+
const config = loadConfig(dir);
|
|
161
|
+
const tasksDir = join(dir, config.tasks_dir);
|
|
162
|
+
const file = readdirSync(tasksDir).find(
|
|
163
|
+
(f) => f.endsWith(".md") && f.startsWith(id)
|
|
164
|
+
);
|
|
165
|
+
if (!file) {
|
|
166
|
+
throw new Error(`Task ${id} not found`);
|
|
167
|
+
}
|
|
168
|
+
const filePath = join(tasksDir, file);
|
|
169
|
+
const content = readFileSync(filePath, "utf-8");
|
|
170
|
+
const task = parse(content, file);
|
|
171
|
+
task.status = "done";
|
|
172
|
+
const archivePath = join(tasksDir, ".archive", file);
|
|
173
|
+
writeFileSync(archivePath, render(task));
|
|
174
|
+
unlinkSync(filePath);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const TASK_TYPES = [
|
|
178
|
+
"feat",
|
|
179
|
+
"fix",
|
|
180
|
+
"chore",
|
|
181
|
+
"docs",
|
|
182
|
+
"perf",
|
|
183
|
+
"refactor",
|
|
184
|
+
"test",
|
|
185
|
+
"ci",
|
|
186
|
+
"build",
|
|
187
|
+
"style"
|
|
188
|
+
];
|
|
189
|
+
|
|
37
190
|
const createCommand = defineCommand({
|
|
38
191
|
meta: {
|
|
39
192
|
name: "create",
|
|
@@ -60,14 +213,24 @@ const createCommand = defineCommand({
|
|
|
60
213
|
}
|
|
61
214
|
},
|
|
62
215
|
run({ args }) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
216
|
+
if (!TASK_TYPES.includes(args.type)) {
|
|
217
|
+
consola.error(`Invalid type "${args.type}". Must be one of: ${TASK_TYPES.join(", ")}`);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
const dir = process.cwd();
|
|
221
|
+
try {
|
|
222
|
+
const task = createTask({
|
|
223
|
+
dir,
|
|
224
|
+
type: args.type,
|
|
225
|
+
title: args.title,
|
|
226
|
+
description: args.desc,
|
|
227
|
+
acceptance_criteria: args.ac ? [args.ac] : void 0
|
|
228
|
+
});
|
|
229
|
+
consola.success(`Created ${task.id}: ${task.title}`);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
consola.error(String(err.message));
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
71
234
|
}
|
|
72
235
|
});
|
|
73
236
|
|
|
@@ -87,17 +250,23 @@ const listCommand = defineCommand({
|
|
|
87
250
|
}
|
|
88
251
|
},
|
|
89
252
|
run({ args }) {
|
|
90
|
-
process.cwd();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
253
|
+
const dir = process.cwd();
|
|
254
|
+
try {
|
|
255
|
+
const tasks = listTasks({
|
|
256
|
+
dir,
|
|
257
|
+
type: args.type,
|
|
258
|
+
status: args.status
|
|
259
|
+
});
|
|
260
|
+
if (tasks.length === 0) {
|
|
261
|
+
consola.info("No tasks found");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
for (const task of tasks) {
|
|
265
|
+
consola.log(`${task.id} ${task.type.padEnd(10)} ${task.status.padEnd(12)} ${task.title}`);
|
|
266
|
+
}
|
|
267
|
+
} catch (err) {
|
|
268
|
+
consola.error(String(err.message));
|
|
269
|
+
process.exit(1);
|
|
101
270
|
}
|
|
102
271
|
}
|
|
103
272
|
});
|
|
@@ -116,8 +285,13 @@ const doneCommand = defineCommand({
|
|
|
116
285
|
},
|
|
117
286
|
run({ args }) {
|
|
118
287
|
const dir = process.cwd();
|
|
119
|
-
|
|
120
|
-
|
|
288
|
+
try {
|
|
289
|
+
archiveTask(dir, args.id);
|
|
290
|
+
consola.success(`Archived ${args.id}`);
|
|
291
|
+
} catch (err) {
|
|
292
|
+
consola.error(String(err.message));
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
121
295
|
}
|
|
122
296
|
});
|
|
123
297
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xxkeefer/mrkl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lightweight CLI tool for structured markdown task tracking",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "unbuild",
|
|
11
11
|
"dev": "node --import tsx src/cli.ts",
|
|
12
|
-
"test": "vitest run"
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"release": "./scripts/release.sh"
|
|
13
14
|
},
|
|
14
15
|
"keywords": [
|
|
15
16
|
"cli",
|
|
@@ -18,7 +19,9 @@
|
|
|
18
19
|
"markdown"
|
|
19
20
|
],
|
|
20
21
|
"license": "MIT",
|
|
21
|
-
"files": [
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
22
25
|
"repository": {
|
|
23
26
|
"type": "git",
|
|
24
27
|
"url": "git+https://github.com/xxKeefer/mrkl.git"
|