@synity/bitrix-skills 1.3.0 → 1.3.2

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 (31) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/bin/bitrix-skills.js +0 -0
  3. package/dist/cli.js +80 -31
  4. package/dist/features/task-sync/index.js +0 -0
  5. package/package.json +20 -16
  6. package/src/features/bx/feature.json +5 -3
  7. package/src/features/bx-calendar/feature.json +7 -3
  8. package/src/features/bx-crm/assets/SKILL.md +73 -36
  9. package/src/features/bx-crm/assets/commerce.md +56 -27
  10. package/src/features/bx-crm/assets/convert.md +70 -0
  11. package/src/features/bx-crm/assets/document.md +103 -0
  12. package/src/features/bx-crm/assets/flows.md +144 -0
  13. package/src/features/bx-crm/assets/onboard.md +91 -73
  14. package/src/features/bx-crm/assets/report.md +64 -33
  15. package/src/features/bx-crm/assets/research.md +62 -24
  16. package/src/features/bx-crm/assets/vn-norms.md +50 -0
  17. package/src/features/bx-crm/feature.json +7 -3
  18. package/src/features/bx-task/assets/lib/bx-api.sh +0 -0
  19. package/src/features/bx-task/assets/lib/bx-resolve-task.sh +0 -0
  20. package/src/features/bx-task/feature.json +6 -3
  21. package/src/features/task-sync/assets/githooks/commit-msg +0 -0
  22. package/src/features/task-sync/assets/githooks/install.sh +0 -0
  23. package/src/features/task-sync/assets/scripts/bitrix-attach-files.sh +0 -0
  24. package/src/features/task-sync/assets/scripts/bitrix-lib.sh +0 -0
  25. package/src/features/task-sync/assets/scripts/bitrix-render-digest.sh +0 -0
  26. package/src/features/task-sync/assets/scripts/bitrix-session-check.sh +0 -0
  27. package/src/features/task-sync/assets/scripts/bitrix-session-sync.sh +0 -0
  28. package/src/features/task-sync/assets/scripts/bitrix-skill-end.sh +0 -0
  29. package/src/features/task-sync/assets/scripts/bitrix-skill-start.sh +0 -0
  30. package/src/features/task-sync/assets/skill/SKILL.md +173 -0
  31. package/src/features/task-sync/feature.json +9 -4
