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

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 (35) hide show
  1. package/cli/commands/add.sh +17 -0
  2. package/cli/commands/doctor.sh +28 -7
  3. package/cli/commands/init.sh +51 -20
  4. package/cli/commands/install.sh +9 -3
  5. package/cli/commands/migrate.sh +11 -2
  6. package/cli/commands/registry.sh +53 -26
  7. package/cli/commands/remove.sh +13 -1
  8. package/cli/commands/search.sh +8 -7
  9. package/cli/commands/status.sh +5 -4
  10. package/cli/commands/sync.sh +19 -1
  11. package/cli/commands/update.sh +4 -0
  12. package/cli/commands/upgrade.sh +50 -9
  13. package/cli/lib/cli-common.sh +28 -47
  14. package/cli/lib/manifest.sh +79 -0
  15. package/cli/lib/registry.sh +77 -14
  16. package/cli/lib/semver.sh +5 -2
  17. package/engine/agents/intelligence-operator.md +4 -4
  18. package/engine/docs/ADAPTERS.md +2 -1
  19. package/engine/docs/CLI.md +14 -11
  20. package/engine/docs/CONVENTIONS.md +5 -3
  21. package/engine/rules/intelligence-authoring.md +4 -4
  22. package/engine/scripts/ENGINE_SHA +1 -0
  23. package/engine/scripts/lib/common.sh +6 -2
  24. package/engine/skills/intelligence-add-agent/SKILL.md +7 -7
  25. package/engine/skills/intelligence-add-rule/SKILL.md +5 -5
  26. package/engine/skills/intelligence-add-skill/SKILL.md +5 -5
  27. package/engine/skills/intelligence-extract-skill/SKILL.md +2 -2
  28. package/engine/skills/intelligence-install-adapter/SKILL.md +6 -6
  29. package/engine/skills/intelligence-learn-from-context/SKILL.md +1 -1
  30. package/engine/skills/intelligence-review-skills/SKILL.md +5 -5
  31. package/engine/skills/intelligence-sync/SKILL.md +5 -7
  32. package/engine/skills/intelligence-uninstall-adapter/SKILL.md +2 -2
  33. package/engine/skills/intelligence-update/SKILL.md +6 -0
  34. package/package.json +1 -1
  35. package/registry/index.yaml +4 -0
@@ -81,6 +81,23 @@ if [ "$mode" = "registry" ]; then
81
81
  echo " resolving $name via $RES_VIA -> $url${path:+ ($path)}"
82
82
  fi
83
83
 
84
+ # The source must exist before anything else is attempted — a name that fell
85
+ # through to the convention is often just a typo, and that must say so, not
86
+ # die mid-pipeline with no message.
87
+ if ! GIT_TERMINAL_PROMPT=0 git ls-remote "$url" >/dev/null 2>&1; then
88
+ echo "ERROR: no reachable repository at $url" >&2
89
+ if [ "${RES_VIA:-}" = "convention" ]; then
90
+ echo " No registry declares '$name', so it resolved by convention to github.com/${name#@}." >&2
91
+ echo " Either the name is misspelled ('intelligence search' lists what registries offer)," >&2
92
+ echo " the repo is private (check your git credentials), or its registry is not added yet:" >&2
93
+ echo " intelligence registry add <registry-repo-url>" >&2
94
+ suggest_similar "$manifest" "$name"
95
+ else
96
+ echo " Declared by ${RES_VIA:-the manifest} — check the URL and your git access." >&2
97
+ fi
98
+ exit 1
99
+ fi
100
+
84
101
  # Pick the version: an explicit ref pins; otherwise the highest stable tag
85
102
  # matching the range (npm's behaviour, `^highest` becoming the requested range
86
103
  # when none was given).
@@ -43,14 +43,23 @@ else
43
43
  ok "sync_version $stamp matches the engine"
44
44
  fi
45
45
 
