@ainova-systems/intelligence 0.11.0-rc.6 → 0.11.0-rc.7

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 (50) hide show
  1. package/README.md +2 -2
  2. package/cli/commands/add.sh +8 -7
  3. package/cli/commands/install.sh +8 -1
  4. package/cli/commands/migrate.sh +49 -19
  5. package/cli/commands/registry.sh +1 -0
  6. package/cli/commands/sync.sh +1 -1
  7. package/cli/commands/update.sh +12 -3
  8. package/cli/engine-package.yaml +2 -2
  9. package/cli/intelligence +27 -15
  10. package/cli/lib/cli-common.sh +34 -7
  11. package/cli/lib/lockfile.sh +14 -8
  12. package/cli/lib/manifest.sh +20 -1
  13. package/cli/lib/registry.sh +25 -11
  14. package/cli/lib/semver.sh +4 -2
  15. package/engine/ENGINE_SHA +1 -0
  16. package/engine/{scripts/adapters → adapters}/agents.sh +10 -15
  17. package/engine/{scripts/lib → lib}/common.sh +15 -519
  18. package/engine/lib/contract.sh +120 -0
  19. package/engine/sync.sh +233 -0
  20. package/package.json +6 -5
  21. package/engine/INIT.md +0 -500
  22. package/engine/docs/CLI.md +0 -90
  23. package/engine/scripts/ENGINE_SHA +0 -1
  24. package/engine/scripts/lib/layout.sh +0 -51
  25. package/engine/scripts/lib/migrations.sh +0 -708
  26. package/engine/scripts/sync.sh +0 -311
  27. package/engine/scripts/update.sh +0 -237
  28. /package/engine/{scripts/VERSION → VERSION} +0 -0
  29. /package/engine/{scripts/adapters → adapters}/_template.sh +0 -0
  30. /package/engine/{scripts/adapters → adapters}/claude.sh +0 -0
  31. /package/engine/{scripts/adapters → adapters}/codex.sh +0 -0
  32. /package/engine/{scripts/adapters → adapters}/copilot.sh +0 -0
  33. /package/engine/{scripts/adapters → adapters}/cursor.sh +0 -0
  34. /package/engine/{scripts/adapters → adapters}/opencode.sh +0 -0
  35. /package/engine/{scripts/adapters → adapters}/pi.sh +0 -0
  36. /package/{engine → packages/sync}/agents/intelligence-architect.md +0 -0
  37. /package/{engine → packages/sync}/agents/intelligence-operator.md +0 -0
  38. /package/{engine → packages/sync}/docs/ADAPTERS.md +0 -0
  39. /package/{engine → packages/sync}/docs/CONVENTIONS.md +0 -0
  40. /package/{engine → packages/sync}/rules/intelligence-authoring.md +0 -0
  41. /package/{engine → packages/sync}/skills/intelligence-add-agent/SKILL.md +0 -0
  42. /package/{engine → packages/sync}/skills/intelligence-add-rule/SKILL.md +0 -0
  43. /package/{engine → packages/sync}/skills/intelligence-add-skill/SKILL.md +0 -0
  44. /package/{engine → packages/sync}/skills/intelligence-extract-skill/SKILL.md +0 -0
  45. /package/{engine → packages/sync}/skills/intelligence-install-adapter/SKILL.md +0 -0
  46. /package/{engine → packages/sync}/skills/intelligence-learn-from-context/SKILL.md +0 -0
  47. /package/{engine → packages/sync}/skills/intelligence-review-skills/SKILL.md +0 -0
  48. /package/{engine → packages/sync}/skills/intelligence-sync/SKILL.md +0 -0
  49. /package/{engine → packages/sync}/skills/intelligence-uninstall-adapter/SKILL.md +0 -0
  50. /package/{engine → packages/sync}/skills/intelligence-update/SKILL.md +0 -0