@@ -0,0 +1,103 @@
1
+ # bx:crm — Document Template Fill
2
+
3
+ Covers document and PDF generation for contracts, quotes, invoices, and CRM templates.
4
+
5
+ ---
6
+
7
+ ## Trigger Phrases
8
+
9
+ - "render document", "fill contract", "generate PDF"
10
+ - "tạo báo giá PDF", "fill hợp đồng", "xuất PDF", "tạo hợp đồng từ deal"
11
+
12
+ ---
13
+
14
+ ## Strategy
15
+
16
+ Primary flow: use Bitrix native `crm.documentgenerator.*` REST via `codemode.request`.
17
+
18
+ Fallback flow: user provides a template file/link/text; fetch CRM data, propose a mapping table, and let the user run the final fill in their template tool.
19
+
20
+ ---
21
+
22
+ ## Primary Flow — Bitrix Document Generator
23
+
24
+ ```js
25
+ // 1. List templates
26
+ codemode.request({
27
+ method: "GET",
28
+ path: "/crm.documentgenerator.template.list/"
29
+ })
30
+
31
+ // 2. Pick template by NAME and supported entity type.
32
+
33
+ // 3. Render document
34
+ codemode.request({
35
+ method: "POST",
36
+ path: "/crm.documentgenerator.document.add/",
37
+ body: {
38
+ templateId,
39
+ entityTypeId,
40
+ entityId,
41
+ values: {}
42
+ }
43
+ })
44
+ ```
45
+
46
+ Typical return: `{ document: { id, fileUrl } }`.
47
+
48
+ Use Bitrix `entityTypeId` values discovered from the portal; do not hardcode if uncertain.
49
+
50
+ ---
51
+
52
+ ## Verify Checklist — Primary
53
+
54
+ - [ ] Template supports the selected `entityTypeId`.
55
+ - [ ] `entityId` belongs to the intended customer/deal.
56
+ - [ ] `fileUrl` returned and is accessible to the intended user.
57
+ - [ ] Visual check confirms placeholders are filled.
58
+
59
+ ---
60
+
61
+ ## Fallback Flow — User Paste
62
+
63
+ 1. Ask user for template path, URL, or pasted fields.
64
+ 2. Fetch CRM data with read helpers only: deal, contact, company, products, estimate/invoice if needed.
65
+ 3. Present a mapping table:
66
+
67
+ ```text
68
+ | Placeholder | CRM source | Value preview |
69
+ |---|---|---|
70
+ | {{CompanyName}} | company.RQ_COMPANY_FULL_NAME | ... |
71
+ | {{TaxCode}} | company.RQ_VAT_ID | ... |
72
+ | {{DealTotal}} | deal.OPPORTUNITY + CURRENCY_ID | ... |
73
+ ```
74
+
75
+ 4. Ask user to confirm mapping before any generated document is shared.
76
+
77
+ ---
78
+
79
+ ## Verify Checklist — Fallback
80
+
81
+ - [ ] Mapping table reviewed by user.
82
+ - [ ] No placeholder left unmapped unless user accepts it.
83
+ - [ ] Sensitive data appears only for the intended entity.
84
+ - [ ] Final document is visually checked before sending.
85
+
86
+ ---
87
+
88
+ ## Pitfalls
89
+
90
+ | Pitfall | Risk | Action |
91
+ |---|---|---|
92
+ | Hardcoded `templateId` | Wrong portal template | Always list templates first |
93
+ | Entity type mismatch | 4xx or blank fields | Match template to entity type |
94
+ | Wrong `entityId` | PII leak | Verify ownership before render |
95
+ | Synity Docs MCP assumed | Tool unavailable | Use generic fallback flow |
96
+
97
+ ---
98
+
99
+ ## See Also
100
+
101
+ - Entity preparation: [onboard.md](./onboard.md)
102
+ - Estimate/invoice context: [commerce.md](./commerce.md)
103
+ - Contract PDF combos: [flows.md](./flows.md)
@@ -0,0 +1,144 @@
1
+ # bx:crm — Cross-Flow Combos
2
+
3
+ Use this file when the user asks for a multi-helper CRM workflow.
4
+
5
+ ---
6
+
7
+ ## Combo Template
8
+
9
+ Each combo includes trigger phrases, sequence, verify checks, and underlying SOP files. On failure mid-chain, stop and report created IDs.
10
+
11
+ ---
12
+
13
+ ## Combo 1 — Deal + Estimate
14
+
15
+ **Trigger:** "tạo deal kèm báo giá", "deal with quote".
16
+
17
+ **Sequence:**
18
+ ```js
19
+ const deal = await createDealWithParties({ contact, company, deal })
20
+ await setDealProducts({ dealId: deal.dealId, products })
21
+ const estimate = await createEstimate({ dealId: deal.dealId, title, currency })
22
+ await setEstimateProducts({ estimateId: estimate.estimateId, products })
23
+ ```
24
+
25
+ **Verify:** dealId + estimateId returned; products and currency match.
26
+
27
+ **See also:** [onboard.md](./onboard.md), [commerce.md](./commerce.md)
28
+
29
+ ---
30
+
31
+ ## Combo 2 — Deal + Invoice
32
+
33
+ **Trigger:** "tạo deal kèm invoice", "deal with invoice".
34
+
35
+ **Sequence:**
36
+ ```js
37
+ const deal = await createDealWithParties({ contact, company, deal })
38
+ await setDealProducts({ dealId: deal.dealId, products })
39
+ // Run commerce.md steps 1-7: estimate -> confirmation -> approve -> invoice.
40
+ // Do not create an invoice directly from a new deal.
41
+ ```
42
+
43
+ **Verify:** estimate approved first; invoice status is `N`; payer and products are linked.
44
+
45
+ **See also:** [onboard.md](./onboard.md), [commerce.md](./commerce.md)
46
+
47
+ ---
48
+
49
+ ## Combo 3 — Lead → Deal
50
+
51
+ **Trigger:** "convert lead", "qualify lead", "chuyển lead thành deal".
52
+
53
+ **Sequence:**
54
+ ```js
55
+ await setLeadProducts({ leadId, products }) // only if products are provided
56
+ const result = await convertLeadToDeal({
57
+ leadId,
58
+ createDealParams: { stageId, categoryId },
59
+ upsertContact: true,
60
+ upsertCompany: true
61
+ })
62
+ ```
63
+
64
+ **Verify:** lead is converted; deal exists; contact/company linked; warnings reviewed.
65
+
66
+ **See also:** [convert.md](./convert.md), [onboard.md](./onboard.md)
67
+
68
+ ---
69
+
70
+ ## Combo 4 — Báo Giá → Hợp Đồng PDF
71
+
72
+ **Trigger:** "duyệt báo giá rồi tạo hợp đồng PDF", "quote to contract PDF".
73
+
74
+ **Sequence:**
75
+ ```js
76
+ await approveEstimate({ estimateId })
77
+ await closeEstimatesForDeal({ dealId })
78
+ // Then run document.md Primary Flow: list templates, verify entity, render.
79
+ ```
80
+
81
+ **Verify:** estimate approved; other estimates closed; document URL returned.
82
+
83
+ **See also:** [commerce.md](./commerce.md), [document.md](./document.md)
84
+
85
+ ---
86
+
87
+ ## Combo 5 — Quote-to-Cash Full
88
+
89
+ **Trigger:** "full quote to cash", "đầy đủ báo giá đến hóa đơn".
90
+
91
+ **Sequence:**
92
+ ```js
93
+ const deal = await createDealWithParties({ contact, company, deal })
94
+ await setDealProducts({ dealId: deal.dealId, products })
95
+ const estimate = await createEstimate({ dealId: deal.dealId, title, currency })
96
+ await setEstimateProducts({ estimateId: estimate.estimateId, products })
97
+ // STOP. Do not continue until customer/user confirms the estimate.
98
+ ```
99
+
100
+ **Post-confirmation sequence:**
101
+ ```js
102
+ await approveEstimate({ estimateId: estimate.estimateId })
103
+ await closeEstimatesForDeal({ dealId: deal.dealId })
104
+ const invoice = await createSmartInvoice({ dealId: deal.dealId, contactId: deal.contactId, companyId: deal.companyId })
105
+ await copyDealProductsToInvoice({ dealId: deal.dealId, invoiceId: invoice.invoiceId })
106
+ // Then run document.md Primary Flow for invoice PDF.
107
+ ```
108
+
109
+ **Verify:** deal, estimate, invoice, products, payer, and document URL all pass their subfile checklists.
110
+
111
+ **See also:** [onboard.md](./onboard.md), [commerce.md](./commerce.md), [document.md](./document.md)
112
+
113
+ ---
114
+
115
+ ## Combo 6 — Payment Link Send
116
+
117
+ **Trigger:** "gửi link thanh toán", "send payment link".
118
+
119
+ **Sequence:**
120
+ ```js
121
+ // Start from an existing approved invoice or run Combo 5 through invoice creation.
122
+ const paysystems = await codemode.request({ method: "GET", path: "/sale.paysystem.list/" })
123
+ // Only use a portal-approved payment URL generator/API. Do not hand-construct signed URLs.
124
+ ```
125
+
126
+ **Verify:** invoice exists; payer linked; URL belongs to invoice and intended customer.
127
+
128
+ **See also:** [commerce.md](./commerce.md)
129
+
130
+ ---
131
+
132
+ ## Global Cross-Flow Rules
133
+
134
+ - If any write fails mid-chain, stop and report partial state with IDs.
135
+ - Never auto-rollback; helpers do not guarantee rollback support.
136
+ - Do not use `idempotencyKey` until MCP D1 migration is fixed.
137
+ - Keep currency consistent through deal, estimate, invoice, and product rows.
138
+
139
+ ## Verify Checklist
140
+
141
+ - [ ] Every write step had user confirmation or an approved prior checkpoint.
142
+ - [ ] Invoice was created only after estimate approval unless user supplied an existing approved invoice.
143
+ - [ ] Document rendering followed [document.md](./document.md) template/entity checks.
144
+ - [ ] Partial failures reported created IDs and next manual action.
@@ -1,32 +1,12 @@
1
1
  # bx:crm — Onboard (Write SOP)