46
- if [ -f "$IP_ROOT/.intelligence/engine/.version" ]; then
47
- staged="$(tr -d ' \t\r\n' < "$IP_ROOT/.intelligence/engine/.version")"
48
- [ "$staged" = "$eng" ] && ok "engine content staged ($staged)" || warn "staged engine content is $staged, bundled is $eng — run 'intelligence install'"
49
- else
50
- warn "engine content not staged — run 'intelligence install'"
46
+ # The engine-content package must sit at the bundled engine version — the
47
+ # content documents exactly the scripts the CLI executes.
48
+ sync_locked="$(qmap_field "$lock" "packages" "$SYNC_PKG_NAME" "resolved")"
49
+ if [ -n "$sync_locked" ]; then
50
+ if [ "${sync_locked#v}" = "$eng" ]; then
51
+ ok "$SYNC_PKG_NAME at $sync_locked (matches the engine)"
52
+ else
53
+ warn "$SYNC_PKG_NAME locked at $sync_locked but the engine is $eng — run 'intelligence upgrade'"
54
+ fi
55
+ fi
56
+ if [ -d "$IP_ROOT/.intelligence/engine" ]; then
57
+ warn "stale .intelligence/engine directory (pre-package layout) — run 'intelligence upgrade'"
51
58
  fi
52
59
 
53
- # Manifest packages: names valid, locked, installed.
60
+ # Manifest packages: names valid, locked, installed — and the lock's source
61
+ # still the one resolution would pick today (a registry silently re-pointing
62
+ # an installed name is the dependency-confusion tail this check cuts off).
54
63
  while IFS= read -r name; do
55
64
  [ -n "$name" ] || continue
56
65
  assert_valid_pkg_name "$name"
@@ -60,6 +69,13 @@ while IFS= read -r name; do
60
69
  warn "$name is locked but not installed — run 'intelligence install'"
61
70
  else
62
71
  ok "$name @ $(qmap_field "$lock" "packages" "$name" "resolved")"
72
+ if [ -z "$(qmap_field "$manifest" "packages" "$name" "url")" ]; then
73
+ locked_url="$(qmap_field "$lock" "packages" "$name" "url")"
74
+ resolve_package_source "$manifest" "$name" 2>/dev/null || true
75
+ if [ -n "${RES_URL:-}" ] && [ -n "$locked_url" ] && [ "$RES_URL" != "$locked_url" ]; then
76
+ warn "$name now resolves to $RES_URL but the lock pins $locked_url — a registry re-pointed the name; verify before 'intelligence update'"
77
+ fi
78
+ fi
63
79
  fi
64
80
  done < <(qmap_keys "$manifest" "packages")
65
81
 
@@ -85,8 +101,13 @@ for section in rules agents skills; do
85
101
  esac
86
102
  if [ ! -d "$IP_ROOT/$src" ]; then
87
103
  case "$src" in
