@damian87/omp 0.8.0 → 0.9.2

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 (66) hide show
  1. package/.github/skills/slack/SKILL.md +82 -0
  2. package/README.md +36 -38
  3. package/catalog/capabilities.json +46 -0
  4. package/catalog/skills-general.json +26 -0
  5. package/dist/src/cli.js +58 -5
  6. package/dist/src/cli.js.map +1 -1
  7. package/dist/src/copilot/version.js +10 -0
  8. package/dist/src/copilot/version.js.map +1 -1
  9. package/dist/src/env/init.d.ts +6 -0
  10. package/dist/src/env/init.js +18 -0
  11. package/dist/src/env/init.js.map +1 -1
  12. package/dist/src/gateway/notify.d.ts +35 -0
  13. package/dist/src/gateway/notify.js +261 -0
  14. package/dist/src/gateway/notify.js.map +1 -0
  15. package/dist/src/gateway/target-parser.d.ts +76 -0
  16. package/dist/src/gateway/target-parser.js +105 -0
  17. package/dist/src/gateway/target-parser.js.map +1 -0
  18. package/dist/src/schedule/commands.js +1 -0
  19. package/dist/src/schedule/commands.js.map +1 -1
  20. package/dist/src/schedule/runner.d.ts +9 -0
  21. package/dist/src/schedule/runner.js +31 -1
  22. package/dist/src/schedule/runner.js.map +1 -1
  23. package/dist/src/schedule/types.d.ts +9 -0
  24. package/docs/slack-setup.md +33 -0
  25. package/package.json +11 -3
  26. package/plugin.json +12 -4
  27. package/scripts/lib/version-check.mjs +3 -0
  28. package/dist/src/mcp/server.d.ts +0 -10
  29. package/dist/src/mcp/server.js +0 -44
  30. package/dist/src/mcp/server.js.map +0 -1
  31. package/dist/src/mcp/tools/daily-log.d.ts +0 -2
  32. package/dist/src/mcp/tools/daily-log.js +0 -148
  33. package/dist/src/mcp/tools/daily-log.js.map +0 -1
  34. package/dist/src/mcp/tools/index.d.ts +0 -9
  35. package/dist/src/mcp/tools/index.js +0 -15
  36. package/dist/src/mcp/tools/index.js.map +0 -1
  37. package/dist/src/mcp/tools/notepad.d.ts +0 -2
  38. package/dist/src/mcp/tools/notepad.js +0 -135
  39. package/dist/src/mcp/tools/notepad.js.map +0 -1
  40. package/dist/src/mcp/tools/project-memory.d.ts +0 -2
  41. package/dist/src/mcp/tools/project-memory.js +0 -91
  42. package/dist/src/mcp/tools/project-memory.js.map +0 -1
  43. package/dist/src/mcp/tools/shared-memory.d.ts +0 -2
  44. package/dist/src/mcp/tools/shared-memory.js +0 -148
  45. package/dist/src/mcp/tools/shared-memory.js.map +0 -1
  46. package/dist/src/mcp/tools/state.d.ts +0 -2
  47. package/dist/src/mcp/tools/state.js +0 -107
  48. package/dist/src/mcp/tools/state.js.map +0 -1
  49. package/dist/src/mcp/tools/trace.d.ts +0 -10
  50. package/dist/src/mcp/tools/trace.js +0 -102
  51. package/dist/src/mcp/tools/trace.js.map +0 -1
  52. package/dist/src/mcp/types.d.ts +0 -29
  53. package/dist/src/mcp/types.js +0 -7
  54. package/dist/src/mcp/types.js.map +0 -1
  55. package/dist/test/catalog.test.d.ts +0 -1
  56. package/dist/test/catalog.test.js +0 -21
  57. package/dist/test/catalog.test.js.map +0 -1
  58. package/dist/test/jira.test.d.ts +0 -1
  59. package/dist/test/jira.test.js +0 -26
  60. package/dist/test/jira.test.js.map +0 -1
  61. package/dist/test/lint.test.d.ts +0 -1
  62. package/dist/test/lint.test.js +0 -9
  63. package/dist/test/lint.test.js.map +0 -1
  64. package/dist/test/sync.test.d.ts +0 -1
  65. package/dist/test/sync.test.js +0 -15
  66. package/dist/test/sync.test.js.map +0 -1
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: slack
3
+ description: Post an outbound Slack notification — ONLY when the user types the explicit `/slack` slash command. Never auto-trigger from natural-language phrases like "tell Slack" or "notify me on Slack"; those go to the regular chat. One-way (publish only); the v0.8.0 gateway bridge still handles bidirectional DM chat.
4
+ argument-hint: "<message> [--target slack:C…|D…|G…|U…]"
5
+ ---
6
+
7
+ # /slack — outbound Slack notification
8
+
9
+ Post a single message to Slack and exit. **No conversation; no listener.** The
10
+ v0.8.0 chat bridge (`omp gateway serve` + `@omp-copilot` DM) handles bidirectional
11
+ chat; this skill is for one-shot publishes.
12
+
13
+ ## Activation rules (read first)
14
+
15
+ - **Activate ONLY** when the user typed the literal slash command `/slack` (with or
16
+ without arguments). Never auto-activate from natural-language phrases like "tell
17
+ Slack X", "ping me on Slack", "notify me on Slack" — those are conversational.
18
+ - **Confirm the destination before posting**, every time:
19
+ - If `--target` is present, echo it back: "Sending to `slack:<ID>` — confirm? (y/N)"
20
+ - If `--target` is absent AND `SLACK_HOME_CHANNEL` is a channel (`C…`/`G…`):
21
+ treat as a potential broadcast. Ask: "Post to default channel `<id>` (a channel,
22
+ not a DM)? (y/N)" — only proceed on explicit `y`.
23
+ - If `SLACK_HOME_CHANNEL` is a user (`U…`) or DM (`D…`), proceed without
24
+ confirmation — the user already configured a personal default.
25
+ - If the user types just `/slack` with no message, ask: *"What should I send and where?"*.
26
+
27
+ ## Contract
28
+
29
+ - Default target = `SLACK_HOME_CHANNEL` from `~/.omp/.env` (set via `omp env init`).
30
+ - Explicit target = `--target slack:<ID>` (ID = `C…`/`G…`/`D…`/`U…`).
31
+ - `U…` targets are auto-resolved to a DM channel (`conversations.open`) before posting.
32
+ - Stateless: each invocation is a fresh REST call to `chat.postMessage`. No daemon. No socket.
33
+
34
+ ## When invoked
35
+
36
+ Follow the **Activation rules** above. After confirmation passes, post the message.
37
+
38
+ ## How
39
+
40
+ Run via the omp CLI:
41
+
42
+ ```
43
+ omp gateway notify --text "<message>" [--target slack:<ID>] [--thread-ts <ts>]
44
+ ```
45
+
46
+ That's the only command this skill drives. The flags map 1:1 to user intent:
47
+
48
+ - No flags after `--text`: post to `SLACK_HOME_CHANNEL`.
49
+ - `--target slack:C0BOQV5434G`: explicit channel.
50
+ - `--target slack:U0123ABCD`: explicit user (auto-DM).
51
+ - `--target slack:C0…:1700.000123` or `--thread-ts 1700.000123`: pin to a thread.
52
+
53
+ ## Output handling
54
+
55
+ The CLI returns a structured result:
56
+
57
+ - Success: `posted to <channel> (ts=<ts>[, opened IM])` — relay that.
58
+ - Failure: `notify failed [<CODE>]: <reason>` — surface the code + reason so the user
59
+ can act. Common codes:
60
+ - `MISSING_TOKEN` — run `omp env init` to set `SLACK_BOT_TOKEN`.
61
+ - `MISSING_TARGET` — pass `--target` or run `omp env init` to set `SLACK_HOME_CHANNEL`.
62
+ - `BAD_TARGET` / `BAD_HOME_CHANNEL` — the ID is malformed; show the user the expected shape.
63
+ - `OPEN_FAILED` (user-id targets) — bot can't DM that user (likely missing `im:write` scope or user outside workspace).
64
+ - `POST_FAILED` — Slack returned `ok: false`; the reason field has Slack's own error name (`channel_not_found`, `not_in_channel`, etc.).
65
+ - `RATE_LIMITED` / `TIMEOUT` / `NETWORK_ERROR` — transient; try again.
66
+
67
+ ## Boundaries
68
+
69
+ - This skill **never** waits for a reply. For interactive chat, the user opens Slack and DMs `@omp-copilot` (handled by `omp gateway serve`).
70
+ - This skill **never** stores state. There is no routing table, no per-session registration.
71
+ - This skill **does not** start the v0.8.0 inbound bridge. Daemons stay out of scope.
72
+
73
+ ## Example exchanges
74
+
75
+ User: `/slack the migration just finished cleanly`
76
+ Tool call: `omp gateway notify --text "the migration just finished cleanly"`
77
+
78
+ User: `/slack tell #releases the build is green --target slack:C0RELEASE9`
79
+ Tool call: `omp gateway notify --text "the build is green" --target slack:C0RELEASE9`
80
+
81
+ User: `/slack ping U0123ABCD with "deploy needs review"`
82
+ Tool call: `omp gateway notify --text "deploy needs review" --target slack:U0123ABCD`
package/README.md CHANGED
@@ -53,7 +53,8 @@ That's it.
53
53
  - **Team-first orchestration** — parallel tmux panes, each running an independent agent session
