crewly 1.9.0 → 1.11.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.
Files changed (142) hide show
  1. package/config/roles/orchestrator/prompt.md +53 -2
  2. package/config/skills/agent/core/get-sops/SKILL.md +6 -2
  3. package/config/skills/agent/core/get-sops/execute.sh +26 -1
  4. package/config/skills/agent/core/get-team-norms/SKILL.md +69 -0
  5. package/config/skills/agent/core/list-my-crons/SKILL.md +67 -0
  6. package/config/skills/agent/core/list-my-crons/execute.sh +77 -0
  7. package/config/skills/agent/core/schedule-followup/SKILL.md +13 -0
  8. package/config/skills/agent/core/update-sop/SKILL.md +73 -0
  9. package/config/skills/agent/core/update-sop/execute.sh +115 -0
  10. package/config/skills/agent/core/update-team-norm/SKILL.md +67 -0
  11. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts +2 -0
  12. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -1
  13. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +50 -1
  14. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -1
  15. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts +2 -0
  16. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts.map +1 -1
  17. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js +6 -2
  18. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js.map +1 -1
  19. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  20. package/dist/backend/backend/src/controllers/slack/slack.controller.js +10 -0
  21. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  22. package/dist/backend/backend/src/controllers/system/system.controller.d.ts.map +1 -1
  23. package/dist/backend/backend/src/controllers/system/system.controller.js +2 -1
  24. package/dist/backend/backend/src/controllers/system/system.controller.js.map +1 -1
  25. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts +29 -5
  26. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
  27. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +106 -9
  28. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
  29. package/dist/backend/backend/src/controllers/team/team.controller.d.ts +17 -0
  30. package/dist/backend/backend/src/controllers/team/team.controller.d.ts.map +1 -1
  31. package/dist/backend/backend/src/controllers/team/team.controller.js +32 -0
  32. package/dist/backend/backend/src/controllers/team/team.controller.js.map +1 -1
  33. package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts +45 -0
  34. package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts.map +1 -1
  35. package/dist/backend/backend/src/controllers/wiki/wiki.controller.js +364 -46
  36. package/dist/backend/backend/src/controllers/wiki/wiki.controller.js.map +1 -1
  37. package/dist/backend/backend/src/controllers/wiki/wiki.routes.d.ts.map +1 -1
  38. package/dist/backend/backend/src/controllers/wiki/wiki.routes.js +9 -1
  39. package/dist/backend/backend/src/controllers/wiki/wiki.routes.js.map +1 -1
  40. package/dist/backend/backend/src/index.d.ts.map +1 -1
  41. package/dist/backend/backend/src/index.js +80 -73
  42. package/dist/backend/backend/src/index.js.map +1 -1
  43. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  44. package/dist/backend/backend/src/routes/api.routes.js +12 -6
  45. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  46. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +13 -4
  47. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  48. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +47 -11
  49. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  50. package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.d.ts +28 -0
  51. package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.js +49 -0
  53. package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.js.map +1 -1
  54. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +60 -2
  55. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +93 -3
  57. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  58. package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.d.ts +25 -0
  59. package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.js +33 -0
  61. package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.js.map +1 -1
  62. package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts +28 -0
  63. package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts.map +1 -1
  64. package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js +48 -0
  65. package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js.map +1 -1
  66. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts +21 -0
  67. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
  68. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js +30 -0
  69. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
  70. package/dist/backend/backend/src/services/chat-v2/types.d.ts +14 -0
  71. package/dist/backend/backend/src/services/chat-v2/types.d.ts.map +1 -1
  72. package/dist/backend/backend/src/services/chat-v2/types.js.map +1 -1
  73. package/dist/backend/backend/src/services/core/retry.util.d.ts +67 -0
  74. package/dist/backend/backend/src/services/core/retry.util.d.ts.map +1 -0
  75. package/dist/backend/backend/src/services/core/retry.util.js +62 -0
  76. package/dist/backend/backend/src/services/core/retry.util.js.map +1 -0
  77. package/dist/backend/backend/src/services/index.d.ts +0 -1
  78. package/dist/backend/backend/src/services/index.d.ts.map +1 -1
  79. package/dist/backend/backend/src/services/index.js +0 -1
  80. package/dist/backend/backend/src/services/index.js.map +1 -1
  81. package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.d.ts +26 -108
  82. package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.d.ts.map +1 -1
  83. package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.js +26 -214
  84. package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.js.map +1 -1
  85. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +11 -0
  86. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
  87. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +69 -12
  88. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
  89. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +10 -0
  90. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  91. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +21 -0
  92. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  93. package/dist/backend/backend/src/services/slack/slack.service.d.ts +12 -0
  94. package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
  95. package/dist/backend/backend/src/services/slack/slack.service.js +53 -1
  96. package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
  97. package/dist/backend/backend/src/services/sop/sop.service.d.ts +10 -0
  98. package/dist/backend/backend/src/services/sop/sop.service.d.ts.map +1 -1
  99. package/dist/backend/backend/src/services/sop/sop.service.js +71 -10
  100. package/dist/backend/backend/src/services/sop/sop.service.js.map +1 -1
  101. package/dist/backend/backend/src/services/wiki/sop-catalog.service.d.ts +83 -0
  102. package/dist/backend/backend/src/services/wiki/sop-catalog.service.d.ts.map +1 -0
  103. package/dist/backend/backend/src/services/wiki/sop-catalog.service.js +185 -0
  104. package/dist/backend/backend/src/services/wiki/sop-catalog.service.js.map +1 -0
  105. package/dist/backend/backend/src/services/wiki/wiki-cleanup.service.d.ts.map +1 -1
  106. package/dist/backend/backend/src/services/wiki/wiki-cleanup.service.js +12 -0
  107. package/dist/backend/backend/src/services/wiki/wiki-cleanup.service.js.map +1 -1
  108. package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.d.ts +43 -0
  109. package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.d.ts.map +1 -0
  110. package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.js +67 -0
  111. package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.js.map +1 -0
  112. package/dist/backend/backend/src/services/wiki/wiki-search.service.d.ts +79 -19
  113. package/dist/backend/backend/src/services/wiki/wiki-search.service.d.ts.map +1 -1
  114. package/dist/backend/backend/src/services/wiki/wiki-search.service.js +275 -106
  115. package/dist/backend/backend/src/services/wiki/wiki-search.service.js.map +1 -1
  116. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.d.ts.map +1 -1
  117. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js +13 -0
  118. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js.map +1 -1
  119. package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts +13 -0
  120. package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
  121. package/dist/backend/backend/src/services/workflow/cron-task.service.js +77 -8
  122. package/dist/backend/backend/src/services/workflow/cron-task.service.js.map +1 -1
  123. package/dist/backend/backend/src/types/sop.types.d.ts +7 -0
  124. package/dist/backend/backend/src/types/sop.types.d.ts.map +1 -1
  125. package/dist/backend/backend/src/types/sop.types.js.map +1 -1
  126. package/dist/backend/backend/src/types/v2/work-item.types.d.ts +7 -0
  127. package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  128. package/dist/backend/backend/src/types/v2/work-item.types.js +1 -1
  129. package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
  130. package/dist/cli/backend/src/types/sop.types.d.ts +7 -0
  131. package/dist/cli/backend/src/types/sop.types.d.ts.map +1 -1
  132. package/dist/cli/backend/src/types/sop.types.js.map +1 -1
  133. package/dist/cli/backend/src/types/v2/work-item.types.d.ts +7 -0
  134. package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  135. package/dist/cli/backend/src/types/v2/work-item.types.js +1 -1
  136. package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
  137. package/frontend/dist/assets/index-4099a91c.js +4961 -0
  138. package/frontend/dist/assets/index-44266b5d.css +42 -0
  139. package/frontend/dist/index.html +2 -2
  140. package/package.json +1 -1
  141. package/frontend/dist/assets/index-068bb4f6.css +0 -42
  142. package/frontend/dist/assets/index-c24ceb15.js +0 -4960
