feat: Enhance Mattermost integration by adding sync commands and context checks for recent messages
This commit is contained in:
@@ -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.
|
||||
|
||||
40
.opencode/commands/latest-message.md
Normal file
40
.opencode/commands/latest-message.md
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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 },
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user