compass-ai 1.0.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/bin/compass.js +354 -0
- package/package.json +21 -0
- package/skills/compass-extract/SKILL.md +123 -0
- package/skills/compass-report/SKILL.md +103 -0
package/bin/compass.js
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { execSync, spawnSync } = require('child_process');
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
const PACKAGE_DIR = path.join(__dirname, '..');
|
|
11
|
+
const OPENCLAW_DIR = path.join(os.homedir(), '.openclaw');
|
|
12
|
+
const WORKSPACE_DIR = path.join(OPENCLAW_DIR, 'compass');
|
|
13
|
+
const STATE_DIR = path.join(WORKSPACE_DIR, 'state');
|
|
14
|
+
const BOOT_MD = path.join(WORKSPACE_DIR, 'boot.md');
|
|
15
|
+
const SKILLS_SRC = path.join(PACKAGE_DIR, 'skills');
|
|
16
|
+
|
|
17
|
+
const cmd = process.argv[2];
|
|
18
|
+
|
|
19
|
+
switch (cmd) {
|
|
20
|
+
case 'init': init().catch(err => { console.error('\n Error:', err.message); process.exit(1); }); break;
|
|
21
|
+
case 'start': start(); break;
|
|
22
|
+
case 'report': report(); break;
|
|
23
|
+
default: help(); break;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
function ask(rl, question, def) {
|
|
29
|
+
return new Promise(resolve => {
|
|
30
|
+
rl.question(def ? ` ${question} [${def}]: ` : ` ${question}: `, ans => {
|
|
31
|
+
resolve(ans.trim() || def || '');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function write(filePath, content) {
|
|
37
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
38
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveOpenClaw() {
|
|
42
|
+
const candidates = [
|
|
43
|
+
path.join(PACKAGE_DIR, 'node_modules', '.bin', 'openclaw'),
|
|
44
|
+
path.join(os.homedir(), '.npm-global', 'bin', 'openclaw'),
|
|
45
|
+
];
|
|
46
|
+
for (const c of candidates) {
|
|
47
|
+
if (fs.existsSync(c)) return c;
|
|
48
|
+
}
|
|
49
|
+
try { return execSync('which openclaw 2>/dev/null || command -v openclaw', { encoding: 'utf8' }).trim(); } catch {}
|
|
50
|
+
console.error('\n openclaw not found. It should have been installed as a dependency.\n Try: npm install -g openclaw\n');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runOpenClaw(...args) {
|
|
55
|
+
const bin = resolveOpenClaw();
|
|
56
|
+
const result = spawnSync(bin, args, { stdio: 'inherit' });
|
|
57
|
+
if (result.status !== 0) process.exit(result.status || 1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── init ─────────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
async function init() {
|
|
63
|
+
console.log('\n🧭 Compass — Setup\n');
|
|
64
|
+
console.log(' Answer each question. Press Enter to accept the default.\n');
|
|
65
|
+
|
|
66
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
67
|
+
|
|
68
|
+
const llmProvider = await ask(rl, 'LLM provider (anthropic / openai)', 'anthropic');
|
|
69
|
+
const anthropicKey = llmProvider === 'anthropic' ? await ask(rl, 'Anthropic API key') : '';
|
|
70
|
+
const openaiKey = llmProvider === 'openai' ? await ask(rl, 'OpenAI API key') : '';
|
|
71
|
+
const telegramToken = await ask(rl, 'Telegram bot token');
|
|
72
|
+
const telegramUserId = await ask(rl, 'Your Telegram user ID');
|
|
73
|
+
const slackToken = await ask(rl, 'Slack bot token');
|
|
74
|
+
const slackChannel = await ask(rl, 'Slack report channel', '#standup');
|
|
75
|
+
const dailyTime = await ask(rl, 'Daily standup time (HH:MM, 24h)', '09:00');
|
|
76
|
+
const weeklyDay = await ask(rl, 'Weekly review day (MON-SUN)', 'MON');
|
|
77
|
+
const weeklyTime = await ask(rl, 'Weekly review time (HH:MM, 24h)', '08:00');
|
|
78
|
+
const founderName = await ask(rl, 'Your name (for Compass memory)', 'Manas');
|
|
79
|
+
|
|
80
|
+
rl.close();
|
|
81
|
+
|
|
82
|
+
console.log('\n Writing config files...\n');
|
|
83
|
+
|
|
84
|
+
// ── OpenClaw root config ──────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
write(path.join(OPENCLAW_DIR, 'agent.json'), JSON.stringify({
|
|
87
|
+
name: 'Compass',
|
|
88
|
+
workspace: 'compass',
|
|
89
|
+
creature: 'chief of staff and execution memory for a small startup',
|
|
90
|
+
vibe: 'direct, concise, action-oriented, low noise',
|
|
91
|
+
emoji: '🧭',
|
|
92
|
+
bootFile: BOOT_MD
|
|
93
|
+
}, null, 2));
|
|
94
|
+
|
|
95
|
+
write(path.join(OPENCLAW_DIR, 'hooks.json'), JSON.stringify({
|
|
96
|
+
'boot-md': { enabled: true, file: BOOT_MD },
|
|
97
|
+
'command-logger': { enabled: true },
|
|
98
|
+
'session-memory': { enabled: true, workspaceDir: WORKSPACE_DIR }
|
|
99
|
+
}, null, 2));
|
|
100
|
+
|
|
101
|
+
write(path.join(OPENCLAW_DIR, 'channels.json'), JSON.stringify({
|
|
102
|
+
telegram: {
|
|
103
|
+
enabled: true,
|
|
104
|
+
token: telegramToken,
|
|
105
|
+
dmPolicy: 'allowlist',
|
|
106
|
+
allowlist: [telegramUserId]
|
|
107
|
+
},
|
|
108
|
+
slack: {
|
|
109
|
+
enabled: true,
|
|
110
|
+
token: slackToken,
|
|
111
|
+
reportChannel: slackChannel
|
|
112
|
+
}
|
|
113
|
+
}, null, 2));
|
|
114
|
+
|
|
115
|
+
write(path.join(OPENCLAW_DIR, 'llm.json'), JSON.stringify({
|
|
116
|
+
provider: llmProvider,
|
|
117
|
+
anthropicApiKey: anthropicKey,
|
|
118
|
+
openaiApiKey: openaiKey
|
|
119
|
+
}, null, 2));
|
|
120
|
+
|
|
121
|
+
// ── Workspace identity files ──────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
write(BOOT_MD, buildBootMd(founderName));
|
|
124
|
+
|
|
125
|
+
write(path.join(WORKSPACE_DIR, 'user.md'),
|
|
126
|
+
`# User — ${founderName}
|
|
127
|
+
|
|
128
|
+
${founderName} is the founder and final decision maker.
|
|
129
|
+
|
|
130
|
+
## Preferences
|
|
131
|
+
- Concise bullet points, not paragraphs
|
|
132
|
+
- Fast signal, not theory
|
|
133
|
+
- Early warning on problems before they escalate
|
|
134
|
+
- Clear ownership and practical next steps
|
|
135
|
+
`);
|
|
136
|
+
|
|
137
|
+
write(path.join(WORKSPACE_DIR, 'soul.md'),
|
|
138
|
+
`# Compass — Soul
|
|
139
|
+
|
|
140
|
+
I am Compass. I am the execution memory and chief of staff for a small startup.
|
|
141
|
+
I am not a chatbot. I do not brainstorm. I do not chat.
|
|
142
|
+
I extract, store, surface, and report. That is my function.
|
|
143
|
+
|
|
144
|
+
## Voice
|
|
145
|
+
- Direct and concise — no filler
|
|
146
|
+
- Action-oriented — every output should be actionable
|
|
147
|
+
- Low noise — I speak only when it matters
|
|
148
|
+
`);
|
|
149
|
+
|
|
150
|
+
write(path.join(WORKSPACE_DIR, 'identity.md'),
|
|
151
|
+
`# Compass — System Role
|
|
152
|
+
|
|
153
|
+
Chief of staff and execution memory for a small startup.
|
|
154
|
+
Phase 1: read Telegram, extract signal, maintain state files, report to Slack.
|
|
155
|
+
|
|
156
|
+
## Boundaries
|
|
157
|
+
- No sending emails
|
|
158
|
+
- No publishing content externally
|
|
159
|
+
- No spending money
|
|
160
|
+
- No modifying outside systems
|
|
161
|
+
- No autonomous decisions without approval
|
|
162
|
+
`);
|
|
163
|
+
|
|
164
|
+
write(path.join(WORKSPACE_DIR, 'tools.md'),
|
|
165
|
+
`# Compass — Allowed Tools
|
|
166
|
+
|
|
167
|
+
## Allowed
|
|
168
|
+
- read_file: state files only
|
|
169
|
+
- write_file: state files only
|
|
170
|
+
- slack_post: configured channel only
|
|
171
|
+
|
|
172
|
+
## Blocked
|
|
173
|
+
- send_email: NO
|
|
174
|
+
- http_request to external URLs: NO
|
|
175
|
+
- execute_command: NO
|
|
176
|
+
- delete_file: NO
|
|
177
|
+
`);
|
|
178
|
+
|
|
179
|
+
// ── State files ───────────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
const stateTables = {
|
|
182
|
+
'tasks.md': '# Tasks\n\n| Task | Owner | Status | Deadline | Blocker | Priority |\n|------|-------|--------|----------|---------|----------|\n',
|
|
183
|
+
'decisions.md': '# Decisions\n\n| Decision | Why | Alternatives | Source | Date |\n|----------|-----|--------------|--------|------|\n',
|
|
184
|
+
'blockers.md': '# Blockers\n\n| Blocker | Blocked Item | Owner | Date Added | Age |\n|---------|-------------|-------|------------|-----|\n',
|
|
185
|
+
'commitments.md': '# Commitments\n\n| Commitment | Owner | Deadline | Status | Source |\n|------------|-------|----------|--------|--------|\n',
|
|
186
|
+
'metrics.md': '# Metrics\n\n| Metric | Value | Date | Source |\n|--------|-------|------|--------|\n',
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
for (const [name, content] of Object.entries(stateTables)) {
|
|
190
|
+
write(path.join(STATE_DIR, name), content);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
fs.mkdirSync(path.join(STATE_DIR, 'daily-summaries'), { recursive: true });
|
|
194
|
+
fs.mkdirSync(path.join(STATE_DIR, 'weekly-summaries'), { recursive: true });
|
|
195
|
+
|
|
196
|
+
// ── Copy skills into workspace ────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
copyDir(SKILLS_SRC, path.join(WORKSPACE_DIR, 'skills'));
|
|
199
|
+
|
|
200
|
+
// ── Cron jobs (Linux only) ────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
if (process.platform === 'linux') {
|
|
203
|
+
setupCron(dailyTime, weeklyDay, weeklyTime);
|
|
204
|
+
} else {
|
|
205
|
+
console.log(' Cron: skipped (not Linux). Set up scheduled reports manually.');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
console.log(' ✅ Config written to', OPENCLAW_DIR);
|
|
209
|
+
console.log(' ✅ State files created in', STATE_DIR);
|
|
210
|
+
console.log('\n─────────────────────────────────────────');
|
|
211
|
+
console.log(' Compass is ready.\n');
|
|
212
|
+
console.log(' Start: compass start');
|
|
213
|
+
console.log(' Daily report: compass report --type daily');
|
|
214
|
+
console.log(' Weekly report: compass report --type weekly');
|
|
215
|
+
console.log('\n Next: open Telegram → find your bot → send /start\n');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── start ─────────────────────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
function start() {
|
|
221
|
+
console.log('🧭 Starting Compass...');
|
|
222
|
+
runOpenClaw('start', '--headless');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ── report ────────────────────────────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
function report() {
|
|
228
|
+
const typeIdx = process.argv.indexOf('--type');
|
|
229
|
+
const type = typeIdx !== -1 ? process.argv[typeIdx + 1] : 'daily';
|
|
230
|
+
if (!['daily', 'weekly'].includes(type)) {
|
|
231
|
+
console.error(' Type must be "daily" or "weekly"');
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
runOpenClaw('run', 'compass-report', '--type', type);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ── help ──────────────────────────────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
function help() {
|
|
240
|
+
console.log(`
|
|
241
|
+
🧭 Compass — AI Chief of Staff
|
|
242
|
+
|
|
243
|
+
Usage:
|
|
244
|
+
compass init First-time setup (run once)
|
|
245
|
+
compass start Start the Compass agent
|
|
246
|
+
compass report --type daily Trigger a daily standup report
|
|
247
|
+
compass report --type weekly Trigger a weekly review report
|
|
248
|
+
`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ── utilities ─────────────────────────────────────────────────────────────────
|
|
252
|
+
|
|
253
|
+
function buildBootMd(founderName) {
|
|
254
|
+
return `# Compass — System Identity
|
|
255
|
+
|
|
256
|
+
You are Compass. You are the chief of staff and execution memory for a small startup.
|
|
257
|
+
|
|
258
|
+
You are not a general assistant. You do not chat. You extract signal, maintain memory, and report clearly.
|
|
259
|
+
|
|
260
|
+
## Who you serve
|
|
261
|
+
|
|
262
|
+
${founderName} is the founder and final decision maker. They have final authority on all strategic decisions.
|
|
263
|
+
|
|
264
|
+
Preferences:
|
|
265
|
+
- Concise bullet points, not paragraphs
|
|
266
|
+
- Fast signal, not theory
|
|
267
|
+
- Early warning on problems
|
|
268
|
+
- Clear ownership and practical next steps
|
|
269
|
+
|
|
270
|
+
## What you do
|
|
271
|
+
|
|
272
|
+
1. Read company conversations from Telegram
|
|
273
|
+
2. Extract tasks, decisions, blockers, commitments, metrics
|
|
274
|
+
3. Update state files in ${STATE_DIR}/
|
|
275
|
+
4. Answer questions from stored context only
|
|
276
|
+
5. Post scheduled summaries to Slack
|
|
277
|
+
|
|
278
|
+
## State files you maintain
|
|
279
|
+
|
|
280
|
+
- ${STATE_DIR}/tasks.md
|
|
281
|
+
- ${STATE_DIR}/decisions.md
|
|
282
|
+
- ${STATE_DIR}/blockers.md
|
|
283
|
+
- ${STATE_DIR}/commitments.md
|
|
284
|
+
- ${STATE_DIR}/metrics.md
|
|
285
|
+
- ${STATE_DIR}/daily-summaries/
|
|
286
|
+
- ${STATE_DIR}/weekly-summaries/
|
|
287
|
+
|
|
288
|
+
## Extraction rules
|
|
289
|
+
|
|
290
|
+
When you read a message or conversation, look for:
|
|
291
|
+
|
|
292
|
+
- Tasks: "I'll do X" → owner + action + deadline
|
|
293
|
+
- Decisions: "We're going with X" → what + why + alternatives
|
|
294
|
+
- Blockers: "Can't proceed because X" → blocked item + reason + who is blocked
|
|
295
|
+
- Commitments: "I'll send X by Friday" → owner + deliverable + deadline
|
|
296
|
+
- Metrics: "We hit 100 users" → metric + value + date
|
|
297
|
+
|
|
298
|
+
## Communication rules
|
|
299
|
+
|
|
300
|
+
Telegram:
|
|
301
|
+
- Stay silent unless directly mentioned (@Compass) or explicitly commanded
|
|
302
|
+
- When asked, respond in 3-5 bullet points maximum
|
|
303
|
+
- Never derail a conversation
|
|
304
|
+
|
|
305
|
+
Slack:
|
|
306
|
+
- Post proactive daily summaries at standup time
|
|
307
|
+
- Post weekly reviews on Monday morning
|
|
308
|
+
- Lead with blockers and risks
|
|
309
|
+
- Keep every message scannable in 30 seconds
|
|
310
|
+
|
|
311
|
+
## What you must not do in Phase 1
|
|
312
|
+
|
|
313
|
+
- No sending emails
|
|
314
|
+
- No publishing content externally
|
|
315
|
+
- No spending money
|
|
316
|
+
- No modifying outside systems
|
|
317
|
+
- No making business decisions
|
|
318
|
+
`;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function copyDir(src, dest) {
|
|
322
|
+
if (!fs.existsSync(src)) return;
|
|
323
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
324
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
325
|
+
const srcPath = path.join(src, entry.name);
|
|
326
|
+
const destPath = path.join(dest, entry.name);
|
|
327
|
+
if (entry.isDirectory()) copyDir(srcPath, destPath);
|
|
328
|
+
else fs.copyFileSync(srcPath, destPath);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function setupCron(dailyTime, weeklyDay, weeklyTime) {
|
|
333
|
+
const bin = resolveOpenClaw();
|
|
334
|
+
const [dH, dM] = dailyTime.split(':');
|
|
335
|
+
const [wH, wM] = weeklyTime.split(':');
|
|
336
|
+
const dowMap = { SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6 };
|
|
337
|
+
const dow = dowMap[weeklyDay.toUpperCase()] ?? 1;
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
let existing = '';
|
|
341
|
+
try { existing = execSync('crontab -l', { encoding: 'utf8' }); } catch {}
|
|
342
|
+
const lines = existing.split('\n').filter(l => l && !l.includes('compass report'));
|
|
343
|
+
lines.push(`${dM} ${dH} * * * ${bin} report --type daily`);
|
|
344
|
+
lines.push(`${wM} ${wH} * * ${dow} ${bin} report --type weekly`);
|
|
345
|
+
|
|
346
|
+
const tmp = path.join(os.tmpdir(), 'compass-cron');
|
|
347
|
+
fs.writeFileSync(tmp, lines.join('\n') + '\n');
|
|
348
|
+
execSync(`crontab ${tmp}`);
|
|
349
|
+
fs.unlinkSync(tmp);
|
|
350
|
+
console.log(' ✅ Cron jobs set.');
|
|
351
|
+
} catch {
|
|
352
|
+
console.log(' Cron setup failed — set up manually if needed.');
|
|
353
|
+
}
|
|
354
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "compass-ai",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI chief of staff for startups — built on OpenClaw",
|
|
5
|
+
"bin": {
|
|
6
|
+
"compass": "./bin/compass.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"skills/",
|
|
11
|
+
"compass-boot.md"
|
|
12
|
+
],
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"openclaw": "latest"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"keywords": ["ai", "chief-of-staff", "telegram", "slack", "openclaw"],
|
|
20
|
+
"license": "MIT"
|
|
21
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compass-extract
|
|
3
|
+
description: Compass signal extractor. Reads a Telegram conversation and extracts tasks, decisions, blockers, commitments, and metrics into the compass-state files.
|
|
4
|
+
metadata: { "openclaw": { "emoji": "🧭" } }
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Compass Extract
|
|
8
|
+
|
|
9
|
+
## What this skill does
|
|
10
|
+
|
|
11
|
+
Reads raw Telegram conversation text and extracts operational signal into structured state files.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
- After receiving a batch of Telegram messages
|
|
16
|
+
- When asked "extract from this conversation"
|
|
17
|
+
- Automatically on new Telegram message batches (when configured)
|
|
18
|
+
|
|
19
|
+
## Inputs
|
|
20
|
+
|
|
21
|
+
- `conversation`: raw text of the Telegram messages to process
|
|
22
|
+
- `channel`: which Telegram channel/group the messages came from
|
|
23
|
+
- `date`: date of the conversation (ISO format)
|
|
24
|
+
|
|
25
|
+
## Extraction targets
|
|
26
|
+
|
|
27
|
+
### Tasks
|
|
28
|
+
Pattern: someone commits to doing something
|
|
29
|
+
- "I'll finish X" → task
|
|
30
|
+
- "Can you do X by Y?" + acceptance → task
|
|
31
|
+
- "We need to X before Z" → task
|
|
32
|
+
Format to write:
|
|
33
|
+
```
|
|
34
|
+
- **Task**: [action]
|
|
35
|
+
**Owner**: [name]
|
|
36
|
+
**Status**: todo
|
|
37
|
+
**Deadline**: [date or none]
|
|
38
|
+
**Blocker**: none
|
|
39
|
+
**Priority**: [high/medium/low based on urgency signals]
|
|
40
|
+
**Source**: [channel] [date]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Decisions
|
|
44
|
+
Pattern: a choice is made between options
|
|
45
|
+
- "We're going with X"
|
|
46
|
+
- "Decided to X instead of Y"
|
|
47
|
+
- "Let's do X" with agreement
|
|
48
|
+
Format to write:
|
|
49
|
+
```
|
|
50
|
+
- **Decision**: [what was decided]
|
|
51
|
+
**Why**: [reason given, or "not stated"]
|
|
52
|
+
**Alternatives**: [other options mentioned, or "none mentioned"]
|
|
53
|
+
**Source**: [channel] [date]
|
|
54
|
+
**Date**: [date]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Blockers
|
|
58
|
+
Pattern: something is preventing progress
|
|
59
|
+
- "Can't do X because Y"
|
|
60
|
+
- "Stuck on X"
|
|
61
|
+
- "Waiting on X before we can Y"
|
|
62
|
+
Format to write:
|
|
63
|
+
```
|
|
64
|
+
- **Blocker**: [what is blocked] — [reason]
|
|
65
|
+
**Affects**: [task or person]
|
|
66
|
+
**Raised**: [date]
|
|
67
|
+
**Age**: 0 days
|
|
68
|
+
**Status**: open
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Commitments
|
|
72
|
+
Pattern: a promise with a deadline
|
|
73
|
+
- "I'll send X by Friday"
|
|
74
|
+
- "Will have X ready by [date]"
|
|
75
|
+
Format to write:
|
|
76
|
+
```
|
|
77
|
+
- **Commitment**: [what was promised]
|
|
78
|
+
**Owner**: [who promised it]
|
|
79
|
+
**Deadline**: [date]
|
|
80
|
+
**Status**: pending
|
|
81
|
+
**Source**: [channel] [date]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Metrics
|
|
85
|
+
Pattern: a number or status update about the business
|
|
86
|
+
- "We hit 100 users"
|
|
87
|
+
- "Revenue is X"
|
|
88
|
+
- "Conversion rate dropped to Y"
|
|
89
|
+
Format to write:
|
|
90
|
+
```
|
|
91
|
+
- **Metric**: [metric name]
|
|
92
|
+
**Value**: [value]
|
|
93
|
+
**Date**: [date]
|
|
94
|
+
**Source**: [who reported it]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Output
|
|
98
|
+
|
|
99
|
+
After extraction, append new items to the appropriate state files:
|
|
100
|
+
- Tasks → {STATE_DIR}/tasks.md
|
|
101
|
+
- Decisions → {STATE_DIR}/decisions.md
|
|
102
|
+
- Blockers → {STATE_DIR}/blockers.md
|
|
103
|
+
- Commitments → {STATE_DIR}/commitments.md
|
|
104
|
+
- Metrics → {STATE_DIR}/metrics.md
|
|
105
|
+
|
|
106
|
+
Then respond with a brief extraction summary:
|
|
107
|
+
```
|
|
108
|
+
Extracted from [channel] [date]:
|
|
109
|
+
- [N] tasks
|
|
110
|
+
- [N] decisions
|
|
111
|
+
- [N] blockers
|
|
112
|
+
- [N] commitments
|
|
113
|
+
- [N] metrics
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
List only the items found. Skip categories with zero extractions.
|
|
117
|
+
|
|
118
|
+
## Rules
|
|
119
|
+
|
|
120
|
+
- Do not invent items that are not clearly stated
|
|
121
|
+
- If ambiguous, note it as a question rather than inventing an answer
|
|
122
|
+
- Do not duplicate items already in state files
|
|
123
|
+
- Flag stale blockers (>2 days old) with a note
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compass-report
|
|
3
|
+
description: Compass reporting skill. Generates daily standup summaries and weekly reviews from compass-state files and posts them to Slack.
|
|
4
|
+
metadata: { "openclaw": { "emoji": "📊", "requires": { "config": ["channels.slack"] } } }
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Compass Report
|
|
8
|
+
|
|
9
|
+
## What this skill does
|
|
10
|
+
|
|
11
|
+
Reads the compass-state files and produces structured summaries. Posts them to a configured Slack channel.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
- Daily: generate standup summary (post to #standup or configured channel)
|
|
16
|
+
- Weekly: generate weekly review (post to #general or configured channel)
|
|
17
|
+
- On demand: "compass report" or "@Compass summary"
|
|
18
|
+
|
|
19
|
+
## Report types
|
|
20
|
+
|
|
21
|
+
### Daily Standup
|
|
22
|
+
|
|
23
|
+
Structure:
|
|
24
|
+
```
|
|
25
|
+
*Compass Daily — [Date]*
|
|
26
|
+
|
|
27
|
+
*Priorities today*
|
|
28
|
+
[top 3 active tasks by priority]
|
|
29
|
+
|
|
30
|
+
*Blockers*
|
|
31
|
+
[all open blockers — none if clear]
|
|
32
|
+
|
|
33
|
+
*Recent wins*
|
|
34
|
+
[tasks completed or decisions made since last standup]
|
|
35
|
+
|
|
36
|
+
*Watch*
|
|
37
|
+
[commitments due soon, stale items, repeated issues]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Rules:
|
|
41
|
+
- Maximum 15 lines total
|
|
42
|
+
- Lead with blockers if any exist
|
|
43
|
+
- Only list tasks that are in-progress or high priority
|
|
44
|
+
- Flag any commitment due within 48 hours
|
|
45
|
+
|
|
46
|
+
### Weekly Review
|
|
47
|
+
|
|
48
|
+
Structure:
|
|
49
|
+
```
|
|
50
|
+
*Compass Weekly — Week of [Date]*
|
|
51
|
+
|
|
52
|
+
*Progress*
|
|
53
|
+
[tasks completed this week]
|
|
54
|
+
|
|
55
|
+
*Decisions made*
|
|
56
|
+
[decisions logged this week]
|
|
57
|
+
|
|
58
|
+
*Misses*
|
|
59
|
+
[commitments missed or overdue]
|
|
60
|
+
|
|
61
|
+
*Metrics movement*
|
|
62
|
+
[any metrics logged this week]
|
|
63
|
+
|
|
64
|
+
*Open blockers*
|
|
65
|
+
[all blockers still open — age shown]
|
|
66
|
+
|
|
67
|
+
*Next priorities*
|
|
68
|
+
[top tasks for the coming week]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Rules:
|
|
72
|
+
- Emphasize changes, not noise
|
|
73
|
+
- Call out repeated blockers explicitly
|
|
74
|
+
- Flag overdue commitments clearly
|
|
75
|
+
- Maximum 25 lines total
|
|
76
|
+
|
|
77
|
+
## How to generate
|
|
78
|
+
|
|
79
|
+
1. Read {STATE_DIR}/tasks.md
|
|
80
|
+
2. Read {STATE_DIR}/blockers.md
|
|
81
|
+
3. Read {STATE_DIR}/commitments.md
|
|
82
|
+
4. Read {STATE_DIR}/metrics.md (for weekly only)
|
|
83
|
+
5. Read {STATE_DIR}/decisions.md (for weekly only)
|
|
84
|
+
6. Compose the report
|
|
85
|
+
7. Post to Slack using the slack skill
|
|
86
|
+
8. Save a copy to {STATE_DIR}/daily-summaries/[date].md or {STATE_DIR}/weekly-summaries/[date].md
|
|
87
|
+
|
|
88
|
+
## Slack posting
|
|
89
|
+
|
|
90
|
+
Use the slack skill to post to the configured report channel.
|
|
91
|
+
Keep formatting clean — use Slack markdown (*bold*, bullet lists).
|
|
92
|
+
Do not use emoji unless a blocker needs a red flag.
|
|
93
|
+
|
|
94
|
+
## On-demand questions
|
|
95
|
+
|
|
96
|
+
If mentioned directly (@Compass), answer from state files only.
|
|
97
|
+
Do not invent status. If status is unknown, say so.
|
|
98
|
+
|
|
99
|
+
Examples:
|
|
100
|
+
- "@Compass what's blocked?" → list open blockers from blockers.md
|
|
101
|
+
- "@Compass what did we decide about X?" → search decisions.md
|
|
102
|
+
- "@Compass who owns the landing page?" → search tasks.md
|
|
103
|
+
- "@Compass what are our metrics?" → read metrics.md
|