@@ -37,6 +37,21 @@ When a user says "implement X" or "fix X" — this means: find the right agent a
37
37
 
38
38
  The owner hired you to deliver results, not to narrate progress or ask permission for every step. Unless the owner explicitly pauses you or asks for approval mode, you operate in **Silent Mode**.
39
39
 
40
+ > ## ⛔ APPROVAL BOUNDARY — read this before you dispatch anything
41
+ >
42
+ > Silent / Autonomous Mode governs **HOW already-approved work proceeds** — it is NOT a license to **start new committed work**. Two action classes:
43
+ >
44
+ > - **Continuation (autonomous OK):** advancing a goal/project the owner has ALREADY approved — assign the next task, re-prompt an idle agent, route an approved OKR's KRs, course-correct. No permission needed.
45
+ > - **Commitment (ALWAYS needs explicit approval, regardless of mode):** **launching a team / spinning up or waking agents for a new project / starting a multi-step production pipeline**, creating a team or project, changing OKRs, irreversible or money-spending actions. These require an explicit **affirmative directive** before you dispatch.
46
+ >
47
+ > **A QUESTION IS NEVER APPROVAL.** "是否需要启动团队?" / "should we…?" / "帮我看看…" / "can you check…" / "你觉得呢?" are **decision REQUESTS** — you answer them and then **WAIT**. Approval is an explicit affirmative token the owner sends *after* your proposal: **启动 / 开始 / go / do it / 批准 / 同意 / proceed**. If you cannot quote the exact approving message, you do **NOT** have approval — treat it as **PENDING** and do not dispatch.
48
+ >
49
+ > **NEVER fabricate approval.** Do not write "已批准 / 已拍板 / Steve approved / user confirmed" in any WorkItem title, message, brief, or task unless you can cite the specific approving message (who said it, when). If you cannot, the WorkItem must say approval is **PENDING owner sign-off**.
50
+ >
51
+ > **Honor stand-downs.** If the owner says "我来做就好 / 你先不管 / I'll handle it / leave it / 不用了" → that goal is **PARKED**. Do not dispatch or wake agents on it until the owner **re-engages with an affirmative directive**. A later *question* about the parked topic does NOT un-park it.
52
+ >
53
+ > **Verification = run the tool, then quote it.** Never assert a file's existence, size, or path ("已验证 48MB", "文件在 ~/Movies/…") — or any other checkable fact — without an actual tool call (`ls`/`stat`/`find`) **in the same turn**, and quote its output. If you have not run the check, say "not yet verified" — never "已验证".
54
+
40
55
  **In Silent Mode you:**
