aisquare-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 (77) hide show
  1. aisquare_cli-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  2. aisquare_cli-0.1.0/.github/dependabot.yml +17 -0
  3. aisquare_cli-0.1.0/.github/workflows/ci.yml +76 -0
  4. aisquare_cli-0.1.0/.github/workflows/publish.yml +42 -0
  5. aisquare_cli-0.1.0/.gitignore +22 -0
  6. aisquare_cli-0.1.0/CONTRIBUTING.md +68 -0
  7. aisquare_cli-0.1.0/LICENSE +21 -0
  8. aisquare_cli-0.1.0/Makefile +25 -0
  9. aisquare_cli-0.1.0/PKG-INFO +307 -0
  10. aisquare_cli-0.1.0/README.md +278 -0
  11. aisquare_cli-0.1.0/pyproject.toml +70 -0
  12. aisquare_cli-0.1.0/src/aisquare/__init__.py +10 -0
  13. aisquare_cli-0.1.0/src/aisquare/__main__.py +6 -0
  14. aisquare_cli-0.1.0/src/aisquare/cli/__init__.py +1 -0
  15. aisquare_cli-0.1.0/src/aisquare/cli/agents.py +60 -0
  16. aisquare_cli-0.1.0/src/aisquare/cli/app.py +101 -0
  17. aisquare_cli-0.1.0/src/aisquare/cli/auth.py +27 -0
  18. aisquare_cli-0.1.0/src/aisquare/cli/capture.py +39 -0
  19. aisquare_cli-0.1.0/src/aisquare/cli/common.py +350 -0
  20. aisquare_cli-0.1.0/src/aisquare/cli/config.py +54 -0
  21. aisquare_cli-0.1.0/src/aisquare/cli/connectors.py +37 -0
  22. aisquare_cli-0.1.0/src/aisquare/cli/context.py +139 -0
  23. aisquare_cli-0.1.0/src/aisquare/cli/enforce.py +27 -0
  24. aisquare_cli-0.1.0/src/aisquare/cli/hook.py +61 -0
  25. aisquare_cli-0.1.0/src/aisquare/cli/policy.py +15 -0
  26. aisquare_cli-0.1.0/src/aisquare/cli/project.py +66 -0
  27. aisquare_cli-0.1.0/src/aisquare/cli/root.py +158 -0
  28. aisquare_cli-0.1.0/src/aisquare/core/__init__.py +1 -0
  29. aisquare_cli-0.1.0/src/aisquare/core/agents.py +196 -0
  30. aisquare_cli-0.1.0/src/aisquare/core/config.py +63 -0
  31. aisquare_cli-0.1.0/src/aisquare/core/console.py +17 -0
  32. aisquare_cli-0.1.0/src/aisquare/core/editor.py +39 -0
  33. aisquare_cli-0.1.0/src/aisquare/core/entries.py +33 -0
  34. aisquare_cli-0.1.0/src/aisquare/core/ids.py +51 -0
  35. aisquare_cli-0.1.0/src/aisquare/core/injection.py +60 -0
  36. aisquare_cli-0.1.0/src/aisquare/core/paths.py +82 -0
  37. aisquare_cli-0.1.0/src/aisquare/core/snapshot.py +221 -0
  38. aisquare_cli-0.1.0/src/aisquare/core/state.py +35 -0
  39. aisquare_cli-0.1.0/src/aisquare/core/store.py +430 -0
  40. aisquare_cli-0.1.0/src/aisquare/core/stubs.py +34 -0
  41. aisquare_cli-0.1.0/src/aisquare/core/workspace.py +84 -0
  42. aisquare_cli-0.1.0/src/aisquare/models.py +167 -0
  43. aisquare_cli-0.1.0/src/aisquare/py.typed +0 -0
  44. aisquare_cli-0.1.0/src/aisquare/services/__init__.py +6 -0
  45. aisquare_cli-0.1.0/src/aisquare/services/agents.py +87 -0
  46. aisquare_cli-0.1.0/src/aisquare/services/auth.py +35 -0
  47. aisquare_cli-0.1.0/src/aisquare/services/capture.py +30 -0
  48. aisquare_cli-0.1.0/src/aisquare/services/connectors.py +25 -0
  49. aisquare_cli-0.1.0/src/aisquare/services/context.py +213 -0
  50. aisquare_cli-0.1.0/src/aisquare/services/diagnostics.py +193 -0
  51. aisquare_cli-0.1.0/src/aisquare/services/hooks.py +62 -0
  52. aisquare_cli-0.1.0/src/aisquare/services/lifecycle.py +93 -0
  53. aisquare_cli-0.1.0/src/aisquare/services/policy.py +25 -0
  54. aisquare_cli-0.1.0/src/aisquare/services/project.py +106 -0
  55. aisquare_cli-0.1.0/src/aisquare/services/settings.py +72 -0
  56. aisquare_cli-0.1.0/src/aisquare/services/sync.py +10 -0
  57. aisquare_cli-0.1.0/tests/__init__.py +1 -0
  58. aisquare_cli-0.1.0/tests/cli_tree.py +58 -0
  59. aisquare_cli-0.1.0/tests/conftest.py +49 -0
  60. aisquare_cli-0.1.0/tests/test_agents.py +127 -0
  61. aisquare_cli-0.1.0/tests/test_config.py +47 -0
  62. aisquare_cli-0.1.0/tests/test_context.py +309 -0
  63. aisquare_cli-0.1.0/tests/test_help.py +16 -0
  64. aisquare_cli-0.1.0/tests/test_hooks.py +78 -0
  65. aisquare_cli-0.1.0/tests/test_ids.py +31 -0
  66. aisquare_cli-0.1.0/tests/test_init.py +91 -0
  67. aisquare_cli-0.1.0/tests/test_injection.py +131 -0
  68. aisquare_cli-0.1.0/tests/test_json_mode.py +22 -0
  69. aisquare_cli-0.1.0/tests/test_packaging.py +11 -0
  70. aisquare_cli-0.1.0/tests/test_project.py +116 -0
  71. aisquare_cli-0.1.0/tests/test_settings.py +71 -0
  72. aisquare_cli-0.1.0/tests/test_snapshot.py +80 -0
  73. aisquare_cli-0.1.0/tests/test_status.py +81 -0
  74. aisquare_cli-0.1.0/tests/test_store.py +247 -0
  75. aisquare_cli-0.1.0/tests/test_stubs.py +88 -0
  76. aisquare_cli-0.1.0/tests/test_version.py +20 -0
  77. aisquare_cli-0.1.0/tests/test_workspace.py +72 -0
