feat: Enhance Mattermost integration by adding sync commands and context checks for recent messages

This commit is contained in:
2026-04-15 08:48:00 -06:00
parent e389e0ae5e
commit 1885855558
7 changed files with 133 additions and 3 deletions

View File

@@ -12,6 +12,7 @@ Behavior rules:
- Treat `README.md`, `ai/context/`, `ai/state/`, `knowledge/`, and `ai/logs/` as the persistent memory of the project.
- Before answering a prompt that depends on current state, verify the latest relevant files instead of relying only on conversation history.
- If the prompt asks for the latest Mattermost message, the last message from Jeff/current manager, or what someone just said, force a Mattermost refresh before answering and do not rely on stale inbox context.
- For any meaningful prompt, decide whether the interaction adds, corrects, or sharpens project memory.
- When the user provides new durable information, update the right workspace files before or while answering.
- If existing context is stale, correct it directly instead of leaving conflicting versions.

View File

@@ -0,0 +1,40 @@
---
description: Force-sync Mattermost and answer from the latest matching message
---
Force-refresh Mattermost first, then answer the user's question from the refreshed inbox.
Use this when the user asks for:
- the latest or last message
- what Jeff or another person just said
- the latest Mattermost update
- the latest message in `fidelity-preguntas`
Run sync:
!`if [ -n "$FIDELITY_MATTERMOST_SYNC_CMD" ]; then bash -lc "$FIDELITY_MATTERMOST_SYNC_CMD"; elif [ -f scripts/mattermost/sync.sh ]; then bash scripts/mattermost/sync.sh; else echo "No Mattermost sync command is configured."; fi`
Read refreshed Mattermost context:
!`if [ -s ai/inbox/mattermost-latest.md ]; then cat ai/inbox/mattermost-latest.md; elif [ -s scripts/mattermost/generated/mattermost_context.jsonl ]; then cat scripts/mattermost/generated/mattermost_context.jsonl; else echo "No Mattermost context available after sync."; fi`
User request:
$ARGUMENTS
Instructions:
- Do not answer from old conversation memory.
- Use only the refreshed Mattermost output above.
- If the user asks for Jeff/current manager, filter messages by `jeff`, `jeff.dewitte`, or the current manager profile when visible.
- If multiple messages match, return the newest matching message first.
- Include timestamp, channel, sender, and concise summary.
- If the message changes project context, update the appropriate workspace memory after answering.
- If sync fails or no refreshed context is available, say that directly and do not infer from stale context.
Return:
1. Latest matching message
2. Why it matters
3. Any memory update made

View File

@@ -13,6 +13,8 @@ Run the command and use its output as fresh communication context:
!`if [ -n "$FIDELITY_MATTERMOST_SYNC_CMD" ]; then bash -lc "$FIDELITY_MATTERMOST_SYNC_CMD"; elif [ -f scripts/mattermost/sync.sh ]; then bash scripts/mattermost/sync.sh; else echo "No Mattermost sync command is configured."; fi`
Use this command implicitly when the user asks for the latest or last Mattermost message, especially messages from Jeff or the current manager.
Then:
- if the command fails, stop there and do not edit any workspace files

View File

