Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install

- name: Package-manager secret guard
run: bun run check:package-secrets

- name: Typecheck
run: bun run typecheck

- name: Test
run: bun test

- name: Build
run: bun run build
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ sdk/node_modules/
*.db-wal
.env
.env.local
bun.lock
.hasna/
26 changes: 22 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# AGENTS.md — How AI Agents Should Use @hasna/configs
# AGENTS.md — How AI Agents Should Use @hasna/instructions

## MCP Setup

Expand Down Expand Up @@ -39,10 +39,15 @@ configs mcp install --claude --profile minimal # 3 tools (lowest context cost
### Restore Secrets on New Machine
```
1. Import backup: configs import backup.tar.gz (CLI)
2. render_template("npmrc", use_env=true) → fills {{NPM_AUTH_TOKEN}} from env
3. apply_config("npmrc") → writes to ~/.npmrc
2. Keep ~/.npmrc env-backed: //registry.npmjs.org/:_authToken=${NPM_TOKEN}
3. Load NPM_TOKEN from the shell, CI secret store, or an approved vault at runtime
4. Run configs package-manager-scan --home --fail-on-findings before committing
```

Do not render or write a literal npm token into `~/.npmrc`. The safe home
credential flow stores only the scoped registry line plus `${NPM_TOKEN}` and
keeps the token value in the runtime environment or secret manager.

### Sync Project Configs
```
1. sync_known(agent="claude") → sync all Claude Code configs
Expand Down Expand Up @@ -84,7 +89,20 @@ Configs automatically redacts secrets before storing. Patterns detected:
- Key names: `*_API_KEY`, `*_TOKEN`, `*_SECRET`, `*_PASSWORD`, `*_CREDENTIAL`, `*_AUTH*`
- Value patterns: npm tokens, GitHub tokens, Anthropic/OpenAI keys, AWS keys, JWTs, Slack tokens

Redacted values become `{{VAR_NAME}}` template placeholders. Use `render_template` with `use_env=true` to fill them from environment variables.
Redacted values become `{{VAR_NAME}}` template placeholders, except `.npmrc`
auth tokens, which are converted to npm's `${NPM_TOKEN}` environment reference
so home and repo package-manager configs do not store literal tokens.

## Package-Manager Secret Guard

Use `configs package-manager-scan --fail-on-findings .` in repo CI or
pre-commit hooks. Add `--home` for local operator checks. The guard scans repo
`.npmrc`, home `.npmrc`, Bun config, lockfiles, and shell profiles, and prints
only paths, line numbers, rule names, surfaces, and tracked status.

Bun release-age quarantine must remain enabled. `minimumReleaseAgeExcludes`
should contain only exact `@hasna/<package>` names; do not use wildcard or
third-party excludes.

## Constraints

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ src/cli/index.tsx — 28 CLI commands (Commander + chalk)
src/mcp/index.ts — 16 MCP tools (lean stubs + CONFIGS_PROFILE)
src/server/index.ts — Hono REST API (port 3457, serves dashboard + ?fields=)
src/index.ts — library exports
sdk/ — @hasna/configs-sdk (zero-dep fetch client)
sdk/ — @hasna/instructions-sdk (zero-dep fetch client)
dashboard/ — React+Vite (5 pages)
```

Expand Down
39 changes: 39 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# syntax=docker/dockerfile:1
# @hasna/instructions self_hosted service — ARM64 / Bun.
# Default CMD runs instructions-serve (cloud / PURE REMOTE per Amendment A1: the
# serve process reads/writes RDS Postgres directly with @hasna/contracts API-key
# auth). The ECS one-shot migration task overrides the command with `... migrate`.

FROM --platform=linux/arm64 oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lock ./
COPY dashboard/package.json ./dashboard/package.json
RUN bun install --frozen-lockfile --ignore-scripts

FROM --platform=linux/arm64 oven/bun:1 AS build
WORKDIR /app
COPY package.json bun.lock tsconfig.json ./
COPY --from=deps /app/node_modules ./node_modules
COPY src ./src
COPY scripts ./scripts
RUN bun run build:server

FROM --platform=linux/arm64 oven/bun:1 AS runner
WORKDIR /app
# Amazon RDS global CA bundle so TLS to the shared RDS succeeds under
# verify-full-capable clients.
COPY docker/rds-global-bundle.pem /etc/ssl/certs/rds-global-bundle.pem
ENV NODE_ENV=production \
HASNA_INSTRUCTIONS_STORAGE_MODE=cloud \
NODE_EXTRA_CA_CERTS=/etc/ssl/certs/rds-global-bundle.pem \
PGSSLROOTCERT=/etc/ssl/certs/rds-global-bundle.pem \
INSTRUCTIONS_NO_OPEN=true \
HOST=0.0.0.0 \
PORT=8080
COPY package.json bun.lock ./
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
EXPOSE 8080
# Fail-closed: instructions-serve /v1 refuses to serve without a cloud DSN +
# signing secret (503), and /ready reports DB reachability — no silent stub.
CMD ["bun", "dist/server/index.js"]
157 changes: 118 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
# @hasna/configs
# @hasna/instructions