2
2
 
3
- Covers: contact, company (VN), deal, lead creation.
3
+ Covers: contact, company (VN), deal, lead creation, and safe updates.
4
4
 
5
- ---
6
-
7
- ## Phone Normalization (CRITICAL)
8
-
9
- `upsertContact` auto-normalizes — **pass raw phone, never pre-format**.
10
-
11
- | Input | Stored as |
12
- |-------|-----------|
13
- | `0977680550` | `+84977680550` |
14
- | `0977 68 0550` | `+84977680550` |
15
- | `84977680550` | `+84977680550` |
16
- | `+84977680550` | `+84977680550` (idempotent) |
17
- | `+6591234567` | `+6591234567` (non-VN: keep) |
5
+ ## Phone + VN Normalization
18
6
 
19
- ---
20
-
21
- ## HONORIFIC
7
+ See [vn-norms.md](./vn-norms.md) for phone `+84`, HONORIFIC, address, and MST rules.
22
8
 
23
- | Input | Value |
24
- |-------|-------|
25
- | Anh / Mr | `Anh` |
26
- | Chị / Ms / Mrs | `Chị` |
27
- | Ông (senior male) | `Ông` |
28
- | Bà (senior female) | `Bà` |
29
- | Unknown | omit field |
9
+ `upsertContact` auto-normalizes phone numbers. Pass raw phone, never pre-format.
30
10
 
