Skip to main content

Portable memory layer for coding agents — persistent user and project context across sessions.

Project description

aisquare

Portable memory layer for coding agents. aisquare installs into agents like Claude Code and keeps their context — your preferences and each project's conventions — persistent across sessions and machines.

Status: early. The full command surface exists and parses arguments. Implemented and backed by a local SQLite store: init, remember, the full context group, inject/why, the project group (incl. a Repomix codebase snapshot), status/doctor, the config group, log, and the agents group — which installs into Claude Code (see Claude Code integration). The remaining commands (auth/cloud, capture, sync, connectors, policy) are stubs: each prints ⚠ aisquare <command> is not implemented yet (planned: <tier>) to stderr and exits 70. Features land one service module at a time — see Implementing a feature.

Implemented

aisquare init                    # set up ~/.aisquare, register & onboard this project
aisquare remember "prefer pytest over unittest" --user --tag testing
aisquare context add "run make check before pushing" --project
aisquare context list            # user pool + the active project's pool
aisquare context search pytest   # full-text search (SQLite FTS5)
aisquare context show a3f2       # by id or unambiguous prefix (git-style)
aisquare context edit a3f2       # opens the entry in $EDITOR
aisquare context promote a3f2    # move a project entry into the user pool
aisquare context remove a3f2     # soft-delete (tombstoned)
aisquare context export out.md   # export in-scope context (md or --format json)
aisquare context import notes.md # seed context from Markdown bullets or JSON
aisquare context preview         # the context block that would be injected
aisquare inject                  # emit that block (and record the injection)
aisquare why                     # explain the last injection
aisquare project list            # registered projects (active one marked *)
aisquare project switch alpha    # pin the active project (name or id prefix)
aisquare project onboard         # pack a Repomix snapshot + seed ecosystem facts
aisquare agents scan             # detect installed agents (Claude Code, …)
aisquare agents connect claude-code  # install hooks + ingest CLAUDE.md
aisquare log                     # captured prompt history for this project
aisquare status                  # health, pools, active project, agents
aisquare doctor                  # checks deps/install/hooks/snapshot + how to fix each
aisquare config set default_pool user   # read/write config (get/list/redaction)
aisquare --json context list     # machine-readable output (any command)

Context lives in two pools — user (global) and project — persisted in a SQLite database at ~/.aisquare/context.db. The active project is whichever you project switch to (pinned in state.json), else the one containing your working directory; everything scopes to it consistently. Entries carry sync-ready metadata (updated_at, soft-delete tombstones) and time-sortable, prefix-addressable ids from day one.

Claude Code integration

aisquare agents connect claude-code makes aisquare an active part of Claude Code by writing two hooks into ~/.claude/settings.json (merged, never clobbering your other settings; remove them with agents disconnect):

  • SessionStartaisquare hook session-start — injects a directive that points Claude at the codebase snapshot (skeleton first, full pack on demand) and the prompt history, plus your in-scope context — so Claude orients without burning tokens grepping for files.
  • UserPromptSubmitaisquare hook user-prompt-submit — captures how you prompt, so Claude can replay your intent (aisquare log).

The codebase snapshot (project onboard, or init) mirrors the server-side Repomix packing for sync-consistency: a full pack (repomix --style xml), a skeleton (--compress), and a per-file index (char offsets + token counts), stored under ~/.aisquare/projects/<id>/snapshot/. Requires Node + repomix on PATH (run via npx otherwise); if neither is present the snapshot is skipped, not fatal.

Requirements

  • Python 3.11+
  • Optional, for codebase snapshots: Node.js + repomix (or it runs via npx)

Install

pipx install aisquare-cli     # recommended — isolated, stable `aisquare` / `asq` on PATH
# or:  pip install aisquare-cli

Installs two equivalent commands: aisquare and asq. The PyPI distribution is aisquare-cli (the aisquare name belongs to the AISquare SDK); the command stays aisquare. pipx is recommended so the Claude Code hook keeps a stable path. Run aisquare doctor to see what's set up and what's missing.

Install (development)