104
+ # Store content is restorable state: absent means un-installed.
88
105
  .intelligence/*) warn "sources.$section '$src' missing — run 'intelligence install'" ;;
89
- *) warn "sources.$section '$src' does not exist" ;;
106
+ # A project-owned dir that does not exist yet is a legitimate
107
+ # state — a project consuming only packages authors nothing of
108
+ # its own — and the engine simply skips it. Declared up front so
109
+ # authoring later needs no config edit.
110
+ *) ok "sources.$section '$src' — not created yet (optional)" ;;
90
111
  esac
91
112
  fi
92
113
  done < <(read_yaml_list "$manifest" "$section")
@@ -8,15 +8,21 @@
8
8
  set -euo pipefail
9
9
  source "$CLI_DIR/lib/cli-common.sh"
10
10
 
11
- targets_arg="" no_sync=0
11
+ targets_arg="" no_sync=0 content_dir="intelligence" bare=0
12
12
  while [ $# -gt 0 ]; do
13
13
  case "$1" in
14
14
  --targets) shift; targets_arg="${1:-}" ;;
15
+ --dir) shift; content_dir="${1:-}" ;;
15
16
  --no-sync) no_sync=1 ;;
17
+ --bare) bare=1 ;;
16
18
  *) die "unknown option '$1'" ;;
17
19
  esac
18
20
  shift || true
19
21
  done
22
+ [ -n "$content_dir" ] || die "--dir needs a directory name"
23
+ case "$content_dir" in
24
+ /*|*..*|.intelligence|.intelligence/*) die "--dir must be a plain directory inside the repo, and not the package store" ;;
25
+ esac
20
26
 
21
27
  detect_project
22
28
  case "$IP_MODE" in
@@ -41,23 +47,32 @@ fi
41
47
  manifest="$root/intelligence.yaml"
42
48
  {
43
49
  echo "# Intelligence manifest — what this project's AI tooling knows and where it goes."
44
- echo "# Managed by the intelligence CLI (https://github.com/ainova-systems/intelligence-sync)."
50
+ echo "# Managed by the intelligence CLI. Reference: https://github.com/ainova-systems/intelligence-sync"
45
51
  echo "project:"
46
52
  echo " name: \"$(basename "$root")\""
53
+ [ "$content_dir" = "intelligence" ] || echo " intelligence_dir: \"$content_dir\""
47
54
  echo ""
55
+ echo "# Schema version of this manifest. The CLI keeps it in step with its engine."
48
56
  echo "sync_version: \"$(bundled_engine_version)\""
49
57
  echo ""
58
+ echo "# Where content comes from. Nothing here has to exist: a directory that is"
59
+ echo "# absent is skipped, so a project consuming only packages authors nothing of"
60
+ echo "# its own. To write your own, create the directory and sync — no config edit:"
61
+ echo "# $content_dir/rules/context.md an always-on rule; describe the project,"
62
+ echo "# its stack, and the constraints to respect"
63
+ echo "# $content_dir/agents/<name>.md a persona (frontmatter: name, description,"
64
+ echo "# tier: heavy|standard|light, access: full|readonly)"
65
+ echo "# $content_dir/skills/<name>/SKILL.md a procedure invoked as /<name>"
66
+ echo "# '.intelligence/' is CLI-managed (packages + engine content) — never edit it."
50
67
  echo "sources:"
51
68
  echo " rules:"
52
- echo " - \"intelligence/rules\""
53
- echo " - \".intelligence/engine/rules\""
69
+ echo " - \"$content_dir/rules\""
54
70
  echo " agents:"
55
- echo " - \"intelligence/agents\""
56
- echo " - \".intelligence/engine/agents\""
71
+ echo " - \"$content_dir/agents\""
57
72
  echo " skills:"
58
- echo " - \"intelligence/skills\""
59
- echo " - \".intelligence/engine/skills\""
73
+ echo " - \"$content_dir/skills\""
60
74
  echo ""
75
+ echo "# Generated output, one entry per tool. Add or drop tools freely."
61
76
  echo "targets:"
62
77
  for t in $targets; do
63
78
  case "$t" in
@@ -65,18 +80,24 @@ manifest="$root/intelligence.yaml"
65
80
  *) echo " $t: { enabled: true, output: \".$t\" }" ;;
66
81
  esac
67
82
  done
83
+ echo ""
84
+ echo "# Packages are managed with 'intelligence add' / 'search' / 'list':"
85
+ echo "# intelligence add @ainova-systems/core from the registry"
86
+ echo "# intelligence add github:org/repo any repo with rules|agents|skills"
87
+ echo "# packages:"
88
+ echo "# \"@ainova-systems/core\":"
89
+ echo "# version: \"^0.3.0\""
90
+ echo ""
91
+ echo "# Registries are a TRUST LIST: git repos holding an index.yaml, consulted"
92
+ echo "# in order — the first to declare a name wins, then the CLI's bundled index,"
93
+ echo "# then the @org/name -> github.com/org/name convention. Adding one is a"
94
+ echo "# committed, reviewable act of trust in the names it declares; sources are"
95
+ echo "# pinned by url+sha in intelligence.lock, and overriding a bundled name"
96
+ echo "# warns loudly. Managed with 'intelligence registry add|remove'."
97
+ echo "# registries:"
98
+ echo "# - \"https://github.com/acme/intelligence-registry.git\""
68
99
  } > "$manifest"
69
100
 
70
- mkdir -p "$root/intelligence/rules" "$root/intelligence/agents" "$root/intelligence/skills"
71
- if [ ! -f "$root/intelligence/rules/context.md" ]; then
72
- cat > "$root/intelligence/rules/context.md" <<EOF
73
- # Project Context
74
-
75
- <!-- Always-on context every AI tool receives. Describe what this project is,
76
- its stack, and the constraints an agent must respect. -->
77
- EOF
78
- fi
79
-
80
101
  # The package store is restorable state, never committed — the npm model.
81
102
  if [ ! -f "$root/.gitignore" ] || ! grep -q '^\.intelligence/' "$root/.gitignore"; then
82
103
  {
@@ -86,9 +107,19 @@ if [ ! -f "$root/.gitignore" ] || ! grep -q '^\.intelligence/' "$root/.gitignore
86
107
  } >> "$root/.gitignore"
87
108
  fi
88
109
 
89
- ensure_engine_staged "$root"
110
+ # The engine's own content (meta-skills, authoring rule, engine agents) is an
111
+ # ordinary package: auto-selected, opted out with --bare, removable later with
112
+ # 'intelligence remove @ainova-systems/sync --force'.
113
+ if [ "$bare" -eq 0 ]; then
114
+ sync_pkg_entry "$manifest"
115
+ sync_pkg_install "$root"
116
+ fi
117
+
90
118
  echo "initialized: intelligence.yaml (targets:$(printf ' %s' $targets))"
91
- echo "next: add intelligence packages — e.g. 'intelligence add @ainova-systems/core'"
119
+ [ "$bare" -eq 1 ] && echo " bare setup: no packages — engine meta-skills not installed"
120
+ echo " add packages: intelligence add @ainova-systems/core (browse: intelligence search)"
121
+ echo " write your own: create $content_dir/rules/context.md, then intelligence sync"
122
+ echo " docs: https://github.com/ainova-systems/intelligence-sync#readme"
92
123
 
93
124
  if [ "$no_sync" -eq 0 ]; then
94
125
  cd "$root"
@@ -22,8 +22,6 @@ require_v2
22
22
  manifest="$IP_ROOT/intelligence.yaml"
23
23
  lock="$IP_ROOT/intelligence.lock"
24
24
 
25
- ensure_engine_staged "$IP_ROOT"
26
-
27
25
  # Manifest packages missing from the lock: resolve them now (npm install
28
26
  # semantics). Under --frozen that is an error instead.
29
27
  missing=""
@@ -36,6 +34,12 @@ done < <(qmap_keys "$manifest" "packages")
36
34
  if [ -n "$missing" ]; then
37
35
  [ "$frozen" -eq 1 ] && die "lockfile is missing packages:$missing — run 'intelligence install' without --frozen"
38
36
  for name in $missing; do
37
+ # The engine-content package needs no remote resolution: its pin IS
38
+ # the bundled engine version and it seeds from the bundle, offline.
39
+ if [ "$name" = "$SYNC_PKG_NAME" ]; then
40
+ sync_pkg_install "$IP_ROOT"
41
+ continue
42
+ fi
39
43
  url="$(qmap_field "$manifest" "packages" "$name" "url")"
40
44
  path="$(qmap_field "$manifest" "packages" "$name" "path")"
41
45
  ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
@@ -67,7 +71,9 @@ if [ -f "$lock" ]; then
67
71
  continue
68
72
  fi
69
73
  got="$(fetch_package "$url" "$resolved" "$path" "$IP_ROOT/$rel")"
70
- if [ -n "$sha" ] && [ "$got" != "$sha" ]; then
74
+ # An empty sha (bundle-seeded content on a dev build) means unknown,
75
+ # not moved — only two known-and-different shas are a finding.
76
+ if [ -n "$sha" ] && [ -n "$got" ] && [ "$got" != "$sha" ]; then
71
77
  if [ "$frozen" -eq 1 ]; then
72
78
  die "$name: fetched $got but the lock pins $sha — the ref moved; refusing under --frozen"
73
79
  fi
@@ -59,7 +59,9 @@ trap cleanup_stage EXIT INT TERM
59
59
  mkdir -p "$stage/.intelligence/packages"
60
60
 
61
61
  echo "== staging =="
62
- stage_engine_content "$stage/.intelligence/engine"
62
+ # Engine content arrives as the @ainova-systems/sync package, seeded from the
63
+ # bundle (fetch_package's guard — no network).
64
+ sync_pkg_sha="$(fetch_package "$SYNC_PKG_URL" "v$eng" "$SYNC_PKG_PATH" "$stage/$SYNC_PKG_STORE")"
63
65
 
64
66
  # Legacy pack name -> v2 package name (@org/repo derived from the url), plus
65
67
  # per-pack source facts. Recorded as rows for the config rewrite and the lock.
@@ -117,7 +119,7 @@ rewrite_source_value() {
117
119
  local v="$1" pack name rest
118
120
  case "$v" in
119
121
  "$module_rel"/rules|"$module_rel"/agents|"$module_rel"/skills)
120
- printf '.intelligence/engine/%s' "${v##*/}"
122
+ printf '%s/%s' "$SYNC_PKG_STORE" "${v##*/}"
121
123
  return 0 ;;
