@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,53 @@
1
+ import { makeTmux } from "../team/tmux.js";
2
+ /**
3
+ * Regex matching a Copilot session name produced by the launch scheme
4
+ * (`omp-${Date.now()}`). Naturally excludes `omp-team-*` and any other
5
+ * non-digit suffixes.
6
+ */
7
+ export const COPILOT_SESSION_RE = /^omp-\d+$/;
8
+ /**
9
+ * Resolve the target Copilot tmux session via three-tier precedence:
10
+ * 1. Explicit --session flag
11
+ * 2. COPILOT_TMUX_SESSION env var
12
+ * 3. Auto-discovery: scan tmux for exactly one session matching COPILOT_SESSION_RE
13
+ *
14
+ * Returns a structured result — never throws.
15
+ */
16
+ export function resolveSession(opts = {}) {
17
+ const { flag, env } = opts;
18
+ const tmux = opts.tmux ?? makeTmux();
19
+ if (flag && flag.length > 0) {
20
+ return { ok: true, session: flag, source: "flag" };
21
+ }
22
+ if (env && env.length > 0) {
23
+ return { ok: true, session: env, source: "env" };
24
+ }
25
+ let sessions;
26
+ try {
27
+ sessions = tmux.listSessions();
28
+ }
29
+ catch (err) {
30
+ // Honor the no-throw contract: surface tmux failures as a structured result.
31
+ return {
32
+ ok: false,
33
+ error: `failed to list tmux sessions (${err instanceof Error ? err.message : String(err)}) — pass --session <name>`,
34
+ };
35
+ }
36
+ const candidates = sessions.filter((name) => COPILOT_SESSION_RE.test(name));
37
+ if (candidates.length === 1) {
38
+ return { ok: true, session: candidates[0], source: "discovery" };
39
+ }
40
+ if (candidates.length === 0) {
41
+ return {
42
+ ok: false,
43
+ error: "no running copilot session found — launch with `omp`, or pass --session <name>",
44
+ };
45
+ }
46
+ // More than one match — refuse to pick silently.
47
+ return {
48
+ ok: false,
49
+ error: `multiple copilot sessions found — pass --session <name> to specify one:\n ${candidates.join("\n ")}`,
50
+ candidates,
51
+ };
52
+ }
53
+ //# sourceMappingURL=resolve-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-session.js","sourceRoot":"","sources":["../../../src/comms/resolve-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgB,MAAM,iBAAiB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAyB9C;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,OAA2B,EAAE;IAC1D,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,EAAE,CAAC;IAErC,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACrD,CAAC;IAED,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACnD,CAAC;IAED,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,6EAA6E;QAC7E,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,iCAAiC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,2BAA2B;SACpH,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAE5E,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IACpE,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,gFAAgF;SACxF,CAAC;IACJ,CAAC;IAED,iDAAiD;IACjD,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,8EAA8E,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QAC9G,UAAU;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /** Set/replace today's goal. Returns the date and stored goal. */
2
+ export declare function setDailyGoal(cwd: string, goal: string): {
3
+ date: string;
4
+ goal: string;
5
+ };
6
+ /** Append a timestamped entry (`- HH:MM — <text>`) to today's log. */
7
+ export declare function addLogEntry(cwd: string, text: string): {
8
+ date: string;
9
+ count: number;
10
+ };
11
+ /**
12
+ * Delete day-files older than `keepDays` days; returns the removed dates.
13
+ * Daily files already age out of context (only a breadcrumb is injected and
14
+ * reads are recency-bounded), so this is disk housekeeping, not a context fix.
15
+ */
16
+ export declare function pruneDailyLog(cwd: string, keepDays: number): string[];
17
+ /** Read today + the previous `days` days, newest-first, capped to ~4KB. */
18
+ export declare function readDailyLog(cwd: string, days: number): string;
@@ -0,0 +1,138 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { ompRoot } from "./omp-root.js";
4
+ const DAY_FILE_RE = /^\d{4}-\d{2}-\d{2}\.md$/;
5
+ const READ_CHAR_BUDGET = 4000;
6
+ function pad(n) {
7
+ return String(n).padStart(2, "0");
8
+ }
9
+ function todayStr(d = new Date()) {
10
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
11
+ }
12
+ function timeStr(d = new Date()) {
13
+ return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
14
+ }
15
+ function dailyDir(cwd) {
16
+ return join(ompRoot(cwd), ".omp", "memory", "daily");
17
+ }
18
+ function dayFile(cwd, date = todayStr()) {
19
+ return join(dailyDir(cwd), `${date}.md`);
20
+ }
21
+ function parseDay(text) {
22
+ let section = null;
23
+ const goalLines = [];
24
+ const log = [];
25
+ for (const line of text.split("\n")) {
26
+ if (/^#\s+/.test(line))
27
+ continue;
28
+ if (/^##\s+Goal\s*$/i.test(line)) {
29
+ section = "goal";
30
+ continue;
31
+ }
32
+ if (/^##\s+Log\s*$/i.test(line)) {
33
+ section = "log";
34
+ continue;
35
+ }
36
+ if (section === "goal")
37
+ goalLines.push(line);
38
+ // Preserve any non-empty line a user may have hand-written, verbatim (bullets,
39
+ // prose, indented sub-notes). Only blank spacer lines are dropped on round-trip.
40
+ else if (section === "log" && line.trim() !== "")
41
+ log.push(line);
42
+ }
43
+ return { goal: goalLines.join("\n").trim(), log };
44
+ }
45
+ function serializeDay(date, doc) {
46
+ const parts = [`# ${date}`, "", "## Goal", doc.goal.trim(), "", "## Log", ...doc.log];
47
+ return `${parts.join("\n").replace(/\n+$/, "")}\n`;
48
+ }
49
+ function readDay(cwd, date = todayStr()) {
50
+ const p = dayFile(cwd, date);
51
+ if (!existsSync(p))
52
+ return { goal: "", log: [] };
53
+ try {
54
+ return parseDay(readFileSync(p, "utf8"));
55
+ }
56
+ catch {
57
+ return { goal: "", log: [] };
58
+ }
59
+ }
60
+ function writeDay(cwd, doc, date = todayStr()) {
61
+ const p = dayFile(cwd, date);
62
+ mkdirSync(dirname(p), { recursive: true });
63
+ const tmp = `${p}.tmp.${process.pid}.${Date.now()}`;
64
+ writeFileSync(tmp, serializeDay(date, doc), "utf8");
65
+ renameSync(tmp, p);
66
+ }
67
+ /** Set/replace today's goal. Returns the date and stored goal. */
68
+ export function setDailyGoal(cwd, goal) {
69
+ const doc = readDay(cwd);
70
+ doc.goal = String(goal ?? "").trim();
71
+ writeDay(cwd, doc);
72
+ return { date: todayStr(), goal: doc.goal };
73
+ }
74
+ /** Append a timestamped entry (`- HH:MM — <text>`) to today's log. */
75
+ export function addLogEntry(cwd, text) {
76
+ // Collapse to a single line so an entry can never contain a `## Goal`/`## Log`
77
+ // marker that parseDay would later misread as a section boundary.
78
+ const clean = String(text ?? "")
79
+ .replace(/\s*\n\s*/g, " ")
80
+ .trim();
81
+ const doc = readDay(cwd);
82
+ doc.log.push(`- ${timeStr()} — ${clean}`);
83
+ writeDay(cwd, doc);
84
+ return { date: todayStr(), count: doc.log.length };
85
+ }
86
+ /**
87
+ * Delete day-files older than `keepDays` days; returns the removed dates.
88
+ * Daily files already age out of context (only a breadcrumb is injected and
89
+ * reads are recency-bounded), so this is disk housekeeping, not a context fix.
90
+ */
91
+ export function pruneDailyLog(cwd, keepDays) {
92
+ const dir = dailyDir(cwd);
93
+ if (!existsSync(dir))
94
+ return [];
95
+ const cutoff = todayStr(new Date(Date.now() - Math.max(0, keepDays) * 86400000));
96
+ const removed = [];
97
+ for (const f of readdirSync(dir)) {
98
+ if (!DAY_FILE_RE.test(f))
99
+ continue;
100
+ const date = f.slice(0, 10);
101
+ if (date < cutoff) {
102
+ try {
103
+ unlinkSync(join(dir, f));
104
+ removed.push(date);
105
+ }
106
+ catch {
107
+ // skip unremovable file
108
+ }
109
+ }
110
+ }
111
+ return removed.sort();
112
+ }
113
+ /** Read today + the previous `days` days, newest-first, capped to ~4KB. */
114
+ export function readDailyLog(cwd, days) {
115
+ const dir = dailyDir(cwd);
116
+ if (!existsSync(dir))
117
+ return "";
118
+ const files = readdirSync(dir)
119
+ .filter((f) => DAY_FILE_RE.test(f))
120
+ .sort()
121
+ .reverse()
122
+ .slice(0, Math.max(0, days) + 1);
123
+ let out = "";
124
+ for (const f of files) {
125
+ try {
126
+ out += `${readFileSync(join(dir, f), "utf8").trim()}\n\n`;
127
+ }
128
+ catch {
129
+ // skip unreadable day file
130
+ }
131
+ if (out.length > READ_CHAR_BUDGET) {
132
+ out = `${out.slice(0, READ_CHAR_BUDGET)}\n…(truncated)`;
133
+ break;
134
+ }
135
+ }
136
+ return out.trim();
137
+ }
138
+ //# sourceMappingURL=daily-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daily-log.js","sourceRoot":"","sources":["../../src/daily-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** The repo objective, or "" when unset. */
2
+ export declare function readRepoGoal(cwd: string): string;
3
+ /** Set/replace the repo objective (collapsed to one north-star line). */
4
+ export declare function writeRepoGoal(cwd: string, goal: string): string;
@@ -0,0 +1,44 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { ompRoot } from "./omp-root.js";
4
+ // The repo's durable objective ("what we want to achieve in this repo"), stored
5
+ // once per project at .omp/goal.md — distinct from a daily log's per-day goal.
6
+ // Exposed through the `omp goal` CLI subcommands (NOT MCP), so the project dir
7
+ // is the CLI's cwd and never ambiguous.
8
+ function goalFile(cwd) {
9
+ return join(ompRoot(cwd), ".omp", "goal.md");
10
+ }
11
+ // Strip ONLY our own serialized `# Repo Goal` header (not any heading), so a
12
+ // hand-authored objective — even one that starts with `#` — is never lost.
13
+ function parseGoal(text) {
14
+ const noBom = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
15
+ const lines = noBom.split("\n");
16
+ if (/^#\s+Repo Goal\s*$/i.test(lines[0] ?? ""))
17
+ lines.shift();
18
+ return lines.join("\n").trim();
19
+ }
20
+ /** The repo objective, or "" when unset. */
21
+ export function readRepoGoal(cwd) {
22
+ const p = goalFile(cwd);
23
+ if (!existsSync(p))
24
+ return "";
25
+ try {
26
+ return parseGoal(readFileSync(p, "utf8"));
27
+ }
28
+ catch {
29
+ return "";
30
+ }
31
+ }
32
+ /** Set/replace the repo objective (collapsed to one north-star line). */
33
+ export function writeRepoGoal(cwd, goal) {
34
+ const clean = String(goal ?? "")
35
+ .replace(/\s*\n\s*/g, " ")
36
+ .trim();
37
+ const p = goalFile(cwd);
38
+ mkdirSync(dirname(p), { recursive: true });
39
+ const tmp = `${p}.tmp.${process.pid}.${Date.now()}`;
40
+ writeFileSync(tmp, `# Repo Goal\n\n${clean}\n`, "utf8");
41
+ renameSync(tmp, p);
42
+ return clean;
43
+ }
44
+ //# sourceMappingURL=goal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal.js","sourceRoot":"","sources":["../../src/goal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,gFAAgF;AAChF,+EAA+E;AAC/E,+EAA+E;AAC/E,wCAAwC;AACxC,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC;AAED,6EAA6E;AAC7E,2EAA2E;AAC3E,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAC9D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9B,IAAI,CAAC;QACH,OAAO,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,IAAY;IACrD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;SAC7B,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC;SACzB,IAAI,EAAE,CAAC;IACV,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IACxB,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IACpD,aAAa,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;IACxD,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACnB,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Write/refresh the managed memory block in .github/copilot-instructions.md so
3
+ * Copilot surfaces it every session. Creates the file if absent. Returns the
4
+ * path and whether a block was written. Best-effort; never throws.
5
+ */
6
+ export declare function syncInstructionsMemory(cwd: string): {
7
+ path: string;
8
+ wrote: boolean;
9
+ };
@@ -0,0 +1,72 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { ompRoot } from "./omp-root.js";
4
+ import { readRepoGoal } from "./goal.js";
5
+ import { readDirectives, noteIndex } from "./project-memory.js";
6
+ // Copilot CLI does not execute plugin lifecycle hooks (verified: neither
7
+ // SessionStart nor UserPromptSubmit fire), so we can't inject memory via hooks.
8
+ // Copilot DOES read .github/copilot-instructions.md, so we render the always-on
9
+ // memory (repo goal + directives) into a managed block there. The block is
10
+ // refreshed whenever goal/directives/notes change, so it appears every session.
11
+ const START = "<!-- omp:memory:start -->";
12
+ const END = "<!-- omp:memory:end -->";
13
+ const MAX_DIRECTIVES = 20;
14
+ function instructionsPath(cwd) {
15
+ return join(ompRoot(cwd), ".github", "copilot-instructions.md");
16
+ }
17
+ function renderBlock(cwd) {
18
+ const goal = readRepoGoal(cwd);
19
+ const directives = readDirectives(cwd);
20
+ const notes = noteIndex(cwd);
21
+ const lines = [START, "## Active memory (managed by omp — do not edit between these markers)"];
22
+ if (goal)
23
+ lines.push("", `**Repo goal:** ${goal}`);
24
+ if (directives.length > 0) {
25
+ lines.push("", "**Directives — must follow this session:**");
26
+ for (const d of directives.slice(0, MAX_DIRECTIVES))
27
+ lines.push(`- ${d}`);
28
+ if (directives.length > MAX_DIRECTIVES)
29
+ lines.push(`- (+${directives.length - MAX_DIRECTIVES} more — \`omp project-memory read\`)`);
30
+ }
31
+ lines.push("", `**On demand:** \`omp project-memory index\` (${notes.length} note${notes.length === 1 ? "" : "s"}) · \`omp project-memory read <id>\` · \`omp daily-log read\``, END);
32
+ return lines.join("\n");
33
+ }
34
+ /**
35
+ * Write/refresh the managed memory block in .github/copilot-instructions.md so
36
+ * Copilot surfaces it every session. Creates the file if absent. Returns the
37
+ * path and whether a block was written. Best-effort; never throws.
38
+ */
39
+ export function syncInstructionsMemory(cwd) {
40
+ const p = instructionsPath(cwd);
41
+ const block = renderBlock(cwd);
42
+ try {
43
+ const content = existsSync(p) ? readFileSync(p, "utf8") : "";
44
+ const starts = content.split(START).length - 1;
45
+ const ends = content.split(END).length - 1;
46
+ let next;
47
+ if (starts === 1 && ends === 1) {
48
+ const s = content.indexOf(START);
49
+ const e = content.indexOf(END);
50
+ if (e <= s)
51
+ return { path: p, wrote: false }; // markers out of order — don't risk a clobber
52
+ next = content.slice(0, s) + block + content.slice(e + END.length);
53
+ }
54
+ else if (starts === 0 && ends === 0) {
55
+ next = content.trim() === "" ? `# oh-my-copilot\n\n${block}\n` : `${content.trimEnd()}\n\n${block}\n`;
56
+ }
57
+ else {
58
+ // Orphan or duplicate markers = corrupt managed region. Fail closed rather
59
+ // than risk replacing user content between mismatched markers.
60
+ return { path: p, wrote: false };
61
+ }
62
+ mkdirSync(dirname(p), { recursive: true });
63
+ const tmp = `${p}.tmp.${process.pid}.${Date.now()}`;
64
+ writeFileSync(tmp, next, "utf8");
65
+ renameSync(tmp, p);
66
+ return { path: p, wrote: true };
67
+ }
68
+ catch {
69
+ return { path: p, wrote: false };
70
+ }
71
+ }
72
+ //# sourceMappingURL=instructions-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions-memory.js","sourceRoot":"","sources":["../../src/instructions-memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { type ToolDefinition } from "../types.js";
2
+ export declare const dailyLogTools: ToolDefinition[];
@@ -0,0 +1,148 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { jsonResult, textResult } from "../types.js";
4
+ const DAY_FILE_RE = /^\d{4}-\d{2}-\d{2}\.md$/;
5
+ const READ_CHAR_BUDGET = 4000;
6
+ function pad(n) {
7
+ return String(n).padStart(2, "0");
8
+ }
9
+ function todayStr(d = new Date()) {
10
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
11
+ }
12
+ function timeStr(d = new Date()) {
13
+ return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
14
+ }
15
+ function dailyDir(cwd) {
16
+ return join(resolve(cwd), ".omp", "memory", "daily");
17
+ }
18
+ function dayFile(cwd, date = todayStr()) {
19
+ return join(dailyDir(cwd), `${date}.md`);
20
+ }
21
+ function parseDay(text) {
22
+ let section = null;
23
+ const goalLines = [];
24
+ const log = [];
25
+ for (const line of text.split("\n")) {
26
+ if (/^#\s+/.test(line))
27
+ continue;
28
+ if (/^##\s+Goal\s*$/i.test(line)) {
29
+ section = "goal";
30
+ continue;
31
+ }
32
+ if (/^##\s+Log\s*$/i.test(line)) {
33
+ section = "log";
34
+ continue;
35
+ }
36
+ if (section === "goal")
37
+ goalLines.push(line);
38
+ // Preserve any non-empty line a user may have hand-written, verbatim (bullets,
39
+ // prose, indented sub-notes). Only blank spacer lines are dropped on round-trip.
40
+ else if (section === "log" && line.trim() !== "")
41
+ log.push(line);
42
+ }
43
+ return { goal: goalLines.join("\n").trim(), log };
44
+ }
45
+ function serializeDay(date, doc) {
46
+ const parts = [`# ${date}`, "", "## Goal", doc.goal.trim(), "", "## Log", ...doc.log];
47
+ return `${parts.join("\n").replace(/\n+$/, "")}\n`;
48
+ }
49
+ function readDay(cwd, date = todayStr()) {
50
+ const p = dayFile(cwd, date);
51
+ if (!existsSync(p))
52
+ return { goal: "", log: [] };
53
+ try {
54
+ return parseDay(readFileSync(p, "utf8"));
55
+ }
56
+ catch {
57
+ return { goal: "", log: [] };
58
+ }
59
+ }
60
+ function writeDay(cwd, doc, date = todayStr()) {
61
+ const p = dayFile(cwd, date);
62
+ mkdirSync(dirname(p), { recursive: true });
63
+ const tmp = `${p}.tmp.${process.pid}.${Date.now()}`;
64
+ writeFileSync(tmp, serializeDay(date, doc), "utf8");
65
+ renameSync(tmp, p);
66
+ }
67
+ function readRecent(cwd, days) {
68
+ const dir = dailyDir(cwd);
69
+ if (!existsSync(dir))
70
+ return "";
71
+ const files = readdirSync(dir)
72
+ .filter((f) => DAY_FILE_RE.test(f))
73
+ .sort()
74
+ .reverse()
75
+ .slice(0, Math.max(0, days) + 1);
76
+ let out = "";
77
+ for (const f of files) {
78
+ try {
79
+ out += `${readFileSync(join(dir, f), "utf8").trim()}\n\n`;
80
+ }
81
+ catch {
82
+ // skip unreadable day file
83
+ }
84
+ if (out.length > READ_CHAR_BUDGET) {
85
+ out = `${out.slice(0, READ_CHAR_BUDGET)}\n…(truncated)`;
86
+ break;
87
+ }
88
+ }
89
+ return out.trim();
90
+ }
91
+ export const dailyLogTools = [
92
+ {
93
+ name: "daily_log_set_goal",
94
+ category: "daily_log",
95
+ description: "Set/replace today's Goal in .omp/memory/daily/<today>.md.",
96
+ inputSchema: {
97
+ type: "object",
98
+ properties: { goal: { type: "string" }, cwd: { type: "string" } },
99
+ required: ["goal"],
100
+ },
101
+ handler: (args) => {
102
+ const cwd = args.cwd ?? process.cwd();
103
+ const doc = readDay(cwd);
104
+ doc.goal = String(args.goal ?? "");
105
+ writeDay(cwd, doc);
106
+ return jsonResult({ ok: true, date: todayStr(), goal: doc.goal });
107
+ },
108
+ },
109
+ {
110
+ name: "daily_log_add",
111
+ category: "daily_log",
112
+ description: "Append a timestamped entry to today's Log in .omp/memory/daily/<today>.md.",
113
+ inputSchema: {
114
+ type: "object",
115
+ properties: { text: { type: "string" }, cwd: { type: "string" } },
116
+ required: ["text"],
117
+ },
118
+ handler: (args) => {
119
+ const cwd = args.cwd ?? process.cwd();
120
+ // Collapse to a single line so an entry can never contain a `## Goal`/`## Log`
121
+ // marker that parseDay would later misread as a section boundary.
122
+ const text = String(args.text ?? "")
123
+ .replace(/\s*\n\s*/g, " ")
124
+ .trim();
125
+ if (!text)
126
+ return jsonResult({ ok: false, error: "text is required" });
127
+ const doc = readDay(cwd);
128
+ doc.log.push(`- ${timeStr()} — ${text}`);
129
+ writeDay(cwd, doc);
130
+ return jsonResult({ ok: true, date: todayStr(), count: doc.log.length });
131
+ },
132
+ },
133
+ {
134
+ name: "daily_log_read",
135
+ category: "daily_log",
136
+ description: "Read the daily log for today plus the previous `days` days (default 1). Char-capped to ~4KB.",
137
+ inputSchema: {
138
+ type: "object",
139
+ properties: { days: { type: "number" }, cwd: { type: "string" } },
140
+ },
141
+ handler: (args) => {
142
+ const cwd = args.cwd ?? process.cwd();
143
+ const days = typeof args.days === "number" ? args.days : 1;
144
+ return textResult(readRecent(cwd, days) || "(no daily log entries)");
145
+ },
146
+ },
147
+ ];
148
+ //# sourceMappingURL=daily-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daily-log.js","sourceRoot":"","sources":["../../../../src/mcp/tools/daily-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function ompRoot(start: string): string;
@@ -0,0 +1,19 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ // Resolve the project root for .omp storage: walk up from `start` to the nearest
4
+ // project marker (.git, then package.json), so memory is scoped to the actual
5
+ // project even when omp is run from a nested directory or a parent workspace.
6
+ // Falls back to `start` when no marker is found. Used by every .omp path so the
7
+ // CLI and hooks agree on one location per project.
8
+ export function ompRoot(start) {
9
+ let dir = resolve(start);
10
+ while (true) {
11
+ if (existsSync(join(dir, ".git")) || existsSync(join(dir, "package.json")))
12
+ return dir;
13
+ const parent = dirname(dir);
14
+ if (parent === dir)
15
+ return resolve(start);
16
+ dir = parent;
17
+ }
18
+ }
19
+ //# sourceMappingURL=omp-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"omp-root.js","sourceRoot":"","sources":["../../src/omp-root.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,iFAAiF;AACjF,8EAA8E;AAC9E,8EAA8E;AAC9E,gFAAgF;AAChF,mDAAmD;AACnD,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QACvF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;QAC1C,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare function readDirectives(cwd: string): string[];
2
+ /** Append a must-follow directive; returns the new directive count. */
3
+ export declare function addDirective(cwd: string, directive: string): number;
4
+ export interface NoteMeta {
5
+ id: string;
6
+ title: string;
7
+ }
8
+ /** Create a note (title + optional body); returns its id (slug, deduped). */
9
+ export declare function addNote(cwd: string, title: string, body?: string): string;
10
+ /** Cheap index of (id, title) — the only thing surfaced; bodies stay on disk. */
11
+ export declare function noteIndex(cwd: string): NoteMeta[];
12
+ /** Full note body by id, or null when missing. */
13
+ export declare function readNote(cwd: string, id: string): string | null;