AI coding agent configuration manager — store, version, apply, and share all your AI coding configs. CLI + MCP + REST API + Dashboard.
> Formerly `@hasna/configs`. Same tool, renamed. The MCP server keeps the
> `configs` name aliased so existing fleet MCP configs keep working.

[![npm](https://img.shields.io/npm/v/@hasna/configs)](https://www.npmjs.com/package/@hasna/configs)
AI coding agent instruction & configuration manager — store, version, apply, and
share all your AI coding configs and instruction sources. CLI + MCP + HTTP API
(`instructions-serve`) + generated SDK + Dashboard.

[![npm](https://img.shields.io/npm/v/@hasna/instructions)](https://www.npmjs.com/package/@hasna/instructions)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)

## Install

```bash
npm install -g @hasna/configs
npm install -g @hasna/instructions
```

## CLI Usage

```bash
configs --help
configs list # compact, paged summary
configs list --verbose # expanded metadata, still paged
configs list --json # full machine-readable records
configs show <slug> # full metadata + content
configs inspect <slug> # alias for show
configs profile resolve
configs profile apply --auto
instructions --help
instructions list # compact, paged summary
instructions list --verbose # expanded metadata, still paged
instructions list --json # full machine-readable records
instructions show <slug> # full metadata + content
instructions inspect <slug> # alias for show
instructions profile resolve
instructions profile apply --auto
instructions package-manager-scan --fail-on-findings .
instructions package-manager-scan --home --fail-on-findings .
```

Collection commands are compact by default to keep agent terminals and context
Expand All @@ -32,66 +39,136 @@ small. Human output is capped at 20 rows unless you pass `--limit`; use
- `--json` preserves full machine-readable records for automation.
- `show`/`inspect` and `snapshot show` print full config or snapshot content.

## Package-Manager Secret Guard

`instructions package-manager-scan` blocks package-manager credential ingress without
printing credential values. It scans repo `.npmrc` files, home `.npmrc`, Bun
config, lockfiles, and shell profiles, then reports only path, line, rule,
surface, and tracked status.

Use it in CI or pre-commit hooks:

```bash
instructions package-manager-scan --fail-on-findings .
```

Use `--home` for local operator scans:

```bash
instructions package-manager-scan --home --fail-on-findings .
```

Safe `.npmrc` auth uses npm's environment reference, not a rendered literal:

```ini
@hasna:registry=https://registry.npmjs.org/
# Add the npm auth-token entry with an environment reference, not a literal value.
```

Bun release-age quarantine must stay enabled. Keep
`minimumReleaseAgeExcludes` narrowed to exact `@hasna/<package>` names only;
wildcards and third-party package names fail the guard.

## MCP Server

```bash
configs-mcp
instructions-mcp # or the aliased binary: configs-mcp
```

Agent-facing MCP tools follow the same gradual disclosure model. `list_configs`
and `list_profiles` return paged compact envelopes by default and accept
`limit`, `cursor`, and `verbose`. `apply_config` and `apply_profile` omit
The MCP server identity is intentionally kept as `configs` so the existing
fleet MCP configuration keeps working; the fleet rename is a separate controlled
step. Agent-facing MCP tools follow the same gradual disclosure model.
`list_configs` and `list_profiles` return paged compact envelopes by default and
accept `limit`, `cursor`, and `verbose`. `apply_config` and `apply_profile` omit
`previous_content` and `new_content` unless `verbose: true` is passed. Use
`get_config` when full config content is needed.

## HTTP mode
## HTTP mode (MCP)

```bash
configs-mcp --http # http://127.0.0.1:8807/mcp
MCP_HTTP=1 configs-mcp
instructions-mcp --http # http://127.0.0.1:8807/mcp
MCP_HTTP=1 instructions-mcp
```

Health: `GET http://127.0.0.1:8807/health`. MCP is also mounted on `configs-serve` at `/mcp`.
Health: `GET http://127.0.0.1:8807/health`. MCP is also mounted on
`instructions-serve` at `/mcp`.

## REST API
## HTTP API server (`instructions-serve`)

```bash
configs-serve
instructions-serve
```

## Storage Sync
Surfaces:

- `GET /health`, `GET /ready`, `GET /version` → `{ status, version, mode }`
- `GET /openapi.json`, `GET /v1/openapi.json` → the OpenAPI 3.1 document the SDK
is generated from.
- `/v1/*` — versioned cloud API (configs, profiles, snapshots, stats).
- `/api/*` — the local dashboard/REST surface.

### Cloud mode (self-hosted, Amendment A1 pure-remote)

When `HASNA_INSTRUCTIONS_DATABASE_URL` is set the `/v1` API reads/writes the
shared cloud Postgres **directly** (no local sync/cache in the service) and every
`/v1` request is authenticated with a `@hasna/contracts` API key
(`x-api-key` or `Authorization: Bearer`). Reads need `instructions:read`, writes
need `instructions:write` (an `instructions:*` key satisfies both).

```bash
# apply the schema (idempotent; never clobbers existing tables)
instructions-serve migrate

# mint a key with the SAME signing secret the server verifies with
contracts issue-key --app instructions --scopes 'instructions:*'
```

Env: `HASNA_INSTRUCTIONS_DATABASE_URL` (DSN) and
`HASNA_INSTRUCTIONS_API_SIGNING_KEY` (HMAC signing secret; `HASNA_API_SIGNING_KEY`
and `API_KEY_SIGNING_SECRET` are also accepted). Client apps use
`INSTRUCTIONS_API_URL` + `INSTRUCTIONS_API_KEY` — never a DSN.

## SDK

`@hasna/instructions-sdk` ships a zero-dependency typed client. The versioned
`InstructionsV1Client` is generated from the serve OpenAPI document
(`bun run generate:sdk`).

## Storage Sync (local CLI)

This package supports optional remote storage sync through a package-local Postgres connection:
The local CLI supports optional remote storage sync through a package-local
Postgres connection:

```bash
export HASNA_CONFIGS_DATABASE_URL=postgres://...
configs storage status
configs storage push
configs storage pull
configs storage sync
export HASNA_INSTRUCTIONS_DATABASE_URL=postgres://...
instructions storage status
instructions storage push
instructions storage pull
instructions storage sync
```

The MCP server also exposes `storage_status`, `storage_push`, `storage_pull`, and `storage_sync`.
The MCP server also exposes `storage_status`, `storage_push`, `storage_pull`, and
`storage_sync`.

## Data Directory

Data is stored in `~/.hasna/configs/`.
Local data is stored in `~/.hasna/configs/` (unchanged, for fleet continuity).

## Session Instruction Rendering

`configs session plan` and `configs session apply` render OpenIdentities and
OpenConfigs instruction sources into provider-native files for Claude, Codex,
Cursor, OpenCode, and Codewith.
`instructions session plan` and `instructions session apply` render
OpenIdentities and instruction sources into provider-native files for Claude,
Codex, Cursor, OpenCode, and Codewith.

```bash
configs session plan \
instructions session plan \
--tool codewith \
--profile account999 \
--identity-export ./instructions.json \
--source project:repo-rules=./CODEWITH.md \
--json

configs session apply \
instructions session apply \
--tool codex \
--profile account999 \
--identity-export ./instructions.json
Expand All @@ -106,12 +183,14 @@ writes local snapshots before mutating managed files.

## Machine-aware Profiles

`configs init` now seeds two platform profiles:
`instructions init` seeds two platform profiles:

- `linux-arm64` for `linux-node-a` / `linux-node-b`
- `macos-arm64` for `macos-node-a` / `macos-node-b`

These profiles resolve machine variables like `{{WORKSPACE_ROOT}}`, `{{BUN_BIN_DIR}}`, `{{BUN_PATH}}`, and `{{PATH_PREFIX}}`, so synced configs can be portable across Linux and macOS arm64 machines.
These profiles resolve machine variables like `{{WORKSPACE_ROOT}}`,
`{{BUN_BIN_DIR}}`, `{{BUN_PATH}}`, and `{{PATH_PREFIX}}`, so synced configs can be
portable across Linux and macOS arm64 machines.

They also include project dashboard variables used by agent-managed project
workflows:
Expand All @@ -121,7 +200,7 @@ workflows:
- `{{PROJECT_DASHBOARD_SNAPSHOTS_DIR}}` -> `.hasna/project/dashboard/snapshots`
- `{{PROJECT_CHANNEL_PREFIX}}` -> `iproj-`

`configs init` and `bun run seed` seed the
`instructions init` and `bun run seed` seed the
`agent-managed-project-dashboard-standard` reference. It documents the standard
`.hasna/project` layout, `projects dashboard *` commands, provider panel
commands, `#iproj-*` channel naming, durable todos/goal workflow, and the rule
Expand Down
Loading
Loading