crewly 1.4.61 → 1.4.63
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/config/skills/agent/content-calendar/execute.sh +2 -2
- package/config/skills/agent/marketing/brand-onboarding/SKILL.md +76 -0
- package/config/skills/agent/marketing/brand-onboarding/execute.sh +312 -0
- package/config/skills/agent/marketing/submit-for-approval/SKILL.md +73 -0
- package/config/skills/agent/marketing/submit-for-approval/execute.sh +138 -0
- package/config/skills/agent/marketing/weekly-content-planning/SKILL.md +52 -0
- package/config/skills/agent/marketing/weekly-content-planning/execute.sh +202 -0
- package/config/skills/agent/marketing/weekly-content-planning/execute.test.sh +151 -0
- package/config/skills/agent/marketing/weekly-marketing-report/SKILL.md +51 -0
- package/config/skills/agent/marketing/weekly-marketing-report/execute.sh +190 -0
- package/config/skills/agent/marketing/weekly-marketing-report/execute.test.sh +241 -0
- package/config/skills/orchestrator/send-to-remote/SKILL.md +51 -0
- package/config/skills/orchestrator/send-to-remote/execute.sh +114 -0
- package/config/templates/marketing-team/README.md +42 -0
- package/config/templates/marketing-team/goals.md +21 -0
- package/config/templates/marketing-team/knowledge/docs/brand-voice-guide.md +61 -0
- package/config/templates/marketing-team/knowledge/docs/content-best-practices.md +64 -0
- package/config/templates/marketing-team/knowledge/index.json +24 -0
- package/config/templates/marketing-team/learned-patterns.json +5 -0
- package/config/templates/marketing-team/norms/brand-consistency.md +40 -0
- package/config/templates/marketing-team/norms/content-quality-checklist.md +45 -0
- package/config/templates/marketing-team/quality-gates.yaml +47 -0
- package/config/templates/marketing-team/roles/analyst.md +63 -0
- package/config/templates/marketing-team/roles/strategist.md +26 -0
- package/config/templates/marketing-team/roles/writer.md +58 -0
- package/config/templates/marketing-team/template.json +90 -0
- package/config/templates/marketing-team/workflows/weekly-content-cycle.yaml +48 -0
- package/dist/backend/backend/src/constants.d.ts +9 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +9 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.d.ts +16 -0
- package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.js +140 -0
- package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/cross-machine/index.d.ts +7 -0
- package/dist/backend/backend/src/controllers/cross-machine/index.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/cross-machine/index.js +7 -0
- package/dist/backend/backend/src/controllers/cross-machine/index.js.map +1 -0
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +3 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +46 -6
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts +13 -0
- package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-builder.service.js +50 -5
- package/dist/backend/backend/src/services/ai/prompt-builder.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts +8 -0
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js +52 -3
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts +5 -1
- package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay-client.service.js +14 -2
- package/dist/backend/backend/src/services/cloud/relay-client.service.js.map +1 -1
- package/dist/backend/backend/src/services/index.d.ts +2 -0
- package/dist/backend/backend/src/services/index.d.ts.map +1 -1
- package/dist/backend/backend/src/services/index.js +2 -0
- package/dist/backend/backend/src/services/index.js.map +1 -1
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.d.ts +155 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.js +469 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.d.ts +107 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.js +104 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.service.d.ts +124 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.service.js +256 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.service.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.types.d.ts +80 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.types.js +16 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.types.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/index.d.ts +12 -0
- package/dist/backend/backend/src/services/onboarding/index.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/index.js +10 -0
- package/dist/backend/backend/src/services/onboarding/index.js.map +1 -0
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.d.ts +147 -0
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.js +306 -0
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.js.map +1 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +7 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +76 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.js +8 -2
- package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/backend/backend/src/types/cross-machine.types.d.ts +103 -0
- package/dist/backend/backend/src/types/cross-machine.types.d.ts.map +1 -0
- package/dist/backend/backend/src/types/cross-machine.types.js +47 -0
- package/dist/backend/backend/src/types/cross-machine.types.js.map +1 -0
- package/dist/cli/backend/src/constants.d.ts +9 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +9 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/cli/src/commands/cloud.d.ts +18 -2
- package/dist/cli/cli/src/commands/cloud.d.ts.map +1 -1
- package/dist/cli/cli/src/commands/cloud.js +72 -16
- package/dist/cli/cli/src/commands/cloud.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for weekly-marketing-report skill
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
EXECUTE="${SCRIPT_DIR}/execute.sh"
|
|
7
|
+
TEST_PROJECT=$(mktemp -d)
|
|
8
|
+
PASS=0
|
|
9
|
+
FAIL=0
|
|
10
|
+
|
|
11
|
+
cleanup() {
|
|
12
|
+
rm -rf "$TEST_PROJECT"
|
|
13
|
+
}
|
|
14
|
+
trap cleanup EXIT
|
|
15
|
+
|
|
16
|
+
assert_eq() {
|
|
17
|
+
local test_name="$1" expected="$2" actual="$3"
|
|
18
|
+
if [ "$expected" = "$actual" ]; then
|
|
19
|
+
echo " ✓ ${test_name}"
|
|
20
|
+
PASS=$((PASS + 1))
|
|
21
|
+
else
|
|
22
|
+
echo " ✗ ${test_name}: expected '${expected}', got '${actual}'"
|
|
23
|
+
FAIL=$((FAIL + 1))
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
assert_true() {
|
|
28
|
+
local test_name="$1" value="$2"
|
|
29
|
+
if [ "$value" = "true" ]; then
|
|
30
|
+
echo " ✓ ${test_name}"
|
|
31
|
+
PASS=$((PASS + 1))
|
|
32
|
+
else
|
|
33
|
+
echo " ✗ ${test_name}: expected true, got '${value}'"
|
|
34
|
+
FAIL=$((FAIL + 1))
|
|
35
|
+
fi
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
echo "=== Weekly Marketing Report Skill Tests ==="
|
|
39
|
+
echo ""
|
|
40
|
+
|
|
41
|
+
# ─────────────────────────────────────────────
|
|
42
|
+
# Test 1: Missing required parameters
|
|
43
|
+
# ─────────────────────────────────────────────
|
|
44
|
+
echo "Test 1: Missing required parameters"
|
|
45
|
+
RESULT=$(bash "$EXECUTE" '{}' 2>&1 || true)
|
|
46
|
+
HAS_ERROR=$(echo "$RESULT" | jq -r '.error // empty' 2>/dev/null || echo "")
|
|
47
|
+
assert_true "exits with error on missing params" "$([ -n "$HAS_ERROR" ] && echo true || echo false)"
|
|
48
|
+
|
|
49
|
+
# ─────────────────────────────────────────────
|
|
50
|
+
# Test 2: Empty calendar (no file)
|
|
51
|
+
# ─────────────────────────────────────────────
|
|
52
|
+
echo "Test 2: No calendar file"
|
|
53
|
+
EMPTY_PROJECT=$(mktemp -d)
|
|
54
|
+
RESULT=$(bash "$EXECUTE" "{
|
|
55
|
+
\"projectPath\": \"${EMPTY_PROJECT}\",
|
|
56
|
+
\"weekEndDate\": \"2026-03-29\",
|
|
57
|
+
\"businessName\": \"EmptyBiz\"
|
|
58
|
+
}")
|
|
59
|
+
|
|
60
|
+
assert_true "returns success" "$(echo "$RESULT" | jq -r '.success')"
|
|
61
|
+
assert_eq "totalPosts is 0" "0" "$(echo "$RESULT" | jq -r '.report.totalPosts')"
|
|
62
|
+
assert_true "has recommendations" "$(echo "$RESULT" | jq -r '.report.recommendations | length > 0')"
|
|
63
|
+
assert_true "has markdown" "$(echo "$RESULT" | jq -r '.markdown' | grep -q 'Weekly Marketing Report' && echo true || echo false)"
|
|
64
|
+
rm -rf "$EMPTY_PROJECT"
|
|
65
|
+
|
|
66
|
+
# ─────────────────────────────────────────────
|
|
67
|
+
# Test 3: Calendar with sample data
|
|
68
|
+
# ─────────────────────────────────────────────
|
|
69
|
+
echo "Test 3: Calendar with sample data"
|
|
70
|
+
CALENDAR_DIR="${TEST_PROJECT}/.crewly/content"
|
|
71
|
+
mkdir -p "$CALENDAR_DIR"
|
|
72
|
+
cat > "${CALENDAR_DIR}/calendar.json" << 'CALEOF'
|
|
73
|
+
{
|
|
74
|
+
"entries": [
|
|
75
|
+
{
|
|
76
|
+
"id": "cc-test-001",
|
|
77
|
+
"title": "AI Trends Thread",
|
|
78
|
+
"platform": "x",
|
|
79
|
+
"type": "thread",
|
|
80
|
+
"scheduledDate": "2026-03-23",
|
|
81
|
+
"status": "published",
|
|
82
|
+
"publishedAt": "2026-03-23T10:00:00Z",
|
|
83
|
+
"line": "crewly",
|
|
84
|
+
"topic": "AI trends",
|
|
85
|
+
"notes": "",
|
|
86
|
+
"tags": ["ai"],
|
|
87
|
+
"createdAt": "2026-03-22T00:00:00Z",
|
|
88
|
+
"updatedAt": "2026-03-23T10:00:00Z"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "cc-test-002",
|
|
92
|
+
"title": "LinkedIn Thought Leadership",
|
|
93
|
+
"platform": "linkedin",
|
|
94
|
+
"type": "post",
|
|
95
|
+
"scheduledDate": "2026-03-24",
|
|
96
|
+
"status": "published",
|
|
97
|
+
"publishedAt": "2026-03-24T09:00:00Z",
|
|
98
|
+
"line": "crewly",
|
|
99
|
+
"topic": "Leadership",
|
|
100
|
+
"notes": "",
|
|
101
|
+
"tags": ["leadership"],
|
|
102
|
+
"createdAt": "2026-03-22T00:00:00Z",
|
|
103
|
+
"updatedAt": "2026-03-24T09:00:00Z"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"id": "cc-test-003",
|
|
107
|
+
"title": "Product Update Post",
|
|
108
|
+
"platform": "x",
|
|
109
|
+
"type": "post",
|
|
110
|
+
"scheduledDate": "2026-03-25",
|
|
111
|
+
"status": "draft",
|
|
112
|
+
"line": "crewly",
|
|
113
|
+
"topic": "Product update",
|
|
114
|
+
"notes": "",
|
|
115
|
+
"tags": ["product"],
|
|
116
|
+
"createdAt": "2026-03-22T00:00:00Z",
|
|
117
|
+
"updatedAt": "2026-03-22T00:00:00Z"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "cc-test-004",
|
|
121
|
+
"title": "Community Question",
|
|
122
|
+
"platform": "linkedin",
|
|
123
|
+
"type": "post",
|
|
124
|
+
"scheduledDate": "2026-03-26",
|
|
125
|
+
"status": "ready",
|
|
126
|
+
"line": "crewly",
|
|
127
|
+
"topic": "Community",
|
|
128
|
+
"notes": "",
|
|
129
|
+
"tags": ["community"],
|
|
130
|
+
"createdAt": "2026-03-22T00:00:00Z",
|
|
131
|
+
"updatedAt": "2026-03-22T00:00:00Z"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"id": "cc-test-old",
|
|
135
|
+
"title": "Old Post (outside week)",
|
|
136
|
+
"platform": "x",
|
|
137
|
+
"type": "post",
|
|
138
|
+
"scheduledDate": "2026-03-10",
|
|
139
|
+
"status": "published",
|
|
140
|
+
"publishedAt": "2026-03-10T10:00:00Z",
|
|
141
|
+
"line": "crewly",
|
|
142
|
+
"topic": "Old",
|
|
143
|
+
"notes": "",
|
|
144
|
+
"tags": [],
|
|
145
|
+
"createdAt": "2026-03-09T00:00:00Z",
|
|
146
|
+
"updatedAt": "2026-03-10T10:00:00Z"
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"metadata": {
|
|
150
|
+
"createdAt": "2026-03-22T00:00:00Z",
|
|
151
|
+
"version": "1.0"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
CALEOF
|
|
155
|
+
|
|
156
|
+
RESULT=$(bash "$EXECUTE" "{
|
|
157
|
+
\"projectPath\": \"${TEST_PROJECT}\",
|
|
158
|
+
\"weekEndDate\": \"2026-03-29\",
|
|
159
|
+
\"businessName\": \"TestBiz\"
|
|
160
|
+
}")
|
|
161
|
+
|
|
162
|
+
assert_true "returns success" "$(echo "$RESULT" | jq -r '.success')"
|
|
163
|
+
assert_eq "totalPosts is 4" "4" "$(echo "$RESULT" | jq -r '.report.totalPosts')"
|
|
164
|
+
assert_eq "publishedCount is 2" "2" "$(echo "$RESULT" | jq -r '.report.publishedCount')"
|
|
165
|
+
assert_eq "completionRate is 50" "50" "$(echo "$RESULT" | jq -r '.report.completionRate')"
|
|
166
|
+
|
|
167
|
+
# Platform breakdown
|
|
168
|
+
assert_eq "x platform count" "2" "$(echo "$RESULT" | jq -r '.report.platformBreakdown.x')"
|
|
169
|
+
assert_eq "linkedin platform count" "2" "$(echo "$RESULT" | jq -r '.report.platformBreakdown.linkedin')"
|
|
170
|
+
|
|
171
|
+
# Status breakdown
|
|
172
|
+
assert_eq "published status count" "2" "$(echo "$RESULT" | jq -r '.report.statusBreakdown.published')"
|
|
173
|
+
assert_eq "draft status count" "1" "$(echo "$RESULT" | jq -r '.report.statusBreakdown.draft')"
|
|
174
|
+
assert_eq "ready status count" "1" "$(echo "$RESULT" | jq -r '.report.statusBreakdown.ready')"
|
|
175
|
+
|
|
176
|
+
# Pipeline health
|
|
177
|
+
assert_eq "pipeline draft count" "1" "$(echo "$RESULT" | jq -r '.report.pipelineHealth.draft')"
|
|
178
|
+
assert_eq "pipeline ready count" "1" "$(echo "$RESULT" | jq -r '.report.pipelineHealth.ready')"
|
|
179
|
+
assert_eq "pipeline published count" "2" "$(echo "$RESULT" | jq -r '.report.pipelineHealth.published')"
|
|
180
|
+
|
|
181
|
+
# Recommendations
|
|
182
|
+
assert_true "has recommendations" "$(echo "$RESULT" | jq -r '.report.recommendations | length > 0')"
|
|
183
|
+
|
|
184
|
+
# Markdown output
|
|
185
|
+
assert_true "markdown contains business name" "$(echo "$RESULT" | jq -r '.markdown' | grep -q 'TestBiz' && echo true || echo false)"
|
|
186
|
+
assert_true "markdown contains Platform Breakdown" "$(echo "$RESULT" | jq -r '.markdown' | grep -q 'Platform Breakdown' && echo true || echo false)"
|
|
187
|
+
|
|
188
|
+
# ─────────────────────────────────────────────
|
|
189
|
+
# Test 4: Excludes out-of-range entries
|
|
190
|
+
# ─────────────────────────────────────────────
|
|
191
|
+
echo "Test 4: Excludes out-of-range entries"
|
|
192
|
+
# The old post (2026-03-10) should NOT be in the week ending 2026-03-29
|
|
193
|
+
assert_eq "old post excluded from count" "4" "$(echo "$RESULT" | jq -r '.report.totalPosts')"
|
|
194
|
+
|
|
195
|
+
# ─────────────────────────────────────────────
|
|
196
|
+
# Test 5: Report with custom calendarPath
|
|
197
|
+
# ─────────────────────────────────────────────
|
|
198
|
+
echo "Test 5: Custom calendarPath"
|
|
199
|
+
CUSTOM_CAL=$(mktemp)
|
|
200
|
+
cat > "$CUSTOM_CAL" << 'CALEOF'
|
|
201
|
+
{
|
|
202
|
+
"entries": [
|
|
203
|
+
{
|
|
204
|
+
"id": "cc-custom-001",
|
|
205
|
+
"title": "Custom Post",
|
|
206
|
+
"platform": "x",
|
|
207
|
+
"type": "post",
|
|
208
|
+
"scheduledDate": "2026-04-01",
|
|
209
|
+
"status": "published",
|
|
210
|
+
"publishedAt": "2026-04-01T10:00:00Z",
|
|
211
|
+
"line": "crewly",
|
|
212
|
+
"topic": "Custom",
|
|
213
|
+
"notes": "",
|
|
214
|
+
"tags": [],
|
|
215
|
+
"createdAt": "2026-04-01T00:00:00Z",
|
|
216
|
+
"updatedAt": "2026-04-01T10:00:00Z"
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
"metadata": {"createdAt":"2026-04-01T00:00:00Z","version":"1.0"}
|
|
220
|
+
}
|
|
221
|
+
CALEOF
|
|
222
|
+
|
|
223
|
+
RESULT=$(bash "$EXECUTE" "{
|
|
224
|
+
\"calendarPath\": \"${CUSTOM_CAL}\",
|
|
225
|
+
\"weekEndDate\": \"2026-04-05\",
|
|
226
|
+
\"businessName\": \"CustomBiz\"
|
|
227
|
+
}")
|
|
228
|
+
|
|
229
|
+
assert_true "returns success with custom path" "$(echo "$RESULT" | jq -r '.success')"
|
|
230
|
+
assert_eq "finds 1 entry" "1" "$(echo "$RESULT" | jq -r '.report.totalPosts')"
|
|
231
|
+
rm -f "$CUSTOM_CAL"
|
|
232
|
+
|
|
233
|
+
# ─────────────────────────────────────────────
|
|
234
|
+
# Summary
|
|
235
|
+
# ─────────────────────────────────────────────
|
|
236
|
+
echo ""
|
|
237
|
+
echo "=== Results: ${PASS} passed, ${FAIL} failed ==="
|
|
238
|
+
|
|
239
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
240
|
+
exit 1
|
|
241
|
+
fi
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# send-to-remote
|
|
2
|
+
|
|
3
|
+
Send a cross-machine message to a remote Crewly instance via Slack.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use this skill when you need to communicate with a Crewly orchestrator running on a different machine. Both machines must be connected to the same Slack workspace and have cross-machine messaging configured.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
1. Both machines must have Slack connected
|
|
12
|
+
2. Cross-machine messaging must be configured on both machines (via `POST /api/cross-machine/configure`)
|
|
13
|
+
3. Both machines must be in the same Slack workspace
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Delegate a task to a remote machine
|
|
19
|
+
bash execute.sh '{"targetMachine":"<device-id>","type":"delegate-task","message":"Run the full test suite on branch feature/xyz"}'
|
|
20
|
+
|
|
21
|
+
# Send a message to a remote orchestrator
|
|
22
|
+
bash execute.sh '{"targetMachine":"<device-id>","type":"send-message","message":"Build completed successfully"}'
|
|
23
|
+
|
|
24
|
+
# Request status from a remote machine
|
|
25
|
+
bash execute.sh '{"targetMachine":"<device-id>","type":"status-request"}'
|
|
26
|
+
|
|
27
|
+
# Ping all machines (broadcast)
|
|
28
|
+
bash execute.sh '{"targetMachine":"*","type":"ping"}'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Parameters
|
|
32
|
+
|
|
33
|
+
| Parameter | Required | Description |
|
|
34
|
+
|-----------|----------|-------------|
|
|
35
|
+
| `targetMachine` | Yes | Target device ID (UUID from `~/.crewly/device.json`) or `*` for broadcast |
|
|
36
|
+
| `type` | Yes | Message type: `delegate-task`, `send-message`, `status-request`, `ping` |
|
|
37
|
+
| `message` | No | Human-readable message text |
|
|
38
|
+
| `payload` | No | JSON object with additional structured data |
|
|
39
|
+
|
|
40
|
+
## Message Types
|
|
41
|
+
|
|
42
|
+
- **delegate-task**: Ask the remote orchestrator to execute a task
|
|
43
|
+
- **send-message**: Send an informational message
|
|
44
|
+
- **status-request**: Request status from the remote machine (triggers automatic response)
|
|
45
|
+
- **ping**: Check if a machine is online (triggers automatic pong response)
|
|
46
|
+
|
|
47
|
+
## Finding Device IDs
|
|
48
|
+
|
|
49
|
+
To find a remote machine's device ID:
|
|
50
|
+
1. On the remote machine: `cat ~/.crewly/device.json` → `deviceId` field
|
|
51
|
+
2. Or use `GET /api/cross-machine/status` on the local machine to see your own ID
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Send a cross-machine message to a remote Crewly instance via Slack
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash execute.sh '{"targetMachine":"device-uuid","type":"delegate-task","message":"Run tests"}'
|
|
6
|
+
# bash execute.sh --target device-uuid --type delegate-task --message "Run tests"
|
|
7
|
+
# bash execute.sh --target "*" --type ping # Broadcast ping to all machines
|
|
8
|
+
#
|
|
9
|
+
# Parameters:
|
|
10
|
+
# targetMachine / --target Target device ID or "*" for broadcast (required)
|
|
11
|
+
# type / --type Message type: delegate-task|send-message|status-request|ping (required)
|
|
12
|
+
# message / --message Message text (optional, included in payload)
|
|
13
|
+
# payload / --payload JSON payload string (optional)
|
|
14
|
+
#
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
source "${SCRIPT_DIR}/../_common/lib.sh"
|
|
18
|
+
|
|
19
|
+
print_usage() {
|
|
20
|
+
cat <<'EOF_USAGE'
|
|
21
|
+
Usage:
|
|
22
|
+
bash execute.sh '{"targetMachine":"device-uuid","type":"delegate-task","message":"..."}'
|
|
23
|
+
bash execute.sh --target <deviceId> --type <type> [--message <text>] [--payload <json>]
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--target | -t Target device ID or "*" for broadcast (required)
|
|
27
|
+
--type | -T Message type: delegate-task, send-message, status-request, ping (required)
|
|
28
|
+
--message | -m Message text (optional)
|
|
29
|
+
--payload | -p JSON payload object (optional)
|
|
30
|
+
--help | -h Show this help
|
|
31
|
+
EOF_USAGE
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
INPUT_JSON=""
|
|
35
|
+
TARGET=""
|
|
36
|
+
TYPE=""
|
|
37
|
+
MESSAGE=""
|
|
38
|
+
PAYLOAD=""
|
|
39
|
+
|
|
40
|
+
# Detect legacy JSON argument
|
|
41
|
+
if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
|
|
42
|
+
INPUT_JSON="$1"
|
|
43
|
+
shift || true
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
while [[ $# -gt 0 ]]; do
|
|
47
|
+
case "$1" in
|
|
48
|
+
--target|-t)
|
|
49
|
+
TARGET="$2"
|
|
50
|
+
shift 2
|
|
51
|
+
;;
|
|
52
|
+
--type|-T)
|
|
53
|
+
TYPE="$2"
|
|
54
|
+
shift 2
|
|
55
|
+
;;
|
|
56
|
+
--message|-m)
|
|
57
|
+
MESSAGE="$2"
|
|
58
|
+
shift 2
|
|
59
|
+
;;
|
|
60
|
+
--payload|-p)
|
|
61
|
+
PAYLOAD="$2"
|
|
62
|
+
shift 2
|
|
63
|
+
;;
|
|
64
|
+
--help|-h)
|
|
65
|
+
print_usage
|
|
66
|
+
exit 0
|
|
67
|
+
;;
|
|
68
|
+
*)
|
|
69
|
+
if [[ -z "$INPUT_JSON" && ${1:0:1} == '{' ]]; then
|
|
70
|
+
INPUT_JSON="$1"
|
|
71
|
+
shift
|
|
72
|
+
else
|
|
73
|
+
echo '{"error":"Unknown argument: '"$1"'"}' >&2
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
;;
|
|
77
|
+
esac
|
|
78
|
+
done
|
|
79
|
+
|
|
80
|
+
# Parse JSON input
|
|
81
|
+
if [ -n "$INPUT_JSON" ]; then
|
|
82
|
+
TARGET=${TARGET:-$(printf '%s\n' "$INPUT_JSON" | jq -r '.targetMachine // empty')}
|
|
83
|
+
TYPE=${TYPE:-$(printf '%s\n' "$INPUT_JSON" | jq -r '.type // empty')}
|
|
84
|
+
MESSAGE=${MESSAGE:-$(printf '%s\n' "$INPUT_JSON" | jq -r '.message // empty')}
|
|
85
|
+
PAYLOAD=${PAYLOAD:-$(printf '%s\n' "$INPUT_JSON" | jq -r '.payload // empty')}
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
require_param "targetMachine" "$TARGET"
|
|
89
|
+
require_param "type" "$TYPE"
|
|
90
|
+
|
|
91
|
+
# Build the API request body
|
|
92
|
+
export _TARGET="$TARGET"
|
|
93
|
+
export _TYPE="$TYPE"
|
|
94
|
+
export _MESSAGE="${MESSAGE:-}"
|
|
95
|
+
|
|
96
|
+
if [ -n "$PAYLOAD" ] && [ "$PAYLOAD" != "null" ]; then
|
|
97
|
+
export _PAYLOAD="$PAYLOAD"
|
|
98
|
+
BODY=$(jq -n '{
|
|
99
|
+
targetMachine: env._TARGET,
|
|
100
|
+
type: env._TYPE,
|
|
101
|
+
message: (if env._MESSAGE != "" then env._MESSAGE else null end),
|
|
102
|
+
payload: (env._PAYLOAD | fromjson)
|
|
103
|
+
} | with_entries(select(.value != null))')
|
|
104
|
+
unset _PAYLOAD
|
|
105
|
+
else
|
|
106
|
+
BODY=$(jq -n '{
|
|
107
|
+
targetMachine: env._TARGET,
|
|
108
|
+
type: env._TYPE,
|
|
109
|
+
message: (if env._MESSAGE != "" then env._MESSAGE else null end)
|
|
110
|
+
} | with_entries(select(.value != null))')
|
|
111
|
+
fi
|
|
112
|
+
unset _TARGET _TYPE _MESSAGE
|
|
113
|
+
|
|
114
|
+
api_call POST "/cross-machine/send" "$BODY"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# AI Marketing Team Template
|
|
2
|
+
|
|
3
|
+
A 3-agent AI marketing team designed for SMBs. Automates content strategy, creation, and analytics across social media platforms.
|
|
4
|
+
|
|
5
|
+
## Team Composition
|
|
6
|
+
|
|
7
|
+
- **Maya (Strategist)**: Plans weekly content calendars, researches trends, monitors competitors, and delegates content tasks to the Writer.
|
|
8
|
+
- **Alex (Writer)**: Creates platform-specific social media posts, blog articles, and email newsletters following brand guidelines.
|
|
9
|
+
- **Jordan (Analyst)**: Compiles weekly performance reports with platform metrics, top performers, and data-driven recommendations.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
The team follows a weekly content cycle:
|
|
14
|
+
|
|
15
|
+
1. **Strategy** (Maya) — Research trends, review last week's performance, create content calendar
|
|
16
|
+
2. **Creation** (Alex) — Write content pieces from the calendar, following platform and brand guidelines
|
|
17
|
+
3. **Analysis** (Jordan) — Compile performance metrics, identify top content, recommend optimizations
|
|
18
|
+
|
|
19
|
+
## Included Skills
|
|
20
|
+
|
|
21
|
+
- `content-calendar` — CRUD operations for content scheduling
|
|
22
|
+
- `social-media-post` — Generate platform-specific posts
|
|
23
|
+
- `seo-blog-writer` — Write SEO-optimized blog content
|
|
24
|
+
- `daily-standup-report` — Generate team activity reports
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx crewly create team --template marketing-team
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Customization
|
|
33
|
+
|
|
34
|
+
1. Fill in `knowledge/docs/brand-voice-guide.md` with your brand's voice and tone
|
|
35
|
+
2. Update `goals.md` with your specific marketing KPIs
|
|
36
|
+
3. Adjust content mix ratios in the Strategist's role prompt
|
|
37
|
+
|
|
38
|
+
## Quality Gates
|
|
39
|
+
|
|
40
|
+
- **Strategy Quality** — Validates calendar completeness and content mix
|
|
41
|
+
- **Content Quality** — Checks brand voice, grammar, and platform formatting
|
|
42
|
+
- **Report Quality** — Ensures metrics accuracy and actionable recommendations
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Marketing Team Goals
|
|
2
|
+
|
|
3
|
+
## Objective 1: Content Consistency and Quality
|
|
4
|
+
- **KR1**: Publish a minimum of 5 pieces of content per week across all platforms.
|
|
5
|
+
- **KR2**: Maintain 100% adherence to the weekly content calendar (no missed posts).
|
|
6
|
+
- **KR3**: Achieve a minimum content quality score of 80/100 across all peer reviews.
|
|
7
|
+
|
|
8
|
+
## Objective 2: Audience Growth and Engagement
|
|
9
|
+
- **KR1**: Grow total audience across platforms by 10% month-over-month.
|
|
10
|
+
- **KR2**: Achieve an average engagement rate of 3%+ on X and LinkedIn posts.
|
|
11
|
+
- **KR3**: Increase content reach by 15% week-over-week through optimized posting times and formats.
|
|
12
|
+
|
|
13
|
+
## Objective 3: Brand Voice and Strategic Alignment
|
|
14
|
+
- **KR1**: 100% of published content passes the brand voice consistency checklist.
|
|
15
|
+
- **KR2**: Maintain a content mix ratio within 5% of target (40% educational, 30% engagement, 20% promotional, 10% community).
|
|
16
|
+
- **KR3**: Respond to trending topics within 24 hours of identification.
|
|
17
|
+
|
|
18
|
+
## Objective 4: Data-Driven Optimization
|
|
19
|
+
- **KR1**: Deliver weekly performance reports within 24 hours of the reporting period end.
|
|
20
|
+
- **KR2**: Implement at least 2 data-backed strategy adjustments per week based on analytics.
|
|
21
|
+
- **KR3**: Reduce cost-per-engagement by 10% month-over-month through content optimization.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Brand Voice Guide
|
|
2
|
+
|
|
3
|
+
> **Note**: This is a template. Fill in the sections below during brand onboarding to customize for your business.
|
|
4
|
+
|
|
5
|
+
## Brand Identity
|
|
6
|
+
|
|
7
|
+
- **Business Name**: [Your Business Name]
|
|
8
|
+
- **Industry**: [Your Industry]
|
|
9
|
+
- **Target Audience**: [Primary audience description]
|
|
10
|
+
- **Brand Mission**: [One-sentence mission statement]
|
|
11
|
+
|
|
12
|
+
## Voice Attributes
|
|
13
|
+
|
|
14
|
+
Define 3-5 adjectives that describe your brand voice:
|
|
15
|
+
|
|
16
|
+
1. [e.g., Professional]
|
|
17
|
+
2. [e.g., Approachable]
|
|
18
|
+
3. [e.g., Innovative]
|
|
19
|
+
4. [e.g., Trustworthy]
|
|
20
|
+
|
|
21
|
+
## Tone Guidelines
|
|
22
|
+
|
|
23
|
+
### We Sound Like
|
|
24
|
+
- [Example: A knowledgeable friend who explains complex topics simply]
|
|
25
|
+
- [Example: An enthusiastic expert who genuinely cares about helping]
|
|
26
|
+
|
|
27
|
+
### We Never Sound Like
|
|
28
|
+
- [Example: Condescending or overly academic]
|
|
29
|
+
- [Example: Pushy or sales-driven]
|
|
30
|
+
|
|
31
|
+
## Language Rules
|
|
32
|
+
|
|
33
|
+
### Preferred Terms
|
|
34
|
+
| Instead of... | Use... |
|
|
35
|
+
|---------------|--------|
|
|
36
|
+
| [e.g., Users] | [e.g., Customers] |
|
|
37
|
+
| [e.g., Buy] | [e.g., Get started] |
|
|
38
|
+
|
|
39
|
+
### Words to Avoid
|
|
40
|
+
- [e.g., Cheap, basic, simple]
|
|
41
|
+
|
|
42
|
+
## Platform Tone Adjustments
|
|
43
|
+
|
|
44
|
+
### X (Twitter)
|
|
45
|
+
- Tone shift: [e.g., More casual, punchy]
|
|
46
|
+
- Emoji usage: [e.g., Moderate, 1-2 per post]
|
|
47
|
+
|
|
48
|
+
### LinkedIn
|
|
49
|
+
- Tone shift: [e.g., More professional, thought-leadership]
|
|
50
|
+
- Emoji usage: [e.g., Minimal]
|
|
51
|
+
|
|
52
|
+
### Instagram
|
|
53
|
+
- Tone shift: [e.g., More visual, story-driven]
|
|
54
|
+
- Emoji usage: [e.g., Liberal, use to enhance readability]
|
|
55
|
+
|
|
56
|
+
## Content Pillars
|
|
57
|
+
|
|
58
|
+
1. **[Pillar 1 Name]**: [Description — e.g., Industry insights and trends]
|
|
59
|
+
2. **[Pillar 2 Name]**: [Description — e.g., Product tips and tutorials]
|
|
60
|
+
3. **[Pillar 3 Name]**: [Description — e.g., Customer success stories]
|
|
61
|
+
4. **[Pillar 4 Name]**: [Description — e.g., Behind-the-scenes and culture]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Social Media Content Best Practices
|
|
2
|
+
|
|
3
|
+
## General Principles
|
|
4
|
+
|
|
5
|
+
### The 80/20 Rule
|
|
6
|
+
- 80% of content should inform, educate, or entertain your audience
|
|
7
|
+
- 20% can directly promote your product or service
|
|
8
|
+
|
|
9
|
+
### Platform-Native Content
|
|
10
|
+
- Never cross-post identical content across platforms
|
|
11
|
+
- Adapt format, length, and tone for each platform
|
|
12
|
+
- Respect platform-specific conventions (e.g., threads on X, carousels on LinkedIn)
|
|
13
|
+
|
|
14
|
+
## Content Creation Guidelines
|
|
15
|
+
|
|
16
|
+
### Headlines and Hooks
|
|
17
|
+
- Lead with the most compelling insight or question
|
|
18
|
+
- Use numbers and specifics ("3 lessons" beats "some lessons")
|
|
19
|
+
- Create curiosity gaps without being clickbait
|
|
20
|
+
- Front-load value — readers decide in 2 seconds
|
|
21
|
+
|
|
22
|
+
### Visual Content
|
|
23
|
+
- Use consistent brand colors and fonts
|
|
24
|
+
- Prefer data visualizations over stock photos
|
|
25
|
+
- Screenshots and real examples outperform generic graphics
|
|
26
|
+
- Always include alt text for accessibility
|
|
27
|
+
|
|
28
|
+
### Hashtag Strategy
|
|
29
|
+
- Research hashtag reach before using them
|
|
30
|
+
- Mix high-volume and niche hashtags
|
|
31
|
+
- Create a branded hashtag for campaigns
|
|
32
|
+
- Platform limits: X (1-2), LinkedIn (3-5), Instagram (5-15)
|
|
33
|
+
|
|
34
|
+
## Engagement Best Practices
|
|
35
|
+
|
|
36
|
+
### Timing
|
|
37
|
+
- Post during peak audience activity hours
|
|
38
|
+
- Maintain consistent posting schedule
|
|
39
|
+
- Space posts at least 2 hours apart on the same platform
|
|
40
|
+
|
|
41
|
+
### Community Building
|
|
42
|
+
- Respond to comments within 1 hour during business hours
|
|
43
|
+
- Ask questions to encourage discussion
|
|
44
|
+
- Share and comment on relevant community content
|
|
45
|
+
- Tag relevant people and companies when appropriate
|
|
46
|
+
|
|
47
|
+
### Content Repurposing
|
|
48
|
+
- Turn blog posts into social media threads
|
|
49
|
+
- Convert data points into infographics
|
|
50
|
+
- Extract quotes from long-form content for standalone posts
|
|
51
|
+
- Compile related posts into roundup articles
|
|
52
|
+
|
|
53
|
+
## Measuring Success
|
|
54
|
+
|
|
55
|
+
### Key Metrics by Platform
|
|
56
|
+
- **X**: Impressions, engagement rate, retweets, profile clicks
|
|
57
|
+
- **LinkedIn**: Impressions, engagement rate, shares, profile views
|
|
58
|
+
- **Instagram**: Reach, saves, shares, comments
|
|
59
|
+
- **Facebook**: Reach, engagement, shares, click-through rate
|
|
60
|
+
|
|
61
|
+
### Benchmarks
|
|
62
|
+
- Good engagement rate: 1-3% (varies by platform and audience size)
|
|
63
|
+
- Good click-through rate: 2-5%
|
|
64
|
+
- Healthy follower growth: 2-5% monthly
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"lastUpdated": "2026-03-22T00:00:00.000Z",
|
|
4
|
+
"documents": [
|
|
5
|
+
{
|
|
6
|
+
"id": "content-best-practices",
|
|
7
|
+
"title": "Social Media Content Best Practices",
|
|
8
|
+
"path": "docs/content-best-practices.md",
|
|
9
|
+
"category": "best-practice",
|
|
10
|
+
"tags": ["social-media", "content", "marketing", "engagement"],
|
|
11
|
+
"summary": "Best practices for creating effective social media content across platforms.",
|
|
12
|
+
"createdAt": "2026-03-22T00:00:00.000Z"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "brand-voice-guide",
|
|
16
|
+
"title": "Brand Voice Guide",
|
|
17
|
+
"path": "docs/brand-voice-guide.md",
|
|
18
|
+
"category": "terminology",
|
|
19
|
+
"tags": ["brand", "voice", "tone", "guidelines"],
|
|
20
|
+
"summary": "Template for defining brand voice, tone, and messaging guidelines. Filled during onboarding.",
|
|
21
|
+
"createdAt": "2026-03-22T00:00:00.000Z"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Brand Consistency Rules
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
These rules ensure all marketing output maintains a consistent brand identity across platforms, agents, and content types.
|
|
6
|
+
|
|
7
|
+
## Mandatory Rules
|
|
8
|
+
|
|
9
|
+
### 1. Voice Consistency
|
|
10
|
+
- All content must follow the Brand Voice Guide (see `knowledge/docs/brand-voice-guide.md`)
|
|
11
|
+
- When in doubt about tone, err on the side of the brand's primary voice attribute
|
|
12
|
+
- Platform-specific tone adjustments are allowed, but core voice must remain recognizable
|
|
13
|
+
|
|
14
|
+
### 2. Visual Identity
|
|
15
|
+
- Use approved brand colors in all graphics and visual directions
|
|
16
|
+
- Maintain consistent font usage across platforms
|
|
17
|
+
- Logo placement follows brand guidelines
|
|
18
|
+
- Image style should be consistent (e.g., always flat design, always photography)
|
|
19
|
+
|
|
20
|
+
### 3. Messaging Hierarchy
|
|
21
|
+
- Lead with the value proposition, not the feature
|
|
22
|
+
- Use the approved tagline in profile bios and campaign headers
|
|
23
|
+
- Key messages should be repeated across platforms (rephrased, not copied)
|
|
24
|
+
|
|
25
|
+
### 4. Content Pillars
|
|
26
|
+
- Every post must map to at least one defined content pillar
|
|
27
|
+
- Maintain the target content mix ratio (educational/engagement/promotional/community)
|
|
28
|
+
- Track pillar distribution in the weekly report
|
|
29
|
+
|
|
30
|
+
### 5. Cross-Platform Consistency
|
|
31
|
+
- Same campaign runs on all active platforms (adapted per platform)
|
|
32
|
+
- Announcements go out simultaneously across platforms
|
|
33
|
+
- Profile information is kept in sync (bio, links, profile image)
|
|
34
|
+
|
|
35
|
+
## Enforcement
|
|
36
|
+
|
|
37
|
+
- The Strategist (Maya) is responsible for brand consistency review
|
|
38
|
+
- Content that fails brand consistency must be revised before publication
|
|
39
|
+
- Weekly reports should include a brand consistency score or flag
|
|
40
|
+
- New team members must review the Brand Voice Guide before creating content
|