agencycore-cli 0.1.0__tar.gz

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 (51) hide show
  1. agencycore_cli-0.1.0/.claude/rules/code-patterns.md +43 -0
  2. agencycore_cli-0.1.0/.claude/rules/project-structure.md +75 -0
  3. agencycore_cli-0.1.0/.claude/rules/testing.md +32 -0
  4. agencycore_cli-0.1.0/.github/workflows/publish.yml +42 -0
  5. agencycore_cli-0.1.0/.gitignore +6 -0
  6. agencycore_cli-0.1.0/CLAUDE.md +72 -0
  7. agencycore_cli-0.1.0/LICENSE +21 -0
  8. agencycore_cli-0.1.0/PKG-INFO +131 -0
  9. agencycore_cli-0.1.0/README.md +98 -0
  10. agencycore_cli-0.1.0/pyproject.toml +55 -0
  11. agencycore_cli-0.1.0/src/ac_cli/__init__.py +0 -0
  12. agencycore_cli-0.1.0/src/ac_cli/client.py +81 -0
  13. agencycore_cli-0.1.0/src/ac_cli/commands/__init__.py +0 -0
  14. agencycore_cli-0.1.0/src/ac_cli/commands/auth.py +102 -0
  15. agencycore_cli-0.1.0/src/ac_cli/commands/crm/__init__.py +172 -0
  16. agencycore_cli-0.1.0/src/ac_cli/commands/crm/activities.py +193 -0
  17. agencycore_cli-0.1.0/src/ac_cli/commands/crm/communications.py +377 -0
  18. agencycore_cli-0.1.0/src/ac_cli/commands/crm/companies.py +145 -0
  19. agencycore_cli-0.1.0/src/ac_cli/commands/crm/deals.py +201 -0
  20. agencycore_cli-0.1.0/src/ac_cli/commands/crm/imports.py +74 -0
  21. agencycore_cli-0.1.0/src/ac_cli/commands/crm/lists.py +206 -0
  22. agencycore_cli-0.1.0/src/ac_cli/commands/crm/people.py +154 -0
  23. agencycore_cli-0.1.0/src/ac_cli/commands/envoy/__init__.py +33 -0
  24. agencycore_cli-0.1.0/src/ac_cli/commands/envoy/dashboard.py +27 -0
  25. agencycore_cli-0.1.0/src/ac_cli/commands/envoy/outbox.py +186 -0
  26. agencycore_cli-0.1.0/src/ac_cli/commands/envoy/recipients.py +87 -0
  27. agencycore_cli-0.1.0/src/ac_cli/commands/envoy/sequences.py +200 -0
  28. agencycore_cli-0.1.0/src/ac_cli/commands/envoy/steps.py +131 -0
  29. agencycore_cli-0.1.0/src/ac_cli/commands/health.py +31 -0
  30. agencycore_cli-0.1.0/src/ac_cli/config.py +50 -0
  31. agencycore_cli-0.1.0/src/ac_cli/formatting.py +45 -0
  32. agencycore_cli-0.1.0/src/ac_cli/main.py +26 -0
  33. agencycore_cli-0.1.0/tests/__init__.py +0 -0
  34. agencycore_cli-0.1.0/tests/conftest.py +52 -0
  35. agencycore_cli-0.1.0/tests/test_crm_activities.py +100 -0
  36. agencycore_cli-0.1.0/tests/test_crm_communications.py +357 -0
  37. agencycore_cli-0.1.0/tests/test_crm_companies.py +100 -0
  38. agencycore_cli-0.1.0/tests/test_crm_dashboard.py +71 -0
  39. agencycore_cli-0.1.0/tests/test_crm_deals.py +107 -0
  40. agencycore_cli-0.1.0/tests/test_crm_imports.py +67 -0
  41. agencycore_cli-0.1.0/tests/test_crm_lists.py +136 -0
  42. agencycore_cli-0.1.0/tests/test_crm_people.py +86 -0
  43. agencycore_cli-0.1.0/tests/test_crm_search.py +50 -0
  44. agencycore_cli-0.1.0/tests/test_envoy_dashboard.py +30 -0
  45. agencycore_cli-0.1.0/tests/test_envoy_outbox.py +132 -0
  46. agencycore_cli-0.1.0/tests/test_envoy_recipients.py +69 -0
  47. agencycore_cli-0.1.0/tests/test_envoy_sequences.py +120 -0
  48. agencycore_cli-0.1.0/tests/test_envoy_steps.py +97 -0
  49. agencycore_cli-0.1.0/tests/test_formatting.py +49 -0
  50. agencycore_cli-0.1.0/tests/test_token_refresh.py +151 -0
  51. agencycore_cli-0.1.0/uv.lock +1759 -0
