crewly 1.8.4 → 1.8.6

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 (85) hide show
  1. package/config/roles/_common/wiki-instructions.md +33 -0
  2. package/config/roles/orchestrator/prompt.md +66 -4
  3. package/config/roles/team-leader/prompt.md +38 -0
  4. package/config/skills/agent/core/wiki-query/SKILL.md +66 -0
  5. package/config/skills/agent/core/wiki-query/execute.sh +107 -0
  6. package/config/skills/orchestrator/wiki-bookkeep/SKILL.md +71 -0
  7. package/config/skills/orchestrator/wiki-bookkeep/execute.sh +72 -0
  8. package/config/skills/orchestrator/wiki-ingest/SKILL.md +63 -0
  9. package/config/skills/orchestrator/wiki-ingest/execute.sh +113 -0
  10. package/config/skills/orchestrator/wiki-process-queue/SKILL.md +71 -0
  11. package/config/skills/orchestrator/wiki-process-queue/execute.sh +93 -0
  12. package/config/skills/orchestrator/wiki-queue-add/SKILL.md +89 -0
  13. package/config/skills/orchestrator/wiki-queue-add/execute.sh +115 -0
  14. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  15. package/dist/backend/backend/src/controllers/chat/chat.controller.js +20 -0
  16. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  17. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  18. package/dist/backend/backend/src/controllers/slack/slack.controller.js +15 -0
  19. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  20. package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts +134 -0
  21. package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts.map +1 -0
  22. package/dist/backend/backend/src/controllers/wiki/wiki.controller.js +718 -0
  23. package/dist/backend/backend/src/controllers/wiki/wiki.controller.js.map +1 -0
  24. package/dist/backend/backend/src/controllers/wiki/wiki.routes.d.ts +23 -0
  25. package/dist/backend/backend/src/controllers/wiki/wiki.routes.d.ts.map +1 -0
  26. package/dist/backend/backend/src/controllers/wiki/wiki.routes.js +43 -0
  27. package/dist/backend/backend/src/controllers/wiki/wiki.routes.js.map +1 -0
  28. package/dist/backend/backend/src/index.d.ts.map +1 -1
  29. package/dist/backend/backend/src/index.js +65 -0
  30. package/dist/backend/backend/src/index.js.map +1 -1
  31. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  32. package/dist/backend/backend/src/routes/api.routes.js +4 -0
  33. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  34. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.d.ts +142 -0
  35. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.d.ts.map +1 -0
  36. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.js +265 -0
  37. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.js.map +1 -0
  38. package/dist/backend/backend/src/services/session/pty/pty-session.d.ts +28 -0
  39. package/dist/backend/backend/src/services/session/pty/pty-session.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/session/pty/pty-session.js +162 -4
  41. package/dist/backend/backend/src/services/session/pty/pty-session.js.map +1 -1
  42. package/dist/backend/backend/src/services/wiki/referenced-by.resolver.d.ts +69 -0
  43. package/dist/backend/backend/src/services/wiki/referenced-by.resolver.d.ts.map +1 -0
  44. package/dist/backend/backend/src/services/wiki/referenced-by.resolver.js +174 -0
  45. package/dist/backend/backend/src/services/wiki/referenced-by.resolver.js.map +1 -0
  46. package/dist/backend/backend/src/services/wiki/schema-loader.service.d.ts +57 -0
  47. package/dist/backend/backend/src/services/wiki/schema-loader.service.d.ts.map +1 -0
  48. package/dist/backend/backend/src/services/wiki/schema-loader.service.js +183 -0
  49. package/dist/backend/backend/src/services/wiki/schema-loader.service.js.map +1 -0
  50. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.d.ts +86 -0
  51. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.d.ts.map +1 -0
  52. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.js +187 -0
  53. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.js.map +1 -0
  54. package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.d.ts +116 -0
  55. package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.d.ts.map +1 -0
  56. package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.js +299 -0
  57. package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.js.map +1 -0
  58. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts +74 -0
  59. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts.map +1 -0
  60. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js +154 -0
  61. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js.map +1 -0
  62. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts +100 -0
  63. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.js +212 -0
  65. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.js.map +1 -0
  66. package/dist/backend/backend/src/services/wiki/wiki-process.service.d.ts +84 -0
  67. package/dist/backend/backend/src/services/wiki/wiki-process.service.d.ts.map +1 -0
  68. package/dist/backend/backend/src/services/wiki/wiki-process.service.js +138 -0
  69. package/dist/backend/backend/src/services/wiki/wiki-process.service.js.map +1 -0
  70. package/dist/backend/backend/src/services/wiki/wiki-query.service.d.ts +115 -0
  71. package/dist/backend/backend/src/services/wiki/wiki-query.service.d.ts.map +1 -0
  72. package/dist/backend/backend/src/services/wiki/wiki-query.service.js +291 -0
  73. package/dist/backend/backend/src/services/wiki/wiki-query.service.js.map +1 -0
  74. package/dist/backend/backend/src/services/wiki/wiki-queue.service.d.ts +115 -0
  75. package/dist/backend/backend/src/services/wiki/wiki-queue.service.d.ts.map +1 -0
  76. package/dist/backend/backend/src/services/wiki/wiki-queue.service.js +261 -0
  77. package/dist/backend/backend/src/services/wiki/wiki-queue.service.js.map +1 -0
  78. package/dist/backend/backend/src/services/wiki/wiki.types.d.ts +84 -0
  79. package/dist/backend/backend/src/services/wiki/wiki.types.d.ts.map +1 -0
  80. package/dist/backend/backend/src/services/wiki/wiki.types.js +10 -0
  81. package/dist/backend/backend/src/services/wiki/wiki.types.js.map +1 -0
  82. package/frontend/dist/assets/{index-b279da34.js → index-cc115bb4.js} +246 -246
  83. package/frontend/dist/assets/{index-c07e04c0.css → index-db3f5041.css} +1 -1
  84. package/frontend/dist/index.html +2 -2
  85. package/package.json +1 -1
