@elevasis/sdk 1.22.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/cli.cjs +980 -42
  2. package/dist/index.d.ts +1221 -220
  3. package/dist/index.js +390 -15
  4. package/dist/test-utils/index.d.ts +964 -211
  5. package/dist/test-utils/index.js +257 -14
  6. package/dist/worker/index.js +122 -14
  7. package/package.json +3 -3
  8. package/reference/claude-config/rules/operations.md +3 -3
  9. package/reference/claude-config/rules/organization-model.md +88 -85
  10. package/reference/claude-config/rules/organization-os.md +104 -104
  11. package/reference/claude-config/rules/vibe.md +235 -235
  12. package/reference/claude-config/skills/om/SKILL.md +324 -0
  13. package/reference/claude-config/skills/{knowledge → om}/operations/customers.md +110 -109
  14. package/reference/claude-config/skills/{knowledge → om}/operations/features.md +77 -76
  15. package/reference/claude-config/skills/{knowledge → om}/operations/goals.md +119 -118
  16. package/reference/claude-config/skills/{knowledge → om}/operations/identity.md +94 -93
  17. package/reference/claude-config/skills/{knowledge → om}/operations/labels.md +94 -94
  18. package/reference/claude-config/skills/{knowledge → om}/operations/offerings.md +110 -109
  19. package/reference/claude-config/skills/{knowledge → om}/operations/roles.md +100 -99
  20. package/reference/claude-config/skills/{knowledge → om}/operations/techStack.md +30 -30
  21. package/reference/claude-config/skills/project/SKILL.md +1088 -1088
  22. package/reference/claude-config/skills/setup/SKILL.md +275 -275
  23. package/reference/claude-config/skills/tutorial/SKILL.md +259 -259
  24. package/reference/claude-config/skills/tutorial/progress-template.md +74 -74
  25. package/reference/claude-config/skills/tutorial/technical.md +1303 -1303
  26. package/reference/claude-config/skills/tutorial/vibe-coder.md +890 -890
  27. package/reference/claude-config/sync-notes/2026-05-14-organization-model-ontology-refactor.md +7 -4
  28. package/reference/claude-config/sync-notes/2026-05-15-om-skill-rename-and-write-family.md +52 -0
  29. package/reference/examples/organization-model.ts +26 -2
  30. package/reference/scaffold/core/organization-model.mdx +16 -11
  31. package/reference/scaffold/recipes/add-a-feature.md +28 -26
  32. package/reference/scaffold/recipes/add-a-resource.md +26 -16
  33. package/reference/scaffold/recipes/customize-organization-model.md +5 -3
  34. package/reference/scaffold/recipes/extend-lead-gen.md +9 -9
  35. package/reference/scaffold/recipes/index.md +1 -1
  36. package/reference/scaffold/recipes/query-the-knowledge-graph.md +189 -185
  37. package/reference/scaffold/reference/contracts.md +139 -101
  38. package/reference/scaffold/reference/glossary.md +74 -72
  39. package/reference/claude-config/skills/knowledge/SKILL.md +0 -345
  40. /package/reference/claude-config/skills/{knowledge → om}/operations/codify-level-a.md +0 -0
  41. /package/reference/claude-config/skills/{knowledge → om}/operations/codify-level-b.md +0 -0