31
11
  ---
32
12
 
@@ -35,93 +15,131 @@ Covers: contact, company (VN), deal, lead creation.
35
15
  ```js
36
16
  upsertContact({
37
17
  name: "Nguyen Van A",
38
- phone: "0977680550", // raw — helper normalizes
18
+ phone: "0977680550",
39
19
  email: "a@example.com",
40
20
  HONORIFIC: "Anh",
41
- match: { phone } // dedup check before create
21
+ match: { phone }
42
22
  })
43
23
  ```
44
24
 
45
- Minimum: `name` + (`phone` OR `email`) + `HONORIFIC` (if determinable).
46
-
47
- ---
25
+ Minimum: `name` + (`phone` OR `email`) + `HONORIFIC` if determinable. See [vn-norms.md](./vn-norms.md) for the HONORIFIC table.
48
26
 
49
27
  ## Company — VN Rule (CRITICAL)
50
28
 
51
- **NEVER `crm.company.add` directly for VN companies with MST.**
29
+ **Never call `crm.company.add` directly for VN companies with MST.**
52
30
 
53
31
  `upsertCompanyByTaxCode` auto-does:
54
- 1. VietQR lookup → legal name + address from GDT tax registry
55
- 2. Sets `RQ_INN` = `RQ_VAT_ID` = taxCode on requisite
56
- 3. Creates address entity (needed for `{CompanyAddressLegal}` in document templates)
32
+ 1. VietQR lookup → legal name + address from GDT tax registry.
33
+ 2. Sets `RQ_INN` = `RQ_VAT_ID` = taxCode on requisite.
34
+ 3. Creates address entity for document templates.
57
35
 
58
36
  ```js
59
37
  upsertCompanyByTaxCode({
60
- taxCode: "0315427733", // MST — REQUIRED
61
- title: "Pegasus Realty", // optional, VietQR fills if missing
38
+ taxCode: "0315427733",
39
+ title: "Pegasus Realty",
62
40
  phone: "...",
63
- email: "...",
41
+ email: "..."
64
42
  })
65
43
  ```
66
44
 
67
- **Verify checklist for company:**
68
- - `RQ_INN` populated
69
- - `RQ_VAT_ID` populated (same value)
70
- - `RQ_COMPANY_FULL_NAME` populated (legal name from registry)
71
- - Address entity exists (ENTITY_TYPE_ID=8)
72
-
73
- ---
45
+ **Verify company:**
46
+ - `RQ_INN` and `RQ_VAT_ID` populated.
47
+ - `RQ_COMPANY_FULL_NAME` populated from registry.
48
+ - Address entity exists (`ENTITY_TYPE_ID=8`).
74
49
 
75
50
  ## Deal — Required Fields
76
51
 
77
- Use `createDealWithParties` for atomic contact + company + deal in one call.
52
+ Use `createDealWithParties` for atomic contact + company + deal.
78
53
 
79
54
  | Field | How to get | Example |