@@ -0,0 +1,33 @@
1
+ ## LLM-Wiki — Queue Worth-Saving Content (per-turn discipline)
2
+
3
+ The Crewly LLM-wiki captures **only what agents judge worth remembering**. There is NO automatic capture — you are the gate.
4
+
5
+ **Before yielding the turn**, scan your conversation (what you sent, what teammates sent you) and ask:
6
+
7
+ - Did a **decision** get made? (pricing, scope, sequencing, hire, deprecation, scheduling)
8
+ - Did a **fact** about a person, customer, partner, or competitor surface that future-you / the team needs?
9
+ - Did a **pattern, gotcha, or learning** get exposed that the team should not re-discover?
10
+ - Did Steve / a TL / another agent **lock** something previously fluid?
11
+
12
+ If YES, before yielding, call `config/skills/orchestrator/wiki-queue-add/execute.sh`:
13
+
14
+ ```bash
15
+ --vault <path> # project: <project>/.crewly/wiki; team: ~/.crewly/teams/<id>/wiki; global: ~/.crewly/global-wiki
16
+ --content "<the fact / decision / learning text>"
17
+ --reason "<one sentence: WHY this is wiki-worthy>" # required — empty reason is rejected
18
+ --source-ref "<chat msg id | slack thread | WI id | file path>"
19
+ --source-type user_chat | slack_message | spec_file | pr_merge | record_learning | task_verified
20
+ ```
21
+
22
+ **DO NOT queue:**
23
+ - Routine status checks (`standup`, `?`, `ok`, `got it`)
24
+ - Implementation details already in code / PR / spec
25
+ - Conversation pleasantries
26
+ - Anything already in the wiki (call `wiki-query` first if unsure)
27
+ - Items where you can't articulate a non-trivial `--reason`
28
+
29
+ **One queue call per worth-saving event.** Don't batch unrelated facts.
30
+
31
+ The item lands in the queue with `status: pending`. Later — either when you're idle, OR when a `[BOOKKEEP]` message arrives — drain the queue with `wiki-process-queue`: claim an item, read the vault context, let your LLM pick a target folder (no preset taxonomy beyond the frozen ones), call `wiki-ingest`, then POST `/queue/<id>/process` to commit. If after reading the context you decide the item isn't actually worth saving, POST `/queue/<id>/skip` with a `skipReason`.
32
+
33
+ **Bookkeep cadence:** when you receive `[BOOKKEEP] vault=…`, run `wiki-bookkeep` for that vault. The report surfaces duplicate clusters and consolidation candidates — use your LLM to merge them.
@@ -522,11 +522,73 @@ bash {{ORCHESTRATOR_SKILLS_PATH}}/reply-slack/execute.sh '{"channelId":"D0AC7NF5
522
522
 
523
523
  Before yielding the turn:
524
524
  1. Did I receive any `[CHAT:slack-...]` messages this turn?
