argustack 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -398
- package/dist/adapters/db/client.d.ts +13 -0
- package/dist/adapters/db/client.d.ts.map +1 -0
- package/dist/adapters/db/client.js +70 -0
- package/dist/adapters/db/client.js.map +1 -0
- package/dist/adapters/db/index.d.ts +4 -0
- package/dist/adapters/db/index.d.ts.map +1 -0
- package/dist/adapters/db/index.js +3 -0
- package/dist/adapters/db/index.js.map +1 -0
- package/dist/adapters/db/mapper.d.ts +31 -0
- package/dist/adapters/db/mapper.d.ts.map +1 -0
- package/dist/adapters/db/mapper.js +55 -0
- package/dist/adapters/db/mapper.js.map +1 -0
- package/dist/adapters/db/provider.d.ts +24 -0
- package/dist/adapters/db/provider.d.ts.map +1 -0
- package/dist/adapters/db/provider.js +159 -0
- package/dist/adapters/db/provider.js.map +1 -0
- package/dist/adapters/db/sql-validator.d.ts +10 -0
- package/dist/adapters/db/sql-validator.d.ts.map +1 -0
- package/dist/adapters/db/sql-validator.js +41 -0
- package/dist/adapters/db/sql-validator.js.map +1 -0
- package/dist/adapters/postgres/schema.d.ts.map +1 -1
- package/dist/adapters/postgres/schema.js +55 -0
- package/dist/adapters/postgres/schema.js.map +1 -1
- package/dist/adapters/postgres/storage.d.ts +3 -0
- package/dist/adapters/postgres/storage.d.ts.map +1 -1
- package/dist/adapters/postgres/storage.js +51 -0
- package/dist/adapters/postgres/storage.js.map +1 -1
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/generators.d.ts.map +1 -1
- package/dist/cli/init/generators.js +1 -1
- package/dist/cli/init/generators.js.map +1 -1
- package/dist/cli/init/index.d.ts.map +1 -1
- package/dist/cli/init/index.js +13 -2
- package/dist/cli/init/index.js.map +1 -1
- package/dist/cli/init/setup-db.d.ts.map +1 -1
- package/dist/cli/init/setup-db.js +128 -12
- package/dist/cli/init/setup-db.js.map +1 -1
- package/dist/cli/init/setup-git.d.ts.map +1 -1
- package/dist/cli/init/setup-git.js +5 -1
- package/dist/cli/init/setup-git.js.map +1 -1
- package/dist/cli/init/setup-jira.d.ts.map +1 -1
- package/dist/cli/init/setup-jira.js +5 -2
- package/dist/cli/init/setup-jira.js.map +1 -1
- package/dist/cli/init/types.d.ts +2 -0
- package/dist/cli/init/types.d.ts.map +1 -1
- package/dist/cli/init/types.js.map +1 -1
- package/dist/cli/sync.d.ts +1 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +48 -1
- package/dist/cli/sync.js.map +1 -1
- package/dist/core/ports/db-provider.d.ts +33 -0
- package/dist/core/ports/db-provider.d.ts.map +1 -0
- package/dist/core/ports/db-provider.js +2 -0
- package/dist/core/ports/db-provider.js.map +1 -0
- package/dist/core/ports/index.d.ts +1 -0
- package/dist/core/ports/index.d.ts.map +1 -1
- package/dist/core/ports/storage.d.ts +5 -0
- package/dist/core/ports/storage.d.ts.map +1 -1
- package/dist/core/types/config.d.ts.map +1 -1
- package/dist/core/types/config.js +5 -2
- package/dist/core/types/config.js.map +1 -1
- package/dist/core/types/database.d.ts +37 -0
- package/dist/core/types/database.d.ts.map +1 -0
- package/dist/core/types/database.js +2 -0
- package/dist/core/types/database.js.map +1 -0
- package/dist/core/types/index.d.ts +1 -0
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/database.d.ts +4 -0
- package/dist/mcp/tools/database.d.ts.map +1 -0
- package/dist/mcp/tools/database.js +254 -0
- package/dist/mcp/tools/database.js.map +1 -0
- package/dist/mcp/tools/estimate.d.ts +10 -0
- package/dist/mcp/tools/estimate.d.ts.map +1 -1
- package/dist/mcp/tools/estimate.js +3 -3
- package/dist/mcp/tools/estimate.js.map +1 -1
- package/dist/use-cases/pull-db.d.ts +25 -0
- package/dist/use-cases/pull-db.d.ts.map +1 -0
- package/dist/use-cases/pull-db.js +59 -0
- package/dist/use-cases/pull-db.js.map +1 -0
- package/package.json +2 -1
- package/dist/cli/init.d.ts +0 -24
- package/dist/cli/init.d.ts.map +0 -1
- package/dist/cli/init.js +0 -957
- package/dist/cli/init.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,445 +1,105 @@
|
|
|
1
1
|
# Argustack
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/argustack)
|
|
4
|
+
[](https://www.npmjs.com/package/argustack)
|
|
5
|
+
[](LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
**Ask AI about your Jira, Git, and GitHub — powered by local data, not cloud APIs.**
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
2. **Augmentation** — inject relevant context into the AI prompt
|
|
9
|
-
3. **Generation** — LLM generates answers grounded in your actual Jira tickets, code, and databases
|
|
9
|
+
Argustack downloads your project data into local PostgreSQL, cross-references everything, and gives Claude direct access via 15 MCP tools. All data stays on your machine.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
> *Was ticket PROJ-123 implemented as described?*
|
|
12
|
+
> *Who reviewed the PR and what was the feedback?*
|
|
13
|
+
> *How long will it take Sarah to fix this bug?*
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
YOUR SOURCES YOUR MACHINE
|
|
15
|
-
┌──────────────────────┐ ┌─────────────────────────────────────┐
|
|
16
|
-
│ Jira Cloud / Server │ │ │
|
|
17
|
-
│ (issues, comments, │ pull │ PostgreSQL (Docker, localhost) │
|
|
18
|
-
│ changelogs, etc.) │ ──────►│ ├── issues (all fields) │
|
|
19
|
-
└──────────────────────┘ │ ├── issue_comments (discussions) │
|
|
20
|
-
│ ├── issue_changelogs (history) │
|
|
21
|
-
┌──────────────────────┐ │ ├── issue_worklogs (time logs) │
|
|
22
|
-
│ Git repository │ pull │ ├── issue_links (relations) │
|
|
23
|
-
│ (commits, files, │ ──────►│ ├── commits (history) │
|
|
24
|
-
│ diffs, authors) │ │ ├── commit_files (per-file Δ) │
|
|
25
|
-
└──────────────────────┘ │ ├── commit_issue_refs (cross-ref)│
|
|
26
|
-
│ │ │
|
|
27
|
-
┌──────────────────────┐ │ │ GitHub (optional, via token) │
|
|
28
|
-
│ GitHub API │ pull │ ├── pull_requests (PRs + meta) │
|
|
29
|
-
│ (PRs, reviews, │ ──────►│ ├── pr_reviews (approvals) │
|
|
30
|
-
│ releases) │ │ ├── pr_comments (discussions) │
|
|
31
|
-
└──────────────────────┘ │ ├── pr_issue_refs (PR↔Jira) │
|
|
32
|
-
│ └── releases (tags) │
|
|
33
|
-
┌──────────────────────┐ │ │
|
|
34
|
-
│ Database (planned) │ │ MCP Server (localhost, stdio) │
|
|
35
|
-
└──────────────────────┘ │ └── queries DB ──► Claude / LLM │
|
|
36
|
-
│ │
|
|
37
|
-
│ .env (credentials — never leaves) │
|
|
38
|
-
└─────────────────────────────────────┘
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
> *Is this bug still relevant or already fixed in code?*
|
|
42
|
-
> *Was the feature implemented as described in the ticket?*
|
|
43
|
-
> *Who worked on this module and what changed last month?*
|
|
44
|
-
> *Which commits and PRs reference ticket PROJ-123?*
|
|
45
|
-
> *Who approved the PR and what was the review feedback?*
|
|
46
|
-
|
|
47
|
-
## How it works
|
|
15
|
+
## Features
|
|
48
16
|
|
|
49
|
-
**
|
|
50
|
-
|
|
51
|
-
**
|
|
52
|
-
|
|
53
|
-
**
|
|
17
|
+
- **Jira** — issues, comments, changelogs, worklogs, links, all custom fields
|
|
18
|
+
- **Git** — commits, per-file diffs, automatic issue cross-references
|
|
19
|
+
- **GitHub** — PRs, reviews, comments, releases, automatic issue cross-references
|
|
20
|
+
- **CSV import** — Jira CSV export for teams without API access
|
|
21
|
+
- **Cross-source timeline** — Jira + Git + GitHub events in chronological order
|
|
22
|
+
- **Semantic search** — find issues by meaning, not just keywords (pgvector)
|
|
23
|
+
- **Task estimation** — predict duration per developer based on actual history
|
|
24
|
+
- **15 MCP tools** — Claude queries your data directly via SQL
|
|
25
|
+
- **100% local** — no cloud, no accounts, no telemetry
|
|
54
26
|
|
|
55
27
|
## Quick Start
|
|
56
28
|
|
|
57
|
-
### Prerequisites
|
|
58
|
-
|
|
59
|
-
- [Node.js](https://nodejs.org/) >= 20
|
|
60
|
-
- [Docker](https://www.docker.com/) (or [OrbStack](https://orbstack.dev/) / Podman)
|
|
61
|
-
|
|
62
|
-
### Install & setup
|
|
63
|
-
|
|
64
29
|
```bash
|
|
65
30
|
npm i -g argustack
|
|
66
|
-
argustack init
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
Argustack supports **two modes** of initialization:
|
|
70
|
-
|
|
71
|
-
#### Interactive mode (default)
|
|
72
|
-
|
|
73
|
-
Run `argustack init` and follow the prompts:
|
|
74
|
-
|
|
31
|
+
argustack init # interactive setup — sources, credentials, Docker
|
|
32
|
+
argustack sync # pull all data
|
|
33
|
+
argustack mcp install # connect to Claude Desktop
|
|
75
34
|
```
|
|
76
|
-
? Workspace directory: ~/projects/my-team
|
|
77
|
-
? Sources: ✔ Jira — issues, comments, changelogs
|
|
78
|
-
✔ Git — commits, diffs, authors
|
|
79
|
-
✔ GitHub — PRs, reviews, releases
|
|
80
|
-
? Jira URL: https://your-team.atlassian.net
|
|
81
|
-
? Email: you@company.com
|
|
82
|
-
? API Token: ****
|
|
83
|
-
|
|
84
|
-
Testing connection... Connected! Found 3 projects: MKT, BRAND, WEB
|
|
85
|
-
|
|
86
|
-
? Projects to pull: ✔ MKT ✔ BRAND
|
|
87
|
-
? Where is your Git repository?
|
|
88
|
-
● Clone from GitHub — select repos using your token
|
|
89
|
-
? GitHub token (PAT): ****
|
|
90
|
-
|
|
91
|
-
Fetching repositories... Found 12 repos.
|
|
92
35
|
|
|
93
|
-
|
|
94
|
-
? GitHub source: Auto-configured from clone step
|
|
95
|
-
|
|
96
|
-
? Start database and sync now? Yes
|
|
97
|
-
|
|
98
|
-
✔ Database running!
|
|
99
|
-
✔ Jira sync complete!
|
|
100
|
-
MKT: 506 issues (150/506 ████░░░░ 30% → 506/506 done)
|
|
101
|
-
✔ Git sync complete!
|
|
102
|
-
your-org/frontend: 735 commits (400/735 ██████░░ 54% → 735/735 done)
|
|
103
|
-
✔ GitHub sync complete!
|
|
104
|
-
66 PRs (30/66 ██████░░ 45% → 66/66 done), 124 reviews, 3 releases
|
|
105
|
-
```
|
|
36
|
+
That's it. Ask Claude about your project.
|
|
106
37
|
|
|
107
|
-
|
|
38
|
+
### Non-interactive mode
|
|
108
39
|
|
|
109
|
-
|
|
110
|
-
2. Collect credentials and test connections
|
|
111
|
-
3. For Git — choose local path, clone from GitHub (multi-select repos), or clone from URL
|
|
112
|
-
4. For GitHub — auto-configured if you cloned from GitHub in the previous step, or enter a Personal Access Token (see [GitHub token setup](#github-token-setup))
|
|
113
|
-
5. Create a workspace with Docker config
|
|
114
|
-
6. Start PostgreSQL + pgweb automatically
|
|
115
|
-
7. Pull all your data
|
|
116
|
-
|
|
117
|
-
#### Non-interactive mode (for AI agents and CI/CD)
|
|
118
|
-
|
|
119
|
-
Pass `--no-interactive` with all values as CLI flags — no prompts, no terminal needed:
|
|
40
|
+
For AI agents and CI/CD — pass everything as flags:
|
|
120
41
|
|
|
121
42
|
```bash
|
|
122
|
-
argustack init \
|
|
123
|
-
--no-interactive \
|
|
124
|
-
--dir ~/projects/my-team \
|
|
43
|
+
argustack init --no-interactive \
|
|
125
44
|
--source jira,git,github \
|
|
126
45
|
--jira-url "https://your-team.atlassian.net" \
|
|
127
46
|
--jira-email "you@company.com" \
|
|
128
47
|
--jira-token "ATATT3x..." \
|
|
129
|
-
--jira-projects
|
|
130
|
-
--git-repo /path/to/
|
|
48
|
+
--jira-projects PROJ,MKT \
|
|
49
|
+
--git-repo /path/to/repo \
|
|
131
50
|
--github-token "github_pat_..." \
|
|
132
51
|
--github-owner your-org \
|
|
133
52
|
--github-repo your-repo
|
|
134
53
|
```
|
|
135
54
|
|
|
136
|
-
Then start the database and sync:
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
cd ~/projects/my-team
|
|
140
|
-
docker compose up -d
|
|
141
|
-
argustack sync
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
All available flags:
|
|
145
|
-
|
|
146
|
-
| Flag | Description |
|
|
147
|
-
|------|-------------|
|
|
148
|
-
| `--no-interactive` | Run without prompts — all values from flags |
|
|
149
|
-
| `-d, --dir <path>` | Workspace directory (default: current) |
|
|
150
|
-
| `-s, --source <list>` | Comma-separated: `jira,git,github,csv,db` |
|
|
151
|
-
| `--jira-url <url>` | Jira instance URL |
|
|
152
|
-
| `--jira-email <email>` | Jira user email |
|
|
153
|
-
| `--jira-token <token>` | Jira API token |
|
|
154
|
-
| `--jira-projects <keys>` | Comma-separated project keys, or `all` |
|
|
155
|
-
| `--git-repo <paths>` | Git repo paths, comma-separated |
|
|
156
|
-
| `--github-token <token>` | GitHub Personal Access Token |
|
|
157
|
-
| `--github-owner <owner>` | GitHub repo owner |
|
|
158
|
-
| `--github-repo <repo>` | GitHub repo name |
|
|
159
|
-
| `--csv-file <path>` | Path to Jira CSV export file |
|
|
160
|
-
| `--db-port <port>` | Argustack PostgreSQL port (default: `5434`) |
|
|
161
|
-
| `--pgweb-port <port>` | pgweb UI port (default: `8086`) |
|
|
162
|
-
|
|
163
|
-
This mode is ideal for:
|
|
164
|
-
- **AI agents** — a project manager tells their AI agent "set up argustack" and it runs everything autonomously
|
|
165
|
-
- **CI/CD pipelines** — automated workspace provisioning
|
|
166
|
-
- **Scripted setups** — reproducible environment creation
|
|
167
|
-
|
|
168
|
-
Browse your data at [localhost:8086](http://localhost:8086) — pgweb UI for running SQL queries and exploring tables in your browser.
|
|
169
|
-
|
|
170
|
-
### Connect to Claude
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
argustack mcp install
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Adds Argustack as an MCP server to Claude Desktop. Now you can ask Claude questions about your project data directly.
|
|
177
|
-
|
|
178
55
|
## Commands
|
|
179
56
|
|
|
180
57
|
```bash
|
|
181
|
-
argustack init # create workspace
|
|
182
|
-
argustack
|
|
183
|
-
argustack sync
|
|
184
|
-
argustack sync
|
|
185
|
-
argustack
|
|
186
|
-
argustack
|
|
187
|
-
argustack sync csv # import from Jira CSV export
|
|
188
|
-
argustack sync csv -f /path/to.csv # import specific CSV file
|
|
189
|
-
argustack sync -p PROJ # pull specific Jira project
|
|
190
|
-
argustack sync --since 2025-01-01 # incremental pull (only new/updated)
|
|
58
|
+
argustack init # create workspace
|
|
59
|
+
argustack sync # pull all sources
|
|
60
|
+
argustack sync jira|git|github|csv # pull specific source
|
|
61
|
+
argustack sync --since 2025-01-01 # incremental pull
|
|
62
|
+
argustack embed # generate embeddings (requires OpenAI key)
|
|
63
|
+
argustack mcp install # connect to Claude Desktop
|
|
191
64
|
argustack sources # list configured sources
|
|
192
65
|
argustack status # workspace info
|
|
193
|
-
argustack embed # generate embeddings for semantic search
|
|
194
|
-
argustack mcp install # connect to Claude Desktop
|
|
195
66
|
```
|
|
196
67
|
|
|
197
|
-
## What gets stored
|
|
198
|
-
|
|
199
|
-
All data goes into local PostgreSQL in Docker on your machine (nothing leaves `localhost`):
|
|
200
|
-
|
|
201
|
-
### Jira tables
|
|
202
|
-
|
|
203
|
-
| Table | Content |
|
|
204
|
-
|-------|---------|
|
|
205
|
-
| `issues` | All issues — typed columns + `custom_fields` JSONB + full `raw_json` |
|
|
206
|
-
| `issue_comments` | Comments with authors and timestamps |
|
|
207
|
-
| `issue_changelogs` | Every field change in history |
|
|
208
|
-
| `issue_worklogs` | Time tracking entries |
|
|
209
|
-
| `issue_links` | Issue-to-issue relationships |
|
|
210
|
-
|
|
211
|
-
Every custom field is preserved exactly as Jira returns it. 500 custom fields? All stored. Zero filtering, zero data loss.
|
|
212
|
-
|
|
213
|
-
### Git tables
|
|
214
|
-
|
|
215
|
-
| Table | Content |
|
|
216
|
-
|-------|---------|
|
|
217
|
-
| `commits` | Commit hash, message, author, email, date, full-text search |
|
|
218
|
-
| `commit_files` | Per-file changes — path, status, additions, deletions |
|
|
219
|
-
| `commit_issue_refs` | Cross-reference: commit ↔ Jira issue (extracted from commit messages) |
|
|
220
|
-
|
|
221
|
-
Commit messages mentioning issue keys like `PROJ-123` or `PROJ-45` are automatically linked to Jira issues.
|
|
222
|
-
|
|
223
|
-
### GitHub tables
|
|
224
|
-
|
|
225
|
-
Select "GitHub" during `argustack init` or add later with `argustack source add github`:
|
|
226
|
-
|
|
227
|
-
| Table | Content |
|
|
228
|
-
|-------|---------|
|
|
229
|
-
| `pull_requests` | PRs — state, author, reviewers, additions/deletions, merge info, full-text search |
|
|
230
|
-
| `pr_reviews` | Review approvals and change requests |
|
|
231
|
-
| `pr_comments` | Inline review comments with file paths and line numbers |
|
|
232
|
-
| `pr_files` | Per-file changes in each PR |
|
|
233
|
-
| `pr_issue_refs` | Cross-reference: PR ↔ Jira issue (extracted from PR title and body) |
|
|
234
|
-
| `releases` | GitHub releases with tags, notes, and full-text search |
|
|
235
|
-
|
|
236
|
-
PR titles and bodies mentioning issue keys like `PROJ-123` are automatically linked to Jira issues — just like commits.
|
|
237
|
-
|
|
238
68
|
## MCP Tools
|
|
239
69
|
|
|
240
|
-
|
|
70
|
+
After sync, Claude queries your data through these tools:
|
|
241
71
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
|
245
|
-
|
|
246
|
-
| `
|
|
247
|
-
| `get_issue` | Full issue details with comments, changelogs, custom fields |
|
|
248
|
-
| `issue_stats` | Aggregate stats — by status, type, assignee, project |
|
|
72
|
+
| Tool | Purpose |
|
|
73
|
+
|------|---------|
|
|
74
|
+
| `query_issues` | Search issues — full-text, filters, raw SQL |
|
|
75
|
+
| `get_issue` | Full issue details with comments and changelogs |
|
|
76
|
+
| `issue_stats` | Aggregates by status, type, assignee |
|
|
249
77
|
| `pull_jira` | Sync latest data from Jira |
|
|
250
|
-
| `list_projects` |
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
|
255
|
-
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
258
|
-
| `
|
|
259
|
-
|
|
260
|
-
### GitHub tools
|
|
261
|
-
|
|
262
|
-
| Tool | What it does |
|
|
263
|
-
|------|-------------|
|
|
264
|
-
| `query_prs` | Search PRs — full-text, state, author, base branch, or raw SQL |
|
|
265
|
-
| `issue_prs` | Cross-reference: find all PRs mentioning a Jira issue key with reviews |
|
|
266
|
-
| `query_releases` | List releases with full-text search |
|
|
267
|
-
|
|
268
|
-
### Cross-source tools
|
|
269
|
-
|
|
270
|
-
| Tool | What it does |
|
|
271
|
-
|------|-------------|
|
|
272
|
-
| `issue_timeline` | Chronological timeline — changelogs + commits + PRs for one issue |
|
|
273
|
-
| `semantic_search` | Find similar issues by meaning (pgvector embeddings) |
|
|
274
|
-
| `estimate` | Predict task duration per developer. With effort data: "without bugs" / "with bugs" predictions. With CSV only: resolution timeline in business days (lead time, not active dev) |
|
|
275
|
-
|
|
276
|
-
### System tools
|
|
277
|
-
|
|
278
|
-
| Tool | What it does |
|
|
279
|
-
|------|-------------|
|
|
78
|
+
| `list_projects` | Available Jira projects |
|
|
79
|
+
| `query_commits` | Search commits by text, author, date, file |
|
|
80
|
+
| `issue_commits` | All commits mentioning a Jira issue key |
|
|
81
|
+
| `commit_stats` | Top authors, most changed files |
|
|
82
|
+
| `query_prs` | Search PRs by text, state, author |
|
|
83
|
+
| `issue_prs` | All PRs mentioning a Jira issue key |
|
|
84
|
+
| `query_releases` | List releases with search |
|
|
85
|
+
| `issue_timeline` | Full chronological timeline: Jira + Git + GitHub |
|
|
86
|
+
| `semantic_search` | Find similar issues by meaning (pgvector) |
|
|
87
|
+
| `estimate` | Predict task duration per developer |
|
|
280
88
|
| `workspace_info` | Current workspace configuration |
|
|
281
89
|
|
|
282
|
-
##
|
|
283
|
-
|
|
284
|
-
**Embeddings** turn issue text into numerical vectors that capture meaning. Two issues about "login not working" and "SSO authentication fails" will have similar vectors — even though they share zero keywords.
|
|
285
|
-
|
|
286
|
-
How it works:
|
|
287
|
-
|
|
288
|
-
1. `argustack embed` sends each issue's `summary + description` to OpenAI API (`text-embedding-3-small` model)
|
|
289
|
-
2. Returns a 1536-dimensional vector per issue, stored in PostgreSQL via pgvector
|
|
290
|
-
3. `semantic_search` MCP tool embeds your question, then finds issues with the closest vectors using cosine similarity
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
# Generate embeddings for all issues (requires OPENAI_API_KEY in .env)
|
|
294
|
-
argustack embed
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
After embedding, ask Claude: *"Find issues similar to payment timeout errors"* — it will search by meaning, not keywords.
|
|
298
|
-
|
|
299
|
-
**Costs:** text-embedding-3-small costs ~$0.02 per 1M tokens. 10,000 issues ≈ $0.05-0.10.
|
|
300
|
-
|
|
301
|
-
**Optional:** Embeddings require an OpenAI API key. All other features work without it.
|
|
302
|
-
|
|
303
|
-
## GitHub token setup
|
|
304
|
-
|
|
305
|
-
Argustack only reads data from GitHub — it never writes anything. You need a **fine-grained Personal Access Token** with read-only permissions.
|
|
306
|
-
|
|
307
|
-
1. Go to [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new)
|
|
308
|
-
2. **Token name** — anything (e.g. `argustack`)
|
|
309
|
-
3. **Description** — optional, can leave empty
|
|
310
|
-
4. **Resource owner** — your account, or the organization that owns the repo
|
|
311
|
-
5. **Expiration** — "No expiration" recommended. Token is read-only and stays in your local `.env`
|
|
312
|
-
6. **Repository access** — pick one:
|
|
313
|
-
- **Only select repositories** (recommended) — pick specific repos, max 50
|
|
314
|
-
- **All repositories** — all your current and future repos
|
|
315
|
-
- **Public repositories** — read-only access to public repos only
|
|
316
|
-
5. **Permissions** → Repository permissions (3 total):
|
|
317
|
-
|
|
318
|
-
| Permission | Access | Why |
|
|
319
|
-
|---|---|---|
|
|
320
|
-
| **Contents** | Read-only | Releases, downloads, tags |
|
|
321
|
-
| **Metadata** | Read-only | Repository info (auto-selected, required) |
|
|
322
|
-
| **Pull requests** | Read-only | PRs, reviews, comments, files |
|
|
323
|
-
|
|
324
|
-
7. Click "Generate token" and copy it
|
|
325
|
-
|
|
326
|
-
During `argustack init`, select "GitHub" as a source and paste the token when asked. Or add to `.env` manually:
|
|
327
|
-
|
|
328
|
-
```bash
|
|
329
|
-
GITHUB_TOKEN=github_pat_...
|
|
330
|
-
GITHUB_OWNER=your-org
|
|
331
|
-
GITHUB_REPO=your-repo
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
Then run `argustack source add github` and `argustack sync github`.
|
|
335
|
-
|
|
336
|
-
## Multiple workspaces
|
|
337
|
-
|
|
338
|
-
Each data source = separate workspace (like git repos):
|
|
339
|
-
|
|
340
|
-
```
|
|
341
|
-
~/projects/
|
|
342
|
-
├── client-alpha/ # argustack init → Alpha's Jira + Git
|
|
343
|
-
│ ├── .argustack/
|
|
344
|
-
│ ├── .env # Alpha credentials
|
|
345
|
-
│ └── docker-compose.yml
|
|
346
|
-
│
|
|
347
|
-
├── client-beta/ # argustack init → Beta's Jira + Git
|
|
348
|
-
│ ├── .argustack/
|
|
349
|
-
│ ├── .env # Beta credentials
|
|
350
|
-
│ └── docker-compose.yml
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
## Security & Credentials
|
|
354
|
-
|
|
355
|
-
**Argustack is a CLI tool. It has no backend, no cloud, no accounts.** Everything runs on your machine.
|
|
356
|
-
|
|
357
|
-
When you run `argustack init`, it creates a `.env` file in your workspace with your credentials:
|
|
358
|
-
|
|
359
|
-
```bash
|
|
360
|
-
# .env — YOUR file, on YOUR machine, never uploaded anywhere
|
|
361
|
-
|
|
362
|
-
# === Jira ===
|
|
363
|
-
JIRA_URL=https://your-team.atlassian.net
|
|
364
|
-
JIRA_EMAIL=you@company.com
|
|
365
|
-
JIRA_API_TOKEN=your-api-token-here
|
|
366
|
-
JIRA_PROJECTS=PROJ,OTHER
|
|
367
|
-
|
|
368
|
-
# === Git ===
|
|
369
|
-
GIT_REPO_PATHS=/path/to/repo1,/path/to/repo2
|
|
370
|
-
|
|
371
|
-
# === GitHub ===
|
|
372
|
-
GITHUB_TOKEN=github_pat_...
|
|
373
|
-
GITHUB_OWNER=your-org
|
|
374
|
-
GITHUB_REPO=your-repo
|
|
375
|
-
|
|
376
|
-
# === Argustack internal PostgreSQL (match docker-compose.yml) ===
|
|
377
|
-
DB_HOST=localhost
|
|
378
|
-
DB_PORT=5434
|
|
379
|
-
DB_USER=argustack
|
|
380
|
-
DB_PASSWORD=argustack_local
|
|
381
|
-
DB_NAME=argustack
|
|
382
|
-
|
|
383
|
-
# === OpenAI embeddings (optional, for semantic search) ===
|
|
384
|
-
# OPENAI_API_KEY=sk-...
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
**Where credentials go:**
|
|
388
|
-
|
|
389
|
-
| What | Where | Who can see |
|
|
390
|
-
|------|-------|-------------|
|
|
391
|
-
| Jira token | `.env` on your disk | Only you |
|
|
392
|
-
| GitHub token | `.env` on your disk | Only you |
|
|
393
|
-
| Jira data | PostgreSQL in Docker on `localhost:5434` | Only you |
|
|
394
|
-
| Git + GitHub data | PostgreSQL in Docker on `localhost:5434` | Only you |
|
|
395
|
-
| Database password | `.env` on your disk | Only you |
|
|
396
|
-
| Source code (this repo) | GitHub | Everyone — **no secrets here** |
|
|
397
|
-
|
|
398
|
-
**What Argustack does NOT do:**
|
|
399
|
-
- Does not send your data to any external server
|
|
400
|
-
- Does not have analytics, telemetry, or tracking
|
|
401
|
-
- Does not store credentials anywhere except your local `.env`
|
|
402
|
-
- Does not require registration or accounts
|
|
403
|
-
|
|
404
|
-
**`.env` is in `.gitignore`** — if you accidentally run `git add .`, your credentials won't be committed.
|
|
405
|
-
|
|
406
|
-
## Architecture — Hexagonal (Ports & Adapters)
|
|
407
|
-
|
|
408
|
-
```
|
|
409
|
-
Driving adapters (entries): cli/, mcp/
|
|
410
|
-
Driven adapters (external systems): adapters/jira/, adapters/git/, adapters/github/, adapters/postgres/
|
|
411
|
-
|
|
412
|
-
Dependency Rule: cli/,mcp/ → use-cases/ → core/ports ← adapters/
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
Core has zero dependencies — only pure TypeScript types and interfaces. Adapters implement core ports and are independently replaceable.
|
|
90
|
+
## Security
|
|
416
91
|
|
|
417
|
-
|
|
92
|
+
Argustack is a CLI tool with no backend, no cloud, no accounts. Credentials stay in `.env` on your machine. Data stays in PostgreSQL on `localhost`. Nothing is uploaded anywhere.
|
|
418
93
|
|
|
419
|
-
|
|
420
|
-
- Commander.js — CLI (driving adapter)
|
|
421
|
-
- MCP SDK — Claude integration (driving adapter)
|
|
422
|
-
- jira.js — Jira REST API (driven adapter)
|
|
423
|
-
- Octokit — GitHub REST API (driven adapter)
|
|
424
|
-
- es-git — native Git bindings, N-API, powered by libgit2 (driven adapter)
|
|
425
|
-
- csv-parse — streaming RFC 4180 CSV parser (driven adapter)
|
|
426
|
-
- PostgreSQL 16 + pgvector — storage + vector search (driven adapter)
|
|
427
|
-
- Docker — database infrastructure
|
|
94
|
+
## Documentation
|
|
428
95
|
|
|
429
|
-
|
|
96
|
+
Full documentation available at **[Argustack DataRoom](https://app.paperlink.online/s/0aa7d2d6/argustack)**:
|
|
430
97
|
|
|
431
|
-
-
|
|
432
|
-
-
|
|
433
|
-
-
|
|
434
|
-
-
|
|
435
|
-
-
|
|
436
|
-
- [x] Cross-source timeline (issue_timeline: changelogs + commits + PRs)
|
|
437
|
-
- [x] Multi-repo Git support (multiple repos per workspace, `GIT_REPO_PATHS`)
|
|
438
|
-
- [x] Progress indicators during sync (e.g. `150/506 issues (30%)`)
|
|
439
|
-
- [x] CSV import (Jira export without API token)
|
|
440
|
-
- [x] Estimate tool — task duration prediction with data-source awareness
|
|
441
|
-
- [ ] Database adapter (schema, sample data)
|
|
442
|
-
- [ ] Cross-source analysis (Jira ticket vs actual code vs DB state)
|
|
98
|
+
- **Quick Start Guide** — from zero to first query in 5 minutes
|
|
99
|
+
- **Use Cases & Examples** — real scenarios for PMs, team leads, developers, CTOs
|
|
100
|
+
- **MCP Tools Reference** — all 15 tools with parameters and examples
|
|
101
|
+
- **Estimate Tool Deep Dive** — algorithm, scoring, data sources
|
|
102
|
+
- **Architecture Guide** — hexagonal architecture, directory structure, extending
|
|
443
103
|
|
|
444
104
|
## License
|
|
445
105
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
import type { DbEngine } from '../../core/types/database.js';
|
|
3
|
+
export interface DbConnectionConfig {
|
|
4
|
+
engine: DbEngine;
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
user: string;
|
|
8
|
+
password: string;
|
|
9
|
+
database: string;
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function createKnexClient(config: DbConnectionConfig): Knex;
|
|
13
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/adapters/db/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAK7D,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAyCD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CA+BjE"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import knex from 'knex';
|
|
2
|
+
const STATEMENT_TIMEOUT_MS = 30_000;
|
|
3
|
+
const POOL_IDLE_TIMEOUT_MS = 10_000;
|
|
4
|
+
const ENGINE_TO_KNEX_CLIENT = {
|
|
5
|
+
postgresql: 'pg',
|
|
6
|
+
mysql: 'mysql2',
|
|
7
|
+
mssql: 'tedious',
|
|
8
|
+
sqlite: 'better-sqlite3',
|
|
9
|
+
oracledb: 'oracledb',
|
|
10
|
+
};
|
|
11
|
+
function buildAfterCreate(engine) {
|
|
12
|
+
return function afterCreate(conn, done) {
|
|
13
|
+
if (engine === 'postgresql') {
|
|
14
|
+
conn.query('SET default_transaction_read_only = true', (err) => {
|
|
15
|
+
if (err) {
|
|
16
|
+
done(err, conn);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
conn.query(`SET statement_timeout = '${String(STATEMENT_TIMEOUT_MS)}'`, (err2) => {
|
|
20
|
+
done(err2, conn);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
else if (engine === 'mysql') {
|
|
25
|
+
conn.query('SET SESSION TRANSACTION READ ONLY', (err) => {
|
|
26
|
+
if (err) {
|
|
27
|
+
done(err, conn);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
conn.query(`SET SESSION max_execution_time = ${String(STATEMENT_TIMEOUT_MS)}`, (err2) => {
|
|
31
|
+
done(err2, conn);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
done(null, conn);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function createKnexClient(config) {
|
|
41
|
+
const client = ENGINE_TO_KNEX_CLIENT[config.engine];
|
|
42
|
+
if (config.engine === 'sqlite') {
|
|
43
|
+
return knex({
|
|
44
|
+
client,
|
|
45
|
+
connection: { filename: config.database, options: { readonly: true } },
|
|
46
|
+
useNullAsDefault: true,
|
|
47
|
+
pool: { min: 0, max: 1 },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return knex({
|
|
51
|
+
client,
|
|
52
|
+
connection: {
|
|
53
|
+
host: config.host,
|
|
54
|
+
port: config.port,
|
|
55
|
+
user: config.user,
|
|
56
|
+
password: config.password,
|
|
57
|
+
database: config.database,
|
|
58
|
+
ssl: config.host !== 'localhost' && config.host !== '127.0.0.1'
|
|
59
|
+
? { rejectUnauthorized: false }
|
|
60
|
+
: false,
|
|
61
|
+
},
|
|
62
|
+
pool: {
|
|
63
|
+
min: 0,
|
|
64
|
+
max: 2,
|
|
65
|
+
idleTimeoutMillis: POOL_IDLE_TIMEOUT_MS,
|
|
66
|
+
afterCreate: buildAfterCreate(config.engine),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/adapters/db/client.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,oBAAoB,GAAG,MAAM,CAAC;AACpC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAYpC,MAAM,qBAAqB,GAA6B;IACtD,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,gBAAgB;IACxB,QAAQ,EAAE,UAAU;CACrB,CAAC;AAEF,SAAS,gBAAgB,CAAC,MAAgB;IACxC,OAAO,SAAS,WAAW,CACzB,IAAkE,EAClE,IAA2C;QAE3C,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,0CAA0C,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC7D,IAAI,GAAG,EAAE,CAAC;oBACR,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;oBAChB,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,KAAK,CAAC,4BAA4B,MAAM,CAAC,oBAAoB,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE;oBAC/E,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACnB,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,mCAAmC,EAAE,CAAC,GAAG,EAAE,EAAE;gBACtD,IAAI,GAAG,EAAE,CAAC;oBACR,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;oBAChB,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,KAAK,CAAC,oCAAoC,MAAM,CAAC,oBAAoB,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;oBACtF,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACnB,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAA0B;IACzD,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEpD,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;YACV,MAAM;YACN,UAAU,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;YACtE,gBAAgB,EAAE,IAAI;YACtB,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE;SACzB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;QACV,MAAM;QACN,UAAU,EAAE;YACV,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,GAAG,EAAE,MAAM,CAAC,IAAI,KAAK,WAAW,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW;gBAC7D,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE;gBAC/B,CAAC,CAAC,KAAK;SACV;QACD,IAAI,EAAE;YACJ,GAAG,EAAE,CAAC;YACN,GAAG,EAAE,CAAC;YACN,iBAAiB,EAAE,oBAAoB;YACvC,WAAW,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC;SAC7C;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,WAAW,EAA4B,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DbTable, DbColumn, DbForeignKey, DbIndex } from '../../core/types/database.js';
|
|
2
|
+
export interface RawTableRow {
|
|
3
|
+
table_schema: string;
|
|
4
|
+
table_name: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RawColumnRow {
|
|
7
|
+
table_name: string;
|
|
8
|
+
column_name: string;
|
|
9
|
+
data_type: string;
|
|
10
|
+
is_nullable: string;
|
|
11
|
+
column_default: string | null;
|
|
12
|
+
ordinal_position: number;
|
|
13
|
+
}
|
|
14
|
+
export interface RawForeignKeyRow {
|
|
15
|
+
table_name: string;
|
|
16
|
+
column_name: string;
|
|
17
|
+
referenced_table: string;
|
|
18
|
+
referenced_column: string;
|
|
19
|
+
}
|
|
20
|
+
export interface RawIndexRow {
|
|
21
|
+
table_name: string;
|
|
22
|
+
index_name: string;
|
|
23
|
+
column_name: string;
|
|
24
|
+
is_unique: boolean;
|
|
25
|
+
is_primary: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare function mapTableRow(row: RawTableRow, sourceName: string): Omit<DbTable, 'columns'>;
|
|
28
|
+
export declare function mapColumnRow(row: RawColumnRow, primaryKeys: Set<string>): DbColumn;
|
|
29
|
+
export declare function mapForeignKeyRow(row: RawForeignKeyRow): DbForeignKey;
|
|
30
|
+
export declare function mapIndexRows(rows: RawIndexRow[]): DbIndex[];
|
|
31
|
+
//# sourceMappingURL=mapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapper.d.ts","sourceRoot":"","sources":["../../../src/adapters/db/mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAE7F,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAQ1F;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,QAAQ,CAWlF;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,GAAG,YAAY,CAOpE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,CAyB3D"}
|