@damian87/omp 0.10.0 → 0.13.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/copilot-instructions.md +16 -0
- package/.github/skills/jira-ticket/SKILL.md +4 -4
- package/.github/skills/omp-autopilot/SKILL.md +4 -0
- package/.github/skills/research-codebase/SKILL.md +4 -0
- package/.github/skills/schedule/SKILL.md +4 -0
- package/.github/skills/team/SKILL.md +4 -0
- package/.github/skills/ultrawork/SKILL.md +4 -0
- package/.github/skills/weighted-consensus/SKILL.md +4 -0
- package/README.md +4 -1
- package/dist/src/cli.js +134 -4
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/comms.d.ts +2 -0
- package/dist/src/commands/comms.js +110 -0
- package/dist/src/commands/comms.js.map +1 -0
- package/dist/src/commands/council.d.ts +2 -0
- package/dist/src/commands/council.js +77 -0
- package/dist/src/commands/council.js.map +1 -0
- package/dist/src/commands/env.d.ts +2 -0
- package/dist/src/commands/env.js +95 -0
- package/dist/src/commands/env.js.map +1 -0
- package/dist/src/commands/gateway.d.ts +3 -0
- package/dist/src/commands/gateway.js +129 -0
- package/dist/src/commands/gateway.js.map +1 -0
- package/dist/src/commands/memory.d.ts +7 -0
- package/dist/src/commands/memory.js +202 -0
- package/dist/src/commands/memory.js.map +1 -0
- package/dist/src/commands/mode.d.ts +4 -0
- package/dist/src/commands/mode.js +119 -0
- package/dist/src/commands/mode.js.map +1 -0
- package/dist/src/commands/schedule.d.ts +2 -0
- package/dist/src/commands/schedule.js +91 -0
- package/dist/src/commands/schedule.js.map +1 -0
- package/dist/src/commands/team.d.ts +2 -0
- package/dist/src/commands/team.js +146 -0
- package/dist/src/commands/team.js.map +1 -0
- package/dist/src/commands/utils.d.ts +13 -0
- package/dist/src/commands/utils.js +68 -0
- package/dist/src/commands/utils.js.map +1 -0
- package/dist/src/copilot/doctor.d.ts +1 -0
- package/dist/src/copilot/doctor.js +226 -27
- package/dist/src/copilot/doctor.js.map +1 -1
- package/dist/src/copilot/launch.js +13 -5
- package/dist/src/copilot/launch.js.map +1 -1
- package/dist/src/copilot/setup.js +13 -0
- package/dist/src/copilot/setup.js.map +1 -1
- package/dist/src/cost/index.d.ts +3 -0
- package/dist/src/cost/index.js +4 -0
- package/dist/src/cost/index.js.map +1 -0
- package/dist/src/cost/ledger.d.ts +21 -0
- package/dist/src/cost/ledger.js +72 -0
- package/dist/src/cost/ledger.js.map +1 -0
- package/dist/src/cost/summary.d.ts +22 -0
- package/dist/src/cost/summary.js +68 -0
- package/dist/src/cost/summary.js.map +1 -0
- package/dist/src/cost/tokenize.d.ts +7 -0
- package/dist/src/cost/tokenize.js +24 -0
- package/dist/src/cost/tokenize.js.map +1 -0
- package/dist/src/goal.js +6 -8
- package/dist/src/goal.js.map +1 -1
- package/dist/src/instructions-memory.js +26 -3
- package/dist/src/instructions-memory.js.map +1 -1
- package/dist/src/memory-review/apply.d.ts +7 -0
- package/dist/src/memory-review/apply.js +75 -0
- package/dist/src/memory-review/apply.js.map +1 -0
- package/dist/src/memory-review/config.d.ts +22 -0
- package/dist/src/memory-review/config.js +54 -0
- package/dist/src/memory-review/config.js.map +1 -0
- package/dist/src/memory-review/guard.d.ts +5 -0
- package/dist/src/memory-review/guard.js +37 -0
- package/dist/src/memory-review/guard.js.map +1 -0
- package/dist/src/memory-review/index.d.ts +17 -0
- package/dist/src/memory-review/index.js +87 -0
- package/dist/src/memory-review/index.js.map +1 -0
- package/dist/src/memory-review/prompt.d.ts +18 -0
- package/dist/src/memory-review/prompt.js +89 -0
- package/dist/src/memory-review/prompt.js.map +1 -0
- package/dist/src/memory-review/spawn.d.ts +2 -0
- package/dist/src/memory-review/spawn.js +51 -0
- package/dist/src/memory-review/spawn.js.map +1 -0
- package/dist/src/memory-review/transcript.d.ts +24 -0
- package/dist/src/memory-review/transcript.js +212 -0
- package/dist/src/memory-review/transcript.js.map +1 -0
- package/dist/src/memory-review/trigger.d.ts +21 -0
- package/dist/src/memory-review/trigger.js +27 -0
- package/dist/src/memory-review/trigger.js.map +1 -0
- package/dist/src/project-memory.d.ts +9 -0
- package/dist/src/project-memory.js +72 -1
- package/dist/src/project-memory.js.map +1 -1
- package/dist/src/state.js +25 -37
- package/dist/src/state.js.map +1 -1
- package/dist/src/utils/fs.d.ts +14 -0
- package/dist/src/utils/fs.js +32 -0
- package/dist/src/utils/fs.js.map +1 -0
- package/dist/src/utils/paths.d.ts +14 -0
- package/dist/src/utils/paths.js +21 -0
- package/dist/src/utils/paths.js.map +1 -0
- package/docs/general-skills.md +1 -0
- package/docs/memory-mode.md +94 -0
- package/hooks/hooks.json +9 -2
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/error.mjs +9 -7
- package/scripts/lib/cost-ledger.mjs +91 -0
- package/scripts/lib/hook-input.mjs +51 -0
- package/scripts/lib/hook-output.mjs +53 -11
- package/scripts/lib/memory-review-trigger.mjs +59 -0
- package/scripts/lib/minify.mjs +80 -0
- package/scripts/lib/pending-directives.mjs +36 -0
- package/scripts/post-tool-use-failure.mjs +21 -0
- package/scripts/post-tool-use.mjs +71 -8
- package/scripts/pre-tool-use.mjs +8 -6
- package/scripts/prompt-submit.mjs +12 -5
- package/scripts/session-end.mjs +15 -5
- package/scripts/session-start.mjs +9 -4
|
@@ -13,6 +13,22 @@ Default behaviours installed with this repo. Override per project as needed.
|
|
|
13
13
|
- Read the diff before committing.
|
|
14
14
|
- If unsure about scope, ask.
|
|
15
15
|
|
|
16
|
+
## Cost/token discipline
|
|
17
|
+
Cost data is local, best-effort, and estimated. `omp cost [--today] [--session <id>]`
|
|
18
|
+
summarizes prompt/tool token estimates from the hook ledger; it is not provider billing.
|
|
19
|
+
|
|
20
|
+
The cost hooks apply when this plugin's `hooks/hooks.json` is active in a Copilot CLI
|
|
21
|
+
session. They give session-wide visibility for skills invoked inside that session, not
|
|
22
|
+
standalone coverage for copied skills, raw shell scripts, or external CLIs.
|
|
23
|
+
|
|
24
|
+
Before rerunning noisy commands or failed edits, inspect the latest output and narrow the
|
|
25
|
+
next attempt. Prefer bounded summaries for large logs. Oversized postToolUse output is
|
|
26
|
+
minimized before it re-enters model context, with raw output preserved on disk and savings
|
|
27
|
+
recorded in the cost ledger. Diagnostics (errors, stack traces, assertions) are preserved
|
|
28
|
+
inline; other trimmed detail must be recovered by re-reading the raw file path noted in the
|
|
29
|
+
hook output (an extra tool call), so full fidelity depends on the model following that pointer.
|
|
30
|
+
Budget gates and retry-cost guidance are not current live behavior.
|
|
31
|
+
|
|
16
32
|
## Skills
|
|
17
33
|
Slash commands under `.github/skills/<name>/SKILL.md` are auto-discovered by Copilot. See `omp list` for the catalog active in this project.
|
|
18
34
|
|
|
@@ -18,21 +18,21 @@ Use `/jira-ticket` when work tracking is requested.
|
|
|
18
18
|
### Create
|
|
19
19
|
- Build from an approved plan or implementation slice (a plan file or markdown)
|
|
20
20
|
- Include: Summary, Description, Acceptance Criteria
|
|
21
|
-
- Render the payload with `omp jira render <plan-file>` — this **never** writes to Jira. To create/comment/update, run `omp jira apply <plan-file-or-ticket-key>`, which defaults to **dry-run** and only writes when Jira is configured with `JIRA_MODE=live
|
|
21
|
+
- Render the payload with `omp jira render <plan-file>` — this **never** writes to Jira. To create/comment/update, run `omp jira apply <plan-file-or-ticket-key>`, which defaults to **dry-run** and only writes when Jira is configured with `JIRA_MODE=live`, the user has explicitly confirmed, and the CLI has explicit body/field support for the operation you need.
|
|
22
22
|
|
|
23
23
|
### Comment
|
|
24
24
|
- Add implementation evidence, verification results, or status updates
|
|
25
25
|
- Format for readability (use Jira wiki markup, not Markdown)
|
|
26
|
-
-
|
|
26
|
+
- Draft the comment payload and preview via dry-run only; do not run live comment writes from this skill until the CLI accepts an explicit comment body/body file.
|
|
27
27
|
|
|
28
28
|
### Safe update
|
|
29
29
|
- Only update known simple fields (summary, description, labels)
|
|
30
|
-
-
|
|
30
|
+
- Draft update fields and preview via dry-run only; do not run live update writes from this skill until the CLI accepts explicit update fields.
|
|
31
31
|
- Do not guess transitions, issue links, project keys, or secrets
|
|
32
32
|
|
|
33
33
|
## Rules
|
|
34
34
|
|
|
35
|
-
- Always preview with `omp jira render` or `omp jira apply … --dry-run` (the default)
|
|
35
|
+
- Always preview with `omp jira render` or `omp jira apply … --dry-run` (the default); live writes require explicit user confirmation plus CLI support for the exact payload being written
|
|
36
36
|
- If Jira config is missing, the commands stay in dry-run and print the payload — never fail silently
|
|
37
37
|
- Do not guess project keys, transitions, or credentials
|
|
38
38
|
- Keep acceptance criteria testable and specific
|
|
@@ -73,3 +73,7 @@ Before claiming done:
|
|
|
73
73
|
- [ ] Lint clean (if applicable)
|
|
74
74
|
- [ ] `/verify` or `/ultraqa` produced PASS evidence
|
|
75
75
|
- [ ] No uncommitted work left behind
|
|
76
|
+
|
|
77
|
+
## Cost/token note
|
|
78
|
+
|
|
79
|
+
This skill can drive multiple tool calls or long-running output. Use `omp cost [--today] [--session <id>]` for local hook-ledger estimates only; it is not provider billing. Keep injected summaries concise and prefer bounded output when rerunning noisy commands.
|
|
@@ -56,3 +56,7 @@ Show concise summary to user with key file references. Ask if they have follow-u
|
|
|
56
56
|
### 7. Follow-ups
|
|
57
57
|
|
|
58
58
|
If the user has follow-ups, read `reference/follow-up.md` for the append protocol.
|
|
59
|
+
|
|
60
|
+
## Cost/token note
|
|
61
|
+
|
|
62
|
+
This skill can drive multiple tool calls or long-running output. Use `omp cost [--today] [--session <id>]` for local hook-ledger estimates only; it is not provider billing. Keep injected summaries concise and prefer bounded output when rerunning noisy commands.
|
|
@@ -69,3 +69,7 @@ omp schedule add --id nightly-tests --cron "0 2 * * *" \
|
|
|
69
69
|
--prompt "Run the test suite; if anything fails, open an issue with the log." \
|
|
70
70
|
--allow-all-tools --max-runs 7
|
|
71
71
|
```
|
|
72
|
+
|
|
73
|
+
## Cost/token note
|
|
74
|
+
|
|
75
|
+
This skill can drive multiple tool calls or long-running output. Use `omp cost [--today] [--session <id>]` for local hook-ledger estimates only; it is not provider billing. Keep injected summaries concise and prefer bounded output when rerunning noisy commands.
|
|
@@ -127,3 +127,7 @@ Use `/ralplan` before `/team` to produce the plan. Use `/verify` after completio
|
|
|
127
127
|
- Each pane is an independent session — no shared state
|
|
128
128
|
- Workers can message each other via `omp team api send-message` (runtime mode only)
|
|
129
129
|
- If tasks depend on each other, use `/ralph` instead
|
|
130
|
+
|
|
131
|
+
## Cost/token note
|
|
132
|
+
|
|
133
|
+
This skill can drive multiple tool calls or long-running output. Use `omp cost [--today] [--session <id>]` for local hook-ledger estimates only; it is not provider billing. Keep injected summaries concise and prefer bounded output when rerunning noisy commands.
|
|
@@ -71,3 +71,7 @@ Summarise: completed, failed, blocked. Then clear the tracked state with `omp ul
|
|
|
71
71
|
- `Completed` — items done with evidence
|
|
72
72
|
- `Failed/blockers` — items that couldn't be completed and why
|
|
73
73
|
- `Verification` — test/lint/build results per wave
|
|
74
|
+
|
|
75
|
+
## Cost/token note
|
|
76
|
+
|
|
77
|
+
This skill can drive multiple tool calls or long-running output. Use `omp cost [--today] [--session <id>]` for local hook-ledger estimates only; it is not provider billing. Keep injected summaries concise and prefer bounded output when rerunning noisy commands.
|
|
@@ -127,3 +127,7 @@ default roster is used.
|
|
|
127
127
|
|
|
128
128
|
Pair with `/code-review` (pass a review rubric + the diff as context) for a
|
|
129
129
|
multi-model review verdict, or with `/ralplan` to weigh competing plans.
|
|
130
|
+
|
|
131
|
+
## Cost/token note
|
|
132
|
+
|
|
133
|
+
This skill can drive multiple tool calls or long-running output. Use `omp cost [--today] [--session <id>]` for local hook-ledger estimates only; it is not provider billing. Keep injected summaries concise and prefer bounded output when rerunning noisy commands.
|
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ That's it.
|
|
|
55
55
|
- **Persistent execution** — Ralph, UltraQA, and Ultrawork keep going until the goal is verified
|
|
56
56
|
- **File-state coordination** — workers swap typed messages over an outbox/inbox cursor with atomic `O_EXCL` task locks; no broker or daemon to babysit
|
|
57
57
|
- **Chat bridge** — `omp gateway` runs long-lived chat connectors (Slack today, more next) so you can DM Copilot from anywhere
|
|
58
|
-
- **Lifecycle hooks** — `
|
|
58
|
+
- **Lifecycle hooks** — `sessionStart`, `userPromptSubmitted`, `preToolUse`, `postToolUse`, `postToolUseFailure`, `sessionEnd`, `errorOccurred`
|
|
59
59
|
- **Doctor included** — `omp doctor` verifies plugin manifest, skills discovery, hooks, and the underlying `copilot` CLI in one shot
|
|
60
60
|
|
|
61
61
|
---
|
|
@@ -84,6 +84,7 @@ That's it.
|
|
|
84
84
|
|
|
85
85
|
- **Context & history as CLI subcommands** — `omp state` (key-value with TTL), `omp project-memory` (notes + directives), `omp trace` (per-session timeline + summary), `omp goal` / `omp memory sync` (managed repo context), `omp daily-log`
|
|
86
86
|
- **Lightweight Copilot context** — managed instructions keep only the repo goal plus on-demand memory commands; set `OMP_DISABLE_INSTRUCTIONS_MEMORY=1` to skip writing the managed block entirely
|
|
87
|
+
- **Estimated cost ledger** — `omp cost [--today] [--session <id>]` summarizes local prompt/tool token estimates recorded by hooks. These are best-effort estimates, not provider billing.
|
|
87
88
|
- **File-state worker coordination** — outbox JSONL + byte cursor, atomic `O_EXCL` task locks, optimistic CAS on claim
|
|
88
89
|
- **Idle nudge** — content-based pane idle detection that pokes stuck workers
|
|
89
90
|
- **Mode-state loops** — single source of truth per loop (Ralph/Ultrawork/UltraQA state files)
|
|
@@ -195,6 +196,7 @@ omp ralph start "<task>" [--max-iterations N]
|
|
|
195
196
|
omp ultrawork start "<objective>" [--task-count N]
|
|
196
197
|
omp ultraqa start "<goal>" [--max-cycles N]
|
|
197
198
|
omp council "<question>" [--models a,b,c] [--context @file] [--json] # multi-model council
|
|
199
|
+
omp cost [--today] [--session <id>] # summarize estimated hook-ledger tokens
|
|
198
200
|
omp comms status | send | recv | ask # drive a running copilot tmux session
|
|
199
201
|
omp gateway serve [--only slack] # run chat connectors (today: slack)
|
|
200
202
|
omp gateway status [--json] # per-connector readiness (no sockets)
|
|
@@ -272,6 +274,7 @@ omp grows in vertical slices. Items aren't pinned to specific semver versions
|
|
|
272
274
|
- **Slack outbound — `omp gateway notify`** — stateless REST `chat.postMessage` from any process (cron `--notify-target`, in-session `/slack <message>`, ad-hoc `omp gateway notify --text "..."`). Default destination from `SLACK_HOME_CHANNEL`; explicit `--target slack:C…/G…/D…/U…` overrides; `U…` auto-resolves to a DM via `conversations.open`.
|
|
273
275
|
- **Weighted-consensus council** — multi-model council with role weights + minority report. Via `omp council` or `/weighted-consensus`.
|
|
274
276
|
- **Suggest** — `omp suggest "<task>"` recommends a slash-skill workflow without launching one.
|
|
277
|
+
- **Estimated cost ledger** — hook-driven prompt/tool token estimates are visible through `omp cost`; oversized `postToolUse` output is minimized before it re-enters model context, with raw output preserved on disk (diagnostics kept inline; other trimmed detail is recoverable by re-reading the raw path the hook reports — an extra tool call, so best with capable models). Budget gates and retry-cost guidance remain next-step optimization work, not shipped behavior yet.
|
|
275
278
|
|
|
276
279
|
### Up next
|
|
277
280
|
|
package/dist/src/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ function printResult(result, json) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
function help() {
|
|
29
|
-
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 comms status [--session <name>] [--json] (is copilot on + online? auto-discovers session)\n comms send --text "<prompt>" [--force] [--session <name>] [--json]\n comms recv [--wait] [--lines <n>] [--timeout <ms>] [--session <name>] [--json]\n comms ask --text "<prompt>" [--force] [--lines <n>] [--timeout <ms>] [--session <name>] [--json]\n gateway serve [--only <name>[,<name>]] (run all configured connectors; today: slack)\n gateway status [--json] [--only <name>[,...]] (per-connector readiness; no sockets opened)\n gateway doctor [--json] [--only <name>[,...]] (alias for 'gateway status')\n gateway notify --text "<msg>" [--target slack:C\\|D\\|G\\|U... [:thread_ts]] [--thread-ts <ts>] [--json]\n (one-shot outbound Slack post; falls back to SLACK_HOME_CHANNEL)\n slack serve (deprecated alias for 'gateway serve --only slack')\n slack doctor [--json] (deprecated alias for 'gateway status --only slack')\n env init [--force] (interactive: write ~/.omp/.env with Slack tokens + optional SLACK_HOME_CHANNEL)\n non-interactive: set OMP_INIT_BOT_TOKEN/OMP_INIT_APP_TOKEN/OMP_INIT_HOME_CHANNEL\n (env vars preferred over --bot-token/--app-token/--home-channel flags)\n (--session is optional when exactly one omp-<digits> tmux session is running)\n${registeredCommandHelpLines().join("\n")}\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 schedule add --id <id> --cron "<expr>" --prompt "<text>" [--bin copilot] [--model <m>] [--cwd <dir>] [--timeout <ms>] [--max-runs <n>] [--ttl-hours <h>] [--allow-all-tools] [--notify-target slack:<ID>] [--dry-run] [--json]\n schedule list [--json]\n schedule status <id> [--json]\n schedule run-now <id> [--json]\n schedule remove <id> [--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`;
|
|
29
|
+
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] [--hooks]\n cost [--json] [--session <id>] [--days <n>]\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 comms status [--session <name>] [--json] (is copilot on + online? auto-discovers session)\n comms send --text "<prompt>" [--force] [--session <name>] [--json]\n comms recv [--wait] [--lines <n>] [--timeout <ms>] [--session <name>] [--json]\n comms ask --text "<prompt>" [--force] [--lines <n>] [--timeout <ms>] [--session <name>] [--json]\n gateway serve [--only <name>[,<name>]] (run all configured connectors; today: slack)\n gateway status [--json] [--only <name>[,...]] (per-connector readiness; no sockets opened)\n gateway doctor [--json] [--only <name>[,...]] (alias for 'gateway status')\n gateway notify --text "<msg>" [--target slack:C\\|D\\|G\\|U... [:thread_ts]] [--thread-ts <ts>] [--json]\n (one-shot outbound Slack post; falls back to SLACK_HOME_CHANNEL)\n slack serve (deprecated alias for 'gateway serve --only slack')\n slack doctor [--json] (deprecated alias for 'gateway status --only slack')\n env init [--force] (interactive: write ~/.omp/.env with Slack tokens + optional SLACK_HOME_CHANNEL)\n non-interactive: set OMP_INIT_BOT_TOKEN/OMP_INIT_APP_TOKEN/OMP_INIT_HOME_CHANNEL\n (env vars preferred over --bot-token/--app-token/--home-channel flags)\n (--session is optional when exactly one omp-<digits> tmux session is running)\n${registeredCommandHelpLines().join("\n")}\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 schedule add --id <id> --cron "<expr>" --prompt "<text>" [--bin copilot] [--model <m>] [--cwd <dir>] [--timeout <ms>] [--max-runs <n>] [--ttl-hours <h>] [--allow-all-tools] [--notify-target slack:<ID>] [--dry-run] [--json]\n schedule list [--json]\n schedule status <id> [--json]\n schedule run-now <id> [--json]\n schedule remove <id> [--json]\n goal set "<objective>" [--json]\n goal read [--json]\n memory sync [--json] (render goal+directives into copilot-instructions.md)\n config get [--json] | config set memory-mode on|off | config set memory-review-model <slug> | config set memory-review-min-messages <n> [--global]\n (--global writes ~/.omp/config.json; applies to every project. project .omp/config.json overrides it)\n memory-review --session <uuid|latest> [--model <slug>] [--json] (cheap-model end-of-session review; opt-in via memory-mode)\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>" | prune-notes --keep <n>|--older-than <days> [--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`;
|
|
30
30
|
}
|
|
31
31
|
async function resolveExistingInputPath(value) {
|
|
32
32
|
const { existsSync } = await import("node:fs");
|
|
@@ -40,6 +40,33 @@ async function resolveExistingInputPath(value) {
|
|
|
40
40
|
return direct;
|
|
41
41
|
}
|
|
42
42
|
const BARE_LAUNCH_FLAGS = new Set(["--madmax", "--yolo"]);
|
|
43
|
+
// Snapshot existing Copilot session dirs BEFORE launch, so after a headless run
|
|
44
|
+
// we can identify the exact session it created (instead of guessing "latest").
|
|
45
|
+
async function snapshotSessionsForReview() {
|
|
46
|
+
try {
|
|
47
|
+
const { listSessionIds } = await import("./memory-review/transcript.js");
|
|
48
|
+
return listSessionIds();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Headless `copilot -p` skips hooks, so when omp launched copilot in headless
|
|
55
|
+
// mode we detach the end-of-session memory review here. We review the session
|
|
56
|
+
// that appeared since `before` — never a guessed "latest" — and skip if none
|
|
57
|
+
// is identifiable. Best-effort: a failure here must never affect the launch.
|
|
58
|
+
async function maybeTriggerHeadlessReview(argv, cwd, before) {
|
|
59
|
+
try {
|
|
60
|
+
const { triggerHeadlessReview } = await import("./memory-review/trigger.js");
|
|
61
|
+
const { newestSessionSince } = await import("./memory-review/transcript.js");
|
|
62
|
+
const cliPath = join(packageRootFromImportMeta(import.meta.url), "dist", "src", "cli.js");
|
|
63
|
+
const sessionId = newestSessionSince(before) ?? "";
|
|
64
|
+
triggerHeadlessReview({ cwd, argv, cliPath, sessionId });
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// never fail a launch on the review trigger
|
|
68
|
+
}
|
|
69
|
+
}
|
|
43
70
|
export async function runCli(argv = process.argv.slice(2)) {
|
|
44
71
|
const [group, command, value] = argv;
|
|
45
72
|
const json = hasFlag(argv, "--json");
|
|
@@ -70,11 +97,14 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
70
97
|
// case argv is empty, so normalizeCopilotLaunchArgs emits no --yolo.
|
|
71
98
|
if (!group || BARE_LAUNCH_FLAGS.has(group)) {
|
|
72
99
|
const { launchCopilot } = await import("./copilot/launch.js");
|
|
100
|
+
const launchCwd = flagValue(argv, "--root") ?? process.cwd();
|
|
101
|
+
const beforeSessions = await snapshotSessionsForReview();
|
|
73
102
|
const result = await launchCopilot({
|
|
74
103
|
args: argv,
|
|
75
104
|
bin: flagValue(argv, "--bin"),
|
|
76
|
-
cwd:
|
|
105
|
+
cwd: launchCwd,
|
|
77
106
|
});
|
|
107
|
+
await maybeTriggerHeadlessReview(argv, launchCwd, beforeSessions);
|
|
78
108
|
return json
|
|
79
109
|
? { ok: result.ok, exitCode: result.exitCode, output: result }
|
|
80
110
|
: {
|
|
@@ -107,20 +137,32 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
107
137
|
importMetaUrl: import.meta.url,
|
|
108
138
|
copilotBin: flagValue(argv, "--copilot-bin"),
|
|
109
139
|
skipCopilot: hasFlag(argv, "--skip-copilot"),
|
|
140
|
+
checkHooks: hasFlag(argv, "--hooks"),
|
|
110
141
|
});
|
|
111
142
|
return json
|
|
112
143
|
? { ok: report.ok, exitCode: report.ok ? 0 : 1, output: report }
|
|
113
144
|
: { ok: report.ok, exitCode: report.ok ? 0 : 1, message: formatDoctor(report) };
|
|
114
145
|
}
|
|
146
|
+
if (group === "cost") {
|
|
147
|
+
const { summarizeCost, formatCostSummary } = await import("./cost/summary.js");
|
|
148
|
+
const summary = summarizeCost(flagValue(argv, "--root") ?? process.cwd(), {
|
|
149
|
+
sessionId: flagValue(argv, "--session"),
|
|
150
|
+
today: hasFlag(argv, "--today"),
|
|
151
|
+
});
|
|
152
|
+
return json ? { ok: true, output: summary } : { ok: true, message: formatCostSummary(summary) };
|
|
153
|
+
}
|
|
115
154
|
if (group === "launch") {
|
|
116
155
|
const dashIndex = argv.indexOf("--");
|
|
117
156
|
const passthrough = dashIndex >= 0 ? argv.slice(dashIndex + 1) : argv.slice(1);
|
|
118
157
|
const { launchCopilot } = await import("./copilot/launch.js");
|
|
158
|
+
const launchCwd = flagValue(argv, "--root") ?? process.cwd();
|
|
159
|
+
const beforeSessions = await snapshotSessionsForReview();
|
|
119
160
|
const result = await launchCopilot({
|
|
120
161
|
args: passthrough,
|
|
121
162
|
bin: flagValue(argv, "--bin"),
|
|
122
|
-
cwd:
|
|
163
|
+
cwd: launchCwd,
|
|
123
164
|
});
|
|
165
|
+
await maybeTriggerHeadlessReview(passthrough, launchCwd, beforeSessions);
|
|
124
166
|
return json
|
|
125
167
|
? { ok: result.ok, exitCode: result.exitCode, output: result }
|
|
126
168
|
: {
|
|
@@ -192,6 +234,80 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
192
234
|
}
|
|
193
235
|
return { ok: false, exitCode: 1, message: "Unknown memory subcommand. Try: memory sync" };
|
|
194
236
|
}
|
|
237
|
+
if (group === "config") {
|
|
238
|
+
const { readMemoryConfig, setMemoryConfigValue } = await import("./memory-review/config.js");
|
|
239
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
240
|
+
// OMP_HOME_OVERRIDE relocates the global ~/.omp config dir (test seam; also
|
|
241
|
+
// lets users point at a custom home). Undefined => os.homedir() default.
|
|
242
|
+
const homeDir = process.env.OMP_HOME_OVERRIDE || undefined;
|
|
243
|
+
// `--global` writes to ~/.omp/config.json so the setting applies everywhere.
|
|
244
|
+
const scope = hasFlag(argv, "--global") ? "global" : "project";
|
|
245
|
+
if (command === "get" || command === undefined) {
|
|
246
|
+
const cfg = readMemoryConfig(cwd, { homeDir });
|
|
247
|
+
return json
|
|
248
|
+
? { ok: true, output: cfg }
|
|
249
|
+
: {
|
|
250
|
+
ok: true,
|
|
251
|
+
message: `memory-mode=${cfg.memoryMode}\nmemory-review-model=${cfg.memoryReviewModel}\nmemory-review-min-messages=${cfg.memoryReviewMinMessages}`,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (command === "set") {
|
|
255
|
+
const setVal = argv[3];
|
|
256
|
+
const where = scope === "global" ? " (global ~/.omp)" : "";
|
|
257
|
+
if (value === "memory-mode") {
|
|
258
|
+
if (setVal !== "on" && setVal !== "off") {
|
|
259
|
+
return { ok: false, exitCode: 1, message: "usage: omp config set memory-mode on|off [--global]" };
|
|
260
|
+
}
|
|
261
|
+
setMemoryConfigValue(cwd, "memoryMode", setVal, { scope, homeDir });
|
|
262
|
+
return json ? { ok: true, output: { memoryMode: setVal, scope } } : { ok: true, message: `memory-mode=${setVal}${where}` };
|
|
263
|
+
}
|
|
264
|
+
if (value === "memory-review-model") {
|
|
265
|
+
if (!setVal || setVal.startsWith("-")) {
|
|
266
|
+
return { ok: false, exitCode: 1, message: "usage: omp config set memory-review-model <slug> [--global]" };
|
|
267
|
+
}
|
|
268
|
+
setMemoryConfigValue(cwd, "memoryReviewModel", setVal, { scope, homeDir });
|
|
269
|
+
return json ? { ok: true, output: { memoryReviewModel: setVal, scope } } : { ok: true, message: `memory-review-model=${setVal}${where}` };
|
|
270
|
+
}
|
|
271
|
+
if (value === "memory-review-min-messages") {
|
|
272
|
+
const n = Number(setVal);
|
|
273
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
274
|
+
return { ok: false, exitCode: 1, message: "usage: omp config set memory-review-min-messages <non-negative integer> [--global]" };
|
|
275
|
+
}
|
|
276
|
+
setMemoryConfigValue(cwd, "memoryReviewMinMessages", String(Math.floor(n)), { scope, homeDir });
|
|
277
|
+
return json ? { ok: true, output: { memoryReviewMinMessages: Math.floor(n), scope } } : { ok: true, message: `memory-review-min-messages=${Math.floor(n)}${where}` };
|
|
278
|
+
}
|
|
279
|
+
return { ok: false, exitCode: 1, message: "Unknown config key. Try: memory-mode | memory-review-model | memory-review-min-messages" };
|
|
280
|
+
}
|
|
281
|
+
return { ok: false, exitCode: 1, message: "Unknown config subcommand. Try: config get | config set <key> <value>" };
|
|
282
|
+
}
|
|
283
|
+
if (group === "memory-review") {
|
|
284
|
+
const { runMemoryReview } = await import("./memory-review/index.js");
|
|
285
|
+
const { createReviewSpawn } = await import("./memory-review/spawn.js");
|
|
286
|
+
const { isValidSessionId, latestSessionId } = await import("./memory-review/transcript.js");
|
|
287
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
288
|
+
let sessionId = flagValue(argv, "--session") ?? "";
|
|
289
|
+
if (!sessionId || sessionId === "latest") {
|
|
290
|
+
sessionId = latestSessionId() ?? "";
|
|
291
|
+
}
|
|
292
|
+
if (!sessionId) {
|
|
293
|
+
return { ok: false, exitCode: 1, message: "usage: omp memory-review --session <uuid|latest>" };
|
|
294
|
+
}
|
|
295
|
+
if (!isValidSessionId(sessionId)) {
|
|
296
|
+
return { ok: false, exitCode: 1, message: "invalid --session id" };
|
|
297
|
+
}
|
|
298
|
+
const res = await runMemoryReview({
|
|
299
|
+
cwd,
|
|
300
|
+
sessionId,
|
|
301
|
+
spawn: createReviewSpawn(flagValue(argv, "--bin")),
|
|
302
|
+
model: flagValue(argv, "--model"),
|
|
303
|
+
});
|
|
304
|
+
return json
|
|
305
|
+
? { ok: true, output: res }
|
|
306
|
+
: {
|
|
307
|
+
ok: true,
|
|
308
|
+
message: res.ran ? `memory-review ran: ${JSON.stringify(res.summary)}` : `memory-review skipped: ${res.reason}`,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
195
311
|
if (group === "daily-log") {
|
|
196
312
|
const { setDailyGoal, addLogEntry, readDailyLog, pruneDailyLog } = await import("./daily-log.js");
|
|
197
313
|
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
@@ -304,6 +420,20 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
304
420
|
if (command === "index") {
|
|
305
421
|
return { ok: true, output: { notes: pm.noteIndex(cwd) } };
|
|
306
422
|
}
|
|
423
|
+
if (command === "prune-notes") {
|
|
424
|
+
const keepRaw = flagValue(argv, "--keep");
|
|
425
|
+
const olderRaw = flagValue(argv, "--older-than");
|
|
426
|
+
const keep = keepRaw !== undefined && Number.isFinite(Number(keepRaw)) ? Number(keepRaw) : undefined;
|
|
427
|
+
const olderThanDays = olderRaw !== undefined && Number.isFinite(Number(olderRaw)) ? Number(olderRaw) : undefined;
|
|
428
|
+
if (keep === undefined && olderThanDays === undefined) {
|
|
429
|
+
return { ok: false, exitCode: 1, message: "usage: omp project-memory prune-notes --keep <n> | --older-than <days>" };
|
|
430
|
+
}
|
|
431
|
+
const removed = pm.pruneNotes(cwd, { keep, olderThanDays });
|
|
432
|
+
syncInstructionsMemory(cwd); // refresh the managed block after pruning
|
|
433
|
+
return json
|
|
434
|
+
? { ok: true, output: { removed } }
|
|
435
|
+
: { ok: true, message: `pruned ${removed.length} note${removed.length === 1 ? "" : "s"}` };
|
|
436
|
+
}
|
|
307
437
|
if (command === "read" || command === undefined) {
|
|
308
438
|
// `read <id>` loads one note's body on demand; bare `read` returns the
|
|
309
439
|
// bounded summary (directives + note index — never note bodies).
|
|
@@ -318,7 +448,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
318
448
|
return {
|
|
319
449
|
ok: false,
|
|
320
450
|
exitCode: 1,
|
|
321
|
-
message: 'Unknown project-memory subcommand. Try: project-memory read [<id>] | index | add-note "<title>" [--body "<text>"] | add-directive "<rule>"',
|
|
451
|
+
message: 'Unknown project-memory subcommand. Try: project-memory read [<id>] | index | add-note "<title>" [--body "<text>"] | add-directive "<rule>" | prune-notes --keep <n>|--older-than <days>',
|
|
322
452
|
};
|
|
323
453
|
}
|
|
324
454
|
if (group === "trace") {
|