python3 -m venv .venv
source .venv/bin/activate
make install          # = pip install -e ".[dev]"

Quick check

aisquare --help
aisquare --version
aisquare doctor              # install/deps/integration health
asq ctx list                 # aliases work too

Command tree

aisquare
├── init [path] [--api-key K] [--local] [--agent A]... [--no-onboard] [--reinit] [-y]
├── status · doctor · inject · sync · why · log · open
├── remember <text> [--user|--project] [--tag T]...
├── login · logout · whoami · upgrade · uninstall
├── auth        status · rotate · token
├── agents      list · connect <name> · disconnect <name> · scan · status [name]
├── connectors  list · add <name> · remove <name> · status
├── context     list · add <text> [--user|--project] [--tag T]... · show <id> · edit <id>
│   (alias ctx) remove <id> · search <query> · preview · import <file>
│               export [file] [--format md|json] · promote <id>
├── project     info · list · switch <name> · link <repo> · onboard [path] [--refresh]
│   (alias workspace)
├── capture     status · pause · resume · start · stop
├── config      list · get <key> · set <key> <value> · redaction <off|standard|strict>
├── policy      list
└── enforce     status · enable · disable

Global flags

Flag Meaning
-V, --version Print the version and exit
-v, --verbose Verbose output
-q, --quiet Suppress non-essential output
--json Machine-readable JSON on stdout
--profile NAME Configuration profile to use
--no-color Disable coloured output

Global flags go before the command: aisquare --json context list.

Exit codes

Code Meaning
0 Success
2 Usage error (bad arguments)
70 Command not implemented yet

Architecture

src/aisquare/
├── cli/          # THIN Typer layer: one module per command group.
│   │             # Parses arguments, then calls exactly one service function.
│   ├── app.py    # root app: global flags, --version, group registration
│   ├── root.py   # top-level commands (init, status, remember, ...)
│   ├── common.py # shared parsing helpers (e.g. --user/--project → pool)
│   └── <group>.py
├── services/     # SERVICE layer: one module per domain. Real behaviour goes
│   │             # here. Today every function raises the shared stub.
│   └── <domain>.py
├── core/         # shared infrastructure (already real):
│   ├── paths.py  #   ~/.aisquare layout (override with $AISQUARE_HOME)
│   ├── config.py #   typed TOML config load/save (Pydantic + tomllib/tomli-w)
│   ├── store.py  #   SQLite context store (ContextStore protocol + open_store)
│   ├── ids.py    #   ULID-style, time-sortable, prefix-addressable entry ids
│   ├── entries.py#   shared ContextEntry factory (add / import / onboard)
│   ├── workspace.py #  resolve the active project (pin in state.json, else cwd)
│   ├── injection.py #  assemble the context block + record injections (why)
│   ├── agents.py #   detect agents + install Claude Code hooks (settings.json)
│   ├── snapshot.py #  Repomix codebase pack (full + skeleton + index)
│   ├── editor.py #   launch $EDITOR for `context edit`
│   ├── state.py  #   runtime state from the global flags
│   ├── console.py#   Rich console factories honouring --no-color
│   └── stubs.py  #   stub() — the consistent not-implemented behaviour
└── models.py     # Pydantic domain models (ContextEntry, DataEnvelope, ...)

Flow: cli/<group>.py parses arguments → calls services/<domain>.py → (today) core/stubs.py:stub() prints the not-implemented message and raises typer.Exit(70).

What is real today: --help everywhere, --version, global-flag parsing into core/state.py, the ~/.aisquare/ layout, TOML config load/save, the SQLite context store (core/store.py), and the commands wired to it — init, remember, the full context group (add, list, show, edit, remove, search, promote, import, export, preview), inject, why, the project group (info, list, switch, link, onboard+snapshot), status, doctor, the config group (list, get, set, redaction), log, and the agents group (scan, list, status, connect+hooks, disconnect). Everything else is a stub.

~/.aisquare/ layout