54
54
  - **Bare-flag bypass** — `omp --madmax` injects `--yolo` so non-interactive runs never block on a permission prompt
55
55
  - **Persistent execution** — Ralph, UltraQA, and Ultrawork keep going until the goal is verified
56
- - **MCP-powered shared state** — workers swap typed messages over an outbox/inbox cursor instead of summarising each other's summaries
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
+ - **Chat bridge** — `omp gateway` runs long-lived chat connectors (Slack today, more next) so you can DM Copilot from anywhere
57
58
  - **Lifecycle hooks** — `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `SessionEnd`, `Error`
58
59
  - **Doctor included** — `omp doctor` verifies plugin manifest, skills discovery, hooks, and the underlying `copilot` CLI in one shot
59
60
 
@@ -76,14 +77,14 @@ That's it.
76
77
  ### Intelligent Orchestration
77
78
 
78
79
  - **7 specialized agents** — planner, architect, executor, verifier, code-reviewer, designer, researcher (all `--agent <name>` compatible with Copilot CLI)
79
- - **19 in-session skills** auto-discovered from `.github/skills/`
80
+ - **22 in-session skills** auto-discovered from `.github/skills/`
80
81
  - **Smart pipeline routing** — `/research-codebase` → `/ralplan` → `/team` / `/ralph` / `/ultrawork` → `/code-review` → `/ultraqa`
81
82
 
82
83
  ### Developer Experience
83
84
 
84
- - **MCP server** ships with `notepad`, `project-memory`, `shared-memory`, `state`, and `trace` tools out of the box
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`
85
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
86
- - **File-state coordination** — outbox JSONL + byte cursor, atomic `O_EXCL` task locks, optimistic CAS on claim
87
+ - **File-state worker coordination** — outbox JSONL + byte cursor, atomic `O_EXCL` task locks, optimistic CAS on claim
87
88
  - **Idle nudge** — content-based pane idle detection that pokes stuck workers