525
- 2. For EACH such message, did I make at least one Bash call to `reply-slack/execute.sh`?
526
- 3. If no, the response was NOT delivered `[NOTIFY]` alone is not sufficient.
527
- 4. If the answer to (2) is "no," call `reply-slack` now BEFORE yielding the turn.
525
+ 2. **Did I receive any `[DELIVER_REQUIRED]` messages this turn?** (System reminder that a worker agent posted `[DONE]` to a slack thread the user is waiting on, and I haven't yet forwarded the deliverable. See the dedicated section below.)
526
+ 3. For EACH such message, did I make at least one Bash call to `reply-slack/execute.sh`?
527
+ 4. If no, the response was NOT delivered `[NOTIFY]` alone is not sufficient.
528
+ 5. If the answer to (3) is "no," call `reply-slack` now BEFORE yielding the turn.
528
529
 
529
- This is a hard pre-yield check. Do not yield if any Slack message is unanswered.
530
+ This is a hard pre-yield check. Do not yield if any Slack message or `[DELIVER_REQUIRED]` is unanswered.
531
+
532
+ ## Handling `[DELIVER_REQUIRED]` Messages — MANDATORY
533
+
534
+ The system delivers `[DELIVER_REQUIRED]` to your inbox when a worker agent has posted `[DONE]` / `[COMPLETED]` / `[DELIVERED]` to a Slack thread the user originated, AND you haven't yet forwarded the deliverable via `reply-slack`. This catches the 2026-05-23 failure mode where you internally narrate "pipeline closed" but never actually call `reply-slack`, leaving the user in silence.
535
+
536
+ Shape:
537
+ ```
538
+ [DELIVER_REQUIRED] Steve is waiting on a deliverable in Slack thread <conversationId>.
539
+
540
+ Agent `<sender>` posted [DONE] <N> min ago with this content (preview):
541
+ > <first ~200 chars of the agent's [DONE] message>
542
+
543
+ Action required: call reply-slack now with the deliverable. Treat this
544
+ the same as a [CHAT:slack-...] message — do NOT yield the turn until
545
+ the reply lands on the thread. ...
546
+ ```
547
+
548
+ **Action protocol** (in order, no exceptions):
549
+
550
+ 1. **Look up the actual deliverable.** The `[DONE]` preview is truncated — the full content is in the agent's session output (use `get-agent-logs` if needed) and likely also persisted to a spec / findings file inside `.crewly/`. Read it.
551
+ 2. **Decide if it's the FINAL deliverable for the user request OR a Phase-N intermediate.**
552
+ - **Final**: call `reply-slack` with a Steve-facing summary (Owner-Facing Communication Standard — no internal session names, no jargon). Include any links / file paths Steve needs.
553
+ - **Intermediate (Phase 1 of 2, etc.)**: still call `reply-slack` — Steve gets a "Phase 1 done, Phase 2 starting, revised ETA" update. Then continue the pipeline. The enforcer will keep tracking until the FINAL phase delivers.
554
+ 3. **One `reply-slack` call clears the reminder** (the system marks the thread as delivered on successful `/api/slack/send`). If you ignore the `[DELIVER_REQUIRED]`, you'll get escalating reminders at 3 / 10 / 30 min, then a final "budget exhausted — giving up" log entry, and Steve is permanently silent.
555
+
556
+ **Hard rule:** if there is a `[DELIVER_REQUIRED]` in your inbox at end-of-turn AND no matching `reply-slack` call was made this turn, you are LITERALLY incomplete. Make the call before yielding. No mental-model substitution — the actual skill call must happen.
557
+
558
+ ## LLM-Wiki — Queue Worth-Saving Content (MANDATORY per-turn discipline)
559
+
560
+ The wiki captures **only what agents judge worth remembering**. There is
561
+ NO automatic chat-to-vault pipeline anymore (keyword heuristics were
562
+ removed 2026-05-22). You are the gate.
563
+
564
+ **Before yielding the turn**, scan the messages you sent or received and ask:
565
+
566
+ - Did a **decision** get made? (pricing, scope, sequencing, hire, deprecation, scheduling)
567
+ - Did a **fact** about a person, customer, partner, or competitor surface that future-me will need?
568
+ - Did a **pattern, gotcha, or learning** get exposed that the team should not re-discover?
569
+ - Did Steve / a TL **lock** something previously fluid?
570
+
571
+ If YES, before you yield, call `config/skills/orchestrator/wiki-queue-add/execute.sh` with:
572
+ - `--vault` → the project vault (`<project-root>/.crewly/wiki`) for project-scoped
573
+ content; the team vault (`~/.crewly/teams/<team-id>/wiki`) for cross-project
574
+ team norms; `~/.crewly/global-wiki` for cross-project synthesis.
575
+ - `--content` → the actual fact / decision / learning text (keep it terse but complete).
576
+ - `--reason` → one sentence justifying why this is wiki-worthy. **Required**. The reason is the audit trail and helps the processor decide where it lands. Refusal to write a reason = refusal to queue.
577
+ - `--source-ref` → stable reference (slack msg id, chat id, WI id, file path).
578
+ - `--source-type` → `user_chat` (default), `slack_message`, `spec_file`, `pr_merge`, `record_learning`, `task_verified`.
579
+
580
+ **DO NOT queue:**
581
+ - Routine status checks ("standup", "?", "ok", "got it")
582
+ - Implementation details already captured in code / PR / spec files
583
+ - Conversation pleasantries
584
+ - Content already in the wiki (call `wiki-query` first if unsure)
585
+ - Items where you can't articulate a non-trivial `--reason`
586
+
587
+ **One queue call per worth-saving event.** Don't batch unrelated facts into a single item — the processor needs to classify each one. If a turn produced 3 distinct worth-saving facts, make 3 queue-add calls.
588
+
589
+ After queueing, continue the turn normally. A separate `wiki-process-queue` run (batch, run periodically by you OR when bookkeep fires) classifies queued items, picks the target page (`llm-curated/customers/<name>.md`, `llm-curated/decisions/<slug>.md`, etc. — the LLM decides; no preset taxonomy beyond the frozen folders), and calls `wiki-ingest` to write.
590
+
591
+ **Bookkeep cadence:** when you receive a `[BOOKKEEP] vault=…` message OR when you notice the vault has accumulated many new pages since your last pass, run `wiki-bookkeep` to dedupe, consolidate, and prune. (Bookkeep skill ships separately; until it does, the WI brief will spell out the consolidation rules.)
530
592
 
531
593
  ## Handling `[ESCALATION]` Messages — MANDATORY
532
594
 
@@ -284,3 +284,41 @@ You are failing the task if you:
284
284
  - Stop after partial progress without assigning next action.
285
285
  - Delegate without checking completion.
286
286
  - Produce status updates but no artifact, code, decision, or verified result.
287
+
288
+ ## LLM-Wiki — Queue Worth-Saving Content (MANDATORY per-turn discipline)
289
+
290
+ TLs are the last filter before content reaches the wiki. ORC operates cross-team; YOU operate inside one team and see worker conversations + verify outputs. That makes you the right agent to capture team-scoped patterns, decisions, and norms.
291
+
292
+ **Before yielding the turn**, scan messages from your workers + the user and ask:
293
+
294
+ - Did a **team decision** get locked? (architecture, SOP change, hiring/handoff, policy)
295
+ - Did a **worker surface a gotcha / pattern** worth sharing across the team?
296
+ - Did a **person fact** appear (customer, partner, candidate) that the team needs to remember?
297
+ - Did a **verification reveal something subtle** about how the system actually works?
298
+
299
+ If YES, call `config/skills/orchestrator/wiki-queue-add/execute.sh`:
300
+
301
+ ```bash
302
+ --vault ~/.crewly/teams/<your-team-id>/wiki # team vault for team-scoped
303
+ <project>/.crewly/wiki # project vault for project-scoped
304
+ --content "<the captured fact / decision / pattern>"
305
+ --reason "<one sentence: WHY future-team needs this>" # required
306
+ --source-ref "<WI id | chat msg id | spec path>"
307
+ --source-type record_learning | task_verified | spec_file | user_chat | slack_message
308
+ ```
309
+
310
+ **DO NOT queue:**
311
+ - Routine status updates ("done", "in progress", "blocked-pending-X")
312
+ - Implementation details captured in the code/PR — link the PR, don't paraphrase
313
+ - Anything already in the wiki — call `wiki-query` first if unsure
314
+ - Items where you can't articulate why this matters in one sentence
315
+
316
+ **One queue call per worth-saving event.** If a verification turn produces 3 distinct learnings, that's 3 queue-add calls.
317
+
318
+ **Drain the queue** when idle OR when `[BOOKKEEP] vault=…` arrives:
319
+ 1. `wiki-process-queue --claimed-by <your-session>` → claims item + returns vault context.
320
+ 2. Your LLM picks a target page under `llm-curated/` — invent sub-folder names; **NO preset taxonomy**.
321
+ 3. `wiki-ingest --target <relative-path>` to write.
322
+ 4. POST `/api/wiki/queue/<id>/process` to commit, OR `/queue/<id>/skip` if you decide it's actually a duplicate after seeing context.
323
+
324
+ Frozen folders (`memory/`, `sop/`, `team-norm/`, `sop-overrides/`) are OFF-LIMITS — ingest will reject with 422.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: Wiki Query
3
+ description: Read a project/team/global vault and return the system-context payload your runtime feeds to the LLM for synthesis. Single-vault scope (Phase 1).
4
+ version: 1.0.0
5
+ category: knowledge
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
+ - researcher
21
+ ---
22
+
23
+ # Wiki Query
24
+
25
+ Read a vault (`<project>/.crewly/wiki/`, `~/.crewly/teams/<id>/wiki/`, or `~/.crewly/global-wiki/`) and get back the structured context the caller's LLM uses for synthesis.
26
+
27
+ Per Atlas v2.1 §3, this skill **does not call an LLM itself**. It emits the LLM-agnostic system-context (vault SCHEMA, recent log entries, candidate pages by keyword overlap). The caller's runtime concatenates this with the per-LLM task-instruction prompt at `prompts/<runtime>.md` and makes the LLM call locally.
28
+
29
+ ## When to use
30
+
31
+ - ORC assembling cross-team context for a planning turn.
32
+ - TL pulling the team's last 20 decisions for a sprint review.
33
+ - A worker checking "did we already record an opinion on X?" before writing one.
34
+
35
+ ## Inputs
36
+
37
+ | Flag | Required | Description |
38
+ |---|---|---|
39
+ | `--vault <path>` | yes | Absolute path to a vault root (must contain `SCHEMA.md`). |
40
+ | `--query <text>` | yes | Natural-language question. Used for keyword-overlap candidate ranking. |
41
+ | `--top-k <n>` | no | How many candidate pages to return (default 5). |
42
+ | `--recent-log <n>` | no | How many tail log entries to include (default 20). |
43
+ | `--json <obj>` | alt | Pass all fields as JSON. |
44
+
45
+ ## Output
46
+
47
+ JSON with `success`, `result.context`. The context has:
48
+
49
+ - `vault` — scope, id, absolute path
50
+ - `schemaSummary` — frozen folders, llm-curated layout, write_policy
51
+ - `recentLog` — last N entries (most-recent first), with timestamp / sourceType / caller / body
52
+ - `candidatePages` — top-K pages by keyword overlap with `query`
53
+ - `callerNotes` — synthesis hints the LLM should honor (e.g. refuse writes into frozen paths)
54
+
55
+ ## Example
56
+
57
+ ```bash
58
+ bash execute.sh \
59
+ --vault ~/Desktop/projects/crewly-projects/crewly/.crewly/wiki \
60
+ --query "what did we decide about Crewly Pro pricing in May"
61
+ ```
62
+
63
+ ## Failure modes
64
+
65
+ - `400 invalid_input` — missing vault/query or bad topK
66
+ - `404 schema_missing` — vault has no SCHEMA.md
@@ -0,0 +1,107 @@
1
+ #!/bin/bash
2
+ # wiki-query — fetch a vault's system-context payload for LLM synthesis.
3
+ #
4
+ # Per Atlas v2.1 §3: this skill emits ONLY the system-context (LLM-agnostic).
5
+ # The caller's runtime concatenates this with its per-LLM task-instruction
6
+ # at prompts/<runtime>.md before making the LLM call. The skill itself
7
+ # never curls an LLM endpoint.
8
+ #
9
+ # Usage:
10
+ # bash execute.sh --vault /path/to/vault --query "what did we decide about pricing"
11
+ # bash execute.sh --vault /path/to/vault --query "..." --top-k 10
12
+ # bash execute.sh --json '{"vaultPath":"/path","query":"...","topK":5}'
13
+ # cat input.json | bash execute.sh
14
+ set -euo pipefail
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ source "${SCRIPT_DIR}/../../_common/lib.sh"
17
+
18
+ INPUT_JSON=""
19
+ VAULT_PATH=""
20
+ QUERY=""
21
+ TOP_K=""
22
+ RECENT_LOG=""
23
+
24
+ # Detect legacy JSON positional arg
25
+ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
26
+ INPUT_JSON="$1"
27
+ shift || true
28
+ fi
29
+
30
+ while [[ $# -gt 0 ]]; do
31
+ case "$1" in
32
+ --vault|-v)
33
+ VAULT_PATH="$2"
34
+ shift 2
35
+ ;;
36
+ --query|-q)
37
+ QUERY="$2"
38
+ shift 2
39
+ ;;
40
+ --top-k|-k)
41
+ TOP_K="$2"
42
+ shift 2
43
+ ;;
44
+ --recent-log|-r)
45
+ RECENT_LOG="$2"
46
+ shift 2
47
+ ;;
48
+ --json|-j)
49
+ INPUT_JSON="$2"
50
+ shift 2
51
+ ;;
52
+ --help|-h)
53
+ cat <<EOF
54
+ Usage:
55
+ execute.sh --vault <vault-dir> --query "<question>" [--top-k 5] [--recent-log 20]
56
+ execute.sh --json '{"vaultPath":"...","query":"...","topK":5,"recentLogEntries":20}'
57
+
58
+ Outputs JSON system-context for the caller's LLM. Skill makes no LLM calls itself.
59
+ EOF
60
+ exit 0
61
+ ;;
62
+ --)
63
+ shift
64
+ break
65
+ ;;
66
+ *)
67
+ if [[ -z "$INPUT_JSON" && ${1:0:1} == '{' ]]; then
68
+ INPUT_JSON="$1"
69
+ shift
70
+ else
71
+ error_exit "Unknown argument: $1"
72
+ fi
73
+ ;;
74
+ esac
75
+ done
76
+
77
+ # Read JSON from stdin if no other input
78
+ if [ -z "$INPUT_JSON" ] && [ -z "$QUERY" ] && [ ! -t 0 ]; then
79
+ STDIN_DATA="$(cat)"
80
+ if [[ ${STDIN_DATA:0:1} == '{' ]]; then
81
+ INPUT_JSON="$STDIN_DATA"
82
+ else
83
+ QUERY="$STDIN_DATA"
84
+ fi
85
+ fi
86
+
87
+ # Parse JSON if supplied
88
+ if [ -n "$INPUT_JSON" ]; then
89
+ INPUT=$(read_json_input "$INPUT_JSON")
90
+ [ -z "$VAULT_PATH" ] && VAULT_PATH=$(printf '%s' "$INPUT" | jq -r '.vaultPath // empty')
91
+ [ -z "$QUERY" ] && QUERY=$(printf '%s' "$INPUT" | jq -r '.query // empty')
92
+ [ -z "$TOP_K" ] && TOP_K=$(printf '%s' "$INPUT" | jq -r '.topK // empty')
93
+ [ -z "$RECENT_LOG" ] && RECENT_LOG=$(printf '%s' "$INPUT" | jq -r '.recentLogEntries // empty')
94
+ fi
95
+
96
+ require_param "vaultPath (--vault)" "$VAULT_PATH"
97
+ require_param "query (--query)" "$QUERY"
98
+
99
+ # Build body with env-var passthrough so jq escapes safely.
100
+ export _WQ_VAULT="$VAULT_PATH"
101
+ export _WQ_QUERY="$QUERY"
102
+ BODY=$(jq -n '{vaultPath: env._WQ_VAULT, query: env._WQ_QUERY}')
103
+ [ -n "$TOP_K" ] && BODY=$(echo "$BODY" | jq --argjson k "$TOP_K" '. + {topK: $k}')
104
+ [ -n "$RECENT_LOG" ] && BODY=$(echo "$BODY" | jq --argjson n "$RECENT_LOG" '. + {recentLogEntries: $n}')
105
+ unset _WQ_VAULT _WQ_QUERY
106
+
107
+ api_call POST "/wiki/query" "$BODY"
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: Wiki Bookkeep
3
+ description: Vault health report — md counts, duplicate clusters, queue snapshot, consolidation recommendations. Returns signals; your LLM decides what to merge or archive.
4
+ version: 1.0.0
5
+ category: knowledge
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - orchestrator
9
+ - team-leader
10
+ ---
11
+
12
+ # Wiki Bookkeep
13
+
14
+ Periodic vault health check. Returns a structured report (md counts, recent activity, duplicate clusters by filename similarity, stale-page count, queue snapshot, recommendations). **The skill does not write to the vault.** Your LLM reads the report and decides next actions.
15
+
16
+ ## When to use
17
+
18
+ - When `wiki-process-queue` returns `no_pending_items` — quiet moment, good for housekeeping.
19
+ - When the report's `shouldFire` was `true` on the last run (you owe the vault a consolidation pass).
20
+ - When you receive a `[BOOKKEEP] vault=…` message (a future cron + threshold trigger will deliver these).
21
+
22
+ ## Inputs
23
+
24
+ | Flag | Required | Default | Description |
25
+ |---|---|---|---|
26
+ | `--vault <path>` | yes | — | Absolute vault path. |
27
+ | `--window-days <n>` | no | 7 | "Recent activity" window for `recentMdCount`. |
28
+ | `--threshold <n>` | no | 10 | `shouldFire` triggers when `recentMdCount >= threshold` OR there are duplicate clusters. |
29
+
30
+ ## What you do with the report
31
+
32
+ 1. **Drain pending queue first** — if `queue.pending > 0`, run `wiki-process-queue` until empty before doing anything else.
33
+ 2. **Consolidate duplicate clusters** — for each cluster in `duplicateCandidates`, read the member pages, ask your LLM whether they should merge, and if yes:
34
+ - Call `wiki-ingest` with a consolidated body that supersedes the originals (preserve the most-load-bearing info, deduplicate restated facts).
35
+ - Note: page DELETION is not in Phase 1 — surface "these N old pages can be archived" in your reply for human review.
36
+ 3. **Hot-folder rollups** — if `countsByFolder` shows >20 pages in one bucket without a recent index page, generate one. Pattern: `llm-curated/<folder>/index.md` linking + summarizing each page.
37
+ 4. **Stale-page flag** — if `staleCount > 0`, mention to the user — they decide which to keep.
38
+
39
+ ## Output
40
+
41
+ ```json
42
+ {
43
+ "success": true,
44
+ "report": {
45
+ "vault": {"scope": "project", "id": "crewly", "path": "..."},
46
+ "generatedAt": "2026-05-23T03:00:00Z",
47
+ "windowDays": 7,
48
+ "threshold": 10,
49
+ "shouldFire": true,
50
+ "totalMdCount": 47,
51
+ "recentMdCount": 12,
52
+ "countsByFolder": {"llm-curated/decisions": 8, "llm-curated/customers": 3, ...},
53
+ "duplicateCandidates": [
54
+ {"basis": "llm-curated/customers/anthropic-pricing", "pages": ["...-v1.md", "...-v2.md"]}
55
+ ],
56
+ "staleCount": 4,
57
+ "queue": {"pending": 2, "claimed": 0, "processed": 11, "skipped": 1, "total": 14},
58
+ "recommendations": [
59
+ "Window has 12 new md(s) — at or above threshold (10). Consider a consolidation pass.",
60
+ "1 likely-duplicate cluster(s) detected. ...",
61
+ ...
62
+ ]
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Failure modes
68
+
69
+ - `404 vault_missing` — the vault directory does not exist.
70
+ - `404 schema_missing` — vault has no SCHEMA.md.
71
+ - `400 invalid_input` — vaultPath not absolute / windowDays or threshold non-positive.
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ # wiki-bookkeep — vault health report + consolidation signals.
3
+ #
4
+ # Use this skill periodically (or when the queue stats / md-count
5
+ # threshold says "fire"). The skill returns a JSON report:
6
+ # - total / recent md counts
7
+ # - per-folder bucket counts
8
+ # - likely-duplicate clusters (filename Jaccard)
9
+ # - stale-page count (90+ days untouched)
10
+ # - queue snapshot (pending/claimed/processed/skipped)
11
+ # - recommendations
12
+ #
13
+ # This skill does NOT write to the vault. After reading the report,
14
+ # YOUR runtime decides which clusters to merge (call wiki-ingest with
15
+ # a consolidated body) and which pages to archive (out of Phase 1
16
+ # scope — flag in chat for now).
17
+ #
18
+ # Usage:
19
+ # bash execute.sh --vault /path
20
+ # bash execute.sh --vault /path --window-days 14 --threshold 10
21
+ set -euo pipefail
22
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
+ source "${SCRIPT_DIR}/../../_common/lib.sh"
24
+
25
+ INPUT_JSON=""
26
+ VAULT_PATH=""
27
+ WINDOW=""
28
+ THRESHOLD=""
29
+
30
+ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
31
+ INPUT_JSON="$1"; shift || true
32
+ fi
33
+
34
+ while [[ $# -gt 0 ]]; do
35
+ case "$1" in
36
+ --vault|-v) VAULT_PATH="$2"; shift 2 ;;
37
+ --window-days|-d) WINDOW="$2"; shift 2 ;;
38
+ --threshold|-t) THRESHOLD="$2"; shift 2 ;;
39
+ --json|-j) INPUT_JSON="$2"; shift 2 ;;
40
+ --help|-h)
41
+ cat <<EOF
42
+ Usage:
43
+ execute.sh --vault <path> [--window-days 7] [--threshold 10]
44
+
45
+ Returns a JSON bookkeeping report with should-fire signal, recent activity,
46
+ duplicate clusters, queue snapshot, and recommendations. The skill makes
47
+ no LLM calls; you decide next actions based on the report.
48
+ EOF
49
+ exit 0 ;;
50
+ --) shift; break ;;
51
+ *)
52
+ if [[ -z "$INPUT_JSON" && ${1:0:1} == '{' ]]; then INPUT_JSON="$1"; shift
53
+ else error_exit "Unknown argument: $1"; fi ;;
54
+ esac
55
+ done
56
+
57
+ if [ -n "$INPUT_JSON" ]; then
58
+ INPUT=$(read_json_input "$INPUT_JSON")
59
+ [ -z "$VAULT_PATH" ] && VAULT_PATH=$(printf '%s' "$INPUT" | jq -r '.vaultPath // empty')
60
+ [ -z "$WINDOW" ] && WINDOW=$(printf '%s' "$INPUT" | jq -r '.windowDays // empty')
61
+ [ -z "$THRESHOLD" ] && THRESHOLD=$(printf '%s' "$INPUT" | jq -r '.threshold // empty')
62
+ fi
63
+
64
+ require_param "vaultPath (--vault)" "$VAULT_PATH"
65
+
66
+ export _WB_V="$VAULT_PATH"
67
+ BODY=$(jq -n '{vaultPath: env._WB_V}')
68
+ [ -n "$WINDOW" ] && BODY=$(echo "$BODY" | jq --argjson w "$WINDOW" '. + {windowDays: $w}')
69
+ [ -n "$THRESHOLD" ] && BODY=$(echo "$BODY" | jq --argjson t "$THRESHOLD" '. + {threshold: $t}')
70
+ unset _WB_V
71
+
72
+ api_call POST "/wiki/bookkeep" "$BODY"
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: Wiki Ingest
3
+ description: Append a source (spec / decision / chat / pr / learning) into a vault's llm-curated/ tree. Refuses writes into frozen paths.
4
+ version: 1.0.0
5
+ category: knowledge
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - orchestrator
9
+ - team-leader
10
+ ---
11
+
12
+ # Wiki Ingest
13
+
14
+ Append a source to a vault's `llm-curated/log.md` (default) or to a dedicated page under `llm-curated/` (via `--target`). Per Atlas v2.1 §2 + §3.
15
+
16
+ The chat subscriber **already** auto-ingests every user→ORC chat message; this skill is for explicit manual ingest:
17
+
18
+ - TL promoting a decision to `llm-curated/decisions/<date>-<slug>.md`
19
+ - ORC archiving a synthesis memo into `~/.crewly/global-wiki/llm-curated/`
20
+ - A worker filing a `record-learning`-style pattern page
21
+
22
+ ## Inputs
23
+
24
+ | Flag | Required | Description |
25
+ |---|---|---|
26
+ | `--vault <path>` | yes | Absolute vault root (must contain `SCHEMA.md`). |
27
+ | `--source-type <t>` | yes | One of: `user_chat`, `slack_message`, `spec_file`, `pr_merge`, `record_learning`, `task_verified`. |
28
+ | `--source-ref <ref>` | yes | Stable reference for audit (URL, file path, message id, …). |
29
+ | `--source-body <text>` | yes | Body content to ingest. |
30
+ | `--caller <sess>` | no | Session/user that authored the source (defaults to source-ref). |
31
+ | `--target <relpath>` | no | Override the default `llm-curated/log.md`. Must NOT be inside a frozen folder. |
32
+
33
+ ## Output
34
+
35
+ ```json
36
+ {
37
+ "success": true,
38
+ "result": {
39
+ "ok": true,
40
+ "pagesWritten": ["llm-curated/log.md"],
41
+ "logEntry": "## [ISO] sourceType | caller ...",
42
+ "frozenPathsTouched": []
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Failure modes
48
+
49
+ - `422 frozen_path` — target lives under a `hardcoded:` (frozen) folder. The `outcome.frozenFolders` field lists every frozen path in the vault.
50
+ - `404 schema_missing` — vault has no `SCHEMA.md`.
51
+ - `400 invalid_input | empty_body` — missing/invalid args.
52
+
53
+ ## Example: ingest a TL-promoted decision
54
+
55
+ ```bash
56
+ bash execute.sh \
57
+ --vault ~/.crewly/teams/ad923b66-19dd-4d73-9c92-dc1107d37501/wiki \
58
+ --source-type user_chat \
59
+ --source-ref slack://thread/D0ABC/1779363330.313849 \
60
+ --source-body "Pricing locked at \$999 setup + \$799/month." \
61
+ --caller user/steve \
62
+ --target llm-curated/decisions/2026-05-22-crewly-pro-pricing.md
63
+ ```
@@ -0,0 +1,113 @@
1
+ #!/bin/bash
2
+ # wiki-ingest — append a source into a vault's llm-curated/log.md.
3
+ #
4
+ # Phase A scope (Atlas v2.1 §4 + §4.5): rules-only routing — the caller
5
+ # decides which vault. The chat subscriber already auto-ingests every
6
+ # user→ORC message; this skill is for MANUAL ingest (specs, decisions,
7
+ # LLM-promoted content, etc.) where you want explicit control.
8
+ #
9
+ # Refuses writes into frozen paths (memory/, sop-overrides/, sop/,
10
+ # team-norm/). Only llm-curated/ is restructure-eligible.
11
+ #
12
+ # Usage:
13
+ # bash execute.sh --vault /path --source-type spec_file --source-ref /path/to/spec.md --source-body "..."
14
+ # bash execute.sh --vault /path --source-type user_chat --source-ref msg-1 --source-body "..." --caller user/steve
15
+ # bash execute.sh --vault /path --target llm-curated/decisions/2026-05-22-pricing.md --source-type user_chat --source-ref s1 --source-body "..."
16
+ # bash execute.sh --json '{"vaultPath":"...","sourceType":"...","sourceRef":"...","sourceBody":"..."}'
17
+ set -euo pipefail
18
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
+ source "${SCRIPT_DIR}/../../_common/lib.sh"
20
+
21
+ INPUT_JSON=""
22
+ VAULT_PATH=""
23
+ SOURCE_TYPE=""
24
+ SOURCE_REF=""
25
+ SOURCE_BODY=""
26
+ CALLER=""
27
+ TARGET=""
28
+
29
+ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
30
+ INPUT_JSON="$1"
31
+ shift || true
32
+ fi
33
+
34
+ while [[ $# -gt 0 ]]; do
35
+ case "$1" in
36
+ --vault|-v)
37
+ VAULT_PATH="$2"; shift 2 ;;
38
+ --source-type|-t)
39
+ SOURCE_TYPE="$2"; shift 2 ;;
40
+ --source-ref|-r)
41
+ SOURCE_REF="$2"; shift 2 ;;
42
+ --source-body|-b)
43
+ SOURCE_BODY="$2"; shift 2 ;;
44
+ --caller|-c)
45
+ CALLER="$2"; shift 2 ;;
46
+ --target)
47
+ TARGET="$2"; shift 2 ;;
48
+ --json|-j)
49
+ INPUT_JSON="$2"; shift 2 ;;
50
+ --help|-h)
51
+ cat <<EOF
52
+ Usage:
53
+ execute.sh --vault <vault> --source-type <type> --source-ref <ref> --source-body <body> [--caller <sess>] [--target <relpath>]
54
+ execute.sh --json '{"vaultPath":"...","sourceType":"...","sourceRef":"...","sourceBody":"...","callerSession":"...","targetRelativePath":"..."}'
55
+
56
+ sourceType MUST be one of:
57
+ user_chat | slack_message | spec_file | pr_merge | record_learning | task_verified
58
+
59
+ Default target is "llm-curated/log.md". Override with --target for a dedicated page (must NOT be inside a frozen folder).
60
+ EOF
61
+ exit 0 ;;
62
+ --)
63
+ shift; break ;;
64
+ *)
65
+ if [[ -z "$INPUT_JSON" && ${1:0:1} == '{' ]]; then
66
+ INPUT_JSON="$1"; shift
67
+ else
68
+ error_exit "Unknown argument: $1"
69
+ fi ;;
70
+ esac
71
+ done
72
+
73
+ # stdin JSON fallback
74
+ if [ -z "$INPUT_JSON" ] && [ -z "$SOURCE_BODY" ] && [ ! -t 0 ]; then
75
+ STDIN_DATA="$(cat)"
76
+ if [[ ${STDIN_DATA:0:1} == '{' ]]; then
77
+ INPUT_JSON="$STDIN_DATA"
78
+ else
79
+ SOURCE_BODY="$STDIN_DATA"
80
+ fi
81
+ fi
82
+
83
+ if [ -n "$INPUT_JSON" ]; then
84
+ INPUT=$(read_json_input "$INPUT_JSON")
85
+ [ -z "$VAULT_PATH" ] && VAULT_PATH=$(printf '%s' "$INPUT" | jq -r '.vaultPath // empty')
86
+ [ -z "$SOURCE_TYPE" ] && SOURCE_TYPE=$(printf '%s' "$INPUT" | jq -r '.sourceType // empty')
87
+ [ -z "$SOURCE_REF" ] && SOURCE_REF=$(printf '%s' "$INPUT" | jq -r '.sourceRef // empty')
88
+ [ -z "$SOURCE_BODY" ] && SOURCE_BODY=$(printf '%s' "$INPUT" | jq -r '.sourceBody // empty')
89
+ [ -z "$CALLER" ] && CALLER=$(printf '%s' "$INPUT" | jq -r '.callerSession // empty')
90
+ [ -z "$TARGET" ] && TARGET=$(printf '%s' "$INPUT" | jq -r '.targetRelativePath // empty')
91
+ fi
92
+
93
+ require_param "vaultPath (--vault)" "$VAULT_PATH"
94
+ require_param "sourceType (--source-type)" "$SOURCE_TYPE"
95
+ require_param "sourceRef (--source-ref)" "$SOURCE_REF"
96
+ require_param "sourceBody (--source-body)" "$SOURCE_BODY"
97
+
98
+ # Build body via env vars (safe escaping).
99
+ export _WI_VAULT="$VAULT_PATH"
100
+ export _WI_TYPE="$SOURCE_TYPE"
101
+ export _WI_REF="$SOURCE_REF"
102
+ export _WI_BODY="$SOURCE_BODY"
103
+ BODY=$(jq -n '{
104
+ vaultPath: env._WI_VAULT,
105
+ sourceType: env._WI_TYPE,
106
+ sourceRef: env._WI_REF,
107
+ sourceBody: env._WI_BODY
108
+ }')
109
+ [ -n "$CALLER" ] && { export _WI_CALLER="$CALLER"; BODY=$(echo "$BODY" | jq '. + {callerSession: env._WI_CALLER}'); unset _WI_CALLER; }
110
+ [ -n "$TARGET" ] && { export _WI_TARGET="$TARGET"; BODY=$(echo "$BODY" | jq '. + {targetRelativePath: env._WI_TARGET}'); unset _WI_TARGET; }
111
+ unset _WI_VAULT _WI_TYPE _WI_REF _WI_BODY
112
+
113
+ api_call POST "/wiki/ingest" "$BODY"