@@ -0,0 +1,43 @@
1
+ ---
2
+ paths:
3
+ - "src/**/*.py"
4
+ ---
5
+
6
+ # Code Patterns
7
+
8
+ ## Environment Selection
9
+ - `ac login` defaults to staging (`STAGING_*` constants in `config.py`)
10
+ - `ac login --dev` uses local dev (`DEV_*` constants in `config.py`)
11
+ - Explicit `--api-url`, `--supabase-url`, `--supabase-anon-key` flags override environment defaults
12
+
13
+ ## Command Structure
14
+ - Each command group is a `typer.Typer()` sub-app registered via `app.add_typer()` in `main.py`
15
+ - CRM commands use `_api_request()` helper for HTTP calls with built-in error handling
16
+ - Use `_build_body()` helper to construct request bodies from non-None fields (handles tags splitting)
17
+ - IDs are positional `typer.Argument()`, optional fields are `typer.Option(None, ...)`
18
+ - Tags: accept comma-separated string on CLI, `_build_body` splits to list automatically
19
+ - Dates: ISO format strings (`2026-03-15`)
20
+
21
+ ## API Route Prefix
22
+ - CRM routes use the `_CRM` constant from `crm/__init__.py` (currently `/api/v1/crm`)
23
+ - Envoy routes use the `_ENVOY` constant from `envoy/__init__.py` (currently `/api/v1/envoy`)
24
+ - Both domains share `_api_request()` and `_build_body()` from `crm/__init__.py`
25
+ - Non-versioned routes (`/whoami`, `/health`) use root paths directly
26
+ - No trailing slashes on API paths
27
+
28
+ ## Error Handling
29
+ - `_api_request()` catches both `httpx.HTTPStatusError` (API errors) and `httpx.HTTPError` (connection errors)
30
+ - `_handle_error()` extracts detail from API error responses and exits with code 1
31
+ - Non-CRM/Envoy commands (auth, health) handle errors directly in the command function
32
+
33
+ ## Output
34
+ - Rich output by default via `formatting.py` helpers (`print_table`, `print_detail`)
35
+ - `--json` flag outputs raw JSON to stdout for piping/scripting
36
+ - `--json` is a global option on both the `crm` and `envoy` app group callbacks, passed via `typer.Context`
37
+ - Access in subcommands: add `ctx: typer.Context` parameter, read `ctx.obj["json"]`
38
+
39
+ ## Delete Commands
40
+ - Always require `--yes` / `-y` flag or interactive `typer.confirm()` prompt
41
+
42
+ ## Create Commands
43
+ - Fetch `organization_id` from `/whoami` before creating resources
@@ -0,0 +1,75 @@
1
+ # Project Structure
2
+
3
+ ```
4
+ ac-cli/
5
+ ├── src/ac_cli/
6
+ │ ├── main.py # Entry point, registers all sub-apps
7
+ │ ├── client.py # Authenticated httpx client from stored config
8
+ │ ├── config.py # Load/save ~/.agencycore/config.json, environment constants (STAGING_*, DEV_*)
9
+ │ ├── formatting.py # Shared output: print_table, print_detail, print_json
10
+ │ └── commands/
11
+ │ ├── auth.py # login, logout, whoami
12
+ │ ├── health.py # health check (no auth)
13
+ │ ├── crm/ # CRM subcommands (package)
14
+ │ │ ├── __init__.py # app, shared helpers (_CRM, _api_request, _build_body, _handle_error), search, dashboard
15
+ │ │ ├── companies.py # companies_app: list, get, create, update, delete
16
+ │ │ ├── people.py # people_app: list, get, create, update, delete
17
+ │ │ ├── deals.py # deals_app: list, get, create, update, move, order, delete
18
+ │ │ ├── activities.py # activities_app: list, get, create, update, complete, delete
19
+ │ │ ├── communications.py # communications_app: list, get, thread, unread, mark-read, delete, delete-thread, update, archive, unarchive, contact-by-email, resolve-contact, draft-email, generate-draft, unread-thread-ids
20
+ │ │ ├── lists.py # lists_app: list, get, create, update, members, add-member, remove-member, delete
21
+ │ │ └── imports.py # imports_app: preview, commit
22
+ │ └── envoy/ # Envoy outreach subcommands (package)
23
+ │ ├── __init__.py # app, _ENVOY prefix, envoy_callback, registers sub-apps
24
+ │ ├── sequences.py # sequences_app: list, get, create, update, delete, launch, pause, resume
25
+ │ ├── steps.py # steps_app: create, update, delete, reorder, stats
26
+ │ ├── recipients.py # recipients_app: list, add, remove
27
+ │ ├── outbox.py # outbox_app: pending, sent, step-drafts, update-draft, approve, reject, regenerate
28
+ │ └── dashboard.py # dashboard_command (standalone command, not sub-app)
29
+ ├── tests/
30
+ │ ├── conftest.py # Shared fixtures (mock_api, mock_config, invoke)
31
+ │ ├── test_formatting.py
32
+ │ ├── test_crm_search.py
33
+ │ ├── test_crm_companies.py
34
+ │ ├── test_crm_people.py
35
+ │ ├── test_crm_deals.py
36
+ │ ├── test_crm_activities.py
37
+ │ ├── test_crm_communications.py
38
+ │ ├── test_crm_dashboard.py
39
+ │ ├── test_crm_lists.py
40
+ │ ├── test_crm_imports.py
41
+ │ ├── test_envoy_sequences.py
42
+ │ ├── test_envoy_steps.py
43
+ │ ├── test_envoy_recipients.py
44
+ │ ├── test_envoy_outbox.py
45
+ │ ├── test_envoy_dashboard.py
46
+ │ └── test_token_refresh.py
47
+ ├── pyproject.toml
48
+ └── uv.lock
49
+ ```
50
+
51
+ ## Adding New CRM Subcommand Groups
52
+ 1. Create `src/ac_cli/commands/crm/<group>.py` with `<group>_app = typer.Typer(help="...")`
53
+ 2. Import shared helpers: `from ac_cli.commands.crm import _CRM, _api_request, _build_body`
54
+ 3. Add `ctx: typer.Context` as first parameter to commands that need `--json` output
55
+ 4. Use `_api_request("get", ...)` for API calls and `_build_body(...)` for request bodies
56
+ 5. Read JSON flag via `ctx.obj["json"]`
57
+ 6. Register in `crm/__init__.py`: import the app and call `app.add_typer(<group>_app, name="<group>")`
58
+
59
+ ## Adding Envoy Subcommand Groups
60
+ 1. Create `src/ac_cli/commands/envoy/<group>.py` with `<group>_app = typer.Typer(help="...")`
61
+ 2. Import shared helpers: `from ac_cli.commands.crm import _api_request, _build_body`
62
+ 3. Import envoy prefix: `from ac_cli.commands.envoy import _ENVOY`
63
+ 4. Add `ctx: typer.Context` as first parameter to commands that need `--json` output
64
+ 5. Read JSON flag via `ctx.obj["json"]`
65
+ 6. Register in `envoy/__init__.py`: import the app and call `app.add_typer(<group>_app, name="<group>")`
66
+
67
+ ## Adding Non-CRM/Envoy Command Groups
68
+ 1. Create `src/ac_cli/commands/<group>.py` (or package) with `app = typer.Typer(help="...")`
69
+ 2. Import and register in `main.py`: `app.add_typer(<group>.app, name="<group>")`
70
+
71
+ ## API Endpoints
72
+ - CRM commands hit `/api/v1/crm/*` — the `_CRM` constant in `crm/__init__.py` holds this prefix
73
+ - Envoy commands hit `/api/v1/envoy/*` — the `_ENVOY` constant in `envoy/__init__.py` holds this prefix
74
+ - Both share `_api_request()` and `_build_body()` helpers from `crm/__init__.py`
75
+ - The API base URL is stored in `~/.agencycore/config.json` (set during `ac login`). Environment constants live in `config.py`: `STAGING_*` (default) and `DEV_*` (used with `ac login --dev`).
@@ -0,0 +1,32 @@
1
+ ---
2
+ paths:
3
+ - "tests/**/*.py"
4
+ ---
5
+
6
+ # Testing
7
+
8
+ ## Stack
9
+ - **pytest** for test runner
10
+ - **respx** for mocking httpx requests (cleaner than pytest-httpx for sync client)
11
+ - **typer.testing.CliRunner** for invoking CLI commands
12
+
13
+ ## Fixtures (in `tests/conftest.py`)
14
+ - `cli_runner` — CliRunner instance
15
+ - `mock_config` — patches `load_config()` to return valid auth config
16
+ - `mock_api` — respx mock router intercepting httpx calls to API base URL
17
+ - `invoke` — convenience function combining all three: `invoke(["crm", "companies", "list"])`
18
+
19
+ ## What to Test Per Command
20
+ - **Happy path**: correct API call made, response rendered (table or JSON)
21
+ - **`--json` flag**: output is valid JSON (parse with `json.loads`)
22
+ - **Error handling**: API returns 404/422/500 → CLI prints error, exits code 1
23
+ - **`--yes` on delete**: skips confirmation prompt
24
+ - **Abort on delete**: `input="n\n"` → exits code 1
25
+
26
+ ## Running
27
+ ```bash
28
+ uv run pytest # All tests (165 tests)
29
+ uv run pytest tests/test_crm_deals.py # Single CRM test file
30
+ uv run pytest tests/test_envoy_outbox.py # Single Envoy test file
31
+ uv run pytest -k "test_companies" # By name pattern
32
+ ```
@@ -0,0 +1,42 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ id-token: write
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: astral-sh/setup-uv@v4
19
+ with:
20
+ version: "latest"
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ run: uv python install ${{ matrix.python-version }}
23
+ - name: Install dependencies
24
+ run: uv sync --all-extras --python ${{ matrix.python-version }}
25
+ - name: Run tests
26
+ run: uv run pytest
27
+
28
+ publish:
29
+ runs-on: ubuntu-latest
30
+ needs: test
31
+ environment:
32
+ name: pypi
33
+ url: https://pypi.org/p/agencycore-cli
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: astral-sh/setup-uv@v4
37
+ with:
38
+ version: "latest"
39
+ - name: Build package
40
+ run: uv build
41
+ - name: Publish to PyPI
42
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ *.egg-info/
5
+ dist/
6
+ build/
@@ -0,0 +1,72 @@
1
+ # ac-cli
2
+
3
+ Python CLI for AgencyCore. Built with Typer + httpx + Rich + Supabase auth.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ uv sync # Install all dependencies
9
+ uv sync --all-extras # Install with dev dependencies (pytest, respx)
10
+ ```
11
+
12
+ ## Commands
13
+
14
+ ```
15
+ ac login / logout / whoami # Auth commands
16
+ ac health check # API health (no auth)
17
+
18
+ # CRM
19
+ ac crm search <query> # Search companies, contacts, deals
20
+ ac crm companies list|get|create|update|delete
21
+ ac crm people list|get|create|update|delete
22
+ ac crm deals list|get|create|update|move|order|delete
23
+ ac crm activities list|get|create|update|complete|delete
24
+ ac crm comms list|get|thread|unread|mark-read|delete|delete-thread|update|archive|unarchive|contact-by-email|resolve-contact|draft-email|generate-draft|unread-thread-ids
25
+ ac crm dashboard [--period N]
26
+ ac crm lists list|get|create|update|members|add-member|remove-member|delete
27
+ ac crm import preview|commit
28
+
29
+ # Envoy (outreach)
30
+ ac envoy sequences list|get|create|update|delete|launch|pause|resume
31
+ ac envoy steps create|update|delete|reorder|stats
32
+ ac envoy recipients list|add|remove
33
+ ac envoy outbox pending|sent|step-drafts|update-draft|approve|reject|regenerate
34
+ ac envoy dashboard
35
+ ```
36
+
37
+ All CRM and Envoy commands support `--json` flag for scripting (set on `ac crm --json` or `ac envoy --json`). The flag is passed via `typer.Context`.
38
+
39
+ ## Running Checks
40
+
41
+ ```bash
42
+ uv run pytest # Run all tests (165 tests)
43
+ uv run pytest tests/test_crm_companies.py -v # Single test file
44
+ uv run python -m ac_cli.main --help # Verify CLI loads
45
+ ```
46
+
47
+ ## Dependency Management
48
+
49
+ Uses **uv** for dependency management. Both `pyproject.toml` and `uv.lock` must stay in sync.
50
+
51
+ ```bash
52
+ uv add <package> # Add a dependency
53
+ uv add --dev <package> # Add a dev dependency
54
+ uv lock # Regenerate lockfile after manual pyproject.toml edits
55
+ ```
56
+
57
+ ## Config
58
+
59
+ Credentials stored in `~/.agencycore/config.json` (file mode 0600).
60
+
61
+ ### Environments
62
+
63
+ `ac login` defaults to **staging**. Use `--dev` for local development:
64
+
65
+ ```bash
66
+ ac login # Staging (api.agencycore.dev)
67
+ ac login --dev # Local dev (localhost:8008 + local Supabase)
68
+ ```
69
+
70
+ Environment constants (URLs, anon keys) are defined in `config.py`:
71
+ - `STAGING_*` — staging API + Supabase
72
+ - `DEV_*` — localhost API + local Supabase (`127.0.0.1:54321`)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 AlterityNetwork
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: agencycore-cli
3
+ Version: 0.1.0
4
+ Summary: AgencyCore CLI — manage your CRM, outreach, and more from the terminal
5
+ Project-URL: Homepage, https://github.com/AlterityNetwork/ac-cli
6
+ Project-URL: Repository, https://github.com/AlterityNetwork/ac-cli
7
+ Project-URL: Issues, https://github.com/AlterityNetwork/ac-cli/issues
8
+ Author-email: AlterityNetwork <dev@agencycore.dev>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agencycore,cli,crm,pipeline,sales
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Office/Business
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: httpx>=0.27
25
+ Requires-Dist: rich>=13.0
26
+ Requires-Dist: socksio>=1.0.0
27
+ Requires-Dist: supabase>=2.0
28
+ Requires-Dist: typer>=0.9
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=8.0; extra == 'dev'
31
+ Requires-Dist: respx>=0.22; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # AgencyCore CLI
35
+
36
+ Command-line interface for [AgencyCore](https://agencycore.dev) — manage your CRM, outreach sequences, and pipeline from the terminal.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install agencycore-cli
42
+ ```
43
+
44
+ Or with [uv](https://docs.astral.sh/uv/):
45
+
46
+ ```bash
47
+ uv pip install agencycore-cli
48
+ ```
49
+
50
+ Requires Python 3.10+.
51
+
52
+ ## Quick Start
53
+
54
+ ```bash
55
+ # Log in (connects to AgencyCore staging by default)
56
+ ac login
57
+
58
+ # Use --dev for local development
59
+ ac login --dev
60
+
61
+ # Check your identity
62
+ ac whoami
63
+
64
+ # Check API health
65
+ ac health check
66
+ ```
67
+
68
+ ## CRM Commands
69
+
70
+ ```bash
71
+ # Companies
72
+ ac crm companies list
73
+ ac crm companies get <id>
74
+ ac crm companies create --name "Acme Corp" --industry Technology
75
+
76
+ # People (Contacts)
77
+ ac crm people list
78
+ ac crm people create --email jane@acme.com --full-name "Jane Smith"
79
+
80
+ # Deals
81
+ ac crm deals list
82
+ ac crm deals create --name "Enterprise Deal" --stage qualified --amount 50000
83
+ ac crm deals move <id> --stage negotiation
84
+
85
+ # Activities
86
+ ac crm activities list --status pending
87
+ ac crm activities create --type call --title "Follow up" --due-date 2026-03-20
88
+
89
+ # Communications
90
+ ac crm comms unread
91
+ ac crm comms draft-email --contact-id <id> --subject "Hello" --content "..."
92
+
93
+ # Search & Dashboard
94
+ ac crm search "acme"
95
+ ac crm dashboard --period 30
96
+
97
+ # Lists & Import
98
+ ac crm lists list
99
+ ac crm import preview --file contacts.json
100
+ ```
101
+
102
+ ## Envoy (Outreach)
103
+
104
+ ```bash
105
+ ac envoy sequences list
106
+ ac envoy outbox pending
107
+ ac envoy dashboard
108
+ ```
109
+
110
+ ## Output Modes
111
+
112
+ All commands support rich table output (default) or JSON for scripting:
113
+
114
+ ```bash
115
+ ac crm deals list # Pretty tables
116
+ ac crm --json deals list # Raw JSON
117
+ ac crm --json deals list | jq '.[].name' # Pipe to jq
118
+ ```
119
+
120
+ ## Development
121
+
122
+ ```bash
123
+ git clone https://github.com/AlterityNetwork/ac-cli.git
124
+ cd ac-cli
125
+ uv sync --all-extras
126
+ uv run pytest
127
+ ```
128
+
129
+ ## License
130
+
131
+ MIT
@@ -0,0 +1,98 @@
1
+ # AgencyCore CLI
2
+
3
+ Command-line interface for [AgencyCore](https://agencycore.dev) — manage your CRM, outreach sequences, and pipeline from the terminal.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install agencycore-cli
9
+ ```
10
+
11
+ Or with [uv](https://docs.astral.sh/uv/):
12
+
13
+ ```bash
14
+ uv pip install agencycore-cli
15
+ ```
16
+
17
+ Requires Python 3.10+.
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ # Log in (connects to AgencyCore staging by default)
23
+ ac login
24
+
25
+ # Use --dev for local development
26
+ ac login --dev
27
+
28
+ # Check your identity
29
+ ac whoami
30
+
31
+ # Check API health
32
+ ac health check
33
+ ```
34
+
35
+ ## CRM Commands
36
+
37
+ ```bash
38
+ # Companies
39
+ ac crm companies list
40
+ ac crm companies get <id>
41
+ ac crm companies create --name "Acme Corp" --industry Technology
42
+
43
+ # People (Contacts)
44
+ ac crm people list
45
+ ac crm people create --email jane@acme.com --full-name "Jane Smith"
46
+
47
+ # Deals
48
+ ac crm deals list
49
+ ac crm deals create --name "Enterprise Deal" --stage qualified --amount 50000
50
+ ac crm deals move <id> --stage negotiation
51
+
52
+ # Activities
53
+ ac crm activities list --status pending
54
+ ac crm activities create --type call --title "Follow up" --due-date 2026-03-20
55
+
56
+ # Communications
57
+ ac crm comms unread
58
+ ac crm comms draft-email --contact-id <id> --subject "Hello" --content "..."
59
+
60
+ # Search & Dashboard
61
+ ac crm search "acme"
62
+ ac crm dashboard --period 30
63
+
64
+ # Lists & Import
65
+ ac crm lists list
66
+ ac crm import preview --file contacts.json
67
+ ```
68
+
69
+ ## Envoy (Outreach)
70
+
71
+ ```bash
72
+ ac envoy sequences list
73
+ ac envoy outbox pending
74
+ ac envoy dashboard
75
+ ```
76
+
77
+ ## Output Modes
78
+
79
+ All commands support rich table output (default) or JSON for scripting:
80
+
81
+ ```bash
82
+ ac crm deals list # Pretty tables
83
+ ac crm --json deals list # Raw JSON
84
+ ac crm --json deals list | jq '.[].name' # Pipe to jq
85
+ ```
86
+
87
+ ## Development
88
+
89
+ ```bash
90
+ git clone https://github.com/AlterityNetwork/ac-cli.git
91
+ cd ac-cli
92
+ uv sync --all-extras
93
+ uv run pytest
94
+ ```
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "agencycore-cli"
7
+ version = "0.1.0"
8
+ description = "AgencyCore CLI — manage your CRM, outreach, and more from the terminal"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "AlterityNetwork", email = "dev@agencycore.dev" },
14
+ ]
15
+ keywords = ["crm", "cli", "agencycore", "sales", "pipeline"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "Intended Audience :: End Users/Desktop",
21
+ "License :: OSI Approved :: MIT License",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Office/Business",
28
+ ]
29
+ dependencies = [
30
+ "typer>=0.9",
31
+ "httpx>=0.27",
32
+ "supabase>=2.0",
33
+ "rich>=13.0",
34
+ "socksio>=1.0.0",
35
+ ]
36
+
37
+ [project.optional-dependencies]
38
+ dev = [
39
+ "pytest>=8.0",
40
+ "respx>=0.22",
41
+ ]
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/AlterityNetwork/ac-cli"
45
+ Repository = "https://github.com/AlterityNetwork/ac-cli"
46
+ Issues = "https://github.com/AlterityNetwork/ac-cli/issues"
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["src/ac_cli"]
50
+
51
+ [tool.pytest.ini_options]
52
+ testpaths = ["tests"]
53
+
54
+ [project.scripts]
55
+ ac = "ac_cli.main:app"
File without changes
@@ -0,0 +1,81 @@
1
+ """Authenticated httpx client for the AgencyCore API."""
2
+
3
+ import httpx
4
+ import typer
5
+
6
+ from ac_cli.config import load_config, save_config
7
+
8
+
9
+ def _refresh_access_token(cfg: dict) -> str:
10
+ """Use stored refresh_token to obtain a new access_token from Supabase.
11
+
12
+ Persists the new tokens to config. Returns the new access_token.
13
+ Raises typer.Exit on failure.
14
+ """
15
+ from supabase import create_client
16
+
17
+ supabase_url = cfg.get("supabase_url")
18
+ supabase_anon_key = cfg.get("supabase_anon_key")
19
+ refresh_token = cfg.get("refresh_token")
20
+
21
+ if not supabase_url or not supabase_anon_key or not refresh_token:
22
+ typer.echo("Session expired. Run `ac login` to re-authenticate.")
23
+ raise typer.Exit(code=1)
24
+
25
+ try:
26
+ sb = create_client(supabase_url, supabase_anon_key)
27
+ response = sb.auth.refresh_session(refresh_token)
28
+ except Exception as exc:
29
+ typer.echo(f"Token refresh failed: {exc}\nRun `ac login` to re-authenticate.")
30
+ raise typer.Exit(code=1) from exc
31
+
32
+ session = response.session
33
+ if not session:
34
+ typer.echo("Token refresh returned no session. Run `ac login` to re-authenticate.")
35
+ raise typer.Exit(code=1)
36
+
37
+ cfg["access_token"] = session.access_token
38
+ cfg["refresh_token"] = session.refresh_token
39
+ save_config(cfg)
40
+ return session.access_token
41
+
42
+
43
+ class _AuthClient(httpx.Client):
44
+ """httpx.Client that auto-refreshes expired Supabase tokens on 401."""
45
+
46
+ def __init__(self, cfg: dict, **kwargs: object) -> None:
47
+ self._cfg = cfg
48
+ super().__init__(**kwargs)
49
+
50
+ def send(self, request: httpx.Request, **kwargs: object) -> httpx.Response:
51
+ response = super().send(request, **kwargs)
52
+
53
+ if response.status_code == 401:
54
+ new_token = _refresh_access_token(self._cfg)
55
+ request.headers["Authorization"] = f"Bearer {new_token}"
56
+ self.headers["Authorization"] = f"Bearer {new_token}"
57
+ response = super().send(request, **kwargs)
58
+
59
+ return response
60
+
61
+
62
+ def get_api_client() -> httpx.Client:
63
+ """Return an httpx.Client with base URL and auth header from stored config.
64
+
65
+ The client auto-refreshes the Supabase access token on 401 responses.
66
+ Raises typer.Exit if not logged in.
67
+ """
68
+ cfg = load_config()
69
+ access_token = cfg.get("access_token")
70
+ api_url = cfg.get("api_url")
71
+
72
+ if not access_token or not api_url:
73
+ typer.echo("Not logged in. Run `ac login` first.")
74
+ raise typer.Exit(code=1)
75
+
76
+ return _AuthClient(
77
+ cfg=cfg,
78
+ base_url=api_url,
79
+ headers={"Authorization": f"Bearer {access_token}"},
80
+ timeout=30.0,
81
+ )
File without changes