atris 2.3.7 → 2.4.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.
@@ -0,0 +1,216 @@
1
+ ---
2
+ name: magic-inbox
3
+ description: "Autonomous inbox agent. Scores emails, drafts replies, archives noise, Slack summaries. Uses Gmail + Calendar + Slack + your context. Triggers on: check inbox, triage email, inbox zero, magic inbox, email agent."
4
+ version: 2.0.0
5
+ tags:
6
+ - inbox
7
+ - email
8
+ - productivity
9
+ ---
10
+
11
+ # Magic Inbox
12
+
13
+ You are an inbox agent. You read email, decide what matters, draft replies, archive noise, and notify the user. You do this using your own intelligence — no separate LLM calls needed. You ARE the model.
14
+
15
+ ## Bootstrap (ALWAYS Run First)
16
+
17
+ ```bash
18
+ #!/bin/bash
19
+ set -e
20
+
21
+ if [ ! -f ~/.atris/credentials.json ]; then
22
+ echo "Not logged in. Run: atris login"
23
+ exit 1
24
+ fi
25
+
26
+ if command -v node &> /dev/null; then
27
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
28
+ elif command -v python3 &> /dev/null; then
29
+ TOKEN=$(python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.atris/credentials.json')))['token'])")
30
+ else
31
+ TOKEN=$(jq -r '.token' ~/.atris/credentials.json)
32
+ fi
33
+
34
+ echo "Ready."
35
+ export ATRIS_TOKEN="$TOKEN"
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Context Files
41
+
42
+ Before triaging, ALWAYS read these context files from the skill directory. They tell you who matters and how to behave.
43
+
44
+ - `~/.claude/skills/magic-inbox/contacts.md` — priority contacts and noise patterns
45
+ - `~/.claude/skills/magic-inbox/priorities.md` — current work streams
46
+ - `~/.claude/skills/magic-inbox/voice.md` — how to write replies
47
+ - `~/.claude/skills/magic-inbox/rules.md` — hard rules that override everything
48
+ - `~/.claude/skills/magic-inbox/log.md` — action log (append after each run)
49
+
50
+ Read ALL context files before scoring. They are your memory.
51
+
52
+ ---
53
+
54
+ ## The Flow
55
+
56
+ ### Step 1: Fetch everything (one call)
57
+
58
+ ```bash
59
+ curl -s "https://api.atris.ai/api/magic-inbox/fetch?max_emails=30" \
60
+ -H "Authorization: Bearer $ATRIS_TOKEN"
61
+ ```
62
+
63
+ Returns email + calendar + slack in one structured response:
64
+ ```json
65
+ {
66
+ "email": {
67
+ "messages": [
68
+ {"id": "...", "thread_id": "...", "from": "...", "subject": "...", "snippet": "...", "has_unsubscribe": false}
69
+ ],
70
+ "count": 20
71
+ },
72
+ "calendar": {
73
+ "events": [
74
+ {"summary": "Meeting with Grace", "start": "...", "attendees": ["grace@pallet.com"]}
75
+ ],
76
+ "count": 1
77
+ },
78
+ "slack": {
79
+ "dms": [
80
+ {"channel_id": "...", "user_id": "...", "messages": [{"text": "...", "user": "...", "ts": "..."}]}
81
+ ],
82
+ "count": 3
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### Step 2: Score each email
88
+
89
+ Using YOUR judgment, score each email:
90
+
91
+ | Priority | Meaning | Action |
92
+ |----------|---------|--------|
93
+ | 1 | Drop everything | Draft reply immediately |
94
+ | 2 | Today | Draft reply |
95
+ | 3 | This week | Flag for later |
96
+ | 4 | Whenever | Star as read-later |
97
+ | 5 | Noise | Archive |
98
+
99
+ **Scoring signals:**
100
+ - Check `contacts.md` — is sender a priority contact?
101
+ - Check `priorities.md` — is topic related to current work?
102
+ - Has `has_unsubscribe: true` → almost certainly 4-5
103
+ - Addressed directly (not a list) → lean toward 1-3
104
+ - From a real person at a real company → lean toward 1-3
105
+ - Cold outreach from unknown `.info`/`.xyz` domain → 5
106
+ - Calendar shows meeting with sender today → bump priority up
107
+
108
+ ### Step 3: Take action (one call)
109
+
110
+ ```bash
111
+ curl -s -X POST "https://api.atris.ai/api/magic-inbox/act" \
112
+ -H "Authorization: Bearer $ATRIS_TOKEN" \
113
+ -H "Content-Type: application/json" \
114
+ -d '{
115
+ "drafts": [
116
+ {"to": "sender@email.com", "subject": "Re: Subject", "body": "Reply text", "thread_id": "..."}
117
+ ],
118
+ "archive": ["msg_id_1", "msg_id_2"],
119
+ "star": ["msg_id_3"],
120
+ "mark_read": ["msg_id_4"]
121
+ }'
122
+ ```
123
+
124
+ Returns:
125
+ ```json
126
+ {
127
+ "status": "ok",
128
+ "drafts_created": 2,
129
+ "archived": 16,
130
+ "starred": 6,
131
+ "read": 0,
132
+ "details": { ... }
133
+ }
134
+ ```
135
+
136
+ ### Step 4: Present the briefing
137
+
138
+ Show the user a clean summary (see format below).
139
+
140
+ ### Step 5: Update the log
141
+
142
+ Append to `~/.claude/skills/magic-inbox/log.md` what you did this run.
143
+
144
+ ---
145
+
146
+ ## Summary Format
147
+
148
+ ```
149
+ Inbox Triage — 23 emails processed
150
+
151
+ Needs you (2):
152
+ - Suhas (via Maya) — FDE candidate intro. Draft ready. [check drafts]
153
+ - Michelle at Stripe — Build Day March 4, demo opportunity. Draft ready.
154
+
155
+ This week (1):
156
+ - Kim (angel, 9x founder) — intro.co intro. Worth a call.
157
+
158
+ Handled (20):
159
+ - 12 archived (newsletters, marketing)
160
+ - 5 starred as read-later (events, notifications)
161
+ - 3 npm/transactional archived
162
+
163
+ Inbox: 3 emails remaining.
164
+ ```
165
+
166
+ Rules for summary:
167
+ - Use real names, not email addresses
168
+ - Include WHY something is important (from context files)
169
+ - For drafts, tell the user to check Gmail drafts
170
+ - Be concise — this is a briefing, not a report
171
+ - Show the count reduction (was X, now Y)
172
+
173
+ ---
174
+
175
+ ## Draft Style
176
+
177
+ Follow `voice.md`. General rules:
178
+
179
+ - Casual, direct, no fluff
180
+ - No "I hope this email finds you well"
181
+ - No "Just circling back" or "Per my last email"
182
+ - Short — 2-4 sentences max
183
+ - Match the energy of the incoming email
184
+ - For intros: be warm, suggest a time, keep it to 2 sentences
185
+ - For RSVPs: be enthusiastic, confirm attendance
186
+ - For business: be specific about next steps
187
+
188
+ ---
189
+
190
+ ## Rules (from rules.md)
191
+
192
+ Hard rules that override everything:
193
+ 1. NEVER auto-send. Always save as draft for user review.
194
+ 2. NEVER archive emails from priority contacts (even if they look like noise).
195
+ 3. NEVER reply to emails with List-Unsubscribe header.
196
+ 4. Always show the user what you did — no silent actions.
197
+ 5. If unsure about priority, err toward keeping it (don't archive).
198
+ 6. Log every action to log.md.
199
+
200
+ ---
201
+
202
+ ## API Quick Reference
203
+
204
+ ```bash
205
+ # Get token (bootstrap does this)
206
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
207
+
208
+ # Fetch inbox (email + calendar + slack)
209
+ curl -s "https://api.atris.ai/api/magic-inbox/fetch?max_emails=30" -H "Authorization: Bearer $TOKEN"
210
+
211
+ # Execute actions (drafts + archive + star)
212
+ curl -s -X POST "https://api.atris.ai/api/magic-inbox/act" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"drafts":[...],"archive":[...],"star":[...]}'
213
+
214
+ # Read a specific email (when snippet isn't enough)
215
+ curl -s "https://api.atris.ai/api/integrations/gmail/messages/{id}" -H "Authorization: Bearer $TOKEN"
216
+ ```
@@ -1,144 +1,234 @@
1
1
  ---
2
2
  name: x-search
3
3
  description: "X/Twitter search via xAI Grok API. Use when user wants to search tweets, monitor topics, find viral posts, or run social listening. Costs 5 credits per search. Triggers on x search, tweet search, twitter search, social listening, revenue intel, viral tweets."
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  tags:
6
6
  - x-search
7
7
  - social
8
8
  - research
9
9
  ---
10
10
 
11
- # x-search
11
+ # X Search
12
+
13
+ > Drop this in `~/.claude/skills/x-search/SKILL.md` and Claude Code becomes your X/Twitter intelligence tool.
14
+
15
+ ## Bootstrap (ALWAYS Run First)
16
+
17
+ Before any X search operation, run this bootstrap to ensure everything is set up:
18
+
19
+ ```bash
20
+ #!/bin/bash
21
+ set -e
22
+
23
+ # 1. Check if atris CLI is installed
24
+ if ! command -v atris &> /dev/null; then
25
+ echo "Installing atris CLI..."
26
+ npm install -g atris
27
+ fi
28
+
29
+ # 2. Check if logged in to AtrisOS
30
+ if [ ! -f ~/.atris/credentials.json ]; then
31
+ echo "Not logged in to AtrisOS."
32
+ echo ""
33
+ echo "Option 1 (interactive): Run 'atris login' and follow prompts"
34
+ echo "Option 2 (non-interactive): Get token from https://atris.ai/auth/cli"
35
+ echo " Then run: atris login --token YOUR_TOKEN"
36
+ echo ""
37
+ exit 1
38
+ fi
39
+
40
+ # 3. Extract token
41
+ if command -v node &> /dev/null; then
42
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
43
+ elif command -v python3 &> /dev/null; then
44
+ TOKEN=$(python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.atris/credentials.json')))['token'])")
45
+ elif command -v jq &> /dev/null; then
46
+ TOKEN=$(jq -r '.token' ~/.atris/credentials.json)
47
+ else
48
+ echo "Error: Need node, python3, or jq to read credentials"
49
+ exit 1
50
+ fi
51
+
52
+ # 4. Quick auth check
53
+ STATUS=$(curl -s "https://api.atris.ai/api/me" \
54
+ -H "Authorization: Bearer $TOKEN")
55
+
56
+ if echo "$STATUS" | grep -q "Token expired\|Not authenticated\|Unauthorized"; then
57
+ echo "Token expired. Please re-authenticate:"
58
+ echo " Run: atris login --force"
59
+ exit 1
60
+ fi
61
+
62
+ echo "Ready. X Search is available (5 credits per search)."
63
+ export ATRIS_TOKEN="$TOKEN"
64
+ ```
65
+
66
+ ---
12
67
 
13
- Search X/Twitter via xAI's Grok API. 5 credits per search action. Returns tweets with full text, author, engagement metrics, and citations.
68
+ ## API Reference
14
69
 
15
- ## Quick Search
70
+ Base: `https://api.atris.ai/api/x-search`
16
71
 
17
- ```python
18
- cd backend
19
- source ../venv/bin/activate
72
+ All requests require: `-H "Authorization: Bearer $TOKEN"`
73
+
74
+ ### Get Token (after bootstrap)
75
+ ```bash
76
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
20
77
  ```
21
78
 
22
- ```python
23
- import asyncio
24
- from dotenv import load_dotenv
25
- load_dotenv('.env')
79
+ ### Search X/Twitter
80
+ ```bash
81
+ curl -s -X POST "https://api.atris.ai/api/x-search/search" \
82
+ -H "Authorization: Bearer $TOKEN" \
83
+ -H "Content-Type: application/json" \
84
+ -d '{
85
+ "query": "\"CRM is dead\" OR \"Salesforce alternative\"",
86
+ "limit": 10
87
+ }'
88
+ ```
26
89
 
27
- from clients.xai import XAIClient, bill_xai_action
90
+ **With date filter** (last N days only):
91
+ ```bash
92
+ curl -s -X POST "https://api.atris.ai/api/x-search/search" \
93
+ -H "Authorization: Bearer $TOKEN" \
94
+ -H "Content-Type: application/json" \
95
+ -d '{
96
+ "query": "AI agents replacing SaaS",
97
+ "limit": 10,
98
+ "days_back": 7
99
+ }'
100
+ ```
28
101
 
29
- # 1. Bill the user (5 credits)
30
- bill = await bill_xai_action(USER_ID, "x_search")
31
- if not bill["ok"]:
32
- print(f"Insufficient credits: {bill['error']}")
102
+ **Response:**
103
+ ```json
104
+ {
105
+ "status": "success",
106
+ "credits_used": 5,
107
+ "credits_remaining": 995,
108
+ "data": {
109
+ "content": "1. @levelsio: AI agents are replacing...",
110
+ "citations": ["https://x.com/levelsio/status/..."],
111
+ "usage": {"prompt_tokens": 200, "completion_tokens": 800}
112
+ }
113
+ }
114
+ ```
33
115
 
34
- # 2. Search
35
- client = XAIClient()
36
- result = await client.search_x('"CRM is dead" OR "Salesforce is dead"', limit=10)
37
- print(result["content"])
38
- print(f"Usage: {result['usage']}")
116
+ ### Research a Person
117
+ ```bash
118
+ curl -s -X POST "https://api.atris.ai/api/x-search/research-person" \
119
+ -H "Authorization: Bearer $TOKEN" \
120
+ -H "Content-Type: application/json" \
121
+ -d '{
122
+ "name": "Leah Bonvissuto",
123
+ "handle": "leahbon",
124
+ "company": "Presentr",
125
+ "context": "Interested in revenue intelligence and AI for GTM"
126
+ }'
39
127
  ```
40
128
 
41
- ## Date-Constrained Search (last N days)
129
+ **Response:**
130
+ ```json
131
+ {
132
+ "status": "success",
133
+ "credits_used": 5,
134
+ "credits_remaining": 990,
135
+ "data": {
136
+ "content": "### 1. Profile\n**Name:** Leah Bonvissuto\n...",
137
+ "citations": ["https://x.com/..."],
138
+ "usage": {"prompt_tokens": 300, "completion_tokens": 1200}
139
+ }
140
+ }
141
+ ```
42
142
 
43
- ```python
44
- from xai_sdk.chat import user as xai_user
143
+ ---
45
144
 
46
- client = XAIClient()
47
- tools = client._get_tools()
145
+ ## Workflows
146
+
147
+ ### "Search X for tweets about a topic"
148
+ 1. Run bootstrap
149
+ 2. Search: `POST /x-search/search` with `{query, limit}`
150
+ 3. Display results: tweet text, author, engagement, links
151
+
152
+ ### "Find tweets from the last week about X"
153
+ 1. Run bootstrap
154
+ 2. Search with date filter: `POST /x-search/search` with `{query, limit, days_back: 7}`
155
+ 3. Display results sorted by engagement
156
+
157
+ ### "Research a person before a meeting"
158
+ 1. Run bootstrap
159
+ 2. Research: `POST /x-search/research-person` with `{name, handle, company, context}`
160
+ 3. Display profile, background, talking points
161
+
162
+ ### "Monitor keyword clusters for revenue intel"
163
+ 1. Run bootstrap
164
+ 2. Run multiple searches across keyword clusters:
165
+ - `"CRM is dead" OR "Salesforce is dead" OR "HubSpot sucks"`
166
+ - `"revenue operations" (broken OR frustrated OR replacing)`
167
+ - `(founder OR CEO) "tech stack" (consolidating OR ripping out)`
168
+ 3. Each search costs 5 credits
169
+ 4. Combine results, rank by engagement, draft replies
170
+
171
+ ### "Find viral tweets in my industry"
172
+ 1. Run bootstrap
173
+ 2. Search with engagement filter: `POST /x-search/search` with query including `min_faves:50`
174
+ 3. Display top tweets sorted by likes/retweets
48
175
 
49
- chat = client._get_client().chat.create(
50
- model="grok-4-1-fast",
51
- tools=[tools.x_search()],
52
- )
176
+ ---
53
177
 
54
- chat.append(xai_user(f"""Search X/Twitter for: "your query here"
178
+ ## Query Tips
55
179
 
56
- IMPORTANT: Only return tweets posted within the last 7 days.
57
- Return the 10 most relevant posts with:
58
- - Full tweet text (quoted exactly)
59
- - Author handle
60
- - Exact date/time
61
- - Engagement (likes, retweets, views)
180
+ | Goal | Query Example |
181
+ |------|--------------|
182
+ | Specific phrase | `"revenue operations"` |
183
+ | OR logic | `"CRM is dead" OR "Salesforce alternative"` |
184
+ | From a user | `from:levelsio` |
185
+ | High engagement | `"AI agents" min_faves:50` |
186
+ | Exclude retweets | `"your query" -is:retweet` |
187
+ | Multiple keywords | `(founder OR CEO) ("AI adoption" OR "AI native")` |
62
188
 
63
- Prioritize posts with high engagement (likes > 5, views > 1000)."""))
189
+ ---
64
190
 
65
- content = ""
66
- for response, chunk in chat.stream():
67
- if chunk.content:
68
- content += chunk.content
69
- print(content)
70
- ```
191
+ ## Billing
71
192
 
72
- ## Revenue Intel Digest (multi-cluster)
193
+ - Every search costs **5 credits** (flat)
194
+ - 1 credit = $0.01, so 1 search = $0.05
195
+ - Research person also costs 5 credits
196
+ - Credits are deducted server-side before the search runs
197
+ - If insufficient credits, returns `402 Insufficient credits`
73
198
 
74
- ```python
75
- from atris.team.gtm.skills.revenue_intel import run_digest, format_for_slack
199
+ ---
76
200
 
77
- # Runs 5 keyword clusters, drafts replies for top 5 items
78
- # Cost: 5 credits per cluster = 25 credits total
79
- result = await run_digest(
80
- days_back=1,
81
- user_id=USER_ID,
82
- draft_count=5,
83
- )
201
+ ## Error Handling
84
202
 
85
- print(result["digest"]) # Ranked results by cluster
86
- print(result["drafts"]) # Reply options A/B per item
87
- print(result["credits_used"])
203
+ | Error | Meaning | Solution |
204
+ |-------|---------|----------|
205
+ | `401 Not authenticated` | Invalid/expired token | Run `atris login` |
206
+ | `402 Insufficient credits` | Not enough credits | Purchase credits at atris.ai |
207
+ | `502 Search failed` | xAI API issue | Retry in a few seconds |
88
208
 
89
- # Format for Slack delivery
90
- slack_msg = await format_for_slack(result)
91
- ```
209
+ ---
92
210
 
93
- ## Custom Keyword Clusters
211
+ ## Quick Reference
94
212
 
95
- ```python
96
- my_clusters = {
97
- "competitors": '"Competitor Name" (review OR complaint OR switching OR alternative)',
98
- "industry_pain": '"your industry" (frustrated OR broken OR replacing OR ripping out)',
99
- "thought_leaders": '(founder OR CEO OR CTO) ("your topic" OR "related topic") min_faves:10',
100
- }
213
+ ```bash
214
+ # Setup (one time)
215
+ npm install -g atris && atris login
101
216
 
102
- result = await run_digest(clusters=my_clusters, user_id=USER_ID, days_back=7)
103
- ```
217
+ # Get token
218
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
104
219
 
105
- ## Billing
220
+ # Search tweets
221
+ curl -s -X POST "https://api.atris.ai/api/x-search/search" \
222
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
223
+ -d '{"query": "AI agents", "limit": 10}'
106
224
 
107
- 1. Every xAI action costs **5 credits** (flat)
108
- 2. 1 credit = $0.01, so 1 search = $0.05
109
- 3. `bill_xai_action(user_id, action_name)` checks balance and deducts before the call
110
- 4. Returns `{"ok": False, "error": "Insufficient credits"}` if balance < 5
111
- 5. Transaction logged as `txn_type="xai_action"` in `credit_transactions`
112
-
113
- | Plan | Monthly Credits | Searches/Month |
114
- |------|----------------|----------------|
115
- | Free | 50 | 10 |
116
- | Pro ($20/mo) | 1,000 | 200 |
117
- | Max ($200/mo) | unlimited | unlimited |
118
- | Credit pack ($10) | 1,000 | 200 |
119
-
120
- ## Cost Breakdown
121
-
122
- | Component | Cost to us | What it does |
123
- |-----------|-----------|--------------|
124
- | xAI x_search tool | $0.005/call | Searches X, returns batch of tweets |
125
- | grok-4-1-fast tokens | ~$0.003/call | Processes and formats results |
126
- | Total per search | ~$0.008 | Raw cost |
127
- | We charge | $0.05 (5 credits) | 84% margin |
128
-
129
- ## Key Files
130
-
131
- | File | What |
132
- |------|------|
133
- | `backend/clients/xai.py` | XAIClient + bill_xai_action() |
134
- | `atris/team/gtm/skills/revenue_intel.py` | Multi-cluster digest pipeline |
135
- | `backend/tools/core/reply_guy_tool.py` | Reply generation tool (bills 5 credits) |
136
- | `backend/tools/integrations/research_tweet_tool.py` | Research + tweet tool (bills 5 credits) |
137
-
138
- ## Rules
139
-
140
- 1. Always bill before searching — never search without `bill_xai_action()`
141
- 2. Date filtering is prompt-based, not API-level — include explicit date cutoff in prompt
142
- 3. Use `min_faves:N` in queries to filter low-engagement noise
143
- 4. Never auto-post tweets — always require user approval
144
- 5. Internal usage (no user_id) skips billing — platform absorbs cost
225
+ # Search last 7 days only
226
+ curl -s -X POST "https://api.atris.ai/api/x-search/search" \
227
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
228
+ -d '{"query": "AI agents", "limit": 10, "days_back": 7}'
229
+
230
+ # Research a person
231
+ curl -s -X POST "https://api.atris.ai/api/x-search/research-person" \
232
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
233
+ -d '{"name": "John Doe", "handle": "johndoe", "company": "Acme"}'
234
+ ```
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: template-member
3
+ role: Replace with role title
4
+ description: Replace with one-line description of what this member does.
5
+ version: 1.0.0
6
+
7
+ skills: []
8
+
9
+ permissions:
10
+ can-read: true
11
+ approval-required: []
12
+
13
+ tools: []
14
+ ---
15
+
16
+ # Insert persona, workflow, and rules below
@@ -4,13 +4,18 @@ role: Idea Shaper
4
4
  description: Shape ideas, explore possibilities, adapt to user depth
5
5
  version: 1.0.0
6
6
 
7
- skills: []
7
+ skills:
8
+ - idea-shaping
9
+ - reality-mapping
8
10
 
9
11
  permissions:
10
12
  can-read: true
11
13
  can-plan: false
12
14
  can-execute: false
13
15
  can-approve: false
16
+ approval-required: []
17
+
18
+ tools: []
14
19
  ---
15
20
 
16
21
  # Brainstormer — Idea & Reality Shaper
@@ -4,13 +4,18 @@ role: Builder
4
4
  description: Execute from build specs, one step at a time
5
5
  version: 1.0.0
6
6
 
7
- skills: []
7
+ skills:
8
+ - code-writer
9
+ - test-runner
8
10
 
9
11
  permissions:
10
12
  can-read: true
11
13
  can-plan: false
12
14
  can-execute: true
13
15
  can-approve: false
16
+ approval-required: [delete, refactor-outside-scope]
17
+
18
+ tools: []
14
19
  ---
15
20
 
16
21
  # Executor — Builder
@@ -4,7 +4,9 @@ role: Closer
4
4
  description: Document, capture learnings, publish, celebrate
5
5
  version: 1.0.0
6
6
 
7
- skills: []
7
+ skills:
8
+ - doc-writer
9
+ - publish-helper
8
10
 
9
11
  permissions:
10
12
  can-read: true
@@ -12,6 +14,9 @@ permissions:
12
14
  can-execute: false
13
15
  can-approve: false
14
16
  can-ship: true
17
+ approval-required: []
18
+
19
+ tools: []
15
20
  ---
16
21
 
17
22
  # Launcher — The Closer
@@ -4,13 +4,18 @@ role: Planner
4
4
  description: Transform messy human intent into precise execution plans
5
5
  version: 1.0.0
6
6
 
7
- skills: []
7
+ skills:
8
+ - codebase-scout
9
+ - feature-spec
8
10
 
9
11
  permissions:
10
12
  can-read: true
11
13
  can-plan: true
12
14
  can-execute: false
13
15
  can-approve: false
16
+ approval-required: []
17
+
18
+ tools: []
14
19
  ---
15
20
 
16
21
  # Navigator — Planner
@@ -4,13 +4,18 @@ role: Deep Researcher
4
4
  description: Find ground truth on any topic — competitors, standards, technologies, markets
5
5
  version: 1.0.0
6
6
 
7
- skills: []
7
+ skills:
8
+ - deep-search
9
+ - source-verification
8
10
 
9
11
  permissions:
10
12
  can-read: true
11
13
  can-execute: false
12
14
  can-plan: false
13
15
  can-approve: false
16
+ approval-required: []
17
+
18
+ tools: []
14
19
  ---
15
20
 
16
21
  # Researcher — Deep Researcher
@@ -4,7 +4,9 @@ role: Reviewer
4
4
  description: Validate execution, run tests, ensure quality before shipping
5
5
  version: 1.0.0
6
6
 
7
- skills: []
7
+ skills:
8
+ - test-runner
9
+ - doc-updater
8
10
 
9
11
  permissions:
10
12
  can-read: true
@@ -12,6 +14,9 @@ permissions:
12
14
  can-execute: false
13
15
  can-approve: true
14
16
  can-ship: true
17
+ approval-required: []
18
+
19
+ tools: []
15
20
  ---
16
21
 
17
22
  # Validator — Reviewer
package/commands/init.js CHANGED
@@ -475,24 +475,53 @@ function initAtris() {
475
475
  });
476
476
 
477
477
 
478
- // Copy team members (MEMBER.md format — directory per member)
479
- const members = ['navigator', 'executor', 'validator', 'launcher', 'brainstormer'];
478
+ // Copy team members (MEMBER.md format — directory per member with skills/tools/context)
479
+ const members = ['navigator', 'executor', 'validator', 'launcher', 'brainstormer', 'researcher'];
480
480
  members.forEach(name => {
481
481
  const sourceFile = path.join(__dirname, '..', 'atris', 'team', name, 'MEMBER.md');
482
- const targetDir = path.join(teamDir, name);
483
- const targetFile = path.join(targetDir, 'MEMBER.md');
482
+ const targetMemberDir = path.join(teamDir, name);
483
+ const targetFile = path.join(targetMemberDir, 'MEMBER.md');
484
484
  const legacyFile = path.join(teamDir, `${name}.md`);
485
485
 
486
486
  // Skip if already exists (either format)
487
487
  if (fs.existsSync(targetFile) || fs.existsSync(legacyFile)) return;
488
488
 
489
489
  if (fs.existsSync(sourceFile)) {
490
- fs.mkdirSync(targetDir, { recursive: true });
490
+ fs.mkdirSync(targetMemberDir, { recursive: true });
491
+ fs.mkdirSync(path.join(targetMemberDir, 'skills'), { recursive: true });
492
+ fs.mkdirSync(path.join(targetMemberDir, 'tools'), { recursive: true });
493
+ fs.mkdirSync(path.join(targetMemberDir, 'context'), { recursive: true });
491
494
  fs.copyFileSync(sourceFile, targetFile);
492
- console.log(`✓ Created team/${name}/MEMBER.md`);
495
+ console.log(`✓ Created team/${name}/ (MEMBER.md + skills/ + tools/ + context/)`);
493
496
  }
494
497
  });
495
498
 
499
+ // Copy MEMBER.md template for creating custom members
500
+ const templateSourceDir = path.join(__dirname, '..', 'atris', 'team', '_template');
501
+ const templateTargetDir = path.join(teamDir, '_template');
502
+ if (!fs.existsSync(templateTargetDir)) {
503
+ fs.mkdirSync(templateTargetDir, { recursive: true });
504
+ const templateContent = `---
505
+ name: template-member
506
+ role: Replace with role title
507
+ description: Replace with one-line description of what this member does.
508
+ version: 1.0.0
509
+
510
+ skills: []
511
+
512
+ permissions:
513
+ can-read: true
514
+ approval-required: []
515
+
516
+ tools: []
517
+ ---
518
+
519
+ # Insert persona, workflow, and rules below
520
+ `;
521
+ fs.writeFileSync(path.join(templateTargetDir, 'MEMBER.md'), templateContent);
522
+ console.log('✓ Created team/_template/MEMBER.md');
523
+ }
524
+
496
525
  // Detect project context and generate profile
497
526
  const profile = detectProjectContext(process.cwd());
498
527
  const profileFile = path.join(targetDir, '.project-profile.json');
@@ -233,6 +233,9 @@ skills: []
233
233
 
234
234
  permissions:
235
235
  can-read: true
236
+ approval-required: []
237
+
238
+ tools: []
236
239
  ---
237
240
 
238
241
  # ${role}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "2.3.7",
3
+ "version": "2.4.0",
4
4
  "description": "atrisDev (atris dev) - CLI for AI coding agents. Works with Claude Code, Cursor, Windsurf. Make any codebase AI-navigable.",
5
5
  "main": "bin/atris.js",
6
6
  "bin": {