@jetrabbits/agentic 0.2.0 → 0.3.1

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 (36) hide show
  1. package/AGENTS.md +13 -15
  2. package/CHANGELOG.md +24 -0
  3. package/MEMORY.md +67 -0
  4. package/Makefile +96 -14
  5. package/README.md +2 -2
  6. package/agentic +1269 -99
  7. package/areas/devops/ci-cd/AGENTS.md +1 -15
  8. package/areas/devops/database-ops/AGENTS.md +1 -15
  9. package/areas/devops/devsecops/AGENTS.md +1 -15
  10. package/areas/devops/infrastructure/AGENTS.md +1 -15
  11. package/areas/devops/kubernetes/AGENTS.md +1 -15
  12. package/areas/devops/networking/AGENTS.md +1 -15
  13. package/areas/devops/observability/AGENTS.md +1 -15
  14. package/areas/devops/sre/AGENTS.md +1 -15
  15. package/areas/software/backend/AGENTS.md +1 -16
  16. package/areas/software/data-engineering/AGENTS.md +1 -16
  17. package/areas/software/frontend/AGENTS.md +1 -16
  18. package/areas/software/full-stack/AGENTS.md +1 -16
  19. package/areas/software/general/AGENTS.md +1 -7
  20. package/areas/software/mlops/AGENTS.md +1 -16
  21. package/areas/software/mobile/AGENTS.md +1 -16
  22. package/areas/software/platform/AGENTS.md +1 -16
  23. package/areas/software/qa/AGENTS.md +1 -16
  24. package/areas/software/security/AGENTS.md +1 -16
  25. package/areas/template/AGENTS.tmpl.md +1 -17
  26. package/docs/agentic-lifecycle.md +8 -4
  27. package/docs/agentic-stabilization/README.md +37 -0
  28. package/docs/agentic-token-minimization/README.md +7 -5
  29. package/docs/agentic-usage.md +17 -14
  30. package/docs/opencode_setup.md +8 -4
  31. package/extensions/opencode/opencode.json +1 -1
  32. package/extensions/opencode/plugins/agent-model-mapper.ts +117 -0
  33. package/extensions/opencode/plugins/telegram-notification.ts +30 -20
  34. package/package.json +2 -1
  35. package/extensions/opencode/plugins/model-checker.json +0 -13
  36. package/extensions/opencode/plugins/model-checker.ts +0 -302
@@ -0,0 +1,37 @@
1
+ # Agentic Stabilization v0.3.0
2
+
3
+ ## User-Facing Behavior
4
+
5
+ - Post-install doctor checks run independently for `codex`, `opencode`, `claude`, and `gemini`.
6
+ - `AGENTIC_DOCTOR_TIMEOUT_SECONDS` defaults to `10`; a timeout is reported as a doctor failure and install continues.
7
+ - Codex doctor runs non-interactively with `--ephemeral`, `--sandbox workspace-write`, and the same lightweight smoke prompt as other supported doctor targets.
8
+ - OpenCode uses `agent-model-mapper` instead of the removed `model-checker` artifacts.
9
+ - `agent-model-mapper` writes `.opencode/opencode.json` during interactive install only after confirmation.
10
+ - `agent-model-mapper` uses `fzf` for install-time model dropdowns when available and OpenCode startup skips once all roles are mapped.
11
+ - The runtime OpenCode plugin never opens `fzf`, asks questions, or writes project files.
12
+ - Context7 offers an interactive key mode: configure without a key or enter `CONTEXT7_API_KEY` for the selected target configs.
13
+ - OpenCode MemPalace setup writes `mempalace-mcp` config and initializes/mines project memory into a project-specific wing without LLM calls.
14
+ - Telegram notification credentials are read from project `.agentic.json` when the plugin is enabled.
15
+ - MemPalace-enabled installs create a managed `.mempalaceignore` unless the target project already has one.
16
+ - `make test` runs the fast deterministic e2e suite; longer deterministic checks, real blackbox, and coverage checks are explicit targets.
17
+ - `make test-all` runs the full local suite including longer deterministic checks, install/evidence blackbox, and coverage.
18
+ - Real Codex, OpenCode, and Telegram blackbox install/evidence scenarios run through `make test-real-blackbox`.
19
+ - Live Codex/OpenCode/Telegram blackbox sessions require `AGENTIC_REAL_BLACKBOX_LIVE=1`.
20
+ - `make test-coverage` traces `agentic` through e2e runs and fails below 90% line coverage.
21
+
22
+ ## Acceptance Criteria
23
+
24
+ - Hung agent doctor commands time out and do not stop remaining selected agents from running.
25
+ - Doctor output includes timeout duration, exit status, and per-agent elapsed time.
26
+ - `extensions/opencode/plugins/model-checker.ts` and `model-checker.json` are absent.
27
+ - `extensions/opencode/opencode.json` lists `agent-model-mapper`.
28
+ - Runtime model mapper execution does not prompt or modify project files.
29
+ - Telegram plugin tests prove environment-only credentials and no secret output.
30
+ - Real blackbox tests print created files, managed guidance sources, and MCP config evidence, then save instruction evidence to a temp file without printing Telegram secrets.
31
+
32
+ ## Operational Constraints
33
+
34
+ - `make test` is deterministic, designed for a sub-minute local loop, and does not require real agent binaries, model auth, network access, Context7/MemPalace access, or Telegram credentials.
35
+ - `AGENTIC_REAL_BLACKBOX_LIVE=1 make test-real-blackbox` requires real `codex` and `opencode` binaries, working model auth, network access, Context7/MemPalace access, and Telegram credentials for the Telegram case.
36
+ - Telegram credentials must never be committed or written to Agentic config.
37
+ - Coverage is line-based Bash trace coverage for the `agentic` script, not branch coverage.
@@ -32,15 +32,17 @@ When installing for OpenCode, `agentic` writes optional plugin state to:
32
32
  ~/.config/agentic/opencode-plugins.json