80
- |-------|-----------|---------|
81
- | `STAGE_ID` | `codemode.entityIds()` → dealStageSemanticId | **never hardcode** |
82
- | `CURRENCY_ID` | from user input | `USD`, `VND` |
83
- | `OPPORTUNITY` | from user input (number) | `588` |
84
- | `SOURCE_ID` | see mapping below | `OTHER` |
85
-
86
- **SOURCE_ID mapping:**
87
-
88
- | Context | Value |
89
- |---------|-------|
90
- | Event / offline meeting | `OTHER` |
91
- | Website / form | `WEB` |
92
- | Referral / partner | `PARTNER` |
93
- | Cold call / outreach | `CALL` |
94
- | Social / ad | `ADVERTISING` |
55
+ |---|---|---|
56
+ | `STAGE_ID` | `codemode.entityIds()` | never hardcode |
57
+ | `CURRENCY_ID` | user input | `USD`, `VND` |
58
+ | `OPPORTUNITY` | user input number | `588` |
59
+ | `SOURCE_ID` | mapping below | `OTHER` |
60
+
61
+ **SOURCE_ID mapping:** Event/offline=`OTHER`, website/form=`WEB`, partner=`PARTNER`, call=`CALL`, social/ad=`ADVERTISING`.
95
62
 
96
63
  **COMMENTS — BANT template:**
64
+ ```text
65
+ Budget: <amount/range/unknown>
66
+ Authority: <decision maker>
67
+ Need: <problem or goal>
68
+ Timeline: <start date>
69
+ Notes: <context>
97
70
  ```
98
- Budget: <amount or range, or "unknown">
99
- Authority: <who makes the decision>
100
- Need: <specific problem / goal>
101
- Timeline: <when they want to start>
102
- Notes: <additional context>
71
+
72
+ ## Lead Create + Products
73
+
74
+ Use lead when the prospect is unqualified or missing deal-level budget/timeline.
75
+
76
+ ```js
77
+ createLeadWithParties({
78
+ title: "Nguyen Van A - CRM training",
79
+ name: "Nguyen Van A",
80
+ phone: "0977680550",
81
+ email: "a@example.com",
82
+ companyTitle: "ABC Training",
83
+ sourceId: "WEB",
84
+ comments: "Need: CRM training\nTimeline: Q2"
85
+ })
86
+
87
+ setLeadProducts({
88
+ leadId,
89
+ products: [{ name: "Workshop", price: 5000000, quantity: 1, currency: "VND" }]
90
+ })
103
91
  ```
104
92
 
105
- > **Omit `idempotencyKey`** causes D1 table error at runtime.
93
+ For qualification, convert via [convert.md](./convert.md).
94
+
95
+ ## Update Existing Entity
96
+
97
+ 1. Search before update: `codemode.search({ keywords, entities: ["contact","company","deal","lead"], intent: "read" })`.
98
+ 2. Read current entity by ID and validate target ownership.
99
+ 3. Discover allowed fields with `crm.{entity}.fields` when field names are uncertain.
100
+ 4. Show before/after field diff and confirm every update, even for a single match.
101
+ 5. Send only changed fields.
102
+
103
+ ```js
104
+ // MCP gap fallback when no typed update helper exists.
105
+ codemode.request({
106
+ method: "POST",
107
+ path: "/crm.deal.update/",
108
+ body: { id: dealId, fields: { OPPORTUNITY: 9000000, ASSIGNED_BY_ID: 12 } }
109
+ })
110
+ ```
111
+
112
+ Skip this search rule for `upsertContact` and `upsertCompanyByTaxCode`; they dedup internally.
113
+
114
+ ## Custom Fields & Routing
115
+
116
+ - `UF_CRM_*`: discover with `codemode.entityIds()` or `crm.{entity}.fields`.
117
+ - `categoryId`: route deal to the correct pipeline.
118
+ - `assignedById`: responsible user. Resolve user IDs before writing.
119
+ - Do not echo full custom-field PII unless user explicitly asks.
120
+
121
+ ## Idempotency
122
+
123
+ Omit `idempotencyKey`. **Verified 2026-05-15:** D1 table `idempotency_keys` missing; passing the key throws `SQLITE_ERROR` before write. Re-verify when MCP version changes. Escalation: MCP team.
106
124
 
107
125
  ---
108
126
 
109
127
  ## Verify Checklist (mandatory after every write op)
110
128
 
