@damian87/omp 0.4.0 → 0.5.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/.github/agents/researcher.md +7 -6
- package/.github/copilot-instructions.md +23 -0
- package/.github/skills/daily-log/SKILL.md +64 -0
- package/.github/skills/goal/SKILL.md +33 -0
- package/.github/skills/weighted-consensus/SKILL.md +12 -4
- package/README.md +16 -0
- package/dist/src/cli.js +198 -10
- package/dist/src/cli.js.map +1 -1
- package/dist/src/comms/index.d.ts +116 -0
- package/dist/src/comms/index.js +258 -0
- package/dist/src/comms/index.js.map +1 -0
- package/dist/src/comms/resolve-session.d.ts +35 -0
- package/dist/src/comms/resolve-session.js +53 -0
- package/dist/src/comms/resolve-session.js.map +1 -0
- package/dist/src/council/config.d.ts +1 -0
- package/dist/src/council/config.js +5 -0
- package/dist/src/council/config.js.map +1 -1
- package/dist/src/council/engine.d.ts +2 -0
- package/dist/src/council/engine.js +26 -3
- package/dist/src/council/engine.js.map +1 -1
- package/dist/src/council/index.d.ts +1 -1
- package/dist/src/council/index.js.map +1 -1
- package/dist/src/council/prompts.js +0 -1
- package/dist/src/council/prompts.js.map +1 -1
- package/dist/src/council/synth.js +1 -1
- package/dist/src/council/synth.js.map +1 -1
- package/dist/src/council/types.d.ts +2 -0
- package/dist/src/daily-log.d.ts +18 -0
- package/dist/src/daily-log.js +138 -0
- package/dist/src/daily-log.js.map +1 -0
- package/dist/src/goal.d.ts +4 -0
- package/dist/src/goal.js +44 -0
- package/dist/src/goal.js.map +1 -0
- package/dist/src/instructions-memory.d.ts +9 -0
- package/dist/src/instructions-memory.js +72 -0
- package/dist/src/instructions-memory.js.map +1 -0
- package/dist/src/mcp/tools/daily-log.d.ts +2 -0
- package/dist/src/mcp/tools/daily-log.js +148 -0
- package/dist/src/mcp/tools/daily-log.js.map +1 -0
- package/dist/src/omp-root.d.ts +1 -0
- package/dist/src/omp-root.js +19 -0
- package/dist/src/omp-root.js.map +1 -0
- package/dist/src/project-memory.d.ts +13 -0
- package/dist/src/project-memory.js +105 -0
- package/dist/src/project-memory.js.map +1 -0
- package/dist/src/state.d.ts +17 -0
- package/dist/src/state.js +101 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/trace.d.ts +19 -0
- package/dist/src/trace.js +74 -0
- package/dist/src/trace.js.map +1 -0
- package/package.json +3 -1
- package/scripts/lib/daily-log.mjs +155 -0
- package/scripts/lib/hook-output.mjs +2 -1
- package/scripts/lib/omp-root.mjs +15 -0
- package/scripts/lib/project-memory.mjs +21 -0
- package/scripts/prompt-submit.mjs +14 -2
- package/scripts/session-end.mjs +6 -1
- package/scripts/session-start.mjs +52 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: researcher
|
|
3
|
-
description: Map the codebase or external docs to answer a specific question. Returns evidence, not opinions. Backed by the oh-my-copilot
|
|
3
|
+
description: Map the codebase or external docs to answer a specific question. Returns evidence, not opinions. Backed by the oh-my-copilot CLI (omp daily-log / project-memory / state) for note-taking across turns.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# researcher
|
|
@@ -17,10 +17,11 @@ Find evidence and document it. Do not propose changes.
|
|
|
17
17
|
- Open questions that need more digging (if any).
|
|
18
18
|
|
|
19
19
|
## Guidance
|
|
20
|
-
- Use the
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
20
|
+
- Use the `omp` CLI for note-taking (run as shell commands):
|
|
21
|
+
- `omp daily-log add "<text>"` to record interim findings as you go.
|
|
22
|
+
- `omp project-memory add-note "<text>"` for facts worth keeping for the whole project.
|
|
23
|
+
- `omp state write <key> <json>` / `omp state read <key>` for transient structured scratch between turns.
|
|
24
|
+
- `omp trace timeline` if the question involves event history.
|
|
24
25
|
- Cite, don't paraphrase. Quote the line you found.
|
|
25
26
|
- If you can't find an answer, say so plainly — don't invent one.
|
|
26
|
-
- Do not modify code or files unrelated to research artifacts (
|
|
27
|
+
- Do not modify code or files unrelated to research artifacts (daily log, project memory).
|
|
@@ -33,3 +33,26 @@ Skip when the session was fewer than 3 turns or contained no user corrections.
|
|
|
33
33
|
Invoke `/self-evolve` **at most once per session.** After it reports back, do not invoke it again — even if the user replies "thanks" or "done" to the report itself. Just acknowledge and end the session.
|
|
34
34
|
|
|
35
35
|
The skill lives at `.github/skills/self-evolve/SKILL.md`. It is self-contained: it tells you which signals count as corrections, where to log them, and when a recurring pattern should become a draft skill.
|
|
36
|
+
|
|
37
|
+
## Daily log & repo goal
|
|
38
|
+
|
|
39
|
+
Keep lightweight memory in `.omp/` so the next session has context. **Be sparse** — these are
|
|
40
|
+
memory aids, not a transcript. Float kills their value.
|
|
41
|
+
|
|
42
|
+
- **Repo goal** (`.omp/goal.md`, via `/goal`): set/update the one-line objective when it is
|
|
43
|
+
established or genuinely shifts. Not every session.
|
|
44
|
+
- **Daily log** (`.omp/memory/daily/<date>.md`, via `/daily-log`):
|
|
45
|
+
- When a heavy mode (`/ralph`, `/ralplan`, `/omp-autopilot`, `/team`, `/ultrawork`,
|
|
46
|
+
`/ultraqa`) starts, set today's goal from its objective: `omp daily-log set-goal "<text>"`.
|
|
47
|
+
- When such a mode finishes, add **one** concise summary entry — `omp daily-log add "<text>"`:
|
|
48
|
+
what changed, key decisions, next step.
|
|
49
|
+
- Otherwise add an entry only at a genuine milestone you judge worth remembering.
|
|
50
|
+
- **Project memory**: `omp project-memory add-directive "<rule>"` for a must-follow rule —
|
|
51
|
+
directives are **injected at every session start** (never on-demand), so add only real,
|
|
52
|
+
lasting rules and keep them few. `omp project-memory add-note "<title>" [--body "<text>"]`
|
|
53
|
+
for durable facts; list them with `omp project-memory index` and load one with
|
|
54
|
+
`omp project-memory read <id>` — bodies stay on disk until asked, so notes never bloat context.
|
|
55
|
+
- **Caps & retention:** keep directives to a handful (only the first ~12 are injected at start);
|
|
56
|
+
a few daily entries per day — decisions and outcomes, not narration; skip trivial sessions;
|
|
57
|
+
never log per skill invocation. Trim old day-files with `omp daily-log prune [--keep-days <n>]`
|
|
58
|
+
(default 30).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: daily-log
|
|
3
|
+
description: Keep a simple per-project daily memory log under the project's daily-memory directory. Review it at session start when relevant, set today's goal, append milestones, and summarize before wrapping up. Use when the user says /daily-log, asks to record/recall what was done, or at end of session.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Daily Log
|
|
7
|
+
|
|
8
|
+
A lightweight, human-readable journal stored **per project** at `.omp/memory/daily/<YYYY-MM-DD>.md`.
|
|
9
|
+
Each day has a `## Goal` and a timestamped `## Log`. It is written and read through the `omp`
|
|
10
|
+
CLI (run these as shell commands):
|
|
11
|
+
|
|
12
|
+
- `omp daily-log set-goal "<text>"` — set/replace today's goal (the day's intent).
|
|
13
|
+
- `omp daily-log add "<text>"` — append a timestamped bullet to today's log.
|
|
14
|
+
- `omp daily-log read [--days N]` — read today + the previous N days (default 1).
|
|
15
|
+
|
|
16
|
+
The command writes under the current project's `.omp/`, so every project keeps its own log
|
|
17
|
+
automatically.
|
|
18
|
+
|
|
19
|
+
## When to use each
|
|
20
|
+
|
|
21
|
+
### At session start (review)
|
|
22
|
+
The SessionStart hook injects a one-line breadcrumb, e.g.:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
[DAILY LOG] Goal: <today's goal>
|
|
26
|
+
N entries logged in the last 7 days — run `omp daily-log read` to load if relevant.
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If the breadcrumb looks relevant to what the user is asking for — same feature, an open
|
|
30
|
+
goal, or continuation of yesterday's work — run `omp daily-log read` to pull the full recent
|
|
31
|
+
log. If the new request is clearly unrelated, **skip it**; loading is optional.
|
|
32
|
+
|
|
33
|
+
### When intent is established
|
|
34
|
+
Once you and the user agree on what today is about, run `omp daily-log set-goal "<text>"` with a
|
|
35
|
+
short one-line goal (e.g. "Ship the daily-log feature").
|
|
36
|
+
|
|
37
|
+
### At milestones
|
|
38
|
+
After a meaningful step — a decision made, a feature landed, a blocker hit — run
|
|
39
|
+
`omp daily-log add "<text>"` with a concise note. Record *what changed and why*, not narration.
|
|
40
|
+
Good: "Chose breadcrumb+lazy-load over always-inject to keep start message small."
|
|
41
|
+
Avoid: "Ran the build."
|
|
42
|
+
|
|
43
|
+
### Resume nudge (start of the next session)
|
|
44
|
+
There is no per-turn nudge. Instead, if a session did real work but wrote nothing,
|
|
45
|
+
the **next** SessionStart injects a one-line reminder:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
[DAILY LOG] Your last session made progress but recorded nothing ...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Treat it as a prompt to capture what changed before moving on — or, better, summarize
|
|
52
|
+
into the log *before ending* the session (below), so the nudge never needs to fire.
|
|
53
|
+
|
|
54
|
+
### Before wrapping up (the real end-of-session write)
|
|
55
|
+
When the user signals they're done, **summarize the session into the log** with one or two
|
|
56
|
+
`omp daily-log add "<text>"` commands: what was accomplished, key decisions, and the next step
|
|
57
|
+
for tomorrow. The SessionEnd hook cannot reach the model, so this end-of-session summary happens here.
|
|
58
|
+
|
|
59
|
+
## Notes
|
|
60
|
+
- Keep entries short; the log is a memory aid, not a transcript.
|
|
61
|
+
- The file is safe to hand-edit: the CLI preserves your lines in the Goal and Log
|
|
62
|
+
sections verbatim (only blank spacer lines between entries are dropped on rewrite).
|
|
63
|
+
- Do not put secrets in the log — it is a plain file committed nowhere by this skill but
|
|
64
|
+
readable by anyone with repo access.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: goal
|
|
3
|
+
description: Set or show the repo's durable objective — what we want to achieve in this repo — stored per project at .omp/goal.md. Use when the user says /goal, states the repo's north-star, or asks "what are we trying to achieve here".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Goal
|
|
7
|
+
|
|
8
|
+
The repo's **durable objective** — the north-star for this project — stored per project at
|
|
9
|
+
`.omp/goal.md`. It is distinct from a daily log's per-day goal: this is the long-lived "what
|
|
10
|
+
we want to achieve in this repo", set rarely and updated only when the objective changes.
|
|
11
|
+
|
|
12
|
+
It is read and written through the `omp` CLI (run these as shell commands):
|
|
13
|
+
|
|
14
|
+
- `omp goal set "<objective>"` — set/replace the repo objective (one concise line).
|
|
15
|
+
- `omp goal read` — print the current repo objective.
|
|
16
|
+
|
|
17
|
+
The command writes under the current project's `.omp/`, so every repo keeps its own goal
|
|
18
|
+
automatically.
|
|
19
|
+
|
|
20
|
+
## When to use
|
|
21
|
+
|
|
22
|
+
- **`/goal <text>`** — the user states the objective; run `omp goal set "<one concise line>"`.
|
|
23
|
+
- **`/goal`** (no text) — run `omp goal read` and show the current objective. If none is set,
|
|
24
|
+
offer to set one.
|
|
25
|
+
- The SessionStart hook surfaces the goal as a `[REPO GOAL] …` line, so it is already in
|
|
26
|
+
context at the start of each session — only run `omp goal read` when the user asks explicitly.
|
|
27
|
+
|
|
28
|
+
## Notes
|
|
29
|
+
|
|
30
|
+
- Keep it to a single north-star sentence; it is not a task list. Use the daily log
|
|
31
|
+
(`/daily-log`) for day-to-day progress toward this goal.
|
|
32
|
+
- Update it when the objective genuinely shifts — not every session.
|
|
33
|
+
- Do not put secrets in it; `.omp/goal.md` is a plain file readable by anyone with repo access.
|
|
@@ -26,9 +26,10 @@ synthesizer model reasons over their answers — using each member's weight as a
|
|
|
26
26
|
3. **Synthesis** — a low-context synthesizer model merges survivors into a final
|
|
27
27
|
`verdict`, `confidence`, `rationale`, and `minority_report`. Evidence quality
|
|
28
28
|
can override weight.
|
|
29
|
-
4. **Graceful degradation** —
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
4. **Graceful degradation** — errored, unavailable, or unparseable members are
|
|
30
|
+
dropped; timed-out members that produced valid JSON are recovered as
|
|
31
|
+
survivors. The council synthesizes from all survivors and only fails if fewer
|
|
32
|
+
than `min-survivors` (default 2) remain.
|
|
32
33
|
|
|
33
34
|
## Agent execution steps (FOLLOW EXACTLY)
|
|
34
35
|
|
|
@@ -48,7 +49,7 @@ degradation, and synthesis for you — do NOT try to call models yourself:
|
|
|
48
49
|
```
|
|
49
50
|
omp council "<question>" [--models a,b,c | model:role:weight,...] \
|
|
50
51
|
[--context <text|@file>] [--rubric <text|@file>] \
|
|
51
|
-
[--synth <model>] [--probe] [--timeout <ms>] \
|
|
52
|
+
[--synth <model>] [--probe] [--timeout <ms>] [--synth-timeout <ms>] \
|
|
52
53
|
[--min-survivors <n>] [--max-concurrency <n>] [--tmp-dir <dir>] [--json]
|
|
53
54
|
```
|
|
54
55
|
|
|
@@ -62,6 +63,12 @@ omp council "<question>" [--models a,b,c | model:role:weight,...] \
|
|
|
62
63
|
default; the normal run already reports unavailable models inline).
|
|
63
64
|
- `--json` — emit the full structured result for programmatic use; omit it for a
|
|
64
65
|
readable verdict + dropped-member summary.
|
|
66
|
+
- `--synth-timeout <ms>` — synthesizer timeout (default: 2× `--timeout`). The
|
|
67
|
+
synth processes all member outputs so may need longer than individual members.
|
|
68
|
+
The synth prompt does NOT include the shared context (members already digested
|
|
69
|
+
it), so this is mainly needed for councils with many members or long rationale.
|
|
70
|
+
- Timed-out members that produced valid JSON before the kill signal are
|
|
71
|
+
automatically recovered as survivors — they are not dropped.
|
|
65
72
|
|
|
66
73
|
If `omp council` is not found (the published `omp` predates this feature), fall
|
|
67
74
|
back to the local build from the repo root: `node dist/src/cli.js council "<question>" ...`
|
|
@@ -94,6 +101,7 @@ default roster is used.
|
|
|
94
101
|
"synthesizer": "gpt-4.1",
|
|
95
102
|
"minSurvivors": 2,
|
|
96
103
|
"maxConcurrency": 4,
|
|
104
|
+
"synthTimeoutMs": 240000,
|
|
97
105
|
"probe": false,
|
|
98
106
|
"members": [
|
|
99
107
|
{ "model": "gpt-5-mini", "role": "critic", "weight": 0.4 },
|
package/README.md
CHANGED
|
@@ -284,3 +284,19 @@ To unlink and revert to the published package:
|
|
|
284
284
|
npm unlink -g @damian87/omp
|
|
285
285
|
npm i -g @damian87/omp
|
|
286
286
|
```
|
|
287
|
+
|
|
288
|
+
## Releasing (maintainers)
|
|
289
|
+
|
|
290
|
+
One command runs the whole flow — quality gates → version bump → tag → push →
|
|
291
|
+
GitHub release → `npm publish`. It's a maintainer script (`tools/release.sh`),
|
|
292
|
+
**not** a published plugin skill, so it never ships in the package or appears in
|
|
293
|
+
the in-session skill catalog. npm 2FA is on, so pass an authenticator code via `--otp`.
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npm run release -- minor --otp 123456 # bump 0.x.0, tag, push, GH release, publish
|
|
297
|
+
npm run release -- patch --dry-run # validate everything, change nothing
|
|
298
|
+
npm run release:publish-only -- --otp 123456 # build + publish the CURRENT version (recover a half-done release)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The script refuses to run unless you're on a clean, up-to-date `main` and the
|
|
302
|
+
gates (`test`, `lint:skills`, `check:catalog`, `build`) pass.
|
package/dist/src/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ function printResult(result, json) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
function help() {
|
|
25
|
-
return `oh-my-copilot\n\nRun \`omp\` with no arguments to launch copilot (permissions bypass OFF).\nUse \`omp help\` to show this list.\n\nCommands:\n (no args) launch copilot (bypass OFF by default)\n version [--json]\n list [--json]\n setup [--dry-run] [--scope project|user] [--plugin-root <dir>] [--json]\n doctor [--json] [--copilot-bin <path>] [--skip-copilot]\n launch -- <args...>\n --madmax [args...] (bare-flag launch with permissions bypass; alias of --yolo)\n team <N:role> "<task>" [--name <name>] [--json]\n team status <name> [--json]\n team shutdown <name> [--json]\n team api claim-task --input '<json>' [--json]\n team api transition-task-status --input '<json>' [--json]\n team api send-message --input '<json>' [--json]\n team api broadcast --input '<json>' [--json]\n team api mailbox-list --input '<json>' [--json]\n team api mailbox-mark-delivered --input '<json>' [--json]\n council "<question>" [--models a,b,c|m:role:weight] [--context <text|@file>] [--rubric <text|@file>] [--synth <model>] [--probe] [--timeout <ms>] [--min-survivors <n>] [--max-concurrency <n>] [--tmp-dir <dir>] [--json]\n
|
|
25
|
+
return `oh-my-copilot\n\nRun \`omp\` with no arguments to launch copilot (permissions bypass OFF).\nUse \`omp help\` to show this list.\n\nCommands:\n (no args) launch copilot (bypass OFF by default)\n version [--json]\n list [--json]\n setup [--dry-run] [--scope project|user] [--plugin-root <dir>] [--json]\n doctor [--json] [--copilot-bin <path>] [--skip-copilot]\n launch -- <args...>\n --madmax [args...] (bare-flag launch with permissions bypass; alias of --yolo)\n team <N:role> "<task>" [--name <name>] [--json]\n team status <name> [--json]\n team shutdown <name> [--json]\n team api claim-task --input '<json>' [--json]\n team api transition-task-status --input '<json>' [--json]\n team api send-message --input '<json>' [--json]\n team api broadcast --input '<json>' [--json]\n team api mailbox-list --input '<json>' [--json]\n team api mailbox-mark-delivered --input '<json>' [--json]\n council "<question>" [--models a,b,c|m:role:weight] [--context <text|@file>] [--rubric <text|@file>] [--synth <model>] [--probe] [--timeout <ms>] [--synth-timeout <ms>] [--min-survivors <n>] [--max-concurrency <n>] [--tmp-dir <dir>] [--json]\n ralph start "<task>" [--max-iterations <n>] [--session-id <id>] [--json]\n ralph status [--json]\n ralph tick [--json]\n ralph cancel [--json]\n ultrawork start "<objective>" [--task-count <n>] [--summary <s>] [--json]\n ultrawork status [--json]\n ultrawork cancel [--json]\n ultraqa start "<goal>" [--max-cycles <n>] [--json]\n ultraqa cycle pass|fail|pending [--json]\n ultraqa status [--json]\n ultraqa cancel [--json]\n goal set "<objective>" [--json]\n goal read [--json]\n memory sync [--json] (render goal+directives into copilot-instructions.md)\n daily-log set-goal "<text>" [--json]\n daily-log add "<text>" [--json]\n daily-log read [--days <n>] [--json]\n daily-log prune [--keep-days <n>] [--json]\n state write <key> <val> [--ttl <s>] | read|delete|status <key> | list | cleanup [--json]\n project-memory read [<id>] | index | add-note "<title>" [--body "<text>"] | add-directive "<rule>" [--json]\n trace timeline [<sessionId>] [--limit <n>] | summary [<sessionId>] | add <sessionId> <event> [<json>] [--json]\n catalog list [--json]\n catalog validate [--json]\n catalog capability <id> [--json]\n project inspect [--json]\n skill install <skill-dir> [--root <repo>] [--scope project|user] [--dry-run] [--json]\n lint:skills [--root <repo>]\n sync:dry-run [--root <repo>]\n jira:dry-run [--root <repo>]\n jira render <plan-file> [--root <repo>] [--json]\n jira apply <ticket-key-or-plan-file> --comment|--update|--transition|--link [--dry-run] [--json]\n`;
|
|
26
26
|
}
|
|
27
27
|
async function resolveExistingInputPath(value) {
|
|
28
28
|
const { existsSync } = await import("node:fs");
|
|
@@ -117,14 +117,6 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
117
117
|
if (group === "council") {
|
|
118
118
|
return await handleCouncilCommand(argv, json);
|
|
119
119
|
}
|
|
120
|
-
if (group === "mcp") {
|
|
121
|
-
const { runMcpServer } = await import("./mcp/server.js");
|
|
122
|
-
const { allTools } = await import("./mcp/tools/index.js");
|
|
123
|
-
const { filterToolsByEnv } = await import("./mcp/server.js");
|
|
124
|
-
const tools = filterToolsByEnv(allTools);
|
|
125
|
-
await runMcpServer({ name: "oh-my-copilot", version: "0.1.0", tools });
|
|
126
|
-
return { ok: true, message: "mcp server exited" };
|
|
127
|
-
}
|
|
128
120
|
if (group === "ralph") {
|
|
129
121
|
return await handleModeCommand("ralph", argv, json);
|
|
130
122
|
}
|
|
@@ -134,6 +126,195 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
134
126
|
if (group === "ultraqa") {
|
|
135
127
|
return await handleModeCommand("ultraqa", argv, json);
|
|
136
128
|
}
|
|
129
|
+
if (group === "goal") {
|
|
130
|
+
const { readRepoGoal, writeRepoGoal } = await import("./goal.js");
|
|
131
|
+
const { syncInstructionsMemory } = await import("./instructions-memory.js");
|
|
132
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
133
|
+
if (command === "set") {
|
|
134
|
+
if (!value || !value.trim() || value.startsWith("-")) {
|
|
135
|
+
return { ok: false, exitCode: 1, message: 'usage: omp goal set "<objective>"' };
|
|
136
|
+
}
|
|
137
|
+
const goal = writeRepoGoal(cwd, value);
|
|
138
|
+
syncInstructionsMemory(cwd); // refresh the managed block Copilot reads
|
|
139
|
+
return json ? { ok: true, output: { ok: true, goal } } : { ok: true, message: `repo goal set: ${goal}` };
|
|
140
|
+
}
|
|
141
|
+
if (command === "read" || command === undefined) {
|
|
142
|
+
const goal = readRepoGoal(cwd);
|
|
143
|
+
return json ? { ok: true, output: { goal } } : { ok: true, message: goal || "(no repo goal set)" };
|
|
144
|
+
}
|
|
145
|
+
return { ok: false, exitCode: 1, message: 'Unknown goal subcommand. Try: goal set "<text>" | goal read' };
|
|
146
|
+
}
|
|
147
|
+
if (group === "memory") {
|
|
148
|
+
const { syncInstructionsMemory } = await import("./instructions-memory.js");
|
|
149
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
150
|
+
if (command === "sync" || command === undefined) {
|
|
151
|
+
const r = syncInstructionsMemory(cwd);
|
|
152
|
+
return json
|
|
153
|
+
? { ok: r.wrote, output: r }
|
|
154
|
+
: { ok: r.wrote, message: r.wrote ? `memory synced to ${r.path}` : `could not write ${r.path}` };
|
|
155
|
+
}
|
|
156
|
+
return { ok: false, exitCode: 1, message: "Unknown memory subcommand. Try: memory sync" };
|
|
157
|
+
}
|
|
158
|
+
if (group === "daily-log") {
|
|
159
|
+
const { setDailyGoal, addLogEntry, readDailyLog, pruneDailyLog } = await import("./daily-log.js");
|
|
160
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
161
|
+
if (command === "set-goal") {
|
|
162
|
+
if (!value || !value.trim() || value.startsWith("-")) {
|
|
163
|
+
return { ok: false, exitCode: 1, message: 'usage: omp daily-log set-goal "<text>"' };
|
|
164
|
+
}
|
|
165
|
+
const res = setDailyGoal(cwd, value);
|
|
166
|
+
return json ? { ok: true, output: { ok: true, ...res } } : { ok: true, message: `daily goal set (${res.date}): ${res.goal}` };
|
|
167
|
+
}
|
|
168
|
+
if (command === "add") {
|
|
169
|
+
if (!value || !value.trim() || value.startsWith("-")) {
|
|
170
|
+
return { ok: false, exitCode: 1, message: 'usage: omp daily-log add "<text>"' };
|
|
171
|
+
}
|
|
172
|
+
const res = addLogEntry(cwd, value);
|
|
173
|
+
return json ? { ok: true, output: { ok: true, ...res } } : { ok: true, message: `logged (${res.date}); ${res.count} entr${res.count === 1 ? "y" : "ies"} today` };
|
|
174
|
+
}
|
|
175
|
+
if (command === "read" || command === undefined) {
|
|
176
|
+
const daysRaw = flagValue(argv, "--days");
|
|
177
|
+
const days = daysRaw !== undefined && Number.isFinite(Number(daysRaw)) ? Number(daysRaw) : 1;
|
|
178
|
+
const text = readDailyLog(cwd, days);
|
|
179
|
+
return json ? { ok: true, output: { log: text } } : { ok: true, message: text || "(no daily log entries)" };
|
|
180
|
+
}
|
|
181
|
+
if (command === "prune") {
|
|
182
|
+
const keepRaw = flagValue(argv, "--keep-days");
|
|
183
|
+
const keepDays = keepRaw !== undefined && Number.isFinite(Number(keepRaw)) ? Number(keepRaw) : 30;
|
|
184
|
+
const removed = pruneDailyLog(cwd, keepDays);
|
|
185
|
+
return json
|
|
186
|
+
? { ok: true, output: { removed } }
|
|
187
|
+
: { ok: true, message: `pruned ${removed.length} day-file(s) older than ${keepDays}d` };
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
ok: false,
|
|
191
|
+
exitCode: 1,
|
|
192
|
+
message: 'Unknown daily-log subcommand. Try: daily-log set-goal "<text>" | add "<text>" | read [--days N] | prune [--keep-days N]',
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (group === "state") {
|
|
196
|
+
const s = await import("./state.js");
|
|
197
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
198
|
+
if (command === "read" && value) {
|
|
199
|
+
return { ok: true, output: s.stateRead(cwd, value) };
|
|
200
|
+
}
|
|
201
|
+
if (command === "write" && value) {
|
|
202
|
+
const raw = argv[3];
|
|
203
|
+
if (raw === undefined)
|
|
204
|
+
return { ok: false, exitCode: 1, message: "usage: omp state write <key> <json-or-string> [--ttl <sec>]" };
|
|
205
|
+
let parsed;
|
|
206
|
+
try {
|
|
207
|
+
parsed = JSON.parse(raw);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
parsed = raw;
|
|
211
|
+
}
|
|
212
|
+
const ttlRaw = flagValue(argv, "--ttl");
|
|
213
|
+
const ttl = ttlRaw !== undefined && Number.isFinite(Number(ttlRaw)) ? Number(ttlRaw) : undefined;
|
|
214
|
+
const expiresAt = s.stateWrite(cwd, value, parsed, ttl);
|
|
215
|
+
return json ? { ok: true, output: { ok: true, expiresAt } } : { ok: true, message: `wrote ${value}${expiresAt ? ` (expires ${expiresAt})` : ""}` };
|
|
216
|
+
}
|
|
217
|
+
if (command === "delete" && value) {
|
|
218
|
+
s.stateDelete(cwd, value);
|
|
219
|
+
return { ok: true, message: `deleted ${value}` };
|
|
220
|
+
}
|
|
221
|
+
if (command === "list") {
|
|
222
|
+
return { ok: true, output: { keys: s.stateList(cwd) } };
|
|
223
|
+
}
|
|
224
|
+
if (command === "cleanup") {
|
|
225
|
+
const deleted = s.stateCleanup(cwd);
|
|
226
|
+
return json ? { ok: true, output: { deleted } } : { ok: true, message: `cleaned ${deleted} expired` };
|
|
227
|
+
}
|
|
228
|
+
if (command === "status" && value) {
|
|
229
|
+
return { ok: true, output: s.stateStatus(cwd, value) };
|
|
230
|
+
}
|
|
231
|
+
return { ok: false, exitCode: 1, message: "Unknown state subcommand. Try: state read <key> | write <key> <val> [--ttl s] | delete <key> | list | cleanup | status <key>" };
|
|
232
|
+
}
|
|
233
|
+
if (group === "project-memory") {
|
|
234
|
+
const pm = await import("./project-memory.js");
|
|
235
|
+
const { syncInstructionsMemory } = await import("./instructions-memory.js");
|
|
236
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
237
|
+
if (command === "add-note") {
|
|
238
|
+
// Accept a --title flag or a positional title; reject a flag-like value in
|
|
239
|
+
// either slot, so `add-note --title --body x` doesn't store "--body".
|
|
240
|
+
const titleFlag = flagValue(argv, "--title");
|
|
241
|
+
const title = titleFlag && !titleFlag.startsWith("-")
|
|
242
|
+
? titleFlag
|
|
243
|
+
: value && !value.startsWith("-")
|
|
244
|
+
? value
|
|
245
|
+
: undefined;
|
|
246
|
+
if (!title || !title.trim()) {
|
|
247
|
+
return { ok: false, exitCode: 1, message: 'usage: omp project-memory add-note "<title>" [--body "<text>"]' };
|
|
248
|
+
}
|
|
249
|
+
const id = pm.addNote(cwd, title, flagValue(argv, "--body"));
|
|
250
|
+
syncInstructionsMemory(cwd); // refresh the managed block Copilot reads
|
|
251
|
+
return json ? { ok: true, output: { ok: true, id } } : { ok: true, message: `note added: ${id}` };
|
|
252
|
+
}
|
|
253
|
+
if (command === "add-directive") {
|
|
254
|
+
const directiveFlag = flagValue(argv, "--directive");
|
|
255
|
+
const directive = directiveFlag && !directiveFlag.startsWith("-")
|
|
256
|
+
? directiveFlag
|
|
257
|
+
: value && !value.startsWith("-")
|
|
258
|
+
? value
|
|
259
|
+
: undefined;
|
|
260
|
+
if (!directive || !directive.trim()) {
|
|
261
|
+
return { ok: false, exitCode: 1, message: 'usage: omp project-memory add-directive "<rule>"' };
|
|
262
|
+
}
|
|
263
|
+
const count = pm.addDirective(cwd, directive);
|
|
264
|
+
syncInstructionsMemory(cwd); // refresh the managed block Copilot reads
|
|
265
|
+
return json ? { ok: true, output: { ok: true, count } } : { ok: true, message: `directive added (${count} total)` };
|
|
266
|
+
}
|
|
267
|
+
if (command === "index") {
|
|
268
|
+
return { ok: true, output: { notes: pm.noteIndex(cwd) } };
|
|
269
|
+
}
|
|
270
|
+
if (command === "read" || command === undefined) {
|
|
271
|
+
// `read <id>` loads one note's body on demand; bare `read` returns the
|
|
272
|
+
// bounded summary (directives + note index — never note bodies).
|
|
273
|
+
if (value && !value.startsWith("-")) {
|
|
274
|
+
const note = pm.readNote(cwd, value);
|
|
275
|
+
if (note === null)
|
|
276
|
+
return { ok: false, exitCode: 1, message: `no note with id: ${value}` };
|
|
277
|
+
return { ok: true, message: note };
|
|
278
|
+
}
|
|
279
|
+
return { ok: true, output: { directives: pm.readDirectives(cwd), notes: pm.noteIndex(cwd) } };
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
ok: false,
|
|
283
|
+
exitCode: 1,
|
|
284
|
+
message: 'Unknown project-memory subcommand. Try: project-memory read [<id>] | index | add-note "<title>" [--body "<text>"] | add-directive "<rule>"',
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
if (group === "trace") {
|
|
288
|
+
const tr = await import("./trace.js");
|
|
289
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
290
|
+
const sid = value && !value.startsWith("-") ? value : undefined;
|
|
291
|
+
if (command === "timeline" || command === undefined) {
|
|
292
|
+
const limitRaw = flagValue(argv, "--limit");
|
|
293
|
+
const limit = limitRaw !== undefined && Number.isFinite(Number(limitRaw)) ? Number(limitRaw) : 50;
|
|
294
|
+
return { ok: true, output: tr.traceTimeline(cwd, sid, limit) };
|
|
295
|
+
}
|
|
296
|
+
if (command === "summary") {
|
|
297
|
+
return { ok: true, output: tr.traceSummary(cwd, sid) };
|
|
298
|
+
}
|
|
299
|
+
if (command === "add" && sid) {
|
|
300
|
+
const event = argv[3];
|
|
301
|
+
if (!event)
|
|
302
|
+
return { ok: false, exitCode: 1, message: "usage: omp trace add <sessionId> <event> [json-payload]" };
|
|
303
|
+
const payloadRaw = argv[4];
|
|
304
|
+
let payload;
|
|
305
|
+
if (payloadRaw !== undefined) {
|
|
306
|
+
try {
|
|
307
|
+
payload = JSON.parse(payloadRaw);
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
payload = payloadRaw;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
tr.appendTraceEntry(cwd, sid, { event, payload });
|
|
314
|
+
return { ok: true, message: `trace appended to ${sid}` };
|
|
315
|
+
}
|
|
316
|
+
return { ok: false, exitCode: 1, message: "Unknown trace subcommand. Try: trace timeline [sessionId] [--limit N] | summary [sessionId] | add <sessionId> <event> [json]" };
|
|
317
|
+
}
|
|
137
318
|
if (group === "catalog") {
|
|
138
319
|
if (command === "list") {
|
|
139
320
|
const bundle = loadCatalogBundle();
|
|
@@ -328,10 +509,12 @@ async function handleCouncilCommand(argv, json) {
|
|
|
328
509
|
const context = await readFlagOrFile(flagValue(argv, "--context"));
|
|
329
510
|
const rubric = await readFlagOrFile(flagValue(argv, "--rubric"));
|
|
330
511
|
let perMemberTimeoutMs;
|
|
512
|
+
let synthTimeoutMs;
|
|
331
513
|
let minSurvivors;
|
|
332
514
|
let maxConcurrency;
|
|
333
515
|
try {
|
|
334
516
|
perMemberTimeoutMs = parsePositiveIntFlag(flagValue(argv, "--timeout"), "--timeout");
|
|
517
|
+
synthTimeoutMs = parsePositiveIntFlag(flagValue(argv, "--synth-timeout"), "--synth-timeout");
|
|
335
518
|
minSurvivors = parsePositiveIntFlag(flagValue(argv, "--min-survivors"), "--min-survivors");
|
|
336
519
|
maxConcurrency = parsePositiveIntFlag(flagValue(argv, "--max-concurrency"), "--max-concurrency");
|
|
337
520
|
}
|
|
@@ -351,10 +534,15 @@ async function handleCouncilCommand(argv, json) {
|
|
|
351
534
|
synthesizerModel: flagValue(argv, "--synth"),
|
|
352
535
|
probe,
|
|
353
536
|
perMemberTimeoutMs,
|
|
537
|
+
synthTimeoutMs,
|
|
354
538
|
minSurvivors,
|
|
355
539
|
maxConcurrency,
|
|
356
540
|
tmpDir: flagValue(argv, "--tmp-dir"),
|
|
357
|
-
}, {
|
|
541
|
+
}, {
|
|
542
|
+
cwd: flagValue(argv, "--root") ?? process.cwd(),
|
|
543
|
+
bin: flagValue(argv, "--bin"),
|
|
544
|
+
onProgress: json ? undefined : (msg) => process.stderr.write(`${msg}\n`),
|
|
545
|
+
});
|
|
358
546
|
if (json) {
|
|
359
547
|
return { ok: result.ok, exitCode: result.ok ? 0 : 1, output: result };
|
|
360
548
|
}
|