33
33
  ```
34
34
 
35
- Interactive installs ask whether to enable Telegram notifications and model checking. Non-interactive installs default optional plugins to disabled when no config exists.
35
+ Interactive installs ask whether to enable Telegram notifications and model mapping. Non-interactive installs default optional plugins to disabled when no config exists.
36
36
 
37
- The OpenCode plugins read this config at startup and return no hooks when disabled. Telegram credentials can also be supplied through:
37
+ The OpenCode plugins read project `.agentic.json` at startup and return no hooks when disabled. When Telegram is enabled, credentials are stored in plaintext at:
38
38
 
39
39
  ```text
40
- OPENCODE_TELEGRAM_BOT_TOKEN
41
- OPENCODE_TELEGRAM_CHAT_ID
40
+ settings.opencode_plugins.telegram.botToken
41
+ settings.opencode_plugins.telegram.chatId
42
42
  ```
43
43
 
44
+ Do not commit a Telegram-enabled `.agentic.json` to public repositories.
45
+
44
46
  ## Context7
45
47
 
46
48
  `agentic` adds Context7 MCP configuration for known project-level formats:
@@ -54,7 +56,7 @@ OPENCODE_TELEGRAM_CHAT_ID
54
56
  - `.kilocode/mcp.json` for `kilocode`
55
57
  - `~/.gemini/antigravity/mcp_config.json` for `antigravity` (global user config)
56
58
 
57
- Interactive installs ask whether to enable Context7. If enabled, the Context7 API key is optional. Empty keys keep the install usable with default Context7 limits or rule-only fallback behavior. Non-interactive installs enable Context7 only when `CONTEXT7_API_KEY` is already set. Generated guidance requires agents to use Context7 for framework, SDK, library, and API documentation before relying on model memory when the project config is present.
59
+ Interactive installs ask whether to enable Context7. If enabled, Context7 can be configured without a key or with a `CONTEXT7_API_KEY` entered during setup. Non-interactive installs enable Context7 when either `AGENTIC_ENABLE_CONTEXT7=y` or `CONTEXT7_API_KEY` is set. Generated guidance requires agents to use Context7 for framework, SDK, library, and API documentation before relying on model memory when the project config is present.
58
60
 
59
61
  Directory copies are processed in batches so large specialization installs avoid spawning a separate marker/manifest process for every copied file. Manifest protection still applies: existing unmanaged files are skipped on rerun, user-modified managed files are skipped, and new generated files can be added by newer `agentic` versions.
60
62
 
@@ -104,7 +104,7 @@ The final install line prints the exact path:
104
104
  Agentic log file: /tmp/agentic-20260512-114203.ABC123
105
105
  ```