88
89
  - **Mode-state loops** — single source of truth per loop (Ralph/Ultrawork/UltraQA state files)
89
90
 
@@ -115,6 +116,8 @@ These run **inside a Copilot CLI session** after the plugin is installed.
115
116
  | `/caveman` | Ultra-compressed communication mode | `/caveman` |
116
117
  | `/worktree` | Git worktree-based parallel branch work | `/worktree` |
117
118
  | `/schedule` | Durable local cron job — re-runs a prompt on a schedule, survives reboot | `/schedule "check the PR every 15 min"` |
119
+ | `/goal` | Set/read the repo-level goal injected into the managed Copilot context | `/goal "ship v1.0 of the billing flow"` |
120
+ | `/daily-log` | Per-day goal + work log surfaced at the start of new sessions | `/daily-log "ratelimit refactor landed"` |
118
121
 
119
122
  ---
120
123
 
@@ -195,15 +198,22 @@ omp council "<question>" [--models a,b,c] [--context @file] [--json] # multi-m
195
198
  omp comms status | send | recv | ask # drive a running copilot tmux session
196
199
  omp gateway serve [--only slack] # run chat connectors (today: slack)
197
200
  omp gateway status [--json] # per-connector readiness (no sockets)
201
+ omp gateway notify --text "<msg>" [--target slack:C…|G…|D…|U… [:thread_ts]] [--thread-ts <ts>] [--json]
202
+ # one-shot outbound Slack post; falls back to SLACK_HOME_CHANNEL
198
203
  omp slack serve # deprecated alias of `gateway serve --only slack`
