@ainova-systems/intelligence 0.12.0 → 0.13.0
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/README.md +1 -0
- package/bin/intelligence.js +10 -1
- package/cli/commands/update.sh +24 -14
- package/cli/commands/upgrade.sh +100 -0
- package/cli/intelligence +3 -1
- package/cli/internal/check.sh +2 -2
- package/cli/lib/cli-common.sh +1 -0
- package/cli/lib/cli-install.sh +131 -0
- package/cli/lib/gitignore.sh +45 -19
- package/cli/lib/registry.sh +1 -1
- package/cli/lib/semver.sh +66 -0
- package/engine/ENGINE_SHA +1 -1
- package/engine/VERSION +1 -1
- package/engine/lib/contract.sh +2 -2
- package/package.json +1 -1
- package/packages/sync/references/conventions.md +1 -0
- package/packages/sync/skills/intelligence-update/SKILL.md +9 -4
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@ intelligence sync
|
|
|
35
35
|
| `intelligence init [--preview\|--apply]` | Create, convert, restore or align a project |
|
|
36
36
|
| `intelligence sync [adapter]` | Restore locked content if needed, then render all or one enabled adapter |
|
|
37
37
|
| `intelligence update [@scope/name] [--preview\|--apply]` | Plan or apply project and ranged-package updates |
|
|
38
|
+
| `intelligence upgrade [--next] [--preview\|--apply]` | Replace the installed CLI with the newest version on its npm channel |
|
|
38
39
|
| `intelligence package add\|remove\|list\|search` | Manage versioned Intelligence Packages |
|
|
39
40
|
| `intelligence adapter list\|create\|enable\|disable\|remove` | Manage render adapters |
|
|
40
41
|
| `intelligence status [--check]` | Inspect project state and consistency |
|
package/bin/intelligence.js
CHANGED
|
@@ -47,10 +47,19 @@ function findBash() {
|
|
|
47
47
|
|
|
48
48
|
const cli = path.join(__dirname, '..', 'cli', 'intelligence').replace(/\\/g, '/');
|
|
49
49
|
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
|
50
|
+
const binName = Object.keys(pkg.bin)[0];
|
|
50
51
|
|
|
51
52
|
const result = spawnSync(findBash(), [cli].concat(process.argv.slice(2)), {
|
|
52
53
|
stdio: 'inherit',
|
|
53
|
-
|
|
54
|
+
// What npm installed, for `update` (channel check) and `upgrade` (replace
|
|
55
|
+
// exactly this package): its version and name, the command npm linked as
|
|
56
|
+
// its shim, and this launcher's path inside the package.
|
|
57
|
+
env: Object.assign({}, process.env, {
|
|
58
|
+
INTELLIGENCE_NPM_VERSION: pkg.version,
|
|
59
|
+
INTELLIGENCE_NPM_PACKAGE: pkg.name,
|
|
60
|
+
INTELLIGENCE_NPM_BIN: binName,
|
|
61
|
+
INTELLIGENCE_NPM_LAUNCHER: pkg.bin[binName],
|
|
62
|
+
}),
|
|
54
63
|
});
|
|
55
64
|
if (result.error) {
|
|
56
65
|
console.error('intelligence: failed to launch bash: ' + result.error.message);
|
package/cli/commands/update.sh
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
# intelligence update [@scope/name] [--preview|--apply]
|
|
3
3
|
#
|
|
4
4
|
# One update surface: show the installed-CLI/project/package plan, then either
|
|
5
|
-
# stop, ask interactively, or apply without a prompt.
|
|
6
|
-
# installation
|
|
5
|
+
# stop, ask interactively, or apply without a prompt. Replacing the global npm
|
|
6
|
+
# installation is `intelligence upgrade`, which the plan names; it never
|
|
7
|
+
# happens inside a project command.
|
|
7
8
|
set -euo pipefail
|
|
8
9
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
9
10
|
|
|
@@ -30,18 +31,27 @@ project_change=0
|
|
|
30
31
|
echo "Update plan"
|
|
31
32
|
echo ""
|
|
32
33
|
echo "CLI:"
|
|
33
|
-
if [ -n "${INTELLIGENCE_NPM_VERSION:-}" ] && [ "${
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
if [ -n "${INTELLIGENCE_NPM_VERSION:-}" ] && [ -n "${INTELLIGENCE_NPM_PACKAGE:-}" ] \
|
|
35
|
+
&& [ "${IS_SKIP_NPM_CHECK:-0}" != "1" ] && command -v npm >/dev/null 2>&1; then
|
|
36
|
+
channel="$(npm_channel_for "$INTELLIGENCE_NPM_VERSION")"
|
|
37
|
+
# Ask the way `upgrade` will install: in global mode at this tree's own
|
|
38
|
+
# prefix when npm made it, so the plan and the install read one config.
|
|
39
|
+
cli_install_classify "$(cd "$CLI_DIR/.." && pwd -P)" "$INTELLIGENCE_NPM_PACKAGE" "${INTELLIGENCE_NPM_BIN:-}"
|
|
40
|
+
npm_prefix=""
|
|
41
|
+
[ "$CLI_INSTALL_KIND" != "npm" ] || npm_prefix="$(native_path "$CLI_INSTALL_PREFIX")"
|
|
42
|
+
# An empty or non-version answer is "not checked", never a comparison.
|
|
43
|
+
if available="$(cli_registry_version "$INTELLIGENCE_NPM_PACKAGE" "$channel" "$npm_prefix")"; then
|
|
44
|
+
case "$(semver_cmp_full "$available" "$INTELLIGENCE_NPM_VERSION")" in
|
|
45
|
+
1)
|
|
46
|
+
echo " $INTELLIGENCE_NPM_VERSION -> $available (npm $channel)"
|
|
47
|
+
echo " run: intelligence upgrade"
|
|
48
|
+
echo " then rerun: intelligence update --apply"
|
|
49
|
+
;;
|
|
50
|
+
0) echo " $INTELLIGENCE_NPM_VERSION (up to date on npm $channel)" ;;
|
|
51
|
+
*) echo " $INTELLIGENCE_NPM_VERSION (ahead of npm $channel $available)" ;;
|
|
52
|
+
esac
|
|
41
53
|
else
|
|
42
|
-
echo " $INTELLIGENCE_NPM_VERSION
|
|
43
|
-
echo " run: npm install -g @ainova-systems/intelligence@$channel"
|
|
44
|
-
echo " then rerun: intelligence update --apply"
|
|
54
|
+
echo " installed: $INTELLIGENCE_NPM_VERSION; registry check unavailable"
|
|
45
55
|
fi
|
|
46
56
|
else
|
|
47
57
|
echo " source checkout/registry check skipped; engine $eng"
|
|
@@ -50,7 +60,7 @@ fi
|
|
|
50
60
|
echo ""
|
|
51
61
|
echo "Project:"
|
|
52
62
|
if project_stamped_ahead "$IP_ROOT"; then
|
|
53
|
-
echo " schema $stamp is ahead of engine $eng — left as is; update the CLI to align it"
|
|
63
|
+
echo " schema $stamp is ahead of engine $eng — left as is; update the CLI to align it: intelligence upgrade (--next when the stamp came from a prerelease line)"
|
|
54
64
|
elif project_needs_upgrade "$IP_ROOT"; then
|
|
55
65
|
echo " lifecycle alignment required (stamp ${stamp:-unstamped}, engine $eng)"
|
|
56
66
|
project_change=1
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# intelligence upgrade [--next] [--preview|--apply]
|
|
3
|
+
#
|
|
4
|
+
# Replace the installed CLI with the newest version on its npm channel:
|
|
5
|
+
# `next` when the running version is a prerelease or --next is given,
|
|
6
|
+
# `latest` otherwise. `update` plans this step and points here; this is the
|
|
7
|
+
# one command that writes to an npm prefix, and it never reads or writes a
|
|
8
|
+
# project.
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
source "$CLI_DIR/lib/cli-common.sh"
|
|
11
|
+
|
|
12
|
+
mode="ask" preview_seen=0 apply_seen=0 channel=""
|
|
13
|
+
while [ $# -gt 0 ]; do
|
|
14
|
+
case "$1" in
|
|
15
|
+
--preview) preview_seen=1; mode="preview" ;;
|
|
16
|
+
--apply) apply_seen=1; mode="apply" ;;
|
|
17
|
+
--next) channel="next" ;;
|
|
18
|
+
*) die "usage: intelligence upgrade [--next] [--preview|--apply]" ;;
|
|
19
|
+
esac
|
|
20
|
+
shift
|
|
21
|
+
done
|
|
22
|
+
[ "$preview_seen" -eq 0 ] || [ "$apply_seen" -eq 0 ] || die "choose either --preview or --apply"
|
|
23
|
+
|
|
24
|
+
pkg_dir="$(cd "$CLI_DIR/.." && pwd -P)"
|
|
25
|
+
installed="${INTELLIGENCE_NPM_VERSION:-}"
|
|
26
|
+
pkg="${INTELLIGENCE_NPM_PACKAGE:-}"
|
|
27
|
+
bin="${INTELLIGENCE_NPM_BIN:-}"
|
|
28
|
+
# All of it comes from the npm launcher; `bash cli/intelligence` in a checkout has none.
|
|
29
|
+
[ -n "$installed" ] && [ -n "$pkg" ] && [ -n "$bin" ] && [ -n "${INTELLIGENCE_NPM_LAUNCHER:-}" ] \
|
|
30
|
+
|| die "this CLI runs from a source checkout ($pkg_dir), not an npm installation — pull the repository instead"
|
|
31
|
+
command -v npm >/dev/null 2>&1 || die "npm is not on PATH — it installed this CLI and is needed to replace it"
|
|
32
|
+
[ -n "$channel" ] || channel="$(npm_channel_for "$installed")"
|
|
33
|
+
|
|
34
|
+
# Where this tree lives is decided locally, before any network: a tree npm
|
|
35
|
+
# did not make is refused whatever the registry would say. The hint names
|
|
36
|
+
# the exact version when the registry answers and the channel otherwise.
|
|
37
|
+
cli_install_classify "$pkg_dir" "$pkg" "$bin"
|
|
38
|
+
if [ "$CLI_INSTALL_KIND" != "npm" ]; then
|
|
39
|
+
available="$(cli_registry_version "$pkg" "$channel" || true)"
|
|
40
|
+
die "this CLI ($pkg_dir) is not an npm global installation — $(cli_install_hint "$CLI_INSTALL_KIND" "$pkg" "${available:-$channel}")"
|
|
41
|
+
fi
|
|
42
|
+
prefix="$(native_path "$CLI_INSTALL_PREFIX")"
|
|
43
|
+
launcher="$(native_path "$pkg_dir/$INTELLIGENCE_NPM_LAUNCHER")"
|
|
44
|
+
|
|
45
|
+
available="$(cli_registry_version "$pkg" "$channel" "$prefix")" \
|
|
46
|
+
|| die "npm could not report a version for $pkg dist-tag $channel — check the network or the registry and retry"
|
|
47
|
+
|
|
48
|
+
case "$(semver_cmp_full "$available" "$installed")" in
|
|
49
|
+
0)
|
|
50
|
+
echo "CLI $installed is up to date (npm $channel)"
|
|
51
|
+
exit 0
|
|
52
|
+
;;
|
|
53
|
+
-1)
|
|
54
|
+
# Never a downgrade: a channel moved back on purpose is a decision
|
|
55
|
+
# the user takes by hand, with the command that does it.
|
|
56
|
+
echo "CLI $installed is newer than npm $channel ($available); nothing to do"
|
|
57
|
+
echo " to move back deliberately: npm install -g --prefix $(quote_for_shell "$prefix") $pkg@$available"
|
|
58
|
+
exit 0
|
|
59
|
+
;;
|
|
60
|
+
esac
|
|
61
|
+
|
|
62
|
+
echo "CLI upgrade: $installed -> $available (npm $channel)"
|
|
63
|
+
echo " npm install -g --prefix $(quote_for_shell "$prefix") $pkg@$available"
|
|
64
|
+
[ "$mode" != "preview" ] || exit 0
|
|
65
|
+
if [ "$mode" = "ask" ]; then
|
|
66
|
+
[ -t 0 ] || die "upgrade requires confirmation — rerun with --preview or --apply"
|
|
67
|
+
printf '\nInstall %s? [Y/n] ' "$available"
|
|
68
|
+
read -r answer
|
|
69
|
+
case "$answer" in
|
|
70
|
+
""|y|Y|yes|YES) ;;
|
|
71
|
+
*) echo "upgrade cancelled"; exit 0 ;;
|
|
72
|
+
esac
|
|
73
|
+
fi
|
|
74
|
+
echo ""
|
|
75
|
+
|
|
76
|
+
# npm rewrites the directory this script lives in, and Windows refuses to
|
|
77
|
+
# delete a file another process still holds open. `bash -c` parses its whole
|
|
78
|
+
# program before running it, so the fresh shell reads nothing from the
|
|
79
|
+
# package while npm replaces it, and exec leaves no old process behind.
|
|
80
|
+
# The version installed is the one the plan showed, never a moving tag, and
|
|
81
|
+
# success is what the new launcher reports, not what npm returned.
|
|
82
|
+
exec "$BASH" -c '
|
|
83
|
+
set -euo pipefail
|
|
84
|
+
pkg="$1" from="$2" to="$3" launcher="$4" prefix="$5" bin="$6"
|
|
85
|
+
npm install -g --prefix "$prefix" "$pkg@$to"
|
|
86
|
+
echo ""
|
|
87
|
+
if [ -f "$launcher" ] && command -v node >/dev/null 2>&1; then
|
|
88
|
+
got="$(node "$launcher" version 2>/dev/null || true)"
|
|
89
|
+
case "$got" in
|
|
90
|
+
"$to"|"$to "*) echo "upgraded: $from -> $to"; echo "$got" ;;
|
|
91
|
+
*)
|
|
92
|
+
echo "ERROR: npm reported success but the installed launcher answers: ${got:-<nothing>}" >&2
|
|
93
|
+
echo " expected $to — inspect the installation at $prefix" >&2
|
|
94
|
+
exit 1
|
|
95
|
+
;;
|
|
96
|
+
esac
|
|
97
|
+
else
|
|
98
|
+
echo "upgraded: $from -> $to (not verified — run: $bin version)"
|
|
99
|
+
fi
|
|
100
|
+
' intelligence-upgrade "$pkg" "$installed" "$available" "$launcher" "$prefix" "$bin"
|
package/cli/intelligence
CHANGED
|
@@ -19,7 +19,7 @@ if [ -f "$CLI_DIR/../engine/sync.sh" ] && [ -f "$CLI_DIR/../engine/VERSION" ]; t
|
|
|
19
19
|
fi
|
|
20
20
|
if [ -z "$IS_ENGINE_DIR" ]; then
|
|
21
21
|
echo "ERROR: bundled sync engine not found next to the CLI ($CLI_DIR)." >&2
|
|
22
|
-
echo " Reinstall: npm i -g
|
|
22
|
+
echo " Reinstall: npm i -g ${INTELLIGENCE_NPM_PACKAGE:-<the CLI package>}" >&2
|
|
23
23
|
exit 1
|
|
24
24
|
fi
|
|
25
25
|
export IS_ENGINE_DIR
|
|
@@ -51,6 +51,8 @@ Usage: intelligence <command> [args]
|
|
|
51
51
|
update [@scope/name] Show an update plan; ask before applying it
|
|
52
52
|
--preview Show the plan without asking or writing
|
|
53
53
|
--apply Apply the plan without asking
|
|
54
|
+
upgrade [--next] [--preview|--apply] Replace the installed CLI with the newest npm version
|
|
55
|
+
--next Follow the prerelease line instead of the stable one
|
|
54
56
|
package <command> add | remove | list | search
|
|
55
57
|
adapter <command> list | create | enable | disable | remove
|
|
56
58
|
status [--check] Project state; --check runs deep consistency checks
|
package/cli/internal/check.sh
CHANGED
|
@@ -44,9 +44,9 @@ if [ -z "$stamp" ]; then
|
|
|
44
44
|
warn "manifest has no schema_version — run 'intelligence init'"
|
|
45
45
|
elif _ver_gt "$stamp" "$eng"; then
|
|
46
46
|
if [ "$(_ver_major "$stamp")" -gt "$(_ver_major "$eng")" ]; then
|
|
47
|
-
warn "manifest schema $stamp is a newer major than this CLI's engine $eng — update the CLI:
|
|
47
|
+
warn "manifest schema $stamp is a newer major than this CLI's engine $eng — update the CLI: intelligence upgrade (--next for a prerelease line)"
|
|
48
48
|
else
|
|
49
|
-
note "manifest schema $stamp is newer than this CLI's engine $eng — the project uses a newer CLI; update it:
|
|
49
|
+
note "manifest schema $stamp is newer than this CLI's engine $eng — the project uses a newer CLI; update it: intelligence upgrade (--next for a prerelease line)"
|
|
50
50
|
fi
|
|
51
51
|
elif _ver_gt "$eng" "$stamp"; then
|
|
52
52
|
warn "manifest schema $stamp behind engine $eng — run 'intelligence init'"
|
package/cli/lib/cli-common.sh
CHANGED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# The installed CLI's own npm installation — shared by `upgrade`, which
|
|
3
|
+
# replaces the tree it runs from, and `update`, which plans that step.
|
|
4
|
+
#
|
|
5
|
+
# npm is never asked where its global root is: `npm root -g` answers for the
|
|
6
|
+
# current configuration rather than for the running tree (nvm, a custom
|
|
7
|
+
# prefix), and npm redacts UUID-like path segments from everything it prints.
|
|
8
|
+
# The tree's own location and the shim npm linked for it say where it lives.
|
|
9
|
+
|
|
10
|
+
cli_on_windows() {
|
|
11
|
+
case "${OSTYPE:-}" in msys*|cygwin*) return 0 ;; esac
|
|
12
|
+
return 1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
# native_path <posix-path> — the spelling a native program (npm, node) needs:
|
|
16
|
+
# Windows form under Git Bash, unchanged elsewhere.
|
|
17
|
+
native_path() {
|
|
18
|
+
if cli_on_windows; then cygpath -w "$1"; else printf '%s' "$1"; fi
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# quote_for_shell <string> — one shell word for a command printed to be
|
|
22
|
+
# copied. Windows paths carry no `$` or backtick and cmd.exe reads single
|
|
23
|
+
# quotes literally, so they get double quotes; elsewhere single quotes with
|
|
24
|
+
# an embedded quote as '\''. sed rather than ${var//…}: Bash 3.2 (macOS)
|
|
25
|
+
# expands a backslash-quote replacement differently.
|
|
26
|
+
quote_for_shell() {
|
|
27
|
+
if cli_on_windows; then
|
|
28
|
+
printf '"%s"' "$1"
|
|
29
|
+
else
|
|
30
|
+
printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# cli_install_classify <pkg_dir> <pkg> <bin> — where the running tree lives.
|
|
35
|
+
# Sets CLI_INSTALL_KIND to npm | npx | volta | pnpm | bun | yarn | nix |
|
|
36
|
+
# dependency | unknown, and CLI_INSTALL_PREFIX (POSIX spelling) for npm only.
|
|
37
|
+
#
|
|
38
|
+
# npm's global layout is `<prefix>/lib/node_modules/<pkg>` on POSIX and
|
|
39
|
+
# `<prefix>/node_modules/<pkg>` on Windows, and a project's own node_modules
|
|
40
|
+
# looks exactly like the latter. The shim npm links at the prefix root is
|
|
41
|
+
# what proves a prefix: `<prefix>/bin/<bin>` pointing into this tree, or
|
|
42
|
+
# `<prefix>/<bin>.cmd` naming it. Known foreign stores are named first
|
|
43
|
+
# because Volta's package images are laid out like an npm prefix without
|
|
44
|
+
# being one.
|
|
45
|
+
cli_install_classify() {
|
|
46
|
+
local pkg_dir="$1" pkg="$2" bin="$3" prefix="" kind="unknown" shim target pkg_bs
|
|
47
|
+
CLI_INSTALL_KIND=""; CLI_INSTALL_PREFIX=""
|
|
48
|
+
case "$pkg_dir" in
|
|
49
|
+
*/_npx/*) kind="npx" ;;
|
|
50
|
+
*/.volta/*|*/Volta/*) kind="volta" ;;
|
|
51
|
+
*/.pnpm/*) kind="pnpm" ;;
|
|
52
|
+
*/.bun/install/global/*) kind="bun" ;;
|
|
53
|
+
*/yarn/global/*|*/Yarn/Data/global/*) kind="yarn" ;;
|
|
54
|
+
/nix/store/*|/gnu/store/*) kind="nix" ;;
|
|
55
|
+
*)
|
|
56
|
+
if cli_on_windows; then
|
|
57
|
+
case "$pkg_dir" in
|
|
58
|
+
*/node_modules/"$pkg")
|
|
59
|
+
prefix="${pkg_dir%/node_modules/"$pkg"}"
|
|
60
|
+
prefix="${prefix:-/}"
|
|
61
|
+
shim="$prefix/$bin.cmd"
|
|
62
|
+
# The cmd shim spells its target with backslashes.
|
|
63
|
+
pkg_bs="$(printf '%s' "$pkg" | tr '/' '\\')"
|
|
64
|
+
if [ ! -f "$shim" ] || ! grep -Fq "node_modules\\$pkg_bs\\bin\\" "$shim"; then
|
|
65
|
+
prefix=""
|
|
66
|
+
fi
|
|
67
|
+
;;
|
|
68
|
+
esac
|
|
69
|
+
else
|
|
70
|
+
case "$pkg_dir" in
|
|
71
|
+
*/lib/node_modules/"$pkg")
|
|
72
|
+
prefix="${pkg_dir%/lib/node_modules/"$pkg"}"
|
|
73
|
+
prefix="${prefix:-/}"
|
|
74
|
+
shim="$prefix/bin/$bin"
|
|
75
|
+
target=""
|
|
76
|
+
if [ -L "$shim" ]; then
|
|
77
|
+
target="$(readlink "$shim")"
|
|
78
|
+
case "$target" in /*) ;; *) target="$prefix/bin/$target" ;; esac
|
|
79
|
+
target="$(cd "$(dirname "$target")" 2>/dev/null && pwd -P || true)"
|
|
80
|
+
fi
|
|
81
|
+
[ "$target" = "$pkg_dir/bin" ] || prefix=""
|
|
82
|
+
;;
|
|
83
|
+
esac
|
|
84
|
+
fi
|
|
85
|
+
if [ -n "$prefix" ]; then
|
|
86
|
+
kind="npm"
|
|
87
|
+
else
|
|
88
|
+
case "$pkg_dir" in */node_modules/"$pkg") kind="dependency" ;; esac
|
|
89
|
+
fi
|
|
90
|
+
;;
|
|
91
|
+
esac
|
|
92
|
+
# Read by upgrade and update after the call — per-file shellcheck cannot see that.
|
|
93
|
+
# shellcheck disable=SC2034
|
|
94
|
+
CLI_INSTALL_KIND="$kind"
|
|
95
|
+
# shellcheck disable=SC2034
|
|
96
|
+
CLI_INSTALL_PREFIX="$prefix"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# cli_install_hint <kind> <pkg> <version-or-channel> — the command that
|
|
100
|
+
# upgrades a tree npm did not make, for the refusal message.
|
|
101
|
+
cli_install_hint() {
|
|
102
|
+
local kind="$1" spec="$2@$3"
|
|
103
|
+
case "$kind" in
|
|
104
|
+
npx) printf 'nothing is installed globally (this run came through npx) — install it: npm install -g %s' "$spec" ;;
|
|
105
|
+
volta) printf 'volta install %s' "$spec" ;;
|
|
106
|
+
pnpm) printf 'pnpm add -g %s' "$spec" ;;
|
|
107
|
+
bun) printf 'bun add -g %s' "$spec" ;;
|
|
108
|
+
yarn) printf 'yarn global add %s' "$spec" ;;
|
|
109
|
+
nix) printf 'this tree is a Nix/Guix store path — upgrade it with that package manager (%s)' "$spec" ;;
|
|
110
|
+
dependency) printf 'this is a project dependency — move it in package.json: npm install -D %s' "$spec" ;;
|
|
111
|
+
*) printf 'upgrade it with the tool that installed it (ask for %s), or pull it if it is a checkout' "$spec" ;;
|
|
112
|
+
esac
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# cli_registry_version <pkg> <channel> [native-prefix] — the version the
|
|
116
|
+
# registry serves on that dist-tag, validated; prints nothing and fails when
|
|
117
|
+
# npm does not answer with one. With a prefix the lookup runs in global mode
|
|
118
|
+
# at that prefix — the configuration `npm install -g --prefix` will use, so
|
|
119
|
+
# the plan and the install cannot read different registries. `--no-json`
|
|
120
|
+
# overrides a `json=true` in any npmrc, which would quote the answer.
|
|
121
|
+
cli_registry_version() {
|
|
122
|
+
local pkg="$1" channel="$2" prefix="${3:-}" out
|
|
123
|
+
if [ -n "$prefix" ]; then
|
|
124
|
+
out="$(npm view --global --prefix "$prefix" --no-json "$pkg" "dist-tags.$channel" 2>/dev/null || true)"
|
|
125
|
+
else
|
|
126
|
+
out="$(npm view --no-json "$pkg" "dist-tags.$channel" 2>/dev/null || true)"
|
|
127
|
+
fi
|
|
128
|
+
out="${out//[$' \t\r\n']/}"
|
|
129
|
+
is_npm_version "$out" || return 1
|
|
130
|
+
printf '%s' "$out"
|
|
131
|
+
}
|
package/cli/lib/gitignore.sh
CHANGED
|
@@ -15,34 +15,60 @@ gitignore_path_is_ignored() {
|
|
|
15
15
|
git -C "$root" check-ignore -q --no-index -- "$path"
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
# True when the file's last lines are exactly these, in order.
|
|
19
|
+
gitignore_tail_is() {
|
|
20
|
+
local file="$1"; shift
|
|
21
|
+
[ -f "$file" ] || return 1
|
|
22
|
+
local want tail_lines
|
|
23
|
+
want="$(printf '%s\n' "$@")"
|
|
24
|
+
tail_lines="$(tail -n "$#" -- "$file")"
|
|
25
|
+
[ "$tail_lines" = "$want" ]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Git cannot re-include a child of an excluded directory, so an include needs an
|
|
29
|
+
# explicit negation for every parent. A negation only wins when it comes after
|
|
30
|
+
# the rule excluding the parent, and the only repair an append can make is to
|
|
31
|
+
# move the whole chain last — so the repair runs when, and only when, the chain
|
|
32
|
+
# is not already there.
|
|
33
|
+
#
|
|
34
|
+
# Probe the include TARGET, never a parent with a trailing slash: `dir/*` — the
|
|
35
|
+
# ignore this very policy writes — matches `dir/` because `*` matches the empty
|
|
36
|
+
# string, so a parent probe answers "still ignored" in the healthy end state.
|
|
37
|
+
# Keyed on that, the repair re-appended a line already last, which cannot change
|
|
38
|
+
# the answer, and the file grew by one line on every run.
|
|
22
39
|
gitignore_add_effective_include() {
|
|
23
|
-
local root="$1" value="${2#./}" dir parent="" part rc
|
|
40
|
+
local root="$1" value="${2#./}" dir parent="" part line rc
|
|
41
|
+
local -a negations=()
|
|
24
42
|
dir="${value%/*}"
|
|
25
43
|
if [ "$dir" != "$value" ]; then
|
|
26
44
|
while IFS= read -r part; do
|
|
27
45
|
[ -n "$part" ] || continue
|
|
28
46
|
if [ -n "$parent" ]; then parent="$parent/$part"; else parent="$part"; fi
|
|
29
|
-
|
|
30
|
-
if gitignore_path_is_ignored "$root" "$parent/"; then
|
|
31
|
-
printf '!%s/\n' "$parent" >> "$root/.gitignore"
|
|
32
|
-
else
|
|
33
|
-
rc=$?
|
|
34
|
-
[ "$rc" -eq 1 ] || [ "$rc" -eq 2 ] || return "$rc"
|
|
35
|
-
fi
|
|
47
|
+
negations+=("!$parent/")
|
|
36
48
|
done < <(printf '%s\n' "$dir" | tr '/' '\n')
|
|
37
49
|
fi
|
|
50
|
+
negations+=("!$value")
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
for line in "${negations[@]}"; do
|
|
53
|
+
gitignore_add_line "$root" "$line"
|
|
54
|
+
done
|
|
55
|
+
|
|
56
|
+
# `|| rc=$?` keeps the probe out of `set -e`'s reach: its honest "not
|
|
57
|
+
# ignored" is exit 1, and a bare call would abort the whole command there.
|
|
58
|
+
rc=0
|
|
59
|
+
gitignore_path_is_ignored "$root" "$value" || rc=$?
|
|
60
|
+
case "$rc" in
|
|
61
|
+
0) ;;
|
|
62
|
+
1|2) return 0 ;;
|
|
63
|
+
*) return "$rc" ;;
|
|
64
|
+
esac
|
|
65
|
+
|
|
66
|
+
# Still ignored: an earlier negation is shadowed by a later rule. Move the
|
|
67
|
+
# chain last. Already last means appending cannot help — leave it, and let
|
|
68
|
+
# `status --check` report the re-inclusion this policy could not make
|
|
69
|
+
# effective rather than growing the file forever.
|
|
70
|
+
gitignore_tail_is "$root/.gitignore" "${negations[@]}" && return 0
|
|
71
|
+
printf '%s\n' "${negations[@]}" >> "$root/.gitignore"
|
|
46
72
|
}
|
|
47
73
|
|
|
48
74
|
ensure_gitignore_header() {
|
package/cli/lib/registry.sh
CHANGED
|
@@ -136,7 +136,7 @@ fetch_package() {
|
|
|
136
136
|
if is_bundle_source "$url" "$ref" "$subpath" \
|
|
137
137
|
&& { [ -z "$locked_sha" ] || [ -z "$bundle_sha" ] || [ "$locked_sha" = "$bundle_sha" ]; }; then
|
|
138
138
|
[ -n "${IS_BUNDLED_PKG_DIR:-}" ] && [ -d "$IS_BUNDLED_PKG_DIR" ] \
|
|
139
|
-
|| die "bundled engine content not found next to the CLI — reinstall
|
|
139
|
+
|| die "bundled engine content not found next to the CLI — reinstall ${INTELLIGENCE_NPM_PACKAGE:-the CLI package}"
|
|
140
140
|
rm -rf "$dest"
|
|
141
141
|
mkdir -p "$dest"
|
|
142
142
|
cp -R "$IS_BUNDLED_PKG_DIR/." "$dest/"
|
package/cli/lib/semver.sh
CHANGED
|
@@ -21,6 +21,72 @@ semver_cmp() {
|
|
|
21
21
|
}'
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
# semver_cmp_full <a> <b> — prints -1 / 0 / 1 with SemVer prerelease
|
|
25
|
+
# precedence: 1.0.0-rc.1 < 1.0.0-rc.2 < 1.0.0; build metadata is ignored.
|
|
26
|
+
# Package ranges never see prereleases, so semver_cmp stays digits-only; this
|
|
27
|
+
# one is for the CLI's own npm versions, whose `next` channel is a prerelease
|
|
28
|
+
# line.
|
|
29
|
+
semver_cmp_full() {
|
|
30
|
+
awk -v a="${1#v}" -v b="${2#v}" '
|
|
31
|
+
# Numeric identifiers compare as numbers (no leading zeros, so length
|
|
32
|
+
# then digits — exact beyond what a double holds) and sort before
|
|
33
|
+
# alphanumeric ones; those compare as ASCII strings, never as numbers
|
|
34
|
+
# awk might read into them (1e5).
|
|
35
|
+
function idcmp(x, y, xn, yn) {
|
|
36
|
+
xn = (x ~ /^[0-9]+$/); yn = (y ~ /^[0-9]+$/)
|
|
37
|
+
if (xn && yn) {
|
|
38
|
+
if (length(x) != length(y)) return (length(x) < length(y)) ? -1 : 1
|
|
39
|
+
return (x "" < y "") ? -1 : (x "" > y "") ? 1 : 0
|
|
40
|
+
}
|
|
41
|
+
if (xn) return -1
|
|
42
|
+
if (yn) return 1
|
|
43
|
+
return (x "" < y "") ? -1 : (x "" > y "") ? 1 : 0
|
|
44
|
+
}
|
|
45
|
+
BEGIN {
|
|
46
|
+
sub(/\+.*$/, "", a); sub(/\+.*$/, "", b)
|
|
47
|
+
pa = ""; pb = ""
|
|
48
|
+
if (i = index(a, "-")) { pa = substr(a, i + 1); a = substr(a, 1, i - 1) }
|
|
49
|
+
if (i = index(b, "-")) { pb = substr(b, i + 1); b = substr(b, 1, i - 1) }
|
|
50
|
+
na = split(a, A, "."); nb = split(b, B, ".")
|
|
51
|
+
for (i = 1; i <= 3; i++) {
|
|
52
|
+
x = (i <= na) ? A[i] + 0 : 0
|
|
53
|
+
y = (i <= nb) ? B[i] + 0 : 0
|
|
54
|
+
if (x < y) { print -1; exit }
|
|
55
|
+
if (x > y) { print 1; exit }
|
|
56
|
+
}
|
|
57
|
+
# A release outranks every prerelease of the same core.
|
|
58
|
+
if (pa == "" && pb == "") { print 0; exit }
|
|
59
|
+
if (pa == "") { print 1; exit }
|
|
60
|
+
if (pb == "") { print -1; exit }
|
|
61
|
+
na = split(pa, PA, "."); nb = split(pb, PB, ".")
|
|
62
|
+
n = (na < nb) ? na : nb
|
|
63
|
+
for (i = 1; i <= n; i++) {
|
|
64
|
+
c = idcmp(PA[i], PB[i])
|
|
65
|
+
if (c != 0) { print c; exit }
|
|
66
|
+
}
|
|
67
|
+
r = (na < nb) ? -1 : (na > nb) ? 1 : 0
|
|
68
|
+
print r
|
|
69
|
+
}'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# npm_channel_for <version> — the npm dist-tag a CLI version came from:
|
|
73
|
+
# `next` for a prerelease, `latest` otherwise. `update` and `upgrade` both
|
|
74
|
+
# derive the channel from the running version; one rule here keeps them equal.
|
|
75
|
+
npm_channel_for() {
|
|
76
|
+
case "$1" in
|
|
77
|
+
*-*) printf 'next' ;;
|
|
78
|
+
*) printf 'latest' ;;
|
|
79
|
+
esac
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# is_npm_version <string> — 0 iff the string is a version as npm publishes
|
|
83
|
+
# it: `x.y.z`, an optional `-prerelease`, an optional `+build`, nothing else.
|
|
84
|
+
# A registry answer becomes an npm argument, so anything else is refused.
|
|
85
|
+
is_npm_version() {
|
|
86
|
+
local re='^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'
|
|
87
|
+
[[ "$1" =~ $re ]]
|
|
88
|
+
}
|
|
89
|
+
|
|
24
90
|
# semver_is_stable <version> — 0 iff `[v]x.y.z` with nothing else.
|
|
25
91
|
semver_is_stable() {
|
|
26
92
|
case "${1#v}" in
|
package/engine/ENGINE_SHA
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
012f75bfb3c3036df3df9e914597ba937ab75400
|
package/engine/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.13.0
|
package/engine/lib/contract.sh
CHANGED
|
@@ -126,14 +126,14 @@ check_version_compat() {
|
|
|
126
126
|
if [ "$(_ver_major "$stamp")" -gt "$(_ver_major "$eng")" ]; then
|
|
127
127
|
is_status ahead-of-engine "stamp=$stamp engine=$eng"
|
|
128
128
|
echo " ERROR: project stamped $stamp but this engine is $eng — a newer major schema; refusing." >&2
|
|
129
|
-
echo " Update the CLI first:
|
|
129
|
+
echo " Update the CLI first: intelligence upgrade (--next for a prerelease line)" >&2
|
|
130
130
|
return "$IS_RC_AHEAD"
|
|
131
131
|
fi
|
|
132
132
|
# One warning per command, however many processes re-check on the way to
|
|
133
133
|
# the engine (CLI preflight first, then the engine itself). The line has
|
|
134
134
|
# no indent on purpose: `sync --compact` keeps `WARNING:` lines.
|
|
135
135
|
if [ "${IS_SCHEMA_AHEAD_WARNED:-}" != "$stamp" ]; then
|
|
136
|
-
echo "WARNING: project schema $stamp is newer than this CLI's engine $eng — the project uses a newer CLI; update it:
|
|
136
|
+
echo "WARNING: project schema $stamp is newer than this CLI's engine $eng — the project uses a newer CLI; update it: intelligence upgrade (--next for a prerelease line)" >&2
|
|
137
137
|
export IS_SCHEMA_AHEAD_WARNED="$stamp"
|
|
138
138
|
fi
|
|
139
139
|
return 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainova-systems/intelligence",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
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"
|
|
@@ -392,6 +392,7 @@ The public lifecycle is deliberately compact:
|
|
|
392
392
|
- `intelligence init [--preview|--apply]` is universal: it creates a new setup, aligns an existing Intelligence project, or plans/applies conversion of an eligible legacy Intelligence Sync project.
|
|
393
393
|
- `intelligence sync [adapter] [--compact]` first aligns an existing Intelligence project with the installed CLI, restores a missing store strictly from `intelligence.lock`, then renders. Compact mode shows context sizes, actionable warnings and final status on success, and all diagnostics on failure. In CI it refuses an alignment that would change tracked files and points to a local `intelligence init --apply` plus review/commit.
|
|
394
394
|
- `intelligence update [@scope/name] [--preview|--apply]` is the only update surface. It prints the CLI/project/package plan; default mode prompts, `--preview` never writes, and `--apply` does not prompt. It never moves `ref:` pins.
|
|
395
|
+
- `intelligence upgrade [--next] [--preview|--apply]` replaces the installed CLI with the newest version on its npm channel (`next` for a prerelease or with `--next`, otherwise `latest`) with the same modes. It touches no project, never downgrades, and refuses an installation that npm did not make.
|
|
395
396
|
- `intelligence package add|remove|list|search` owns package inventory.
|
|
396
397
|
- `intelligence adapter list|create|enable|disable|remove` owns adapter inventory and target state.
|
|
397
398
|
- `intelligence status [--check]` reports state; `--check` runs deep consistency checks.
|
|
@@ -28,10 +28,15 @@ result.
|
|
|
28
28
|
mode also shows the plan and prompts; after approval, use `--apply` for an
|
|
29
29
|
unambiguous non-interactive execution.
|
|
30
30
|
|
|
31
|
-
4. If the plan reports a newer global CLI, run
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
4. If the plan reports a newer global CLI, run `intelligence upgrade --apply`
|
|
32
|
+
after approval — it installs exactly the version the plan showed — then
|
|
33
|
+
rerun `intelligence update --preview` with the new executable. When the
|
|
34
|
+
project section reports a schema stamped ahead of the stable line, the CLI
|
|
35
|
+
that stamped it was a prerelease: use `intelligence upgrade --next --apply`.
|
|
36
|
+
If `upgrade` refuses because npm did not make this installation, run the
|
|
37
|
+
command its message names instead. Apply the resulting plan with
|
|
38
|
+
`intelligence update --apply`, or `intelligence update $ARGUMENTS --apply`
|
|
39
|
+
when one package was requested.
|
|
35
40
|
|
|
36
41
|
5. An applied update that renders must finish with `IS_STATUS=ok`; preserve and
|
|
37
42
|
stop on any other status. Then run `intelligence status --check`. Do not run
|