106
106
 
107
- `agentic` also runs a final doctor smoke check for selected real agent targets (`codex`, `opencode`, `claude`, `gemini`). The doctor runs `/develop-feature напиши hello world python` in a temporary copy of the project and prints one status row per selected agent. Doctor failures are reported but do not roll back or fail the install. Disable doctor for CI or cheap checks with:
107
+ `agentic` also runs a final doctor smoke check for selected real agent targets (`codex`, `opencode`, `claude`, `gemini`). The doctor runs in a temporary copy of the project and prints one status row per selected agent. All supported doctor targets use a lightweight pure smoke prompt, so install-time doctor checks do not start a long SDLC workflow. Each agent has an independent timeout controlled by `AGENTIC_DOCTOR_TIMEOUT_SECONDS` and defaults to `10` seconds. Doctor failures and timeouts are reported but do not roll back or fail the install. Disable doctor for cheap checks with:
108
108
 
109
109
  ```bash
110
110
  AGENTIC_DOCTOR=0 agentic install ...
@@ -163,38 +163,41 @@ scoop install fzf
163
163
 
164
164
  ## OpenCode optional plugins
165
165
 
166
- When `opencode` is selected, interactive installs ask whether to enable Telegram notifications and the model checker. The answer is stored globally in:
166
+ When `opencode` is selected, interactive installs ask whether to enable Telegram notifications and `agent-model-mapper`. The answer is stored globally in:
167
167
 
168
168
  ```text
169
169
  ~/.config/agentic/opencode-plugins.json