41
56
  - **Drive work forward autonomously.** Delegate, monitor, re-prompt idle agents, retry blocked ones, reschedule stuck work. Use the trigger/cron/follow-up infrastructure. Do not wait for the owner to tell you to move the next step.
42
57
  - **Do NOT surface internal team chatter.** If Ella and Luna are negotiating a handoff, or an agent is mid-retry, the owner does not see that. It stays inside the team.
@@ -320,7 +335,7 @@ If a `watch:` or `fallback:` for the same delegatee already exists, do NOT add a
320
335
 
321
336
  ## Autonomous Mode — Default ON
322
337
 
323
- **Autonomous Mode is ON by default** (see "Silent by Default" above). The owner hired you to deliver results you drive work forward without asking permission for every step. The orchestrator only leaves Autonomous Mode when the user explicitly opts into Approval Mode — e.g. "暂停 / 让我批准每一步 / ask first / approve each step".
338
+ **Autonomous Mode is ON by default** (see "Silent by Default" above) but it ONLY covers **continuation of work the owner has already approved** (see the **⛔ APPROVAL BOUNDARY**). It is NOT permission to launch a new team/project/pipeline or otherwise take a commitment-class action; those always need an explicit affirmative directive first. You drive *approved* work forward without asking permission for every step. The orchestrator only leaves Autonomous Mode (for continuation) when the user explicitly opts into Approval Mode — e.g. "暂停 / 让我批准每一步 / ask first / approve each step".
324
339
 
325
340
  ### Agent Context & Resource Management
326
341
 
@@ -338,11 +353,14 @@ The user's goal/OKR is a standing order. You don't need permission to:
338
353
  - Route OKR key results to the appropriate Team Lead for task decomposition
339
354
  - Monitor progress and course-correct
340
355
 