122
124
  @*)
123
125
  rest="${v#@}"
@@ -168,6 +170,13 @@ while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
168
170
  [ -n "$ref" ] && qmap_set "$manifest_stage" "packages" "$name" "ref" "$ref"
169
171
  done < "$pack_rows"
170
172
 
173
+ # The engine's own content rides along as a package, pinned to the engine.
174
+ qmap_set "$manifest_stage" "packages" "$SYNC_PKG_NAME" "version" "$eng"
175
+ qmap_set "$manifest_stage" "packages" "$SYNC_PKG_NAME" "url" "$SYNC_PKG_URL"
176
+ qmap_set "$manifest_stage" "packages" "$SYNC_PKG_NAME" "path" "$SYNC_PKG_PATH"
177
+ printf '%s\037%s\037%s\037%s\037%s\037%s\n' \
178
+ "$SYNC_PKG_NAME" "$eng" "$SYNC_PKG_URL" "$SYNC_PKG_PATH" "v$eng" "$sync_pkg_sha" >> "$lock_rows"
179
+
171
180
  # ---- Verify ---------------------------------------------------------------
172
181
  echo "== verifying staged state =="
173
182
  fail=0
@@ -1,8 +1,10 @@
1
1
  #!/bin/bash
2
- # intelligence registry <list | add @scope <registry-repo-url> | remove @scope>
2
+ # intelligence registry <list | add <registry-repo-url> [--force] | remove <url>>
3
3
  #