111
- - [ ] Contact: `PHONE[0].VALUE` starts with `+84` for VN numbers
112
- - [ ] Company: `RQ_VAT_ID` populated, address entity exists
113
- - [ ] Deal: `STAGE_ID` set, `OPPORTUNITY > 0`, payer linked
114
- - [ ] Lead: `SOURCE_ID` set, phone/email present
129
+ - [ ] Contact: VN phone stored with `+84`; HONORIFIC matches [vn-norms.md](./vn-norms.md).
130
+ - [ ] Company: `RQ_VAT_ID` populated, address entity exists, MST format valid.
131
+ - [ ] Deal: `STAGE_ID` set, `OPPORTUNITY > 0`, payer linked.
132
+ - [ ] Lead: `SOURCE_ID` set, phone/email present, products set when user requested products.
133
+ - [ ] Update: only intended fields changed.
115
134
 
116
135
  ---
117
136
 
118
137
  ## Known Pitfalls
119
138
 
120
139
  | Mistake | Symptom | Fix |
121
- |---------|---------|-----|
122
- | Used `bx-task` for CRM | Creates project task, not CRM record | Use `/bx:crm` |
140
+ |---|---|---|
141
+ | Used `bx:task` for CRM | Creates project task, not CRM record | Use `/bx:crm` |
123
142
  | `crm.contact.add` directly | No dedup, phone not normalized | `upsertContact` |
124
- | `crm.company.add` directly | Requisite missing RQ_VAT_ID + no address | `upsertCompanyByTaxCode` |
143
+ | `crm.company.add` directly | Requisite missing tax fields + address | `upsertCompanyByTaxCode` |
125
144
  | Passed `idempotencyKey` | D1 table error at runtime | Omit the field |
126
- | Pre-formatted phone `+84977...` | Helper double-normalizes (idempotent, fine) | Pass raw anyway |
127
145
  | Skipped verify step | Bugs discovered sessions later | Always verify |
@@ -4,10 +4,16 @@ Covers: dealForecast, stuckInStage, overdueActivities, arReport, teamReport.
4
4
 
5
5
  ---
6
6
 
7
+ ## Timezone
8
+
9
+ MCP may return UTC ISO timestamps. Convert display times to Asia/Saigon (UTC+7). Date-only summaries use the Asia/Saigon date.
10
+
11
+ ---
12
+
7
13
  ## Helper Selection
8
14
 
9
15
  | Report | Helper | Typical Frequency |
10
- |--------|--------|-------------------|
16
+ |---|---|---|
11
17
  | Pipeline forecast month/quarter | `dealForecast` | Weekly |
12
18
  | Deals stuck too long in stage | `stuckInStage` | Weekly |
13
19
  | Overdue activities | `overdueActivities` | Daily |
@@ -19,61 +25,71 @@ Covers: dealForecast, stuckInStage, overdueActivities, arReport, teamReport.
19
25
 
20
26
  ## Output Template — dealForecast
21
27
 
22
- ```
23
- | Month | Predicted | Won | At Risk | # Deals |
24
- |--------|-----------|-----|---------|---------|
25
- | T5/26 | $X | $Y | $Z | N |
28
+ ```text
29
+ | Month | Predicted | Won | At Risk | # Deals |
30
+ |---|---:|---:|---:|---:|
31
+ | T5/26 | $X | $Y | $Z | N |
26
32
  ```
27
33
 
28
- Red flags: deals in same stage > 30 days with no activity → highlight with 🔴.
34
+ Red flags: deals in same stage > 30 days with no activity.
29
35
 
30
36
  ---
31
37
 
32
38
  ## Output Template — stuckInStage
33
39
 
34
- Group by pipeline stage sort by days stuck DESC within each group.
40
+ Group by pipeline stage. Sort by days stuck DESC within each group.
35
41
 
36
- **Top 5 per stage:**
37
- ```
42
+ ```text
38
43
  Stage: [Stage Name]
39
- 1. [dealId] "[title]" — [owner] — [N] days — last activity: [date]
40
- 2. ...
44
+ 1. [dealId] "[title]" — [owner] — [N] days — last activity: [date]
41
45
  ```
42
46
 
43
47
  ---
44
48
 
45
49
  ## Output Template — overdueActivities
46
50
 
