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.
- package/config/roles/orchestrator/prompt.md +53 -2
- package/config/skills/agent/core/get-sops/SKILL.md +6 -2
- package/config/skills/agent/core/get-sops/execute.sh +26 -1
- package/config/skills/agent/core/get-team-norms/SKILL.md +69 -0
- package/config/skills/agent/core/list-my-crons/SKILL.md +67 -0
- package/config/skills/agent/core/list-my-crons/execute.sh +77 -0
- package/config/skills/agent/core/schedule-followup/SKILL.md +13 -0
- package/config/skills/agent/core/update-sop/SKILL.md +73 -0
- package/config/skills/agent/core/update-sop/execute.sh +115 -0
- package/config/skills/agent/core/update-team-norm/SKILL.md +67 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts +2 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +50 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts +2 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js +6 -2
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.js +10 -0
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/system/system.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/system/system.controller.js +2 -1
- package/dist/backend/backend/src/controllers/system/system.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts +29 -5
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +106 -9
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/team/team.controller.d.ts +17 -0
- package/dist/backend/backend/src/controllers/team/team.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/team/team.controller.js +32 -0
- package/dist/backend/backend/src/controllers/team/team.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts +45 -0
- package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/wiki/wiki.controller.js +364 -46
- package/dist/backend/backend/src/controllers/wiki/wiki.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/wiki/wiki.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/wiki/wiki.routes.js +9 -1
- package/dist/backend/backend/src/controllers/wiki/wiki.routes.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +80 -73
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +12 -6
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +13 -4
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +47 -11
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.d.ts +28 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.js +49 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +60 -2
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +93 -3
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.d.ts +25 -0
- package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.js +33 -0
- package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts +28 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js +48 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts +21 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js +30 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/types.d.ts +14 -0
- package/dist/backend/backend/src/services/chat-v2/types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/types.js.map +1 -1
- package/dist/backend/backend/src/services/core/retry.util.d.ts +67 -0
- package/dist/backend/backend/src/services/core/retry.util.d.ts.map +1 -0
- package/dist/backend/backend/src/services/core/retry.util.js +62 -0
- package/dist/backend/backend/src/services/core/retry.util.js.map +1 -0
- package/dist/backend/backend/src/services/index.d.ts +0 -1
- package/dist/backend/backend/src/services/index.d.ts.map +1 -1
- package/dist/backend/backend/src/services/index.js +0 -1
- package/dist/backend/backend/src/services/index.js.map +1 -1
- package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.d.ts +26 -108
- package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.js +26 -214
- package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +11 -0
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +69 -12
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +10 -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 +21 -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 +12 -0
- package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.js +53 -1
- package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/backend/backend/src/services/sop/sop.service.d.ts +10 -0
- package/dist/backend/backend/src/services/sop/sop.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/sop/sop.service.js +71 -10
- package/dist/backend/backend/src/services/sop/sop.service.js.map +1 -1
- package/dist/backend/backend/src/services/wiki/sop-catalog.service.d.ts +83 -0
- package/dist/backend/backend/src/services/wiki/sop-catalog.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/sop-catalog.service.js +185 -0
- package/dist/backend/backend/src/services/wiki/sop-catalog.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-cleanup.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-cleanup.service.js +12 -0
- package/dist/backend/backend/src/services/wiki/wiki-cleanup.service.js.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.d.ts +43 -0
- package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.js +67 -0
- package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-search.service.d.ts +79 -19
- package/dist/backend/backend/src/services/wiki/wiki-search.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-search.service.js +275 -106
- package/dist/backend/backend/src/services/wiki/wiki-search.service.js.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js +13 -0
- package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js.map +1 -1
- package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts +13 -0
- package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/workflow/cron-task.service.js +77 -8
- package/dist/backend/backend/src/services/workflow/cron-task.service.js.map +1 -1
- package/dist/backend/backend/src/types/sop.types.d.ts +7 -0
- package/dist/backend/backend/src/types/sop.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/sop.types.js.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts +7 -0
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.js +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
- package/dist/cli/backend/src/types/sop.types.d.ts +7 -0
- package/dist/cli/backend/src/types/sop.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/sop.types.js.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts +7 -0
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.js +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
- package/frontend/dist/assets/index-4099a91c.js +4961 -0
- package/frontend/dist/assets/index-44266b5d.css +42 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-068bb4f6.css +0 -42
- 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)
|
|
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":"
|
|
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
|
}
|