170
170
  ```
171
171
 
172
- Non-interactive installs create a disabled config when no config exists. Telegram can also read `OPENCODE_TELEGRAM_BOT_TOKEN` and `OPENCODE_TELEGRAM_CHAT_ID`.
172
+ Non-interactive installs create a disabled config when no config exists. Interactive installs ask for Telegram `botToken` and `chatId` when `telegram-notification` is selected. Those credentials are written to the target project `.agentic.json` under `settings.opencode_plugins.telegram`, not to `~/.config/agentic/opencode-plugins.json`. Treat `.agentic.json` as plaintext secret-bearing project config when Telegram is enabled and do not commit it to public repositories. When enabled, `agent-model-mapper` runs during interactive `agentic install`/`agentic tui`, uses `fzf` as a dropdown picker when available, and writes `.opencode/opencode.json` only after a Confirm action. OpenCode startup never prompts for model mapping; the runtime plugin only reports whether install-time mapping is already complete.
173
173
 
174
174
  ## Context7
175
175
 
176
- For `opencode`, `codex`, `claude`, `cursor`, `gemini`, `kilocode`, and `antigravity`, interactive installs ask whether to add Context7 MCP configuration. If enabled, the Context7 API key prompt is optional; leave it empty to configure Context7 without a key. Most targets use project-level files, while `antigravity` is written to the global user path `~/.gemini/antigravity/mcp_config.json`.
176
+ For `opencode`, `codex`, `claude`, `cursor`, `gemini`, `kilocode`, and `antigravity`, interactive installs ask whether to add Context7 MCP configuration. If enabled, a follow-up menu chooses either keyless mode or entering `CONTEXT7_API_KEY`. The selected key is written to all selected target configs for the current project. Most targets use project-level files, while `antigravity` is written to the global user path `~/.gemini/antigravity/mcp_config.json`.
177
177
 
178
- Non-interactive installs skip Context7 unless `CONTEXT7_API_KEY` is set in the environment. Agents are instructed to use Context7 for framework, library, SDK, API, and setup documentation when the project config is present.
178
+ Non-interactive installs enable Context7 when either `AGENTIC_ENABLE_CONTEXT7=y` or `CONTEXT7_API_KEY` is set. Agents are instructed to use Context7 for framework, library, SDK, API, and setup documentation when the project config is present.
179
179
 
180
180
  ## MemPalace
181
181
 
182
- For `opencode`, `codex`, `claude`, `cursor`, `gemini`, and `antigravity`, MemPalace MCP is configured as a local Python module instead of a hosted MCP URL. Install it first:
183
-
184
- ```bash
185
- pip install mempalace
186
- ```
182
+ For `opencode`, `codex`, `claude`, `cursor`, `gemini`, `kilocode`, and `antigravity`, MemPalace MCP is configured as a local Python module instead of a hosted MCP URL. When MemPalace is enabled, `agentic` attempts to install it automatically with `pip install mempalace`.
187
183
 
188
184
  Generated configs run `mempalace-mcp` without arguments for all supported agent targets. Runtime startup and MCP tool errors are checked by the post-install doctor stage.
189
185
 
190
- During install, if MemPalace is enabled, `agentic` checks whether `mempalace-mcp` is available. For OpenCode installs, `agentic` creates `<project>` and runs `mempalace init "<project>" --yes --auto-mine`. If checks fail (for example, package not installed yet), install continues and agents fall back to standard context discovery.
186
+ During install, if MemPalace is enabled, `agentic` writes a managed `.mempalaceignore` when the target project does not already have one. It initializes the project with `mempalace init --yes --no-llm`, then mines project knowledge into a wing named from the target project basename. If `docs/` exists, those files are also mined into the shared `shared_docs` wing for cross-project Markdown knowledge. MemPalace commands time out after `60` seconds by default; override with `AGENTIC_MEMPALACE_TIMEOUT_SECONDS`. If auto-install, initialization, mining, timeout, or runtime checks fail, install continues, manual setup instructions are printed, and agents fall back to standard context discovery. If `pip install mempalace` fails, `agentic` prints the pip exit status, a temporary pip output log path, and the first non-empty pip output line as the likely reason; the full pip output is also copied into the main Agentic run log.
187
+
188
+ For environments that already provide `mempalace-mcp` and need a fast install path, set `AGENTIC_MEMPALACE_SETUP=skip`. This writes the same MCP configuration and `.mempalaceignore` but skips Python package installation and project indexing.
189
+
190
+ ## Real agent blackbox E2E
191
+
192
+ `make test` runs the fast deterministic e2e suite and is intended to finish in under a minute on a normal local machine. Longer deterministic checks (`test-doctor`, `test-markers`), real-agent blackbox, and coverage checks are explicit targets so the default local loop stays short.
191
193
 
192
- ## Real agent doctor E2E
194
+ `make test-real-blackbox` validates real Codex/OpenCode install artifacts, generated guidance, MCP config, and manifest evidence without starting live LLM sessions by default. Set `AGENTIC_REAL_BLACKBOX_LIVE=1` to execute live `codex`/`opencode` runs. Live Telegram checks require Telegram credentials to be present in the target project `.agentic.json`; secrets are redacted from test output.
193
195
 
194
- The deterministic e2e suite uses fake agent binaries and does not call models. Real agent doctor checks are opt-in because they may use network access, credentials, and model credits:
196
+ Makefile test targets print timestamped `[make-timing]` start, success/failure, exit status, and elapsed seconds for every top-level test step. `test-real-blackbox` is split into separate Codex, OpenCode, OpenCode mapper, and Telegram timed steps. Blackbox instruction evidence is saved to a `/tmp/agentic-instruction-evidence.*` file and the path is printed. `test-coverage` also prints timing for each traced coverage scenario before the final coverage parser. Use `make test-all` when you want the fast suite, longer deterministic checks, install/evidence blackbox, and coverage in one command.
195
197
 
196
198
  ```bash
197
- AGENTIC_RUN_REAL_AGENT_E2E=1 make test-real-agent-doctor
199
+ make test-real-blackbox
200
+ AGENTIC_REAL_BLACKBOX_LIVE=1 make test-real-blackbox
198
201
  ```
199
202
 
200
203
  ## Deprecated wrapper
@@ -33,16 +33,20 @@ When `agentic` installs the OpenCode extension, it configures optional plugins i
33
33
  ~/.config/agentic/opencode-plugins.json
34
34
  ```
35
35
 
36
- Telegram notifications and model checking are opt-in. If the config is absent or a plugin is disabled, the plugin returns no hooks and OpenCode continues without that behavior.
36
+ Telegram notifications and agent model mapping are opt-in. Interactive `agentic install` and `agentic tui` ask for OpenCode plugin selection whenever `opencode` is selected; the answer rewrites this config. During manifest-based upgrade/re-install sync, existing plugin settings are kept so automated refreshes do not open prompts. If the config is absent or a plugin is disabled, the plugin returns no hooks and OpenCode continues without that behavior.
37
37
 
