@ainova-systems/intelligence 0.11.0-rc.1
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/LICENSE +21 -0
- package/README.md +46 -0
- package/bin/intelligence.js +59 -0
- package/cli/commands/add.sh +133 -0
- package/cli/commands/doctor.sh +100 -0
- package/cli/commands/init.sh +96 -0
- package/cli/commands/install.sh +84 -0
- package/cli/commands/list.sh +28 -0
- package/cli/commands/migrate.sh +251 -0
- package/cli/commands/registry.sh +51 -0
- package/cli/commands/remove.sh +39 -0
- package/cli/commands/status.sh +34 -0
- package/cli/commands/sync.sh +22 -0
- package/cli/commands/update.sh +59 -0
- package/cli/commands/upgrade.sh +27 -0
- package/cli/intelligence +71 -0
- package/cli/lib/cli-common.sh +149 -0
- package/cli/lib/lockfile.sh +97 -0
- package/cli/lib/manifest.sh +211 -0
- package/cli/lib/registry.sh +141 -0
- package/cli/lib/semver.sh +127 -0
- package/engine/INIT.md +498 -0
- package/engine/agents/intelligence-architect.md +53 -0
- package/engine/agents/intelligence-operator.md +49 -0
- package/engine/docs/ADAPTERS.md +212 -0
- package/engine/docs/CLI.md +91 -0
- package/engine/docs/CONVENTIONS.md +440 -0
- package/engine/rules/intelligence-authoring.md +114 -0
- package/engine/scripts/VERSION +1 -0
- package/engine/scripts/adapters/_template.sh +86 -0
- package/engine/scripts/adapters/agents.sh +299 -0
- package/engine/scripts/adapters/claude.sh +136 -0
- package/engine/scripts/adapters/codex.sh +118 -0
- package/engine/scripts/adapters/copilot.sh +193 -0
- package/engine/scripts/adapters/cursor.sh +146 -0
- package/engine/scripts/adapters/opencode.sh +200 -0
- package/engine/scripts/adapters/pi.sh +256 -0
- package/engine/scripts/lib/common.sh +1602 -0
- package/engine/scripts/lib/layout.sh +51 -0
- package/engine/scripts/lib/migrations.sh +708 -0
- package/engine/scripts/sync.sh +311 -0
- package/engine/scripts/update.sh +237 -0
- package/engine/skills/intelligence-add-agent/SKILL.md +62 -0
- package/engine/skills/intelligence-add-rule/SKILL.md +54 -0
- package/engine/skills/intelligence-add-skill/SKILL.md +53 -0
- package/engine/skills/intelligence-extract-skill/SKILL.md +47 -0
- package/engine/skills/intelligence-install-adapter/SKILL.md +31 -0
- package/engine/skills/intelligence-learn-from-context/SKILL.md +69 -0
- package/engine/skills/intelligence-review-skills/SKILL.md +86 -0
- package/engine/skills/intelligence-sync/SKILL.md +18 -0
- package/engine/skills/intelligence-uninstall-adapter/SKILL.md +42 -0
- package/engine/skills/intelligence-update/SKILL.md +159 -0
- package/package.json +39 -0
- package/registry/index.yaml +15 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: intelligence-architect
|
|
3
|
+
description: "Design and prune the intelligence layer - rule vs skill vs agent, split what grew, remove duplication and hardcoded paths"
|
|
4
|
+
tier: heavy
|
|
5
|
+
access: full
|
|
6
|
+
skills:
|
|
7
|
+
- intelligence-add-rule
|
|
8
|
+
- intelligence-add-agent
|
|
9
|
+
- intelligence-add-skill
|
|
10
|
+
- intelligence-extract-skill
|
|
11
|
+
- intelligence-review-skills
|
|
12
|
+
- intelligence-learn-from-context
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Intelligence architect
|
|
16
|
+
|
|
17
|
+
Owns `<umbrella>/` itself: what the layer is made of, and what it is allowed to grow into. Not the project's code — the instructions that shape how everyone else writes it.
|
|
18
|
+
|
|
19
|
+
## Expertise
|
|
20
|
+
|
|
21
|
+
Where a piece of knowledge belongs. Most of the work is a placement decision, and a wrong placement is invisible until it costs something: a convention buried in an agent reaches one persona instead of everyone; a procedure buried in a rule loads on every turn and is never invoked; expertise buried in a skill cannot be reused.
|
|
22
|
+
|
|
23
|
+
The rest is subtraction — the same thing said in three files, the literal path that breaks on the next move, the defect written down as if it were the design.
|
|
24
|
+
|
|
25
|
+
## What this agent optimises for
|
|
26
|
+
|
|
27
|
+
**Subtraction.** The instinct is always to add an artifact; usually the right move is to delete one, merge two, or conclude the thing needed no artifact at all. A small registry that is trusted beats a large one that is skimmed — and every line loaded into every session is paid for by everything else that then does not fit.
|
|
28
|
+
|
|
29
|
+
The `intelligence-authoring` rule carries the constraints and loads whenever this agent works. It is not repeated here.
|
|
30
|
+
|
|
31
|
+
## Boundaries
|
|
32
|
+
|
|
33
|
+
- **A claim you cannot verify is one you do not get to write** — not a hedged version of it either. Say the gap out loud and leave it open. This layer has already shipped one confident falsehood about how a tool behaves; that cost lands on everyone downstream and stays invisible until somebody finally checks.
|
|
34
|
+
|
|
35
|
+
## Build & verify
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
<sync-cmd> # expect IS_STATUS=ok
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The per-artifact checks are procedure, so they live in the meta-skills rather than in the rule. Reach for the one that fits the change instead of re-deriving the checks:
|
|
42
|
+
|
|
43
|
+
| Skill | Use it to |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `intelligence-add-rule` / `intelligence-add-agent` / `intelligence-add-skill` | author one artifact |
|
|
46
|
+
| `intelligence-extract-skill` | turn an observed workflow into a skill |
|
|
47
|
+
| `intelligence-review-skills` | audit the layer for duplication, drift, size, hardcoded paths |
|
|
48
|
+
| `intelligence-learn-from-context` | fold a session's lessons back into the layer |
|
|
49
|
+
| `intelligence-sync` | project the source to every tool channel |
|
|
50
|
+
| `intelligence-update` | update or migrate the engine |
|
|
51
|
+
| `intelligence-install-adapter` / `intelligence-uninstall-adapter` | turn a tool channel on or off |
|
|
52
|
+
|
|
53
|
+
A change is done when the sync is green and the skill you invoked reports clean. Size is a separate judgement: the caps are ceilings, not quotas, and a short artifact is not a defect.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: intelligence-operator
|
|
3
|
+
description: "Run the engine's mechanical flows - sync, update, adapter install and removal"
|
|
4
|
+
tier: standard
|
|
5
|
+
access: full
|
|
6
|
+
skills:
|
|
7
|
+
- intelligence-sync
|
|
8
|
+
- intelligence-update
|
|
9
|
+
- intelligence-install-adapter
|
|
10
|
+
- intelligence-uninstall-adapter
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Intelligence operator
|
|
14
|
+
|
|
15
|
+
Operates the engine: projects the source tree to every enabled tool channel, updates or migrates the
|
|
16
|
+
engine, turns tool channels on and off. What the layer contains - which rules, agents and skills
|
|
17
|
+
exist and what they say - is `intelligence-architect`'s judgement; this agent runs the machinery
|
|
18
|
+
that ships it.
|
|
19
|
+
|
|
20
|
+
## Expertise
|
|
21
|
+
|
|
22
|
+
The bash-to-skill status contract (`docs/CONVENTIONS.md`, Migration & Module Contract). Every flow
|
|
23
|
+
here is deterministic and fail-closed: `sync.sh` is a pure synchronizer that refuses across an
|
|
24
|
+
un-applied schema, `update.sh` stages, verifies a sentinel and only then commits, and each skill
|
|
25
|
+
branches on `IS_STATUS`. The work is running the right flow, reading the code it returns, and doing
|
|
26
|
+
what that code says - including stopping. The guards carry the decisions, which is why this agent
|
|
27
|
+
runs on the standard tier: the failure mode is a loud refusal and a retry, not a plausible wrong
|
|
28
|
+
answer.
|
|
29
|
+
|
|
30
|
+
## Boundaries
|
|
31
|
+
|
|
32
|
+
- **Every flow goes through its skill.** The steps and their guards live in `intelligence-sync`,
|
|
33
|
+
`intelligence-update`, `intelligence-install-adapter` and `intelligence-uninstall-adapter`;
|
|
34
|
+
improvising around them produces an unverified version of the same work.
|
|
35
|
+
- **Operating is not authoring.** A change to what an artifact says - a rule body, an agent persona,
|
|
36
|
+
a skill's steps - belongs to `intelligence-architect` and the authoring meta-skills. This agent
|
|
37
|
+
ships what exists and never decides what should exist.
|
|
38
|
+
- **A state the engine refuses to resolve goes to a human.** `ambiguous` and every other non-ok
|
|
39
|
+
status exists so that nobody guesses past a refusal; report the code and its detail, do the one
|
|
40
|
+
thing the contract names for it, and stop rather than force a way through.
|
|
41
|
+
|
|
42
|
+
## Build & Verify
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
<sync-cmd> # expect IS_STATUS=ok
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Done is the invoked skill reporting its own success criteria met: `IS_STATUS=ok` (`migrated` counts,
|
|
49
|
+
for an update), per-target counts matching the sources, and no warning left unhandled.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# intelligence-sync: Writing a New Adapter
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
An adapter transforms source prompts (from `intelligence/`) into an IDE-specific format. Each adapter is a single bash file, discovered by filename.
|
|
6
|
+
|
|
7
|
+
## Where adapters live
|
|
8
|
+
|
|
9
|
+
`sync.sh` scans two directories, in this order:
|
|
10
|
+
|
|
11
|
+
| Location | Owner | Survives `update.sh`? |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `intelligence/sync/scripts/adapters/` | **upstream** — the built-ins shipped with the engine | No — the engine replaces this directory wholesale on every update |
|
|
14
|
+
| `intelligence/adapters/` (beside `config.yaml`) | **the project** | Yes — updates never touch project content |
|
|
15
|
+
|
|
16
|
+
Write custom adapters in the project's `intelligence/adapters/`. A file placed in the engine's own `adapters/` is deleted by the next `update.sh`, silently and without a diff to notice. A project adapter whose name matches a built-in **overrides** it (sync prints a `NOTE:` line saying so) — an escape hatch for patching a built-in without forking the engine. An adapter that would serve every project is worth contributing upstream instead.
|
|
17
|
+
|
|
18
|
+
The umbrella folder is not hardcoded anywhere — `intelligence/` above means "whatever directory holds `config.yaml`".
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
1. Copy `intelligence/sync/scripts/adapters/_template.sh` to `intelligence/adapters/<name>.sh`
|
|
23
|
+
2. Replace `<name>` placeholders with your adapter name
|
|
24
|
+
3. Implement the `sync_to_<name>()` function
|
|
25
|
+
4. Add target to `config.yaml`:
|
|
26
|
+
```yaml
|
|
27
|
+
targets:
|
|
28
|
+
<name>: { enabled: true, output: ".<name>" }
|
|
29
|
+
```
|
|
30
|
+
5. Run `bash intelligence/sync/scripts/sync.sh <name>` to test
|
|
31
|
+
|
|
32
|
+
## Adapter Contract
|
|
33
|
+
|
|
34
|
+
### Required Function
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
sync_to_<name>(repo_root, config_file, output_dir)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This is called by `sync.sh` for each enabled target.
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
- `repo_root` -- absolute path to the project root
|
|
44
|
+
- `config_file` -- absolute path to `config.yaml`
|
|
45
|
+
- `output_dir` -- absolute path to output directory (e.g., `/project/.cursor`)
|
|
46
|
+
|
|
47
|
+
### Available Library Functions
|
|
48
|
+
|
|
49
|
+
Source `lib/common.sh` for these utilities:
|
|
50
|
+
|
|
51
|
+
| Function | Description |
|
|
52
|
+
|----------|-------------|
|
|
53
|
+
| `finalize_output_file(file)` | **Call this on every file you write.** Expands layout tokens (`<umbrella>`, `<module>`) and converts CRLF to LF |
|
|
54
|
+
| `normalize_file_to_lf(file)` | LF conversion only — for intermediate files that are not adapter output |
|
|
55
|
+
| `lint_frontmatter(file)` | Warn about unquoted colons, leading tabs, and literal double quotes inside unquoted values (stderr) |
|
|
56
|
+
| `get_frontmatter_value(key, file)` | Extract YAML frontmatter value |
|
|
57
|
+
| `has_frontmatter(file)` | Check for `---` header |
|
|
58
|
+
| `has_paths(file)` | Check for `paths:` field |
|
|
59
|
+
| `get_model(config, ide, tier)` | Resolve model from `models:` override or default |
|
|
60
|
+
| `get_model_default(ide, tier)` | Hardcoded default for `<ide>:<tier>` |
|
|
61
|
+
| `map_access_to_claude_tools(access)` | Tool string for access level |
|
|
62
|
+
| `map_access_to_claude_disallowed(access)` | Disallowed tools string |
|
|
63
|
+
| `read_yaml_list(config, section)` | Read list from `config.yaml` |
|
|
64
|
+
| `resolve_source_dir(repo_root, src)` | Map a source entry to a local dir — `"$repo_root/$src"` for a path, or a shallow clone for a pack reference (`@<name>[/<subpath>]`) or an inline `git+<url>` spec |
|
|
65
|
+
| `source_is_local_path(src)` | True (0) if a source entry is a plain repo-relative path — i.e. neither of the two below. Use this, not a negated `source_is_remote`, whenever a token is about to be pattern-matched against a real directory |
|
|
66
|
+
| `source_is_pack(src)` | True (0) if a source entry references a pack declared under `packs:` (`@<name>`) |
|
|
67
|
+
| `source_is_remote(src)` | True (0) if a source entry is an inline remote `git+` spec |
|
|
68
|
+
| `get_target_field(config, target, field)` | Read a field from a target's config block |
|
|
69
|
+
|
|
70
|
+
### Transformation Patterns
|
|
71
|
+
|
|
72
|
+
Each adapter handles three prompt types. Here's how the built-in adapters approach each:
|
|
73
|
+
|
|
74
|
+
**Rules:**
|
|
75
|
+
|
|
76
|
+
intelligence-sync routes rule content based on **scope** (always-on vs path-scoped) and on which channels each IDE actually reads, to avoid duplicating content into multiple places.
|
|
77
|
+
|
|
78
|
+
| Source | `agents` (AGENTS.md) | `claude` | `cursor` | `copilot` | `codex` | `pi` | `opencode` |
|
|
79
|
+
|--------|----------------------|----------|----------|-----------|---------|------|------------|
|
|
80
|
+
| Always-on (no `paths:`) | inlined as canonical | copied as-is | skipped (Cursor reads AGENTS.md) | skipped (Copilot reads AGENTS.md) | skipped (Codex reads AGENTS.md) | skipped (Pi reads AGENTS.md) | skipped (opencode reads AGENTS.md) |
|
|
81
|
+
| Path-scoped (with `paths:`) | listed by name only | copied as-is | `paths:` → `globs:` in `.mdc` | `paths:` → `applyTo:` in `.instructions.md` | not supported by Codex | copied to `.pi/intelligence-sync/rules/` + surfaced by generated extension | not supported (opencode has no native scoping; users may opt in via `instructions:` globs in `opencode.json`) |
|
|
82
|
+
| Listing | full table in AGENTS.md | n/a | n/a | n/a | n/a | extension prompt snippet | n/a |
|
|
83
|
+
|
|
84
|
+
**Skills:**
|
|
85
|
+
|
|
86
|
+
Skills follow the [Agent Skills open standard](https://agentskills.io). All supported tools read `SKILL.md` directly — no semantic transformation needed. Skill directories are copied **in full** via `copy_skill_bundle` in `lib/common.sh`: bundled resources (`references/`, `scripts/`, `assets/`) ship alongside `SKILL.md`, because skill bodies point at them by relative path and a copy without them is broken at runtime. Markdown files are LF-normalized; everything else is copied byte-for-byte.
|
|
87
|
+
|
|
88
|
+
`copy_skill_bundle` also quotes free-text `SKILL.md` frontmatter (`description`, `argument-hint`) for **every** target, not just the strict-YAML ones: `argument-hint: [pr-number]` is a YAML flow *sequence* unquoted, and Claude Code rejects the skill with "argument-hint must be a string" — it vanishes from the picker with no other signal. Adapters must therefore copy skills through this helper rather than plain `cp`.
|
|
89
|
+
|
|
90
|
+
| Pattern | Used by | Output location |
|
|
91
|
+
|---------|---------|-----------------|
|
|
92
|
+
| Copy skill dirs in full (SKILL.md + bundled resources) | Claude, Cursor, Copilot, Codex, Pi, opencode | `.claude/skills/`, `.cursor/skills/`, `.github/skills/`, `.agents/skills/` (shared by Codex, Pi, opencode) |
|
|
93
|
+
|
|
94
|
+
**Agents:**
|
|
95
|
+
|
|
96
|
+
| Pattern | Used by |
|
|
97
|
+
|---------|---------|
|
|
98
|
+
| Transform frontmatter | Claude (`tier`→`model` via `get_model`, `access`→`tools`/`disallowedTools`), Cursor (`tier`→`model` via `get_model`, `access: readonly`→`readonly: true`) |
|
|
99
|
+
| Transform to `.agent.md` | Copilot (`tier`→`model`, `access: readonly`→restricted `tools` array) |
|
|
100
|
+
| Transform to `.toml` | Codex (`name`, `description`, `model`, `model_reasoning_effort` from tier, `sandbox_mode` from access, `developer_instructions`) |
|
|
101
|
+
| Transform to prompt template | Pi (`.pi/prompts/intelligence-agent-<name>.md`; `readonly` becomes prompt guidance, `full` stays implicit) |
|
|
102
|
+
| Transform to markdown subagent | opencode (`.opencode/agents/<name>.md`; `mode: subagent`, `model` from tier via `get_model`, `permission.edit`/`permission.bash` from access) |
|
|
103
|
+
|
|
104
|
+
Model names come from `get_model(config_file, ide, tier)` in `lib/common.sh`. Defaults are baked into `get_model_default()`; users override per-IDE/tier under `models:` in `config.yaml`. Sync prints a drift report when an override no longer matches the current default.
|
|
105
|
+
|
|
106
|
+
### Layout tokens (required)
|
|
107
|
+
|
|
108
|
+
The engine ships artifacts of its own — the `intelligence-authoring` rule and the `intelligence-architect` agent — and they cannot hardcode the umbrella's folder name, because the project chooses it. They write `<umbrella>` and `<module>` instead, and **every adapter must expand them by calling `finalize_output_file` on each file it writes** (it also does the LF normalization that `normalize_file_to_lf` used to do):
|
|
109
|
+
|
|
110
|
+
| Token | Expands to |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `<umbrella>` | repo-relative umbrella dir (e.g. `Intelligence`) |
|
|
113
|
+
| `<module>` | repo-relative engine module (e.g. `Intelligence/sync`) |
|
|
114
|
+
|
|
115
|
+
Values are exported by `sync.sh` (`IS_UMBRELLA_REL`, `IS_MODULE_REL`), derived from the detected layout. Expansion covers frontmatter and body, so `paths: ["<umbrella>/**"]` reaches Claude's `paths:`, Cursor's `globs:` and Copilot's `applyTo:` carrying the project's real folder name. A file written without `finalize_output_file` ships a literal `<umbrella>` into an IDE — CI fails the build if any generated output still contains a token.
|
|
116
|
+
|
|
117
|
+
### Cleanup Contract
|
|
118
|
+
|
|
119
|
+
Every adapter MUST follow these rules to stay safe alongside others:
|
|
120
|
+
|
|
121
|
+
1. **Clean only adapter-owned subpaths.** Never `rm -rf "$output_dir"` — users hand-author siblings (`.claude/settings.json`, `.cursor/settings.json`, `.pi/settings.json`, `.opencode/opencode.json`, `.github/workflows/`, project-specific commands and extensions). Mirror `claude.sh` (deletes only `rules/`, `agents/`, and per-skill subdirs), `pi.sh` (deletes only `.pi/intelligence-sync/`, the named extension file, and `intelligence-agent-*.md` prompt files), or `opencode.sh` (deletes only `.opencode/agents/` wholesale, and inside `.opencode/commands/` only files that carry the `<!-- Generated by intelligence-sync. Do not edit manually. -->` marker — hand-authored sibling commands survive).
|
|
122
|
+
2. **The cleanup block defines what the adapter owns.** It is the same set `/intelligence-uninstall-adapter` removes when the target is turned off: whatever `sync_to_<name>()` deletes on every re-sync, and nothing more. Keep it obvious in the source — an uninstall reads it.
|
|
123
|
+
3. **Use shared helpers for shared dirs.** `.agents/skills/` is read by Codex, Pi, opencode, and any tool implementing the [Agent Skills open standard](https://agentskills.io). All adapters writing there must call `sync_open_skill_dirs`, which owns the full lifecycle (clean per-skill subfolders, recreate the dir, populate). Do NOT duplicate the clean / `mkdir -p` in the adapter — calling sites become divergent and one adapter inevitably drifts. It is also *shared*: an uninstall removes it only when no other open-standard target remains enabled.
|
|
124
|
+
4. **Document owned paths in `.gitignore`.** Each adapter's INIT.md `.gitignore` block lists exactly the paths it writes — no broader. This lets users keep hand-authored content under the same root tracked.
|
|
125
|
+
5. **The output path is validated before you run.** `sync.sh` calls `validate_output_path` for every adapter (including `agents`): the resolved output must stay inside the repo, and must not be the repo root, the intelligence source tree, or any configured source. `..` is canonicalized away first, so a config line cannot walk out of the repository. Adapters never need to re-check this — but they must not write outside the `output_dir` they are handed.
|
|
126
|
+
|
|
127
|
+
## Example: Minimal Adapter
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
#!/bin/bash
|
|
131
|
+
source "$(dirname "$0")/../lib/common.sh"
|
|
132
|
+
|
|
133
|
+
sync_to_myide() {
|
|
134
|
+
local repo_root="$1"
|
|
135
|
+
local config_file="$2"
|
|
136
|
+
local output_dir="$3"
|
|
137
|
+
|
|
138
|
+
echo "=== MyIDE ==="
|
|
139
|
+
mkdir -p "$output_dir/rules"
|
|
140
|
+
|
|
141
|
+
# Copy rules, strip frontmatter
|
|
142
|
+
while IFS= read -r src; do
|
|
143
|
+
[ -z "$src" ] && continue
|
|
144
|
+
# resolve_source_dir maps a source entry to a local dir: "$repo_root/$src"
|
|
145
|
+
# for a local path, or a shallow clone for a pack reference (`@<name>`)
|
|
146
|
+
# or an inline `git+<url>` spec. A pack's url/ref/mirror are read from
|
|
147
|
+
# config.yaml, which it takes from $IS_CONFIG_FILE (exported by sync.sh)
|
|
148
|
+
# unless you pass the config as a third argument.
|
|
149
|
+
local dir
|
|
150
|
+
dir="$(resolve_source_dir "$repo_root" "$src")"
|
|
151
|
+
[ -d "$dir" ] || continue
|
|
152
|
+
for f in "$dir"/*.md; do
|
|
153
|
+
[ -f "$f" ] || continue
|
|
154
|
+
awk '
|
|
155
|
+
BEGIN { in_fm=0; past_fm=0 }
|
|
156
|
+
{ sub(/\r$/, "") }
|
|
157
|
+
/^---$/ {
|
|
158
|
+
if (!past_fm) { in_fm = !in_fm; if (!in_fm) { past_fm=1 }; next }
|
|
159
|
+
}
|
|
160
|
+
past_fm || !in_fm { print }
|
|
161
|
+
' "$f" > "$output_dir/rules/$(basename "$f")"
|
|
162
|
+
# Every written file goes through finalize_output_file: it expands
|
|
163
|
+
# the <umbrella> / <module> layout tokens and normalizes CRLF -> LF.
|
|
164
|
+
finalize_output_file "$output_dir/rules/$(basename "$f")"
|
|
165
|
+
done
|
|
166
|
+
done < <(read_yaml_list "$config_file" "rules")
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Testing
|
|
171
|
+
|
|
172
|
+
Test your adapter by creating a temporary project with `config.yaml` and running:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
REPO_ROOT=/path/to/test/project bash intelligence/sync/scripts/sync.sh <name>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Verify the output directory contains correctly transformed files. The sync entry point also runs `lint_frontmatter` over every source file before adapters fire — unquoted YAML colons and leading tabs surface as warnings on stderr.
|
|
179
|
+
|
|
180
|
+
## Distributing changes
|
|
181
|
+
|
|
182
|
+
When you ship a new adapter, downstream projects pick it up by running:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
bash intelligence/sync/scripts/update.sh
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`update.sh` clones the upstream repo into a `mktemp -d` directory, shows the diff for `intelligence/sync/scripts/` and `intelligence/sync/INIT.md`, and prompts before overwriting. Project content (`config.yaml`, `rules/`, `agents/`, `skills/`, `adapters/`) is never touched. Pass `--yes` for non-interactive runs; set `REPO_URL=<fork>` to use a fork.
|
|
189
|
+
|
|
190
|
+
This is exactly why a project's own adapter belongs in `intelligence/adapters/` and not in the engine's `scripts/adapters/`: the first is project content, the second is overwritten by the command above.
|
|
191
|
+
|
|
192
|
+
## Built-in Adapters Reference
|
|
193
|
+
|
|
194
|
+
| Adapter | Output | Rules | Skills | Agents |
|
|
195
|
+
|---------|--------|-------|--------|--------|
|
|
196
|
+
| `agents.sh` | `AGENTS.md` (committed) | Always-on inlined; scoped listed | Listed in table | Listed in table |
|
|
197
|
+
| `claude.sh` | `.claude/` | Copy as-is (Claude does not read AGENTS.md) | SKILL.md dirs | tier/access → model/tools |
|
|
198
|
+
| `cursor.sh` | `.cursor/` | Scoped only → `.mdc` + globs | Copy as-is | tier → model |
|
|
199
|
+
| `copilot.sh` | `.github/` | Scoped only → `.instructions.md` | SKILL.md dirs | `.agent.md` |
|
|
200
|
+
| `codex.sh` | `.codex/` + `.agents/skills/` | None (AGENTS.md handles) | SKILL.md dirs in `.agents/skills/` | `.toml` in `.codex/agents/` |
|
|
201
|
+
| `pi.sh` | `.pi/` + `.agents/skills/` | Scoped rules copied + listed by generated extension; always-on via AGENTS.md | SKILL.md dirs in `.agents/skills/` | prompt templates in `.pi/prompts/` |
|
|
202
|
+
| `opencode.sh` | `.opencode/` + `.agents/skills/` | None (AGENTS.md handles always-on; opencode has no native scoping) | SKILL.md dirs in `.agents/skills/`; one slash command per skill in `.opencode/commands/<name>.md` (marker-protected) | markdown subagents in `.opencode/agents/` |
|
|
203
|
+
|
|
204
|
+
### Notes on `agents.sh`
|
|
205
|
+
|
|
206
|
+
Unlike IDE adapters, `agents.sh` emits a single committed markdown file intended for humans and generic LLM tooling. It reads a static `header` block from `config.yaml` (under `targets.agents.header`) and appends auto-generated tables (agents, skills) and a list of rules derived from frontmatter. The output carries a "do not edit manually" marker and is regenerated on every sync.
|
|
207
|
+
|
|
208
|
+
#### Why AGENTS.md inlines always-on rules
|
|
209
|
+
|
|
210
|
+
AGENTS.md is the canonical project doc — Cursor, Copilot, Codex, Pi, and opencode read it natively. Always-on rule content is inlined automatically so all five tools see the same context from one source. Path-scoped rules are NOT inlined (would balloon AGENTS.md in monorepos); they live in tool-specific channels with native scoping (`.cursor/rules/*.mdc` with `globs:`, `.github/instructions/*.instructions.md` with `applyTo:`) or, for Pi, in generated on-demand rule files surfaced by a small extension. opencode has no first-class path-scoped channel; users who need scoped rules can opt into them via `instructions:` globs in `opencode.json`.
|
|
211
|
+
|
|
212
|
+
Claude Code does not read AGENTS.md natively (per [open feature request](https://github.com/anthropics/claude-code/issues/6235)) — its adapter copies all rules into `.claude/rules/`. There is no duplication because Claude does not consume AGENTS.md.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# The intelligence CLI
|
|
2
|
+
|
|
3
|
+
The product interface: one command owning the whole lifecycle of a project's AI intelligence. Installed from npm (`npm i -g @ainova-systems/intelligence`); implemented in bash (`cli/` in this repo) behind a small Node launcher, reusing the sync engine unchanged underneath.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
cd your-project
|
|
7
|
+
intelligence init
|
|
8
|
+
intelligence add @ainova-systems/core
|
|
9
|
+
intelligence sync
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## The CLI (v2) project layout
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
project/
|
|
16
|
+
├── intelligence.yaml # manifest, at the repo root (like package.json)
|
|
17
|
+
├── intelligence.lock # resolved package state — commit it
|
|
18
|
+
├── intelligence/ # the project's own rules/ agents/ skills/
|
|
19
|
+
├── .intelligence/ # gitignored store: packages/<name>/, engine/, backup/
|
|
20
|
+
├── .claude/ .cursor/ … # generated by sync
|
|
21
|
+
└── AGENTS.md
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
No engine code lives in the project. The engine ships inside the npm package; its own rules, agents, meta-skills and docs are staged into `.intelligence/engine/` (re-staged whenever the bundled engine version changes) and reach the outputs as ordinary sources. After a fresh clone, `intelligence install` restores the whole store from the lock.
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
27
|
+
|
|
28
|
+
| Command | Does |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `init [--targets a,b] [--no-sync]` | Detect tools by their marker dirs, write a minimal root manifest, `.gitignore` the store, skeleton `intelligence/`, first sync. `agents` + `claude` are always on. |
|
|
31
|
+
| `add <spec> [--name @s/n] [--no-sync]` | Resolve → fetch → wire sources → manifest entry → lock → sync. Specs: `@scope/name[@range]`, `github:org/repo[#path]`, `git+<url>[@ref][#path]`. |
|
|
32
|
+
| `remove <name>` | Inverse of add: manifest, sources, lock, store. |
|
|
33
|
+
| `install [--frozen] [--force]` | Restore the store exactly from the lock; resolve manifest packages the lock lacks (`--frozen` refuses instead, and fails on sha drift). |
|
|
34
|
+
| `update [name]` | Re-resolve ranges, refetch what moved, rewrite the lock. `ref:`-pinned packages never move here. |
|
|
35
|
+
| `upgrade` | Bring the project to this CLI's engine: restage engine content, apply v2 schema migrations, restamp `sync_version`, sync. |
|
|
36
|
+
| `sync [target]` | Run the bundled engine against the manifest. In a vendored (v1) project it delegates to that project's own engine. |
|
|
37
|
+
| `list` / `status` / `doctor` | Inspect; doctor exits 1 on inconsistencies (unlocked packages, missing store, stale stamp, dead sources). |
|
|
38
|
+
| `registry <list\|add @scope <url>\|remove @scope>` | Bind a scope to a registry repo — rare, once per organization. |
|
|
39
|
+
| `migrate [--dry-run] [--force]` | Convert a vendored setup to the CLI setup. Transactional; see below. |
|
|
40
|
+
|
|
41
|
+
## Packages
|
|
42
|
+
|
|
43
|
+
**A package's name is its identity everywhere**: the `packages:` key in the manifest, the store directory (`.intelligence/packages/@scope/name/` — npm's nesting), and the lock key. What a package provides is a convention: whichever of `rules/`, `agents/`, `skills/` exist at its top level get wired into the matching `sources:` sections by `add`.
|
|
44
|
+
|
|
45
|
+
**Name → repo resolution**, first hit wins:
|
|
46
|
+
|
|
47
|
+
1. `registries:` in the manifest — a scope bound to a *registry repo*: any git repo holding an `index.yaml`. Private registries are therefore just private repos; git auth covers access.
|
|
48
|
+
2. The bundled default index (`registry/index.yaml`) — needed exactly when a name is not a repo (monorepos: `@ainova-systems/core` → `intelligence-dev-packs` at `packs/core`).
|
|
49
|
+
3. Convention: `@org/name` → `https://github.com/org/name.git`, content at the repo root. Zero infrastructure: any repo with the three dirs is already a package.
|
|
50
|
+
|
|
51
|
+
**Versions are git tags.** Ranges (`^1.2.0`, `~1.2.0`, exact, `latest`) match stable `x.y.z` tags (optional `v` prefix) listed via `git ls-remote` — no clone to resolve. `add` without a range picks the highest stable tag and records `^that`. A branch or commit pin is `ref:`, the escape hatch that ranges never touch. Prerelease-suffixed tags are invisible to ranges by design.
|
|
52
|
+
|
|
53
|
+
**The lock is the resolved truth.** Per package: requested range, url, path, resolved tag, commit sha. `install` never consults an index or re-resolves a range — offline restore and reproducibility come from the lock alone; `--frozen` makes any divergence fatal (CI).
|
|
54
|
+
|
|
55
|
+
## Manifest shapes and ownership
|
|
56
|
+
|
|
57
|
+
The engine reads what it always read (`project:`, `sync_version:`, `sources:`, `targets:`, `models:`, `ignore:`, `submodules:`) through its own parsers. Two blocks are **CLI-owned** and never touched by the engine — their keys are full quoted package names, which engine parsers deliberately cannot read:
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
packages:
|
|
61
|
+
"@ainova-systems/core":
|
|
62
|
+
version: "^0.3.0" # or: ref: main / url: + path: (direct git specs)
|
|
63
|
+
registries:
|
|
64
|
+
"@acme": "https://github.com/acme/intelligence-registry.git"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`project.intelligence_dir` names the content dir when it is not `intelligence/`. `sync_version` stays the frozen schema-contract key, stamped by the CLI with the bundled engine version — an npm prerelease suffix never reaches it.
|
|
68
|
+
|
|
69
|
+
## How the CLI drives the engine
|
|
70
|
+
|
|
71
|
+
The engine's CLI mode is an env contract, set by the dispatcher and honored only when `IS_CLI=1` (with every variable unset the engine is byte-identical to the vendored one — CI's `legacy-golden` job proves that on every push):
|
|
72
|
+
|
|
73
|
+
| Variable | Value in CLI mode |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `CONFIG_FILE` | `<root>/intelligence.yaml` |
|
|
76
|
+
| `REPO_ROOT` | the project root |
|
|
77
|
+
| `IS_UMBRELLA_REL` | content dir (`intelligence`) |
|
|
78
|
+
| `IS_MODULE_REL` | `.intelligence/engine` |
|
|
79
|
+
| `IS_SYNC_CMD` | `intelligence sync` (expanded for the `<sync-cmd>` token) |
|
|
80
|
+
| `IS_PROTECTED_DIRS` | `<content-dir>:.intelligence` — restores output-path protection for a root manifest |
|
|
81
|
+
| `IS_SUPPRESS_CLI_NOTE` | set to silence the vendored-flow recommendation note |
|
|
82
|
+
|
|
83
|
+
## migrate: vendored (v1) → CLI (v2)
|
|
84
|
+
|
|
85
|
+
Fail-closed preconditions (clean worktree unless `--force`; no half-migrated state; project schema not newer than the engine; an older schema is first brought up by the engine's own migration chain). Then **stage** — the manifest is `config.yaml` transformed comment-preservingly (module sources → `.intelligence/engine/*`, `@pack/sub` references → store paths, `packs:` → `packages:` entries keeping their `ref:` pins), mirrored packs are *copied* from their mirrors (network untouched, sha carried from the `.pack` stamp), transient packs fetched — **verify** (staged sources exist; per-adapter `enabled`/`output` equality against the old config) — **commit**: store and manifest move in, a real sync must report `IS_STATUS=ok`, and only then are the vendored module, `config.yaml` (backed up to `.intelligence/backup/`) and the mirrors removed. Any earlier failure rolls back to an untouched project. `--dry-run` stages, verifies, prints, writes nothing.
|
|
86
|
+
|
|
87
|
+
## Developing and releasing the CLI
|
|
88
|
+
|
|
89
|
+
- `bash cli/tests/e2e-packages.sh` and `bash cli/tests/e2e-lifecycle.sh` — the hermetic suites CI runs (file:// fixtures).
|
|
90
|
+
- `bash npm/build.sh [version]` assembles `npm/dist/` from the repo (cli + engine + registry index).
|
|
91
|
+
- The `release-npm` workflow (manual dispatch) publishes `<engine>-rc.N` to dist-tag `next` from any ref, and the stable `<engine>` version to `latest` only when dispatched from the matching `v<engine>` tag. Requires the `NPM_TOKEN` secret.
|