199
204
  omp slack doctor [--json] # deprecated alias of `gateway status --only slack`
200
205
  omp env init [--force] # write ~/.omp/.env (interactive Slack token setup)
201
- omp schedule add --id <id> --cron "*/15 * * * *" --prompt "<text>" [--allow-all-tools] [--cwd <dir>] [--model <m>] [--timeout <ms>] [--max-runs N] [--ttl-hours H] [--dry-run]
206
+ omp schedule add --id <id> --cron "*/15 * * * *" --prompt "<text>" [--allow-all-tools] [--cwd <dir>] [--model <m>] [--timeout <ms>] [--max-runs N] [--ttl-hours H] [--notify-target slack:U0123ABCD] [--dry-run]
202
207
  omp schedule list # registered jobs + OS-install status
203
208
  omp schedule status <id> # last run + result summary
204
209
  omp schedule run-now <id> # trigger one run immediately
205
210
  omp schedule remove <id> # uninstall the OS entry + delete the job
206
- omp mcp # MCP server over stdio
211
+ omp goal set "<objective>" | read [--json]
212
+ omp memory sync [--json] # render goal + directives into copilot-instructions.md
213
+ omp daily-log set-goal "<text>" | add "<text>" | read [--days N] | prune [--keep-days N] [--json]
214
+ omp state write <key> <val> [--ttl <s>] | read | delete | status <key> | list | cleanup [--json]
215
+ omp project-memory read [<id>] | index | add-note "<title>" [--body "<text>"] | add-directive "<rule>" [--json]
216
+ omp trace timeline [<sessionId>] [--limit N] | summary [<sessionId>] | add <sessionId> <event> [<json>] [--json]
207
217
  omp catalog list | validate | capability <id>
208
218
  omp jira render <plan-file>
209
219
  omp jira apply <key-or-plan> --comment|--update|--transition|--link
@@ -214,6 +224,7 @@ Environment overrides:
214
224
  - `OMP_PLUGIN_ROOT` — path to the plugin checkout (with `OMC_PLUGIN_ROOT` accepted for back-compat)
215
225
  - `OMP_COPILOT_BIN` — alternate `copilot` binary
216
226
  - `OMP_BIN` — absolute path to the `omp` wrapper written into OS-scheduler entries (overrides `which omp`)
227
+ - `OMP_SKIP_USER_ENV` — when `1`, skip auto-loading `~/.omp/.env` (useful for hermetic CI runs)
217
228
 
218
229
  **Scheduled jobs** register a durable per-job entry with the OS scheduler (macOS launchd,
219
230
  Linux systemd-user timers, or a managed `crontab` block as a cross-platform fallback) that
