trekoon 0.1.8 → 0.2.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/.agents/skills/trekoon/SKILL.md +117 -2
- package/README.md +158 -18
- package/package.json +1 -1
- package/src/commands/arg-parser.ts +164 -0
- package/src/commands/epic.ts +256 -3
- package/src/commands/help.ts +45 -4
- package/src/commands/subtask.ts +209 -3
- package/src/commands/sync.ts +130 -52
- package/src/commands/task.ts +257 -3
- package/src/domain/mutation-service.ts +242 -1
- package/src/domain/tracker-domain.ts +171 -0
- package/src/domain/types.ts +27 -0
- package/src/index.ts +1 -1
- package/src/io/output.ts +98 -5
- package/src/runtime/cli-shell.ts +159 -22
- package/src/runtime/command-types.ts +18 -0
- package/src/storage/path.ts +58 -1
- package/src/sync/event-writes.ts +21 -1
package/src/storage/path.ts
CHANGED
|
@@ -1,22 +1,79 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
1
2
|
import { resolve } from "node:path";
|
|
2
3
|
|
|
3
4
|
const DB_DIRNAME = ".trekoon";
|
|
4
5
|
const DB_FILENAME = "trekoon.db";
|
|
5
6
|
|
|
6
7
|
export interface StoragePaths {
|
|
8
|
+
readonly invocationCwd: string;
|
|
7
9
|
readonly worktreeRoot: string;
|
|
8
10
|
readonly storageDir: string;
|
|
9
11
|
readonly databaseFile: string;
|
|
12
|
+
readonly diagnostics: StoragePathDiagnostics;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface StoragePathIssue {
|
|
16
|
+
readonly code: string;
|
|
17
|
+
readonly message: string;
|
|
18
|
+
readonly invocationCwd: string;
|
|
19
|
+
readonly canonicalRoot: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface StoragePathDiagnostics {
|
|
23
|
+
readonly invocationCwd: string;
|
|
24
|
+
readonly canonicalRoot: string;
|
|
25
|
+
readonly warnings: readonly StoragePathIssue[];
|
|
26
|
+
readonly errors: readonly StoragePathIssue[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resolveGitTopLevel(workingDirectory: string): string | null {
|
|
30
|
+
const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
|
|
31
|
+
cwd: workingDirectory,
|
|
32
|
+
encoding: "utf8",
|
|
33
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (result.status !== 0) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const topLevel: string = result.stdout.trim();
|
|
41
|
+
if (!topLevel) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return resolve(topLevel);
|
|
10
46
|
}
|
|
11
47
|
|
|
12
48
|
export function resolveStoragePaths(workingDirectory: string = process.cwd()): StoragePaths {
|
|
13
|
-
const
|
|
49
|
+
const invocationCwd: string = resolve(workingDirectory);
|
|
50
|
+
const canonicalRoot: string = resolveGitTopLevel(invocationCwd) ?? invocationCwd;
|
|
51
|
+
const worktreeRoot: string = canonicalRoot;
|
|
14
52
|
const storageDir: string = resolve(worktreeRoot, DB_DIRNAME);
|
|
15
53
|
const databaseFile: string = resolve(storageDir, DB_FILENAME);
|
|
54
|
+
const warnings: StoragePathIssue[] = [];
|
|
55
|
+
|
|
56
|
+
if (invocationCwd !== canonicalRoot) {
|
|
57
|
+
warnings.push({
|
|
58
|
+
code: "storage_root_diverged_from_cwd",
|
|
59
|
+
message: "Resolved storage root differs from invocation cwd.",
|
|
60
|
+
invocationCwd,
|
|
61
|
+
canonicalRoot,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const diagnostics: StoragePathDiagnostics = {
|
|
66
|
+
invocationCwd,
|
|
67
|
+
canonicalRoot,
|
|
68
|
+
warnings,
|
|
69
|
+
errors: [],
|
|
70
|
+
};
|
|
16
71
|
|
|
17
72
|
return {
|
|
73
|
+
invocationCwd,
|
|
18
74
|
worktreeRoot,
|
|
19
75
|
storageDir,
|
|
20
76
|
databaseFile,
|
|
77
|
+
diagnostics,
|
|
21
78
|
};
|
|
22
79
|
}
|
package/src/sync/event-writes.ts
CHANGED
|
@@ -11,11 +11,31 @@ interface EventRecordInput {
|
|
|
11
11
|
readonly fields: Record<string, unknown>;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
function nextEventTimestamp(db: Database): number {
|
|
15
|
+
const now: number = Date.now();
|
|
16
|
+
const latestEvent = db
|
|
17
|
+
.query(
|
|
18
|
+
`
|
|
19
|
+
SELECT created_at
|
|
20
|
+
FROM events
|
|
21
|
+
ORDER BY created_at DESC, id DESC
|
|
22
|
+
LIMIT 1;
|
|
23
|
+
`,
|
|
24
|
+
)
|
|
25
|
+
.get() as { created_at: number } | null;
|
|
26
|
+
|
|
27
|
+
if (!latestEvent) {
|
|
28
|
+
return now;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return Math.max(now, latestEvent.created_at + 1);
|
|
32
|
+
}
|
|
33
|
+
|
|
14
34
|
export function appendEventWithGitContext(db: Database, cwd: string, input: EventRecordInput): string {
|
|
15
35
|
const git = resolveGitContext(cwd);
|
|
16
36
|
persistGitContext(db, git);
|
|
17
37
|
|
|
18
|
-
const now: number =
|
|
38
|
+
const now: number = nextEventTimestamp(db);
|
|
19
39
|
const eventId: string = randomUUID();
|
|
20
40
|
|
|
21
41
|
db.query(
|