@frehilm/ordna-core 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FreHilm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # @frehilm/ordna-core
2
+
3
+ The data layer for [Ordna](../../README.md). Pure TypeScript — no I/O frameworks, no React, no UI. Importable from a CLI tool, a web server, an Electron main process, or an agent script.
4
+
5
+ This package is what `@frehilm/ordna-cli` and `@frehilm/ordna-web` are built on. If you only want to read and write tasks programmatically, install just this.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @frehilm/ordna-core
11
+ # or: npm i @frehilm/ordna-core
12
+ ```
13
+
14
+ ## Use
15
+
16
+ ```ts
17
+ import {
18
+ createContext,
19
+ listTasks,
20
+ createTask,
21
+ moveTask,
22
+ watchTasks,
23
+ ARCHIVED_STATUS,
24
+ type Task,
25
+ } from "@frehilm/ordna-core";
26
+
27
+ const ctx = createContext("/path/to/repo");
28
+
29
+ const tasks = await listTasks(ctx);
30
+ const created = await createTask({ title: "Implement payment flow", priority: "high" }, ctx);
31
+ await moveTask(created.id, "doing", ctx);
32
+
33
+ const stop = watchTasks(ctx, (event) => {
34
+ // event.type: "added" | "changed" | "removed"
35
+ });
36
+ stop();
37
+ ```
38
+
39
+ ## API
40
+
41
+ ```ts
42
+ createContext(cwd?: string): StoreContext
43
+
44
+ listTasks(ctx, opts?: { status?, assignee?, tag? }): Promise<Task[]>
45
+ getTask(id, ctx): Promise<Task | null>
46
+ createTask(input: TaskCreateInput, ctx): Promise<Task>
47
+ updateTask(id, patch: TaskUpdateInput, ctx): Promise<Task>
48
+ moveTask(id, status, ctx): Promise<Task> // depends_on gate on terminal status
49
+ deleteTask(id, ctx): Promise<void>
50
+
51
+ watchTasks(ctx, cb: (event: TaskEvent) => void): () => Promise<void>
52
+ commitTasks(ctx, message?): Promise<void> // stages tasksDir + git commit
53
+
54
+ isKnownStatus(config, status): boolean
55
+ ARCHIVED_STATUS: "archived"
56
+
57
+ parseTask(raw, filePath): Task
58
+ parseTaskFile(filePath): Promise<Task>
59
+ serializeTask(task, mode: "ordna" | "backlog"): string
60
+ extractAcceptanceCriteria(sections): AcceptanceItem[]
61
+ ```
62
+
63
+ Types: `Task`, `Section`, `AcceptanceItem`, `Priority`, `SchemaMode`, `OrdnaConfig`, `StoreContext`, `TaskCreateInput`, `TaskUpdateInput`, `TaskEvent`.
64
+
65
+ ## Task file format
66
+
67
+ Each task is one markdown file in `tasks/`. The filename is the ID.
68
+
69
+ ```markdown
70
+ ---
71
+ id: T-001
72
+ title: Implement payment flow
73
+ status: todo
74
+ assignee: null
75
+ priority: high
76
+ tags: [payments]
77
+ depends_on: []
78
+ created_at: 2026-04-18
79
+ updated_at: 2026-04-18
80
+ ---
81
+
82
+ ## Goal
83
+ Ship a working payment flow.
84
+
85
+ ## Acceptance Criteria
86
+ - [ ] Card works
87
+ - [ ] Apple Pay works
88
+
89
+ ## Notes
90
+ Careful with PCI.
91
+
92
+ ## Progress
93
+ ```
94
+
95
+ Acceptance criteria are plain markdown checkboxes — the file is the source of truth, structured views are derived.
96
+
97
+ ### Dependencies
98
+
99
+ `depends_on` is enforced by `moveTask`: moving a task to the **terminal status** (last entry of `statuses`, `done` by default) while any dependency is unfinished throws. Other transitions are free.
100
+
101
+ ### Archiving
102
+
103
+ `archived` is a **reserved built-in status** — accepted by `moveTask` / `updateTask` regardless of whether it's listed in `config.statuses`. Use it to retire tasks without polluting the active board. The two UI packages filter archived tasks out of every other view by default.
104
+
105
+ ## Configuration
106
+
107
+ `.ordna/config.yaml` is **optional**. With no config, Ordna behaves exactly as documented above. Config only expands.
108
+
109
+ ```yaml
110
+ tasksDir: tasks # where task files live
111
+ schema: ordna # ordna | backlog
112
+ statuses: [todo, doing, done]
113
+ idPrefix: T # custom prefix, e.g. BUG, EPIC
114
+ zeroPaddedIds: 3 # width of the numeric part (0 = no padding)
115
+ webPort: 7420 # consumed by @frehilm/ordna-web
116
+ ```
117
+
118
+ The last entry of `statuses` is the **terminal status** for the dependency gate.
119
+
120
+ ## Backlog.md compatibility
121
+
122
+ Ordna reads [Backlog.md](https://github.com/MrLesk/Backlog.md) repos out of the box. The parser normalizes both field sets:
123
+
124
+ | Ordna | Backlog.md |
125
+ |---------------|---------------------|
126
+ | `tags` | `labels` |
127
+ | `depends_on` | `dependencies` |
128
+ | `created_at` | `createdDate` |
129
+ | `updated_at` | `updatedDate` |
130
+ | `assignee: "x"` or `null` | `assignee: ["x"]` or `[]` |
131
+
132
+ To open a Backlog repo, point Ordna at its directory and set:
133
+
134
+ ```yaml
135
+ tasksDir: backlog
136
+ schema: backlog
137
+ ```
138
+
139
+ In `schema: backlog` mode the writer uses Backlog-style filenames (`task-1 - title.md`) and field names. Tasks round-trip cleanly between tools.
140
+
141
+ ## Body sections in each schema
142
+
143
+ | Schema | Default body sections |
144
+ |-----------|----------------------------------------------------------------------------------|
145
+ | `ordna` | `## Goal` / `## Acceptance Criteria` / `## Notes` / `## Progress` |
146
+ | `backlog` | `## Description` / `## Acceptance Criteria` / `## Implementation Plan` / `## Implementation Notes` / `## Final Summary` |
147
+
148
+ Section headings are matched case-insensitively, with aliases (`Goal`/`Description`, `Notes`/`Implementation Notes`, `Progress`/`Final Summary`).
149
+
150
+ ## License
151
+
152
+ MIT — see [LICENSE](../../LICENSE).
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ import type { SchemaMode } from "./schema.js";
3
+ export declare const configSchema: z.ZodObject<{
4
+ tasksDir: z.ZodDefault<z.ZodString>;
5
+ schema: z.ZodDefault<z.ZodEnum<["ordna", "backlog"]>>;
6
+ statuses: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
7
+ idPrefix: z.ZodDefault<z.ZodString>;
8
+ zeroPaddedIds: z.ZodDefault<z.ZodNumber>;
9
+ webPort: z.ZodDefault<z.ZodNumber>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ tasksDir: string;
12
+ schema: "ordna" | "backlog";
13
+ statuses: string[];
14
+ idPrefix: string;
15
+ zeroPaddedIds: number;
16
+ webPort: number;
17
+ }, {
18
+ tasksDir?: string | undefined;
19
+ schema?: "ordna" | "backlog" | undefined;
20
+ statuses?: string[] | undefined;
21
+ idPrefix?: string | undefined;
22
+ zeroPaddedIds?: number | undefined;
23
+ webPort?: number | undefined;
24
+ }>;
25
+ export type OrdnaConfig = z.infer<typeof configSchema>;
26
+ export declare const DEFAULT_CONFIG: OrdnaConfig;
27
+ export interface LoadConfigOptions {
28
+ cwd?: string;
29
+ overrides?: Partial<OrdnaConfig>;
30
+ }
31
+ export declare function loadConfig(options?: LoadConfigOptions): OrdnaConfig;
32
+ export declare function resolveTasksDir(config: OrdnaConfig, cwd?: string): string;
33
+ export declare function resolveSchemaMode(config: OrdnaConfig): SchemaMode;
34
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;EAOvB,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAEvD,eAAO,MAAM,cAAc,EAAE,WAAoC,CAAC;AAIlE,MAAM,WAAW,iBAAiB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CACjC;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,WAAW,CAYvE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,SAAgB,GAAG,MAAM,CAEhF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU,CAEjE"}
package/dist/config.js ADDED
@@ -0,0 +1,32 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { parse as parseYaml } from "yaml";
4
+ import { z } from "zod";
5
+ export const configSchema = z.object({
6
+ tasksDir: z.string().default("tasks"),
7
+ schema: z.enum(["ordna", "backlog"]).default("ordna"),
8
+ statuses: z.array(z.string()).min(1).default(["todo", "doing", "done"]),
9
+ idPrefix: z.string().default("T"),
10
+ zeroPaddedIds: z.number().int().min(0).max(10).default(3),
11
+ webPort: z.number().int().min(1).max(65535).default(7420),
12
+ });
13
+ export const DEFAULT_CONFIG = configSchema.parse({});
14
+ const CONFIG_PATH = ".ordna/config.yaml";
15
+ export function loadConfig(options = {}) {
16
+ const cwd = options.cwd ?? process.cwd();
17
+ const configFile = join(cwd, CONFIG_PATH);
18
+ let fromFile = {};
19
+ if (existsSync(configFile)) {
20
+ const raw = readFileSync(configFile, "utf8");
21
+ fromFile = parseYaml(raw) ?? {};
22
+ }
23
+ const merged = { ...fromFile, ...(options.overrides ?? {}) };
24
+ return configSchema.parse(merged);
25
+ }
26
+ export function resolveTasksDir(config, cwd = process.cwd()) {
27
+ return join(cwd, config.tasksDir);
28
+ }
29
+ export function resolveSchemaMode(config) {
30
+ return config.schema;
31
+ }
32
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;IACrC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACrD,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACvE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;IACjC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACzD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;CACzD,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,cAAc,GAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAElE,MAAM,WAAW,GAAG,oBAAoB,CAAC;AAOzC,MAAM,UAAU,UAAU,CAAC,UAA6B,EAAE;IACzD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAE1C,IAAI,QAAQ,GAAY,EAAE,CAAC;IAC3B,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC7C,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,MAAM,MAAM,GAAG,EAAE,GAAI,QAAmB,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE,CAAC;IACzE,OAAO,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAmB,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACvE,OAAO,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACpD,OAAO,MAAM,CAAC,MAAM,CAAC;AACtB,CAAC"}
package/dist/git.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { StoreContext } from "./store.js";
2
+ export declare function commitTasks(ctx: StoreContext, message?: string): Promise<void>;
3
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAqB/C,wBAAsB,WAAW,CAChC,GAAG,EAAE,YAAY,EACjB,OAAO,SAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAOf"}
package/dist/git.js ADDED
@@ -0,0 +1,30 @@
1
+ import { spawn } from "node:child_process";
2
+ function run(cwd, args) {
3
+ return new Promise((resolve, reject) => {
4
+ const proc = spawn("git", args, { cwd });
5
+ let stdout = "";
6
+ let stderr = "";
7
+ proc.stdout.on("data", (chunk) => {
8
+ stdout += chunk.toString();
9
+ });
10
+ proc.stderr.on("data", (chunk) => {
11
+ stderr += chunk.toString();
12
+ });
13
+ proc.on("error", reject);
14
+ proc.on("close", (code) => {
15
+ if (code === 0)
16
+ resolve({ stdout, stderr });
17
+ else
18
+ reject(new Error(`git ${args.join(" ")} failed (${code}): ${stderr.trim()}`));
19
+ });
20
+ });
21
+ }
22
+ export async function commitTasks(ctx, message = "chore(tasks): update") {
23
+ await run(ctx.cwd, ["add", "--", ctx.config.tasksDir]);
24
+ const status = await run(ctx.cwd, ["status", "--porcelain", "--", ctx.config.tasksDir]);
25
+ if (status.stdout.trim().length === 0) {
26
+ throw new Error("No task changes to commit.");
27
+ }
28
+ await run(ctx.cwd, ["commit", "-m", message, "--", ctx.config.tasksDir]);
29
+ }
30
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,SAAS,GAAG,CAAC,GAAW,EAAE,IAAc;IACvC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACzC,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACzB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,IAAI,IAAI,KAAK,CAAC;gBAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;;gBACvC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,IAAI,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;QACpF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAChC,GAAiB,EACjB,OAAO,GAAG,sBAAsB;IAEhC,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxF,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1E,CAAC"}
package/dist/ids.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { OrdnaConfig } from "./config.js";
2
+ export declare function formatId(config: OrdnaConfig, numeric: number): string;
3
+ export declare function parseId(config: OrdnaConfig, id: string): number | null;
4
+ export declare function extractIdFromFilename(config: OrdnaConfig, filename: string): number | null;
5
+ export declare function nextId(config: OrdnaConfig, tasksDir: string): string;
6
+ //# sourceMappingURL=ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,wBAAgB,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIrE;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMtE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW1F;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAapE"}
package/dist/ids.js ADDED
@@ -0,0 +1,44 @@
1
+ import { readdirSync } from "node:fs";
2
+ export function formatId(config, numeric) {
3
+ const padded = config.zeroPaddedIds > 0 ? String(numeric).padStart(config.zeroPaddedIds, "0") : String(numeric);
4
+ return `${config.idPrefix}-${padded}`;
5
+ }
6
+ export function parseId(config, id) {
7
+ const prefix = `${config.idPrefix}-`;
8
+ if (!id.startsWith(prefix))
9
+ return null;
10
+ const rest = id.slice(prefix.length);
11
+ const n = Number.parseInt(rest, 10);
12
+ return Number.isFinite(n) ? n : null;
13
+ }
14
+ export function extractIdFromFilename(config, filename) {
15
+ const withoutExt = filename.replace(/\.md$/i, "");
16
+ const ordna = parseId(config, withoutExt);
17
+ if (ordna !== null)
18
+ return ordna;
19
+ const backlogMatch = withoutExt.match(/^task-(\d+)\b/);
20
+ if (backlogMatch?.[1]) {
21
+ const n = Number.parseInt(backlogMatch[1], 10);
22
+ return Number.isFinite(n) ? n : null;
23
+ }
24
+ return null;
25
+ }
26
+ export function nextId(config, tasksDir) {
27
+ let max = 0;
28
+ try {
29
+ const entries = readdirSync(tasksDir, { withFileTypes: true });
30
+ for (const entry of entries) {
31
+ if (!entry.isFile() || !entry.name.endsWith(".md"))
32
+ continue;
33
+ const n = extractIdFromFilename(config, entry.name);
34
+ if (n !== null && n > max)
35
+ max = n;
36
+ }
37
+ }
38
+ catch (error) {
39
+ if (error.code !== "ENOENT")
40
+ throw error;
41
+ }
42
+ return formatId(config, max + 1);
43
+ }
44
+ //# sourceMappingURL=ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ids.js","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGtC,MAAM,UAAU,QAAQ,CAAC,MAAmB,EAAE,OAAe;IAC5D,MAAM,MAAM,GACX,MAAM,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClG,OAAO,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,MAAmB,EAAE,EAAU;IACtD,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,GAAG,CAAC;IACrC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAmB,EAAE,QAAgB;IAC1E,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAEjC,MAAM,YAAY,GAAG,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACvD,IAAI,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,MAAmB,EAAE,QAAgB;IAC3D,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,SAAS;YAC7D,MAAM,CAAC,GAAG,qBAAqB,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACpD,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,GAAG;gBAAE,GAAG,GAAG,CAAC,CAAC;QACpC,CAAC;IACF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IACrE,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { DEFAULT_CONFIG, configSchema, loadConfig, resolveSchemaMode, resolveTasksDir, type LoadConfigOptions, type OrdnaConfig, } from "./config.js";
2
+ export { commitTasks } from "./git.js";
3
+ export { extractIdFromFilename, formatId, nextId, parseId } from "./ids.js";
4
+ export { extractAcceptanceCriteria, findSection, parseTask, parseTaskFile, splitSections, } from "./parser.js";
5
+ export type { AcceptanceItem, Priority, RawFrontmatter, SchemaMode, Section, Task, TaskCreateInput, TaskUpdateInput, } from "./schema.js";
6
+ export { BACKLOG_BODY_HEADINGS, BODY_HEADING_ALIASES, FRONTMATTER_ALIASES, ORDNA_BODY_HEADINGS, frontmatterSchema, priorityEnum, } from "./schema.js";
7
+ export { ARCHIVED_STATUS, createContext, createTask, deleteTask, getTask, isKnownStatus, listTasks, moveTask, updateTask, type ListTasksOptions, type StoreContext, } from "./store.js";
8
+ export { defaultSectionsFor, serializeTask } from "./writer.js";
9
+ export { watchTasks, type TaskEvent, type TaskEventListener, type WatchOptions } from "./watcher.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,cAAc,EACd,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,WAAW,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC5E,OAAO,EACN,yBAAyB,EACzB,WAAW,EACX,SAAS,EACT,aAAa,EACb,aAAa,GACb,MAAM,aAAa,CAAC;AACrB,YAAY,EACX,cAAc,EACd,QAAQ,EACR,cAAc,EACd,UAAU,EACV,OAAO,EACP,IAAI,EACJ,eAAe,EACf,eAAe,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,eAAe,EACf,aAAa,EACb,UAAU,EACV,UAAU,EACV,OAAO,EACP,aAAa,EACb,SAAS,EACT,QAAQ,EACR,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,YAAY,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { DEFAULT_CONFIG, configSchema, loadConfig, resolveSchemaMode, resolveTasksDir, } from "./config.js";
2
+ export { commitTasks } from "./git.js";
3
+ export { extractIdFromFilename, formatId, nextId, parseId } from "./ids.js";
4
+ export { extractAcceptanceCriteria, findSection, parseTask, parseTaskFile, splitSections, } from "./parser.js";
5
+ export { BACKLOG_BODY_HEADINGS, BODY_HEADING_ALIASES, FRONTMATTER_ALIASES, ORDNA_BODY_HEADINGS, frontmatterSchema, priorityEnum, } from "./schema.js";
6
+ export { ARCHIVED_STATUS, createContext, createTask, deleteTask, getTask, isKnownStatus, listTasks, moveTask, updateTask, } from "./store.js";
7
+ export { defaultSectionsFor, serializeTask } from "./writer.js";
8
+ export { watchTasks } from "./watcher.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,cAAc,EACd,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,eAAe,GAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC5E,OAAO,EACN,yBAAyB,EACzB,WAAW,EACX,SAAS,EACT,aAAa,EACb,aAAa,GACb,MAAM,aAAa,CAAC;AAWrB,OAAO,EACN,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,eAAe,EACf,aAAa,EACb,UAAU,EACV,UAAU,EACV,OAAO,EACP,aAAa,EACb,SAAS,EACT,QAAQ,EACR,UAAU,GAGV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,UAAU,EAA6D,MAAM,cAAc,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type AcceptanceItem, type Section, type Task } from "./schema.js";
2
+ export declare function splitSections(body: string): Section[];
3
+ export declare function findSection(sections: Section[], canonical: string): Section | undefined;
4
+ export declare function extractAcceptanceCriteria(sections: Section[]): AcceptanceItem[];
5
+ export declare function parseTask(raw: string, filePath: string): Task;
6
+ export declare function parseTaskFile(filePath: string): Promise<Task>;
7
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,cAAc,EAKnB,KAAK,OAAO,EACZ,KAAK,IAAI,EAET,MAAM,aAAa,CAAC;AAyCrB,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CA4BrD;AAMD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAGvF;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,CAS/E;AAcD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAoB7D;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGnE"}
package/dist/parser.js ADDED
@@ -0,0 +1,127 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import matter from "gray-matter";
3
+ import { BODY_HEADING_ALIASES, FRONTMATTER_ALIASES, frontmatterSchema, } from "./schema.js";
4
+ function pickAlias(frontmatter, canonical) {
5
+ const aliases = FRONTMATTER_ALIASES[canonical] ?? [canonical];
6
+ for (const key of aliases) {
7
+ if (key in frontmatter && frontmatter[key] !== undefined && frontmatter[key] !== null) {
8
+ return frontmatter[key];
9
+ }
10
+ }
11
+ return undefined;
12
+ }
13
+ function normalizeDate(value) {
14
+ if (value instanceof Date)
15
+ return value.toISOString().slice(0, 10);
16
+ if (typeof value === "string")
17
+ return value;
18
+ return "";
19
+ }
20
+ function normalizeAssignee(value) {
21
+ if (value === undefined || value === null)
22
+ return null;
23
+ if (typeof value === "string")
24
+ return value.length === 0 ? null : value;
25
+ if (Array.isArray(value)) {
26
+ const first = value.find((v) => typeof v === "string" && v.length > 0);
27
+ return first ?? null;
28
+ }
29
+ return null;
30
+ }
31
+ function normalizePriority(value) {
32
+ if (value === "high" || value === "medium" || value === "low")
33
+ return value;
34
+ return null;
35
+ }
36
+ function normalizeStringArray(value) {
37
+ if (!Array.isArray(value))
38
+ return [];
39
+ return value.filter((v) => typeof v === "string");
40
+ }
41
+ export function splitSections(body) {
42
+ const lines = body.split("\n");
43
+ const sections = [];
44
+ let current = null;
45
+ let preamble = "";
46
+ let sawHeading = false;
47
+ for (const line of lines) {
48
+ const match = line.match(/^(#{2})\s+(.+?)\s*$/);
49
+ if (match) {
50
+ if (current)
51
+ sections.push(finalizeSection(current));
52
+ current = { heading: match[2], level: 2, content: "" };
53
+ sawHeading = true;
54
+ continue;
55
+ }
56
+ if (!sawHeading) {
57
+ preamble += (preamble ? "\n" : "") + line;
58
+ }
59
+ else if (current) {
60
+ current.content += (current.content ? "\n" : "") + line;
61
+ }
62
+ }
63
+ if (current)
64
+ sections.push(finalizeSection(current));
65
+ const trimmedPreamble = preamble.replace(/^\n+|\n+$/g, "");
66
+ if (trimmedPreamble.length > 0) {
67
+ sections.unshift({ heading: "", level: 0, content: trimmedPreamble });
68
+ }
69
+ return sections;
70
+ }
71
+ function finalizeSection(section) {
72
+ return { ...section, content: section.content.replace(/^\n+|\n+$/g, "") };
73
+ }
74
+ export function findSection(sections, canonical) {
75
+ const aliases = BODY_HEADING_ALIASES[canonical] ?? [canonical];
76
+ return sections.find((s) => aliases.some((a) => a.toLowerCase() === s.heading.toLowerCase()));
77
+ }
78
+ export function extractAcceptanceCriteria(sections) {
79
+ const section = findSection(sections, "acceptance_criteria");
80
+ if (!section)
81
+ return [];
82
+ const items = [];
83
+ for (const line of section.content.split("\n")) {
84
+ const m = line.match(/^\s*-\s+\[( |x|X)\]\s+(.*)$/);
85
+ if (m)
86
+ items.push({ checked: m[1] !== " ", text: (m[2] ?? "").trim() });
87
+ }
88
+ return items;
89
+ }
90
+ function collectExtraFrontmatter(frontmatter) {
91
+ const known = new Set();
92
+ for (const aliases of Object.values(FRONTMATTER_ALIASES)) {
93
+ for (const key of aliases)
94
+ known.add(key);
95
+ }
96
+ const extra = {};
97
+ for (const [key, value] of Object.entries(frontmatter)) {
98
+ if (!known.has(key))
99
+ extra[key] = value;
100
+ }
101
+ return extra;
102
+ }
103
+ export function parseTask(raw, filePath) {
104
+ const parsed = matter(raw);
105
+ const frontmatter = frontmatterSchema.parse(parsed.data ?? {});
106
+ const sections = splitSections(parsed.content);
107
+ return {
108
+ id: String(frontmatter.id),
109
+ title: String(frontmatter.title),
110
+ status: String(frontmatter.status),
111
+ assignee: normalizeAssignee(pickAlias(frontmatter, "assignee")),
112
+ priority: normalizePriority(pickAlias(frontmatter, "priority")),
113
+ tags: normalizeStringArray(pickAlias(frontmatter, "tags")),
114
+ depends_on: normalizeStringArray(pickAlias(frontmatter, "depends_on")),
115
+ created_at: normalizeDate(pickAlias(frontmatter, "created_at")),
116
+ updated_at: normalizeDate(pickAlias(frontmatter, "updated_at")),
117
+ sections,
118
+ extra_frontmatter: collectExtraFrontmatter(frontmatter),
119
+ filePath,
120
+ rawContent: raw,
121
+ };
122
+ }
123
+ export async function parseTaskFile(filePath) {
124
+ const raw = await readFile(filePath, "utf8");
125
+ return parseTask(raw, filePath);
126
+ }
127
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ import { z } from "zod";
2
+ export type Priority = "high" | "medium" | "low";
3
+ export type SchemaMode = "ordna" | "backlog";
4
+ export interface AcceptanceItem {
5
+ text: string;
6
+ checked: boolean;
7
+ }
8
+ export interface Section {
9
+ heading: string;
10
+ level: number;
11
+ content: string;
12
+ }
13
+ export interface Task {
14
+ id: string;
15
+ title: string;
16
+ status: string;
17
+ assignee: string | null;
18
+ priority: Priority | null;
19
+ tags: string[];
20
+ depends_on: string[];
21
+ created_at: string;
22
+ updated_at: string;
23
+ sections: Section[];
24
+ extra_frontmatter: Record<string, unknown>;
25
+ filePath: string;
26
+ rawContent: string;
27
+ }
28
+ export interface TaskCreateInput {
29
+ title: string;
30
+ status?: string;
31
+ assignee?: string | null;
32
+ priority?: Priority | null;
33
+ tags?: string[];
34
+ depends_on?: string[];
35
+ body?: string;
36
+ }
37
+ export interface TaskUpdateInput {
38
+ title?: string;
39
+ status?: string;
40
+ assignee?: string | null;
41
+ priority?: Priority | null;
42
+ tags?: string[];
43
+ depends_on?: string[];
44
+ sections?: Section[];
45
+ }
46
+ export declare const FRONTMATTER_ALIASES: Record<string, string[]>;
47
+ export declare const ORDNA_BODY_HEADINGS: {
48
+ readonly description: "Goal";
49
+ readonly acceptance_criteria: "Acceptance Criteria";
50
+ readonly notes: "Notes";
51
+ readonly progress: "Progress";
52
+ };
53
+ export declare const BACKLOG_BODY_HEADINGS: {
54
+ readonly description: "Description";
55
+ readonly acceptance_criteria: "Acceptance Criteria";
56
+ readonly implementation_plan: "Implementation Plan";
57
+ readonly implementation_notes: "Implementation Notes";
58
+ readonly final_summary: "Final Summary";
59
+ };
60
+ export declare const BODY_HEADING_ALIASES: Record<string, string[]>;
61
+ export declare const priorityEnum: z.ZodEnum<["high", "medium", "low"]>;
62
+ export declare const frontmatterSchema: z.ZodObject<{
63
+ id: z.ZodString;
64
+ title: z.ZodString;
65
+ status: z.ZodString;
66
+ assignee: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">, z.ZodNull]>>;
67
+ priority: z.ZodOptional<z.ZodNullable<z.ZodEnum<["high", "medium", "low"]>>>;
68
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
69
+ labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
70
+ depends_on: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
71
+ dependencies: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
72
+ created_at: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
73
+ createdDate: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
74
+ updated_at: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
75
+ updatedDate: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
76
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
77
+ id: z.ZodString;
78
+ title: z.ZodString;
79
+ status: z.ZodString;
80
+ assignee: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">, z.ZodNull]>>;
81
+ priority: z.ZodOptional<z.ZodNullable<z.ZodEnum<["high", "medium", "low"]>>>;
82
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
83
+ labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
84
+ depends_on: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
85
+ dependencies: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
86
+ created_at: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
87
+ createdDate: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
88
+ updated_at: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
89
+ updatedDate: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
90
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
91
+ id: z.ZodString;
92
+ title: z.ZodString;
93
+ status: z.ZodString;
94
+ assignee: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">, z.ZodNull]>>;
95
+ priority: z.ZodOptional<z.ZodNullable<z.ZodEnum<["high", "medium", "low"]>>>;
96
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
97
+ labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
98
+ depends_on: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
99
+ dependencies: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
100
+ created_at: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
101
+ createdDate: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
102
+ updated_at: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
103
+ updatedDate: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
104
+ }, z.ZodTypeAny, "passthrough">>;
105
+ export type RawFrontmatter = z.infer<typeof frontmatterSchema>;
106
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,IAAI;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAUxD,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;;;CAKtB,CAAC;AAEX,eAAO,MAAM,qBAAqB;;;;;;CAMxB,CAAC;AAEX,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAMzD,CAAC;AAEF,eAAO,MAAM,YAAY,sCAAoC,CAAC;AAE9D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAgBf,CAAC;AAEhB,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
package/dist/schema.js ADDED
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ export const FRONTMATTER_ALIASES = {
3
+ tags: ["tags", "labels"],
4
+ depends_on: ["depends_on", "dependencies"],
5
+ created_at: ["created_at", "createdDate", "created"],
6
+ updated_at: ["updated_at", "updatedDate", "updated"],
7
+ assignee: ["assignee"],
8
+ priority: ["priority"],
9
+ status: ["status"],
10
+ title: ["title"],
11
+ id: ["id"],
12
+ };
13
+ export const ORDNA_BODY_HEADINGS = {
14
+ description: "Goal",
15
+ acceptance_criteria: "Acceptance Criteria",
16
+ notes: "Notes",
17
+ progress: "Progress",
18
+ };
19
+ export const BACKLOG_BODY_HEADINGS = {
20
+ description: "Description",
21
+ acceptance_criteria: "Acceptance Criteria",
22
+ implementation_plan: "Implementation Plan",
23
+ implementation_notes: "Implementation Notes",
24
+ final_summary: "Final Summary",
25
+ };
26
+ export const BODY_HEADING_ALIASES = {
27
+ description: ["Goal", "Description"],
28
+ acceptance_criteria: ["Acceptance Criteria"],
29
+ notes: ["Notes", "Implementation Notes"],
30
+ progress: ["Progress", "Final Summary"],
31
+ implementation_plan: ["Implementation Plan"],
32
+ };
33
+ export const priorityEnum = z.enum(["high", "medium", "low"]);
34
+ export const frontmatterSchema = z
35
+ .object({
36
+ id: z.string(),
37
+ title: z.string(),
38
+ status: z.string(),
39
+ assignee: z.union([z.string(), z.array(z.string()), z.null()]).optional(),
40
+ priority: priorityEnum.nullable().optional(),
41
+ tags: z.array(z.string()).optional(),
42
+ labels: z.array(z.string()).optional(),
43
+ depends_on: z.array(z.string()).optional(),
44
+ dependencies: z.array(z.string()).optional(),
45
+ created_at: z.union([z.string(), z.date()]).optional(),
46
+ createdDate: z.union([z.string(), z.date()]).optional(),
47
+ updated_at: z.union([z.string(), z.date()]).optional(),
48
+ updatedDate: z.union([z.string(), z.date()]).optional(),
49
+ })
50
+ .passthrough();
51
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type OrdnaConfig } from "./config.js";
2
+ import type { Task, TaskCreateInput, TaskUpdateInput } from "./schema.js";
3
+ export interface StoreContext {
4
+ cwd: string;
5
+ config: OrdnaConfig;
6
+ tasksDir: string;
7
+ }
8
+ export declare const ARCHIVED_STATUS = "archived";
9
+ export declare function isKnownStatus(config: OrdnaConfig, status: string): boolean;
10
+ export interface ListTasksOptions {
11
+ status?: string;
12
+ assignee?: string;
13
+ tag?: string;
14
+ }
15
+ export declare function createContext(cwd?: string): StoreContext;
16
+ export declare function listTasks(ctx?: StoreContext, options?: ListTasksOptions): Promise<Task[]>;
17
+ export declare function getTask(id: string, ctx?: StoreContext): Promise<Task | null>;
18
+ export declare function createTask(input: TaskCreateInput, ctx?: StoreContext): Promise<Task>;
19
+ export declare function updateTask(id: string, patch: TaskUpdateInput, ctx?: StoreContext): Promise<Task>;
20
+ export declare function moveTask(id: string, status: string, ctx?: StoreContext): Promise<Task>;
21
+ export declare function deleteTask(id: string, ctx?: StoreContext): Promise<void>;
22
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAA+B,MAAM,aAAa,CAAC;AAG5E,OAAO,KAAK,EAAuB,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG/F,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAG1E;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,aAAa,CAAC,GAAG,SAAgB,GAAG,YAAY,CAI/D;AAwBD,wBAAsB,SAAS,CAC9B,GAAG,GAAE,YAA8B,EACnC,OAAO,GAAE,gBAAqB,GAC5B,OAAO,CAAC,IAAI,EAAE,CAAC,CAuBjB;AAED,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,YAA8B,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAGnG;AAED,wBAAsB,UAAU,CAC/B,KAAK,EAAE,eAAe,EACtB,GAAG,GAAE,YAA8B,GACjC,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED,wBAAsB,UAAU,CAC/B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,eAAe,EACtB,GAAG,GAAE,YAA8B,GACjC,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAsB,QAAQ,CAC7B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,YAA8B,GACjC,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,YAA8B,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/F"}
package/dist/store.js ADDED
@@ -0,0 +1,151 @@
1
+ import { existsSync, mkdirSync, readdirSync } from "node:fs";
2
+ import { readFile, unlink, writeFile } from "node:fs/promises";
3
+ import { basename, join } from "node:path";
4
+ import { loadConfig, resolveTasksDir } from "./config.js";
5
+ import { formatId, nextId, parseId } from "./ids.js";
6
+ import { parseTask } from "./parser.js";
7
+ import { defaultSectionsFor, serializeTask } from "./writer.js";
8
+ export const ARCHIVED_STATUS = "archived";
9
+ export function isKnownStatus(config, status) {
10
+ if (status === ARCHIVED_STATUS)
11
+ return true;
12
+ return config.statuses.includes(status);
13
+ }
14
+ export function createContext(cwd = process.cwd()) {
15
+ const config = loadConfig({ cwd });
16
+ const tasksDir = resolveTasksDir(config, cwd);
17
+ return { cwd, config, tasksDir };
18
+ }
19
+ function today() {
20
+ return new Date().toISOString().slice(0, 10);
21
+ }
22
+ function slugifyTitle(title) {
23
+ return title
24
+ .toLowerCase()
25
+ .replace(/[^a-z0-9]+/g, "-")
26
+ .replace(/^-+|-+$/g, "")
27
+ .slice(0, 60);
28
+ }
29
+ function filenameFor(id, title, mode, config) {
30
+ if (mode === "backlog") {
31
+ const numeric = parseId(config, id);
32
+ const numericPart = numeric ?? id;
33
+ const slug = slugifyTitle(title) || "task";
34
+ return `task-${numericPart} - ${slug}.md`;
35
+ }
36
+ return `${id}.md`;
37
+ }
38
+ export async function listTasks(ctx = createContext(), options = {}) {
39
+ if (!existsSync(ctx.tasksDir))
40
+ return [];
41
+ const entries = readdirSync(ctx.tasksDir, { withFileTypes: true });
42
+ const tasks = [];
43
+ for (const entry of entries) {
44
+ if (!entry.isFile() || !entry.name.endsWith(".md"))
45
+ continue;
46
+ const filePath = join(ctx.tasksDir, entry.name);
47
+ const raw = await readFile(filePath, "utf8");
48
+ try {
49
+ tasks.push(parseTask(raw, filePath));
50
+ }
51
+ catch {
52
+ // Skip malformed tasks silently; surfaced via dedicated validator later.
53
+ }
54
+ }
55
+ let filtered = tasks;
56
+ if (options.status)
57
+ filtered = filtered.filter((t) => t.status === options.status);
58
+ if (options.assignee)
59
+ filtered = filtered.filter((t) => t.assignee === options.assignee);
60
+ if (options.tag)
61
+ filtered = filtered.filter((t) => t.tags.includes(options.tag));
62
+ filtered.sort((a, b) => a.id.localeCompare(b.id, undefined, { numeric: true }));
63
+ return filtered;
64
+ }
65
+ export async function getTask(id, ctx = createContext()) {
66
+ const tasks = await listTasks(ctx);
67
+ return tasks.find((t) => t.id === id) ?? null;
68
+ }
69
+ export async function createTask(input, ctx = createContext()) {
70
+ if (!existsSync(ctx.tasksDir))
71
+ mkdirSync(ctx.tasksDir, { recursive: true });
72
+ const id = nextId(ctx.config, ctx.tasksDir);
73
+ const status = input.status ?? ctx.config.statuses[0];
74
+ if (!status)
75
+ throw new Error("Config has no statuses defined.");
76
+ if (!isKnownStatus(ctx.config, status)) {
77
+ throw new Error(`Status "${status}" is not in configured statuses.`);
78
+ }
79
+ const now = today();
80
+ const task = {
81
+ id,
82
+ title: input.title,
83
+ status,
84
+ assignee: input.assignee ?? null,
85
+ priority: input.priority ?? null,
86
+ tags: input.tags ?? [],
87
+ depends_on: input.depends_on ?? [],
88
+ created_at: now,
89
+ updated_at: now,
90
+ sections: defaultSectionsFor(ctx.config.schema),
91
+ extra_frontmatter: {},
92
+ filePath: "",
93
+ rawContent: "",
94
+ };
95
+ const filename = filenameFor(id, task.title, ctx.config.schema, ctx.config);
96
+ task.filePath = join(ctx.tasksDir, filename);
97
+ const serialized = serializeTask(task, ctx.config.schema);
98
+ task.rawContent = serialized;
99
+ await writeFile(task.filePath, serialized, "utf8");
100
+ return task;
101
+ }
102
+ export async function updateTask(id, patch, ctx = createContext()) {
103
+ const existing = await getTask(id, ctx);
104
+ if (!existing)
105
+ throw new Error(`Task ${id} not found.`);
106
+ const next = {
107
+ ...existing,
108
+ title: patch.title ?? existing.title,
109
+ status: patch.status ?? existing.status,
110
+ assignee: patch.assignee !== undefined ? patch.assignee : existing.assignee,
111
+ priority: patch.priority !== undefined ? patch.priority : existing.priority,
112
+ tags: patch.tags ?? existing.tags,
113
+ depends_on: patch.depends_on ?? existing.depends_on,
114
+ sections: patch.sections ?? existing.sections,
115
+ updated_at: today(),
116
+ };
117
+ if (next.status !== existing.status && !isKnownStatus(ctx.config, next.status)) {
118
+ throw new Error(`Status "${next.status}" is not in configured statuses.`);
119
+ }
120
+ const serialized = serializeTask(next, ctx.config.schema);
121
+ next.rawContent = serialized;
122
+ await writeFile(existing.filePath, serialized, "utf8");
123
+ return next;
124
+ }
125
+ export async function moveTask(id, status, ctx = createContext()) {
126
+ const terminal = ctx.config.statuses[ctx.config.statuses.length - 1];
127
+ if (status === terminal) {
128
+ const task = await getTask(id, ctx);
129
+ if (!task)
130
+ throw new Error(`Task ${id} not found.`);
131
+ if (task.depends_on.length > 0) {
132
+ const all = await listTasks(ctx);
133
+ const byId = new Map(all.map((t) => [t.id, t]));
134
+ const unfinished = task.depends_on.filter((dep) => {
135
+ const d = byId.get(dep);
136
+ return !d || d.status !== terminal;
137
+ });
138
+ if (unfinished.length > 0) {
139
+ throw new Error(`Cannot move ${id} to ${status}: dependencies not ${terminal}: ${unfinished.join(", ")}`);
140
+ }
141
+ }
142
+ }
143
+ return updateTask(id, { status }, ctx);
144
+ }
145
+ export async function deleteTask(id, ctx = createContext()) {
146
+ const task = await getTask(id, ctx);
147
+ if (!task)
148
+ throw new Error(`Task ${id} not found.`);
149
+ await unlink(task.filePath);
150
+ }
151
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { StoreContext } from "./store.js";
2
+ import type { Task } from "./schema.js";
3
+ export type TaskEvent = {
4
+ type: "added";
5
+ task: Task;
6
+ } | {
7
+ type: "changed";
8
+ task: Task;
9
+ } | {
10
+ type: "removed";
11
+ filePath: string;
12
+ };
13
+ export type TaskEventListener = (event: TaskEvent) => void;
14
+ export interface WatchOptions {
15
+ ignoreInitial?: boolean;
16
+ }
17
+ export declare function watchTasks(ctx: StoreContext, listener: TaskEventListener, options?: WatchOptions): () => Promise<void>;
18
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,MAAM,SAAS,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAE3D,MAAM,WAAW,YAAY;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,UAAU,CACzB,GAAG,EAAE,YAAY,EACjB,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,YAAiB,GACxB,MAAM,OAAO,CAAC,IAAI,CAAC,CA8BrB"}
@@ -0,0 +1,32 @@
1
+ import { basename } from "node:path";
2
+ import chokidar from "chokidar";
3
+ import { parseTaskFile } from "./parser.js";
4
+ export function watchTasks(ctx, listener, options = {}) {
5
+ const watcher = chokidar.watch(ctx.tasksDir, {
6
+ ignoreInitial: options.ignoreInitial ?? true,
7
+ depth: 0,
8
+ persistent: true,
9
+ });
10
+ const emitIfMarkdown = async (type, filePath) => {
11
+ if (!filePath.endsWith(".md"))
12
+ return;
13
+ try {
14
+ const task = await parseTaskFile(filePath);
15
+ listener({ type, task });
16
+ }
17
+ catch {
18
+ // Ignore partial writes or malformed files.
19
+ }
20
+ };
21
+ watcher.on("add", (path) => void emitIfMarkdown("added", path));
22
+ watcher.on("change", (path) => void emitIfMarkdown("changed", path));
23
+ watcher.on("unlink", (path) => {
24
+ if (!path.endsWith(".md"))
25
+ return;
26
+ listener({ type: "removed", filePath: path });
27
+ });
28
+ return async () => {
29
+ await watcher.close();
30
+ };
31
+ }
32
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAc5C,MAAM,UAAU,UAAU,CACzB,GAAiB,EACjB,QAA2B,EAC3B,UAAwB,EAAE;IAE1B,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE;QAC5C,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,IAAI;QAC5C,KAAK,EAAE,CAAC;QACR,UAAU,EAAE,IAAI;KAChB,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,KAAK,EAC3B,IAAyB,EACzB,QAAgB,EACA,EAAE;QAClB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO;QACtC,IAAI,CAAC;YACJ,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC3C,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACR,4CAA4C;QAC7C,CAAC;IACF,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;QAC7B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO;QAClC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,IAAI,EAAE;QACjB,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type SchemaMode, type Section, type Task } from "./schema.js";
2
+ export declare function serializeTask(task: Task, mode: SchemaMode): string;
3
+ export declare function defaultSectionsFor(mode: SchemaMode): Section[];
4
+ //# sourceMappingURL=writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../src/writer.ts"],"names":[],"mappings":"AACA,OAAO,EAGN,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,MAAM,aAAa,CAAC;AA4CrB,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAKlE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,EAAE,CAQ9D"}
package/dist/writer.js ADDED
@@ -0,0 +1,61 @@
1
+ import { stringify as stringifyYaml } from "yaml";
2
+ import { BACKLOG_BODY_HEADINGS, ORDNA_BODY_HEADINGS, } from "./schema.js";
3
+ function buildFrontmatter(task, mode) {
4
+ const base = {
5
+ id: task.id,
6
+ title: task.title,
7
+ status: task.status,
8
+ };
9
+ if (mode === "backlog") {
10
+ base.assignee = task.assignee === null ? [] : [task.assignee];
11
+ if (task.priority !== null)
12
+ base.priority = task.priority;
13
+ base.labels = task.tags;
14
+ base.dependencies = task.depends_on;
15
+ base.createdDate = task.created_at;
16
+ if (task.updated_at)
17
+ base.updatedDate = task.updated_at;
18
+ }
19
+ else {
20
+ base.assignee = task.assignee;
21
+ base.priority = task.priority;
22
+ base.tags = task.tags;
23
+ base.depends_on = task.depends_on;
24
+ base.created_at = task.created_at;
25
+ base.updated_at = task.updated_at;
26
+ }
27
+ for (const [key, value] of Object.entries(task.extra_frontmatter)) {
28
+ if (!(key in base))
29
+ base[key] = value;
30
+ }
31
+ return base;
32
+ }
33
+ function renderSections(sections) {
34
+ const parts = [];
35
+ for (const section of sections) {
36
+ if (section.level === 0 || section.heading === "") {
37
+ if (section.content.length > 0)
38
+ parts.push(section.content);
39
+ continue;
40
+ }
41
+ const hashes = "#".repeat(section.level || 2);
42
+ parts.push(`${hashes} ${section.heading}\n\n${section.content}`.trimEnd());
43
+ }
44
+ return parts.join("\n\n");
45
+ }
46
+ export function serializeTask(task, mode) {
47
+ const frontmatter = buildFrontmatter(task, mode);
48
+ const yaml = stringifyYaml(frontmatter).trimEnd();
49
+ const body = renderSections(task.sections);
50
+ return `---\n${yaml}\n---\n\n${body}\n`;
51
+ }
52
+ export function defaultSectionsFor(mode) {
53
+ const headings = mode === "backlog" ? BACKLOG_BODY_HEADINGS : ORDNA_BODY_HEADINGS;
54
+ const sections = [];
55
+ for (const heading of Object.values(headings)) {
56
+ const content = heading === "Acceptance Criteria" ? "- [ ] " : "";
57
+ sections.push({ heading, level: 2, content });
58
+ }
59
+ return sections;
60
+ }
61
+ //# sourceMappingURL=writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../src/writer.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@frehilm/ordna-core",
3
+ "version": "0.1.0",
4
+ "description": "Git-native task management — pure data layer. Tasks are markdown files; the board is the directory.",
5
+ "license": "MIT",
6
+ "author": "FreHilm <1584617+FreHilm@users.noreply.github.com>",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/FreHilm/ordna.git",
10
+ "directory": "packages/core"
11
+ },
12
+ "homepage": "https://github.com/FreHilm/ordna#readme",
13
+ "bugs": "https://github.com/FreHilm/ordna/issues",
14
+ "keywords": [
15
+ "kanban",
16
+ "tasks",
17
+ "markdown",
18
+ "git",
19
+ "project-management"
20
+ ],
21
+ "type": "module",
22
+ "main": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "dependencies": {
34
+ "chokidar": "^4.0.3",
35
+ "gray-matter": "^4.0.3",
36
+ "yaml": "^2.6.1",
37
+ "zod": "^3.24.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.10.5",
41
+ "typescript": "^5.7.2",
42
+ "vitest": "^2.1.8"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.json",
46
+ "typecheck": "tsc -p tsconfig.json --noEmit",
47
+ "test": "vitest run",
48
+ "test:watch": "vitest"
49
+ }
50
+ }