@@ -1,1088 +1,1088 @@
1
- ---
2
- name: project
3
- description: "Portfolio- and project-level work management -- orientation, intent routing, active projects, milestones, tasks, notes, and resume context -- via the elevasis-sdk project:* CLI."
4
- argument-hint: "[create | list | work | add | update | status | note | milestone | task | delete] [args]"
5
- allowed-tools: Bash, Read, Write, Edit, Glob, Grep
6
- ---
7
-
8
- # Project Management (Portfolio + Per-Project)
9
-
10
- `/project` is the primary work-tracking entrypoint for the template. It covers:
11
-
12
- - **Portfolio orientation** — bare `/project` invocation shows the landscape of active work and suggests next actions.
13
- - **Intent routing** — on an opening message that reads as "do work", decide new-vs-resume and route accordingly.
14
- - **New-work flow** — when the user starts multi-step work without a project, ask: create project, link existing, or standalone.
15
- - **CRUD surface** — projects, milestones, tasks, notes, checklists via `elevasis-sdk project:*`.
16
-
17
- > **Note:** the template no longer ships a separate `/work` skill. Project tasks are the unit of work; use `/project task:*` operations for task lifecycle. Resume context is canonical in `prj_tasks.resume_context` and written via `project:task:save` (never via task-doc frontmatter).
18
-
19
- **Usage:**
20
-
21
- - `/project` — **Portfolio orientation** (see "Orientation Mode" below). List active/blocked projects, surface most-recently-touched project's `resume_context`, suggest next actions.
22
- - `/project list` — Same as no-args orientation but just the project table, no suggestions.
23
- - `/project work <query>` — Fuzzy-match a project/task and print a resume brief (wraps `project:work`).
24
- - `/project status [<client>]` — Detailed status across all projects, or a single project.
25
- - `/project create` — Guided project creation (QnA walkthrough).
26
- - `/project add "Client Name" [--status active] [--value 5000]` — Quick create (flags only, no QnA).
27
- - `/project update <client> --status on_track` — Update project fields.
28
- - `/project milestone add <client> "Phase 1" [--due 2026-05-01]` — Add milestone.
29
- - `/project milestone update <client> "Phase 1" --status completed` — Update milestone.
30
- - `/project task add <client> "API docs" --milestone "Phase 1" [--type documentation]` — Add task.
31
- - `/project task update <client> "API docs" --status approved` — Update task.
32
- - `/project task save <task> --current-state "..."` — Persist `resume_context` (use this, never task-doc frontmatter).
33
- - `/project note <client> "Kickoff call went well" [--type call_note]` — Add note.
34
- - `/project notes <client>` — List notes for project.
35
- - `/project checklist <client> "<milestone>"` — View checklist for a milestone.
36
- - `/project delete <client>` — Delete project (cascades milestones, tasks, notes).
37
-
38
- ---
39
-
40
- ## Ambient Vibe Integration
41
-
42
- This skill is the landing point for three of the seven vibe intent types. Agents arriving from the ambient layer should behave identically to a direct invocation — vibe is a classifier, not a different code path.
43
-
44
- | Vibe intent | What vibe detected | What to do here |
45
- | -------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------- |
46
- | **Capture** | "add a task", "remember to", "track this" | Draft the task/note, confirm with user, then `project:task:create` or `project:note:create` |
47
- | **Transition** | "done", "stuck", "blocked", "finished" | Resolve current task from session context, confirm status, then `project:task:update --status <new>` |
48
- | **Navigate** | "focus on", "switch to", "back to" | Resolve target via `project:resolve <query>` or `project:work <query>`, update scope, narrate new context |
49
-
50
- **The full continuity loop** — how work flows across sessions:
51
-
52
- ```
53
- vibe (Capture) → /project task:create → task in DB
54
-
55
- work happens
56
-
57
- /save → project:task:save → prj_tasks.resume_context
58
-
59
- next session: /project work <query>
60
-
61
- project:work → resume brief (current state + next steps)
62
-
63
- agent picks up exactly where it left off
64
- ```
65
-
66
- When a session begins with an ambiguous opening, check `project:work` before asking the user to re-explain. The resume brief is the canonical continuity payload — trust it.
67
-
68
- ---
69
-
70
- ## Prerequisites
71
-
72
- **Run from the project root** (the directory containing `.elevasis`). Before issuing any other commands, run:
73
-
74
- ```bash
75
- pnpm elevasis-sdk doctor
76
- ```
77
-
78
- If `doctor` fails, **stop immediately and surface the error** — do not retry other commands. Fix the reported issue first (missing `.env`, bad API key, wrong directory, etc.), then re-run `doctor` before proceeding.
79
-
80
- ---
81
-
82
- ## Invocation Contract
83
-
84
- All `elevasis-sdk` commands in this skill use the wrapper script form:
85
-
86
- ```bash
87
- pnpm elevasis-sdk <subcommand> [flags]
88
- ```
89
-
90
- This form is available from the **project root** — the directory that contains the `.elevasis` marker file. The `.elevasis` file is the project-root anchor: `elevasis-sdk` walks up from its invocation directory until it finds this file, then resolves `.env` and all relative paths from that location. After the project-root refactor, CWD within the project tree matters less — but you must still be somewhere inside the project directory tree.
91
-
92
- The long form `pnpm -C operations exec elevasis-sdk <subcommand>` still works and is equivalent. Use the short wrapper form (`pnpm elevasis-sdk`) in all new automation and agent scripts.
93
-
94
- **If the `.elevasis` marker is missing**, the CLI exits with:
95
-
96
- ```
97
- Not inside an Elevasis project. Run this command from a project directory (an Elevasis project has a .elevasis file at its root).
98
- ```
99
-
100
- If you see this error, confirm you are inside a project that was created from the template and has the `.elevasis` marker at its root.
101
-
102
- ---
103
-
104
- ## Transient Input Files (`tmp/`)
105
-
106
- Agents that need to pass structured JSON to `request:submit` or `exec` should write the file to `<projectRoot>/tmp/` rather than to an arbitrary path. The `tmp/` directory is tracked in git (via `tmp/.gitkeep`) but its contents are gitignored, so transient files never pollute the commit history.
107
-
108
- **Workflow:**
109
-
110
- 1. Write the input payload to `tmp/<descriptive-name>.json` from the project root.
111
- 2. Pass the path to the CLI command using the relative form — it resolves against the project root automatically:
112
-
113
- ```bash
114
- pnpm elevasis-sdk request:submit -f tmp/request-report.json --cleanup-input
115
- pnpm elevasis-sdk exec my-workflow -f tmp/exec-payload.json --cleanup-input
116
- ```
117
-
118
- 3. The `--cleanup-input` flag deletes the file automatically after a successful command. On failure the file is left intact for inspection.
119
-
120
- **Safety guard:** `--cleanup-input` only deletes files that are under `<projectRoot>/tmp/`. If the resolved path is outside `tmp/`, the CLI prints a warning to stderr and leaves the file untouched. Files passed via `--input <json>` (inline JSON, no file) are never affected.
121
-
122
- ---
123
-
124
- ## Orientation Mode (bare `/project` invocation)
125
-
126
- When invoked without a subcommand, enter **project mode**: present the portfolio landscape and offer next actions. This is the hub — distinct from `project:work <id>`, which is single-task resume.
127
-
128
- ### Flow
129
-
130
- 1. **List active work** — `--status` accepts exactly one value. Run two calls and merge the results:
131
-
132
- ```bash
133
- pnpm elevasis-sdk project:list --status active --pretty
134
- pnpm elevasis-sdk project:list --status blocked --pretty
135
- ```
136
-
137
- Show a compact table: client, status, last-touched.
138
-
139
- 2. **Surface most-recently-touched project's resume context** — identify the top row (most recent `updated_at`), then print its latest task's `resume_context` (via `project:task:list` → pick most recent in-progress task → `project:task:resume <id> --pretty`). Keep it short: current state + next steps, no files dump.
140
-
141
- 3. **List available operations** — a one-liner reminder of what the user can do from here:
142
-
143
- ```
144
- Available: /project status <client> | /project work <query> | /project task add | /project note | /project create
145
- ```
146
-
147
- 4. **Offer next-action suggestions** — 1-3 concrete follow-ups driven by what's on screen. Examples:
148
- - "3 active projects, 1 blocked (Acme — waiting on credentials). Resume Beta (most recent)?"
149
- - "No active projects. Create one with `/project create`?"
150
- - "Acme's Phase 2 has 2 overdue tasks. Open status?"
151
-
152
- ### Presentation template
153
-
154
- ```
155
- Project Portfolio (2 active, 1 blocked)
156
- =======================================
157
- | Client | Status | Last touched |
158
- |--------------|----------|--------------|
159
- | Beta LLC | on_track | 2h ago |
160
- | Acme Corp | blocked | 1d ago |
161
- | Gamma Inc | active | 4d ago |
162
-
163
- Most recent: Beta LLC — API integration
164
- Current: Endpoint wired, tests passing locally.
165
- Next: Deploy to staging, verify webhook delivery.
166
-
167
- Next? Resume Beta | Status Acme (why blocked?) | Create new project
168
- ```
169
-
170
- ---
171
-
172
- ## Intent Detection (opening-message routing)
173
-
174
- Apply this heuristic on the user's first substantive message of a session, before branching into any other operation. The goal: distinguish **resume existing work** from **start new work**, and confirm before committing.
175
-
176
- ### Signals → classification
177
-
178
- | Signal in opening message | Classify as |
179
- | ----------------------------------------------------------------------------------------------- | ---------------------------------- |
180
- | References a specific file path, PR, commit, or existing task/milestone/project by name | **Resume** |
181
- | "Continue", "pick up", "finish", "the thing we were doing", "yesterday's work" | **Resume** |
182
- | "Let's build X", "new Y", "I want to add Z", "start a new project", "help me with a new client" | **New work** |
183
- | Single-file cosmetic ask: "fix typo in X", "rename Y", "update dep Z" | **Trivial** (skip new-work prompt) |
184
- | Ambiguous / discussion only | Ask before routing |
185
-
186
- ### Routing
187
-
188
- - **Resume** — run `project:work <query>` with the extracted entity (file path stem, project name, task keywords). If one clean match, print the brief and ask "Continue here?". If multiple matches, show top 3 and ask which. If zero matches, fall back to orientation mode with a note ("No match for 'acme webhook'; here's the landscape").
189
- - **New work** — enter the **New-Work Project Decision** flow (next section).
190
- - **Trivial** — proceed directly; do not prompt about projects. Small single-file edits don't need portfolio tracking.
191
- - **Ambiguous** — reply with orientation mode output and ask "Resume something, or start new?"
192
-
193
- **Always confirm before branching.** A misclassified "new" routed as "resume" burns trust faster than a one-line confirmation.
194
-
195
- ---
196
-
197
- ## New-Work Project Decision
198
-
199
- When intent detection classifies the opening ask as **new multi-step work** and the conversation has no established project context, ask this question before writing any code:
200
-
201
- ```
202
- Scope this work under a project?
203
- a) Create new project "<proposed-slug>" (recommended for multi-step work)
204
- b) Link to existing "<closest-match>" (if fuzzy-search finds one)
205
- c) Standalone — no project tracking (one-off / trivial)
206
- ```
207
-
208
- ### Rules
209
-
210
- - **Default to (a)** when the ask is multi-step (multiple files, multiple sessions expected, client-facing deliverable).
211
- - **Offer (b)** only when `project:list` fuzzy-matches produce a plausible candidate (name similarity, shared entity references). If nothing matches, omit the option — do not invent one.
212
- - **Skip the prompt entirely** for trivial single-file edits — intent detection's "Trivial" branch handles this.
213
- - **Propose a slug** by extracting the core noun from the ask. "Build a lead scraper for Acme" → `acme-lead-scraper`. User can override.
214
-
215
- ### After the decision
216
-
217
- - **(a) Create** — invoke the guided `/project create` QnA flow (see below), pre-filling the name/description from the ask.
218
- - **(b) Link** — run `project:resolve <query>` to confirm the match, then add a task to that project for the new work via `project:task:create`.
219
- - **(c) Standalone** — proceed without project tracking. Do not create orphan task docs; standalone work leaves no artifact.
220
-
221
- ---
222
-
223
- ## Connection
224
-
225
- ### Environment
226
-
227
- ```bash
228
- # .env (project root)
229
- ELEVASIS_PLATFORM_KEY=sk_...
230
- ```
231
-
232
- `ELEVASIS_PLATFORM_KEY` is read from the environment automatically. The SDK CLI walks up
233
- directories to find `.env`, so it resolves correctly from both the project root and
234
- `operations/`. `ELEVASIS_API_URL` defaults to `https://api.elevasis.io` (production). Set
235
- `ELEVASIS_API_URL=http://localhost:5170` to target a local API instance, or set
236
- `NODE_ENV=development` if the SDK CLI respects that convention.
237
-
238
- No `--prod` flag is needed — the template targets production by default via `ELEVASIS_API_URL`.
239
-
240
- ### CLI Invocation
241
-
242
- All project operations go through the `elevasis-sdk` CLI. Use the wrapper script form from the
243
- project root:
244
-
245
- ```bash
246
- pnpm elevasis-sdk project:<command> [args]
247
- ```
248
-
249
- The long form `pnpm -C operations exec elevasis-sdk project:<command> [args]` is equivalent and
250
- still works — use it if the wrapper script is not yet available in an older project.
251
-
252
- Organization scoping is handled server-side via `ELEVASIS_PLATFORM_KEY`. No org header or
253
- `--org` flag is required.
254
-
255
- ---
256
-
257
- ## Database Schema
258
-
259
- ### Tables
260
-
261
- | Table | Purpose | Key Columns |
262
- | ---------------- | ---------------------------- | --------------------------------------------------------------------- |
263
- | `prj_projects` | Client projects/contracts | name, kind, status, description, contract_value, start/end dates |
264
- | `prj_milestones` | Phases within projects | name, status, due_date, sequence, completed_at, checklist |
265
- | `prj_tasks` | Work items within milestones | name, status, type, due_date, milestone_id, resume_context, checklist |
266
- | `prj_notes` | Meeting notes, updates | type, content, summary, occurred_at, task_id, milestone_id |
267
-
268
- ### Project Kind
269
-
270
- | Kind | Description |
271
- | ------------------- | -------------------------------- |
272
- | `client_engagement` | External client delivery project |
273
- | `internal` | Internal project work |
274
- | `research` | Research and investigation |
275
- | `other` | Catch-all |
276
-
277
- Default filter for `/project` operations is `kind=client_engagement` unless the user's intent
278
- is clearly internal or `--kind` is specified explicitly.
279
-
280
- ### Status Values
281
-
282
- | Entity | Values |
283
- | --------- | ------------------------------------------------------------------------------------------------------------------------ |
284
- | Project | `active`, `on_track`, `at_risk`, `blocked`, `completed`, `paused` |
285
- | Milestone | `upcoming`, `in_progress`, `completed`, `overdue`, `blocked` |
286
- | Task | `planned`, `in_progress`, `blocked`, `completed`, `cancelled`, `submitted`, `approved`, `rejected`, `revision_requested` |
287
- | Note type | `call_note`, `status_update`, `issue`, `blocker`, `agent_learning` |
288
- | Task type | `documentation`, `code`, `report`, `design`, `refactor`, `feature`, `bug`, `research`, `other` |
289
-
290
- ### Checklist Shape
291
-
292
- Both `prj_milestones.checklist` and `prj_tasks.checklist` store a JSONB array of objects with this shape:
293
-
294
- ```json
295
- [
296
- { "id": "uuid", "label": "Item label", "completed": false },
297
- { "id": "uuid", "label": "Another item", "completed": true }
298
- ]
299
- ```
300
-
301
- The CLI `--checklist` flag on both `project:milestone:update` and `project:task:update` performs a **full replace** — the entire array is replaced with the JSON you supply. There are no item-level add/toggle/remove flags. To mutate a single item:
302
-
303
- 1. Read the current checklist (via psql or `project:task:get` / `project:milestone:list`)
304
- 2. Mutate the array in memory (append, flip `completed`, filter out)
305
- 3. Write the entire array back via `--checklist '<json>'`
306
-
307
- To clear a checklist: `--checklist '[]'`.
308
-
309
- ### Foreign Keys
310
-
311
- - `prj_milestones.project_id` → `prj_projects.id` (CASCADE)
312
- - `prj_tasks.project_id` → `prj_projects.id` (CASCADE)
313
- - `prj_tasks.milestone_id` → `prj_milestones.id` (SET NULL)
314
- - `prj_tasks.parent_task_id` → `prj_tasks.id` (SET NULL) — for subtasks
315
- - `prj_notes.project_id` → `prj_projects.id` (CASCADE)
316
- - `prj_notes.task_id` → `prj_tasks.id` (SET NULL)
317
- - `prj_notes.milestone_id` → `prj_milestones.id` (SET NULL)
318
- - `prj_projects.deal_id` → `acq_deals.id` (SET NULL)
319
- - `prj_projects.client_id` → `clients.id` (SET NULL) — clients-hub link; set via `--client`
320
- - `prj_projects.client_company_id` → `acq_companies.id` (SET NULL) — legacy direct-company link; set via `--client-company-id`
321
-
322
- ---
323
-
324
- ## Operations
325
-
326
- ### `list` — Portfolio Table (no orientation extras)
327
-
328
- For a plain project table without resume context or next-action suggestions (use the bare `/project` orientation mode above when the user wants the full hub experience):
329
-
330
- ```bash
331
- pnpm elevasis-sdk project:list --kind client_engagement --pretty
332
-
333
- # Filter by client (accepts the client's name or UUID)
334
- pnpm elevasis-sdk project:list --client "Acme" --pretty
335
- ```
336
-
337
- Present as:
338
-
339
- ```
340
- Project Portfolio
341
- =================
342
- | Client | Status | Kind | Value |
343
- |--------------------|----------|--------------------|---------|
344
- | Acme Corp | on_track | client_engagement | $5,000 |
345
- | Beta LLC | active | client_engagement | $3,500 |
346
- ```
347
-
348
- For progress detail (milestones/tasks), follow up with `project:milestone:list` and
349
- `project:task:list` per project.
350
-
351
- ### `work <query>` — Resume Brief
352
-
353
- Wraps `project:work` for fuzzy-matched resume. Used by intent-detection "resume" classification.
354
-
355
- ```bash
356
- pnpm elevasis-sdk project:work <query>
357
- ```
358
-
359
- Prints the matched project/task brief with current `resume_context`. If multiple matches, show top 3 and ask which.
360
-
361
- ### `status [<client>]` — Detailed Status
362
-
363
- If client specified, show full detail for that project. If not, show all.
364
-
365
- **Resolve the project first** (see Client Inference below), then:
366
-
367
- ```bash
368
- # Get project details
369
- pnpm elevasis-sdk project:get <project-id>
370
-
371
- # Milestones (ordered by sequence)
372
- pnpm elevasis-sdk project:milestone:list --project <project-id> --pretty
373
-
374
- # Tasks (grouped by milestone)
375
- pnpm elevasis-sdk project:task:list --project <project-id> --pretty
376
-
377
- # Recent notes
378
- pnpm elevasis-sdk project:note:list --project <project-id> --pretty
379
- ```
380
-
381
- Present as:
382
-
383
- ```
384
- Acme Corp — On Track
385
- =====================
386
- Description: Automation project for AI-powered workflows
387
- Contract: $5,000 | Started: 2026-04-01 | Target: 2026-06-01
388
-
389
- Milestones (2/4 complete)
390
- [x] Phase 1: Discovery (completed)
391
- [>] Phase 2: Implementation (in_progress, due 2026-05-01)
392
- [ ] Phase 3: Testing (upcoming, due 2026-05-15)
393
- [ ] Phase 4: Launch (upcoming, due 2026-06-01)
394
-
395
- Tasks (3/8 approved)
396
- Phase 2: Implementation
397
- [approved] Requirements doc (documentation)
398
- [in_progress] API integration (code, due 2026-05-01)
399
- [planned] User guide (documentation)
400
-
401
- Recent Notes
402
- 2026-04-05 [call_note] Weekly check-in — on track, API work started
403
- 2026-04-01 [call_note] Kickoff — confirmed scope and timeline
404
- ```
405
-
406
- #### Tenant Skill Bindings Footer
407
-
408
- After the normal status output, append a short footer when the project kind, tags, name,
409
- description, milestones, or active tasks map to a known tenant domain such as `prospecting`,
410
- `crm`, `outreach`, `finance`, or `support`.
411
-
412
- Resolve the best domain from explicit metadata first (`tags`, `kind`, domain fields), then from
413
- project/task text. If no clear domain maps, omit the footer.
414
-
415
- Footer shape:
416
-
417
- ```text
418
- Related skill bindings
419
- - Domain: <domain>
420
- - Read or change business profile: /knowledge <domain>
421
- - Layering primer: node_modules/@elevasis/sdk/reference/spine/spine-primer.md
422
- ```
423
-
424
- External projects do not expose monorepo-only architecture commands. Never suggest `/org-os`,
425
- `/om-spine`, or monorepo paths in this footer.
426
-
427
- ### `create` — Guided Project Creation (QnA Flow)
428
-
429
- Interactive walkthrough that collects project details step by step using `AskUserQuestion`,
430
- then creates the project, milestones, and kickoff note in one go.
431
-
432
- **Step 1: Project basics**
433
-
434
- Ask via `AskUserQuestion` (single question, free-text via "Other"):
435
-
436
- ```
437
- What's the project name and a brief description of the scope?
438
- ```
439
-
440
- The user will reply with something like "Acme Corp automation -- building a lead gen pipeline
441
- with email outreach". Parse the name and description from the response.
442
-
443
- **Step 2: Deal/company linking**
444
-
445
- Search for matching records using read-only psql (the psql role is SELECT-only).
446
-
447
- Note: `SUPABASE_READONLY_URL` must be set in the template `.env` for psql queries. If not
448
- set, skip this step and advise the user to link via the Command Center UI after creation.
449
-
450
- If `SUPABASE_READONLY_URL` is available, query using `<ORG_ID>` from the platform key's
451
- associated organization (you can retrieve it via `project:list` output which includes
452
- `organization_id`):
453
-
454
- ```bash
455
- source .env && psql "$SUPABASE_READONLY_URL" -c \
456
- "SELECT id, name, domain FROM acq_companies WHERE organization_id = '<ORG_ID>' AND name ILIKE '%<parsed_name>%' ORDER BY updated_at DESC LIMIT 5;"
457
-
458
- source .env && psql "$SUPABASE_READONLY_URL" -c \
459
- "SELECT d.id, d.contact_email, d.cached_stage, c.name AS company_name
460
- FROM acq_deals d
461
- LEFT JOIN acq_contacts ct ON ct.id = d.contact_id
462
- LEFT JOIN acq_companies c ON c.id = ct.company_id
463
- WHERE d.organization_id = '<ORG_ID>'
464
- AND (d.contact_email ILIKE '%<parsed_name>%' OR c.name ILIKE '%<parsed_name>%')
465
- ORDER BY d.updated_at DESC LIMIT 5;"
466
- ```
467
-
468
- If matches found, ask via `AskUserQuestion`:
469
-
470
- ```
471
- Found matching records. Link this project?
472
- - [Company: Acme Corp (acme.com)]
473
- - [Deal: john@acme.com (closed_won)]
474
- - No linking
475
- ```
476
-
477
- If no matches, skip silently.
478
-
479
- **Step 3: Contract details**
480
-
481
- Ask via `AskUserQuestion`:
482
-
483
- ```
484
- Contract details?
485
- - Options with previews showing common structures:
486
- - "Fixed project" -- one-time fee
487
- - "Monthly retainer" -- recurring
488
- - "Hourly" -- time & materials
489
- - Other
490
- ```
491
-
492
- Then follow up for the value, start date, and target end date. These can be collected in a
493
- single question:
494
-
495
- ```
496
- What's the contract value, start date, and target end date?
497
- (e.g., "$5000, starting today, targeting June 1")
498
- ```
499
-
500
- Parse natural language dates ("today", "next Monday", "June 1") into ISO format.
501
-
502
- **Step 4: Milestone suggestions**
503
-
504
- Based on the project description from Step 1, suggest 3-5 milestones that fit the scope. Use
505
- your understanding of the project to propose relevant phases.
506
-
507
- **Guidelines for milestone suggestions:**
508
-
509
- - For automation/integration projects: Discovery, Implementation, Testing, Launch
510
- - For content/marketing projects: Strategy, Content Creation, Review, Distribution
511
- - For consulting/advisory: Assessment, Recommendations, Implementation Support
512
- - Always include a Discovery/Kickoff phase first and a Launch/Handoff phase last
513
- - Suggest due dates spaced evenly between start and target end date
514
-
515
- Present via `AskUserQuestion` with previews:
516
-
517
- ```
518
- Suggested milestones for "<project name>". Select which to include:
519
- (multiSelect: true)
520
-
521
- - Discovery (due <start + 2 weeks>)
522
- - Implementation (due <midpoint>)
523
- - Testing (due <end - 2 weeks>)
524
- - Launch (due <end date>)
525
- ```
526
-
527
- The user can select which ones to keep, modify via "Other", or skip entirely.
528
-
529
- **Step 5: Kickoff note**
530
-
531
- Ask via `AskUserQuestion`:
532
-
533
- ```
534
- Add a kickoff note? This captures initial context (scope confirmed, key contacts, etc.)
535
- - Yes, let me type one
536
- - Skip for now
537
- ```
538
-
539
- If yes, collect the note content.
540
-
541
- **Step 6: Execute**
542
-
543
- Create everything in sequence using the CLI:
544
-
545
- ```bash
546
- # 1. Create project
547
- # If the user mentioned a client by name, resolve it first:
548
- # pnpm elevasis-sdk client:resolve "<client name>"
549
- # Then pass the name (or UUID) via --client
550
- pnpm elevasis-sdk project:create \
551
- --name "<name>" \
552
- --kind client_engagement \
553
- --status active \
554
- --description "<desc>" \
555
- --client "<client-name-or-uuid>"
556
-
557
- # 2. Create milestones
558
- pnpm elevasis-sdk project:milestone:create \
559
- --project <project-id> \
560
- --name "<milestone-name>" \
561
- --status upcoming \
562
- --due-date <date>
563
-
564
- # 3. Create kickoff note (if provided)
565
- pnpm elevasis-sdk project:note:create \
566
- --project <project-id> \
567
- --content "<note-content>" \
568
- --type call_note
569
- ```
570
-
571
- If `--company` or `--deal` linking was confirmed, use `project:update` after creation:
572
-
573
- ```bash
574
- pnpm elevasis-sdk project:update <project-id> --client "<client-name-or-uuid>"
575
- ```
576
-
577
- To remove a client link later:
578
-
579
- ```bash
580
- pnpm elevasis-sdk project:update <project-id> --clear-client
581
- ```
582
-
583
- Do NOT pass `--client ""` to clear — an empty string is rejected as ambiguous. Use `--clear-client` instead.
584
-
585
- **Step 7: Summary**
586
-
587
- Show the created project:
588
-
589
- ```
590
- Project Created
591
- ===============
592
- Name: Acme Corp Automation
593
- Kind: client_engagement
594
- Status: active
595
- Timeline: 2026-04-07 → 2026-06-01
596
-
597
- Milestones:
598
- 1. Discovery (upcoming, due 2026-04-21)
599
- 2. Implementation (upcoming, due 2026-05-08)
600
- 3. Testing (upcoming, due 2026-05-22)
601
- 4. Launch (upcoming, due 2026-06-01)
602
-
603
- Kickoff Note: Confirmed scope -- lead gen pipeline with email outreach.
604
-
605
- Next: /project status acme
606
- ```
607
-
608
- ---
609
-
610
- ### `add "<name>" [options]` — Quick Create Project (No QnA)
611
-
612
- **Options:**
613
-
614
- - `--status <status>` (default: `active`)
615
- - `--value <number>` — contract value (informational, stored in description or notes)
616
- - `--start <date>` — start date
617
- - `--end <date>` — target end date
618
- - `--description "<text>"` — scope description
619
- - `--kind <kind>` (default: `client_engagement`)
620
-
621
- ```bash
622
- pnpm elevasis-sdk project:create \
623
- --name "<name>" \
624
- --kind client_engagement \
625
- --status active \
626
- --description "<desc>"
627
- ```
628
-
629
- **After creating:** Show the created project and suggest next steps (add milestones).
630
-
631
- ### `update <client> [options]` — Update Project
632
-
633
- **Options:** `--status`, `--name`, `--description`, `--client <id-or-name>`, `--clear-client`, `--client-company-id <uuid>`
634
-
635
- First resolve the client (see Client Inference below), then:
636
-
637
- ```bash
638
- pnpm elevasis-sdk project:update <project-id> --status <status>
639
- ```
640
-
641
- ### `milestone add <client> "<name>" [options]` — Add Milestone
642
-
643
- **Options:**
644
-
645
- - `--status <status>` (default: `upcoming`)
646
- - `--due <date>` — due date (ISO)
647
- - `--description "<text>"`
648
-
649
- ```bash
650
- pnpm elevasis-sdk project:milestone:create \
651
- --project <project-id> \
652
- --name "<name>" \
653
- --status upcoming \
654
- --due-date <date>
655
- ```
656
-
657
- ### `milestone update <client> "<milestone>" [options]` — Update Milestone
658
-
659
- Resolve milestone by name (see Client Inference for project resolution, then use
660
- `project:milestone:list` to find the milestone ID by partial name match):
661
-
662
- ```bash
663
- # List milestones to find ID
664
- pnpm elevasis-sdk project:milestone:list --project <project-id>
665
-
666
- # Update status
667
- pnpm elevasis-sdk project:milestone:update <milestone-id> --status completed
668
-
669
- # Update checklist (full replace)
670
- pnpm elevasis-sdk project:milestone:update <milestone-id> \
671
- --checklist '[{"id":"uuid","label":"Item label","completed":false}]'
672
- ```
673
-
674
- If status changes to `completed`, the API auto-sets `completed_at`.
675
-
676
- **Options:** `--status`, `--due-date`, `--name`, `--checklist <json>`
677
-
678
- **Checklist mutations:** The CLI performs a full replace. To add, toggle, or remove an item:
679
-
680
- 1. Read the current checklist via psql or `project:milestone:list`
681
- 2. Mutate the array in memory
682
- 3. Write back the full array via `--checklist '<json>'`
683
-
684
- ### `checklist <client> "<milestone>"` — View Checklist
685
-
686
- Resolve the milestone, then read its `checklist` JSONB column via read-only psql.
687
-
688
- Note: Requires `SUPABASE_READONLY_URL` in the template `.env`. If not set, advise the user
689
- to view the checklist via the Command Center UI, or add `SUPABASE_READONLY_URL` to `.env`.
690
-
691
- ```bash
692
- source .env && psql "$SUPABASE_READONLY_URL" -c \
693
- "SELECT m.name, m.checklist
694
- FROM prj_milestones m
695
- WHERE m.project_id = '<project_id>' AND m.name ILIKE '%<name>%'
696
- LIMIT 1;"
697
- ```
698
-
699
- The `checklist` column is a JSONB array of objects:
700
-
701
- ```json
702
- [
703
- { "id": "uuid", "label": "Set up dev environment", "completed": true },
704
- { "id": "uuid", "label": "Draft scope document", "completed": false }
705
- ]
706
- ```
707
-
708
- Present as:
709
-
710
- ```
711
- Onboarding & Scope — Checklist (2/3 complete)
712
- [x] Set up dev environment
713
- [x] Schedule kickoff call
714
- [ ] Draft scope document
715
- ```
716
-
717
- ### `task add <client> "<name>" [options]` — Add Task
718
-
719
- **Options:**
720
-
721
- - `--milestone "<name>"` — link to a milestone (resolve by name first)
722
- - `--status <status>` (default: `planned`)
723
- - `--type <type>` (default: `other`)
724
- - `--due <date>`
725
- - `--description "<text>"`
726
- - `--checklist <json>` — initial checklist items
727
-
728
- ```bash
729
- # Resolve milestone ID first if --milestone provided
730
- pnpm elevasis-sdk project:milestone:list --project <project-id>
731
-
732
- # Create task
733
- pnpm elevasis-sdk project:task:create \
734
- --project <project-id> \
735
- --title "<name>" \
736
- --status planned \
737
- --type <type> \
738
- --milestone <milestone-id>
739
-
740
- # Create task with initial checklist
741
- pnpm elevasis-sdk project:task:create \
742
- --project <project-id> \
743
- --title "<name>" \
744
- --checklist '[{"id":"1","label":"Step one","completed":false}]'
745
- ```
746
-
747
- ### `task update <client> "<task>" [options]` — Update Task
748
-
749
- Resolve task by name within the project using `project:task:list`, then update by positional ID:
750
-
751
- ```bash
752
- # Find the task ID
753
- pnpm elevasis-sdk project:task:list --project <project-id>
754
-
755
- # Update status (positional <id> comes first, then flags)
756
- pnpm elevasis-sdk project:task:update <task-id> --status <status>
757
-
758
- # Update checklist (full replace)
759
- pnpm elevasis-sdk project:task:update <task-id> \
760
- --checklist '[{"id":"uuid","label":"Step","completed":false}]'
761
-
762
- # Clear checklist
763
- pnpm elevasis-sdk project:task:update <task-id> --checklist '[]'
764
- ```
765
-
766
- **Options:** `--status`, `--title`, `--milestone`, `--description`, `--checklist <json>`
767
-
768
- **Important:** The task ID is a **positional argument** — it comes immediately after `project:task:update`, before any flags. There is no `--task-id` flag.
769
-
770
- If status changes to `approved`, the API auto-sets `completed_at`.
771
-
772
- **Checklist mutations:** Same full-replace semantics as milestones. To add, toggle, or remove an item:
773
-
774
- 1. Read the current task via `project:task:get <task-id>`
775
- 2. Mutate the checklist array in memory
776
- 3. Write back the full array via `project:task:update <task-id> --checklist '<json>'`
777
-
778
- ### `note <client> "<content>" [options]` — Add Note
779
-
780
- **Options:**
781
-
782
- - `--type <type>` (default: `call_note`) — see Note Type Selection below
783
- - `--task <task-id>` — attach to a specific task (UUID)
784
- - `--milestone <milestone-id>` — attach to a milestone (UUID)
785
-
786
- ```bash
787
- pnpm elevasis-sdk project:note:create \
788
- --project <project-id> \
789
- --content "<content>" \
790
- --type <type>
791
-
792
- # Attach to a task
793
- pnpm elevasis-sdk project:note:create \
794
- --project <project-id> \
795
- --content "<content>" \
796
- --type agent_learning \
797
- --task <task-id>
798
- ```
799
-
800
- ### `notes <client>` — List Notes
801
-
802
- ```bash
803
- pnpm elevasis-sdk project:note:list --project <project-id> --pretty
804
- ```
805
-
806
- ### `delete <client>` — Delete Project
807
-
808
- **ALWAYS confirm before deleting.** Show what will be deleted (milestone count, task count,
809
- note count) using the list commands, then ask for confirmation.
810
-
811
- ```bash
812
- pnpm elevasis-sdk project:delete <project-id>
813
- ```
814
-
815
- Cascade deletes all milestones, tasks, and notes.
816
-
817
- ---
818
-
819
- ## Notes Management
820
-
821
- Notes (`prj_notes`) are the durable record of project-scoped signals — conversation outcomes, blockers, status updates, and agent-captured learnings. They complement `resume_context` (which is task-scoped, temporal, and agent-facing) by providing a human-readable audit trail.
822
-
823
- ### When to Create a Note vs. Fall Through to Global Auto-Memory
824
-
825
- **Create a project note** when all of the following hold:
826
-
827
- - A specific project and (optionally) task is in scope in the current session
828
- - The content is project-bound — it would only matter to someone working on this project
829
- - The content is durable — it should be visible in future status views, not just the current session
830
-
831
- **Fall through to global auto-memory** when:
832
-
833
- - No active project or task can be resolved from session context (see Project-Scope Resolution below)
834
- - The content is cross-project or platform-wide (e.g., a general TypeScript pattern, a reusable workflow insight)
835
- - The signal is ephemeral and not worth a DB write (e.g., "reminder: run linter before committing")
836
-
837
- When in doubt and no project context is resolvable, global auto-memory is the safe default.
838
-
839
- ### Note Type Selection Guide
840
-
841
- | Type | Use when |
842
- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
843
- | `call_note` | Transcribing or summarizing a live client / stakeholder call. Include key decisions and action items. |
844
- | `status_update` | Milestone-level progress worth flagging to a human operator: phase complete, substantial deliverable, direction change. Don't emit one every session turn. |
845
- | `issue` | Bug, regression, unexpected failure, contract mismatch. Include reproduction context. |
846
- | `blocker` | Work cannot proceed without external action: decision needed, credential missing, upstream fix required. |
847
- | `agent_learning` | Project-bound knowledge the agent discovered: API quirks, client conventions, deploy gotchas, rate limits. Captures "things a future agent resuming this project should know" so it travels with the project instead of living only in global auto-memory. |
848
-
849
- ### Project-Scope Resolution
850
-
851
- Before writing a note, the agent resolves which project (and optionally task) to attach it to:
852
-
853
- 1. **Explicit in session** — a project/task UUID was mentioned or returned by a recent `project:work <id>` or `project:task:*` invocation in this session. Use it directly.
854
- 2. **Implicit from task list** — if step 1 yields nothing, run (`--status` accepts one value; run both calls and merge):
855
-
856
- ```bash
857
- pnpm elevasis-sdk project:list --status active --pretty
858
- pnpm elevasis-sdk project:list --status blocked --pretty
859
- pnpm elevasis-sdk project:task:list --project <top-project-id> --status in_progress
860
- ```
861
-
862
- Use the most-recently-touched project's in-progress task if there is exactly one. If multiple in-progress tasks exist, prefer the one that most closely matches the current conversation topic.
863
-
864
- 3. **Prompt the user** — if steps 1 and 2 are both ambiguous or empty, ask once: "Which project should I attach this note to? (project UUID or slug, or 'skip' to use global auto-memory)."
865
-
866
- 4. **Fall through** — if the user says "skip" or no resolution is possible, write to global auto-memory and continue.
867
-
868
- Resolution is **agent-side at call time** — there is no middleware or automatic routing layer. The agent infers context from the session.
869
-
870
- ### Agent Workflow: Capturing an `agent_learning`
871
-
872
- When the agent recognizes a project-scoped learning (an API quirk, an undocumented constraint, a client convention discovered during implementation):
873
-
874
- 1. Recognize the signal — e.g., "Apify actor X rate-limits at 20 rps", "client requires ISO dates, not timestamps", "staging env needs VPN before webhook delivery works"
875
- 2. Resolve active project and task from session context (see above)
876
- 3. Write the note:
877
-
878
- ```bash
879
- pnpm elevasis-sdk project:note:create \
880
- --project <project-id> \
881
- --type agent_learning \
882
- --task <task-id> \
883
- --content "<concise, factual statement of what was learned>"
884
- ```
885
-
886
- Omit `--task` if no specific task is in scope (e.g., the learning is milestone-level or project-level).
887
-
888
- 4. If no project can be resolved, write the learning to global auto-memory instead.
889
-
890
- **Content style for `agent_learning` notes:** write as a factual assertion another agent could act on — "Apify actor X rate-limits at 20 rps; add a 50ms delay between calls" rather than "I noticed that Apify seems slow." Keep it under 200 characters when possible.
891
-
892
- ---
893
-
894
- ## Status Transitions from Other Skills
895
-
896
- The `/project` skill is the canonical reference for how lifecycle signals from other skills map to `project:task:update` status transitions. The behavioral hooks live in the emitting skills; this section documents the contract.
897
-
898
- ### Transition Map
899
-
900
- | Signal | Source skill | Transition | CLI call |
901
- | ---------------------------------------- | -------------- | -------------------------------------- | -------------------------------------------------- |
902
- | Successful `git push` via `/deploy` | `deploy/` | linked task → `submitted` | `project:task:update <task-id> --status submitted` |
903
- | `/save` catches "I'm stuck" / blocker | `save/` | linked task → `blocked` + blocker note | `project:task:update <task-id> --status blocked` |
904
- | User/agent says "I'm done" / "task done" | `/project` NLM | active task → `completed` | `project:task:update <task-id> --status completed` |
905
-
906
- ### "I'm Done" Recognition (Natural Language Mode)
907
-
908
- When the user or agent says any of the following in the context of a project task, treat it as a `completed` transition signal:
909
-
910
- - "I'm done", "I'm finished", "done with this", "that's done"
911
- - "task complete", "task finished", "mark as complete"
912
- - "wrap this up", "close this task", "finalize this"
913
-
914
- Resolve the active task from session context (see Project-Scope Resolution), confirm once ("Mark `<task name>` as completed?"), then fire:
915
-
916
- ```bash
917
- pnpm elevasis-sdk project:task:update <task-id> --status completed
918
- ```
919
-
920
- If no task resolves, ask the user which task to complete rather than silently skipping.
921
-
922
- ### Transition Rules (All Skills)
923
-
924
- These rules apply to every skill that emits a transition:
925
-
926
- - **Explicit task ID only** — use the UUID resolved from session context. Never guess.
927
- - **Best-effort** — if the CLI returns non-2xx, emit a warning line and continue. Do not block the primary operation.
928
- - **Idempotent** — if the task is already in the target status, the update is a no-op. Fire it anyway.
929
- - **Confirm before `completed`** — always ask once before marking a task done, since this is hard to undo semantically.
930
-
931
- For full implementation details, see the emitting skills: `deploy/SKILL.md` (Step 8) and `save/SKILL.md` (Steps 5, 5a).
932
-
933
- ---
934
-
935
- ## Client Inference
936
-
937
- Resolve which project the user means using these rules (in priority order):
938
-
939
- 1. **Exact ID** — if the argument looks like a UUID, use it directly with `project:get`
940
- 2. **Name match** — list projects filtered by `client_engagement` kind, then match by name:
941
- ```bash
942
- pnpm elevasis-sdk project:list --kind client_engagement
943
- ```
944
- Filter results by partial name match against the user's input (case-insensitive).
945
- 3. **Single active project** — if only one non-completed `client_engagement` project exists,
946
- use it without asking
947
- 4. **Context from conversation** — if the conversation has been discussing a specific client,
948
- use that one
949
- 5. **Ambiguous** — if multiple match or none match, list projects and ask
950
-
951
- ---
952
-
953
- ## Natural Language Mode
954
-
955
- When args don't match any command pattern, infer intent from natural language:
956
-
957
- | User says | Inferred operation |
958
- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
959
- | "how's acme doing" | `status acme` |
960
- | "add a note: weekly call, everything on track" | `note <active-project> "weekly call, everything on track"` |
961
- | "mark phase 2 complete for acme" | `milestone update acme "phase 2" --status completed` |
962
- | "approve the API docs task" | `task update <project> "API docs" --status approved` |
963
- | "new client: Beta LLC, $3500/mo, starting may 1" | `add "Beta LLC" --value 3500 --start 2026-05-01` |
964
- | "what's overdue?" | List milestones where `due_date < now() AND status != 'completed'` |
965
- | "block acme, waiting on client credentials" | `update acme --status blocked` |
966
- | "show checklist for phase 2" | Read and display checklist for milestone "phase 2" via psql or `project:milestone:list` |
967
- | "add 'deploy staging' to task X's checklist" | Read task X's checklist → append `{id: uuid, label: "deploy staging", completed: false}` → `project:task:update <id> --checklist '<json>'` |
968
- | "mark 'deploy staging' done on task X" | Read task X's checklist → flip `completed` on matching item → `project:task:update <id> --checklist '<json>'` |
969
- | "clear the checklist on task X" | `project:task:update <task-id> --checklist '[]'` |
970
- | "add checklist item to onboarding milestone: review scope doc" | Read milestone checklist → append item → `project:milestone:update <id> --checklist '<json>'` |
971
- | "I'm done" / "task complete" / "done with this" | Resolve active task → confirm → `project:task:update <task-id> --status completed` |
972
- | "save: Apify actor X rate-limits at 20rps" | Resolve active project/task → `project:note:create --project <id> --type agent_learning --task <task-id> "Apify actor X rate-limits at 20rps"` |
973
- | "remember: client requires ISO dates, not timestamps" | Resolve active project/task → `project:note:create --project <id> --type agent_learning --task <task-id> "client requires ISO dates, not timestamps"` |
974
-
975
- **Checklist note:** all checklist mutations use the read-modify-write pattern. The CLI has no item-level flags (`--add-item`, `--toggle`, `--remove-item` do not exist). Always read the current state, mutate the array, then write the full array back.
976
-
977
- ---
978
-
979
- ## Bulk Operations
980
-
981
- ### Add Multiple Milestones
982
-
983
- For creating a standard milestone set, run `project:milestone:create` in sequence:
984
-
985
- ```bash
986
- for name in "Discovery" "Implementation" "Testing" "Launch"; do
987
- pnpm elevasis-sdk project:milestone:create \
988
- --project <project-id> \
989
- --name "$name" \
990
- --status upcoming
991
- done
992
- ```
993
-
994
- ### Overdue Report
995
-
996
- Query via read-only psql for milestones past their due date. Requires `SUPABASE_READONLY_URL`
997
- in the template `.env`. Replace `<ORG_ID>` with the organization ID associated with your
998
- `ELEVASIS_PLATFORM_KEY` (visible in `project:list` output as `organization_id`). If
999
- `SUPABASE_READONLY_URL` is not configured, use `project:milestone:list` per project and filter
1000
- manually.
1001
-
1002
- ```bash
1003
- source .env && psql "$SUPABASE_READONLY_URL" -c \
1004
- "SELECT
1005
- p.name AS project,
1006
- m.name AS milestone,
1007
- m.due_date,
1008
- m.status,
1009
- (CURRENT_DATE - m.due_date::date) AS days_overdue
1010
- FROM prj_milestones m
1011
- JOIN prj_projects p ON p.id = m.project_id
1012
- WHERE p.organization_id = '<ORG_ID>'
1013
- AND m.due_date < CURRENT_DATE
1014
- AND m.status NOT IN ('completed')
1015
- ORDER BY m.due_date;"
1016
- ```
1017
-
1018
- ---
1019
-
1020
- ## Client Linking
1021
-
1022
- Projects can be linked to a client in the clients hub. When a user mentions a client by name, resolve it to an ID and use `--client` to wire the project.
1023
-
1024
- ### Two linking flags, two different columns
1025
-
1026
- - `--client <name-or-uuid>` — links to a clients-hub record (`prj_projects.client_id`). Use this for new client linkage. Accepts a name (the CLI fuzzy-resolves it) or a UUID.
1027
- - `--client-company-id <uuid>` — legacy direct-company linkage (`prj_projects.client_company_id`); kept for back-compat. UUID only.
1028
-
1029
- These are independent. Do not use `--client-company-id` when you mean `--client`.
1030
-
1031
- ### When the user mentions a client by name
1032
-
1033
- 1. Resolve the client name to a UUID first (optional but gives an explicit confirmation step):
1034
- ```bash
1035
- pnpm elevasis-sdk client:resolve "Acme"
1036
- ```
1037
- 2. Pass the name or UUID to `--client` — the CLI fuzzy-resolves names automatically:
1038
- ```bash
1039
- pnpm elevasis-sdk project:create --name "Acme Automation" --kind client_engagement --client "Acme"
1040
- pnpm elevasis-sdk project:update <project-id> --client "Acme"
1041
- ```
1042
- 3. If the client name matches multiple records, the CLI returns an error listing candidates. In that case, run `client:resolve "Acme"` to disambiguate, then pass the UUID directly.
1043
-
1044
- ### Filtering projects by client
1045
-
1046
- ```bash
1047
- pnpm elevasis-sdk project:list --client "Acme" --pretty
1048
- ```
1049
-
1050
- ### Removing a client link
1051
-
1052
- ```bash
1053
- pnpm elevasis-sdk project:update <project-id> --clear-client
1054
- ```
1055
-
1056
- `--client` and `--clear-client` are mutually exclusive on the same command call.
1057
-
1058
- ### Soft recommendation for client engagement projects
1059
-
1060
- When the user creates a `client_engagement` project without mentioning a client, gently note that linking a client is recommended so the project appears in client lineage views. Do not block creation — the flag is optional.
1061
-
1062
- ### Client command reference
1063
-
1064
- The full `client:*` surface (list, get, status, resolve) is available via `elevasis-sdk client:*`. The `client:resolve` command mirrors `project:resolve` in shape and is the canonical tool for name-to-ID translation.
1065
-
1066
- ---
1067
-
1068
- ## Safety Rules
1069
-
1070
- 1. **Always confirm deletes** — show what will be cascade-deleted before executing
1071
- 2. **Organization scoping** — all API calls are automatically org-scoped via
1072
- `ELEVASIS_PLATFORM_KEY`; every direct psql query MUST include an `organization_id` filter
1073
- (use `<ORG_ID>` as a placeholder and confirm the value with the user if unknown)
1074
- 3. **Read before write** — when updating, show current state before applying changes
1075
- 4. **Validate status values** — reject invalid status strings before sending to the CLI
1076
- 5. **Kind default** — default to `--kind client_engagement` when user intent is client
1077
- delivery; use `--kind internal` for internal project work
1078
- 6. **Template project assumption** — the template is a single-organization project. All
1079
- `/project` operations operate within the organization scoped by `ELEVASIS_PLATFORM_KEY`.
1080
- There is no cross-org capability.
1081
- 7. **Checklist full-replace** — the `--checklist` flag replaces the entire array. Always read
1082
- the current checklist before writing to avoid losing existing items.
1083
- 8. **Confirm task completion** — always ask once before marking a task `completed` via "I'm done"
1084
- recognition. This status is semantically significant and warrants a confirmation step.
1085
-
1086
- ---
1087
-
1088
- **Last Updated:** 2026-05-08
1
+ ---
2
+ name: project
3
+ description: "Portfolio- and project-level work management -- orientation, intent routing, active projects, milestones, tasks, notes, and resume context -- via the elevasis-sdk project:* CLI."
4
+ argument-hint: "[create | list | work | add | update | status | note | milestone | task | delete] [args]"
5
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
6
+ ---
7
+
8
+ # Project Management (Portfolio + Per-Project)
9
+
10
+ `/project` is the primary work-tracking entrypoint for the template. It covers:
11
+
12
+ - **Portfolio orientation** — bare `/project` invocation shows the landscape of active work and suggests next actions.
13
+ - **Intent routing** — on an opening message that reads as "do work", decide new-vs-resume and route accordingly.
14
+ - **New-work flow** — when the user starts multi-step work without a project, ask: create project, link existing, or standalone.
15
+ - **CRUD surface** — projects, milestones, tasks, notes, checklists via `elevasis-sdk project:*`.
16
+
17
+ > **Note:** the template no longer ships a separate `/work` skill. Project tasks are the unit of work; use `/project task:*` operations for task lifecycle. Resume context is canonical in `prj_tasks.resume_context` and written via `project:task:save` (never via task-doc frontmatter).
18
+
19
+ **Usage:**
20
+
21
+ - `/project` — **Portfolio orientation** (see "Orientation Mode" below). List active/blocked projects, surface most-recently-touched project's `resume_context`, suggest next actions.
22
+ - `/project list` — Same as no-args orientation but just the project table, no suggestions.
23
+ - `/project work <query>` — Fuzzy-match a project/task and print a resume brief (wraps `project:work`).
24
+ - `/project status [<client>]` — Detailed status across all projects, or a single project.
25
+ - `/project create` — Guided project creation (QnA walkthrough).
26
+ - `/project add "Client Name" [--status active] [--value 5000]` — Quick create (flags only, no QnA).
27
+ - `/project update <client> --status on_track` — Update project fields.
28
+ - `/project milestone add <client> "Phase 1" [--due 2026-05-01]` — Add milestone.
29
+ - `/project milestone update <client> "Phase 1" --status completed` — Update milestone.
30
+ - `/project task add <client> "API docs" --milestone "Phase 1" [--type documentation]` — Add task.
31
+ - `/project task update <client> "API docs" --status approved` — Update task.
32
+ - `/project task save <task> --current-state "..."` — Persist `resume_context` (use this, never task-doc frontmatter).
33
+ - `/project note <client> "Kickoff call went well" [--type call_note]` — Add note.
34
+ - `/project notes <client>` — List notes for project.
35
+ - `/project checklist <client> "<milestone>"` — View checklist for a milestone.
36
+ - `/project delete <client>` — Delete project (cascades milestones, tasks, notes).
37
+
38
+ ---
39
+
40
+ ## Ambient Vibe Integration
41
+
42
+ This skill is the landing point for three of the seven vibe intent types. Agents arriving from the ambient layer should behave identically to a direct invocation — vibe is a classifier, not a different code path.
43
+
44
+ | Vibe intent | What vibe detected | What to do here |
45
+ | -------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------- |
46
+ | **Capture** | "add a task", "remember to", "track this" | Draft the task/note, confirm with user, then `project:task:create` or `project:note:create` |
47
+ | **Transition** | "done", "stuck", "blocked", "finished" | Resolve current task from session context, confirm status, then `project:task:update --status <new>` |
48
+ | **Navigate** | "focus on", "switch to", "back to" | Resolve target via `project:resolve <query>` or `project:work <query>`, update scope, narrate new context |
49
+
50
+ **The full continuity loop** — how work flows across sessions:
51
+
52
+ ```
53
+ vibe (Capture) → /project task:create → task in DB
54
+
55
+ work happens
56
+
57
+ /save → project:task:save → prj_tasks.resume_context
58
+
59
+ next session: /project work <query>
60
+
61
+ project:work → resume brief (current state + next steps)
62
+
63
+ agent picks up exactly where it left off
64
+ ```
65
+
66
+ When a session begins with an ambiguous opening, check `project:work` before asking the user to re-explain. The resume brief is the canonical continuity payload — trust it.
67
+
68
+ ---
69
+
70
+ ## Prerequisites
71
+
72
+ **Run from the project root** (the directory containing `.elevasis`). Before issuing any other commands, run:
73
+
74
+ ```bash
75
+ pnpm elevasis-sdk doctor
76
+ ```
77
+
78
+ If `doctor` fails, **stop immediately and surface the error** — do not retry other commands. Fix the reported issue first (missing `.env`, bad API key, wrong directory, etc.), then re-run `doctor` before proceeding.
79
+
80
+ ---
81
+
82
+ ## Invocation Contract
83
+
84
+ All `elevasis-sdk` commands in this skill use the wrapper script form:
85
+
86
+ ```bash
87
+ pnpm elevasis-sdk <subcommand> [flags]
88
+ ```
89
+
90
+ This form is available from the **project root** — the directory that contains the `.elevasis` marker file. The `.elevasis` file is the project-root anchor: `elevasis-sdk` walks up from its invocation directory until it finds this file, then resolves `.env` and all relative paths from that location. After the project-root refactor, CWD within the project tree matters less — but you must still be somewhere inside the project directory tree.
91
+
92
+ The long form `pnpm -C operations exec elevasis-sdk <subcommand>` still works and is equivalent. Use the short wrapper form (`pnpm elevasis-sdk`) in all new automation and agent scripts.
93
+
94
+ **If the `.elevasis` marker is missing**, the CLI exits with:
95
+
96
+ ```
97
+ Not inside an Elevasis project. Run this command from a project directory (an Elevasis project has a .elevasis file at its root).
98
+ ```
99
+
100
+ If you see this error, confirm you are inside a project that was created from the template and has the `.elevasis` marker at its root.
101
+
102
+ ---
103
+
104
+ ## Transient Input Files (`tmp/`)
105
+
106
+ Agents that need to pass structured JSON to `request:submit` or `exec` should write the file to `<projectRoot>/tmp/` rather than to an arbitrary path. The `tmp/` directory is tracked in git (via `tmp/.gitkeep`) but its contents are gitignored, so transient files never pollute the commit history.
107
+
108
+ **Workflow:**
109
+
110
+ 1. Write the input payload to `tmp/<descriptive-name>.json` from the project root.
111
+ 2. Pass the path to the CLI command using the relative form — it resolves against the project root automatically:
112
+
113
+ ```bash
114
+ pnpm elevasis-sdk request:submit -f tmp/request-report.json --cleanup-input
115
+ pnpm elevasis-sdk exec my-workflow -f tmp/exec-payload.json --cleanup-input
116
+ ```
117
+
118
+ 3. The `--cleanup-input` flag deletes the file automatically after a successful command. On failure the file is left intact for inspection.
119
+
120
+ **Safety guard:** `--cleanup-input` only deletes files that are under `<projectRoot>/tmp/`. If the resolved path is outside `tmp/`, the CLI prints a warning to stderr and leaves the file untouched. Files passed via `--input <json>` (inline JSON, no file) are never affected.
121
+
122
+ ---
123
+
124
+ ## Orientation Mode (bare `/project` invocation)
125
+
126
+ When invoked without a subcommand, enter **project mode**: present the portfolio landscape and offer next actions. This is the hub — distinct from `project:work <id>`, which is single-task resume.
127
+
128
+ ### Flow
129
+
130
+ 1. **List active work** — `--status` accepts exactly one value. Run two calls and merge the results:
131
+
132
+ ```bash
133
+ pnpm elevasis-sdk project:list --status active --pretty
134
+ pnpm elevasis-sdk project:list --status blocked --pretty
135
+ ```
136
+
137
+ Show a compact table: client, status, last-touched.
138
+
139
+ 2. **Surface most-recently-touched project's resume context** — identify the top row (most recent `updated_at`), then print its latest task's `resume_context` (via `project:task:list` → pick most recent in-progress task → `project:task:resume <id> --pretty`). Keep it short: current state + next steps, no files dump.
140
+
141
+ 3. **List available operations** — a one-liner reminder of what the user can do from here:
142
+
143
+ ```
144
+ Available: /project status <client> | /project work <query> | /project task add | /project note | /project create
145
+ ```
146
+
147
+ 4. **Offer next-action suggestions** — 1-3 concrete follow-ups driven by what's on screen. Examples:
148
+ - "3 active projects, 1 blocked (Acme — waiting on credentials). Resume Beta (most recent)?"
149
+ - "No active projects. Create one with `/project create`?"
150
+ - "Acme's Phase 2 has 2 overdue tasks. Open status?"
151
+
152
+ ### Presentation template
153
+
154
+ ```
155
+ Project Portfolio (2 active, 1 blocked)
156
+ =======================================
157
+ | Client | Status | Last touched |
158
+ |--------------|----------|--------------|
159
+ | Beta LLC | on_track | 2h ago |
160
+ | Acme Corp | blocked | 1d ago |
161
+ | Gamma Inc | active | 4d ago |
162
+
163
+ Most recent: Beta LLC — API integration
164
+ Current: Endpoint wired, tests passing locally.
165
+ Next: Deploy to staging, verify webhook delivery.
166
+
167
+ Next? Resume Beta | Status Acme (why blocked?) | Create new project
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Intent Detection (opening-message routing)
173
+
174
+ Apply this heuristic on the user's first substantive message of a session, before branching into any other operation. The goal: distinguish **resume existing work** from **start new work**, and confirm before committing.
175
+
176
+ ### Signals → classification
177
+
178
+ | Signal in opening message | Classify as |
179
+ | ----------------------------------------------------------------------------------------------- | ---------------------------------- |
180
+ | References a specific file path, PR, commit, or existing task/milestone/project by name | **Resume** |
181
+ | "Continue", "pick up", "finish", "the thing we were doing", "yesterday's work" | **Resume** |
182
+ | "Let's build X", "new Y", "I want to add Z", "start a new project", "help me with a new client" | **New work** |
183
+ | Single-file cosmetic ask: "fix typo in X", "rename Y", "update dep Z" | **Trivial** (skip new-work prompt) |
184
+ | Ambiguous / discussion only | Ask before routing |
185
+
186
+ ### Routing
187
+
188
+ - **Resume** — run `project:work <query>` with the extracted entity (file path stem, project name, task keywords). If one clean match, print the brief and ask "Continue here?". If multiple matches, show top 3 and ask which. If zero matches, fall back to orientation mode with a note ("No match for 'acme webhook'; here's the landscape").
189
+ - **New work** — enter the **New-Work Project Decision** flow (next section).
190
+ - **Trivial** — proceed directly; do not prompt about projects. Small single-file edits don't need portfolio tracking.
191
+ - **Ambiguous** — reply with orientation mode output and ask "Resume something, or start new?"
192
+
193
+ **Always confirm before branching.** A misclassified "new" routed as "resume" burns trust faster than a one-line confirmation.
194
+
195
+ ---
196
+
197
+ ## New-Work Project Decision
198
+
199
+ When intent detection classifies the opening ask as **new multi-step work** and the conversation has no established project context, ask this question before writing any code:
200
+
201
+ ```
202
+ Scope this work under a project?
203
+ a) Create new project "<proposed-slug>" (recommended for multi-step work)
204
+ b) Link to existing "<closest-match>" (if fuzzy-search finds one)
205
+ c) Standalone — no project tracking (one-off / trivial)
206
+ ```
207
+
208
+ ### Rules
209
+
210
+ - **Default to (a)** when the ask is multi-step (multiple files, multiple sessions expected, client-facing deliverable).
211
+ - **Offer (b)** only when `project:list` fuzzy-matches produce a plausible candidate (name similarity, shared entity references). If nothing matches, omit the option — do not invent one.
212
+ - **Skip the prompt entirely** for trivial single-file edits — intent detection's "Trivial" branch handles this.
213
+ - **Propose a slug** by extracting the core noun from the ask. "Build a lead scraper for Acme" → `acme-lead-scraper`. User can override.
214
+
215
+ ### After the decision
216
+
217
+ - **(a) Create** — invoke the guided `/project create` QnA flow (see below), pre-filling the name/description from the ask.
218
+ - **(b) Link** — run `project:resolve <query>` to confirm the match, then add a task to that project for the new work via `project:task:create`.
219
+ - **(c) Standalone** — proceed without project tracking. Do not create orphan task docs; standalone work leaves no artifact.
220
+
221
+ ---
222
+
223
+ ## Connection
224
+
225
+ ### Environment
226
+
227
+ ```bash
228
+ # .env (project root)
229
+ ELEVASIS_PLATFORM_KEY=sk_...
230
+ ```
231
+
232
+ `ELEVASIS_PLATFORM_KEY` is read from the environment automatically. The SDK CLI walks up
233
+ directories to find `.env`, so it resolves correctly from both the project root and
234
+ `operations/`. `ELEVASIS_API_URL` defaults to `https://api.elevasis.io` (production). Set
235
+ `ELEVASIS_API_URL=http://localhost:5170` to target a local API instance, or set
236
+ `NODE_ENV=development` if the SDK CLI respects that convention.
237
+
238
+ No `--prod` flag is needed — the template targets production by default via `ELEVASIS_API_URL`.
239
+
240
+ ### CLI Invocation
241
+
242
+ All project operations go through the `elevasis-sdk` CLI. Use the wrapper script form from the
243
+ project root:
244
+
245
+ ```bash
246
+ pnpm elevasis-sdk project:<command> [args]
247
+ ```
248
+
249
+ The long form `pnpm -C operations exec elevasis-sdk project:<command> [args]` is equivalent and
250
+ still works — use it if the wrapper script is not yet available in an older project.
251
+
252
+ Organization scoping is handled server-side via `ELEVASIS_PLATFORM_KEY`. No org header or
253
+ `--org` flag is required.
254
+
255
+ ---
256
+
257
+ ## Database Schema
258
+
259
+ ### Tables
260
+
261
+ | Table | Purpose | Key Columns |
262
+ | ---------------- | ---------------------------- | --------------------------------------------------------------------- |
263
+ | `prj_projects` | Client projects/contracts | name, kind, status, description, contract_value, start/end dates |
264
+ | `prj_milestones` | Phases within projects | name, status, due_date, sequence, completed_at, checklist |
265
+ | `prj_tasks` | Work items within milestones | name, status, type, due_date, milestone_id, resume_context, checklist |
266
+ | `prj_notes` | Meeting notes, updates | type, content, summary, occurred_at, task_id, milestone_id |
267
+
268
+ ### Project Kind
269
+
270
+ | Kind | Description |
271
+ | ------------------- | -------------------------------- |
272
+ | `client_engagement` | External client delivery project |
273
+ | `internal` | Internal project work |
274
+ | `research` | Research and investigation |
275
+ | `other` | Catch-all |
276
+
277
+ Default filter for `/project` operations is `kind=client_engagement` unless the user's intent
278
+ is clearly internal or `--kind` is specified explicitly.
279
+
280
+ ### Status Values
281
+
282
+ | Entity | Values |
283
+ | --------- | ------------------------------------------------------------------------------------------------------------------------ |
284
+ | Project | `active`, `on_track`, `at_risk`, `blocked`, `completed`, `paused` |
285
+ | Milestone | `upcoming`, `in_progress`, `completed`, `overdue`, `blocked` |
286
+ | Task | `planned`, `in_progress`, `blocked`, `completed`, `cancelled`, `submitted`, `approved`, `rejected`, `revision_requested` |
287
+ | Note type | `call_note`, `status_update`, `issue`, `blocker`, `agent_learning` |
288
+ | Task type | `documentation`, `code`, `report`, `design`, `refactor`, `feature`, `bug`, `research`, `other` |
289
+
290
+ ### Checklist Shape
291
+
292
+ Both `prj_milestones.checklist` and `prj_tasks.checklist` store a JSONB array of objects with this shape:
293
+
294
+ ```json
295
+ [
296
+ { "id": "uuid", "label": "Item label", "completed": false },
297
+ { "id": "uuid", "label": "Another item", "completed": true }
298
+ ]
299
+ ```
300
+
301
+ The CLI `--checklist` flag on both `project:milestone:update` and `project:task:update` performs a **full replace** — the entire array is replaced with the JSON you supply. There are no item-level add/toggle/remove flags. To mutate a single item:
302
+
303
+ 1. Read the current checklist (via psql or `project:task:get` / `project:milestone:list`)
304
+ 2. Mutate the array in memory (append, flip `completed`, filter out)
305
+ 3. Write the entire array back via `--checklist '<json>'`
306
+
307
+ To clear a checklist: `--checklist '[]'`.
308
+
309
+ ### Foreign Keys
310
+
311
+ - `prj_milestones.project_id` → `prj_projects.id` (CASCADE)
312
+ - `prj_tasks.project_id` → `prj_projects.id` (CASCADE)
313
+ - `prj_tasks.milestone_id` → `prj_milestones.id` (SET NULL)
314
+ - `prj_tasks.parent_task_id` → `prj_tasks.id` (SET NULL) — for subtasks
315
+ - `prj_notes.project_id` → `prj_projects.id` (CASCADE)
316
+ - `prj_notes.task_id` → `prj_tasks.id` (SET NULL)
317
+ - `prj_notes.milestone_id` → `prj_milestones.id` (SET NULL)
318
+ - `prj_projects.deal_id` → `acq_deals.id` (SET NULL)
319
+ - `prj_projects.client_id` → `clients.id` (SET NULL) — clients-hub link; set via `--client`
320
+ - `prj_projects.client_company_id` → `acq_companies.id` (SET NULL) — legacy direct-company link; set via `--client-company-id`
321
+
322
+ ---
323
+
324
+ ## Operations
325
+
326
+ ### `list` — Portfolio Table (no orientation extras)
327
+
328
+ For a plain project table without resume context or next-action suggestions (use the bare `/project` orientation mode above when the user wants the full hub experience):
329
+
330
+ ```bash
331
+ pnpm elevasis-sdk project:list --kind client_engagement --pretty
332
+
333
+ # Filter by client (accepts the client's name or UUID)
334
+ pnpm elevasis-sdk project:list --client "Acme" --pretty
335
+ ```
336
+
337
+ Present as:
338
+
339
+ ```
340
+ Project Portfolio
341
+ =================
342
+ | Client | Status | Kind | Value |
343
+ |--------------------|----------|--------------------|---------|
344
+ | Acme Corp | on_track | client_engagement | $5,000 |
345
+ | Beta LLC | active | client_engagement | $3,500 |
346
+ ```
347
+
348
+ For progress detail (milestones/tasks), follow up with `project:milestone:list` and
349
+ `project:task:list` per project.
350
+
351
+ ### `work <query>` — Resume Brief
352
+
353
+ Wraps `project:work` for fuzzy-matched resume. Used by intent-detection "resume" classification.
354
+
355
+ ```bash
356
+ pnpm elevasis-sdk project:work <query>
357
+ ```
358
+
359
+ Prints the matched project/task brief with current `resume_context`. If multiple matches, show top 3 and ask which.
360
+
361
+ ### `status [<client>]` — Detailed Status
362
+
363
+ If client specified, show full detail for that project. If not, show all.
364
+
365
+ **Resolve the project first** (see Client Inference below), then:
366
+
367
+ ```bash
368
+ # Get project details
369
+ pnpm elevasis-sdk project:get <project-id>
370
+
371
+ # Milestones (ordered by sequence)
372
+ pnpm elevasis-sdk project:milestone:list --project <project-id> --pretty
373
+
374
+ # Tasks (grouped by milestone)
375
+ pnpm elevasis-sdk project:task:list --project <project-id> --pretty
376
+
377
+ # Recent notes
378
+ pnpm elevasis-sdk project:note:list --project <project-id> --pretty
379
+ ```
380
+
381
+ Present as:
382
+
383
+ ```
384
+ Acme Corp — On Track
385
+ =====================
386
+ Description: Automation project for AI-powered workflows
387
+ Contract: $5,000 | Started: 2026-04-01 | Target: 2026-06-01
388
+
389
+ Milestones (2/4 complete)
390
+ [x] Phase 1: Discovery (completed)
391
+ [>] Phase 2: Implementation (in_progress, due 2026-05-01)
392
+ [ ] Phase 3: Testing (upcoming, due 2026-05-15)
393
+ [ ] Phase 4: Launch (upcoming, due 2026-06-01)
394
+
395
+ Tasks (3/8 approved)
396
+ Phase 2: Implementation
397
+ [approved] Requirements doc (documentation)
398
+ [in_progress] API integration (code, due 2026-05-01)
399
+ [planned] User guide (documentation)
400
+
401
+ Recent Notes
402
+ 2026-04-05 [call_note] Weekly check-in — on track, API work started
403
+ 2026-04-01 [call_note] Kickoff — confirmed scope and timeline
404
+ ```
405
+
406
+ #### Tenant Skill Bindings Footer
407
+
408
+ After the normal status output, append a short footer when the project kind, tags, name,
409
+ description, milestones, or active tasks map to a known tenant domain such as `prospecting`,
410
+ `crm`, `outreach`, `finance`, or `support`.
411
+
412
+ Resolve the best domain from explicit metadata first (`tags`, `kind`, domain fields), then from
413
+ project/task text. If no clear domain maps, omit the footer.
414
+
415
+ Footer shape:
416
+
417
+ ```text
418
+ Related skill bindings
419
+ - Domain: <domain>
420
+ - Read or change business profile: /om <domain>
421
+ - Layering primer: node_modules/@elevasis/sdk/reference/spine/spine-primer.md
422
+ ```
423
+
424
+ External projects do not expose monorepo-only architecture commands. Never suggest `/org-os`,
425
+ `/om-spine`, or monorepo paths in this footer.
426
+
427
+ ### `create` — Guided Project Creation (QnA Flow)
428
+
429
+ Interactive walkthrough that collects project details step by step using `AskUserQuestion`,
430
+ then creates the project, milestones, and kickoff note in one go.
431
+
432
+ **Step 1: Project basics**
433
+
434
+ Ask via `AskUserQuestion` (single question, free-text via "Other"):
435
+
436
+ ```
437
+ What's the project name and a brief description of the scope?
438
+ ```
439
+
440
+ The user will reply with something like "Acme Corp automation -- building a lead gen pipeline
441
+ with email outreach". Parse the name and description from the response.
442
+
443
+ **Step 2: Deal/company linking**
444
+
445
+ Search for matching records using read-only psql (the psql role is SELECT-only).
446
+
447
+ Note: `SUPABASE_READONLY_URL` must be set in the template `.env` for psql queries. If not
448
+ set, skip this step and advise the user to link via the Command Center UI after creation.
449
+
450
+ If `SUPABASE_READONLY_URL` is available, query using `<ORG_ID>` from the platform key's
451
+ associated organization (you can retrieve it via `project:list` output which includes
452
+ `organization_id`):
453
+
454
+ ```bash
455
+ source .env && psql "$SUPABASE_READONLY_URL" -c \
456
+ "SELECT id, name, domain FROM acq_companies WHERE organization_id = '<ORG_ID>' AND name ILIKE '%<parsed_name>%' ORDER BY updated_at DESC LIMIT 5;"
457
+
458
+ source .env && psql "$SUPABASE_READONLY_URL" -c \
459
+ "SELECT d.id, d.contact_email, d.cached_stage, c.name AS company_name
460
+ FROM acq_deals d
461
+ LEFT JOIN acq_contacts ct ON ct.id = d.contact_id
462
+ LEFT JOIN acq_companies c ON c.id = ct.company_id
463
+ WHERE d.organization_id = '<ORG_ID>'
464
+ AND (d.contact_email ILIKE '%<parsed_name>%' OR c.name ILIKE '%<parsed_name>%')
465
+ ORDER BY d.updated_at DESC LIMIT 5;"
466
+ ```
467
+
468
+ If matches found, ask via `AskUserQuestion`:
469
+
470
+ ```
471
+ Found matching records. Link this project?
472
+ - [Company: Acme Corp (acme.com)]
473
+ - [Deal: john@acme.com (closed_won)]
474
+ - No linking
475
+ ```
476
+
477
+ If no matches, skip silently.
478
+
479
+ **Step 3: Contract details**
480
+
481
+ Ask via `AskUserQuestion`:
482
+
483
+ ```
484
+ Contract details?
485
+ - Options with previews showing common structures:
486
+ - "Fixed project" -- one-time fee
487
+ - "Monthly retainer" -- recurring
488
+ - "Hourly" -- time & materials
489
+ - Other
490
+ ```
491
+
492
+ Then follow up for the value, start date, and target end date. These can be collected in a
493
+ single question:
494
+
495
+ ```
496
+ What's the contract value, start date, and target end date?
497
+ (e.g., "$5000, starting today, targeting June 1")
498
+ ```
499
+
500
+ Parse natural language dates ("today", "next Monday", "June 1") into ISO format.
501
+
502
+ **Step 4: Milestone suggestions**
503
+
504
+ Based on the project description from Step 1, suggest 3-5 milestones that fit the scope. Use
505
+ your understanding of the project to propose relevant phases.
506
+
507
+ **Guidelines for milestone suggestions:**
508
+
509
+ - For automation/integration projects: Discovery, Implementation, Testing, Launch
510
+ - For content/marketing projects: Strategy, Content Creation, Review, Distribution
511
+ - For consulting/advisory: Assessment, Recommendations, Implementation Support
512
+ - Always include a Discovery/Kickoff phase first and a Launch/Handoff phase last
513
+ - Suggest due dates spaced evenly between start and target end date
514
+
515
+ Present via `AskUserQuestion` with previews:
516
+
517
+ ```
518
+ Suggested milestones for "<project name>". Select which to include:
519
+ (multiSelect: true)
520
+
521
+ - Discovery (due <start + 2 weeks>)
522
+ - Implementation (due <midpoint>)
523
+ - Testing (due <end - 2 weeks>)
524
+ - Launch (due <end date>)
525
+ ```
526
+
527
+ The user can select which ones to keep, modify via "Other", or skip entirely.
528
+
529
+ **Step 5: Kickoff note**
530
+
531
+ Ask via `AskUserQuestion`:
532
+
533
+ ```
534
+ Add a kickoff note? This captures initial context (scope confirmed, key contacts, etc.)
535
+ - Yes, let me type one
536
+ - Skip for now
537
+ ```
538
+
539
+ If yes, collect the note content.
540
+
541
+ **Step 6: Execute**
542
+
543
+ Create everything in sequence using the CLI:
544
+
545
+ ```bash
546
+ # 1. Create project
547
+ # If the user mentioned a client by name, resolve it first:
548
+ # pnpm elevasis-sdk client:resolve "<client name>"
549
+ # Then pass the name (or UUID) via --client
550
+ pnpm elevasis-sdk project:create \
551
+ --name "<name>" \
552
+ --kind client_engagement \
553
+ --status active \
554
+ --description "<desc>" \
555
+ --client "<client-name-or-uuid>"
556
+
557
+ # 2. Create milestones
558
+ pnpm elevasis-sdk project:milestone:create \
559
+ --project <project-id> \
560
+ --name "<milestone-name>" \
561
+ --status upcoming \
562
+ --due-date <date>
563
+
564
+ # 3. Create kickoff note (if provided)
565
+ pnpm elevasis-sdk project:note:create \
566
+ --project <project-id> \
567
+ --content "<note-content>" \
568
+ --type call_note
569
+ ```
570
+
571
+ If `--company` or `--deal` linking was confirmed, use `project:update` after creation:
572
+
573
+ ```bash
574
+ pnpm elevasis-sdk project:update <project-id> --client "<client-name-or-uuid>"
575
+ ```
576
+
577
+ To remove a client link later:
578
+
579
+ ```bash
580
+ pnpm elevasis-sdk project:update <project-id> --clear-client
581
+ ```
582
+
583
+ Do NOT pass `--client ""` to clear — an empty string is rejected as ambiguous. Use `--clear-client` instead.
584
+
585
+ **Step 7: Summary**
586
+
587
+ Show the created project:
588
+
589
+ ```
590
+ Project Created
591
+ ===============
592
+ Name: Acme Corp Automation
593
+ Kind: client_engagement
594
+ Status: active
595
+ Timeline: 2026-04-07 → 2026-06-01
596
+
597
+ Milestones:
598
+ 1. Discovery (upcoming, due 2026-04-21)
599
+ 2. Implementation (upcoming, due 2026-05-08)
600
+ 3. Testing (upcoming, due 2026-05-22)
601
+ 4. Launch (upcoming, due 2026-06-01)
602
+
603
+ Kickoff Note: Confirmed scope -- lead gen pipeline with email outreach.
604
+
605
+ Next: /project status acme
606
+ ```
607
+
608
+ ---
609
+
610
+ ### `add "<name>" [options]` — Quick Create Project (No QnA)
611
+
612
+ **Options:**
613
+
614
+ - `--status <status>` (default: `active`)
615
+ - `--value <number>` — contract value (informational, stored in description or notes)
616
+ - `--start <date>` — start date
617
+ - `--end <date>` — target end date
618
+ - `--description "<text>"` — scope description
619
+ - `--kind <kind>` (default: `client_engagement`)
620
+
621
+ ```bash
622
+ pnpm elevasis-sdk project:create \
623
+ --name "<name>" \
624
+ --kind client_engagement \
625
+ --status active \
626
+ --description "<desc>"
627
+ ```
628
+
629
+ **After creating:** Show the created project and suggest next steps (add milestones).
630
+
631
+ ### `update <client> [options]` — Update Project
632
+
633
+ **Options:** `--status`, `--name`, `--description`, `--client <id-or-name>`, `--clear-client`, `--client-company-id <uuid>`
634
+
635
+ First resolve the client (see Client Inference below), then:
636
+
637
+ ```bash
638
+ pnpm elevasis-sdk project:update <project-id> --status <status>
639
+ ```
640
+
641
+ ### `milestone add <client> "<name>" [options]` — Add Milestone
642
+
643
+ **Options:**
644
+
645
+ - `--status <status>` (default: `upcoming`)
646
+ - `--due <date>` — due date (ISO)
647
+ - `--description "<text>"`
648
+
649
+ ```bash
650
+ pnpm elevasis-sdk project:milestone:create \
651
+ --project <project-id> \
652
+ --name "<name>" \
653
+ --status upcoming \
654
+ --due-date <date>
655
+ ```
656
+
657
+ ### `milestone update <client> "<milestone>" [options]` — Update Milestone
658
+
659
+ Resolve milestone by name (see Client Inference for project resolution, then use
660
+ `project:milestone:list` to find the milestone ID by partial name match):
661
+
662
+ ```bash
663
+ # List milestones to find ID
664
+ pnpm elevasis-sdk project:milestone:list --project <project-id>
665
+
666
+ # Update status
667
+ pnpm elevasis-sdk project:milestone:update <milestone-id> --status completed
668
+
669
+ # Update checklist (full replace)
670
+ pnpm elevasis-sdk project:milestone:update <milestone-id> \
671
+ --checklist '[{"id":"uuid","label":"Item label","completed":false}]'
672
+ ```
673
+
674
+ If status changes to `completed`, the API auto-sets `completed_at`.
675
+
676
+ **Options:** `--status`, `--due-date`, `--name`, `--checklist <json>`
677
+
678
+ **Checklist mutations:** The CLI performs a full replace. To add, toggle, or remove an item:
679
+
680
+ 1. Read the current checklist via psql or `project:milestone:list`
681
+ 2. Mutate the array in memory
682
+ 3. Write back the full array via `--checklist '<json>'`
683
+
684
+ ### `checklist <client> "<milestone>"` — View Checklist
685
+
686
+ Resolve the milestone, then read its `checklist` JSONB column via read-only psql.
687
+
688
+ Note: Requires `SUPABASE_READONLY_URL` in the template `.env`. If not set, advise the user
689
+ to view the checklist via the Command Center UI, or add `SUPABASE_READONLY_URL` to `.env`.
690
+
691
+ ```bash
692
+ source .env && psql "$SUPABASE_READONLY_URL" -c \
693
+ "SELECT m.name, m.checklist
694
+ FROM prj_milestones m
695
+ WHERE m.project_id = '<project_id>' AND m.name ILIKE '%<name>%'
696
+ LIMIT 1;"
697
+ ```
698
+
699
+ The `checklist` column is a JSONB array of objects:
700
+
701
+ ```json
702
+ [
703
+ { "id": "uuid", "label": "Set up dev environment", "completed": true },
704
+ { "id": "uuid", "label": "Draft scope document", "completed": false }
705
+ ]
706
+ ```
707
+
708
+ Present as:
709
+
710
+ ```
711
+ Onboarding & Scope — Checklist (2/3 complete)
712
+ [x] Set up dev environment
713
+ [x] Schedule kickoff call
714
+ [ ] Draft scope document
715
+ ```
716
+
717
+ ### `task add <client> "<name>" [options]` — Add Task
718
+
719
+ **Options:**
720
+
721
+ - `--milestone "<name>"` — link to a milestone (resolve by name first)
722
+ - `--status <status>` (default: `planned`)
723
+ - `--type <type>` (default: `other`)
724
+ - `--due <date>`
725
+ - `--description "<text>"`
726
+ - `--checklist <json>` — initial checklist items
727
+
728
+ ```bash
729
+ # Resolve milestone ID first if --milestone provided
730
+ pnpm elevasis-sdk project:milestone:list --project <project-id>
731
+
732
+ # Create task
733
+ pnpm elevasis-sdk project:task:create \
734
+ --project <project-id> \
735
+ --title "<name>" \
736
+ --status planned \
737
+ --type <type> \
738
+ --milestone <milestone-id>
739
+
740
+ # Create task with initial checklist
741
+ pnpm elevasis-sdk project:task:create \
742
+ --project <project-id> \
743
+ --title "<name>" \
744
+ --checklist '[{"id":"1","label":"Step one","completed":false}]'
745
+ ```
746
+
747
+ ### `task update <client> "<task>" [options]` — Update Task
748
+
749
+ Resolve task by name within the project using `project:task:list`, then update by positional ID:
750
+
751
+ ```bash
752
+ # Find the task ID
753
+ pnpm elevasis-sdk project:task:list --project <project-id>
754
+
755
+ # Update status (positional <id> comes first, then flags)
756
+ pnpm elevasis-sdk project:task:update <task-id> --status <status>
757
+
758
+ # Update checklist (full replace)
759
+ pnpm elevasis-sdk project:task:update <task-id> \
760
+ --checklist '[{"id":"uuid","label":"Step","completed":false}]'
761
+
762
+ # Clear checklist
763
+ pnpm elevasis-sdk project:task:update <task-id> --checklist '[]'
764
+ ```
765
+
766
+ **Options:** `--status`, `--title`, `--milestone`, `--description`, `--checklist <json>`
767
+
768
+ **Important:** The task ID is a **positional argument** — it comes immediately after `project:task:update`, before any flags. There is no `--task-id` flag.
769
+
770
+ If status changes to `approved`, the API auto-sets `completed_at`.
771
+
772
+ **Checklist mutations:** Same full-replace semantics as milestones. To add, toggle, or remove an item:
773
+
774
+ 1. Read the current task via `project:task:get <task-id>`
775
+ 2. Mutate the checklist array in memory
776
+ 3. Write back the full array via `project:task:update <task-id> --checklist '<json>'`
777
+
778
+ ### `note <client> "<content>" [options]` — Add Note
779
+
780
+ **Options:**
781
+
782
+ - `--type <type>` (default: `call_note`) — see Note Type Selection below
783
+ - `--task <task-id>` — attach to a specific task (UUID)
784
+ - `--milestone <milestone-id>` — attach to a milestone (UUID)
785
+
786
+ ```bash
787
+ pnpm elevasis-sdk project:note:create \
788
+ --project <project-id> \
789
+ --content "<content>" \
790
+ --type <type>
791
+
792
+ # Attach to a task
793
+ pnpm elevasis-sdk project:note:create \
794
+ --project <project-id> \
795
+ --content "<content>" \
796
+ --type agent_learning \
797
+ --task <task-id>
798
+ ```
799
+
800
+ ### `notes <client>` — List Notes
801
+
802
+ ```bash
803
+ pnpm elevasis-sdk project:note:list --project <project-id> --pretty
804
+ ```
805
+
806
+ ### `delete <client>` — Delete Project
807
+
808
+ **ALWAYS confirm before deleting.** Show what will be deleted (milestone count, task count,
809
+ note count) using the list commands, then ask for confirmation.
810
+
811
+ ```bash
812
+ pnpm elevasis-sdk project:delete <project-id>
813
+ ```
814
+
815
+ Cascade deletes all milestones, tasks, and notes.
816
+
817
+ ---
818
+
819
+ ## Notes Management
820
+
821
+ Notes (`prj_notes`) are the durable record of project-scoped signals — conversation outcomes, blockers, status updates, and agent-captured learnings. They complement `resume_context` (which is task-scoped, temporal, and agent-facing) by providing a human-readable audit trail.
822
+
823
+ ### When to Create a Note vs. Fall Through to Global Auto-Memory
824
+
825
+ **Create a project note** when all of the following hold:
826
+
827
+ - A specific project and (optionally) task is in scope in the current session
828
+ - The content is project-bound — it would only matter to someone working on this project
829
+ - The content is durable — it should be visible in future status views, not just the current session
830
+
831
+ **Fall through to global auto-memory** when:
832
+
833
+ - No active project or task can be resolved from session context (see Project-Scope Resolution below)
834
+ - The content is cross-project or platform-wide (e.g., a general TypeScript pattern, a reusable workflow insight)
835
+ - The signal is ephemeral and not worth a DB write (e.g., "reminder: run linter before committing")
836
+
837
+ When in doubt and no project context is resolvable, global auto-memory is the safe default.
838
+
839
+ ### Note Type Selection Guide
840
+
841
+ | Type | Use when |
842
+ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
843
+ | `call_note` | Transcribing or summarizing a live client / stakeholder call. Include key decisions and action items. |
844
+ | `status_update` | Milestone-level progress worth flagging to a human operator: phase complete, substantial deliverable, direction change. Don't emit one every session turn. |
845
+ | `issue` | Bug, regression, unexpected failure, contract mismatch. Include reproduction context. |
846
+ | `blocker` | Work cannot proceed without external action: decision needed, credential missing, upstream fix required. |
847
+ | `agent_learning` | Project-bound knowledge the agent discovered: API quirks, client conventions, deploy gotchas, rate limits. Captures "things a future agent resuming this project should know" so it travels with the project instead of living only in global auto-memory. |
848
+
849
+ ### Project-Scope Resolution
850
+
851
+ Before writing a note, the agent resolves which project (and optionally task) to attach it to:
852
+
853
+ 1. **Explicit in session** — a project/task UUID was mentioned or returned by a recent `project:work <id>` or `project:task:*` invocation in this session. Use it directly.
854
+ 2. **Implicit from task list** — if step 1 yields nothing, run (`--status` accepts one value; run both calls and merge):
855
+
856
+ ```bash
857
+ pnpm elevasis-sdk project:list --status active --pretty
858
+ pnpm elevasis-sdk project:list --status blocked --pretty
859
+ pnpm elevasis-sdk project:task:list --project <top-project-id> --status in_progress
860
+ ```
861
+
862
+ Use the most-recently-touched project's in-progress task if there is exactly one. If multiple in-progress tasks exist, prefer the one that most closely matches the current conversation topic.
863
+
864
+ 3. **Prompt the user** — if steps 1 and 2 are both ambiguous or empty, ask once: "Which project should I attach this note to? (project UUID or slug, or 'skip' to use global auto-memory)."
865
+
866
+ 4. **Fall through** — if the user says "skip" or no resolution is possible, write to global auto-memory and continue.
867
+
868
+ Resolution is **agent-side at call time** — there is no middleware or automatic routing layer. The agent infers context from the session.
869
+
870
+ ### Agent Workflow: Capturing an `agent_learning`
871
+
872
+ When the agent recognizes a project-scoped learning (an API quirk, an undocumented constraint, a client convention discovered during implementation):
873
+
874
+ 1. Recognize the signal — e.g., "Apify actor X rate-limits at 20 rps", "client requires ISO dates, not timestamps", "staging env needs VPN before webhook delivery works"
875
+ 2. Resolve active project and task from session context (see above)
876
+ 3. Write the note:
877
+
878
+ ```bash
879
+ pnpm elevasis-sdk project:note:create \
880
+ --project <project-id> \
881
+ --type agent_learning \
882
+ --task <task-id> \
883
+ --content "<concise, factual statement of what was learned>"
884
+ ```
885
+
886
+ Omit `--task` if no specific task is in scope (e.g., the learning is milestone-level or project-level).
887
+
888
+ 4. If no project can be resolved, write the learning to global auto-memory instead.
889
+
890
+ **Content style for `agent_learning` notes:** write as a factual assertion another agent could act on — "Apify actor X rate-limits at 20 rps; add a 50ms delay between calls" rather than "I noticed that Apify seems slow." Keep it under 200 characters when possible.
891
+
892
+ ---
893
+
894
+ ## Status Transitions from Other Skills
895
+
896
+ The `/project` skill is the canonical reference for how lifecycle signals from other skills map to `project:task:update` status transitions. The behavioral hooks live in the emitting skills; this section documents the contract.
897
+
898
+ ### Transition Map
899
+
900
+ | Signal | Source skill | Transition | CLI call |
901
+ | ---------------------------------------- | -------------- | -------------------------------------- | -------------------------------------------------- |
902
+ | Successful `git push` via `/deploy` | `deploy/` | linked task → `submitted` | `project:task:update <task-id> --status submitted` |
903
+ | `/save` catches "I'm stuck" / blocker | `save/` | linked task → `blocked` + blocker note | `project:task:update <task-id> --status blocked` |
904
+ | User/agent says "I'm done" / "task done" | `/project` NLM | active task → `completed` | `project:task:update <task-id> --status completed` |
905
+
906
+ ### "I'm Done" Recognition (Natural Language Mode)
907
+
908
+ When the user or agent says any of the following in the context of a project task, treat it as a `completed` transition signal:
909
+
910
+ - "I'm done", "I'm finished", "done with this", "that's done"
911
+ - "task complete", "task finished", "mark as complete"
912
+ - "wrap this up", "close this task", "finalize this"
913
+
914
+ Resolve the active task from session context (see Project-Scope Resolution), confirm once ("Mark `<task name>` as completed?"), then fire:
915
+
916
+ ```bash
917
+ pnpm elevasis-sdk project:task:update <task-id> --status completed
918
+ ```
919
+
920
+ If no task resolves, ask the user which task to complete rather than silently skipping.
921
+
922
+ ### Transition Rules (All Skills)
923
+
924
+ These rules apply to every skill that emits a transition:
925
+
926
+ - **Explicit task ID only** — use the UUID resolved from session context. Never guess.
927
+ - **Best-effort** — if the CLI returns non-2xx, emit a warning line and continue. Do not block the primary operation.
928
+ - **Idempotent** — if the task is already in the target status, the update is a no-op. Fire it anyway.
929
+ - **Confirm before `completed`** — always ask once before marking a task done, since this is hard to undo semantically.
930
+
931
+ For full implementation details, see the emitting skills: `deploy/SKILL.md` (Step 8) and `save/SKILL.md` (Steps 5, 5a).
932
+
933
+ ---
934
+
935
+ ## Client Inference
936
+
937
+ Resolve which project the user means using these rules (in priority order):
938
+
939
+ 1. **Exact ID** — if the argument looks like a UUID, use it directly with `project:get`
940
+ 2. **Name match** — list projects filtered by `client_engagement` kind, then match by name:
941
+ ```bash
942
+ pnpm elevasis-sdk project:list --kind client_engagement
943
+ ```
944
+ Filter results by partial name match against the user's input (case-insensitive).
945
+ 3. **Single active project** — if only one non-completed `client_engagement` project exists,
946
+ use it without asking
947
+ 4. **Context from conversation** — if the conversation has been discussing a specific client,
948
+ use that one
949
+ 5. **Ambiguous** — if multiple match or none match, list projects and ask
950
+
951
+ ---
952
+
953
+ ## Natural Language Mode
954
+
955
+ When args don't match any command pattern, infer intent from natural language:
956
+
957
+ | User says | Inferred operation |
958
+ | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
959
+ | "how's acme doing" | `status acme` |
960
+ | "add a note: weekly call, everything on track" | `note <active-project> "weekly call, everything on track"` |
961
+ | "mark phase 2 complete for acme" | `milestone update acme "phase 2" --status completed` |
962
+ | "approve the API docs task" | `task update <project> "API docs" --status approved` |
963
+ | "new client: Beta LLC, $3500/mo, starting may 1" | `add "Beta LLC" --value 3500 --start 2026-05-01` |
964
+ | "what's overdue?" | List milestones where `due_date < now() AND status != 'completed'` |
965
+ | "block acme, waiting on client credentials" | `update acme --status blocked` |
966
+ | "show checklist for phase 2" | Read and display checklist for milestone "phase 2" via psql or `project:milestone:list` |
967
+ | "add 'deploy staging' to task X's checklist" | Read task X's checklist → append `{id: uuid, label: "deploy staging", completed: false}` → `project:task:update <id> --checklist '<json>'` |
968
+ | "mark 'deploy staging' done on task X" | Read task X's checklist → flip `completed` on matching item → `project:task:update <id> --checklist '<json>'` |
969
+ | "clear the checklist on task X" | `project:task:update <task-id> --checklist '[]'` |
970
+ | "add checklist item to onboarding milestone: review scope doc" | Read milestone checklist → append item → `project:milestone:update <id> --checklist '<json>'` |
971
+ | "I'm done" / "task complete" / "done with this" | Resolve active task → confirm → `project:task:update <task-id> --status completed` |
972
+ | "save: Apify actor X rate-limits at 20rps" | Resolve active project/task → `project:note:create --project <id> --type agent_learning --task <task-id> "Apify actor X rate-limits at 20rps"` |
973
+ | "remember: client requires ISO dates, not timestamps" | Resolve active project/task → `project:note:create --project <id> --type agent_learning --task <task-id> "client requires ISO dates, not timestamps"` |
974
+
975
+ **Checklist note:** all checklist mutations use the read-modify-write pattern. The CLI has no item-level flags (`--add-item`, `--toggle`, `--remove-item` do not exist). Always read the current state, mutate the array, then write the full array back.
976
+
977
+ ---
978
+
979
+ ## Bulk Operations
980
+
981
+ ### Add Multiple Milestones
982
+
983
+ For creating a standard milestone set, run `project:milestone:create` in sequence:
984
+
985
+ ```bash
986
+ for name in "Discovery" "Implementation" "Testing" "Launch"; do
987
+ pnpm elevasis-sdk project:milestone:create \
988
+ --project <project-id> \
989
+ --name "$name" \
990
+ --status upcoming
991
+ done
992
+ ```
993
+
994
+ ### Overdue Report
995
+
996
+ Query via read-only psql for milestones past their due date. Requires `SUPABASE_READONLY_URL`
997
+ in the template `.env`. Replace `<ORG_ID>` with the organization ID associated with your
998
+ `ELEVASIS_PLATFORM_KEY` (visible in `project:list` output as `organization_id`). If
999
+ `SUPABASE_READONLY_URL` is not configured, use `project:milestone:list` per project and filter
1000
+ manually.
1001
+
1002
+ ```bash
1003
+ source .env && psql "$SUPABASE_READONLY_URL" -c \
1004
+ "SELECT
1005
+ p.name AS project,
1006
+ m.name AS milestone,
1007
+ m.due_date,
1008
+ m.status,
1009
+ (CURRENT_DATE - m.due_date::date) AS days_overdue
1010
+ FROM prj_milestones m
1011
+ JOIN prj_projects p ON p.id = m.project_id
1012
+ WHERE p.organization_id = '<ORG_ID>'
1013
+ AND m.due_date < CURRENT_DATE
1014
+ AND m.status NOT IN ('completed')
1015
+ ORDER BY m.due_date;"
1016
+ ```
1017
+
1018
+ ---
1019
+
1020
+ ## Client Linking
1021
+
1022
+ Projects can be linked to a client in the clients hub. When a user mentions a client by name, resolve it to an ID and use `--client` to wire the project.
1023
+
1024
+ ### Two linking flags, two different columns
1025
+
1026
+ - `--client <name-or-uuid>` — links to a clients-hub record (`prj_projects.client_id`). Use this for new client linkage. Accepts a name (the CLI fuzzy-resolves it) or a UUID.
1027
+ - `--client-company-id <uuid>` — legacy direct-company linkage (`prj_projects.client_company_id`); kept for back-compat. UUID only.
1028
+
1029
+ These are independent. Do not use `--client-company-id` when you mean `--client`.
1030
+
1031
+ ### When the user mentions a client by name
1032
+
1033
+ 1. Resolve the client name to a UUID first (optional but gives an explicit confirmation step):
1034
+ ```bash
1035
+ pnpm elevasis-sdk client:resolve "Acme"
1036
+ ```
1037
+ 2. Pass the name or UUID to `--client` — the CLI fuzzy-resolves names automatically:
1038
+ ```bash
1039
+ pnpm elevasis-sdk project:create --name "Acme Automation" --kind client_engagement --client "Acme"
1040
+ pnpm elevasis-sdk project:update <project-id> --client "Acme"
1041
+ ```
1042
+ 3. If the client name matches multiple records, the CLI returns an error listing candidates. In that case, run `client:resolve "Acme"` to disambiguate, then pass the UUID directly.
1043
+
1044
+ ### Filtering projects by client
1045
+
1046
+ ```bash
1047
+ pnpm elevasis-sdk project:list --client "Acme" --pretty
1048
+ ```
1049
+
1050
+ ### Removing a client link
1051
+
1052
+ ```bash
1053
+ pnpm elevasis-sdk project:update <project-id> --clear-client
1054
+ ```
1055
+
1056
+ `--client` and `--clear-client` are mutually exclusive on the same command call.
1057
+
1058
+ ### Soft recommendation for client engagement projects
1059
+
1060
+ When the user creates a `client_engagement` project without mentioning a client, gently note that linking a client is recommended so the project appears in client lineage views. Do not block creation — the flag is optional.
1061
+
1062
+ ### Client command reference
1063
+
1064
+ The full `client:*` surface (list, get, status, resolve) is available via `elevasis-sdk client:*`. The `client:resolve` command mirrors `project:resolve` in shape and is the canonical tool for name-to-ID translation.
1065
+
1066
+ ---
1067
+
1068
+ ## Safety Rules
1069
+
1070
+ 1. **Always confirm deletes** — show what will be cascade-deleted before executing
1071
+ 2. **Organization scoping** — all API calls are automatically org-scoped via
1072
+ `ELEVASIS_PLATFORM_KEY`; every direct psql query MUST include an `organization_id` filter
1073
+ (use `<ORG_ID>` as a placeholder and confirm the value with the user if unknown)
1074
+ 3. **Read before write** — when updating, show current state before applying changes
1075
+ 4. **Validate status values** — reject invalid status strings before sending to the CLI
1076
+ 5. **Kind default** — default to `--kind client_engagement` when user intent is client
1077
+ delivery; use `--kind internal` for internal project work
1078
+ 6. **Template project assumption** — the template is a single-organization project. All
1079
+ `/project` operations operate within the organization scoped by `ELEVASIS_PLATFORM_KEY`.
1080
+ There is no cross-org capability.
1081
+ 7. **Checklist full-replace** — the `--checklist` flag replaces the entire array. Always read
1082
+ the current checklist before writing to avoid losing existing items.
1083
+ 8. **Confirm task completion** — always ask once before marking a task `completed` via "I'm done"
1084
+ recognition. This status is semantically significant and warrants a confirmation step.
1085
+
1086
+ ---
1087
+
1088
+ **Last Updated:** 2026-05-08