4
- # A registry binding is rare, per-scope configuration (npm's @scope:registry
5
- # analog): the URL of a git repo whose index.yaml maps names to sources.
4
+ # The registries block is a TRUST LIST: git repos holding an index.yaml, in
5
+ # trust order. Adding one is a committed, reviewable act — after it, every
6
+ # name that registry declares installs like a bundled one. No scope labels:
7
+ # names carry their own scope, and the first registry to declare a name wins.
6
8
  set -euo pipefail
7
9
  source "$CLI_DIR/lib/cli-common.sh"
8
10
 
@@ -11,13 +13,19 @@ case "$sub" in
11
13
  list)
12
14
  detect_project
13
15
  if [ "$IP_MODE" = "v2" ] && [ -f "$IP_ROOT/intelligence.yaml" ]; then
14
- echo "Project bindings (intelligence.yaml):"
16
+ echo "Project registries (intelligence.yaml, trust order):"
15
17
  found=0
16
- while IFS= read -r scope; do
17
- [ -n "$scope" ] || continue
18
+ while IFS= read -r url; do
19
+ [ -n "$url" ] || continue
18
20
  found=1
19
- echo " $scope -> $(qmap_value "$IP_ROOT/intelligence.yaml" "registries" "$scope")"
20
- done < <(qmap_keys "$IP_ROOT/intelligence.yaml" "registries")
21
+ index="$(_fetch_index "${url#git+}")"
22
+ if [ -n "$index" ]; then
23
+ n="$(qmap_keys "$index" "packages" | grep -c . || true)"
24
+ echo " $url — $n package(s)"
25
+ else
26
+ echo " $url — UNREACHABLE or no index.yaml"
27
+ fi
28
+ done < <(registries_list "$IP_ROOT/intelligence.yaml")
21
29
  [ "$found" -eq 0 ] && echo " (none)"
22
30
  fi
23
31
  bundled="$(default_index_file)"
@@ -31,34 +39,53 @@ case "$sub" in
31
39
  echo "Fallback: @org/name -> https://github.com/org/name.git"
32
40
  ;;
33
41
  add)