~/.aisquare/
├── config.toml   # typed configuration (core/config.py)
├── credentials   # API keys / tokens
├── context.db    # SQLite store: context entries, projects, captured prompts
├── state.json    # small runtime state (e.g. the pinned active project)
├── agents.json   # registry of connected agents
├── projects/     # per-project data — <id>/snapshot/ (Repomix pack + skeleton + index)
├── cache/        # disposable cached data (e.g. last_injection.json)
└── log/          # capture and diagnostic logs

Set AISQUARE_HOME to relocate the whole tree (the test suite does this).

Implementing a feature (stub → service)

Each feature is implemented by replacing one stub(...) call in one service module. The CLI wiring, argument parsing and signatures already exist. Example — making aisquare context add real:

1. Implement the service (src/aisquare/services/context.py). Replace the stub with real logic; keep the existing signature, it is already final. Persisted state goes through the ContextStore from core/store.py. The already-implemented add_entry is the worked example:

def add_entry(text: str, pool: Pool | None, tags: list[str]) -> ContextEntry:
    """Add a context entry to the user or project pool."""
    resolved: Pool = pool or load_config().default_pool
    with store_session() as store:
        project_id: str | None = None
        if resolved == "project":
            project = current_project()
            store.ensure_project(project)
            project_id = project.id
        now = datetime.now(tz=UTC)
        entry = ContextEntry(
            id=new_entry_id(), pool=resolved, project_id=project_id, text=text,
            tags=tags, source="cli", created_at=now, updated_at=now,
        )
        return store.add(entry)

2. Render in the CLI layer (src/aisquare/cli/context.py). The CLI module stays thin: parse, call the service, print. Honour --json via the runtime state:

@app.command("add")
def add(text: ..., user: ..., project: ..., tag: ...) -> None:
    """Add a context entry."""
    entry = context_service.add_entry(text, pool=resolve_pool(user, project), tags=tag or [])
    if get_state().json_output:
        typer.echo(entry.model_dump_json())
    else:
        stdout_console().print(f"✓ remembered ({entry.pool}): {entry.text}")

3. Update the tests. The walk-based test in tests/test_stubs.py asserts that every leaf exits with 70; once a command is real it will fail there — add the command to an explicit "implemented" skip-list in that test and write real tests for the new behaviour.

Rules of thumb:

  • CLI modules never contain behaviour; services never parse CLI arguments.
  • Services return data; the CLI renders it. (stub() printing is the one deliberate exception, so all unimplemented commands behave identically.)
  • New shared plumbing goes in core/; new domain shapes go in models.py.

Development

Task Command
Install (editable + dev tools) make install
Run tests make test
Lint make lint
Type-check make typecheck
Format + autofix make fmt
All CI checks make check

Run a single test: pytest tests/test_stubs.py -k "context add".

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aisquare_cli-0.1.0.tar.gz (55.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aisquare_cli-0.1.0-py3-none-any.whl (55.9 kB view details)

Uploaded Python 3

File details

Details for the file aisquare_cli-0.1.0.tar.gz.

File metadata

  • Download URL: aisquare_cli-0.1.0.tar.gz
  • Upload date:
  • Size: 55.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for aisquare_cli-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7e45b204229bd0d79c6f16715c998f58834b9a8827546db6bfc2e9e6187a205d
MD5 466d5a36a392eefc01d578938ab51f71
BLAKE2b-256 7ef33212d3d884ab69191671e352e9a1b11d68bc8b099c7e29cde63a57306c70

See more details on using hashes here.

Provenance

The following attestation bundles were made for aisquare_cli-0.1.0.tar.gz:

Publisher: publish.yml on AISquare-Studio/aisquare-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aisquare_cli-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: aisquare_cli-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 55.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for aisquare_cli-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ae04d0394b2c0e76cf740e65ad3e2218ef2e5d25b4ae0fcc930580cfef71bb0e
MD5 549cd4bbfdd53d15727a209f878aa55f
BLAKE2b-256 9eabea48614a852bae1f04bcf0528c61ec3e223d5b112ad6e2cfdcab5863c41e

See more details on using hashes here.

Provenance

The following attestation bundles were made for aisquare_cli-0.1.0-py3-none-any.whl:

Publisher: publish.yml on AISquare-Studio/aisquare-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page