@damian87/omp 0.4.1 → 0.6.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.
Files changed (90) hide show
  1. package/.github/agents/researcher.md +7 -6
  2. package/.github/copilot-instructions.md +23 -0
  3. package/.github/skills/daily-log/SKILL.md +64 -0
  4. package/.github/skills/goal/SKILL.md +33 -0
  5. package/.github/skills/schedule/SKILL.md +71 -0
  6. package/README.md +19 -2
  7. package/dist/src/cli.js +272 -9
  8. package/dist/src/cli.js.map +1 -1
  9. package/dist/src/comms/index.d.ts +116 -0
  10. package/dist/src/comms/index.js +258 -0
  11. package/dist/src/comms/index.js.map +1 -0
  12. package/dist/src/comms/resolve-session.d.ts +35 -0
  13. package/dist/src/comms/resolve-session.js +53 -0
  14. package/dist/src/comms/resolve-session.js.map +1 -0
  15. package/dist/src/daily-log.d.ts +18 -0
  16. package/dist/src/daily-log.js +138 -0
  17. package/dist/src/daily-log.js.map +1 -0
  18. package/dist/src/goal.d.ts +4 -0
  19. package/dist/src/goal.js +44 -0
  20. package/dist/src/goal.js.map +1 -0
  21. package/dist/src/instructions-memory.d.ts +9 -0
  22. package/dist/src/instructions-memory.js +72 -0
  23. package/dist/src/instructions-memory.js.map +1 -0
  24. package/dist/src/mcp/tools/daily-log.d.ts +2 -0
  25. package/dist/src/mcp/tools/daily-log.js +148 -0
  26. package/dist/src/mcp/tools/daily-log.js.map +1 -0
  27. package/dist/src/omp-root.d.ts +1 -0
  28. package/dist/src/omp-root.js +19 -0
  29. package/dist/src/omp-root.js.map +1 -0
  30. package/dist/src/project-memory.d.ts +13 -0
  31. package/dist/src/project-memory.js +105 -0
  32. package/dist/src/project-memory.js.map +1 -0
  33. package/dist/src/schedule/commands.d.ts +34 -0
  34. package/dist/src/schedule/commands.js +130 -0
  35. package/dist/src/schedule/commands.js.map +1 -0
  36. package/dist/src/schedule/installer.d.ts +20 -0
  37. package/dist/src/schedule/installer.js +76 -0
  38. package/dist/src/schedule/installer.js.map +1 -0
  39. package/dist/src/schedule/installers/crontab.d.ts +17 -0
  40. package/dist/src/schedule/installers/crontab.js +112 -0
  41. package/dist/src/schedule/installers/crontab.js.map +1 -0
  42. package/dist/src/schedule/installers/launchd.d.ts +22 -0
  43. package/dist/src/schedule/installers/launchd.js +125 -0
  44. package/dist/src/schedule/installers/launchd.js.map +1 -0
  45. package/dist/src/schedule/installers/systemd.d.ts +15 -0
  46. package/dist/src/schedule/installers/systemd.js +136 -0
  47. package/dist/src/schedule/installers/systemd.js.map +1 -0
  48. package/dist/src/schedule/job-store.d.ts +21 -0
  49. package/dist/src/schedule/job-store.js +102 -0
  50. package/dist/src/schedule/job-store.js.map +1 -0
  51. package/dist/src/schedule/lock.d.ts +9 -0
  52. package/dist/src/schedule/lock.js +83 -0
  53. package/dist/src/schedule/lock.js.map +1 -0
  54. package/dist/src/schedule/paths.d.ts +15 -0
  55. package/dist/src/schedule/paths.js +36 -0
  56. package/dist/src/schedule/paths.js.map +1 -0
  57. package/dist/src/schedule/runner.d.ts +8 -0
  58. package/dist/src/schedule/runner.js +151 -0
  59. package/dist/src/schedule/runner.js.map +1 -0
  60. package/dist/src/schedule/types.d.ts +60 -0
  61. package/dist/src/schedule/types.js +5 -0
  62. package/dist/src/schedule/types.js.map +1 -0
  63. package/dist/src/state.d.ts +17 -0
  64. package/dist/src/state.js +101 -0
  65. package/dist/src/state.js.map +1 -0
  66. package/dist/src/trace.d.ts +19 -0
  67. package/dist/src/trace.js +74 -0
  68. package/dist/src/trace.js.map +1 -0
  69. package/dist/test/catalog.test.d.ts +1 -0
  70. package/dist/test/catalog.test.js +21 -0
  71. package/dist/test/catalog.test.js.map +1 -0
  72. package/dist/test/jira.test.d.ts +1 -0
  73. package/dist/test/jira.test.js +26 -0
  74. package/dist/test/jira.test.js.map +1 -0
  75. package/dist/test/lint.test.d.ts +1 -0
  76. package/dist/test/lint.test.js +9 -0
  77. package/dist/test/lint.test.js.map +1 -0
  78. package/dist/test/sync.test.d.ts +1 -0
  79. package/dist/test/sync.test.js +15 -0
  80. package/dist/test/sync.test.js.map +1 -0
  81. package/docs/research/2026-06-01-schedule-cron-feature.md +346 -0
  82. package/package.json +1 -1
  83. package/scripts/lib/daily-log.mjs +155 -0
  84. package/scripts/lib/hook-output.mjs +2 -1
  85. package/scripts/lib/omp-root.mjs +15 -0
  86. package/scripts/lib/project-memory.mjs +21 -0
  87. package/scripts/lib/schedule-results.mjs +88 -0
  88. package/scripts/prompt-submit.mjs +14 -2
  89. package/scripts/session-end.mjs +6 -1
  90. package/scripts/session-start.mjs +60 -2