34
- scope="${2:-}"; url="${3:-}"
35
- { [ -n "$scope" ] && [ -n "$url" ]; } || die "usage: intelligence registry add @scope <registry-repo-url>"
36
- case "$scope" in @*/*) die "bind a scope (@acme), not a package name" ;; @*) ;; *) die "scope must start with @" ;; esac
42
+ url="${2:-}"
43
+ force=0
44
+ [ "${3:-}" = "--force" ] && force=1
45
+ [ -n "$url" ] || die "usage: intelligence registry add <registry-repo-url> [--force]"
46
+ case "$url" in
47
+ @*) die "registries are added by URL — names carry their own scope. To install a package: intelligence add $url/<name>" ;;
48
+ esac
37
49
  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.
50
+ # Fail closed: a URL that cannot produce an index is not a registry,
51
+ # and recording it would turn a typo here into a confusing failure at
52
+ # the first add. --force covers the one honest case: the registry is
53
+ # not published (or reachable) yet.
42
54
  index="$(_fetch_index "${url#git+}")"
43
55
  if [ -n "$index" ]; then
44
56
  n="$(qmap_keys "$index" "packages" | grep -c . || true)"
45
- echo " index.yaml reachable — $n package(s) offered"
57
+ echo " index.yaml reachable — $n package(s):"
58
+ qmap_keys "$index" "packages" | sed 's/^/ /'
59
+ elif [ "$force" -eq 1 ]; then
60
+ echo " WARN: no index.yaml at $url — recording anyway (--force)." >&2
46
61
  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
62
+ echo "ERROR: no index.yaml found at $url — not adding it." >&2
63
+ echo " A registry is a git repo with index.yaml at its root, mapping names to sources:" >&2
64
+ echo " packages:" >&2
65
+ echo " \"@acme/backend\":" >&2
66
+ echo " url: \"https://github.com/acme/intelligence.git\"" >&2
67
+ echo " path: \"packs/backend\" # omit when the repo IS the package" >&2
68
+ echo " Format and an example: https://github.com/ainova-systems/intelligence-registry" >&2
69
+ echo " A single repository needs no registry at all: intelligence add github:org/repo" >&2
70
+ echo " Recording it ahead of a registry that does not exist yet: --force" >&2
71
+ exit 1
50
72
  fi
51
- qmap_set_value "$IP_ROOT/intelligence.yaml" "registries" "$scope" "$url"
52
- echo "$scope -> $url"
73
+ registries_add "$IP_ROOT/intelligence.yaml" "$url"
74
+ echo "added: $url"
53
75
  ;;
54
76
  remove)
55
- scope="${2:-}"
56
- [ -n "$scope" ] || die "usage: intelligence registry remove @scope"
77
+ url="${2:-}"
78
+ [ -n "$url" ] || die "usage: intelligence registry remove <url>"
57
79
  require_v2
58
- qmap_delete_key "$IP_ROOT/intelligence.yaml" "registries" "$scope"
59
- echo "unbound $scope"
80
+ registries_remove "$IP_ROOT/intelligence.yaml" "$url"
81
+ # The retired flat-map form also stored scope keys — drop one if the
82
+ # argument names it, so old manifests clean up with the old command.
83
+ case "$url" in
84
+ @*) qmap_delete_key "$IP_ROOT/intelligence.yaml" "registries" "$url" ;;
85
+ esac
86
+ echo "removed: $url"
60
87
  ;;
61
88
  *)
62
- die "usage: intelligence registry <list | add @scope <url> | remove @scope>"
89
+ die "usage: intelligence registry <list | add <url> [--force] | remove <url>>"
63
90
  ;;
64
91
  esac
@@ -6,15 +6,27 @@ source "$CLI_DIR/lib/cli-common.sh"
6
6
  name="${1:-}"
7
7
  [ -n "$name" ] || die "usage: intelligence remove <@scope/name>"
8
8
  shift || true
9
- no_sync=0
9
+ no_sync=0 force=0
10
10
  for a in "$@"; do
11
11
  case "$a" in
12
12
  --no-sync) no_sync=1 ;;
13
+ --force) force=1 ;;
13
14
  *) die "unknown option '$a'" ;;
14
15
  esac
15
16
  done
16
17
 
17
18
  require_v2
19
+
20
+ # Removing the engine-content package guts the outputs (meta-skills, the
21
+ # authoring rule, both engine agents disappear) while everything still
22
+ # reports ok — legal, but never by accident.
23
+ if [ "$name" = "$SYNC_PKG_NAME" ] && [ "$force" -eq 0 ]; then
24
+ echo "ERROR: $SYNC_PKG_NAME is the engine's own content — removing it drops every" >&2
25
+ echo " intelligence-* meta-skill, the authoring rule and the engine agents from" >&2
26
+ echo " the outputs. The sync engine itself keeps working (it ships with the CLI)." >&2
27
+ echo " If that is what you want: intelligence remove $SYNC_PKG_NAME --force" >&2
28
+ exit 1
29
+ fi
18
30
  assert_valid_pkg_name "$name"
19
31
  manifest="$IP_ROOT/intelligence.yaml"
20
32
  lock="$IP_ROOT/intelligence.lock"
@@ -33,6 +33,9 @@ pkg_state() {
33
33
 
34
34
  seen=" "
35
35
  rows=0
36
+ # emit_index <index-file> <origin-label>
37
+ # Registries are consulted in trust order; the first to declare a name wins,
38
+ # so a name already seen is not repeated (that IS the resolution order).
36
39
  emit_index() {
37
40
  local index="$1" origin="$2" name desc state
38
41
  [ -n "$index" ] && [ -f "$index" ] || return 0
@@ -56,17 +59,15 @@ emit_index() {
56
59
  }
57
60
 
58
61
  if [ -n "$manifest" ]; then
59
- while IFS= read -r scope; do
60
- [ -n "$scope" ] || continue
61
- url="$(qmap_value "$manifest" "registries" "$scope")"
62
+ while IFS= read -r url; do
62
63
  [ -n "$url" ] || continue
63
64
  index="$(_fetch_index "${url#git+}")"
64
65
  if [ -z "$index" ]; then
65
- echo " WARN: registry for $scope is unreachable or has no index.yaml: $url" >&2
66
+ echo " WARN: registry unreachable or missing index.yaml: $url" >&2
66
67
  continue
67
68
  fi
68
- emit_index "$index" "$scope -> $url"
69
- done < <(qmap_keys "$manifest" "registries")
69
+ emit_index "$index" "$url"
70
+ done < <(registries_list "$manifest")
70
71
  fi
71
72
  emit_index "$(default_index_file)" "bundled"
72
73
 
@@ -81,4 +82,4 @@ if [ "$rows" -eq 0 ]; then
81
82
  fi
82
83
 
83
84
  echo ""
84
- echo "add one: intelligence add <name> | private registry: intelligence registry add @scope <repo-url>"
85
+ echo "add one: intelligence add <name> | private registry: intelligence registry add <repo-url>"
@@ -13,12 +13,13 @@ case "$IP_MODE" in
13
13
  else
14
14
  echo "Lockfile: none (no packages added yet)"
15
15
  fi
16
- if [ -f "$IP_ROOT/.intelligence/engine/.version" ]; then
17
- echo "Store: .intelligence/engine at $(tr -d ' \t\r\n' < "$IP_ROOT/.intelligence/engine/.version")"
16
+ sync_locked="$(qmap_field "$IP_ROOT/intelligence.lock" "packages" "$SYNC_PKG_NAME" "resolved")"
17
+ if [ -n "$sync_locked" ]; then
18
+ echo "Content: $SYNC_PKG_NAME at $sync_locked"
18
19
  else
19
- echo "Store: not staged — run 'intelligence install'"
20
+ echo "Content: no $SYNC_PKG_NAME (bare setup)"
20
21
  fi
21
- echo "Engine: $(bundled_engine_version) (bundled)"
22
+ echo "Engine: $(bundled_engine_version) (bundled with the CLI)"
22
23
  ;;
23
24
  legacy)
24
25
  echo "Project: $IP_ROOT (vendored v1 setup)"
@@ -6,7 +6,25 @@ source "$CLI_DIR/lib/cli-common.sh"
6
6
  detect_project
7
7
  case "$IP_MODE" in
8
8
  v2)
9
- ensure_engine_staged "$IP_ROOT"
9
+ # Fail closed, never skip silently: a store-backed source that is not
10
+ # on disk means the project is not installed (or predates the package
11
+ # model) — syncing would quietly drop whole rule sets.
12
+ missing="" stale=0
13
+ for section in rules agents skills; do
14
+ while IFS= read -r src; do
15
+ [ -n "$src" ] || continue
16
+ case "$src" in
17
+ .intelligence/engine/*) stale=1 ;;
18
+ .intelligence/*) [ -d "$IP_ROOT/$src" ] || missing="$missing $src" ;;
19
+ esac
20
+ done < <(read_yaml_list "$IP_ROOT/intelligence.yaml" "$section")
21
+ done
22
+ if [ "$stale" -eq 1 ]; then
23
+ die "this manifest predates the engine-content package (.intelligence/engine sources) — run 'intelligence upgrade'"
24
+ fi
25
+ if [ -n "$missing" ]; then
26
+ die "installed content missing on disk:$missing — run 'intelligence install'"
27
+ fi
10
28
  export_engine_env "$IP_ROOT"
11
29
  exec bash "$IS_ENGINE_DIR/scripts/sync.sh" "$@"
12
30
  ;;
@@ -25,6 +25,10 @@ moved=0 checked=0
25
25
  while IFS= read -r name; do
26
26
  [ -n "$name" ] || continue
27
27
  [ -n "$only" ] && [ "$name" != "$only" ] && continue
28
+ if [ "$name" = "$SYNC_PKG_NAME" ]; then
29
+ echo " $name: engine content moves with the engine — 'intelligence upgrade' owns it"
30
+ continue
31
+ fi
28
32
  ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
29
33
  [ -n "$ref" ] && { echo " $name: pinned to ref '$ref' — skipped"; continue; }
30
34
  range="$(qmap_field "$manifest" "packages" "$name" "version")"
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
- # intelligence upgrade — bring the project to this CLI's engine: restage the
3
- # engine content, apply v2 schema migrations (none yet — the chain starts
4
- # empty by design), restamp sync_version, sync.
2
+ # intelligence upgrade — bring the project to this CLI's engine: apply v2
3
+ # schema migrations, align the engine-content package to the bundled version,
4
+ # restamp sync_version, sync.
5
5
  set -euo pipefail
6
6
  source "$CLI_DIR/lib/cli-common.sh"
7
7
 
@@ -14,13 +14,54 @@ if [ -n "$stamp" ] && _ver_gt "$stamp" "$eng"; then
14
14
  die "manifest schema $stamp is newer than this CLI's engine $eng — update the CLI first: npm i -g @ainova-systems/intelligence@latest"
15
15
  fi
16
16
 
17
- # v2 schema migrations live HERE, not in the engine's chain (that one owns
18
- # vendored layouts). Same discipline: ascending, append-only, each migration
19
- # self-detects and no-ops when already applied.
20
- # V2_MIGRATIONS=( ) — first entry arrives with the first breaking v2 change.
17
+ # --- v2 schema migrations -------------------------------------------------
18
+ # The engine's own chain owns vendored layouts; this one owns the manifest.
19
+ # Same discipline: ascending, append-only, each migration self-detects and
20
+ # no-ops when already applied.
21
+
22
+ # v2 migration 1: staged engine content -> the @ainova-systems/sync package.
23
+ # Pre-package manifests listed `.intelligence/engine/{rules,agents,skills}`
24
+ # as sources fed by a CLI-staged copy; that copy is now an ordinary package
25
+ # entry seeded from the bundle. Post-condition: no `.intelligence/engine`
26
+ # source entries, the package present in manifest+lock+store, stale dir gone.
27
+ migrated=0
28
+ for section in rules agents skills; do
29
+ while IFS= read -r src; do
30
+ case "$src" in
31
+ .intelligence/engine/*)
32
+ sources_remove_entry "$manifest" "$section" "$src"
33
+ migrated=1
34
+ ;;
35
+ esac
36
+ done < <(read_yaml_list "$manifest" "$section")
37
+ done
38
+ if [ "$migrated" -eq 1 ]; then
39
+ echo " migrating: staged engine content -> $SYNC_PKG_NAME package"
40
+ rm -rf "$IP_ROOT/.intelligence/engine"
41
+ sync_pkg_entry "$manifest"
42
+ sync_pkg_install "$IP_ROOT"
43
+ fi
44
+
45
+ # --- steady state ---------------------------------------------------------
46
+ # Align the engine-content package with the bundled engine. Only a manifest
47
+ # that HAS the entry is touched — a --bare project opted out, and upgrade
48
+ # respects that.
49
+ have_pkg=0
50
+ while IFS= read -r name; do
51
+ [ "$name" = "$SYNC_PKG_NAME" ] && have_pkg=1
52
+ done < <(qmap_keys "$manifest" "packages")
53
+ if [ "$have_pkg" -eq 1 ] && [ "$migrated" -eq 0 ]; then
54
+ pinned="$(qmap_field "$manifest" "packages" "$SYNC_PKG_NAME" "version")"
55
+ if [ "$pinned" != "$eng" ]; then
56
+ echo " $SYNC_PKG_NAME: $pinned -> $eng"
57
+ fi
58
+ sync_pkg_entry "$manifest"
59
+ sync_pkg_install "$IP_ROOT"
60
+ fi
61
+ if [ "$have_pkg" -eq 0 ] && [ "$migrated" -eq 0 ]; then
62
+ echo " NOTE: $SYNC_PKG_NAME is not in this manifest (bare setup) — engine meta-skills stay uninstalled; 'intelligence add $SYNC_PKG_NAME' opts back in." >&2
63
+ fi
21
64
 
22
- stage_engine_content "$IP_ROOT/.intelligence/engine"
23
- echo " engine content restaged ($eng)"
24
65
  stamp_version "$manifest" "$eng"
25
66
  echo " sync_version -> $eng"
26
67