@@ -252,43 +263,29 @@ Full Slack-app setup (manifest + scopes) lives in [`docs/slack-setup.md`](docs/s
252
263
 
253
264
  ## Roadmap
254
265
 
255
- omp is intentionally small today and growing in vertical slices.
266
+ omp grows in vertical slices. Items aren't pinned to specific semver versions — they land when they're ready.
256
267
 
257
- ### v0.2 — Notification gateways
268
+ ### Already shipped
258
269
 
259
- Telegram, Discord, Slack, and generic webhook integration so long-running modes can ping you when they finish, fail, or stall. Tag with `--telegram` / `--discord` / `--slack` per invocation; configure once with `omp notify add`.
270
+ - **Scheduled tasks** (v0.6.0) durable local cron: `omp schedule add --id pr-watch --cron "*/15 * * * *" --prompt "…"` plus `/schedule` in-session. Each job registers an OS-scheduler entry (launchd / systemd-user / crontab fallback) that fires a fresh agent session, survives reboot, locks out overlap, and surfaces results at the next session start.
271
+ - **Chat bridge — Slack inbound** (v0.8.0) — `omp gateway` runs long-lived chat connectors that forward messages into a running Copilot tmux session and post replies back. Slack is the first connector (Socket Mode, no public URL). `omp env init` walks you through one-time token setup; tokens live in `~/.omp/.env` (auto-loaded on every invocation). See [`docs/slack-setup.md`](docs/slack-setup.md).
272
+ - **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
+ - **Weighted-consensus council** — multi-model council with role weights + minority report. Via `omp council` or `/weighted-consensus`.
274
+ - **Suggest** — `omp suggest "<task>"` recommends a slash-skill workflow without launching one.
260
275
 
261
- ### v0.3 — Checkpoints + rollback
276
+ ### Up next
262
277
 
263
- Auto-snapshot the working tree before any tool-driven file edit. `omp rollback [id]` to revert a checkpoint. A safety net for autonomous loops that go wide before they go right.
278
+ - **More chat connectors** Telegram, Discord, generic webhook on the same `omp gateway` runtime. One file per connector.
279
+ - **Outbound notifications** — long-running modes ping you when they finish, fail, or stall (`omp notify add` + per-invocation `--telegram`/`--discord`/`--slack` tags).
280
+ - **Checkpoints + rollback** — auto-snapshot the working tree before any tool-driven file edit; `omp rollback [id]` reverts a checkpoint. Safety net for autonomous loops.
281
+ - **Browser tool** — web search / page extraction / full automation (navigate, click, type, screenshot) for research skills that need fresh data instead of training-cutoff guesses.
264
282
 
265
- ### v0.4 — Provider advisor (`omp ask`)
283
+ ### Later
266
284
 
267
- One command to consult an alternate provider CLI (`claude`, `codex`, `gemini`) and save the response as a markdown artifact under `.omp/artifacts/ask/`. Same surface in-session via `/ask`.
268
-
269
- ### v0.5Scheduled tasks (shipped)
270
-
271
- Durable local cron: `omp schedule add --id pr-watch --cron "*/15 * * * *" --prompt "…"` plus `/schedule` in-session. Each job registers an OS-scheduler entry (launchd / systemd-user / crontab fallback) that fires a fresh agent session, survives reboot, locks out overlap, and surfaces results at the next session start. Follow-ups: natural-language cron parsing, notification-gateway delivery, pause/resume/edit, and an orphan-sweep (`omp schedule gc`).
272
-
273
- ### v0.6 — Browser tool (MCP)
274
-
275
- A first-class browser MCP tool: web search, page extraction, full automation (navigate, click, type, screenshot). For research skills that need fresh data instead of training-cutoff guesses.
276
-
277
- ### v0.7 — HUD-lite statusline
278
-
279
- Live orchestration metrics in the terminal: active mode, current task, worker count, tokens, cache hit rate, last error.
280
-
281
- ### v0.8 — Provider routing
282
-
283
- Fine-grained per-task provider selection — sorting, whitelists, priority ordering, cost-aware fallback. For mixed pipelines that want Opus for planning and Haiku for grunt work without manual model switching.
284
-
285
- ### v0.9 — Skill learning
286
-
287
- Extract repeating patterns from session transcripts into reusable skill files with strict quality gates. Auto-injects into context when relevant triggers fire.
288
-
289
- ### v1.0 — Pre-built agent templates
290
-
291
- One-shot deployable templates for common workflows: research, security audit, design-system migration, content automation. `omp template add <name>` drops a curated skill + agent pair into your project.
285
+ - **HUD-lite statusline** live orchestration metrics in the terminal: active mode, current task, worker count, tokens, cache hit rate, last error.
286
+ - **Provider routing** — fine-grained per-task provider selection: sorting, whitelists, priority ordering, cost-aware fallback. For mixed pipelines that want Opus for planning and Haiku for grunt work without manual model switching.
287
+ - **Skill learning** extract repeating patterns from session transcripts into reusable skill files with strict quality gates. Auto-injects into context when relevant triggers fire.
288
+ - **Pre-built agent templates** — one-shot deployable templates for common workflows (research, security audit, design-system migration, content automation). `omp template add <name>` drops a curated skill + agent pair into your project.
292
289
 
293
290
  ---
294
291
 
@@ -298,6 +295,7 @@ One-shot deployable templates for common workflows: research, security audit, de
298
295
  - [Copilot distribution](docs/copilot-distribution.md) — project/user skill installs and the case against GitHub App Extensions
299
296
  - [Jira adapter](docs/jira.md) — configuration discovery, safe operations, dry-runs, fallback payloads
300
297
  - [Self-evolve](docs/self-evolve.md) — extracting reusable skills from session transcripts
298
+ - [Slack setup](docs/slack-setup.md) — Slack app manifest, scopes, Socket-Mode token, `omp gateway serve`
301
299
 
302
300
  ## Layout
303
301
 
@@ -306,7 +304,7 @@ One-shot deployable templates for common workflows: research, security audit, de
306
304
  .github/skills/<name>/SKILL.md # in-session slash skills
307
305
  hooks/hooks.json # lifecycle hook manifest
308
306
  scripts/*.mjs # hook implementations
309
- src/ # omp CLI, MCP server, team runtime, mode-state loops
307
+ src/ # omp CLI, team runtime, gateway/comms, schedule, mode-state loops
310
308
  ```
311
309
 
312
310
  Skills follow the [Copilot agent-skills docs](https://docs.github.com/en/copilot) — project skills live in `.github/skills/` and are invoked with `/skill-name`.
@@ -862,6 +862,52 @@
862
862
  "notes": "Use /weighted-consensus from .github/skills/weighted-consensus/SKILL.md."
863
863
  }
864
864
  }
865
+ },
866
+ {
867
+ "id": "slack",
868
+ "name": "slack",
869
+ "title": "Slack Notification",
870
+ "category": "notify",
871
+ "summary": "Post an outbound Slack notification from inside a Copilot session.",
872
+ "notes": "Thin in-session wrapper over `omp gateway notify`. Stateless REST call. v0.8.0 inbound chat bridge unchanged.",
873
+ "defaultCommand": "slack",
874
+ "phase1": true,
875
+ "sourceSkill": "slack",
876
+ "providers": {
877
+ "copilot": "supported"
878
+ },
879
+ "support": {
880
+ "copilot": "native"
881
+ },
882
+ "providerSupport": {
883
+ "copilot": {
884
+ "state": "native",
885
+ "notes": "Use /slack from .github/skills/slack/SKILL.md."
886
+ }
887
+ }
888
+ },
889
+ {
890
+ "id": "notify.slack",
891
+ "name": "notify.slack",
892
+ "title": "Slack Notification",
893
+ "category": "notify",
894
+ "summary": "Alias capability id for `slack`.",
895
+ "notes": "Same skill as `slack`.",
896
+ "defaultCommand": "slack",
897
+ "phase1": true,
898
+ "sourceSkill": "slack",
899
+ "providers": {
900
+ "copilot": "supported"
901
+ },
902
+ "support": {
903
+ "copilot": "native"
904
+ },
905
+ "providerSupport": {
906
+ "copilot": {
907
+ "state": "native",
908
+ "notes": "Use /slack from .github/skills/slack/SKILL.md."
909
+ }
910
+ }
865
911
  }
866
912
  ]
867
913
  }
@@ -502,6 +502,32 @@
502
502
  },
503
503
  "projection": "project-skill",
504
504
  "phase1": true
505
+ },
506
+ {
507
+ "name": "slack",
508
+ "capabilityId": "slack",
509
+ "capabilityIds": [
510
+ "slack",
511
+ "notify.slack"
512
+ ],
513
+ "source": ".github/skills/slack/SKILL.md",
514
+ "sourcePath": ".github/skills/slack/SKILL.md",
515
+ "canonicalPath": ".github/skills/slack/SKILL.md",
516
+ "description": "Post an outbound Slack notification from inside a Copilot session.",
517
+ "summary": "Post an outbound Slack notification from inside a Copilot session.",
518
+ "support": "project-skill",
519
+ "aliases": [],
520
+ "slashCommands": [
521
+ "slack"
522
+ ],
523
+ "projections": {
524
+ "copilot": {
525
+ "command": "/slack",
526
+ "state": "supported"
527
+ }
528
+ },
529
+ "projection": "project-skill",
530
+ "phase1": true
505
531
  }
506
532
  ]
507
533
  }
package/dist/src/cli.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { realpathSync } from "node:fs";
3
+ import { join } from "node:path";
3
4
  import { pathToFileURL } from "node:url";
4
5
  import { findCapability, loadCatalogBundle, validateCatalogBundle } from "./catalog.js";
5
6
  import { findRegisteredCommand, registeredCommandHelpLines } from "./commands/registry.js";
6
7
  import { loadOmpEnv } from "./env/dotenv.js";
7
- import { inspectProject } from "./project.js";
8
+ import { ompRoot } from "./omp-root.js";
9
+ import { inspectProject, packageRootFromImportMeta } from "./project.js";
8
10
  function hasFlag(args, flag) {
9
11
  return args.includes(flag);
10
12
  }
@@ -24,7 +26,7 @@ function printResult(result, json) {
24
26
  }
25
27
  }
26
28
  function help() {
27
- 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 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)\n non-interactive: set OMP_INIT_BOT_TOKEN/OMP_INIT_APP_TOKEN\n (env vars preferred over --bot-token/--app-token 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] [--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]\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`;
28
30
  }
29
31
  async function resolveExistingInputPath(value) {
30
32
  const { existsSync } = await import("node:fs");
@@ -47,7 +49,17 @@ export async function runCli(argv = process.argv.slice(2)) {
47
49
  if (group === "version" || group === "--version" || group === "-v") {
48
50
  const { getVersionInfo, formatVersionInfo } = await import("./copilot/version.js");
49
51
  const info = getVersionInfo({ importMetaUrl: import.meta.url });
50
- return json ? { ok: true, output: info } : { ok: true, message: formatVersionInfo(info) };
52
+ const versionCheckUrl = pathToFileURL(join(packageRootFromImportMeta(import.meta.url), "scripts", "lib", "version-check.mjs")).href;
53
+ const { checkForUpdate, formatUpdateNotice } = (await import(versionCheckUrl));
54
+ const cwd = flagValue(argv, "--root") ?? process.cwd();
55
+ const update = await checkForUpdate({ stateDir: join(ompRoot(cwd), ".omp", "state") });
56
+ if (json) {
57
+ return { ok: true, output: { ...info, update } };
58
+ }
59
+ const message = update
60
+ ? `${formatVersionInfo(info)}\n\n${formatUpdateNotice(update.current, update.latest)}`
61
+ : formatVersionInfo(info);
62
+ return { ok: true, message };
51
63
  }
52
64
  // Auto-load ~/.omp/.env so subcommands that read process.env (slack tokens,
53
65
  // OMP_*, COPILOT_TMUX_SESSION, etc.) work from any cwd without `source .env`.
@@ -717,10 +729,12 @@ async function handleEnvCommand(argv, json) {
717
729
  const appEnv = process.env.OMP_INIT_APP_TOKEN;
718
730
  const sessionEnv = process.env.OMP_INIT_SESSION;
719
731
  const usersEnv = process.env.OMP_INIT_USERS;
732
+ const homeChannelEnv = process.env.OMP_INIT_HOME_CHANNEL;
720
733
  const botFlag = flagValue(argv, "--bot-token");
721
734
  const appFlag = flagValue(argv, "--app-token");
722
735
  const sessionFlag = flagValue(argv, "--session");
723
736
  const usersFlag = flagValue(argv, "--users");
737
+ const homeChannelFlag = flagValue(argv, "--home-channel");
724
738
  if ((botFlag !== undefined || appFlag !== undefined) && process.env.OMP_INIT_NO_WARN !== "1") {
725
739
  console.error("warning: --bot-token/--app-token leak via shell history and `ps`. " +
726
740
  "Prefer OMP_INIT_BOT_TOKEN / OMP_INIT_APP_TOKEN env vars (set OMP_INIT_NO_WARN=1 to silence).");
@@ -729,10 +743,15 @@ async function handleEnvCommand(argv, json) {
729
743
  const appToken = appEnv ?? appFlag;
730
744
  const session = sessionEnv ?? sessionFlag;
731
745
  const users = usersEnv ?? usersFlag;
746
+ const homeChannel = homeChannelEnv ?? homeChannelFlag;
732
747
  // Non-interactive when stdin isn't a TTY OR when any answer is already
733
748
  // supplied (via env or flag). Partial answers are allowed; missing required
734
749
  // ones will be rejected by runEnvInit's validator.
735
- const anyAnswer = botToken !== undefined || appToken !== undefined || session !== undefined || users !== undefined;
750
+ const anyAnswer = botToken !== undefined ||
751
+ appToken !== undefined ||
752
+ session !== undefined ||
753
+ users !== undefined ||
754
+ homeChannel !== undefined;
736
755
  const force = hasFlag(argv, "--force");
737
756
  let result;
738
757
  if (anyAnswer || !process.stdin.isTTY) {
@@ -751,6 +770,7 @@ async function handleEnvCommand(argv, json) {
751
770
  slackAppToken: appToken ?? "",
752
771
  copilotTmuxSession: session ?? "",
753
772
  slackAllowedUsers: users ?? "",
773
+ slackHomeChannel: homeChannel ?? "",
754
774
  },
755
775
  });
756
776
  }
@@ -876,12 +896,36 @@ async function handleGatewayCommand(argv, json) {
876
896
  ];
877
897
  return { ok: ready, exitCode: ready ? 0 : 1, message: lines.join("\n") };
878
898
  }
899
+ if (command === "notify") {
900
+ const text = flagValue(argv, "--text");
901
+ if (!text || !text.trim()) {
902
+ return { ok: false, exitCode: 1, message: 'gateway notify requires --text "<message>"' };
903
+ }
904
+ const target = flagValue(argv, "--target");
905
+ const threadTs = flagValue(argv, "--thread-ts");
906
+ const { notify } = await import("./gateway/notify.js");
907
+ const result = await notify({ text, target, threadTs });
908
+ if (json) {
909
+ return {
910
+ ok: result.ok,
911
+ exitCode: result.ok ? 0 : 1,
912
+ output: result,
913
+ };
914
+ }
915
+ return {
916
+ ok: result.ok,
917
+ exitCode: result.ok ? 0 : 1,
918
+ message: result.ok
919
+ ? `posted to ${result.channel} (ts=${result.ts}${result.openedIm ? ", opened IM" : ""})`
920
+ : `notify failed [${result.code}]: ${result.reason}`,
921
+ };
922
+ }
879
923
  // Silence the unused warning emitted when no command path is hit.
880
924
  void getGatewayStatus;
881
925
  return {
882
926
  ok: false,
883
927
  exitCode: 1,
884
- message: "Unknown gateway subcommand. Try: gateway serve | status | doctor",
928
+ message: "Unknown gateway subcommand. Try: gateway serve | status | doctor | notify --text \"...\"",
885
929
  };
886
930
  }
887
931
  const TEAM_SPEC_RE = /^(\d+):([\w-]+)$/;
@@ -1168,6 +1212,14 @@ async function handleScheduleCommand(argv, json) {
1168
1212
  catch (err) {
1169
1213
  return { ok: false, exitCode: 1, message: String(err instanceof Error ? err.message : err) };
1170
1214
  }
1215
+ const notifyTarget = flagValue(argv, "--notify-target");
1216
+ if (notifyTarget) {
1217
+ const { parseTarget } = await import("./gateway/target-parser.js");
1218
+ const parsed = parseTarget(notifyTarget);
1219
+ if (!parsed.ok) {
1220
+ return { ok: false, exitCode: 1, message: `--notify-target: ${parsed.error}` };
1221
+ }
1222
+ }
1171
1223
  const result = mod.addScheduleJob(cwd, {
1172
1224
  id,
1173
1225
  cron,
@@ -1180,6 +1232,7 @@ async function handleScheduleCommand(argv, json) {
1180
1232
  ttlHours,
1181
1233
  allowAllTools: hasFlag(argv, "--allow-all-tools"),
1182
1234
  dryRun: hasFlag(argv, "--dry-run"),
1235
+ notifyTarget,
1183
1236
  });
1184
1237
  return json
1185
1238
  ? { ok: result.ok, exitCode: result.ok ? 0 : 1, output: result }