@ainova-systems/intelligence 0.11.0-rc.1 → 0.11.0-rc.3

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.
@@ -35,6 +35,19 @@ case "$sub" in
35
35
  { [ -n "$scope" ] && [ -n "$url" ]; } || die "usage: intelligence registry add @scope <registry-repo-url>"
36
36
  case "$scope" in @*/*) die "bind a scope (@acme), not a package name" ;; @*) ;; *) die "scope must start with @" ;; esac
37
37
  require_v2
38
+ # Validate at bind time, not at the first `add`: a registry is a git
39
+ # repo holding index.yaml, and a URL pointing at anything else (a pack
40
+ # repo, a typo) is a mistake worth hearing about now. The binding is
41
+ # still written — the registry may simply not be reachable yet.
42
+ index="$(_fetch_index "${url#git+}")"
43
+ if [ -n "$index" ]; then
44
+ n="$(qmap_keys "$index" "packages" | grep -c . || true)"
45
+ echo " index.yaml reachable — $n package(s) offered"
46
+ else
47
+ echo " WARN: no index.yaml found at $url — 'intelligence add $scope/<name>' will fail until it has one." >&2
48
+ echo " A registry is a git repo with index.yaml at its root (see the README of" >&2
49
+ echo " https://github.com/ainova-systems/intelligence-registry for the format)." >&2
50
+ fi
38
51
  qmap_set_value "$IP_ROOT/intelligence.yaml" "registries" "$scope" "$url"
39
52
  echo "$scope -> $url"
40
53
  ;;
@@ -0,0 +1,84 @@
1
+ #!/bin/bash
2
+ # intelligence search [term] — what the registries offer, and what this project
3
+ # already has. The catalogue view `list` deliberately is not: `list` answers
4
+ # "what does this project depend on", `search` answers "what could it".
5
+ #
6
+ # Sources, in resolution order (a name found earlier wins, exactly as `add`
7
+ # resolves it): every scope bound in the manifest, then the bundled index.
8
+ set -euo pipefail
9
+ source "$CLI_DIR/lib/cli-common.sh"
10
+
11
+ term="${1:-}"
12
+
13
+ detect_project
14
+ manifest=""
15
+ [ "$IP_MODE" = "v2" ] && manifest="$IP_ROOT/intelligence.yaml"
16
+
17
+ # State of one package name in this project: installed / declared / available.
18
+ pkg_state() {
19
+ local name="$1"
20
+ [ -n "$manifest" ] || { printf 'available'; return 0; }
21
+ local declared=0 k
22
+ while IFS= read -r k; do
23
+ [ "$k" = "$name" ] && declared=1
24
+ done < <(qmap_keys "$manifest" "packages")
25
+ if [ "$declared" -eq 0 ]; then
26
+ printf 'available'
27
+ elif [ -d "$IP_ROOT/.intelligence/packages/$name" ]; then
28
+ printf 'installed %s' "$(qmap_field "$IP_ROOT/intelligence.lock" "packages" "$name" "resolved")"
29
+ else
30
+ printf 'declared (run install)'
31
+ fi
32
+ }
33
+
34
+ seen=" "
35
+ rows=0
36
+ emit_index() {
37
+ local index="$1" origin="$2" name desc state
38
+ [ -n "$index" ] && [ -f "$index" ] || return 0
39
+ while IFS= read -r name; do
40
+ [ -n "$name" ] || continue
41
+ case "$seen" in *" $name "*) continue ;; esac
42
+ if [ -n "$term" ]; then
43
+ desc="$(qmap_field "$index" "packages" "$name" "description")"
44
+ case "$name$desc" in
45
+ *"$term"*) ;;
46
+ *) continue ;;
47
+ esac
48
+ fi
49
+ seen="$seen$name "
50
+ desc="$(qmap_field "$index" "packages" "$name" "description")"
51
+ state="$(pkg_state "$name")"
52
+ printf '%-34s %-22s %s\n' "$name" "$state" "${desc:-$(qmap_field "$index" "packages" "$name" "url")}"
53
+ [ "$origin" = "bundled" ] || printf '%-34s %s\n' "" " via $origin"
54
+ rows=$((rows + 1))
55
+ done < <(qmap_keys "$index" "packages")
56
+ }
57
+
58
+ if [ -n "$manifest" ]; then
59
+ while IFS= read -r scope; do
60
+ [ -n "$scope" ] || continue
61
+ url="$(qmap_value "$manifest" "registries" "$scope")"
62
+ [ -n "$url" ] || continue
63
+ index="$(_fetch_index "${url#git+}")"
64
+ if [ -z "$index" ]; then
65
+ echo " WARN: registry for $scope is unreachable or has no index.yaml: $url" >&2
66
+ continue
67
+ fi
68
+ emit_index "$index" "$scope -> $url"
69
+ done < <(qmap_keys "$manifest" "registries")
70
+ fi
71
+ emit_index "$(default_index_file)" "bundled"
72
+
73
+ if [ "$rows" -eq 0 ]; then
74
+ if [ -n "$term" ]; then
75
+ echo "Nothing matching '$term' in the configured registries."
76
+ else
77
+ echo "No registries hold any packages."
78
+ fi
79
+ echo "Any git repo with rules/, agents/ or skills/ is a package: intelligence add @org/repo"
80
+ exit 0
81
+ fi
82
+
83
+ echo ""
84
+ echo "add one: intelligence add <name> | private registry: intelligence registry add @scope <repo-url>"
package/cli/intelligence CHANGED
@@ -46,6 +46,7 @@ Usage: intelligence <command> [args]
46
46
  update [name] Re-resolve version ranges and rewrite the lockfile
47
47
  upgrade Upgrade the project to this CLI's engine (restage + restamp)
48
48
  list Installed packages with requested/locked versions
49
+ search [term] What the registries offer, and what this project has
49
50
  sync [target] Render intelligence to every enabled tool (.claude, AGENTS.md, ...)
50
51
  registry <list|add|remove> Bind a scope to a registry index
51
52
  migrate [--dry-run] Convert a vendored (v1) setup to the CLI setup
@@ -102,7 +102,11 @@ stage_engine_content() {
102
102
  rm -rf "$store"
103
103
  mkdir -p "$store"
104
104
  local d s name skip
105
- for d in rules agents docs; do
105
+ # `scripts` travels too: engine-shipped skills reference `<module>/scripts/…`
106
+ # (adapters, docs) and `<module>` resolves here, so the paths they hand an
107
+ # agent must exist. Running that sync.sh directly is harmless — outside CLI
108
+ # mode it fails closed rather than generating against the wrong layout.
109
+ for d in rules agents docs scripts; do
106
110
  [ -d "$IS_ENGINE_DIR/$d" ] && cp -R "$IS_ENGINE_DIR/$d" "$store/$d"
107
111
  done
108
112
  if [ -d "$IS_ENGINE_DIR/skills" ]; then
@@ -134,10 +134,13 @@ qmap_set() {
134
134
  if (c > 0 && substr(line, 1, c - 1) == field) { print fieldline(); done = 1; next }
135
135
  print; next
136
136
  }
137
- { print }
137
+ { last = $0; print }
138
138
  END {
139
139
  flush_key(); flush_block()
140
140
  if (!blockseen) {
141
+ # A block appended to a file that does not end blank would
142
+ # otherwise glue itself onto the previous section.
143
+ if (last != "") print ""
141
144
  print block ":"
142
145
  print keyline()
143
146
  print fieldline()
@@ -159,10 +162,14 @@ qmap_set_value() {
159
162
  s = substr($0, 4); q = index(s, "\"")
160
163
  if (q > 0 && substr(s, 1, q - 1) == key) { print entry(); done = 1; next }
161
164
  }
162
- { print }
165
+ { last = $0; print }
163
166
  END {
164
167
  if (inb && !done) { print entry(); done = 1 }
165
- if (!blockseen) { print block ":"; print entry() }
168
+ if (!blockseen) {
169
+ if (last != "") print ""
170
+ print block ":"
171
+ print entry()
172
+ }
166
173
  }
167
174
  '
168
175
  }
package/cli/lib/semver.sh CHANGED
@@ -39,9 +39,12 @@ semver_match() {
39
39
  ""|"*"|latest) return 0 ;;
40
40
  esac
41
41
  local op="" base="$range"
42
+ # Both strip patterns are quoted: an unquoted `~` in ${range#~} is a tilde
43
+ # EXPANSION (it becomes $HOME), so the prefix never strips and every
44
+ # `~x.y.z` range silently matches nothing.
42
45
  case "$range" in
43
- "^"*) op="^"; base="${range#^}" ;;
44
- "~"*) op="~"; base="${range#~}" ;;
46
+ "^"*) op="^"; base="${range#'^'}" ;;
47
+ "~"*) op="~"; base="${range#'~'}" ;;
45
48
  esac
46
49
  base="${base#v}"
47
50
  semver_is_stable "$base" || return 1
package/engine/INIT.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  The sync engine is already installed in this directory's `scripts/` subfolder. Your job: analyze this codebase, ask the user targeted questions, and generate the project-specific configuration and content.
4
4
 
5
+ > **The intelligence CLI is the recommended setup for new projects.** If `intelligence --version` works on this machine (or `npm i -g @ainova-systems/intelligence` is acceptable), prefer `intelligence init` + `intelligence add <package>` over this document — it automates what this bootstrap does by hand and adds versioned packages with a lockfile (see `docs/CLI.md`). This document remains the authoritative bootstrap for the **vendored** setup, which stays fully supported; a vendored project can convert later with `intelligence migrate`.
6
+
5
7
  **Execute phases sequentially. Do not skip or combine phases. Each phase has a gate — wait for it before proceeding.**
6
8
 
7
9
  ## Bootstrap: install the engine if it isn't here yet
@@ -110,9 +110,10 @@ The engine ships artifacts of its own — the `intelligence-authoring` rule and
110
110
  | Token | Expands to |
111
111
  |---|---|
112
112
  | `<umbrella>` | repo-relative umbrella dir (e.g. `Intelligence`) |
113
- | `<module>` | repo-relative engine module (e.g. `Intelligence/sync`) |
113
+ | `<module>` | repo-relative engine module (e.g. `Intelligence/sync`; CLI setup: `.intelligence/engine`) |
114
+ | `<sync-cmd>` | the sync invocation — vendored: `bash <module>/scripts/sync.sh`, CLI setup: `intelligence sync` (`IS_SYNC_CMD`) |
114
115
 
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
+ Values are exported by `sync.sh` (`IS_UMBRELLA_REL`, `IS_MODULE_REL`; `IS_SYNC_CMD` comes from the CLI in CLI mode), 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
 
117
118
  ### Cleanup Contract
118
119
 
@@ -56,9 +56,10 @@ An artifact shipped *by the engine* cannot write the umbrella's name down — th
56
56
  | Token | Expands to | Example |
57
57
  |---|---|---|
58
58
  | `<umbrella>` | repo-relative umbrella dir | `Intelligence` |
59
- | `<module>` | repo-relative engine module | `Intelligence/sync` |
59
+ | `<module>` | repo-relative engine module | `Intelligence/sync` (CLI setup: `.intelligence/engine`) |
60
+ | `<sync-cmd>` | how a reader re-runs the sync | vendored: `bash Intelligence/sync/scripts/sync.sh`; CLI setup: `intelligence sync` |
60
61
 
61
- Expansion covers frontmatter and body alike, so `paths: ["<umbrella>/**"]` reaches Claude's `paths:`, Cursor's `globs:` and Copilot's `applyTo:` already carrying the project's real folder name. Project-authored artifacts may use the tokens too, but they have no reason to — they can simply name their own folders.
62
+ Expansion covers frontmatter and body alike, so `paths: ["<umbrella>/**"]` reaches Claude's `paths:`, Cursor's `globs:` and Copilot's `applyTo:` already carrying the project's real folder name. Project-authored artifacts may use the tokens too, but they have no reason to — they can simply name their own folders. `<sync-cmd>` exists because "run a sync" is spelled differently per setup: engine content says the token, and `IS_SYNC_CMD` (set by the CLI) picks the spelling; unset, it reproduces the vendored command string exactly.
62
63
 
63
64
  A custom adapter belongs in the umbrella's `adapters/`, never in the module's `sync/scripts/adapters/` — the module is replaced wholesale on every update, so an adapter written there disappears at the next one. See `docs/ADAPTERS.md`.
64
65
 
@@ -401,6 +402,19 @@ Bash emits `IS_STATUS=<code> [IS_DETAIL=...]` on stdout and exits with the match
401
402
 
402
403
  **Module model.** The engine self-locates by its own path; it does not assume a folder name (`sync/` by convention). Each `<umbrella>/<module>/` is self-contained: its own `scripts/`(+`VERSION`), `skills/`, `INIT.md`, `docs/`. Modules update independently and never touch sibling modules or project content (`rules/`, `agents/`, non-meta `skills/`) nor `config.yaml` beyond the idempotent additive `sources.skills` line and the `sync_version` key.
403
404
 
405
+ **CLI mode (0.11.0+).** The `intelligence` CLI drives the same engine from outside the repo (the npm install dir); the project arrives through an env contract honored only when `IS_CLI=1` — with every variable unset the engine behaves byte-identically to the vendored flow (CI's `legacy-golden` job asserts that):
406
+
407
+ | Variable | CLI-mode value |
408
+ |---|---|
409
+ | `CONFIG_FILE` | `<root>/intelligence.yaml` (the root manifest) |
410
+ | `REPO_ROOT` | project root |
411
+ | `IS_UMBRELLA_REL` / `IS_MODULE_REL` | content dir (default `intelligence`) / `.intelligence/engine` |
412
+ | `IS_SYNC_CMD` | `intelligence sync` (feeds the `<sync-cmd>` token) |
413
+ | `IS_PROTECTED_DIRS` | colon-separated dirs `validate_output_path` must refuse — restores the source-tree protection a root manifest would otherwise disable |
414
+ | `IS_SUPPRESS_CLI_NOTE` | silences the vendored-flow recommendation NOTE (stderr-only either way) |
415
+
416
+ In CLI projects the `intelligence-sync` and `intelligence-update` meta-skills are not installed — `intelligence sync` / `intelligence update` replace them; the other meta-skills ship unchanged and reach outputs from `.intelligence/engine/skills`. `sync.sh` in CLI mode still never migrates: an outdated stamp exits `needs-update` and the CLI's own `upgrade` closes the gap. See `docs/CLI.md` for the full CLI surface.
417
+
404
418
  ## .gitignore Pattern
405
419
 
406
420
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainova-systems/intelligence",
3
- "version": "0.11.0-rc.1",
3
+ "version": "0.11.0-rc.3",
4
4
  "description": "Build, version and distribute AI agent intelligence across your organization — one CLI, versioned Intelligence Packages, and a sync engine for Claude Code, Cursor, Copilot, Codex, Pi and OpenCode.",
5
5
  "bin": {
6
6
  "intelligence": "bin/intelligence.js"
@@ -6,10 +6,17 @@
6
6
  # @org/name -> https://github.com/org/name.git, content at the repo root.
7
7
  # Organizations override or extend this per scope with `intelligence registry
8
8
  # add @scope <registry-repo-url>`.
9
+ #
10
+ # This copy travels inside the CLI package. The same index lives at
11
+ # https://github.com/ainova-systems/intelligence-registry and can be bound
12
+ # explicitly (`intelligence registry add @ainova-systems <url>`) to read the
13
+ # newest entries without waiting for a CLI release.
9
14
  packages:
10
15
  "@ainova-systems/core":
11
16
  url: "https://github.com/ainova-systems/intelligence-dev-packs.git"
12
17
  path: "packs/core"
18
+ description: "Engineering discipline for AI-first development: context engineering, artifact-derived status, review and diagnosis loops."
13
19
  "@ainova-systems/spec":
14
20
  url: "https://github.com/ainova-systems/intelligence-dev-packs.git"
15
21
  path: "packs/spec"
22
+ description: "Spec-driven delivery: specifications, decision records, sliced execution."