@@ -0,0 +1,151 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync, mkdirSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { resolveCopilotBin } from "../copilot/launch.js";
5
+ import { appendRunResult, readJob, writeJob } from "./job-store.js";
6
+ import { acquireLock, forceReleaseStaleLock, isLockStale } from "./lock.js";
7
+ import { jobFilePath, jobLockPath, resultsFilePath, runLogDir } from "./paths.js";
8
+ import { DEFAULT_TIMEOUT_MS } from "./types.js";
9
+ function isExpired(job) {
10
+ if (job.maxRuns !== undefined && job.runCount >= job.maxRuns)
11
+ return true;
12
+ if (job.expiresAt && Date.now() > Date.parse(job.expiresAt))
13
+ return true;
14
+ return false;
15
+ }
16
+ function timestampSlug() {
17
+ return new Date().toISOString().replace(/[:.]/g, "-");
18
+ }
19
+ /** Keep only the newest `keep` per-run logs in a job's log dir (timestamp slugs sort chronologically). */
20
+ function rotateLogs(logDir, keep = 50) {
21
+ if (!existsSync(logDir))
22
+ return;
23
+ const files = readdirSync(logDir)
24
+ .filter((f) => f.endsWith(".log"))
25
+ .sort();
26
+ for (const f of files.slice(0, Math.max(0, files.length - keep))) {
27
+ try {
28
+ unlinkSync(join(logDir, f));
29
+ }
30
+ catch {
31
+ // best effort
32
+ }
33
+ }
34
+ }
35
+ /** Execute one scheduled run (expiry check, overlap lock, timeout, log capture, result append). */
36
+ export async function runScheduledJob(job, paths, opts = {}) {
37
+ const timeoutMs = job.timeoutMs ?? DEFAULT_TIMEOUT_MS;
38
+ const jobPath = jobFilePath(paths.jobsDir, job.id);
39
+ const resultsPath = resultsFilePath(paths.resultsDir, job.id);
40
+ const persist = (status, result, incrementRun) => {
41
+ appendRunResult(resultsPath, result);
42
+ const current = readJob(jobPath) ?? job;
43
+ writeJob(jobPath, {
44
+ ...current,
45
+ runCount: incrementRun ? current.runCount + 1 : current.runCount,
46
+ lastRunAt: result.ts,
47
+ lastStatus: status,
48
+ lastSummary: result.summary,
49
+ lastLogPath: result.logPath,
50
+ active: status === "expired" ? false : current.active,
51
+ });
52
+ };
53
+ // 1. Expiry / max-runs check BEFORE any spawn.
54
+ if (isExpired(job)) {
55
+ const result = {
56
+ ts: new Date().toISOString(),
57
+ exitCode: 0,
58
+ status: "expired",
59
+ summary: "job expired (TTL or max-runs reached); deactivated",
60
+ logPath: "",
61
+ durationMs: 0,
62
+ };
63
+ persist("expired", result, false);
64
+ opts.onExpire?.(job);
65
+ return result;
66
+ }
67
+ // 2. Overlap lock.
68
+ const lockPath = jobLockPath(paths.jobsDir, job.id);
69
+ forceReleaseStaleLock(lockPath, timeoutMs);
70
+ let lock = acquireLock(lockPath);
71
+ if (!lock.acquired) {
72
+ if (isLockStale(lockPath, timeoutMs)) {
73
+ forceReleaseStaleLock(lockPath, timeoutMs);
74
+ lock = acquireLock(lockPath);
75
+ }
76
+ }
77
+ if (!lock.acquired) {
78
+ const result = {
79
+ ts: new Date().toISOString(),
80
+ exitCode: -1,
81
+ status: "locked",
82
+ summary: "previous run still in progress; skipped",
83
+ logPath: "",
84
+ durationMs: 0,
85
+ };
86
+ appendRunResult(resultsPath, result);
87
+ return result;
88
+ }
89
+ // 3. Spawn the agent and capture output.
90
+ const startedAt = Date.now();
91
+ const logDir = runLogDir(paths.logsDir, job.id);
92
+ mkdirSync(logDir, { recursive: true });
93
+ const logPath = join(logDir, `${timestampSlug()}.log`);
94
+ try {
95
+ const bin = resolveCopilotBin(job.bin);
96
+ const args = [];
97
+ if (job.model)
98
+ args.push("--model", job.model);
99
+ args.push("-p", job.prompt);
100
+ if (job.allowAllTools)
101
+ args.push("--allow-all-tools");
102
+ const result = await new Promise((resolveFn) => {
103
+ const child = spawn(bin, args, { stdio: ["ignore", "pipe", "pipe"], cwd: job.cwd });
104
+ let stdout = "";
105
+ let stderr = "";
106
+ let timedOut = false;
107
+ let settled = false;
108
+ let killTimer;
109
+ const timer = setTimeout(() => {
110
+ timedOut = true;
111
+ child.kill("SIGTERM");
112
+ // Escalate to SIGKILL if the child ignores SIGTERM, so a run can't hang forever.
113
+ killTimer = setTimeout(() => child.kill("SIGKILL"), 5000);
114
+ }, timeoutMs);
115
+ child.stdout?.on("data", (d) => {
116
+ stdout += d.toString();
117
+ });
118
+ child.stderr?.on("data", (d) => {
119
+ stderr += d.toString();
120
+ });
121
+ const finish = (exitCode) => {
122
+ if (settled)
123
+ return;
124
+ settled = true;
125
+ clearTimeout(timer);
126
+ if (killTimer)
127
+ clearTimeout(killTimer);
128
+ writeFileSync(logPath, `$ ${bin} ${args.join(" ")}\n\n[stdout]\n${stdout}\n[stderr]\n${stderr}\n`, "utf8");
129
+ rotateLogs(logDir);
130
+ const status = timedOut ? "timeout" : exitCode === 0 ? "ok" : "error";
131
+ const summarySource = (stdout.trim() || stderr.trim()).replace(/\s+/g, " ");
132
+ resolveFn({
133
+ ts: new Date().toISOString(),
134
+ exitCode,
135
+ status,
136
+ summary: summarySource.slice(0, 200) || `exit ${exitCode}`,
137
+ logPath,
138
+ durationMs: Date.now() - startedAt,
139
+ });
140
+ };
141
+ child.on("error", () => finish(127));
142
+ child.on("close", (code) => finish(typeof code === "number" ? code : timedOut ? 124 : 1));
143
+ });
144
+ persist(result.status, result, true);
145
+ return result;
146
+ }
147
+ finally {
148
+ lock.release();
149
+ }
150
+ }
151
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/schedule/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /** Default per-run timeout for a scheduled agent process (5 minutes). */
2
+ export declare const DEFAULT_TIMEOUT_MS = 300000;
3
+ /** Default time-to-live for a scheduled job (3 days), matching Claude Code's expiry. */
4
+ export declare const DEFAULT_TTL_HOURS = 72;
5
+ /** Which OS scheduler owns the time trigger for a job. */
6
+ export type OsBackend = "launchd" | "systemd" | "crontab";
7
+ /** Terminal status of a single scheduled run. */
8
+ export type ScheduleRunStatus = "ok" | "error" | "timeout" | "locked" | "expired";
9
+ /**
10
+ * Persisted scheduled job (`.omp/state/schedule/jobs/<id>.json`).
11
+ * The portable source of truth; the OS-scheduler entry merely mirrors the trigger.
12
+ */
13
+ export interface ScheduleJob {
14
+ id: string;
15
+ /** 5-field cron expression, interpreted in local time. */
16
+ cron: string;
17
+ prompt: string;
18
+ /** Agent CLI to spawn. Resolved via resolveCopilotBin. */
19
+ bin: string;
20
+ model?: string;
21
+ cwd: string;
22
+ timeoutMs: number;
23
+ /** When true, the agent is spawned with `--allow-all-tools` (unattended full access). */
24
+ allowAllTools: boolean;
25
+ createdAt: string;
26
+ /** ISO timestamp after which the job auto-deactivates. */
27
+ expiresAt?: string;
28
+ maxRuns?: number;
29
+ runCount: number;
30
+ /** OS backend that owns this job's trigger (set at install time). */
31
+ backend: OsBackend;
32
+ ompBinPath: string;
33
+ lastRunAt?: string;
34
+ lastStatus?: ScheduleRunStatus;
35
+ lastSummary?: string;
36
+ lastLogPath?: string;
37
+ active: boolean;
38
+ }
39
+ /** One line in `results/<id>.jsonl`. "Seen" state lives in the cursor, not here. */
40
+ export interface ScheduleRunResult {
41
+ ts: string;
42
+ exitCode: number;
43
+ status: ScheduleRunStatus;
44
+ summary: string;
45
+ logPath: string;
46
+ durationMs: number;
47
+ }
48
+ export interface ScheduleAddOptions {
49
+ id: string;
50
+ cron: string;
51
+ prompt: string;
52
+ bin?: string;
53
+ model?: string;
54
+ cwd?: string;
55
+ timeoutMs?: number;
56
+ maxRuns?: number;
57
+ ttlHours?: number;
58
+ allowAllTools?: boolean;
59
+ dryRun?: boolean;
60
+ }
@@ -0,0 +1,5 @@
1
+ /** Default per-run timeout for a scheduled agent process (5 minutes). */
2
+ export const DEFAULT_TIMEOUT_MS = 300_000;
3
+ /** Default time-to-live for a scheduled job (3 days), matching Claude Code's expiry. */
4
+ export const DEFAULT_TTL_HOURS = 72;
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/schedule/types.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAE1C,wFAAwF;AACxF,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC"}
@@ -0,0 +1,17 @@
1
+ /** Write a value under a key, with an optional TTL in seconds. Returns expiresAt. */
2
+ export declare function stateWrite(cwd: string, key: string, value: unknown, ttlSeconds?: number): string | undefined;
3
+ /** Read a value. Returns { value: null } when missing; auto-deletes if expired. */
4
+ export declare function stateRead(cwd: string, key: string): {
5
+ value: unknown;
6
+ expired?: boolean;
7
+ };
8
+ export declare function stateDelete(cwd: string, key: string): void;
9
+ /** List live (non-expired) keys. */
10
+ export declare function stateList(cwd: string): string[];
11
+ /** Delete all expired entries; returns the count removed. */
12
+ export declare function stateCleanup(cwd: string): number;
13
+ export declare function stateStatus(cwd: string, key: string): {
14
+ exists: boolean;
15
+ mtime?: string;
16
+ bytes?: number;
17
+ };
@@ -0,0 +1,101 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { ompRoot } from "./omp-root.js";
4
+ function kvDir(cwd) {
5
+ return join(ompRoot(cwd), ".omp", "state", "kv");
6
+ }
7
+ function kvPath(cwd, key) {
8
+ if (!/^[\w.-]+$/.test(key))
9
+ throw new Error(`invalid key: ${key}`);
10
+ return join(kvDir(cwd), `${key}.json`);
11
+ }
12
+ /** Write a value under a key, with an optional TTL in seconds. Returns expiresAt. */
13
+ export function stateWrite(cwd, key, value, ttlSeconds) {
14
+ const entry = {
15
+ value,
16
+ writtenAt: new Date().toISOString(),
17
+ expiresAt: ttlSeconds != null ? new Date(Date.now() + ttlSeconds * 1000).toISOString() : undefined,
18
+ };
19
+ const path = kvPath(cwd, key);
20
+ mkdirSync(dirname(path), { recursive: true });
21
+ const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;
22
+ writeFileSync(tmp, JSON.stringify(entry, null, 2), "utf8");
23
+ renameSync(tmp, path);
24
+ return entry.expiresAt;
25
+ }
26
+ /** Read a value. Returns { value: null } when missing; auto-deletes if expired. */
27
+ export function stateRead(cwd, key) {
28
+ const path = kvPath(cwd, key);
29
+ if (!existsSync(path))
30
+ return { value: null };
31
+ try {
32
+ const entry = JSON.parse(readFileSync(path, "utf8"));
33
+ if (entry.expiresAt && Date.parse(entry.expiresAt) < Date.now()) {
34
+ unlinkSync(path);
35
+ return { value: null, expired: true };
36
+ }
37
+ return { value: entry.value };
38
+ }
39
+ catch {
40
+ return { value: null };
41
+ }
42
+ }
43
+ export function stateDelete(cwd, key) {
44
+ const path = kvPath(cwd, key);
45
+ if (existsSync(path))
46
+ unlinkSync(path);
47
+ }
48
+ /** List live (non-expired) keys. */
49
+ export function stateList(cwd) {
50
+ const dir = kvDir(cwd);
51
+ if (!existsSync(dir))
52
+ return [];
53
+ const now = Date.now();
54
+ const keys = [];
55
+ for (const f of readdirSync(dir)) {
56
+ if (!f.endsWith(".json"))
57
+ continue;
58
+ try {
59
+ const entry = JSON.parse(readFileSync(join(dir, f), "utf8"));
60
+ if (entry.expiresAt && Date.parse(entry.expiresAt) < now)
61
+ continue;
62
+ keys.push(f.replace(/\.json$/, ""));
63
+ }
64
+ catch {
65
+ // skip unparseable
66
+ }
67
+ }
68
+ return keys.sort();
69
+ }
70
+ /** Delete all expired entries; returns the count removed. */
71
+ export function stateCleanup(cwd) {
72
+ const dir = kvDir(cwd);
73
+ if (!existsSync(dir))
74
+ return 0;
75
+ const now = Date.now();
76
+ let deleted = 0;
77
+ for (const f of readdirSync(dir)) {
78
+ if (!f.endsWith(".json"))
79
+ continue;
80
+ const path = join(dir, f);
81
+ try {
82
+ const entry = JSON.parse(readFileSync(path, "utf8"));
83
+ if (entry.expiresAt && Date.parse(entry.expiresAt) < now) {
84
+ unlinkSync(path);
85
+ deleted++;
86
+ }
87
+ }
88
+ catch {
89
+ // skip
90
+ }
91
+ }
92
+ return deleted;
93
+ }
94
+ export function stateStatus(cwd, key) {
95
+ const path = kvPath(cwd, key);
96
+ if (!existsSync(path))
97
+ return { exists: false };
98
+ const s = statSync(path);
99
+ return { exists: true, mtime: s.mtime.toISOString(), bytes: s.size };
100
+ }
101
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ interface TraceEntry {
2
+ ts: string;
3
+ sessionId?: string;
4
+ event?: string;
5
+ payload?: unknown;
6
+ }
7
+ export declare function appendTraceEntry(cwd: string, sessionId: string, entry: Omit<TraceEntry, "ts" | "sessionId">): void;
8
+ /** Last `limit` entries of a session (default 50; most recent session if omitted). */
9
+ export declare function traceTimeline(cwd: string, sessionId?: string, limit?: number): {
10
+ sessionId?: string;
11
+ entries: TraceEntry[];
12
+ };
13
+ /** Event-name counts for a session. */
14
+ export declare function traceSummary(cwd: string, sessionId?: string): {
15
+ sessionId?: string;
16
+ total: number;
17
+ counts: Record<string, number>;
18
+ };
19
+ export {};
@@ -0,0 +1,74 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { ompRoot } from "./omp-root.js";
4
+ function traceDir(cwd) {
5
+ return join(ompRoot(cwd), ".omp", "state", "trace");
6
+ }
7
+ function tracePath(cwd, sessionId) {
8
+ if (!/^[\w.-]+$/.test(sessionId))
9
+ throw new Error(`invalid sessionId: ${sessionId}`);
10
+ return join(traceDir(cwd), `${sessionId}.jsonl`);
11
+ }
12
+ export function appendTraceEntry(cwd, sessionId, entry) {
13
+ const path = tracePath(cwd, sessionId);
14
+ mkdirSync(dirname(path), { recursive: true });
15
+ appendFileSync(path, `${JSON.stringify({ ts: new Date().toISOString(), sessionId, ...entry })}\n`, "utf8");
16
+ }
17
+ function readEntries(path) {
18
+ if (!existsSync(path))
19
+ return [];
20
+ return readFileSync(path, "utf8")
21
+ .split("\n")
22
+ .filter((l) => l.trim().length > 0)
23
+ .map((l) => {
24
+ try {
25
+ return JSON.parse(l);
26
+ }
27
+ catch {
28
+ return undefined;
29
+ }
30
+ })
31
+ .filter((e) => Boolean(e));
32
+ }
33
+ function pickSessionId(cwd, sessionId) {
34
+ if (sessionId)
35
+ return sessionId;
36
+ const dir = traceDir(cwd);
37
+ if (!existsSync(dir))
38
+ return undefined;
39
+ const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
40
+ if (files.length === 0)
41
+ return undefined;
42
+ // pick the most recently modified session
43
+ return files
44
+ .map((f) => ({ name: f.replace(/\.jsonl$/, ""), path: join(dir, f) }))
45
+ .sort((a, b) => {
46
+ try {
47
+ return statSync(b.path).mtimeMs - statSync(a.path).mtimeMs;
48
+ }
49
+ catch {
50
+ return 0;
51
+ }
52
+ })[0]?.name;
53
+ }
54
+ /** Last `limit` entries of a session (default 50; most recent session if omitted). */
55
+ export function traceTimeline(cwd, sessionId, limit = 50) {
56
+ const sid = pickSessionId(cwd, sessionId);
57
+ if (!sid)
58
+ return { entries: [] };
59
+ return { sessionId: sid, entries: readEntries(tracePath(cwd, sid)).slice(-Math.max(1, limit)) };
60
+ }
61
+ /** Event-name counts for a session. */
62
+ export function traceSummary(cwd, sessionId) {
63
+ const sid = pickSessionId(cwd, sessionId);
64
+ if (!sid)
65
+ return { total: 0, counts: {} };
66
+ const entries = readEntries(tracePath(cwd, sid));
67
+ const counts = {};
68
+ for (const e of entries) {
69
+ const key = e.event ?? "unknown";
70
+ counts[key] = (counts[key] ?? 0) + 1;
71
+ }
72
+ return { sessionId: sid, total: entries.length, counts };
73
+ }
74
+ //# sourceMappingURL=trace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/trace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { loadCapabilityCatalog, loadSkillCatalog, validateCatalog } from '../src/catalog.js';
4
+ test('catalog contains approved MVP skills and valid projections', () => {
5
+ const catalog = loadSkillCatalog();
6
+ assert.deepEqual(validateCatalog(), []);
7
+ const names = catalog.skills.map((skill) => skill.name);
8
+ assert.deepEqual(names, ['grill', 'verify', 'jira-ticket', 'code-review', 'qa']);
9
+ assert.ok(catalog.projectionCommands.includes('grill-me'));
10
+ assert.ok(catalog.projectionCommands.includes('team'));
11
+ assert.ok(catalog.projectionCommands.includes('ralph'));
12
+ });
13
+ test('team and ralph stay thin handoff capabilities for Copilot', () => {
14
+ const capabilities = loadCapabilityCatalog();
15
+ for (const name of ['team', 'ralph']) {
16
+ const capability = capabilities.capabilities.find((entry) => entry.name === name);
17
+ assert.ok(capability, `${name} capability exists`);
18
+ assert.equal(capability.support.copilot, 'thin-handoff');
19
+ }
20
+ });
21
+ //# sourceMappingURL=catalog.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.test.js","sourceRoot":"","sources":["../../test/catalog.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE7F,IAAI,CAAC,4DAA4D,EAAE,GAAG,EAAE;IACtE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IACnC,MAAM,CAAC,SAAS,CAAC,eAAe,EAAE,EAAE,EAAE,CAAC,CAAC;IAExC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC;IACjF,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACvD,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACrE,MAAM,YAAY,GAAG,qBAAqB,EAAE,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAClF,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,IAAI,oBAAoB,CAAC,CAAC;QACnD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { commentPayload, createIssuePayload, discoverJiraConfig, isJiraConfigured, linkFallbackPayload, safeUpdatePayload, transitionFallbackPayload } from '../src/jira.js';
4
+ test('jira config discovery supports env and configuration detection', () => {
5
+ const config = discoverJiraConfig('/tmp/no-such-root', {
6
+ JIRA_SITE_URL: 'https://example.atlassian.net',
7
+ JIRA_EMAIL: 'agent@example.com',
8
+ JIRA_API_TOKEN: 'secret-token',
9
+ JIRA_PROJECT_KEY: 'OMC'
10
+ });
11
+ assert.equal(config.siteUrl, 'https://example.atlassian.net');
12
+ assert.equal(config.projectKey, 'OMC');
13
+ assert.equal(isJiraConfigured(config), true);
14
+ });
15
+ test('jira adapter renders create, comment, update, and fallback payloads', () => {
16
+ const config = discoverJiraConfig('/tmp/no-such-root', { JIRA_PROJECT_KEY: 'OMC' });
17
+ const create = createIssuePayload(config, { summary: 'Implement slice', description: 'Body' });
18
+ assert.equal(create.operation, 'create');
19
+ assert.equal(create.configured, false);
20
+ assert.match(JSON.stringify(create.body), /Implement slice/);
21
+ assert.equal(commentPayload(config, 'OMC-1', 'Evidence').operation, 'comment');
22
+ assert.equal(safeUpdatePayload(config, 'OMC-1', { summary: 'New' }).method, 'PUT');
23
+ assert.equal(transitionFallbackPayload(config, 'OMC-1', 'Done').configured, false);
24
+ assert.equal(linkFallbackPayload(config, 'OMC-1', 'OMC-2').operation, 'link-fallback');
25
+ });
26
+ //# sourceMappingURL=jira.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira.test.js","sourceRoot":"","sources":["../../test/jira.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAE7K,IAAI,CAAC,gEAAgE,EAAE,GAAG,EAAE;IAC1E,MAAM,MAAM,GAAG,kBAAkB,CAAC,mBAAmB,EAAE;QACrD,aAAa,EAAE,+BAA+B;QAC9C,UAAU,EAAE,mBAAmB;QAC/B,cAAc,EAAE,cAAc;QAC9B,gBAAgB,EAAE,KAAK;KACxB,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,+BAA+B,CAAC,CAAC;IAC9D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qEAAqE,EAAE,GAAG,EAAE;IAC/E,MAAM,MAAM,GAAG,kBAAkB,CAAC,mBAAmB,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAC;IAEpF,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/F,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAE7D,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC/E,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnF,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACnF,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;AACzF,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { lintSkills } from '../src/lint.js';
4
+ import { workspaceRoot } from '../src/project.js';
5
+ test('workspace skills satisfy Phase 1 catalog lint', () => {
6
+ const issues = lintSkills(workspaceRoot('..'));
7
+ assert.deepEqual(issues.filter((issue) => issue.level === 'error'), []);
8
+ });
9
+ //# sourceMappingURL=lint.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.test.js","sourceRoot":"","sources":["../../test/lint.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;AAC1E,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { projectCopilotCommands } from '../src/sync.js';
4
+ test('dry-run projects skill aliases and thin runtime handoffs', () => {
5
+ const files = projectCopilotCommands();
6
+ const paths = files.map((file) => file.path);
7
+ assert.ok(paths.includes('.github/copilot/commands/grill.md'));
8
+ assert.ok(paths.includes('.github/copilot/commands/grill-me.md'));
9
+ assert.ok(paths.includes('.github/copilot/commands/team.md'));
10
+ assert.ok(paths.includes('.github/copilot/commands/ralph.md'));
11
+ const team = files.find((file) => file.path.endsWith('/team.md'));
12
+ assert.ok(team?.content.includes('thin capability handoff'));
13
+ assert.ok(team?.content.includes('not a Copilot-native durable runtime'));
14
+ });
15
+ //# sourceMappingURL=sync.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.test.js","sourceRoot":"","sources":["../../test/sync.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAExD,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACpE,MAAM,KAAK,GAAG,sBAAsB,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE7C,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,mCAAmC,CAAC,CAAC,CAAC;IAC/D,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,sCAAsC,CAAC,CAAC,CAAC;IAClE,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,kCAAkC,CAAC,CAAC,CAAC;IAC9D,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,mCAAmC,CAAC,CAAC,CAAC;IAE/D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IAClE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,CAAC;IAC7D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,sCAAsC,CAAC,CAAC,CAAC;AAC5E,CAAC,CAAC,CAAC"}