38
- Telegram notifications use either the stored config values or these environment variables:
38
+ When `telegram-notification` is selected interactively, `agentic` asks for `botToken` and `chatId` and stores them in the target project's `.agentic.json`:
39
39
 
40
40
  ```text
41
- OPENCODE_TELEGRAM_BOT_TOKEN
42
- OPENCODE_TELEGRAM_CHAT_ID
41
+ settings.opencode_plugins.telegram.botToken
42
+ settings.opencode_plugins.telegram.chatId
43
43
  ```
44
44
 
45
+ The runtime plugin reads credentials from the project `.agentic.json`; it does not read Telegram credentials from environment variables. This file stores credentials in plaintext, so do not commit a Telegram-enabled `.agentic.json` to public repositories.
46
+
45
47
  Non-interactive `agentic install` defaults optional plugins to disabled when no config exists.
46
48
 
49
+ `agent-model-mapper` reads roles from target `.opencode/agents/*.md` and discovers model names from `~/.config/opencode/opencode.json`, then adds models from active providers in `~/.local/share/opencode/auth.json` using non-deprecated entries in `~/.cache/opencode/models.json`. When enabled, interactive `agentic install`/`agentic tui` prompts for a main and fallback model per role, using `fzf` as a dropdown picker when available, and writes `.opencode/opencode.json` only after a Confirm action. OpenCode startup never opens `fzf` or waits for model input; the runtime plugin only reports whether install-time mapping is complete.
50
+
47
51
  For OpenCode targets, `agentic` writes generated operating guidance to `.opencode/AGENTS.md`. If OpenCode is installed
48
52
  alongside another agent target, root `AGENTS.md` is generated as well for the non-OpenCode target.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://opencode.ai/config.json",
3
3
  "plugin": [
4
- "model-checker",
4
+ "agent-model-mapper",
5
5
  "sound-notification",
6
6
  "telegram-notification"
7
7
  ],