47
- ```
51
+ ```text
48
52
  | Deal/Contact | Activity | Assigned | Due | Days Overdue |
49
- |-------------|----------|----------|-----|--------------|
50
- | ... | ... | ... | ... | N |
53
+ |---|---|---|---|---:|
54
+ | ... | ... | ... | ... | N |
51
55
  ```
52
56
 
53
- Sort by days overdue DESC. Group by assignee if > 10 items.
57
+ Sort by days overdue DESC. Group by assignee if more than 10 items.
54
58
 
55
59
  ---
56
60
 
57
- ## Output Template — arReport (Aging Buckets)
61
+ ## Output Template — arReport
58
62
 
63
+ ```text
64
+ | Bucket | Amount | % of Total | # Invoices |
65
+ |---|---:|---:|---:|
66
+ | Current | $X | X% | N |
67
+ | 1-30d | $X | X% | N |
68
+ | 31-60d | $X | X% | N |
69
+ | 61-90d | $X | X% | N |
70
+ | 90d+ | $X | X% | N |
59
71
  ```
60
- | Bucket | Amount | % of Total | # Invoices |
61
- |----------|--------|------------|------------|
62
- | Current | $X | X% | N |
63
- | 1–30d | $X | X% | N |
64
- | 31–60d | $X | X% | N |
65
- | 61–90d | $X | X% | N |
66
- | 90d+ | $X | X% | N | ← highlight if > 20%
72
+
73
+ For 90d+ bucket: list each debtor below the table.
74
+
75
+ ---
76
+
77
+ ## Output Template teamReport
78
+
79
+ ```text
80
+ | Rep | # Deals | Total Value | Avg Response | Overdue |
81
+ |---|---:|---:|---:|---:|
82
+ | ... | N | $X | Nh | N |
67
83
  ```
68
84
 
69
- For 90d+ bucket: list each debtor individually below the table.
85
+ Sort by Total Value DESC. Flag response time and overdue counts using the thresholds below.
70
86
 
71
87
  ---
72
88
 
73
89
  ## Interpretation Thresholds
74
90
 
75
- | Metric | Yellow ⚠️ | Red 🔴 | Action |
76
- |--------|----------|--------|--------|
91
+ | Metric | Yellow | Red | Action |
92
+ |---|---|---|---|
77
93
  | stuckInStage | > 14 days | > 30 days | Assign follow-up activity |
78
94
  | AR 90d+ bucket | > 10% total AR | > 20% total AR | Escalate to account manager |
79
95
  | teamReport response_time | > 12h | > 24h | Flag for coaching |
@@ -84,15 +100,30 @@ For 90d+ bucket: list each debtor individually below the table.
84
100
  ## Common Report Calls
85
101
 
86
102
  ```js
87
- // Weekly pipeline review
88
103
  dealForecast({ period: "month", includeAtRisk: true })
104
+ stuckInStage({ minDays: 14, pipelineId: null })
105
+ overdueActivities({ assignedTo: null })
106
+ arReport({ groupByBucket: true, listDebtors90Plus: true })
107
+ teamReport({ period: "month", includeResponseTime: true })
108
+ ```
109
+
110
+ ---
89
111
 
90
- // Stuck deals
91
- stuckInStage({ minDays: 14, pipelineId: null }) // null = all pipelines
112
+ ## Example Dialog — dealForecast
92
113
 
93
- // Daily overdue check
94
- overdueActivities({ assignedTo: null }) // null = all reps
114
+ User: "review pipeline tuần này"
95
115
 
96
- // Monthly AR
97
- arReport({ groupByBucket: true, listDebtors90Plus: true })
116
+ LLM behavior: call `dealForecast({ period: "month", includeAtRisk: true })`, convert dates to Asia/Saigon, then summarize risks.
117
+
118
+ Sample output:
119
+ ```text
120
+ Pipeline T5/26: predicted 1.2B VND, won 420M, at risk 180M across 4 deals.
121
+ Red flag: 3 deals stuck >30 days with no recent activity.
122
+ Next action: assign follow-up activities today for all red-flag deals.
98
123
  ```
124
+
125
+ ## Verify Checklist
126
+
127
+ - [ ] Date/time displayed in Asia/Saigon.
128
+ - [ ] Totals reconcile with helper output.
129
+ - [ ] Red/yellow flags use the threshold table above.