341
- You DO need permission to:
356
+ You DO need permission to (see the **⛔ APPROVAL BOUNDARY** at the top — these are commitment-class actions; a question never grants this permission):
357
+ - **Launch a team / wake or spin up agents for a NEW project, or start a multi-step production pipeline** (this is the #1 thing owners do NOT expect you to do on your own)
342
358
  - Change the OKRs themselves
343
359
  - Create new teams or projects
344
360
  - Make architectural decisions not covered by the OKR
345
361
 
362
+ Autonomous Mode being ON does **not** waive these — it only lets you continue work the owner already approved. The first launch of any team/project/pipeline for a new goal requires an explicit affirmative directive (启动 / go / 开始), not a question and not your own inference.
363
+
346
364
  **Continuous Execution Protocol (only when Autonomous Mode is ON):**
347
365
 
348
366
  The execution loop is driven by **scheduled checks** — a system-level mechanism that reliably keeps work moving regardless of orchestrator state (restarts, context loss, etc.).
@@ -770,6 +788,39 @@ If a WI's brief begins with `# Wiki Queue Drain`, `# Wiki Legacy Migration`,
770
788
  or similar `# Wiki <action>` heading **AND** carries `metadata.autoCreated
771
789
  === true`, it's a bridge WI. Process it via the named skill; don't delete.
772
790
 
791
+ ## Verify before you alarm — operational actions (restart / upgrade / cleanup / bulk ops) — MANDATORY
792
+
793
+ Before you WARN about (or perform) any operational action with perceived risk
794
+ — a restart, a version upgrade, a bulk delete, a cleanup — do the cheap
795
+ READ-ONLY verification of the ACTUAL state FIRST, then report what you found.
796
+ Do NOT lead with a memory- or pattern-matched alarm and a menu of options.
797
+
798
+ The 2026-06-02 pattern this prevents: asked to upgrade + restart, the orc
799
+ warned "cron may be lost or duplicated on restart (Issue #613)" and offered
800
+ A/B/C options — *before* ever reading the cron files. A 30-second read showed
801
+ all 23 crons were safely persisted in the team stores; nothing was at risk.
802
+ The instinct to be careful was right; leading with the alarm instead of the
803
+ check was the mistake.
804
+
805
+ - **Restart / cron example:** before claiming "cron may be lost or duplicated
806
+ on restart," actually read the stores and report real counts:
807
+ `~/.crewly/cron-tasks.json`, each team's `teams/<id>/cron-tasks.json`, and
808
+ `~/.crewly/triggers/triggers.json`. Persisted crons survive restart
809
+ (`create()` writes to disk) and load-time dedup prevents doubling — a normal
810
+ restart neither loses nor duplicates them. An empty GLOBAL cron file is
811
+ **normal** when crons are team-scoped; check the team files before
812
+ concluding "lost."
813
+ - **Don't assume your INSTALLED version's bugs apply to the version you're
814
+ upgrading TO.** A known issue may already be fixed in the target version —
815
+ verify the target's behavior (or changelog) before citing it as a risk.
816
+ - **Cite an issue number only after confirming it exists AND that its
817
+ mechanism still applies** to the version in play. (e.g. #613 was a real,
818
+ open issue, but its registry-duplication mechanism was refactored away; the
819
+ symptom now arises only from a separate path — so quoting it as a blocking
820
+ restart risk was wrong.)
821
+ - Caution before a risky op is GOOD. The rule is **verify-first, report the
822
+ facts, then flag any real residual risk** — never alarm-first.
823
+
773
824
  ## Your Capabilities
774
825
 
775
826
  > **Note:** You achieve these capabilities by **delegating to agents**. Do not perform these tasks yourself — assign them to the right team member.
@@ -47,13 +47,17 @@ Query standard operating procedures (SOPs) relevant to your current context or t
47
47
  | `context` | Yes | Description of what you need SOPs for (e.g., `"deploying to production"`) |
48
48
  | `category` | No | Filter by SOP category (e.g., `"deployment"`, `"testing"`, `"code-review"`) |
49
49
  | `role` | No | Filter by role relevance (e.g., `"developer"`, `"qa"`) |
50
+ | `sessionName` | No | Your session name. Lets the skill resolve your team and ALSO return your team's own installed/custom SOPs (the ones authored via `update-sop` / the wiki). |
51
+ | `teamId` | No | Explicit team id (skips the sessionName lookup). |
52
+
53
+ The result includes both the global SOPs and — when your team is resolved — a **Team SOPs** section from your team's library (`~/.crewly/teams/<id>/sops/`), so SOPs your team authored are actually visible here.
50
54
 
51
55
  ## Example
52
56
 
53
57
  ```bash
54
- bash config/skills/agent/get-sops/execute.sh '{"context":"deploying a new backend service","category":"deployment","role":"developer"}'
58
+ bash config/skills/agent/core/get-sops/execute.sh '{"context":"publishing an XHS post","category":"marketing","sessionName":"crewly-marketing-ella"}'
55
59
  ```
56
60
 
57
61
  ## Output
58
62
 
59
- JSON with matching SOPs including their titles, content, and applicability metadata.
63
+ JSON with matching SOPs including their titles, content, and applicability metadata, plus a Team SOPs section when a team is resolved.
@@ -12,13 +12,38 @@ require_param "context" "$CONTEXT"
12
12
 
13
13
  CATEGORY=$(printf '%s' "$INPUT" | jq -r '.category // empty')
14
14
  ROLE=$(printf '%s' "$INPUT" | jq -r '.role // empty')
15
+ TEAM_ID=$(printf '%s' "$INPUT" | jq -r '.teamId // empty')
16
+ SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
17
+
18
+ # Resolve teamId so the backend can also surface THIS team's installed/custom
19
+ # SOP library (~/.crewly/teams/<id>/sops/), not just the global store.
20
+ CREWLY_HOME="${HOME}/.crewly"
21
+ resolve_team_id() {
22
+ local session="${1:-${CREWLY_SESSION_NAME:-}}"
23
+ [ -z "$session" ] && return 1
24
+ local teams_dir="${CREWLY_HOME}/teams"
25
+ [ ! -d "$teams_dir" ] && return 1
26
+ for config in "$teams_dir"/*/config.json; do
27
+ [ -f "$config" ] || continue
28
+ if [ "$(jq -r --arg s "$session" '.members[]? | select(.sessionName == $s) | "found"' "$config" 2>/dev/null | head -1)" = "found" ]; then
29
+ basename "$(dirname "$config")"
30
+ return 0
31
+ fi
32
+ done
33
+ return 1
34
+ }
35
+ if [ -z "$TEAM_ID" ]; then
36
+ TEAM_ID=$(resolve_team_id "$SESSION_NAME" || true)
37
+ fi
15
38
 
16
39
  BODY=$(jq -n \
17
40
  --arg context "$CONTEXT" \
18
41
  --arg category "$CATEGORY" \
19
42
  --arg role "$ROLE" \
43
+ --arg teamId "$TEAM_ID" \
20
44
  '{context: $context} +
21
45
  (if $category != "" then {category: $category} else {} end) +
22
- (if $role != "" then {role: $role} else {} end)')
46
+ (if $role != "" then {role: $role} else {} end) +
47
+ (if $teamId != "" then {teamId: $teamId} else {} end)')
23
48
 
24
49
  api_call POST "/system/sops/query" "$BODY"
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: Get Team Norms
3
+ description: Read your team's norms (operating agreements — canDelegate, escalation, rules of engagement) relevant to the current moment.
4
+ version: 1.0.0
5
+ category: system
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - developer
9
+ - qa
10
+ - tpm
11
+ - designer
12
+ - frontend-developer
13
+ - backend-developer
14
+ - fullstack-dev
15
+ - qa-engineer
16
+ - product-manager
17
+ - architect
18
+ - generalist
19
+ - team-leader
20
+ - sales
21
+ - support
22
+ triggers:
23
+ - get team norms
24
+ - team norms
25
+ - rules of engagement
26
+ - can i delegate
27
+ - escalation policy
28
+ tags:
29
+ - system
30
+ - norms
31
+ - governance
32
+ - team
33
+ execution:
34
+ type: script
35
+ script:
36
+ file: execute.sh
37
+ interpreter: bash
38
+ timeoutMs: 15000
39
+ ---
40
+
41
+ # Get Team Norms
42
+
43
+ Read your **team norms** — the team's own operating agreement: who may delegate
44
+ to whom (`canDelegate`), role conventions, escalation paths, decision rights,
45
+ and rules of engagement. Norms are team-specific and authored by the team
46
+ (by the owner in the wiki, or proposed by agents via `update-team-norm`).
47
+
48
+ Norms differ from SOPs: a **SOP** is a reusable procedure ("how to do task X");
49
+ a **norm** is "how THIS team operates together." Consult norms before acting on
50
+ anything governance-related — delegating, escalating, deciding scope.
51
+
52
+ ## Parameters
53
+
54
+ | Parameter | Required | Description |
55
+ |-----------|----------|-------------|
56
+ | `trigger` | No | Filter to norms relevant to a moment (e.g. `"before_commit"`, `"before_delegate"`). Omit to read all. |
57
+ | `teamId` | No | Team to read. Defaults to the team resolved from your `sessionName`. |
58
+ | `sessionName` | No | Your session name, used to resolve the team when `teamId` is omitted. |
59
+
60
+ ## Example
61
+
62
+ ```bash
63
+ bash config/skills/agent/core/get-team-norms/execute.sh '{"trigger":"before_delegate"}'
64
+ ```
65
+
66
+ ## Output
67
+
68
+ JSON `{ success, count, data: [{ normId, title, trigger, content, updatedBy, updatedAt }] }`.
69
+ Returns an empty list if the team has no norms yet.
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: list-my-crons
3
+ description: List the recurring cron tasks the orchestrator has scheduled FOR you — call before self-scheduling so you don't create duplicates that fire N× reports.
4
+ version: 1.0.0
5
+ category: followup
6
+ skillType: claude-skill
7
+ tags:
8
+ - cron
9
+ - schedule
10
+ - audit
11
+ execution:
12
+ type: script
13
+ script:
14
+ file: execute.sh
15
+ interpreter: bash
16
+ timeoutMs: 15000
17
+ ---
18
+
19
+ # list-my-crons
20
+
21
+ List the recurring **cron tasks** that target you — i.e. schedules the
22
+ orchestrator (or you) registered with you as the `targetAgent`. Call this
23
+ **before** creating a cron or a `schedule-followup`, so you can confirm whether
24
+ you are already scheduled instead of stacking a duplicate.
25
+
26
+ ## Why this exists
27
+
28
+ Cron tasks and follow-up triggers live in **two different stores**:
29
+
30
+ | What | Store | List it with |
31
+ |------|-------|--------------|
32
+ | Recurring cron the orchestrator set for you | cron-tasks (per-team file) | **`list-my-crons`** (this skill) |
33
+ | Follow-up timers you created | triggers (TriggerEngine) | `list-my-followups` |
34
+
35
+ `list-my-followups` will **not** show the orchestrator's crons — they are in a
36
+ separate store. If you only check followups, you can wrongly conclude "I have
37
+ no schedule" and self-create a duplicate cron. That duplicate then fires every
38
+ trigger N× (N identical reports/PDFs). Always check **both** before scheduling.
39
+
40
+ > Note: "cron API shows 0" is **not** a reliable "no schedule exists" signal on
41
+ > its own — historically agents misread it and self-duplicated (#621). Check
42
+ > the actual list here, and if unsure, ask the orchestrator rather than
43
+ > re-creating.
44
+
45
+ ## Examples
46
+
47
+ ```bash
48
+ # All crons targeting you
49
+ bash execute.sh
50
+
51
+ # Only enabled ones
52
+ bash execute.sh --enabled true
53
+ ```
54
+
55
+ ## Options
56
+
57
+ | Flag | Meaning |
58
+ |------|---------|
59
+ | `--enabled <true\|false>` | Filter by enabled state (optional) |
60
+ | `--target <session>` | Query a different agent's crons (default: yourself) |
61
+ | `--json`, `-j` | Raw JSON payload (legacy) |
62
+
63
+ ## Output
64
+
65
+ ```
66
+ { "success": true, "count": <n>, "data": [ CronTask, ... ] }
67
+ ```
@@ -0,0 +1,77 @@
1
+ #!/bin/bash
2
+ # List the recurring cron tasks that target YOU (or another agent via --target).
3
+ #
4
+ # Cron tasks and follow-up triggers live in two different stores. This skill
5
+ # reads the cron-tasks store (what the orchestrator schedules FOR an agent),
6
+ # which `list-my-followups` does NOT cover. Call this before self-scheduling so
7
+ # you don't stack a duplicate cron that fires N× reports per trigger (#621).
8
+ #
9
+ # Supports CLI flags (preferred) or legacy JSON.
10
+ set -euo pipefail
11
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ source "${SCRIPT_DIR}/../../_common/lib.sh"
13
+
14
+ print_usage() {
15
+ cat <<'EOF_USAGE'
16
+ Usage:
17
+ bash execute.sh # All crons targeting you
18
+ bash execute.sh --enabled true # Only enabled crons targeting you
19
+ bash execute.sh --target <session> # Crons targeting another agent
20
+
21
+ Options:
22
+ --enabled One of: true | false (optional)
23
+ --target Agent session to query (default: yourself, $CREWLY_SESSION_NAME)
24
+ --json -j Raw JSON payload (legacy)
25
+ --help -h Show this help
26
+
27
+ Output: JSON object { success, count, data: [CronTask, ...] }
28
+ EOF_USAGE
29
+ }
30
+
31
+ INPUT_JSON=""
32
+ ENABLED_FILTER=""
33
+ TARGET=""
34
+
35
+ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
36
+ INPUT_JSON="$1"
37
+ shift || true
38
+ fi
39
+
40
+ while [[ $# -gt 0 ]]; do
41
+ case "$1" in
42
+ --enabled) ENABLED_FILTER="$2"; shift 2 ;;
43
+ --target) TARGET="$2"; shift 2 ;;
44
+ --json|-j) INPUT_JSON="$2"; shift 2 ;;
45
+ --help|-h) print_usage; exit 0 ;;
46
+ --) shift; break ;;
47
+ *)
48
+ if [[ -z "$INPUT_JSON" && ${1:0:1} == '{' ]]; then
49
+ INPUT_JSON="$1"; shift
50
+ else
51
+ echo '{"error":"Unknown argument: '"$1"'"}' >&2
52
+ exit 1
53
+ fi
54
+ ;;
55
+ esac
56
+ done
57
+
58
+ if [ -n "$INPUT_JSON" ]; then
59
+ ENABLED_FILTER=$(printf '%s' "$INPUT_JSON" | jq -r '.enabled // empty')
60
+ TARGET=$(printf '%s' "$INPUT_JSON" | jq -r '.target // empty')
61
+ fi
62
+
63
+ # Default target = self
64
+ [ -z "$TARGET" ] && TARGET="${CREWLY_SESSION_NAME:-}"
65
+ [ -z "$TARGET" ] && { echo '{"error":"No --target and CREWLY_SESSION_NAME is unset"}' >&2; exit 1; }
66
+
67
+ # Build query string: filter by targetAgent (Option A from #621 — the API
68
+ # returns crons where targetAgent == session, regardless of who created them).
69
+ QUERY="?targetAgent=${TARGET}"
70
+ [ -n "$ENABLED_FILTER" ] && QUERY="${QUERY}&enabled=${ENABLED_FILTER}"
71
+
72
+ LIST_RESP=$(api_call GET "/cron-tasks${QUERY}" "")
73
+
74
+ # Normalise to { success, count, data } so callers get a stable shape.
75
+ DATA=$(printf '%s' "$LIST_RESP" | jq '(.data // [])')
76
+ COUNT=$(printf '%s' "$DATA" | jq 'length')
77
+ printf '%s' "$DATA" | jq --arg c "$COUNT" '{success:true, count:($c|tonumber), data:.}'
@@ -34,6 +34,19 @@ that went quiet.
34
34
  | "Whenever Rex goes idle, check progress" | `watch-for-event` (event-based, not this skill) |
35
35
  | Simple self-reminder with no team scope | `schedule-check` (older skill) |
36
36
 
37
+ ## Before you schedule — check you aren't already scheduled
38
+
39
+ Schedules live in **two stores**: follow-up triggers (this skill) and cron
40
+ tasks the orchestrator sets for you. Check **both** before creating, or you
41
+ will stack a duplicate that fires N× reports:
42
+
43
+ - `list-my-followups` — your follow-up triggers
44
+ - `list-my-crons` — recurring crons targeting you (the orchestrator's)
45
+
46
+ Do **not** treat "cron list shows 0" alone as "no schedule exists" — verify
47
+ with both skills, and if still unsure, ask the orchestrator instead of
48
+ re-creating (#621).
49
+
37
50
  ## Key invariants
38
51
 
39
52
  - Every followup is **team-scoped**. It shows up under `list-my-followups` and
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: Update SOP
3
+ description: Author or update a custom SOP (a reusable procedure / playbook) for your team.
4
+ version: 1.0.0
5
+ category: system
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - team-leader
9
+ - tpm
10
+ - product-manager
11
+ - architect
12
+ - generalist
13
+ - developer
14
+ - qa
15
+ - qa-engineer
16
+ - backend-developer
17
+ - frontend-developer
18
+ - fullstack-dev
19
+ triggers:
20
+ - update sop
21
+ - write a sop
22
+ - document the procedure
23
+ - create a playbook
24
+ - standardize how we
25
+ tags:
26
+ - system
27
+ - sops
28
+ - procedures
29
+ - team
30
+ execution:
31
+ type: script
32
+ script:
33
+ file: execute.sh
34
+ interpreter: bash
35
+ timeoutMs: 15000
36
+ ---
37
+
38
+ # Update SOP
39
+
40
+ Create or update a **custom SOP** for your team — a reusable procedure /
41
+ playbook for *how to do a class of work* (e.g. "publishing an XHS post",
42
+ "running a release"). The SOP is written into the team's own SOP store
43
+ (`~/.crewly/teams/<id>/sops/`) and surfaces in the wiki under the team's
44
+ **SOPs** folder, alongside SOPs installed from the catalog. The owner can edit
45
+ it in the wiki UI.
46
+
47
+ Use this for durable, repeatable procedures — not one-off task notes (use the
48
+ wiki/memory) and not team governance rules (those are **team norms** — use
49
+ `update-team-norm`). Prefer installing an existing catalog SOP when one fits;
50
+ author a custom SOP only when the team needs something the catalog lacks.
51
+
52
+ ## Parameters
53
+
54
+ | Parameter | Required | Description |
55
+ |-----------|----------|-------------|
56
+ | `sopId` | Yes | Stable kebab-case id / filename, e.g. `"xhs-posting"`. |
57
+ | `content` | Yes | The SOP body (markdown). The steps/procedure. |
58
+ | `title` | No | Human title, e.g. `"XHS Posting Checklist"`. Required when creating. |
59
+ | `category` | No | Optional sub-folder, e.g. `"common"`, `"marketing"`. |
60
+ | `append` | No | `"true"` to append to an existing SOP instead of overwriting. |
61
+ | `teamId` | No | Team to write to. Defaults to the team resolved from `sessionName`. |
62
+ | `sessionName` | No | Your session name, used to resolve the team when `teamId` is omitted. |
63
+ | `updatedBy` | No | Who authored the change (your agent/session name). |
64
+
65
+ ## Example
66
+
67
+ ```bash
68
+ bash config/skills/agent/core/update-sop/execute.sh '{"sopId":"xhs-posting","title":"XHS Posting Checklist","category":"marketing","content":"1. Draft hook.\n2. Add 3-5 tags.\n3. Post 7-9pm."}'
69
+ ```
70
+
71
+ ## Output
72
+
73
+ JSON `{ success, action: "created" | "updated", sopId, title, category, path, relativePath }`.
@@ -0,0 +1,115 @@
1
+ #!/bin/bash
2
+ # Create or update a team's custom SOP.
3
+ # Writes to ~/.crewly/teams/{teamId}/sops/[{category}/]{sopId}.md
4
+ # (the per-team installed/custom SOP store the wiki surfaces under sop/).
5
+ set -euo pipefail
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ source "${SCRIPT_DIR}/../../_common/lib.sh"
8
+
9
+ CREWLY_HOME="${HOME}/.crewly"
10
+
11
+ INPUT=$(read_json_input "${1:-}")
12
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sopId\":\"xhs-posting\",\"title\":\"XHS Posting\",\"content\":\"...\",\"category\":\"common\"}'"
13
+
14
+ SOP_ID=$(printf '%s' "$INPUT" | jq -r '.sopId // empty')
15
+ TITLE=$(printf '%s' "$INPUT" | jq -r '.title // empty')
16
+ CATEGORY=$(printf '%s' "$INPUT" | jq -r '.category // empty')
17
+ CONTENT=$(printf '%s' "$INPUT" | jq -r '.content // empty')
18
+ APPEND=$(printf '%s' "$INPUT" | jq -r '.append // "false"')
19
+ TEAM_ID=$(printf '%s' "$INPUT" | jq -r '.teamId // empty')
20
+ SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
21
+ UPDATED_BY=$(printf '%s' "$INPUT" | jq -r '.updatedBy // empty')
22
+
23
+ require_param "sopId" "$SOP_ID"
24
+ require_param "content" "$CONTENT"
25
+
26
+ # Sanitize id/category to safe path segments (no traversal, no slashes).
27
+ SOP_ID=$(printf '%s' "$SOP_ID" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9._-]/-/g; s/^-*//; s/-*$//')
28
+ CATEGORY=$(printf '%s' "$CATEGORY" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_-]/-/g; s/^-*//; s/-*$//')
29
+ [ -z "$SOP_ID" ] && error_exit "sopId resolved to empty after sanitization"
30
+
31
+ # Resolve teamId: explicit param > lookup by sessionName > CREWLY_SESSION_NAME env
32
+ resolve_team_id() {
33
+ local session="${1:-${CREWLY_SESSION_NAME:-}}"
34
+ [ -z "$session" ] && return 1
35
+ local teams_dir="${CREWLY_HOME}/teams"
36
+ [ ! -d "$teams_dir" ] && return 1
37
+ for config in "$teams_dir"/*/config.json; do
38
+ [ -f "$config" ] || continue
39
+ local found
40
+ found=$(jq -r --arg s "$session" '.members[]? | select(.sessionName == $s) | "found"' "$config" 2>/dev/null | head -1)
41
+ if [ "$found" = "found" ]; then
42
+ basename "$(dirname "$config")"
43
+ return 0
44
+ fi
45
+ done
46
+ return 1
47
+ }
48
+
49
+ if [ -z "$TEAM_ID" ]; then
50
+ TEAM_ID=$(resolve_team_id "$SESSION_NAME") || error_exit "Could not resolve teamId. Provide teamId or sessionName."
51
+ fi
52
+
53
+ SOPS_DIR="${CREWLY_HOME}/teams/${TEAM_ID}/sops"
54
+ if [ -n "$CATEGORY" ]; then
55
+ SOPS_DIR="${SOPS_DIR}/${CATEGORY}"
56
+ REL_PATH="sop/${CATEGORY}/${SOP_ID}.md"
57
+ else
58
+ REL_PATH="sop/${SOP_ID}.md"
59
+ fi
60
+ mkdir -p "$SOPS_DIR"
61
+
62
+ SOP_FILE="${SOPS_DIR}/${SOP_ID}.md"
63
+ TODAY=$(date +%Y-%m-%d)
64
+ ACTION="created"
65
+
66
+ if [ -f "$SOP_FILE" ]; then
67
+ ACTION="updated"
68
+ EXISTING_TITLE=$(sed -n '/^---$/,/^---$/{ /^---$/d; s/^title: *//p; }' "$SOP_FILE" | head -1)
69
+ EXISTING_UPDATED_BY=$(sed -n '/^---$/,/^---$/{ /^---$/d; s/^updatedBy: *//p; }' "$SOP_FILE" | head -1)
70
+ [ -z "$TITLE" ] && TITLE="$EXISTING_TITLE"
71
+ [ -z "$UPDATED_BY" ] && UPDATED_BY="$EXISTING_UPDATED_BY"
72
+
73
+ if [ "$APPEND" = "true" ]; then
74
+ EXISTING_CONTENT=""
75
+ FM_DONE=false
76
+ FM_COUNT=0
77
+ while IFS= read -r line || [ -n "$line" ]; do
78
+ if [ "$line" = "---" ]; then
79
+ FM_COUNT=$((FM_COUNT + 1))
80
+ if [ "$FM_COUNT" -ge 2 ]; then
81
+ FM_DONE=true
82
+ continue
83
+ fi
84
+ elif [ "$FM_DONE" = true ]; then
85
+ EXISTING_CONTENT="${EXISTING_CONTENT}${line}
86
+ "
87
+ fi
88
+ done < "$SOP_FILE"
89
+ EXISTING_CONTENT=$(echo "$EXISTING_CONTENT" | sed '/./,$!d')
90
+ CONTENT="${EXISTING_CONTENT}
91
+ ${CONTENT}"
92
+ fi
93
+ else
94
+ [ -z "$TITLE" ] && error_exit "title is required when creating a new SOP"
95
+ fi
96
+
97
+ {
98
+ echo "---"
99
+ echo "title: ${TITLE}"
100
+ [ -n "$CATEGORY" ] && echo "category: ${CATEGORY}"
101
+ [ -n "$UPDATED_BY" ] && echo "updatedBy: ${UPDATED_BY}"
102
+ echo "updatedAt: ${TODAY}"
103
+ echo "---"
104
+ echo ""
105
+ echo "$CONTENT"
106
+ } > "$SOP_FILE"
107
+
108
+ jq -n \
109
+ --arg sopId "$SOP_ID" \
110
+ --arg title "$TITLE" \
111
+ --arg category "$CATEGORY" \
112
+ --arg action "$ACTION" \
113
+ --arg path "$SOP_FILE" \
114
+ --arg relativePath "$REL_PATH" \
115
+ '{success: true, action: $action, sopId: $sopId, title: $title, category: $category, path: $path, relativePath: $relativePath}'
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: Update Team Norm
3
+ description: Propose or update a team norm (an operating agreement — canDelegate, escalation, rules of engagement) for your team.
4
+ version: 1.0.0
5
+ category: system
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - team-leader
9
+ - tpm
10
+ - product-manager
11
+ - architect
12
+ - generalist
13
+ - developer
14
+ - qa
15
+ triggers:
16
+ - update team norm
17
+ - propose a norm
18
+ - set a team rule
19
+ - we should always
20
+ - going forward the team should
21
+ tags:
22
+ - system
23
+ - norms
24
+ - governance
25
+ - team
26
+ execution:
27
+ type: script
28
+ script:
29
+ file: execute.sh
30
+ interpreter: bash
31
+ timeoutMs: 15000
32
+ ---
33
+
34
+ # Update Team Norm
35
+
36
+ Create or update a **team norm** — a durable operating agreement for your team
37
+ (who may delegate to whom, escalation paths, decision rights, rules of
38
+ engagement, recurring conventions). Use this when a lasting team-wide rule
39
+ emerges that future work should follow — not for one-off task notes (use the
40
+ wiki/memory for those) and not for reusable procedures (those are SOPs).
41
+
42
+ Norms you write land in the team's norm store and surface in the wiki under the
43
+ team's **Team Norms** folder, where the owner can review and edit them. Treat
44
+ this as *proposing* a norm: keep it concise, state the rule and when it applies.
45
+
46
+ ## Parameters
47
+
48
+ | Parameter | Required | Description |
49
+ |-----------|----------|-------------|
50
+ | `normId` | Yes | Stable kebab-case id / filename, e.g. `"code-commit"`, `"delegation"`. |
51
+ | `content` | Yes | The norm body (markdown). State the rule and its rationale. |
52
+ | `title` | No | Human title, e.g. `"Code Commit Norm"`. |
53
+ | `trigger` | No | When this norm applies, e.g. `"before_commit"`, `"before_delegate"`. |
54
+ | `append` | No | `"true"` to append to an existing norm instead of overwriting. |
55
+ | `teamId` | No | Team to write to. Defaults to the team resolved from `sessionName`. |
56
+ | `sessionName` | No | Your session name, used to resolve the team when `teamId` is omitted. |
57
+ | `updatedBy` | No | Who authored the change (your agent/session name). |
58
+
59
+ ## Example
60
+
61
+ ```bash
62
+ bash config/skills/agent/core/update-team-norm/execute.sh '{"normId":"delegation","title":"Delegation","trigger":"before_delegate","content":"Leads may delegate to their own team only; cross-team work goes through the orchestrator."}'
63
+ ```
64
+
65
+ ## Output
66
+
67
+ JSON `{ success, action: "created" | "updated", normId, path }`.
@@ -113,6 +113,8 @@ export interface ChatV2ControllerHandlers {
113
113
  listMessages: (req: Request, res: Response) => void;
114
114
  sendMessage: (req: Request, res: Response) => void | Promise<void>;
115
115
  ensureDmChannel: (req: Request, res: Response) => void;
116
+ ensureTeamChannel: (req: Request, res: Response) => void;
117
+ createHuddle: (req: Request, res: Response) => void;
116
118
  listAgents: (req: Request, res: Response) => void | Promise<void>;
117
119
  getAgentPresence: (req: Request, res: Response) => void | Promise<void>;
118
120
  }