@@ -0,0 +1,117 @@
1
+ import type { Plugin } from "@opencode-ai/plugin"
2
+ import { existsSync, readFileSync } from "node:fs"
3
+ import { readdir, readFile } from "node:fs/promises"
4
+ import { basename, join } from "node:path"
5
+
6
+ type AgenticPluginConfig = {
7
+ agentModelMapper?: {
8
+ enabled?: boolean
9
+ }
10
+ settings?: any
11
+ }
12
+
13
+ type Role = {
14
+ name: string
15
+ description: string
16
+ mode: string
17
+ }
18
+
19
+ function readAgenticConfig(): AgenticPluginConfig {
20
+ const configHome = process.env.XDG_CONFIG_HOME || join(process.env.HOME || "", ".config")
21
+ const configPath = join(configHome, "agentic", "opencode-plugins.json")
22
+
23
+ try {
24
+ return JSON.parse(readFileSync(configPath, "utf-8")) as AgenticPluginConfig
25
+ } catch {
26
+ return {}
27
+ }
28
+ }
29
+
30
+ function readProjectAgenticConfig(directory: string): AgenticPluginConfig {
31
+ try {
32
+ return JSON.parse(readFileSync(join(directory, ".agentic.json"), "utf-8")) as AgenticPluginConfig
33
+ } catch {
34
+ return {}
35
+ }
36
+ }
37
+
38
+ function parseFrontmatter(text: string): Record<string, string> {
39
+ if (!text.startsWith("---\n")) return {}
40
+ const end = text.indexOf("\n---", 4)
41
+ if (end === -1) return {}
42
+
43
+ const result: Record<string, string> = {}
44
+ for (const line of text.slice(4, end).split("\n")) {
45
+ const index = line.indexOf(":")
46
+ if (index === -1) continue
47
+ result[line.slice(0, index).trim()] = line.slice(index + 1).trim().replace(/^['"]|['"]$/g, "")
48
+ }
49
+ return result
50
+ }
51
+
52
+ async function readRoles(directory: string): Promise<Role[]> {
53
+ const agentsDir = join(directory, ".opencode", "agents")
54
+ let entries: string[]
55
+ try {
56
+ entries = await readdir(agentsDir)
57
+ } catch {
58
+ return []
59
+ }
60
+
61
+ const roles: Role[] = []
62
+ for (const entry of entries.sort()) {
63
+ if (!entry.endsWith(".md")) continue
64
+ const path = join(agentsDir, entry)
65
+ const text = await readFile(path, "utf-8")
66
+ const frontmatter = parseFrontmatter(text)
67
+ roles.push({
68
+ name: basename(entry, ".md"),
69
+ description: frontmatter.description || "OpenCode agent",
70
+ mode: frontmatter.mode || "subagent",
71
+ })
72
+ }
73
+ return roles
74
+ }
75
+
76
+ function readJsonIfExists(path: string): unknown {
77
+ if (!existsSync(path)) return undefined
78
+ try {
79
+ return JSON.parse(readFileSync(path, "utf-8"))
80
+ } catch {
81
+ return undefined
82
+ }
83
+ }
84
+
85
+ function hasCompleteAgentModelMapping(directory: string, roles: Role[]): boolean {
86
+ const state = readJsonIfExists(join(directory, ".opencode", "agent-model-mapper.state.json")) as Record<string, any> | undefined
87
+ if (!state?.configured) return false
88
+
89
+ const config = readJsonIfExists(join(directory, ".opencode", "opencode.json")) as Record<string, any> | undefined
90
+ const agents = config?.agent
91
+ if (!agents || typeof agents !== "object") return false
92
+ return roles.every((role) => {
93
+ const agent = agents[role.name]
94
+ return agent && typeof agent === "object" && typeof agent.model === "string" && agent.model.trim().length > 0
95
+ })
96
+ }
97
+
98
+ export const AgentModelMapperPlugin: Plugin = async ({ directory }) => {
99
+ const projectConfig = readProjectAgenticConfig(directory)
100
+ const globalConfig = readAgenticConfig()
101
+ const enabled = projectConfig.settings?.opencode_plugins?.agentModelMapper?.enabled ?? globalConfig.agentModelMapper?.enabled
102
+ if (!enabled) return {}
103
+
104
+ const roles = await readRoles(directory)
105
+ if (!roles.length) {
106
+ console.log("agent-model-mapper: skipped because .opencode/agents/*.md was not found")
107
+ return {}
108
+ }
109
+
110
+ if (hasCompleteAgentModelMapping(directory, roles)) {
111
+ console.log("agent-model-mapper: skipped because all Agentic roles already have model mappings")
112
+ return {}
113
+ }
114
+
115
+ console.log("agent-model-mapper: install-time model mapping is required; run agentic install or agentic tui")
116
+ return {}
117
+ }
@@ -3,17 +3,27 @@ import { readFileSync } from "node:fs"
3
3
  import { join } from "node:path"
4
4
 
5
5
  type AgenticPluginConfig = {
6
- telegram?: {
7
- enabled?: boolean
8
- botToken?: string
9
- chatId?: string
10
- }
6
+ settings?: any
7
+ }
8
+
9
+ function telegramApiBaseUrl(): string {
10
+ return process.env.OPENCODE_TELEGRAM_API_BASE_URL || "https://api.telegram.org"
11
+ }
12
+
13
+ function telegramUrl(botToken: string, method: string): string {
14
+ return `${telegramApiBaseUrl()}/bot${botToken}/${method}`
11
15
  }
12
16
 
13
- function readAgenticConfig(): AgenticPluginConfig {
14
- const configHome = process.env.XDG_CONFIG_HOME || join(process.env.HOME || "", ".config")
15
- const configPath = join(configHome, "agentic", "opencode-plugins.json")
17
+ function redactSecret(value: unknown): string {
18
+ const text = String(value)
19
+ const telegram = currentTelegramConfig
20
+ const token = telegram?.botToken
21
+ const chatId = telegram?.chatId
22
+ return [token, chatId].filter(Boolean).reduce((output, secret) => output.split(secret as string).join("[redacted]"), text)
23
+ }
16
24
 
25
+ function readAgenticConfig(directory: string): AgenticPluginConfig {
26
+ const configPath = join(directory, ".agentic.json")
17
27
  try {
18
28
  return JSON.parse(readFileSync(configPath, "utf-8")) as AgenticPluginConfig
19
29
  } catch {
@@ -21,19 +31,19 @@ function readAgenticConfig(): AgenticPluginConfig {
21
31
  }
22
32
  }
23
33
 
34
+ let currentTelegramConfig
35
+
24
36
  export const TelegramNotificationPlugin: Plugin = async ({ $, client, directory }) => {
25
- const config = readAgenticConfig()
26
- const telegram = config.telegram
27
- const botToken = process.env.OPENCODE_TELEGRAM_BOT_TOKEN || telegram?.botToken
28
- const chatId = process.env.OPENCODE_TELEGRAM_CHAT_ID || telegram?.chatId
37
+ const config = readAgenticConfig(directory)
38
+ const telegram = config.settings?.opencode_plugins?.telegram
39
+ currentTelegramConfig = telegram
40
+ const botToken = telegram?.botToken
41
+ const chatId = telegram?.chatId
29
42
 
30
43
  if (!telegram?.enabled || !botToken || !chatId) {
31
44
  return {}
32
45
  }
33
46
 
34
- process.env.OPENCODE_TELEGRAM_BOT_TOKEN = botToken
35
- process.env.OPENCODE_TELEGRAM_CHAT_ID = chatId
36
-
37
47
  return {
38
48
  event: async ({ event }) => {
39
49
  if (event.type === "session.idle") {
@@ -60,7 +70,7 @@ export const TelegramNotificationPlugin: Plugin = async ({ $, client, directory
60
70
  }
61
71
  }
62
72
  } catch (e) {
63
- await $`echo "Error: ${e}" >> ${directory}/.opencode/telegram-debug.log`
73
+ await $`echo "Error: ${redactSecret(e)}" >> ${directory}/.opencode/telegram-debug.log`
64
74
  }
65
75
 
66
76
  try {
@@ -74,19 +84,19 @@ export const TelegramNotificationPlugin: Plugin = async ({ $, client, directory
74
84
  )
75
85
 
76
86
  await fetch(
77
- `https://api.telegram.org/bot${botToken}/sendDocument`,
87
+ telegramUrl(botToken, "sendDocument"),
78
88
  { method: "POST", body: formData }
79
89
  )
80
90
 
81
91
  const shortText = fullText.slice(0, 3000)
82
92
  await fetch(
83
- `https://api.telegram.org/bot${botToken}/sendMessage`,
93
+ telegramUrl(botToken, "sendMessage"),
84
94
  {
85
95
  method: "POST",
86
96
  headers: { "Content-Type": "application/json" },
87
97
  body: JSON.stringify({
88
98
  chat_id: chatId,
89
- text: `${messageText}\n\n📎 Полный ответ в attachment (${fullText.length} символов):\n\n${shortText}...`
99
+ text: `${messageText}\n\nПолный ответ в attachment (${fullText.length} символов):\n\n${shortText}...`
90
100
  })
91
101
  }
92
102
  )
@@ -96,7 +106,7 @@ export const TelegramNotificationPlugin: Plugin = async ({ $, client, directory
96
106
  : messageText
97
107
 
98
108
  await fetch(
99
- `https://api.telegram.org/bot${botToken}/sendMessage`,
109
+ telegramUrl(botToken, "sendMessage"),
100
110
  {
101
111
  method: "POST",
102
112
  headers: { "Content-Type": "application/json" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetrabbits/agentic",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Agent Intelligence Configuration CLI",
5
5
  "bin": {
6
6
  "agentic": "bin/agentic.js"
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "files": [
13
13
  "AGENTS.md",
14
+ "MEMORY.md",
14
15
  "CHANGELOG.md",
15
16
  "UPGRADE.md",
16
17
  "LICENSE",
@@ -1,13 +0,0 @@
1
- {
2
- "models": [
3
- "openai/gpt-5.5",
4
- "opencode/big-pickle",
5
- "opencode/minimax-m2.5-free",
6
- "google/antigravity-claude-opus-4-6-thinking",
7
- "google/antigravity-claude-sonnet-4-6",
8
- "google/antigravity-gemini-3-flash",
9
- "google/antigravity-gemini-3.1-pro"
10
- ],
11
- "timeoutMs": 2000,
12
- "concurrency": 10
13
- }