@@ -35,6 +35,78 @@ async function resolveSyncContent(directory, stdoutText) {
return (generated || "").trim()
}
function extractPromptText(event) {
const candidates = [
event?.properties?.text,
event?.properties?.message,
event?.properties?.prompt,
event?.text,
event?.message,
event?.prompt,
]
for (const candidate of candidates) {
if (typeof candidate === "string" && candidate.trim()) {
return candidate
}
}
try {
return JSON.stringify(event)
} catch {
return ""
}
}
function requiresFreshMattermost(promptText) {
const text = promptText
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase()
const freshnessTerms = [
"ultimo",
"ultimos",
"ultima",
"ultimas",
"latest",
"last",
"reciente",
"recent",
"nuevo",
"new",
"actualiza",
"sync",
"sincroniza",
"revisa",
"dijo",
"menciono",
"respondio",
"contesto",
"check",
"look at",
"said",
"mentioned",
"replied",
]
const mattermostTerms = [
"mattermost",
"mensaje",
"mensajes",
"message",
"messages",
"jeff",
"manager",
"fidelity-preguntas",
]
return (
freshnessTerms.some((term) => text.includes(term)) &&
mattermostTerms.some((term) => text.includes(term))
)
}
export const MattermostInbox = async ({ $, directory, client }) => {
let lastSyncAt = 0
@@ -43,7 +115,7 @@ export const MattermostInbox = async ({ $, directory, client }) => {
await writeFile(statusPath, `${JSON.stringify(data, null, 2)}\n`, "utf8")
}
async function sync(reason) {
async function sync(reason, options = {}) {
const command = await resolveSyncCommand(directory)
if (!command) return
@@ -54,7 +126,7 @@ export const MattermostInbox = async ({ $, directory, client }) => {
const minIntervalMs = Math.max(1, Number.isNaN(intervalMinutes) ? 15 : intervalMinutes) * 60 * 1000
const now = Date.now()
if (now - lastSyncAt < minIntervalMs) return
if (!options.force && now - lastSyncAt < minIntervalMs) return
lastSyncAt = now
const inboxDir = path.join(directory, "ai/inbox")
@@ -78,6 +150,7 @@ export const MattermostInbox = async ({ $, directory, client }) => {
await writeStatus(statusPath, {
syncedAt: nowIso(),
reason,
forced: Boolean(options.force),
changed: previous !== `${output}\n` && previous !== output,
commandConfigured: true,
commandSource: process.env.FIDELITY_MATTERMOST_SYNC_CMD?.trim() ? "env" : "workspace-default",
@@ -86,6 +159,7 @@ export const MattermostInbox = async ({ $, directory, client }) => {
await writeStatus(statusPath, {
syncedAt: nowIso(),
reason,
forced: Boolean(options.force),
changed: false,
commandConfigured: true,
commandSource: process.env.FIDELITY_MATTERMOST_SYNC_CMD?.trim() ? "env" : "workspace-default",
@@ -96,6 +170,7 @@ export const MattermostInbox = async ({ $, directory, client }) => {
await writeStatus(statusPath, {
syncedAt: nowIso(),
reason,
forced: Boolean(options.force),
changed: false,
commandConfigured: true,
commandSource: process.env.FIDELITY_MATTERMOST_SYNC_CMD?.trim() ? "env" : "workspace-default",
@@ -123,7 +198,14 @@ export const MattermostInbox = async ({ $, directory, client }) => {
}
if (event.type === "tui.prompt.append") {
await sync("tui.prompt.append")
const promptText = extractPromptText(event)
const forceFreshMattermost = requiresFreshMattermost(promptText)
await sync(
forceFreshMattermost
? "tui.prompt.append:fresh-mattermost-request"
: "tui.prompt.append",
{ force: forceFreshMattermost },
)
}
},
}

View File

@@ -38,6 +38,8 @@ These are also loaded through `opencode.json`.
- Assume the workspace may contain stale context until checked.
- Before answering questions that depend on current work state, inspect `ai/state/current.md` and the latest relevant log under `ai/logs/`.
- If `ai/inbox/mattermost-latest.md` exists, inspect it for fresher communication context before answering standup, status, or manager-message prompts.
- If the user asks for the latest/last/recent Mattermost message, the latest message from Jeff/current manager, or what someone just said, synchronize Mattermost first instead of relying on existing inbox context.
- If automatic refresh is uncertain, use the explicit latest-message flow: run the Mattermost sync command, then answer from the refreshed inbox only.
- For any meaningful prompt, decide whether the interaction introduces or corrects project memory.
- If a sync command, extraction script, or inbox refresh fails, do not update logs, state, or context files from that failed attempt.
- Treat sync failures as operational errors, not project context.

View File

@@ -168,6 +168,7 @@ Project commands live under `.opencode/commands/` and are intended to:
- draft standups
- draft manager updates
- draft Copilot prompts for coding work on the Fidelity machine
- force-refresh and inspect latest Mattermost messages
- convert rough notes into daily log updates
This keeps AI output tied to the latest workspace state instead of relying on chat memory alone.

View File

@@ -66,6 +66,8 @@ When drafting messages for a manager or stakeholder:
- Before answering Swift, SwiftUI, iOS architecture, testing, or debugging questions, check `ai/context/ios/` and use the project-local iOS skills when available
- Before generating a prompt for another AI or GitHub Copilot, check `ai/context/process/ai-to-ai-prompting.md` and make the prompt self-contained
- If `ai/inbox/mattermost-latest.md` exists, check it before answering prompts about current status, standups, or supervisor communication
- If the user asks for the latest/last/recent Mattermost message, the latest message from Jeff/current manager, or what someone just said, synchronize Mattermost first and then answer from the refreshed inbox
- If automatic refresh is uncertain, use the explicit latest-message flow: run the Mattermost sync command, then answer from refreshed output only
- Treat all meaningful user prompts as potential memory updates, not only explicit sync commands
- If a Mattermost sync or other context-ingestion step fails, do not update `ai/logs/`, `ai/state/`, or stable context files based on that failure
- Do not record missing dependencies, failed sync attempts, or missing inbox files as project facts unless the user explicitly asks to track the operational issue