@ainova-systems/intelligence 0.11.0-rc.9 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/commands/adapter.sh +46 -6
- package/cli/commands/init.sh +182 -33
- package/cli/commands/package.sh +2 -2
- package/cli/commands/registry.sh +3 -3
- package/cli/commands/status.sh +4 -4
- package/cli/commands/sync.sh +76 -23
- package/cli/commands/update.sh +1 -1
- package/cli/intelligence +18 -3
- package/cli/internal/{upgrade-v2.sh → align-project.sh} +9 -8
- package/cli/internal/check.sh +51 -2
- package/cli/internal/{migrate-v1.sh → convert-legacy.sh} +33 -29
- package/cli/internal/package-add.sh +1 -1
- package/cli/internal/package-list.sh +1 -1
- package/cli/internal/package-remove.sh +1 -1
- package/cli/internal/package-search.sh +1 -1
- package/cli/internal/package-update.sh +1 -1
- package/cli/internal/restore.sh +1 -1
- package/cli/internal/target-state.sh +26 -18
- package/cli/lib/adapter-lifecycle.sh +43 -0
- package/cli/lib/cli-common.sh +12 -8
- package/cli/lib/gitignore.sh +215 -0
- package/cli/lib/manifest.sh +35 -1
- package/cli/lib/onboarding.sh +137 -0
- package/engine/ENGINE_SHA +1 -1
- package/engine/VERSION +1 -1
- package/engine/adapters/_template.sh +19 -2
- package/engine/adapters/agents.sh +14 -3
- package/engine/adapters/claude.sh +15 -0
- package/engine/adapters/codex.sh +11 -1
- package/engine/adapters/copilot.sh +11 -0
- package/engine/adapters/cursor.sh +15 -0
- package/engine/adapters/opencode.sh +13 -2
- package/engine/adapters/pi.sh +18 -5
- package/engine/lib/adapter-contract.sh +100 -0
- package/engine/lib/common.sh +5 -0
- package/engine/lib/contract.sh +2 -2
- package/engine/sync.sh +87 -25
- package/package.json +1 -1
- package/packages/sync/agents/intelligence-architect.md +2 -2
- package/packages/sync/references/adapters.md +51 -7
- package/packages/sync/references/conventions.md +60 -21
- package/packages/sync/references/onboarding-migration.md +87 -0
- package/packages/sync/skills/intelligence-install-adapter/SKILL.md +12 -5
- package/packages/sync/skills/intelligence-learn-from-context/SKILL.md +23 -4
- package/packages/sync/skills/intelligence-learn-from-repository/SKILL.md +94 -38
- package/packages/sync/skills/intelligence-review-skills/SKILL.md +1 -1
- package/packages/sync/skills/intelligence-sync/SKILL.md +9 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Internal: bring
|
|
3
|
-
#
|
|
2
|
+
# Internal: bring an Intelligence project to this CLI's engine: apply schema
|
|
3
|
+
# migrations, align the engine-content package to the bundled version,
|
|
4
4
|
# restamp schema_version, sync.
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
@@ -14,7 +14,7 @@ while [ $# -gt 0 ]; do
|
|
|
14
14
|
shift
|
|
15
15
|
done
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
require_cli_project
|
|
18
18
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
19
19
|
|
|
20
20
|
stamp="$(read_schema_version "$manifest")"
|
|
@@ -31,14 +31,15 @@ if [ -n "$legacy_stamp" ] && _ver_gt "$legacy_stamp" "$eng"; then
|
|
|
31
31
|
die "manifest schema $legacy_stamp is newer than this CLI's engine $eng — update the global CLI first"
|
|
32
32
|
fi
|
|
33
33
|
|
|
34
|
-
# ---
|
|
34
|
+
# --- project schema migrations --------------------------------------------
|
|
35
35
|
# The engine's own chain owns vendored layouts; this one owns the manifest.
|
|
36
36
|
# Same discipline: ascending, append-only, each migration self-detects and
|
|
37
37
|
# no-ops when already applied.
|
|
38
38
|
|
|
39
|
-
#
|
|
39
|
+
# migration 0: the original RC manifest called the permanent schema key
|
|
40
40
|
# `sync_version`. Rename it without changing its value; the final stamp below
|
|
41
|
-
# then aligns it with this engine. The
|
|
41
|
+
# then aligns it with this engine. The legacy Intelligence Sync reader remains
|
|
42
|
+
# separate.
|
|
42
43
|
if [ -n "$legacy_stamp" ]; then
|
|
43
44
|
if [ -n "$stamp" ]; then
|
|
44
45
|
[ "$stamp" = "$legacy_stamp" ] || die "manifest has conflicting schema_version '$stamp' and sync_version '$legacy_stamp'"
|
|
@@ -49,7 +50,7 @@ if [ -n "$legacy_stamp" ]; then
|
|
|
49
50
|
echo " migrating: sync_version -> schema_version"
|
|
50
51
|
fi
|
|
51
52
|
|
|
52
|
-
#
|
|
53
|
+
# migration 1: package entries contain requested intent only. Early RCs
|
|
53
54
|
# also copied resolved url/path into the manifest. Preserve the existing lock
|
|
54
55
|
# as the trusted source and remove that duplicate representation. A direct
|
|
55
56
|
# unversioned package becomes an explicit HEAD pin so its manifest entry stays
|
|
@@ -74,7 +75,7 @@ if [ "$source_fields_migrated" -eq 1 ]; then
|
|
|
74
75
|
echo " migrating: package url/path -> intelligence.lock"
|
|
75
76
|
fi
|
|
76
77
|
|
|
77
|
-
#
|
|
78
|
+
# migration 2: staged engine content -> the @ainova-systems/sync package.
|
|
78
79
|
# Pre-package manifests listed `.intelligence/engine/{rules,agents,skills}`
|
|
79
80
|
# as sources fed by a CLI-staged copy; that copy is now an ordinary package
|
|
80
81
|
# entry seeded from the bundle. Post-condition: no `.intelligence/engine`
|
package/cli/internal/check.sh
CHANGED
|
@@ -20,9 +20,9 @@ case "$IP_MODE" in
|
|
|
20
20
|
exit 0
|
|
21
21
|
;;
|
|
22
22
|
legacy)
|
|
23
|
-
echo "project:
|
|
23
|
+
echo "project: legacy Intelligence Sync at $IP_ROOT"
|
|
24
24
|
ok "vendored engine $(tr -d ' \t\r\n' < "$IP_MODULE_DIR/scripts/VERSION")"
|
|
25
|
-
echo " → 'intelligence init' converts it
|
|
25
|
+
echo " → 'intelligence init' converts it into an Intelligence project"
|
|
26
26
|
exit 0
|
|
27
27
|
;;
|
|
28
28
|
esac
|
|
@@ -86,6 +86,55 @@ while IFS= read -r name; do
|
|
|
86
86
|
fi
|
|
87
87
|
done < <(qmap_keys "$manifest" "packages")
|
|
88
88
|
|
|
89
|
+
# Adapter contracts are the shared source of truth for dependencies, rollback,
|
|
90
|
+
# onboarding backup and Git policy. Validate every declared target, including
|
|
91
|
+
# disabled ones, so a later enable cannot reveal a stale custom adapter.
|
|
92
|
+
content_dir="$(manifest_intelligence_dir "$manifest")"
|
|
93
|
+
while IFS= read -r target; do
|
|
94
|
+
[ -n "$target" ] || continue
|
|
95
|
+
output="$(get_target_output "$manifest" "$target")"
|
|
96
|
+
[ -n "$output" ] || output="$(default_target_output "$target")"
|
|
97
|
+
adapter_file="$(adapter_file_for "$IP_ROOT" "$content_dir" "$target" 2>/dev/null || true)"
|
|
98
|
+
if [ -z "$adapter_file" ]; then
|
|
99
|
+
warn "adapter '$target' is declared but its implementation is missing"
|
|
100
|
+
continue
|
|
101
|
+
fi
|
|
102
|
+
records="$(adapter_contract_records "$target" "$adapter_file" "$output" 2>/dev/null || true)"
|
|
103
|
+
if ! printf '%s\n' "$records" | grep -Fqx $'version\t1'; then
|
|
104
|
+
warn "adapter '$target' has an invalid ownership contract"
|
|
105
|
+
continue
|
|
106
|
+
fi
|
|
107
|
+
while IFS=$'\t' read -r kind value; do
|
|
108
|
+
if [ "$kind" = "requires" ] \
|
|
109
|
+
&& [ "$(is_target_enabled "$manifest" "$target")" = "1" ] \
|
|
110
|
+
&& [ "$(is_target_enabled "$manifest" "$value")" != "1" ]; then
|
|
111
|
+
warn "enabled adapter '$target' requires enabled adapter '$value'"
|
|
112
|
+
fi
|
|
113
|
+
if [ "$(is_target_enabled "$manifest" "$target")" = "1" ]; then
|
|
114
|
+
case "$kind" in
|
|
115
|
+
ignore)
|
|
116
|
+
grep -Fqx -- "$value" "$IP_ROOT/.gitignore" 2>/dev/null \
|
|
117
|
+
|| warn "adapter '$target' Git policy is missing '$value'"
|
|
118
|
+
;;
|
|
119
|
+
include)
|
|
120
|
+
if ! grep -Fqx -- "!$value" "$IP_ROOT/.gitignore" 2>/dev/null; then
|
|
121
|
+
warn "adapter '$target' Git policy is missing '!$value'"
|
|
122
|
+
elif git -C "$IP_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
123
|
+
if git -C "$IP_ROOT" check-ignore -q --no-index -- "$value"; then
|
|
124
|
+
warn "adapter '$target' Git policy cannot re-include '$value' because another ignore rule still wins"
|
|
125
|
+
else
|
|
126
|
+
ignore_rc=$?
|
|
127
|
+
[ "$ignore_rc" -eq 1 ] \
|
|
128
|
+
|| warn "adapter '$target' Git policy for '$value' could not be evaluated"
|
|
129
|
+
fi
|
|
130
|
+
fi
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
fi
|
|
134
|
+
done <<< "$records"
|
|
135
|
+
ok "adapter $target contract v1"
|
|
136
|
+
done < <(target_names "$manifest")
|
|
137
|
+
|
|
89
138
|
# Lock orphans.
|
|
90
139
|
if [ -f "$lock" ]; then
|
|
91
140
|
while IFS= read -r name; do
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Internal
|
|
3
|
-
# to
|
|
2
|
+
# Internal conversion from legacy Intelligence Sync to Intelligence CLI.
|
|
3
|
+
# to an Intelligence project: root intelligence.yaml, .intelligence/ store,
|
|
4
4
|
# intelligence.lock, no vendored engine.
|
|
5
5
|
#
|
|
6
6
|
# Transactional: STAGE everything next to the
|
|
@@ -24,8 +24,8 @@ done
|
|
|
24
24
|
detect_project
|
|
25
25
|
case "$IP_MODE" in
|
|
26
26
|
legacy) ;;
|
|
27
|
-
|
|
28
|
-
*) die "no
|
|
27
|
+
cli) die "already an Intelligence project ($IP_ROOT/intelligence.yaml)" ;;
|
|
28
|
+
*) die "no legacy Intelligence Sync project found here — 'intelligence init' starts a fresh project" ;;
|
|
29
29
|
esac
|
|
30
30
|
root="$IP_ROOT"
|
|
31
31
|
umbrella="$IP_UMBRELLA"
|
|
@@ -36,22 +36,22 @@ umbrella_rel="${umbrella#"$root"/}"
|
|
|
36
36
|
|
|
37
37
|
for existing in intelligence.yaml intelligence.lock .intelligence; do
|
|
38
38
|
if [ -e "$root/$existing" ] || [ -L "$root/$existing" ]; then
|
|
39
|
-
die "$existing already exists at the root — conflicting
|
|
39
|
+
die "$existing already exists at the root — conflicting Intelligence CLI state; move it aside or restore the project before converting"
|
|
40
40
|
fi
|
|
41
41
|
done
|
|
42
42
|
if [ "$dry_run" -eq 0 ] && [ "$force" -eq 0 ]; then
|
|
43
43
|
if [ -n "$(git -C "$root" status --porcelain 2>/dev/null)" ]; then
|
|
44
|
-
die "working tree is not clean — commit or stash first (or --force);
|
|
44
|
+
die "working tree is not clean — commit or stash first (or --force); conversion produces a one-commit diff you can review and revert"
|
|
45
45
|
fi
|
|
46
46
|
fi
|
|
47
47
|
|
|
48
|
-
# `sync_version` belongs to the
|
|
48
|
+
# `sync_version` belongs to the legacy Intelligence Sync format. Intelligence uses
|
|
49
49
|
# `schema_version`, so conversion reads the legacy scalar explicitly.
|
|
50
50
|
stamp="$(top_scalar "$config" "sync_version")"
|
|
51
51
|
eng="$(bundled_engine_version)"
|
|
52
52
|
[ -n "$stamp" ] && _ver_gt "$stamp" "$eng" && die "project schema $stamp is newer than this CLI's engine $eng — update the CLI first"
|
|
53
53
|
|
|
54
|
-
# The
|
|
54
|
+
# The legacy `packs:` block is the one Intelligence Sync shape conversion still reads, and the
|
|
55
55
|
# engine no longer knows it — the reader lives here, with its only consumer.
|
|
56
56
|
get_pack_field() {
|
|
57
57
|
get_nested_yaml_value "$1" "packs" "$2" "$3"
|
|
@@ -73,10 +73,10 @@ validate_mirror_path() {
|
|
|
73
73
|
case "$canon" in "$root"/*) ;; *) die "pack '$pack' mirror escapes the repository: '$mirror'" ;; esac
|
|
74
74
|
rel="${canon#"$root"/}"
|
|
75
75
|
|
|
76
|
-
# A
|
|
76
|
+
# A legacy mirror may live below the content directory (the normal location is
|
|
77
77
|
# intelligence/external/<pack>), but it must never own the directory itself,
|
|
78
78
|
# the vendored module, CLI state, authored sources, or generated outputs.
|
|
79
|
-
case "$umbrella_rel" in "$rel"|"$rel"/*) die "pack '$pack' mirror '$mirror' contains the
|
|
79
|
+
case "$umbrella_rel" in "$rel"|"$rel"/*) die "pack '$pack' mirror '$mirror' contains the legacy content directory" ;; esac
|
|
80
80
|
for protected in .git .intelligence "$module_rel"; do
|
|
81
81
|
paths_overlap "$rel" "$protected" && die "pack '$pack' mirror '$mirror' overlaps protected path '$protected'"
|
|
82
82
|
done
|
|
@@ -115,14 +115,14 @@ cleanup_stage() { rm -rf "$stage"; }
|
|
|
115
115
|
trap cleanup_stage EXIT INT TERM
|
|
116
116
|
mkdir -p "$stage/.intelligence/packages"
|
|
117
117
|
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
# project already at the final
|
|
118
|
+
# Intelligence carries no legacy migration chain: the archived Intelligence Sync
|
|
119
|
+
# engine knows how to walk an old project forward. Conversion accepts only a
|
|
120
|
+
# project already at the final legacy schema and names the exact remedy otherwise —
|
|
121
121
|
# a remedy that keeps working indefinitely, because the archive stays at the
|
|
122
122
|
# URL the project's own update.sh already clones.
|
|
123
|
-
|
|
124
|
-
if [ -z "$stamp" ] || _ver_gt "$
|
|
125
|
-
echo "ERROR: project schema is ${stamp:-absent (pre-0.3.1)}, older than the final
|
|
123
|
+
LEGACY_FINAL_SCHEMA="0.10.0"
|
|
124
|
+
if [ -z "$stamp" ] || _ver_gt "$LEGACY_FINAL_SCHEMA" "$stamp"; then
|
|
125
|
+
echo "ERROR: project schema is ${stamp:-absent (pre-0.3.1)}, older than the final legacy Intelligence Sync schema $LEGACY_FINAL_SCHEMA." >&2
|
|
126
126
|
echo " Bring it forward with its own engine first — that still works:" >&2
|
|
127
127
|
echo " bash $module_rel/scripts/update.sh --yes" >&2
|
|
128
128
|
echo " then re-run: intelligence init" >&2
|
|
@@ -134,7 +134,7 @@ echo "== staging =="
|
|
|
134
134
|
# bundle (fetch_package's guard — no network).
|
|
135
135
|
sync_pkg_sha="$(fetch_package "$SYNC_PKG_URL" "v$eng" "$SYNC_PKG_PATH" "$stage/$SYNC_PKG_STORE")"
|
|
136
136
|
|
|
137
|
-
# Legacy pack name ->
|
|
137
|
+
# Legacy pack name -> Intelligence package name (@org/repo derived from the url), plus
|
|
138
138
|
# per-pack source facts. Recorded as rows for the config rewrite and the lock.
|
|
139
139
|
pack_rows="$stage/packs.rows"
|
|
140
140
|
: > "$pack_rows"
|
|
@@ -325,6 +325,10 @@ if [ -f "$root/.gitignore" ]; then
|
|
|
325
325
|
gitignore_existed=1
|
|
326
326
|
cp "$root/.gitignore" "$stage/gitignore.orig"
|
|
327
327
|
fi
|
|
328
|
+
while IFS= read -r policy_file; do
|
|
329
|
+
[ -f "$root/$policy_file" ] || continue
|
|
330
|
+
cp "$root/$policy_file" "$stage/$policy_file.orig"
|
|
331
|
+
done < <(publisher_ignore_file_names)
|
|
328
332
|
commit_active=1
|
|
329
333
|
rollback() {
|
|
330
334
|
[ "$commit_active" -eq 1 ] || return 0
|
|
@@ -336,6 +340,10 @@ rollback() {
|
|
|
336
340
|
else
|
|
337
341
|
rm -f "$root/.gitignore"
|
|
338
342
|
fi
|
|
343
|
+
while IFS= read -r policy_file; do
|
|
344
|
+
[ -f "$stage/$policy_file.orig" ] || continue
|
|
345
|
+
cp "$stage/$policy_file.orig" "$root/$policy_file"
|
|
346
|
+
done < <(publisher_ignore_file_names)
|
|
339
347
|
rm -rf "$stage"
|
|
340
348
|
echo "rolled back — the vendored setup is untouched" >&2
|
|
341
349
|
}
|
|
@@ -348,24 +356,19 @@ rollback_on_exit() {
|
|
|
348
356
|
# plain set -e/EXIT failure rather than only an explicit sync refusal.
|
|
349
357
|
trap rollback_on_exit EXIT
|
|
350
358
|
trap 'rollback; exit 130' INT TERM
|
|
351
|
-
if [ ! -f "$root/.gitignore" ] || ! grep -q '^\.intelligence/' "$root/.gitignore"; then
|
|
352
|
-
{
|
|
353
|
-
[ -f "$root/.gitignore" ] && [ -n "$(tail -c 1 "$root/.gitignore" 2>/dev/null)" ] && echo ""
|
|
354
|
-
echo "# intelligence CLI package store (restored automatically by 'intelligence sync')"
|
|
355
|
-
echo ".intelligence/"
|
|
356
|
-
} >> "$root/.gitignore"
|
|
357
|
-
fi
|
|
358
359
|
mv "$stage/.intelligence" "$root/.intelligence"
|
|
359
360
|
cp "$manifest_stage" "$root/intelligence.yaml"
|
|
360
361
|
lock_write_from_tsv "$root/intelligence.lock" "$lock_rows"
|
|
362
|
+
ensure_manifest_gitignore "$root" "$root/intelligence.yaml"
|
|
363
|
+
ensure_manifest_publisher_ignores "$root" "$root/intelligence.yaml"
|
|
361
364
|
|
|
362
365
|
sync_out="$(cd "$root" && IS_SUPPRESS_CLI_NOTE=1 bash "$CLI_DIR/commands/sync.sh" 2>&1)" || {
|
|
363
366
|
echo "$sync_out"
|
|
364
|
-
die "sync of the
|
|
367
|
+
die "sync of the converted state failed"
|
|
365
368
|
}
|
|
366
369
|
echo "$sync_out" | grep -q '^IS_STATUS=ok' || {
|
|
367
370
|
echo "$sync_out"
|
|
368
|
-
die "sync of the
|
|
371
|
+
die "sync of the converted state did not report ok"
|
|
369
372
|
}
|
|
370
373
|
# Point of no return: the new state is verified. From here an interrupt must
|
|
371
374
|
# NOT roll back (the old setup is being removed) — only clean the stage.
|
|
@@ -384,7 +387,7 @@ while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
|
384
387
|
esac
|
|
385
388
|
if [ -d "$root/$mirror" ]; then
|
|
386
389
|
rm -rf "${root:?}/$mirror"
|
|
387
|
-
# An emptied
|
|
390
|
+
# An emptied legacy mirror parent (for example intelligence/external/) goes with it.
|
|
388
391
|
rmdir "$(dirname "$root/$mirror")" 2>/dev/null || true
|
|
389
392
|
fi
|
|
390
393
|
done < "$pack_rows"
|
|
@@ -395,6 +398,7 @@ rm -f "$config"
|
|
|
395
398
|
rmdir "$umbrella" 2>/dev/null || true
|
|
396
399
|
|
|
397
400
|
echo ""
|
|
398
|
-
echo "
|
|
401
|
+
echo "converted. Review the diff, then commit. The old config.yaml is kept at .intelligence/backup/config.yaml."
|
|
399
402
|
echo "From now on: intelligence sync | package | update | status."
|
|
400
|
-
|
|
403
|
+
report_tracked_managed_ignores "$root" "$root/intelligence.yaml"
|
|
404
|
+
echo "Next: ask your agent to run /intelligence-learn-from-repository to verify setup and review the converted project context."
|
|
@@ -12,7 +12,7 @@ term="${1:-}"
|
|
|
12
12
|
|
|
13
13
|
detect_project
|
|
14
14
|
manifest=""
|
|
15
|
-
[ "$IP_MODE" = "
|
|
15
|
+
[ "$IP_MODE" = "cli" ] && manifest="$IP_ROOT/intelligence.yaml"
|
|
16
16
|
|
|
17
17
|
# State of one package name in this project: installed / declared / available.
|
|
18
18
|
pkg_state() {
|
package/cli/internal/restore.sh
CHANGED
|
@@ -14,7 +14,7 @@ case "$action" in enable|disable) ;; *) die "internal target state: expected <en
|
|
|
14
14
|
[ -n "$name" ] || die "internal target state: missing adapter name"
|
|
15
15
|
assert_valid_target_name "$name"
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
require_cli_project
|
|
18
18
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
19
19
|
content_dir="$(manifest_intelligence_dir "$manifest")"
|
|
20
20
|
|
|
@@ -25,31 +25,39 @@ if [ "$action" = "enable" ]; then
|
|
|
25
25
|
die "adapter '$name' not found — create it first: intelligence adapter create $name"
|
|
26
26
|
fi
|
|
27
27
|
|
|
28
|
-
# These adapters intentionally omit always-on rules because their tools
|
|
29
|
-
# read AGENTS.md. Do not create a manifest that full sync must refuse.
|
|
30
|
-
case "$name" in
|
|
31
|
-
cursor|copilot|codex|pi|opencode)
|
|
32
|
-
if [ "$(is_target_enabled "$manifest" "agents")" != "1" ]; then
|
|
33
|
-
die "target '$name' requires target 'agents' — enable it first"
|
|
34
|
-
fi
|
|
35
|
-
;;
|
|
36
|
-
esac
|
|
37
|
-
|
|
38
28
|
output="$(get_target_output "$manifest" "$name")"
|
|
39
29
|
[ -n "$output" ] || output="$(default_target_output "$name")"
|
|
30
|
+
validate_adapter_contract_for "$IP_ROOT" "$content_dir" "$name" "$output" \
|
|
31
|
+
|| die "adapter '$name' has an invalid ownership contract"
|
|
32
|
+
while IFS= read -r required; do
|
|
33
|
+
[ -n "$required" ] || continue
|
|
34
|
+
if [ "$(is_target_enabled "$manifest" "$required")" != "1" ]; then
|
|
35
|
+
die "target '$name' requires target '$required' — enable it first"
|
|
36
|
+
fi
|
|
37
|
+
done < <(adapter_records_for "$IP_ROOT" "$content_dir" "$name" "$output" \
|
|
38
|
+
| awk -F '\t' '$1 == "requires" { print $2 }')
|
|
40
39
|
target_set_enabled "$manifest" "$name" true "$output"
|
|
40
|
+
ensure_target_gitignore "$IP_ROOT" "$manifest" "$name"
|
|
41
|
+
ensure_manifest_publisher_ignores "$IP_ROOT" "$manifest"
|
|
41
42
|
echo "enabled: $name (output: $output)"
|
|
42
43
|
exit 0
|
|
43
44
|
fi
|
|
44
45
|
|
|
45
46
|
target_exists "$manifest" "$name" || die "target '$name' is not in the manifest"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
while IFS= read -r dependent; do
|
|
48
|
+
[ -n "$dependent" ] || continue
|
|
49
|
+
[ "$dependent" = "$name" ] && continue
|
|
50
|
+
[ "$(is_target_enabled "$manifest" "$dependent")" = "1" ] || continue
|
|
51
|
+
dependent_output="$(get_target_output "$manifest" "$dependent")"
|
|
52
|
+
[ -n "$dependent_output" ] || dependent_output="$(default_target_output "$dependent")"
|
|
53
|
+
dependent_records="$(adapter_records_for "$IP_ROOT" "$content_dir" "$dependent" "$dependent_output")" \
|
|
54
|
+
|| die "adapter '$dependent' has an invalid ownership contract"
|
|
55
|
+
if awk -F '\t' -v required="$name" \
|
|
56
|
+
'$1 == "requires" && $2 == required { found=1 } END { exit(found ? 0 : 1) }' \
|
|
57
|
+
<<< "$dependent_records"; then
|
|
58
|
+
die "target '$name' is required by enabled target '$dependent' — disable '$dependent' first"
|
|
59
|
+
fi
|
|
60
|
+
done < <(target_names "$manifest")
|
|
53
61
|
output="$(get_target_output "$manifest" "$name")"
|
|
54
62
|
[ -n "$output" ] || output="$(default_target_output "$name")"
|
|
55
63
|
target_set_enabled "$manifest" "$name" false "$output"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CLI consumers of the engine's declarative adapter contract.
|
|
3
|
+
|
|
4
|
+
adapter_file_for() {
|
|
5
|
+
local root="$1" content_dir="$2" name="$3"
|
|
6
|
+
if [ -f "$root/$content_dir/adapters/$name.sh" ]; then
|
|
7
|
+
printf '%s\n' "$root/$content_dir/adapters/$name.sh"
|
|
8
|
+
elif [ -f "$IS_ENGINE_DIR/adapters/$name.sh" ]; then
|
|
9
|
+
printf '%s\n' "$IS_ENGINE_DIR/adapters/$name.sh"
|
|
10
|
+
else
|
|
11
|
+
return 1
|
|
12
|
+
fi
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
adapter_records_for() {
|
|
16
|
+
local root="$1" content_dir="$2" name="$3" output="$4" file
|
|
17
|
+
file="$(adapter_file_for "$root" "$content_dir" "$name")" || {
|
|
18
|
+
echo "ERROR: adapter '$name' not found" >&2
|
|
19
|
+
return 1
|
|
20
|
+
}
|
|
21
|
+
adapter_contract_records "$name" "$file" "$output"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
validate_adapter_contract_for() {
|
|
25
|
+
local root="$1" content_dir="$2" name="$3" output="$4"
|
|
26
|
+
adapter_records_for "$root" "$content_dir" "$name" "$output" >/dev/null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
adapter_contract_paths() {
|
|
30
|
+
local root="$1" content_dir="$2" name="$3" output="$4"
|
|
31
|
+
adapter_records_for "$root" "$content_dir" "$name" "$output" \
|
|
32
|
+
| awk -F '\t' '$1 == "owned" || $1 == "managed" || $1 == "legacy" || $1 == "preserve" { print $2 }'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Keep the record kind for onboarding. A single concrete path may be both an
|
|
36
|
+
# adapter output and a legacy input (AGENTS.md is the important example), so
|
|
37
|
+
# flattening the contract too early loses the information needed to quarantine
|
|
38
|
+
# only legacy entry points.
|
|
39
|
+
adapter_contract_onboarding_records() {
|
|
40
|
+
local root="$1" content_dir="$2" name="$3" output="$4"
|
|
41
|
+
adapter_records_for "$root" "$content_dir" "$name" "$output" \
|
|
42
|
+
| awk -F '\t' '$1 == "owned" || $1 == "managed" || $1 == "legacy" || $1 == "preserve" { print $1 "\t" $2 }'
|
|
43
|
+
}
|
package/cli/lib/cli-common.sh
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
# IS_ENGINE_DIR come exported from the dispatcher.
|
|
12
12
|
source "$IS_ENGINE_DIR/lib/common.sh"
|
|
13
13
|
source "$IS_ENGINE_DIR/lib/contract.sh"
|
|
14
|
+
source "$IS_ENGINE_DIR/lib/adapter-contract.sh"
|
|
14
15
|
|
|
15
16
|
die() {
|
|
16
17
|
echo "ERROR: $*" >&2
|
|
@@ -50,6 +51,9 @@ source "$CLI_DIR/lib/manifest.sh"
|
|
|
50
51
|
source "$CLI_DIR/lib/semver.sh"
|
|
51
52
|
source "$CLI_DIR/lib/registry.sh"
|
|
52
53
|
source "$CLI_DIR/lib/lockfile.sh"
|
|
54
|
+
source "$CLI_DIR/lib/adapter-lifecycle.sh"
|
|
55
|
+
source "$CLI_DIR/lib/gitignore.sh"
|
|
56
|
+
source "$CLI_DIR/lib/onboarding.sh"
|
|
53
57
|
|
|
54
58
|
# The engine-content package: OPTIONAL but auto-selected at init. Package by
|
|
55
59
|
# UX (manifest entry, lockfile row, list/search/remove), bundle by mechanics —
|
|
@@ -70,11 +74,11 @@ DEFAULT_REGISTRY_URL="$(top_scalar "$_EPKG" "default_registry")"
|
|
|
70
74
|
SYNC_PKG_STORE=".intelligence/packages/$SYNC_PKG_NAME"
|
|
71
75
|
|
|
72
76
|
# --- Project detection ---------------------------------------------------
|
|
73
|
-
# Sets: IP_MODE (
|
|
77
|
+
# Sets: IP_MODE (cli|legacy|none), IP_ROOT, IP_UMBRELLA, IP_MODULE_DIR.
|
|
74
78
|
# These ARE this lib's public API — every command reads them after calling
|
|
75
79
|
# detect_project, which per-file shellcheck cannot see.
|
|
76
80
|
# shellcheck disable=SC2034
|
|
77
|
-
#
|
|
81
|
+
# The CLI product wins: the nearest ancestor holding intelligence.yaml. Legacy is a git
|
|
78
82
|
# repo holding an umbrella (a dir with config.yaml) whose module is identified
|
|
79
83
|
# by role — scripts/sync.sh + scripts/VERSION — never by name.
|
|
80
84
|
detect_project() {
|
|
@@ -82,7 +86,7 @@ detect_project() {
|
|
|
82
86
|
local dir="$PWD"
|
|
83
87
|
while :; do
|
|
84
88
|
if [ -f "$dir/intelligence.yaml" ]; then
|
|
85
|
-
IP_MODE="
|
|
89
|
+
IP_MODE="cli"
|
|
86
90
|
IP_ROOT="$(cd "$dir" && pwd)"
|
|
87
91
|
return 0
|
|
88
92
|
fi
|
|
@@ -109,11 +113,11 @@ detect_project() {
|
|
|
109
113
|
return 0
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
|
|
116
|
+
require_cli_project() {
|
|
113
117
|
detect_project
|
|
114
118
|
case "$IP_MODE" in
|
|
115
|
-
|
|
116
|
-
legacy) die "this is a
|
|
119
|
+
cli) ;;
|
|
120
|
+
legacy) die "this is a legacy Intelligence Sync project — run 'intelligence init' to convert it, or keep using its own flow" ;;
|
|
117
121
|
*) die "no intelligence project found here — run 'intelligence init'" ;;
|
|
118
122
|
esac
|
|
119
123
|
}
|
|
@@ -176,7 +180,7 @@ export_engine_env() {
|
|
|
176
180
|
|
|
177
181
|
# --- Project lifecycle preflight -----------------------------------------
|
|
178
182
|
# Public commands are intentionally few. They share this state gate so a CLI
|
|
179
|
-
# installed at a newer engine version brings the current
|
|
183
|
+
# installed at a newer engine version brings the current Intelligence project forward
|
|
180
184
|
# before a mutating operation. The npm install itself cannot do that: it runs
|
|
181
185
|
# outside any project and does not know which repositories the user owns.
|
|
182
186
|
|
|
@@ -268,7 +272,7 @@ ensure_project_current() {
|
|
|
268
272
|
die "project lifecycle requires alignment (stamp ${stamp:-unstamped}, engine $eng) — run 'intelligence init --apply' locally, review and commit the diff"
|
|
269
273
|
fi
|
|
270
274
|
echo " project alignment: stamp ${stamp:-unstamped}, engine $eng"
|
|
271
|
-
bash "$CLI_DIR/internal/
|
|
275
|
+
bash "$CLI_DIR/internal/align-project.sh" --no-sync
|
|
272
276
|
}
|
|
273
277
|
|
|
274
278
|
project_store_missing() {
|