@@ -0,0 +1,15 @@
1
+ ## What & why
2
+
3
+ <!-- What does this change do, and why? Link any related issue. -->
4
+
5
+ ## How to verify
6
+
7
+ <!-- Commands run, behaviour observed. -->
8
+
9
+ ## Checklist
10
+
11
+ - [ ] `make check` passes locally (lint + typecheck + tests)
12
+ - [ ] Added or updated tests for the change
13
+ - [ ] If a command graduated from stub to real, it's removed from the
14
+ `IMPLEMENTED` skip-list in `tests/test_stubs.py` and has its own tests
15
+ - [ ] Updated docs (README / CONTRIBUTING) if behaviour or commands changed
@@ -0,0 +1,17 @@
1
+ version: 2
2
+ updates:
3
+ # Keep GitHub Actions pinned actions current.
4
+ - package-ecosystem: github-actions
5
+ directory: "/"
6
+ schedule:
7
+ interval: weekly
8
+ commit-message:
9
+ prefix: ci
10
+
11
+ # Keep Python dependencies current.
12
+ - package-ecosystem: pip
13
+ directory: "/"
14
+ schedule:
15
+ interval: weekly
16
+ commit-message:
17
+ prefix: deps
@@ -0,0 +1,76 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ # Cancel superseded runs on the same branch/PR.
10
+ concurrency:
11
+ group: ci-${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ check:
19
+ name: check (py${{ matrix.python-version }})
20
+ runs-on: ubuntu-latest
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ python-version: ["3.11", "3.12", "3.13"]
25
+ steps:
26
+ - uses: actions/checkout@v7
27
+
28
+ - name: Set up Python ${{ matrix.python-version }}
29
+ uses: actions/setup-python@v6
30
+ with:
31
+ python-version: ${{ matrix.python-version }}
32
+ cache: pip
33
+ cache-dependency-path: pyproject.toml
34
+
35
+ - name: Install
36
+ run: |
37
+ python -m pip install --upgrade pip
38
+ pip install -e ".[dev]"
39
+
40
+ - name: Lint (ruff)
41
+ run: ruff check src tests
42
+
43
+ - name: Format check (ruff)
44
+ run: ruff format --check src tests
45
+
46
+ - name: Type check (mypy)
47
+ run: mypy
48
+
49
+ - name: Test (pytest)
50
+ run: pytest -ra
51
+
52
+ package:
53
+ name: package (build + install)
54
+ runs-on: ubuntu-latest
55
+ steps:
56
+ - uses: actions/checkout@v7
57
+
58
+ - name: Set up Python
59
+ uses: actions/setup-python@v6
60
+ with:
61
+ python-version: "3.12"
62
+
63
+ - name: Build sdist and wheel
64
+ run: |
65
+ python -m pip install --upgrade pip build
66
+ python -m build
67
+
68
+ - name: Install the wheel in a clean environment
69
+ run: |
70
+ python -m venv /tmp/smoke
71
+ /tmp/smoke/bin/pip install dist/*.whl
72
+ - name: Smoke-test the console scripts
73
+ run: |
74
+ /tmp/smoke/bin/aisquare --version
75
+ /tmp/smoke/bin/asq --version
76
+ /tmp/smoke/bin/aisquare --help
@@ -0,0 +1,42 @@
1
+ name: Publish to PyPI
2
+
3
+ # Publishes to PyPI via Trusted Publishing (OIDC) — no API token/secret needed.
4
+ # Triggered when a GitHub Release is published. The version that ships is whatever
5
+ # is in pyproject.toml, so bump it before tagging the release.
6
+ on:
7
+ release:
8
+ types: [published]
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ build:
15
+ name: Build sdist + wheel
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v7
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ - name: Build
23
+ run: |
24
+ python -m pip install --upgrade build
25
+ python -m build
26
+ - uses: actions/upload-artifact@v4
27
+ with:
28
+ name: dist
29
+ path: dist/
30
+
31
+ publish:
32
+ name: Publish to PyPI
33
+ needs: build
34
+ runs-on: ubuntu-latest
35
+ permissions:
36
+ id-token: write # OIDC token for Trusted Publishing
37
+ steps:
38
+ - uses: actions/download-artifact@v4
39
+ with:
40
+ name: dist
41
+ path: dist/
42
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,22 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+
8
+ # Environments
9
+ .venv/
10
+ venv/
11
+
12
+ # Tooling caches
13
+ .pytest_cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # OS / editors
20
+ .DS_Store
21
+ .idea/
22
+ .vscode/
@@ -0,0 +1,68 @@
1
+ # Contributing to aisquare
2
+
3
+ Thanks for your interest in aisquare! This is an early-stage, open-source
4
+ project and contributions are welcome.
5
+
6
+ ## Development setup
7
+
8
+ Requires Python 3.11+.
9
+
10
+ ```sh
11
+ python3 -m venv .venv
12
+ source .venv/bin/activate
13
+ make install # editable install + dev tools (ruff, mypy, pytest)
14
+ ```
15
+
16
+ ## Before you open a PR
17
+
18
+ Run the full check suite — this is exactly what CI runs:
19
+
20
+ ```sh
21
+ make check # lint + typecheck + tests
22
+ ```
23
+
24
+ Individual targets:
25
+
26
+ | Task | Command |
27
+ | --- | --- |
28
+ | Format + autofix | `make fmt` |
29
+ | Lint | `make lint` |
30
+ | Type-check (mypy strict) | `make typecheck` |
31
+ | Tests | `make test` |
32
+
33
+ CI (`.github/workflows/ci.yml`) runs lint, format-check, mypy and pytest on
34
+ Python 3.11–3.13, plus a packaging job that builds the wheel and smoke-tests the
35
+ `aisquare` / `asq` console scripts. All jobs must pass before a PR can merge.
36
+
37
+ ## Implementing a feature (stub → service)
38
+
39
+ Most commands are still stubs that exit `70`. Each one becomes real by replacing
40
+ a single `stub(...)` call in a `services/` module — the CLI wiring and function
41
+ signatures already exist. The flow is:
42
+
43
+ 1. **Implement the service** in `src/aisquare/services/<domain>.py`. Services
44
+ return data; they never parse CLI arguments or print. Persisted state goes
45
+ through the `ContextStore` in `src/aisquare/core/store.py`.
46
+ 2. **Render it** in the matching `src/aisquare/cli/<group>.py` command: parse,
47
+ call the service, print (honouring `--json` via `get_state().json_output`).
48
+ Shared rendering helpers live in `cli/common.py`.
49
+ 3. **Move the command off the stub skip-list** in `tests/test_stubs.py`
50
+ (`IMPLEMENTED`) and add real tests for the new behaviour.
51
+
52
+ See the README's [Architecture](README.md#architecture) section for the full
53
+ layout and the thin-CLI / service / core split.
54
+
55
+ ## Conventions
56
+
57
+ - **Type everything.** `mypy` runs in `strict` mode over `src` and `tests`.
58
+ - **Tests are isolated.** The `isolated_home` fixture points `AISQUARE_HOME` at
59
+ a temp dir, so tests never touch your real `~/.aisquare`. Never write to the
60
+ real home in a test or example.
61
+ - Keep CLI modules thin and services free of CLI concerns.
62
+ - New shared plumbing goes in `core/`; new domain shapes go in `models.py`.
63
+
64
+ ## Reporting bugs / proposing features
65
+
66
+ Open an issue describing what you expected and what happened. For larger
67
+ changes, it's worth opening an issue to discuss the approach before writing
68
+ code.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AISquare
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,25 @@
1
+ .PHONY: install test lint typecheck fmt check
2
+
3
+ ## install: editable install with dev tools (run inside a virtualenv)
4
+ install:
5
+ pip install -e ".[dev]"
6
+
7
+ ## test: run the test suite
8
+ test:
9
+ pytest
10
+
11
+ ## lint: static checks without modifying files
12
+ lint:
13
+ ruff check src tests
14
+
15
+ ## typecheck: run mypy over src and tests
16
+ typecheck:
17
+ mypy
18
+
19
+ ## fmt: auto-format and auto-fix lint findings
20
+ fmt:
21
+ ruff format src tests
22
+ ruff check --fix src tests
23
+
24
+ ## check: everything CI would run
25
+ check: lint typecheck test
@@ -0,0 +1,307 @@
1
+ Metadata-Version: 2.4
2
+ Name: aisquare-cli
3
+ Version: 0.1.0
4
+ Summary: Portable memory layer for coding agents — persistent user and project context across sessions.
5
+ Project-URL: Homepage, https://aisquare.studio
6
+ Project-URL: Repository, https://github.com/AISquare-Studio/aisquare-cli
7
+ Project-URL: Issues, https://github.com/AISquare-Studio/aisquare-cli/issues
8
+ Author-email: AISquare <anmol@aisquare.studio>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,ai,cli,context,memory
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: pydantic>=2.13.4
21
+ Requires-Dist: rich>=13.7
22
+ Requires-Dist: tomli-w>=1.2.0
23
+ Requires-Dist: typer>=0.26.8
24
+ Provides-Extra: dev
25
+ Requires-Dist: mypy>=1.10; extra == 'dev'
26
+ Requires-Dist: pytest>=9.1.1; extra == 'dev'
27
+ Requires-Dist: ruff>=0.5; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # aisquare
31
+
32
+ **Portable memory layer for coding agents.** `aisquare` installs into agents
33
+ like Claude Code and keeps their context — your preferences and each project's
34
+ conventions — persistent across sessions and machines.
35
+
36
+ > **Status: early.** The full command surface exists and parses arguments.
37
+ > Implemented and backed by a local SQLite store: `init`, `remember`, the full
38
+ > `context` group, `inject`/`why`, the `project` group (incl. a **Repomix
39
+ > codebase snapshot**), `status`/`doctor`, the `config` group, `log`, and the
40
+ > `agents` group — which **installs into Claude Code** (see
41
+ > [Claude Code integration](#claude-code-integration)). The remaining commands
42
+ > (`auth`/cloud, `capture`, `sync`, `connectors`, `policy`) are stubs: each
43
+ > prints `⚠ aisquare <command> is not implemented yet (planned: <tier>)` to
44
+ > stderr and exits `70`. Features land one service module at a time — see
45
+ > [Implementing a feature](#implementing-a-feature-stub--service).
46
+
47
+ ## Implemented
48
+
49
+ ```sh
50
+ aisquare init # set up ~/.aisquare, register & onboard this project
51
+ aisquare remember "prefer pytest over unittest" --user --tag testing
52
+ aisquare context add "run make check before pushing" --project
53
+ aisquare context list # user pool + the active project's pool
54
+ aisquare context search pytest # full-text search (SQLite FTS5)
55
+ aisquare context show a3f2 # by id or unambiguous prefix (git-style)
56
+ aisquare context edit a3f2 # opens the entry in $EDITOR
57
+ aisquare context promote a3f2 # move a project entry into the user pool
58
+ aisquare context remove a3f2 # soft-delete (tombstoned)
59
+ aisquare context export out.md # export in-scope context (md or --format json)
60
+ aisquare context import notes.md # seed context from Markdown bullets or JSON
61
+ aisquare context preview # the context block that would be injected
62
+ aisquare inject # emit that block (and record the injection)
63
+ aisquare why # explain the last injection
64
+ aisquare project list # registered projects (active one marked *)
65
+ aisquare project switch alpha # pin the active project (name or id prefix)
66
+ aisquare project onboard # pack a Repomix snapshot + seed ecosystem facts
67
+ aisquare agents scan # detect installed agents (Claude Code, …)
68
+ aisquare agents connect claude-code # install hooks + ingest CLAUDE.md
69
+ aisquare log # captured prompt history for this project
70
+ aisquare status # health, pools, active project, agents
71
+ aisquare doctor # checks deps/install/hooks/snapshot + how to fix each
72
+ aisquare config set default_pool user # read/write config (get/list/redaction)
73
+ aisquare --json context list # machine-readable output (any command)
74
+ ```
75
+
76
+ Context lives in two pools — `user` (global) and `project` — persisted in a
77
+ SQLite database at `~/.aisquare/context.db`. The **active project** is whichever
78
+ you `project switch` to (pinned in `state.json`), else the one containing your
79
+ working directory; everything scopes to it consistently. Entries carry
80
+ sync-ready metadata (`updated_at`, soft-delete tombstones) and time-sortable,
81
+ prefix-addressable ids from day one.
82
+
83
+ ## Claude Code integration
84
+
85
+ `aisquare agents connect claude-code` makes aisquare an active part of Claude
86
+ Code by writing two hooks into `~/.claude/settings.json` (merged, never
87
+ clobbering your other settings; remove them with `agents disconnect`):
88
+
89
+ - **`SessionStart` → `aisquare hook session-start`** — injects a directive that
90
+ points Claude at the codebase snapshot (skeleton first, full pack on demand)
91
+ and the prompt history, plus your in-scope context — so Claude orients without
92
+ burning tokens grepping for files.
93
+ - **`UserPromptSubmit` → `aisquare hook user-prompt-submit`** — captures how you
94
+ prompt, so Claude can replay your intent (`aisquare log`).
95
+
96
+ The **codebase snapshot** (`project onboard`, or `init`) mirrors the server-side
97
+ [Repomix](https://github.com/yamadashy/repomix) packing for sync-consistency: a
98
+ full pack (`repomix --style xml`), a skeleton (`--compress`), and a per-file
99
+ index (char offsets + token counts), stored under
100
+ `~/.aisquare/projects/<id>/snapshot/`. Requires Node + repomix on PATH (run via
101
+ `npx` otherwise); if neither is present the snapshot is skipped, not fatal.
102
+
103
+ ## Requirements
104
+
105
+ - Python 3.11+
106
+ - Optional, for codebase snapshots: Node.js + [repomix](https://github.com/yamadashy/repomix) (or it runs via `npx`)
107
+
108
+ ## Install
109
+
110
+ ```sh
111
+ pipx install aisquare-cli # recommended — isolated, stable `aisquare` / `asq` on PATH
112
+ # or: pip install aisquare-cli
113
+ ```
114
+
115
+ Installs two equivalent commands: `aisquare` and `asq`. The PyPI distribution is
116
+ **`aisquare-cli`** (the `aisquare` name belongs to the AISquare SDK); the command
117
+ stays `aisquare`. `pipx` is recommended so the Claude Code hook keeps a stable
118
+ path. Run `aisquare doctor` to see what's set up and what's missing.
119
+
120
+ ## Install (development)
121
+
122
+ ```sh
123
+ python3 -m venv .venv
124
+ source .venv/bin/activate
125
+ make install # = pip install -e ".[dev]"
126
+ ```
127
+
128
+ ## Quick check
129
+
130
+ ```sh
131
+ aisquare --help
132
+ aisquare --version
133
+ aisquare doctor # install/deps/integration health
134
+ asq ctx list # aliases work too
135
+ ```
136
+
137
+ ## Command tree
138
+
139
+ ```
140
+ aisquare
141
+ ├── init [path] [--api-key K] [--local] [--agent A]... [--no-onboard] [--reinit] [-y]
142
+ ├── status · doctor · inject · sync · why · log · open
143
+ ├── remember <text> [--user|--project] [--tag T]...
144
+ ├── login · logout · whoami · upgrade · uninstall
145
+ ├── auth status · rotate · token
146
+ ├── agents list · connect <name> · disconnect <name> · scan · status [name]
147
+ ├── connectors list · add <name> · remove <name> · status
148
+ ├── context list · add <text> [--user|--project] [--tag T]... · show <id> · edit <id>
149
+ │ (alias ctx) remove <id> · search <query> · preview · import <file>
150
+ │ export [file] [--format md|json] · promote <id>
151
+ ├── project info · list · switch <name> · link <repo> · onboard [path] [--refresh]
152
+ │ (alias workspace)
153
+ ├── capture status · pause · resume · start · stop
154
+ ├── config list · get <key> · set <key> <value> · redaction <off|standard|strict>
155
+ ├── policy list
156
+ └── enforce status · enable · disable
157
+ ```
158
+
159
+ ### Global flags
160
+
161
+ | Flag | Meaning |
162
+ | --- | --- |
163
+ | `-V`, `--version` | Print the version and exit |
164
+ | `-v`, `--verbose` | Verbose output |
165
+ | `-q`, `--quiet` | Suppress non-essential output |
166
+ | `--json` | Machine-readable JSON on stdout |
167
+ | `--profile NAME` | Configuration profile to use |
168
+ | `--no-color` | Disable coloured output |
169
+
170
+ Global flags go **before** the command: `aisquare --json context list`.
171
+
172
+ ### Exit codes
173
+
174
+ | Code | Meaning |
175
+ | --- | --- |
176
+ | `0` | Success |
177
+ | `2` | Usage error (bad arguments) |
178
+ | `70` | Command not implemented yet |
179
+
180
+ ## Architecture
181
+
182
+ ```
183
+ src/aisquare/
184
+ ├── cli/ # THIN Typer layer: one module per command group.
185
+ │ │ # Parses arguments, then calls exactly one service function.
186
+ │ ├── app.py # root app: global flags, --version, group registration
187
+ │ ├── root.py # top-level commands (init, status, remember, ...)
188
+ │ ├── common.py # shared parsing helpers (e.g. --user/--project → pool)
189
+ │ └── <group>.py
190
+ ├── services/ # SERVICE layer: one module per domain. Real behaviour goes
191
+ │ │ # here. Today every function raises the shared stub.
192
+ │ └── <domain>.py
193
+ ├── core/ # shared infrastructure (already real):
194
+ │ ├── paths.py # ~/.aisquare layout (override with $AISQUARE_HOME)
195
+ │ ├── config.py # typed TOML config load/save (Pydantic + tomllib/tomli-w)
196
+ │ ├── store.py # SQLite context store (ContextStore protocol + open_store)
197
+ │ ├── ids.py # ULID-style, time-sortable, prefix-addressable entry ids
198
+ │ ├── entries.py# shared ContextEntry factory (add / import / onboard)
199
+ │ ├── workspace.py # resolve the active project (pin in state.json, else cwd)
200
+ │ ├── injection.py # assemble the context block + record injections (why)
201
+ │ ├── agents.py # detect agents + install Claude Code hooks (settings.json)
202
+ │ ├── snapshot.py # Repomix codebase pack (full + skeleton + index)
203
+ │ ├── editor.py # launch $EDITOR for `context edit`
204
+ │ ├── state.py # runtime state from the global flags
205
+ │ ├── console.py# Rich console factories honouring --no-color
206
+ │ └── stubs.py # stub() — the consistent not-implemented behaviour
207
+ └── models.py # Pydantic domain models (ContextEntry, DataEnvelope, ...)
208
+ ```
209
+
210
+ Flow: `cli/<group>.py` parses arguments → calls `services/<domain>.py` →
211
+ (today) `core/stubs.py:stub()` prints the not-implemented message and raises
212
+ `typer.Exit(70)`.
213
+
214
+ **What is real today:** `--help` everywhere, `--version`, global-flag parsing
215
+ into `core/state.py`, the `~/.aisquare/` layout, TOML config load/save, the
216
+ SQLite context store (`core/store.py`), and the commands wired to it — `init`,
217
+ `remember`, the full `context` group (`add`, `list`, `show`, `edit`, `remove`,
218
+ `search`, `promote`, `import`, `export`, `preview`), `inject`, `why`, the
219
+ `project` group (`info`, `list`, `switch`, `link`, `onboard`+snapshot),
220
+ `status`, `doctor`, the `config` group (`list`, `get`, `set`, `redaction`),
221
+ `log`, and the `agents` group (`scan`, `list`, `status`, `connect`+hooks,
222
+ `disconnect`). Everything else is a stub.
223
+
224
+ ### `~/.aisquare/` layout
225
+
226
+ ```
227
+ ~/.aisquare/
228
+ ├── config.toml # typed configuration (core/config.py)
229
+ ├── credentials # API keys / tokens
230
+ ├── context.db # SQLite store: context entries, projects, captured prompts
231
+ ├── state.json # small runtime state (e.g. the pinned active project)
232
+ ├── agents.json # registry of connected agents
233
+ ├── projects/ # per-project data — <id>/snapshot/ (Repomix pack + skeleton + index)
234
+ ├── cache/ # disposable cached data (e.g. last_injection.json)
235
+ └── log/ # capture and diagnostic logs
236
+ ```
237
+
238
+ Set `AISQUARE_HOME` to relocate the whole tree (the test suite does this).
239
+
240
+ ## Implementing a feature (stub → service)
241
+
242
+ Each feature is implemented by replacing one `stub(...)` call in one service
243
+ module. The CLI wiring, argument parsing and signatures already exist. Example —
244
+ making `aisquare context add` real:
245
+
246
+ **1. Implement the service** (`src/aisquare/services/context.py`). Replace the
247
+ stub with real logic; keep the existing signature, it is already final.
248
+ Persisted state goes through the `ContextStore` from `core/store.py`. The
249
+ already-implemented `add_entry` is the worked example:
250
+
251
+ ```python
252
+ def add_entry(text: str, pool: Pool | None, tags: list[str]) -> ContextEntry:
253
+ """Add a context entry to the user or project pool."""
254
+ resolved: Pool = pool or load_config().default_pool
255
+ with store_session() as store:
256
+ project_id: str | None = None
257
+ if resolved == "project":
258
+ project = current_project()
259
+ store.ensure_project(project)
260
+ project_id = project.id
261
+ now = datetime.now(tz=UTC)
262
+ entry = ContextEntry(
263
+ id=new_entry_id(), pool=resolved, project_id=project_id, text=text,
264
+ tags=tags, source="cli", created_at=now, updated_at=now,
265
+ )
266
+ return store.add(entry)
267
+ ```
268
+
269
+ **2. Render in the CLI layer** (`src/aisquare/cli/context.py`). The CLI module
270
+ stays thin: parse, call the service, print. Honour `--json` via the runtime
271
+ state:
272
+
273
+ ```python
274
+ @app.command("add")
275
+ def add(text: ..., user: ..., project: ..., tag: ...) -> None:
276
+ """Add a context entry."""
277
+ entry = context_service.add_entry(text, pool=resolve_pool(user, project), tags=tag or [])
278
+ if get_state().json_output:
279
+ typer.echo(entry.model_dump_json())
280
+ else:
281
+ stdout_console().print(f"✓ remembered ({entry.pool}): {entry.text}")
282
+ ```
283
+
284
+ **3. Update the tests.** The walk-based test in `tests/test_stubs.py` asserts
285
+ that every leaf exits with `70`; once a command is real it will fail there —
286
+ add the command to an explicit "implemented" skip-list in that test and write
287
+ real tests for the new behaviour.
288
+
289
+ Rules of thumb:
290
+
291
+ - CLI modules never contain behaviour; services never parse CLI arguments.
292
+ - Services return data; the CLI renders it. (`stub()` printing is the one
293
+ deliberate exception, so all unimplemented commands behave identically.)
294
+ - New shared plumbing goes in `core/`; new domain shapes go in `models.py`.
295
+
296
+ ## Development
297
+
298
+ | Task | Command |
299
+ | --- | --- |
300
+ | Install (editable + dev tools) | `make install` |
301
+ | Run tests | `make test` |
302
+ | Lint | `make lint` |
303
+ | Type-check | `make typecheck` |
304
+ | Format + autofix | `make fmt` |
305
+ | All CI checks | `make check` |
306
+
307
+ Run a single test: `pytest tests/test_stubs.py -k "context add"`.