@frehilm/ordna-core 0.1.1 → 0.1.3
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 +21 -0
- package/dist/provider.d.ts +82 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +2 -0
- package/dist/provider.js.map +1 -0
- package/dist/providers/file.d.ts +47 -0
- package/dist/providers/file.d.ts.map +1 -0
- package/dist/providers/file.js +260 -0
- package/dist/providers/file.js.map +1 -0
- package/dist/providers/load.d.ts +22 -0
- package/dist/providers/load.d.ts.map +1 -0
- package/dist/providers/load.js +47 -0
- package/dist/providers/load.js.map +1 -0
- package/package.json +18 -10
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.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Task, TaskCreateInput, TaskUpdateInput } from "./schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Filters for {@link TaskProvider.list}.
|
|
4
|
+
*/
|
|
5
|
+
export interface ListOptions {
|
|
6
|
+
status?: string;
|
|
7
|
+
assignee?: string;
|
|
8
|
+
tag?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* An event emitted by {@link TaskProvider.watch} when the underlying task set changes.
|
|
12
|
+
*
|
|
13
|
+
* `removed` carries `id` so all providers (file, jira, linear, …) can describe a removal
|
|
14
|
+
* the same way. The optional `filePath` stays for back-compat with the existing file
|
|
15
|
+
* watcher and is populated only when the source provider is file-backed.
|
|
16
|
+
*/
|
|
17
|
+
export type TaskEvent = {
|
|
18
|
+
type: "added";
|
|
19
|
+
task: Task;
|
|
20
|
+
} | {
|
|
21
|
+
type: "changed";
|
|
22
|
+
task: Task;
|
|
23
|
+
} | {
|
|
24
|
+
type: "removed";
|
|
25
|
+
id: string;
|
|
26
|
+
filePath?: string;
|
|
27
|
+
};
|
|
28
|
+
export type TaskEventListener = (event: TaskEvent) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Storage backend for Ordna tasks.
|
|
31
|
+
*
|
|
32
|
+
* The built-in {@link FileTaskProvider} reads / writes markdown files in `tasks/`.
|
|
33
|
+
* Plugins (e.g. `@frehilm/ordna-jira`, `@frehilm/ordna-linear`) implement this same
|
|
34
|
+
* interface against a remote tracker, so the TUI / Web / library work unchanged
|
|
35
|
+
* against any backend selected via `provider:` in `.ordna/config.yaml`.
|
|
36
|
+
*
|
|
37
|
+
* Plugins are expected to expose a factory:
|
|
38
|
+
*
|
|
39
|
+
* export function createProvider(config, cwd): TaskProvider
|
|
40
|
+
*
|
|
41
|
+
* which is called by the dynamic loader in core (T-022).
|
|
42
|
+
*/
|
|
43
|
+
export interface TaskProvider {
|
|
44
|
+
/**
|
|
45
|
+
* Identifier used for diagnostics and to populate `Task.remote.provider` on
|
|
46
|
+
* non-file backends. Common values: `"file"`, `"jira"`, `"linear"`.
|
|
47
|
+
*/
|
|
48
|
+
readonly kind: string;
|
|
49
|
+
/** List tasks matching the optional filters. */
|
|
50
|
+
list(opts?: ListOptions): Promise<Task[]>;
|
|
51
|
+
/** Look up a single task by id. Returns `null` when not found. */
|
|
52
|
+
get(id: string): Promise<Task | null>;
|
|
53
|
+
/** Create a new task. */
|
|
54
|
+
create(input: TaskCreateInput): Promise<Task>;
|
|
55
|
+
/** Patch an existing task. Implementations decide which fields are writable. */
|
|
56
|
+
update(id: string, patch: TaskUpdateInput): Promise<Task>;
|
|
57
|
+
/**
|
|
58
|
+
* Change a task's status. Core enforces the `depends_on` gate before calling
|
|
59
|
+
* this; remote providers may also reject the transition based on their own
|
|
60
|
+
* workflow rules — both errors should surface to the user verbatim.
|
|
61
|
+
*/
|
|
62
|
+
move(id: string, status: string): Promise<Task>;
|
|
63
|
+
/** Remove a task. */
|
|
64
|
+
delete(id: string): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Subscribe to changes. The returned function unsubscribes; call it to stop
|
|
67
|
+
* watching and release any underlying resources.
|
|
68
|
+
*/
|
|
69
|
+
watch(cb: TaskEventListener): () => Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Optional. File-backed provider: stage and commit the tasks directory.
|
|
72
|
+
* Remote providers should leave this undefined; core surfaces a clear error
|
|
73
|
+
* to the user when `ordna commit` is invoked against a provider that doesn't
|
|
74
|
+
* support it.
|
|
75
|
+
*/
|
|
76
|
+
commit?(message?: string): Promise<void>;
|
|
77
|
+
/** Optional. Called once after construction; do any one-time setup here. */
|
|
78
|
+
init?(): Promise<void>;
|
|
79
|
+
/** Optional. Called once before disposal; close watchers, release sockets, etc. */
|
|
80
|
+
dispose?(): Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,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,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAE3D;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC5B;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,gDAAgD;IAChD,IAAI,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAE1C,kEAAkE;IAClE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAEtC,yBAAyB;IACzB,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C,gFAAgF;IAChF,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D;;;;OAIG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD,qBAAqB;IACrB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElC;;;OAGG;IACH,KAAK,CAAC,EAAE,EAAE,iBAAiB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD;;;;;OAKG;IACH,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC,4EAA4E;IAC5E,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB,mFAAmF;IACnF,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { OrdnaConfig } from "../config.js";
|
|
2
|
+
import type { ListOptions, TaskEventListener, TaskProvider } from "../provider.js";
|
|
3
|
+
import type { Task, TaskCreateInput, TaskUpdateInput } from "../schema.js";
|
|
4
|
+
/**
|
|
5
|
+
* Built-in file-system backend for Ordna.
|
|
6
|
+
*
|
|
7
|
+
* Reads / writes one markdown file per task in `<cwd>/<config.tasksDir>`.
|
|
8
|
+
* Watches the directory with chokidar. Implements `commit` via the local
|
|
9
|
+
* `git` binary.
|
|
10
|
+
*
|
|
11
|
+
* Constructed by `loadProvider` (providers/load.ts) when
|
|
12
|
+
* `config.provider === "file"`. External providers (`@frehilm/ordna-jira`,
|
|
13
|
+
* `@frehilm/ordna-linear`, etc.) are dynamically imported by the same
|
|
14
|
+
* loader.
|
|
15
|
+
*/
|
|
16
|
+
export declare class FileTaskProvider implements TaskProvider {
|
|
17
|
+
private readonly cwd;
|
|
18
|
+
private readonly config;
|
|
19
|
+
readonly kind = "file";
|
|
20
|
+
private readonly activeWatchers;
|
|
21
|
+
constructor(cwd: string, config: OrdnaConfig);
|
|
22
|
+
private get tasksDir();
|
|
23
|
+
/**
|
|
24
|
+
* Ensure the tasks directory exists. Safe to call multiple times.
|
|
25
|
+
* Not invoked automatically by `createContext` today — `create()` lazily
|
|
26
|
+
* mkdir's the directory on first write. T-023 may wire `init()` into the
|
|
27
|
+
* createContext path once we have a clear signal that providers need
|
|
28
|
+
* eager initialization (Jira / Linear may want to validate auth here).
|
|
29
|
+
*/
|
|
30
|
+
init(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Close any active chokidar watchers held by this provider. Not yet
|
|
33
|
+
* invoked by core (deferred to T-023). Watchers also have their own
|
|
34
|
+
* unsubscribe returned from `watch()`; this is a sweep for the long-lived
|
|
35
|
+
* provider lifecycle.
|
|
36
|
+
*/
|
|
37
|
+
dispose(): Promise<void>;
|
|
38
|
+
list(options?: ListOptions): Promise<Task[]>;
|
|
39
|
+
get(id: string): Promise<Task | null>;
|
|
40
|
+
create(input: TaskCreateInput): Promise<Task>;
|
|
41
|
+
update(id: string, patch: TaskUpdateInput): Promise<Task>;
|
|
42
|
+
move(id: string, status: string): Promise<Task>;
|
|
43
|
+
delete(id: string): Promise<void>;
|
|
44
|
+
watch(listener: TaskEventListener): () => Promise<void>;
|
|
45
|
+
commit(message?: string): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/providers/file.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,KAAK,EACX,WAAW,EAEX,iBAAiB,EACjB,YAAY,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAEX,IAAI,EACJ,eAAe,EACf,eAAe,EACf,MAAM,cAAc,CAAC;AAgEtB;;;;;;;;;;;GAWG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IAMnD,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANxB,QAAQ,CAAC,IAAI,UAAU;IAEvB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;gBAGrC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,WAAW;IAGrC,OAAO,KAAK,QAAQ,GAEnB;IAED;;;;;;OAMG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAiChD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAKrC,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAoC7C,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCzD,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvC,KAAK,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;IAsCjD,MAAM,CAAC,OAAO,SAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;CAc7D"}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
3
|
+
import { readFile, unlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
|
+
import chokidar, {} from "chokidar";
|
|
6
|
+
import { formatId, nextId, parseId } from "../ids.js";
|
|
7
|
+
import { parseTask, parseTaskFile } from "../parser.js";
|
|
8
|
+
import { defaultSectionsFor, serializeTask } from "../writer.js";
|
|
9
|
+
const ARCHIVED_STATUS = "archived";
|
|
10
|
+
function today() {
|
|
11
|
+
return new Date().toISOString().slice(0, 10);
|
|
12
|
+
}
|
|
13
|
+
function slugifyTitle(title) {
|
|
14
|
+
return title
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
17
|
+
.replace(/^-+|-+$/g, "")
|
|
18
|
+
.slice(0, 60);
|
|
19
|
+
}
|
|
20
|
+
function filenameFor(id, title, mode, config) {
|
|
21
|
+
if (mode === "backlog") {
|
|
22
|
+
const numeric = parseId(config, id);
|
|
23
|
+
const numericPart = numeric ?? id;
|
|
24
|
+
const slug = slugifyTitle(title) || "task";
|
|
25
|
+
return `task-${numericPart} - ${slug}.md`;
|
|
26
|
+
}
|
|
27
|
+
return `${id}.md`;
|
|
28
|
+
}
|
|
29
|
+
function isKnownStatus(config, status) {
|
|
30
|
+
if (status === ARCHIVED_STATUS)
|
|
31
|
+
return true;
|
|
32
|
+
return config.statuses.includes(status);
|
|
33
|
+
}
|
|
34
|
+
function runGit(cwd, args) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const proc = spawn("git", args, { cwd });
|
|
37
|
+
let stdout = "";
|
|
38
|
+
let stderr = "";
|
|
39
|
+
proc.stdout.on("data", (chunk) => {
|
|
40
|
+
stdout += chunk.toString();
|
|
41
|
+
});
|
|
42
|
+
proc.stderr.on("data", (chunk) => {
|
|
43
|
+
stderr += chunk.toString();
|
|
44
|
+
});
|
|
45
|
+
proc.on("error", reject);
|
|
46
|
+
proc.on("close", (code) => {
|
|
47
|
+
if (code === 0)
|
|
48
|
+
resolve({ stdout, stderr });
|
|
49
|
+
else
|
|
50
|
+
reject(new Error(`git ${args.join(" ")} failed (${code}): ${stderr.trim()}`));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Built-in file-system backend for Ordna.
|
|
56
|
+
*
|
|
57
|
+
* Reads / writes one markdown file per task in `<cwd>/<config.tasksDir>`.
|
|
58
|
+
* Watches the directory with chokidar. Implements `commit` via the local
|
|
59
|
+
* `git` binary.
|
|
60
|
+
*
|
|
61
|
+
* Constructed by `loadProvider` (providers/load.ts) when
|
|
62
|
+
* `config.provider === "file"`. External providers (`@frehilm/ordna-jira`,
|
|
63
|
+
* `@frehilm/ordna-linear`, etc.) are dynamically imported by the same
|
|
64
|
+
* loader.
|
|
65
|
+
*/
|
|
66
|
+
export class FileTaskProvider {
|
|
67
|
+
cwd;
|
|
68
|
+
config;
|
|
69
|
+
kind = "file";
|
|
70
|
+
activeWatchers = new Set();
|
|
71
|
+
constructor(cwd, config) {
|
|
72
|
+
this.cwd = cwd;
|
|
73
|
+
this.config = config;
|
|
74
|
+
}
|
|
75
|
+
get tasksDir() {
|
|
76
|
+
return join(this.cwd, this.config.tasksDir);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Ensure the tasks directory exists. Safe to call multiple times.
|
|
80
|
+
* Not invoked automatically by `createContext` today — `create()` lazily
|
|
81
|
+
* mkdir's the directory on first write. T-023 may wire `init()` into the
|
|
82
|
+
* createContext path once we have a clear signal that providers need
|
|
83
|
+
* eager initialization (Jira / Linear may want to validate auth here).
|
|
84
|
+
*/
|
|
85
|
+
async init() {
|
|
86
|
+
const dir = this.tasksDir;
|
|
87
|
+
if (!existsSync(dir))
|
|
88
|
+
mkdirSync(dir, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Close any active chokidar watchers held by this provider. Not yet
|
|
92
|
+
* invoked by core (deferred to T-023). Watchers also have their own
|
|
93
|
+
* unsubscribe returned from `watch()`; this is a sweep for the long-lived
|
|
94
|
+
* provider lifecycle.
|
|
95
|
+
*/
|
|
96
|
+
async dispose() {
|
|
97
|
+
const closing = [];
|
|
98
|
+
for (const w of this.activeWatchers)
|
|
99
|
+
closing.push(w.close());
|
|
100
|
+
this.activeWatchers.clear();
|
|
101
|
+
await Promise.all(closing);
|
|
102
|
+
}
|
|
103
|
+
async list(options = {}) {
|
|
104
|
+
const dir = this.tasksDir;
|
|
105
|
+
if (!existsSync(dir))
|
|
106
|
+
return [];
|
|
107
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
108
|
+
const tasks = [];
|
|
109
|
+
for (const entry of entries) {
|
|
110
|
+
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
111
|
+
continue;
|
|
112
|
+
const filePath = join(dir, entry.name);
|
|
113
|
+
const raw = await readFile(filePath, "utf8");
|
|
114
|
+
try {
|
|
115
|
+
tasks.push(parseTask(raw, filePath));
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Skip malformed tasks silently; surfaced via dedicated validator later.
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
let filtered = tasks;
|
|
122
|
+
if (options.status)
|
|
123
|
+
filtered = filtered.filter((t) => t.status === options.status);
|
|
124
|
+
if (options.assignee)
|
|
125
|
+
filtered = filtered.filter((t) => t.assignee === options.assignee);
|
|
126
|
+
if (options.tag)
|
|
127
|
+
filtered = filtered.filter((t) => t.tags.includes(options.tag));
|
|
128
|
+
filtered.sort((a, b) => a.id.localeCompare(b.id, undefined, { numeric: true }));
|
|
129
|
+
return filtered;
|
|
130
|
+
}
|
|
131
|
+
async get(id) {
|
|
132
|
+
const tasks = await this.list();
|
|
133
|
+
return tasks.find((t) => t.id === id) ?? null;
|
|
134
|
+
}
|
|
135
|
+
async create(input) {
|
|
136
|
+
const dir = this.tasksDir;
|
|
137
|
+
if (!existsSync(dir))
|
|
138
|
+
mkdirSync(dir, { recursive: true });
|
|
139
|
+
const id = nextId(this.config, dir);
|
|
140
|
+
const status = input.status ?? this.config.statuses[0];
|
|
141
|
+
if (!status)
|
|
142
|
+
throw new Error("Config has no statuses defined.");
|
|
143
|
+
if (!isKnownStatus(this.config, status)) {
|
|
144
|
+
throw new Error(`Status "${status}" is not in configured statuses.`);
|
|
145
|
+
}
|
|
146
|
+
const now = today();
|
|
147
|
+
const task = {
|
|
148
|
+
id,
|
|
149
|
+
title: input.title,
|
|
150
|
+
status,
|
|
151
|
+
assignee: input.assignee ?? null,
|
|
152
|
+
priority: input.priority ?? null,
|
|
153
|
+
tags: input.tags ?? [],
|
|
154
|
+
depends_on: input.depends_on ?? [],
|
|
155
|
+
created_at: now,
|
|
156
|
+
updated_at: now,
|
|
157
|
+
sections: defaultSectionsFor(this.config.schema),
|
|
158
|
+
extra_frontmatter: {},
|
|
159
|
+
filePath: "",
|
|
160
|
+
rawContent: "",
|
|
161
|
+
};
|
|
162
|
+
const filename = filenameFor(id, task.title, this.config.schema, this.config);
|
|
163
|
+
task.filePath = join(dir, filename);
|
|
164
|
+
const serialized = serializeTask(task, this.config.schema);
|
|
165
|
+
task.rawContent = serialized;
|
|
166
|
+
await writeFile(task.filePath, serialized, "utf8");
|
|
167
|
+
return task;
|
|
168
|
+
}
|
|
169
|
+
async update(id, patch) {
|
|
170
|
+
const existing = await this.get(id);
|
|
171
|
+
if (!existing)
|
|
172
|
+
throw new Error(`Task ${id} not found.`);
|
|
173
|
+
const next = {
|
|
174
|
+
...existing,
|
|
175
|
+
title: patch.title ?? existing.title,
|
|
176
|
+
status: patch.status ?? existing.status,
|
|
177
|
+
assignee: patch.assignee !== undefined ? patch.assignee : existing.assignee,
|
|
178
|
+
priority: patch.priority !== undefined ? patch.priority : existing.priority,
|
|
179
|
+
tags: patch.tags ?? existing.tags,
|
|
180
|
+
depends_on: patch.depends_on ?? existing.depends_on,
|
|
181
|
+
sections: patch.sections ?? existing.sections,
|
|
182
|
+
updated_at: today(),
|
|
183
|
+
};
|
|
184
|
+
if (next.status !== existing.status && !isKnownStatus(this.config, next.status)) {
|
|
185
|
+
throw new Error(`Status "${next.status}" is not in configured statuses.`);
|
|
186
|
+
}
|
|
187
|
+
const serialized = serializeTask(next, this.config.schema);
|
|
188
|
+
next.rawContent = serialized;
|
|
189
|
+
// Invariant: file-backed tasks always carry filePath. The narrowing here
|
|
190
|
+
// is for TS; at runtime this can't happen through the FileTaskProvider.
|
|
191
|
+
if (!existing.filePath) {
|
|
192
|
+
throw new Error(`Task ${id} has no filePath; FileTaskProvider invariant violated.`);
|
|
193
|
+
}
|
|
194
|
+
await writeFile(existing.filePath, serialized, "utf8");
|
|
195
|
+
return next;
|
|
196
|
+
}
|
|
197
|
+
async move(id, status) {
|
|
198
|
+
// The depends_on gate lives in core's `moveTask` wrapper; by the time
|
|
199
|
+
// we reach here the gate has already accepted the transition.
|
|
200
|
+
return this.update(id, { status });
|
|
201
|
+
}
|
|
202
|
+
async delete(id) {
|
|
203
|
+
const task = await this.get(id);
|
|
204
|
+
if (!task)
|
|
205
|
+
throw new Error(`Task ${id} not found.`);
|
|
206
|
+
if (!task.filePath) {
|
|
207
|
+
throw new Error(`Task ${id} has no filePath; FileTaskProvider invariant violated.`);
|
|
208
|
+
}
|
|
209
|
+
await unlink(task.filePath);
|
|
210
|
+
}
|
|
211
|
+
watch(listener) {
|
|
212
|
+
const watcher = chokidar.watch(this.tasksDir, {
|
|
213
|
+
ignoreInitial: true,
|
|
214
|
+
depth: 0,
|
|
215
|
+
persistent: true,
|
|
216
|
+
});
|
|
217
|
+
this.activeWatchers.add(watcher);
|
|
218
|
+
const emitIfMarkdown = async (type, filePath) => {
|
|
219
|
+
if (!filePath.endsWith(".md"))
|
|
220
|
+
return;
|
|
221
|
+
try {
|
|
222
|
+
const task = await parseTaskFile(filePath);
|
|
223
|
+
listener({ type, task });
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Ignore partial writes or malformed files.
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
watcher.on("add", (path) => void emitIfMarkdown("added", path));
|
|
230
|
+
watcher.on("change", (path) => void emitIfMarkdown("changed", path));
|
|
231
|
+
watcher.on("unlink", (path) => {
|
|
232
|
+
if (!path.endsWith(".md"))
|
|
233
|
+
return;
|
|
234
|
+
listener({
|
|
235
|
+
type: "removed",
|
|
236
|
+
id: basename(path, ".md"),
|
|
237
|
+
filePath: path,
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
return async () => {
|
|
241
|
+
this.activeWatchers.delete(watcher);
|
|
242
|
+
await watcher.close();
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
async commit(message = "chore(tasks): update") {
|
|
246
|
+
const dir = this.config.tasksDir;
|
|
247
|
+
await runGit(this.cwd, ["add", "--", dir]);
|
|
248
|
+
const status = await runGit(this.cwd, [
|
|
249
|
+
"status",
|
|
250
|
+
"--porcelain",
|
|
251
|
+
"--",
|
|
252
|
+
dir,
|
|
253
|
+
]);
|
|
254
|
+
if (status.stdout.trim().length === 0) {
|
|
255
|
+
throw new Error("No task changes to commit.");
|
|
256
|
+
}
|
|
257
|
+
await runGit(this.cwd, ["commit", "-m", message, "--", dir]);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/providers/file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OrdnaConfig } from "../config.js";
|
|
2
|
+
import type { TaskProvider } from "../provider.js";
|
|
3
|
+
/**
|
|
4
|
+
* Plugin contract: each external provider package
|
|
5
|
+
* (`@frehilm/ordna-<name>`) exports a single `createProvider` factory with
|
|
6
|
+
* this signature. See `tasks/T-022.md` and the plugin docs for details.
|
|
7
|
+
*/
|
|
8
|
+
export type CreateProvider = (config: OrdnaConfig, cwd: string) => TaskProvider | Promise<TaskProvider>;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a `TaskProvider` from `config.provider`.
|
|
11
|
+
*
|
|
12
|
+
* - `"file"` returns a `FileTaskProvider` built locally — zero overhead, no
|
|
13
|
+
* dynamic import.
|
|
14
|
+
* - Any other value is loaded from `@frehilm/ordna-<value>` via dynamic
|
|
15
|
+
* `import()`. The package must export a `createProvider(config, cwd)`
|
|
16
|
+
* factory matching {@link CreateProvider}.
|
|
17
|
+
*
|
|
18
|
+
* Errors are deliberately actionable: a missing package surfaces the install
|
|
19
|
+
* command; a malformed package surfaces the contract.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadProvider(config: OrdnaConfig, cwd: string): Promise<TaskProvider>;
|
|
22
|
+
//# sourceMappingURL=load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/providers/load.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAC5B,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,MAAM,KACP,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAa1C;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CACjC,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,YAAY,CAAC,CA6BvB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { FileTaskProvider } from "./file.js";
|
|
2
|
+
const PLUGIN_PACKAGE_PREFIX = "@frehilm/ordna-";
|
|
3
|
+
function isModuleNotFoundError(err, packageName) {
|
|
4
|
+
if (!(err instanceof Error))
|
|
5
|
+
return false;
|
|
6
|
+
const code = err.code;
|
|
7
|
+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
|
|
8
|
+
return err.message.includes(packageName);
|
|
9
|
+
}
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a `TaskProvider` from `config.provider`.
|
|
14
|
+
*
|
|
15
|
+
* - `"file"` returns a `FileTaskProvider` built locally — zero overhead, no
|
|
16
|
+
* dynamic import.
|
|
17
|
+
* - Any other value is loaded from `@frehilm/ordna-<value>` via dynamic
|
|
18
|
+
* `import()`. The package must export a `createProvider(config, cwd)`
|
|
19
|
+
* factory matching {@link CreateProvider}.
|
|
20
|
+
*
|
|
21
|
+
* Errors are deliberately actionable: a missing package surfaces the install
|
|
22
|
+
* command; a malformed package surfaces the contract.
|
|
23
|
+
*/
|
|
24
|
+
export async function loadProvider(config, cwd) {
|
|
25
|
+
const name = config.provider;
|
|
26
|
+
if (name === "file") {
|
|
27
|
+
return new FileTaskProvider(cwd, config);
|
|
28
|
+
}
|
|
29
|
+
const packageName = `${PLUGIN_PACKAGE_PREFIX}${name}`;
|
|
30
|
+
let mod;
|
|
31
|
+
try {
|
|
32
|
+
mod = await import(packageName);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (isModuleNotFoundError(err, packageName)) {
|
|
36
|
+
throw new Error(`Provider "${name}" not installed. Run: pnpm add ${packageName}`);
|
|
37
|
+
}
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
const factory = mod.createProvider;
|
|
41
|
+
if (typeof factory !== "function") {
|
|
42
|
+
throw new Error(`Provider package "${packageName}" does not export a \`createProvider(config, cwd)\` factory. Every Ordna plugin must export this function — see https://github.com/FreHilm/ordna for the plugin contract.`);
|
|
43
|
+
}
|
|
44
|
+
const provider = await factory(config, cwd);
|
|
45
|
+
return provider;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/providers/load.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAY7C,MAAM,qBAAqB,GAAG,iBAAiB,CAAC;AAEhD,SAAS,qBAAqB,CAAC,GAAY,EAAE,WAAmB;IAC/D,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;IACjD,IAAI,IAAI,KAAK,sBAAsB,IAAI,IAAI,KAAK,kBAAkB,EAAE,CAAC;QACpE,OAAO,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,MAAmB,EACnB,GAAW;IAEX,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC;IAE7B,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACrB,OAAO,IAAI,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,qBAAqB,GAAG,IAAI,EAAE,CAAC;IACtD,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACJ,GAAG,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,qBAAqB,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CACd,aAAa,IAAI,kCAAkC,WAAW,EAAE,CAChE,CAAC;QACH,CAAC;QACD,MAAM,GAAG,CAAC;IACX,CAAC;IAED,MAAM,OAAO,GAAI,GAAoC,CAAC,cAAc,CAAC;IACrE,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACd,qBAAqB,WAAW,2KAA2K,CAC3M,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAO,OAA0B,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC;AACjB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frehilm/ordna-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Git-native task management — pure data layer. Tasks are markdown files; the board is the directory.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "FreHilm <1584617+FreHilm@users.noreply.github.com>",
|
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
},
|
|
12
12
|
"homepage": "https://github.com/FreHilm/ordna#readme",
|
|
13
13
|
"bugs": "https://github.com/FreHilm/ordna/issues",
|
|
14
|
-
"keywords": [
|
|
14
|
+
"keywords": [
|
|
15
|
+
"kanban",
|
|
16
|
+
"tasks",
|
|
17
|
+
"markdown",
|
|
18
|
+
"git",
|
|
19
|
+
"project-management"
|
|
20
|
+
],
|
|
15
21
|
"type": "module",
|
|
16
22
|
"main": "./dist/index.js",
|
|
17
23
|
"types": "./dist/index.d.ts",
|
|
@@ -21,13 +27,9 @@
|
|
|
21
27
|
"import": "./dist/index.js"
|
|
22
28
|
}
|
|
23
29
|
},
|
|
24
|
-
"files": [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
28
|
-
"test": "vitest run",
|
|
29
|
-
"test:watch": "vitest"
|
|
30
|
-
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
31
33
|
"dependencies": {
|
|
32
34
|
"chokidar": "^4.0.3",
|
|
33
35
|
"gray-matter": "^4.0.3",
|
|
@@ -38,5 +40,11 @@
|
|
|
38
40
|
"@types/node": "^22.10.5",
|
|
39
41
|
"typescript": "^5.7.2",
|
|
40
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"
|
|
41
49
|
}
|
|
42
|
-
}
|
|
50
|
+
}
|