@@ -0,0 +1,120 @@
1
+ #!/bin/bash
2
+ # shellcheck disable=SC2034 # IS_RC_*/IS_VERSION_KEY are the public bash<->CLI contract, consumed by the scripts that source this lib
3
+ # intelligence-sync: the version and status contract.
4
+ # Source this file — never execute directly.
5
+ #
6
+ # Two things live here, and both are contracts other programs depend on:
7
+ # * the applied-schema stamp (`sync_version` in the manifest) with its
8
+ # read / write / compare helpers, and
9
+ # * the IS_STATUS / IS_RC_* codes every engine flow reports.
10
+ #
11
+ # Schema migrations themselves are NOT here: the CLI owns them (v2 migrations
12
+ # live in `intelligence upgrade`), and a v1 project is brought forward by the
13
+ # archived v1 engine before `intelligence migrate` converts it.
14
+
15
+ # The applied-schema version is a managed key in the manifest.
16
+ #
17
+ # INVARIANT: this key is a PERMANENT, format-stable, top-level scalar contract.
18
+ # Anything else in the manifest may be reshaped; the name, location and shape of
19
+ # this key never are - so any engine, however old or new, can always read "what
20
+ # schema is this?" before parsing the rest.
21
+ IS_VERSION_KEY="sync_version"
22
+
23
+ # read_engine_stamp <config_file> → applied version, or "" if absent.
24
+ read_engine_stamp() {
25
+ local cf="$1"
26
+ [ -f "$cf" ] || return 0
27
+ awk -v k="$IS_VERSION_KEY" '
28
+ { sub(/\r$/, "") }
29
+ $0 ~ "^" k ":" {
30
+ v = $0; sub(/^[^:]*:[[:space:]]*/, "", v)
31
+ gsub(/^["\047]|["\047][[:space:]]*$/, "", v)
32
+ sub(/[[:space:]]+$/, "", v)
33
+ print v; exit
34
+ }
35
+ ' "$cf"
36
+ }
37
+
38
+ # stamp_version <config_file> <version> — idempotent, transactional upsert of
39
+ # the contract key (replace in place if present, else append at top level).
40
+ # No-op if config.yaml does not exist yet (pre-bootstrap).
41
+ stamp_version() {
42
+ local cf="$1" ver="$2"
43
+ [ -f "$cf" ] || return 0
44
+ local tmp="$cf.ver.tmp"
45
+ awk -v k="$IS_VERSION_KEY" -v val="$ver" '
46
+ { sub(/\r$/, "") }
47
+ $0 ~ "^" k ":" { print k ": \"" val "\""; found=1; next }
48
+ { print }
49
+ END { if (!found) print k ": \"" val "\"" }
50
+ ' "$cf" > "$tmp" && mv "$tmp" "$cf"
51
+ }
52
+
53
+ # --- bash ↔ skill status contract -------------------------------------------
54
+ # Bash is the deterministic, fail-closed core: it never guesses. Any state it
55
+ # cannot resolve safely is reported as a machine-readable status line on
56
+ # stdout plus a stable exit code, and the intelligence-update SKILL (the
57
+ # intelligent layer) decides what to do. Codes are part of the public
58
+ # contract — do not renumber.
59
+ IS_RC_OK=0 # success (synced / migrated / nothing to do)
60
+ IS_RC_ERROR=1 # generic error
61
+ IS_RC_CONFIG_MISSING=2 # no manifest found
62
+ IS_RC_AMBIGUOUS=3 # conflicting state; agent/human-only — bash never emits this itself
63
+ IS_RC_AHEAD=4 # project stamped newer than this engine understands
64
+ IS_RC_ABORTED_INCOMPLETE=5 # staged state incomplete; the project was left untouched
65
+ IS_RC_NEEDS_UPDATE=6 # pending schema changes (stamp < engine) — run `intelligence upgrade` first
66
+
67
+ # is_status <code-name> [detail] — emit one parseable line for the skill.
68
+ is_status() {
69
+ local code="$1" detail="${2:-}"
70
+ if [ -n "$detail" ]; then
71
+ echo "IS_STATUS=$code IS_DETAIL=$detail"
72
+ else
73
+ echo "IS_STATUS=$code"
74
+ fi
75
+ }
76
+
77
+ # Engine version = scripts/VERSION next to this lib (BASH_SOURCE works when
78
+ # sourced). Empty if unreadable — callers treat empty as "no guard".
79
+ engine_version() {
80
+ local vf
81
+ vf="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)/VERSION"
82
+ [ -f "$vf" ] && tr -d ' \t\r\n' < "$vf"
83
+ }
84
+
85
+ # _ver_gt A B → true if semver A is strictly greater than B (numeric x.y.z;
86
+ # any non-numeric suffix on a field is ignored). Missing fields = 0.
87
+ # Pre-release/build metadata ordering is intentionally NOT handled — the
88
+ # stamp only ever stores plain x.y.z, so this is sufficient.
89
+ _ver_gt() {
90
+ local a="$1" b="$2" i ai bi
91
+ local -a A B
92
+ IFS=. read -r -a A <<< "$a"
93
+ IFS=. read -r -a B <<< "$b"
94
+ for i in 0 1 2; do
95
+ ai=$(printf '%s' "${A[$i]:-0}" | tr -cd '0-9'); ai=${ai:-0}
96
+ bi=$(printf '%s' "${B[$i]:-0}" | tr -cd '0-9'); bi=${bi:-0}
97
+ if [ "$((10#$ai))" -gt "$((10#$bi))" ]; then return 0; fi
98
+ if [ "$((10#$ai))" -lt "$((10#$bi))" ]; then return 1; fi
99
+ done
100
+ return 1
101
+ }
102
+
103
+ # check_version_compat <config_file> — refuse to operate on a project whose
104
+ # config schema is stamped newer than this engine knows (a stale engine must
105
+ # never rewrite/sync a newer schema). Emits status + returns IS_RC_AHEAD on
106
+ # conflict, else 0.
107
+ check_version_compat() {
108
+ local cf="$1" stamp eng
109
+ stamp="$(read_engine_stamp "$cf")"
110
+ [ -n "$stamp" ] || return 0
111
+ eng="$(engine_version)"
112
+ [ -n "$eng" ] || return 0
113
+ if _ver_gt "$stamp" "$eng"; then
114
+ is_status ahead-of-engine "stamp=$stamp engine=$eng"
115
+ echo " ERROR: project stamped $stamp but this engine is $eng — refusing." >&2
116
+ echo " Update the CLI first: npm i -g @ainova-systems/intelligence@latest" >&2
117
+ return "$IS_RC_AHEAD"
118
+ fi
119
+ return 0
120
+ }
package/engine/sync.sh ADDED
@@ -0,0 +1,233 @@
1
+ #!/bin/bash
2
+ # intelligence-sync: the sync engine's entry point.
3
+ #
4
+ # The engine runs from outside the project (the CLI's install dir), so it never
5
+ # searches the filesystem for one: the project arrives through the environment,
6
+ # exported by `intelligence sync`.
7
+ #
8
+ # CONFIG_FILE the project's manifest (intelligence.yaml at the root)
9
+ # REPO_ROOT the project root
10
+ # IS_UMBRELLA_REL the project's content dir, repo-relative
11
+ # IS_MODULE_REL the installed sync package, repo-relative
12
+ # IS_PROTECTED_DIRS dirs an adapter output may never overlap
13
+ #
14
+ # Usage: intelligence sync [target]
15
+
16
+ set -euo pipefail
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
19
+
20
+ source "$SCRIPT_DIR/lib/common.sh"
21
+ source "$SCRIPT_DIR/lib/contract.sh"
22
+
23
+ if [ -z "${CONFIG_FILE:-}" ] || [ ! -f "${CONFIG_FILE:-}" ]; then
24
+ is_status config-missing "CONFIG_FILE=${CONFIG_FILE:-}"
25
+ echo "ERROR: the engine needs CONFIG_FILE to point at an existing manifest." >&2
26
+ echo " Run it through the CLI: intelligence sync" >&2
27
+ exit "$IS_RC_CONFIG_MISSING"
28
+ fi
29
+
30
+ # Schema version lives in the manifest (the frozen contract key).
31
+ _cf="$CONFIG_FILE"
32
+
33
+ # Stale engine vs project schema stamped NEWER (ahead-of-engine) → refuse.
34
+ _vc_rc=0
35
+ check_version_compat "$_cf" || _vc_rc=$?
36
+ if [ "$_vc_rc" -ne 0 ]; then exit "$_vc_rc"; fi
37
+
38
+ # Schema gap → refuse. sync is a PURE synchronizer: it never migrates, so a
39
+ # project behind this engine must be brought forward by `intelligence upgrade`
40
+ # first. An ABSENT stamp means the same thing — a manifest with no
41
+ # `sync_version` must not silently sync past a schema change.
42
+ _stamp="$(read_engine_stamp "$_cf")"
43
+ _eng="$(engine_version)"
44
+ if [ -z "$_stamp" ]; then
45
+ is_status needs-update "stamped= engine=$_eng (no sync_version)"
46
+ echo "ERROR: the manifest has no sync_version — schema un-applied." >&2
47
+ echo " Run: intelligence upgrade" >&2
48
+ exit "$IS_RC_NEEDS_UPDATE"
49
+ elif [ -n "$_eng" ] && _ver_gt "$_eng" "$_stamp"; then
50
+ is_status needs-update "stamped=$_stamp engine=$_eng"
51
+ echo "ERROR: project at $_stamp but engine is $_eng — pending schema changes." >&2
52
+ echo " Run: intelligence upgrade" >&2
53
+ exit "$IS_RC_NEEDS_UPDATE"
54
+ fi
55
+
56
+ # Normalize REPO_ROOT and CONFIG_FILE to one `cd && pwd` spelling so
57
+ # prefix-stripping in path comparisons works: Git Bash on Windows reaches the
58
+ # same location through `D:/...` and `/d/...`, and the CLI (or the Node shim
59
+ # behind it) may hand us either.
60
+ REPO_ROOT_RAW="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || dirname "$CONFIG_FILE")}"
61
+ REPO_ROOT="$(cd "$REPO_ROOT_RAW" && pwd)"
62
+ unset REPO_ROOT_RAW
63
+ CONFIG_FILE="$(cd "$(dirname "$CONFIG_FILE")" && pwd)/$(basename "$CONFIG_FILE")"
64
+
65
+ # Layout tokens for generated output (see finalize_output_file in common.sh).
66
+ # Package-shipped rules/agents cannot hardcode the content dir's name — the
67
+ # project chooses it — so they write `<umbrella>` / `<module>` and every adapter
68
+ # expands them on the way out.
69
+ IS_UMBRELLA_REL="${IS_UMBRELLA_REL:-intelligence}"
70
+ # No vendor default: the CLI always exports the package path (derived from its
71
+ # distribution data), so a bare invocation must say so rather than guess.
72
+ if [ -z "${IS_MODULE_REL:-}" ]; then
73
+ echo "ERROR: the engine needs IS_MODULE_REL (exported by the intelligence CLI)." >&2
74
+ exit 1
75
+ fi
76
+ export IS_UMBRELLA_REL IS_MODULE_REL
77
+
78
+ # Project-owned adapters live in the content dir: <umbrella>/adapters/.
79
+ INTELLIGENCE_DIR="$REPO_ROOT/$IS_UMBRELLA_REL"
80
+
81
+ TARGET_FILTER="${1:-}"
82
+
83
+ echo "=== intelligence-sync ==="
84
+ echo " Config: $CONFIG_FILE"
85
+ echo " Root: $REPO_ROOT"
86
+ echo ""
87
+
88
+ # Invariant: AGENTS.md is the canonical carrier of always-on rules for
89
+ # Cursor / Copilot / Codex / Pi / opencode (their adapters skip always-on
90
+ # rules to avoid duplication, since each tool reads AGENTS.md natively for
91
+ # baseline project context). If those targets are enabled, `agents` must
92
+ # also be enabled — otherwise always-on rules go nowhere for those tools.
93
+ # Skip the check when the user requested a single target via $TARGET_FILTER:
94
+ # they may be syncing only one IDE intentionally.
95
+ if [ -z "$TARGET_FILTER" ]; then
96
+ agents_enabled=$(is_target_enabled "$CONFIG_FILE" "agents")
97
+ if [ "$agents_enabled" != "1" ]; then
98
+ # AGENTS.md-dependent adapters: any tool whose adapter skips always-on
99
+ # rule emission (because the tool reads AGENTS.md natively) must be
100
+ # listed here. Add new adapters to this list when they ship.
101
+ for tool in cursor copilot codex pi opencode; do
102
+ if [ "$(is_target_enabled "$CONFIG_FILE" "$tool")" = "1" ]; then
103
+ echo "ERROR: targets.$tool is enabled but targets.agents is not." >&2
104
+ echo " $tool relies on AGENTS.md to deliver always-on rules — without it," >&2
105
+ echo " always-on rules would be invisible to $tool." >&2
106
+ echo " Either enable targets.agents in $CONFIG_FILE, or disable targets.$tool." >&2
107
+ exit 1
108
+ fi
109
+ done
110
+ fi
111
+ fi
112
+
113
+ # Lint frontmatter across all source files (rules, agents, skills).
114
+ # Catches issues like unquoted colons that strict YAML consumers reject.
115
+ for section in rules agents skills; do
116
+ while IFS= read -r src; do
117
+ [ -z "$src" ] && continue
118
+ src_dir="$(resolve_source_dir "$REPO_ROOT" "$src")"
119
+ [ -d "$src_dir" ] || continue
120
+ if [ "$section" = "skills" ]; then
121
+ while IFS= read -r f; do
122
+ [ -n "$f" ] && lint_frontmatter "$f"
123
+ done < <(find "$src_dir" -mindepth 2 -maxdepth 2 -name 'SKILL.md' 2>/dev/null)
124
+ else
125
+ for f in "$src_dir"/*.md; do
126
+ [ -f "$f" ] && lint_frontmatter "$f"
127
+ done
128
+ fi
129
+ done < <(read_yaml_list "$CONFIG_FILE" "$section")
130
+ done
131
+
132
+ # Adapters come from two places, discovered by filename (minus `.sh`,
133
+ # `_template` excluded):
134
+ #
135
+ # 1. Built-in — shipped inside the CLI, replaced wholesale on every upgrade
136
+ # 2. Project — <umbrella>/adapters/, owned by the project and never touched
137
+ #
138
+ # A custom adapter therefore belongs in the content dir's `adapters/`; the
139
+ # built-in directory lives inside the installed CLI and is not the project's to
140
+ # edit. A project adapter whose name matches a built-in overrides it (an escape
141
+ # hatch for patching a built-in without forking — announced, never silent).
142
+ ADAPTERS=()
143
+ ADAPTER_FILES=()
144
+
145
+ register_adapter() {
146
+ local name="$1" file="$2"
147
+ local n=${#ADAPTERS[@]} i=0
148
+ while [ "$i" -lt "$n" ]; do
149
+ if [ "${ADAPTERS[$i]}" = "$name" ]; then
150
+ ADAPTER_FILES[$i]="$file"
151
+ echo " NOTE: project adapter '$name' overrides the built-in one ($(basename "$INTELLIGENCE_DIR")/adapters/$(basename "$file"))"
152
+ return 0
153
+ fi
154
+ i=$((i + 1))
155
+ done
156
+ ADAPTERS+=("$name")
157
+ ADAPTER_FILES+=("$file")
158
+ }
159
+
160
+ for adapters_dir in "$SCRIPT_DIR/adapters" "$INTELLIGENCE_DIR/adapters"; do
161
+ [ -d "$adapters_dir" ] || continue
162
+ for adapter_file in "$adapters_dir"/*.sh; do
163
+ [ -f "$adapter_file" ] || continue
164
+ adapter_name="$(basename "$adapter_file" .sh)"
165
+ [ "$adapter_name" = "_template" ] && continue
166
+ register_adapter "$adapter_name" "$adapter_file"
167
+ done
168
+ done
169
+
170
+ synced=0
171
+ adapter_count=${#ADAPTERS[@]}
172
+ adapter_idx=0
173
+
174
+ while [ "$adapter_idx" -lt "$adapter_count" ]; do
175
+ adapter="${ADAPTERS[$adapter_idx]}"
176
+ adapter_file="${ADAPTER_FILES[$adapter_idx]}"
177
+ adapter_idx=$((adapter_idx + 1))
178
+
179
+ # Skip if user requested specific target and this isn't it
180
+ if [ -n "$TARGET_FILTER" ] && [ "$adapter" != "$TARGET_FILTER" ]; then
181
+ continue
182
+ fi
183
+
184
+ # Check if target is enabled in config
185
+ enabled=$(is_target_enabled "$CONFIG_FILE" "$adapter")
186
+ if [ "$enabled" != "1" ] && [ -z "$TARGET_FILTER" ]; then
187
+ continue
188
+ fi
189
+
190
+ # Get output directory
191
+ output=$(get_target_output "$CONFIG_FILE" "$adapter")
192
+ if [ -z "$output" ]; then
193
+ output=".$adapter"
194
+ fi
195
+ output_dir="$REPO_ROOT/$output"
196
+
197
+ # Refuse to run if the output would clobber content — `output: "."`,
198
+ # `output: "intelligence"`, or a `../` path that escapes the repo. Applies
199
+ # to EVERY adapter, `agents` included: dir-writing adapters `rm -rf` their
200
+ # output, and `agents` overwrites whatever single file it is handed. Both
201
+ # turn a bad config line into a destructive write.
202
+ validate_output_path "$REPO_ROOT" "$CONFIG_FILE" "$adapter" "$output_dir"
203
+
204
+ # Source adapter and run.
205
+ # shellcheck source=/dev/null
206
+ source "$adapter_file"
207
+ "sync_to_$adapter" "$REPO_ROOT" "$CONFIG_FILE" "$output_dir"
208
+ echo ""
209
+ synced=$((synced + 1))
210
+ done
211
+
212
+ if [ $synced -eq 0 ]; then
213
+ if [ -n "$TARGET_FILTER" ]; then
214
+ echo "ERROR: Adapter '$TARGET_FILTER' not found."
215
+ echo "Available: ${ADAPTERS[*]}"
216
+ else
217
+ echo "WARNING: No targets enabled in $CONFIG_FILE"
218
+ fi
219
+ exit 1
220
+ fi
221
+
222
+ # Warn about unsynced directories
223
+ warn_unsynced "$REPO_ROOT" "$CONFIG_FILE"
224
+
225
+ # Report model overrides that drift from intelligence-sync defaults
226
+ # (helpful when defaults move forward — e.g., gpt-5.5 -> gpt-5.6).
227
+ report_model_drift "$CONFIG_FILE"
228
+
229
+ echo ""
230
+ # sync.sh never migrates (`intelligence upgrade` owns that), so success is
231
+ # always ok.
232
+ is_status ok "synced=$synced"
233
+ echo "=== Done: $synced target(s) synced ==="
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainova-systems/intelligence",
3
- "version": "0.11.0-rc.6",
3
+ "version": "0.11.0-rc.7",
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"
@@ -8,7 +8,8 @@
8
8
  "files": [
9
9
  "bin",
10
10
  "cli",
11
- "engine"
11
+ "engine",
12
+ "packages"
12
13
  ],
13
14
  "engines": {
14
15
  "node": ">=18"
@@ -16,11 +17,11 @@
16
17
  "license": "MIT",
17
18
  "repository": {
18
19
  "type": "git",
19
- "url": "git+https://github.com/ainova-systems/intelligence-sync.git"
20
+ "url": "git+https://github.com/ainova-systems/intelligence.git"
20
21
  },
21
- "homepage": "https://github.com/ainova-systems/intelligence-sync#readme",
22
+ "homepage": "https://github.com/ainova-systems/intelligence#readme",
22
23
  "bugs": {
23
- "url": "https://github.com/ainova-systems/intelligence-sync/issues"
24
+ "url": "https://github.com/ainova-systems/intelligence/issues"
24
25
  },
25
26
  "keywords": [
26
27
  "ai",