@ainova-systems/intelligence 0.11.0-rc.11 → 0.11.0-rc.13
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 +32 -2
- package/cli/commands/init.sh +115 -20
- package/cli/commands/sync.sh +76 -23
- package/cli/intelligence +16 -1
- package/cli/internal/check.sh +40 -0
- package/cli/internal/convert-legacy.sh +2 -8
- package/cli/internal/target-state.sh +24 -17
- package/cli/lib/adapter-lifecycle.sh +33 -0
- package/cli/lib/cli-common.sh +4 -0
- package/cli/lib/gitignore.sh +53 -0
- package/cli/lib/manifest.sh +17 -0
- package/cli/lib/onboarding.sh +63 -0
- package/engine/ENGINE_SHA +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 +11 -0
- package/engine/adapters/pi.sh +18 -5
- package/engine/lib/adapter-contract.sh +100 -0
- package/engine/sync.sh +87 -25
- package/package.json +1 -1
- package/packages/sync/agents/intelligence-architect.md +1 -1
- package/packages/sync/references/adapters.md +44 -5
- package/packages/sync/references/conventions.md +33 -16
- package/packages/sync/references/onboarding-migration.md +74 -0
- package/packages/sync/skills/intelligence-install-adapter/SKILL.md +12 -5
- package/packages/sync/skills/intelligence-learn-from-context/SKILL.md +44 -3
- package/packages/sync/skills/intelligence-learn-from-repository/SKILL.md +43 -10
- package/packages/sync/skills/intelligence-review-skills/SKILL.md +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Preserve the pre-Intelligence state described by enabled adapter contracts.
|
|
3
|
+
# The snapshot is data-only and explicitly identifies itself as initial
|
|
4
|
+
# onboarding state so recovery skills never mistake it for generated output.
|
|
5
|
+
|
|
6
|
+
onboarding_source_paths() {
|
|
7
|
+
local root="$1" content_dir="$2" targets="$3" target output
|
|
8
|
+
for target in $targets; do
|
|
9
|
+
output="$(default_target_output "$target")"
|
|
10
|
+
adapter_contract_paths "$root" "$content_dir" "$target" "$output"
|
|
11
|
+
done | awk 'NF && !seen[$0]++'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
onboarding_source_count() {
|
|
15
|
+
local root="$1" content_dir="$2" targets="$3" rel count=0
|
|
16
|
+
while IFS= read -r rel; do
|
|
17
|
+
[ -e "$root/$rel" ] || [ -L "$root/$rel" ] || continue
|
|
18
|
+
count=$((count + 1))
|
|
19
|
+
done < <(onboarding_source_paths "$root" "$content_dir" "$targets")
|
|
20
|
+
printf '%s\n' "$count"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
preserve_onboarding_sources() {
|
|
24
|
+
local root="$1" content_dir="$2" targets="$3"
|
|
25
|
+
local rel src dest backup_rel backup stage target
|
|
26
|
+
backup_rel="$content_dir/_backup"
|
|
27
|
+
backup="$root/$backup_rel"
|
|
28
|
+
ONBOARDING_BACKUP_COUNT="$(onboarding_source_count "$root" "$content_dir" "$targets")"
|
|
29
|
+
ONBOARDING_BACKUP_REL=""
|
|
30
|
+
export ONBOARDING_BACKUP_COUNT ONBOARDING_BACKUP_REL
|
|
31
|
+
[ "$ONBOARDING_BACKUP_COUNT" -gt 0 ] || return 0
|
|
32
|
+
[ ! -e "$backup" ] || die "onboarding backup already exists at $backup_rel - review or move it before rerunning init"
|
|
33
|
+
|
|
34
|
+
mkdir -p "$root/$content_dir"
|
|
35
|
+
stage="$root/$content_dir/.backup-stage.$$"
|
|
36
|
+
[ ! -e "$stage" ] || die "temporary onboarding backup path already exists"
|
|
37
|
+
mkdir -p "$stage"
|
|
38
|
+
|
|
39
|
+
{
|
|
40
|
+
printf '# Intelligence initial-state backup contract v1\n'
|
|
41
|
+
printf 'state\tinitial-onboarding\n'
|
|
42
|
+
printf 'source\tpre-intelligence\n'
|
|
43
|
+
for target in $targets; do printf 'target\t%s\n' "$target"; done
|
|
44
|
+
} > "$stage/manifest.tsv"
|
|
45
|
+
|
|
46
|
+
while IFS= read -r rel; do
|
|
47
|
+
src="$root/$rel"
|
|
48
|
+
[ -e "$src" ] || [ -L "$src" ] || continue
|
|
49
|
+
dest="$stage/$rel"
|
|
50
|
+
mkdir -p "$(dirname "$dest")"
|
|
51
|
+
if ! cp -R "$src" "$dest"; then
|
|
52
|
+
rm -rf "$stage"
|
|
53
|
+
die "failed to preserve initial AI instruction path '$rel'"
|
|
54
|
+
fi
|
|
55
|
+
printf 'path\t%s\n' "$rel" >> "$stage/manifest.tsv"
|
|
56
|
+
done < <(onboarding_source_paths "$root" "$content_dir" "$targets")
|
|
57
|
+
|
|
58
|
+
mv "$stage" "$backup"
|
|
59
|
+
ensure_gitignore_header "$root"
|
|
60
|
+
gitignore_add_line "$root" "$backup_rel/"
|
|
61
|
+
ONBOARDING_BACKUP_REL="$backup_rel"
|
|
62
|
+
export ONBOARDING_BACKUP_REL
|
|
63
|
+
}
|
package/engine/ENGINE_SHA
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5b60eb9e97337751d1e5390cae4919aedf26ab7a
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
# occurrence with your adapter name before sourcing.
|
|
8
8
|
#
|
|
9
9
|
# Required:
|
|
10
|
-
# 1.
|
|
11
|
-
# 2.
|
|
10
|
+
# 1. Keep adapter_contract_<name>() accurate for every path this file writes
|
|
11
|
+
# 2. Implement sync_to_<name>() and its three content transforms
|
|
12
|
+
# 3. Enable it after implementation: intelligence adapter enable <name>
|
|
12
13
|
# This adds the target to intelligence.yaml when it is absent:
|
|
13
14
|
# targets:
|
|
14
15
|
# <name>: { enabled: true, output: ".<name>" }
|
|
@@ -35,6 +36,22 @@
|
|
|
35
36
|
# loaded. Keep this template position-independent: after scaffolding it lives
|
|
36
37
|
# under the project's content directory, not beside engine/lib/.
|
|
37
38
|
|
|
39
|
+
# Ownership contract. Paths are repository-relative and derive from the
|
|
40
|
+
# configured target output passed as $1. `owned` paths are exclusively managed;
|
|
41
|
+
# `managed` paths share a directory with another adapter or hand-authored files.
|
|
42
|
+
# Add `legacy` inputs to the initial backup, `preserve` for settings that must
|
|
43
|
+
# never be replaced, and explicit ignore/include records for Git policy.
|
|
44
|
+
adapter_contract_<name>() {
|
|
45
|
+
local output="${1%/}"
|
|
46
|
+
adapter_contract_version 1
|
|
47
|
+
adapter_contract_owned "$output/rules"
|
|
48
|
+
adapter_contract_owned "$output/agents"
|
|
49
|
+
adapter_contract_owned "$output/skills"
|
|
50
|
+
adapter_contract_ignore "$output/rules/"
|
|
51
|
+
adapter_contract_ignore "$output/agents/"
|
|
52
|
+
adapter_contract_ignore "$output/skills/"
|
|
53
|
+
}
|
|
54
|
+
|
|
38
55
|
# Sync rules for <agent-name>
|
|
39
56
|
# Typical transformations:
|
|
40
57
|
# - Copy as-is (like Claude)
|
|
@@ -223,6 +223,16 @@ agents_md_append_rules_list() {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
# Main entry point for AGENTS.md adapter
|
|
226
|
+
adapter_contract_agents() {
|
|
227
|
+
local output="$1"
|
|
228
|
+
if [[ "$output" == */ ]] || [[ "$output" != *.md ]]; then
|
|
229
|
+
output="${output%/}/AGENTS.md"
|
|
230
|
+
fi
|
|
231
|
+
adapter_contract_version 1
|
|
232
|
+
adapter_contract_owned "$output"
|
|
233
|
+
adapter_contract_legacy "AGENTS.md"
|
|
234
|
+
}
|
|
235
|
+
|
|
226
236
|
sync_to_agents() {
|
|
227
237
|
local repo_root="$1"
|
|
228
238
|
local config_file="$2"
|
|
@@ -231,10 +241,11 @@ sync_to_agents() {
|
|
|
231
241
|
echo "=== AGENTS.md ==="
|
|
232
242
|
|
|
233
243
|
# output_dir points at the target file path (e.g., /repo/AGENTS.md).
|
|
234
|
-
# If it looks like a directory (trailing slash
|
|
235
|
-
# extension), append default filename.
|
|
244
|
+
# If it looks like a directory lexically (trailing slash or no .md
|
|
245
|
+
# extension), append the default filename. The ownership contract uses the
|
|
246
|
+
# same rule, so filesystem state cannot make its write-set ambiguous.
|
|
236
247
|
local output_file="$output_dir"
|
|
237
|
-
if [
|
|
248
|
+
if [[ "$output_file" == */ ]] || [[ "$output_file" != *.md ]]; then
|
|
238
249
|
output_file="${output_file%/}/AGENTS.md"
|
|
239
250
|
fi
|
|
240
251
|
|
|
@@ -8,6 +8,21 @@
|
|
|
8
8
|
|
|
9
9
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
|
10
10
|
|
|
11
|
+
adapter_contract_claude() {
|
|
12
|
+
local output="${1%/}"
|
|
13
|
+
adapter_contract_version 1
|
|
14
|
+
adapter_contract_owned "$output/rules"
|
|
15
|
+
adapter_contract_owned "$output/agents"
|
|
16
|
+
adapter_contract_owned "$output/skills"
|
|
17
|
+
adapter_contract_legacy "CLAUDE.md"
|
|
18
|
+
adapter_contract_legacy "$output/commands"
|
|
19
|
+
adapter_contract_preserve "$output/settings.json"
|
|
20
|
+
adapter_contract_preserve "$output/settings.local.json"
|
|
21
|
+
adapter_contract_ignore "CLAUDE.md"
|
|
22
|
+
adapter_contract_ignore "$output/*"
|
|
23
|
+
adapter_contract_include "$output/settings.json"
|
|
24
|
+
}
|
|
25
|
+
|
|
11
26
|
# Sync rules to Claude format (copy as-is, normalize LF)
|
|
12
27
|
sync_claude_rules() {
|
|
13
28
|
local repo_root="$1"
|
package/engine/adapters/codex.sh
CHANGED
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
|
|
14
14
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
|
15
15
|
|
|
16
|
+
adapter_contract_codex() {
|
|
17
|
+
local output="${1%/}"
|
|
18
|
+
adapter_contract_version 1
|
|
19
|
+
adapter_contract_requires agents
|
|
20
|
+
adapter_contract_managed ".agents/skills"
|
|
21
|
+
adapter_contract_owned "$output/agents"
|
|
22
|
+
adapter_contract_ignore ".agents/skills/"
|
|
23
|
+
adapter_contract_ignore "$output/agents/"
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
# Sync skills to Codex format (.agents/skills/{name}/SKILL.md)
|
|
17
27
|
sync_codex_skills() {
|
|
18
28
|
local repo_root="$1"
|
|
@@ -111,7 +121,7 @@ sync_to_codex() {
|
|
|
111
121
|
sync_codex_skills "$repo_root" "$config_file" "$skills_dir"
|
|
112
122
|
|
|
113
123
|
# Agents -> .codex/agents/
|
|
114
|
-
local agents_dir="$
|
|
124
|
+
local agents_dir="$output_dir/agents"
|
|
115
125
|
rm -rf "$agents_dir"
|
|
116
126
|
mkdir -p "$agents_dir"
|
|
117
127
|
sync_codex_agents "$repo_root" "$config_file" "$agents_dir"
|
|
@@ -14,6 +14,17 @@
|
|
|
14
14
|
|
|
15
15
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
|
16
16
|
|
|
17
|
+
adapter_contract_copilot() {
|
|
18
|
+
local output="${1%/}"
|
|
19
|
+
adapter_contract_version 1
|
|
20
|
+
adapter_contract_requires agents
|
|
21
|
+
adapter_contract_owned "$output/instructions"
|
|
22
|
+
adapter_contract_owned "$output/prompts"
|
|
23
|
+
adapter_contract_owned "$output/agents"
|
|
24
|
+
adapter_contract_owned "$output/skills"
|
|
25
|
+
adapter_contract_legacy "$output/copilot-instructions.md"
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
# Sync rules to Copilot format. Only path-scoped rules are emitted —
|
|
18
29
|
# always-on rules live in AGENTS.md (read by Copilot natively).
|
|
19
30
|
sync_copilot_rules() {
|
|
@@ -13,6 +13,21 @@
|
|
|
13
13
|
|
|
14
14
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
|
15
15
|
|
|
16
|
+
adapter_contract_cursor() {
|
|
17
|
+
local output="${1%/}"
|
|
18
|
+
adapter_contract_version 1
|
|
19
|
+
adapter_contract_requires agents
|
|
20
|
+
adapter_contract_owned "$output/rules"
|
|
21
|
+
adapter_contract_owned "$output/agents"
|
|
22
|
+
adapter_contract_owned "$output/skills"
|
|
23
|
+
adapter_contract_legacy ".cursorrules"
|
|
24
|
+
adapter_contract_legacy "$output/commands"
|
|
25
|
+
adapter_contract_preserve "$output/settings.json"
|
|
26
|
+
adapter_contract_ignore ".cursorrules"
|
|
27
|
+
adapter_contract_ignore "$output/*"
|
|
28
|
+
adapter_contract_include "$output/settings.json"
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
# Sync rules to Cursor format (.md -> .mdc, paths -> globs)
|
|
17
32
|
sync_cursor_rules() {
|
|
18
33
|
local repo_root="$1"
|
|
@@ -26,6 +26,17 @@
|
|
|
26
26
|
|
|
27
27
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
|
28
28
|
|
|
29
|
+
adapter_contract_opencode() {
|
|
30
|
+
local output="${1%/}"
|
|
31
|
+
adapter_contract_version 1
|
|
32
|
+
adapter_contract_requires agents
|
|
33
|
+
adapter_contract_managed ".agents/skills"
|
|
34
|
+
adapter_contract_owned "$output/agents"
|
|
35
|
+
adapter_contract_managed "$output/commands"
|
|
36
|
+
adapter_contract_ignore ".agents/skills/"
|
|
37
|
+
adapter_contract_ignore "$output/agents/"
|
|
38
|
+
}
|
|
39
|
+
|
|
29
40
|
# Strip frontmatter, return body only.
|
|
30
41
|
# Direct call to the shared `strip_frontmatter` helper in lib/common.sh —
|
|
31
42
|
# duplicating the awk block here would violate the "all parsing in common.sh"
|
package/engine/adapters/pi.sh
CHANGED
|
@@ -15,6 +15,20 @@
|
|
|
15
15
|
|
|
16
16
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
|
17
17
|
|
|
18
|
+
adapter_contract_pi() {
|
|
19
|
+
local output="${1%/}"
|
|
20
|
+
adapter_contract_version 1
|
|
21
|
+
adapter_contract_requires agents
|
|
22
|
+
adapter_contract_managed ".agents/skills"
|
|
23
|
+
adapter_contract_owned "$output/intelligence-sync"
|
|
24
|
+
adapter_contract_managed "$output/extensions"
|
|
25
|
+
adapter_contract_managed "$output/prompts"
|
|
26
|
+
adapter_contract_ignore ".agents/skills/"
|
|
27
|
+
adapter_contract_ignore "$output/intelligence-sync/"
|
|
28
|
+
adapter_contract_ignore "$output/extensions/intelligence-sync-rules.ts"
|
|
29
|
+
adapter_contract_ignore "$output/prompts/intelligence-agent-*.md"
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
pi_ts_escape() {
|
|
19
33
|
local s="$1"
|
|
20
34
|
s="${s//\\/\\\\}"
|
|
@@ -193,13 +207,12 @@ sync_pi_agents() {
|
|
|
193
207
|
|
|
194
208
|
access_note=""
|
|
195
209
|
if [ "$access" = "readonly" ]; then
|
|
196
|
-
|
|
197
|
-
|
|
210
|
+
# Bash 3.2 (the macOS system shell) misparses an apostrophe in
|
|
211
|
+
# a quoted heredoc nested inside command substitution.
|
|
212
|
+
access_note="## Access Mode
|
|
198
213
|
|
|
199
214
|
Default to read-only analysis. Do not change files or run mutating commands unless the user explicitly asks you to override this agent's normal restriction.
|
|
200
|
-
|
|
201
|
-
EOF
|
|
202
|
-
)
|
|
215
|
+
"
|
|
203
216
|
fi
|
|
204
217
|
|
|
205
218
|
{
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Declarative adapter ownership contract shared by the engine and CLI.
|
|
3
|
+
#
|
|
4
|
+
# Every adapter exposes adapter_contract_<name> <configured-output>. The
|
|
5
|
+
# function emits tab-separated records through the helpers below. Keeping the
|
|
6
|
+
# declaration beside sync_to_<name>() makes backup, rollback, git policy and
|
|
7
|
+
# lifecycle checks consume the same ownership model as the writer itself.
|
|
8
|
+
|
|
9
|
+
adapter_contract_version() { printf 'version\t%s\n' "$1"; }
|
|
10
|
+
adapter_contract_requires() { printf 'requires\t%s\n' "$1"; }
|
|
11
|
+
adapter_contract_owned() { printf 'owned\t%s\n' "$1"; }
|
|
12
|
+
adapter_contract_managed() { printf 'managed\t%s\n' "$1"; }
|
|
13
|
+
adapter_contract_legacy() { printf 'legacy\t%s\n' "$1"; }
|
|
14
|
+
adapter_contract_preserve() { printf 'preserve\t%s\n' "$1"; }
|
|
15
|
+
adapter_contract_ignore() { printf 'ignore\t%s\n' "$1"; }
|
|
16
|
+
adapter_contract_include() { printf 'include\t%s\n' "$1"; }
|
|
17
|
+
|
|
18
|
+
adapter_contract_function() {
|
|
19
|
+
printf 'adapter_contract_%s' "$1"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# Reject records that could address anything outside the repository. Contract
|
|
23
|
+
# paths are always repo-relative; ignore/include records may contain globs.
|
|
24
|
+
adapter_contract_safe_path() {
|
|
25
|
+
local path="$1"
|
|
26
|
+
case "$path" in
|
|
27
|
+
""|/*|*\\*|[A-Za-z]:*|..|../*|*/../*|*/..|*$'\t'*|*$'\n'*) return 1 ;;
|
|
28
|
+
*) return 0 ;;
|
|
29
|
+
esac
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
adapter_contract_safe_concrete_path() {
|
|
33
|
+
adapter_contract_safe_path "$1" || return 1
|
|
34
|
+
case "$1" in
|
|
35
|
+
*'*'*|*'?'*|*'['*) return 1 ;;
|
|
36
|
+
*) return 0 ;;
|
|
37
|
+
esac
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# adapter_contract_records <adapter-name> <adapter-file> <configured-output>
|
|
41
|
+
# Source and query in a subshell so a project adapter cannot leak shell state
|
|
42
|
+
# into the caller. Project adapters are trusted executable code during sync;
|
|
43
|
+
# the isolation here is for correctness, not a security boundary.
|
|
44
|
+
adapter_contract_records() (
|
|
45
|
+
local name="$1" file="$2" output="$3" fn line kind value saw_version=0
|
|
46
|
+
# shellcheck source=/dev/null
|
|
47
|
+
source "$file"
|
|
48
|
+
fn="$(adapter_contract_function "$name")"
|
|
49
|
+
declare -F "$fn" >/dev/null 2>&1 || {
|
|
50
|
+
echo "ERROR: adapter '$name' has no $fn contract" >&2
|
|
51
|
+
return 1
|
|
52
|
+
}
|
|
53
|
+
while IFS= read -r line; do
|
|
54
|
+
[ -n "$line" ] || continue
|
|
55
|
+
kind="${line%%$'\t'*}"
|
|
56
|
+
if [ "$kind" = "$line" ]; then
|
|
57
|
+
echo "ERROR: adapter '$name' emitted a malformed contract record" >&2
|
|
58
|
+
return 1
|
|
59
|
+
fi
|
|
60
|
+
value="${line#*$'\t'}"
|
|
61
|
+
case "$kind" in
|
|
62
|
+
version)
|
|
63
|
+
[ "$value" = "1" ] || {
|
|
64
|
+
echo "ERROR: adapter '$name' uses unsupported contract version '$value'" >&2
|
|
65
|
+
return 1
|
|
66
|
+
}
|
|
67
|
+
saw_version=1
|
|
68
|
+
;;
|
|
69
|
+
requires)
|
|
70
|
+
case "$value" in
|
|
71
|
+
""|[!abcdefghijklmnopqrstuvwxyz]*|*[!abcdefghijklmnopqrstuvwxyz0123456789_]*)
|
|
72
|
+
echo "ERROR: adapter '$name' declares invalid requirement '$value'" >&2
|
|
73
|
+
return 1
|
|
74
|
+
;;
|
|
75
|
+
esac
|
|
76
|
+
;;
|
|
77
|
+
owned|managed|legacy|preserve)
|
|
78
|
+
adapter_contract_safe_concrete_path "$value" || {
|
|
79
|
+
echo "ERROR: adapter '$name' declares unsafe $kind path '$value'" >&2
|
|
80
|
+
return 1
|
|
81
|
+
}
|
|
82
|
+
;;
|
|
83
|
+
ignore|include)
|
|
84
|
+
adapter_contract_safe_path "$value" || {
|
|
85
|
+
echo "ERROR: adapter '$name' declares unsafe $kind path '$value'" >&2
|
|
86
|
+
return 1
|
|
87
|
+
}
|
|
88
|
+
;;
|
|
89
|
+
*)
|
|
90
|
+
echo "ERROR: adapter '$name' emitted unknown contract record '$kind'" >&2
|
|
91
|
+
return 1
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
printf '%s\n' "$line"
|
|
95
|
+
done < <("$fn" "$output")
|
|
96
|
+
[ "$saw_version" -eq 1 ] || {
|
|
97
|
+
echo "ERROR: adapter '$name' contract did not declare version 1" >&2
|
|
98
|
+
return 1
|
|
99
|
+
}
|
|
100
|
+
)
|
package/engine/sync.sh
CHANGED
|
@@ -19,6 +19,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
19
19
|
|
|
20
20
|
source "$SCRIPT_DIR/lib/common.sh"
|
|
21
21
|
source "$SCRIPT_DIR/lib/contract.sh"
|
|
22
|
+
source "$SCRIPT_DIR/lib/adapter-contract.sh"
|
|
22
23
|
|
|
23
24
|
if [ -z "${CONFIG_FILE:-}" ] || [ ! -f "${CONFIG_FILE:-}" ]; then
|
|
24
25
|
is_status config-missing "CONFIG_FILE=${CONFIG_FILE:-}"
|
|
@@ -85,31 +86,6 @@ echo " Config: $CONFIG_FILE"
|
|
|
85
86
|
echo " Root: $REPO_ROOT"
|
|
86
87
|
echo ""
|
|
87
88
|
|
|
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
89
|
# Lint frontmatter across all source files (rules, agents, skills).
|
|
114
90
|
# Catches issues like unquoted colons that strict YAML consumers reject.
|
|
115
91
|
for section in rules agents skills; do
|
|
@@ -167,6 +143,88 @@ for adapters_dir in "$SCRIPT_DIR/adapters" "$INTELLIGENCE_DIR/adapters"; do
|
|
|
167
143
|
done
|
|
168
144
|
done
|
|
169
145
|
|
|
146
|
+
# Validate every selected adapter contract before any output is touched, then
|
|
147
|
+
# snapshot the declared write-set. If a later adapter fails, the EXIT handler
|
|
148
|
+
# restores all earlier adapter outputs so sync is atomic from the repository's
|
|
149
|
+
# point of view.
|
|
150
|
+
SYNC_TX_DIR="$(mktemp -d -t intelligence-sync-XXXXXX)"
|
|
151
|
+
SYNC_TX_INDEX="$SYNC_TX_DIR/paths.tsv"
|
|
152
|
+
SYNC_TX_SEEN="$SYNC_TX_DIR/seen"
|
|
153
|
+
mkdir -p "$SYNC_TX_DIR/data"
|
|
154
|
+
: > "$SYNC_TX_INDEX"
|
|
155
|
+
: > "$SYNC_TX_SEEN"
|
|
156
|
+
SYNC_TX_ACTIVE=0
|
|
157
|
+
|
|
158
|
+
snapshot_sync_path() {
|
|
159
|
+
local adapter_name="$1" rel="$2" src index present=0
|
|
160
|
+
grep -Fqx -- "$rel" "$SYNC_TX_SEEN" && return 0
|
|
161
|
+
printf '%s\n' "$rel" >> "$SYNC_TX_SEEN"
|
|
162
|
+
validate_output_path "$REPO_ROOT" "$CONFIG_FILE" "$adapter_name" "$REPO_ROOT/$rel"
|
|
163
|
+
index="$(wc -l < "$SYNC_TX_INDEX" | tr -d ' ')"
|
|
164
|
+
src="$REPO_ROOT/$rel"
|
|
165
|
+
if [ -e "$src" ] || [ -L "$src" ]; then
|
|
166
|
+
cp -a "$src" "$SYNC_TX_DIR/data/$index"
|
|
167
|
+
present=1
|
|
168
|
+
fi
|
|
169
|
+
printf '%s\t%s\t%s\n' "$index" "$rel" "$present" >> "$SYNC_TX_INDEX"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
restore_sync_snapshot() {
|
|
173
|
+
local index rel present dst
|
|
174
|
+
while IFS=$'\t' read -r index rel present; do
|
|
175
|
+
[ -n "$rel" ] || continue
|
|
176
|
+
dst="$REPO_ROOT/$rel"
|
|
177
|
+
rm -rf "$dst"
|
|
178
|
+
if [ "$present" = "1" ]; then
|
|
179
|
+
mkdir -p "$(dirname "$dst")"
|
|
180
|
+
cp -a "$SYNC_TX_DIR/data/$index" "$dst"
|
|
181
|
+
fi
|
|
182
|
+
done < "$SYNC_TX_INDEX"
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
finish_sync_transaction() {
|
|
186
|
+
local rc=$?
|
|
187
|
+
trap - EXIT INT TERM
|
|
188
|
+
set +e
|
|
189
|
+
if [ "${SYNC_TX_ACTIVE:-0}" = "1" ] && [ "$rc" -ne 0 ]; then
|
|
190
|
+
restore_sync_snapshot
|
|
191
|
+
echo "ERROR: sync failed; all adapter-owned paths were restored to their pre-sync state." >&2
|
|
192
|
+
fi
|
|
193
|
+
rm -rf "$SYNC_TX_DIR"
|
|
194
|
+
exit "$rc"
|
|
195
|
+
}
|
|
196
|
+
trap finish_sync_transaction EXIT
|
|
197
|
+
trap 'exit 130' INT TERM
|
|
198
|
+
|
|
199
|
+
preflight_idx=0
|
|
200
|
+
while [ "$preflight_idx" -lt "${#ADAPTERS[@]}" ]; do
|
|
201
|
+
adapter="${ADAPTERS[$preflight_idx]}"
|
|
202
|
+
adapter_file="${ADAPTER_FILES[$preflight_idx]}"
|
|
203
|
+
preflight_idx=$((preflight_idx + 1))
|
|
204
|
+
if [ -n "$TARGET_FILTER" ] && [ "$adapter" != "$TARGET_FILTER" ]; then
|
|
205
|
+
continue
|
|
206
|
+
fi
|
|
207
|
+
[ "$(is_target_enabled "$CONFIG_FILE" "$adapter")" = "1" ] || continue
|
|
208
|
+
output="$(get_target_output "$CONFIG_FILE" "$adapter")"
|
|
209
|
+
[ -n "$output" ] || output=".$adapter"
|
|
210
|
+
validate_output_path "$REPO_ROOT" "$CONFIG_FILE" "$adapter" "$REPO_ROOT/$output"
|
|
211
|
+
records="$(adapter_contract_records "$adapter" "$adapter_file" "$output")" || exit 1
|
|
212
|
+
while IFS=$'\t' read -r kind value; do
|
|
213
|
+
[ "$kind" = "requires" ] || continue
|
|
214
|
+
if [ "$(is_target_enabled "$CONFIG_FILE" "$value")" != "1" ]; then
|
|
215
|
+
echo "ERROR: targets.$adapter requires enabled target '$value'." >&2
|
|
216
|
+
echo " Enable it first: intelligence adapter enable $value" >&2
|
|
217
|
+
exit 1
|
|
218
|
+
fi
|
|
219
|
+
done <<< "$records"
|
|
220
|
+
while IFS=$'\t' read -r kind value; do
|
|
221
|
+
case "$kind" in
|
|
222
|
+
owned|managed) snapshot_sync_path "$adapter" "$value" ;;
|
|
223
|
+
esac
|
|
224
|
+
done <<< "$records"
|
|
225
|
+
done
|
|
226
|
+
SYNC_TX_ACTIVE=1
|
|
227
|
+
|
|
170
228
|
synced=0
|
|
171
229
|
adapter_count=${#ADAPTERS[@]}
|
|
172
230
|
adapter_idx=0
|
|
@@ -224,6 +282,10 @@ if [ $synced -eq 0 ]; then
|
|
|
224
282
|
exit 1
|
|
225
283
|
fi
|
|
226
284
|
|
|
285
|
+
SYNC_TX_ACTIVE=0
|
|
286
|
+
rm -rf "$SYNC_TX_DIR"
|
|
287
|
+
trap - EXIT INT TERM
|
|
288
|
+
|
|
227
289
|
# Warn about unsynced directories
|
|
228
290
|
warn_unsynced "$REPO_ROOT" "$CONFIG_FILE"
|
|
229
291
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainova-systems/intelligence",
|
|
3
|
-
"version": "0.11.0-rc.
|
|
3
|
+
"version": "0.11.0-rc.13",
|
|
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"
|
|
@@ -47,7 +47,7 @@ The per-artifact checks are procedure, so they live in the meta-skills rather th
|
|
|
47
47
|
| `intelligence-extract-skill` | turn an observed workflow into a skill |
|
|
48
48
|
| `intelligence-review-skills` | audit the layer for duplication, drift, size, hardcoded paths |
|
|
49
49
|
| `intelligence-learn-from-repository` | propose the initial project-owned layer from repository evidence |
|
|
50
|
-
| `intelligence-learn-from-context` | fold a session
|
|
50
|
+
| `intelligence-learn-from-context` | recover/finalize onboarding, or later fold a session lesson into the layer |
|
|
51
51
|
| `intelligence-sync` | project the source to every tool channel |
|
|
52
52
|
| `intelligence-update` | interpret and apply the CLI's unified update plan |
|
|
53
53
|
| `intelligence-install-adapter` / `intelligence-uninstall-adapter` | research and manage a tool adapter |
|
|
@@ -42,14 +42,25 @@ To stop syncing a target:
|
|
|
42
42
|
intelligence adapter disable mytool
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Disabling changes only target state. Generated output is deliberately kept
|
|
45
|
+
Disabling changes only target state. Generated output is deliberately kept so disabling is reversible; the adapter contract identifies its paths for review. A disabled project adapter can be deleted with `intelligence adapter remove mytool`; removal prompts by default, accepts `--apply` for explicit non-interactive use, and also keeps generated output. Built-in adapter source cannot be removed. Use `intelligence adapter list` to inspect source, state and output.
|
|
46
46
|
|
|
47
|
-
## Required
|
|
47
|
+
## Required interface
|
|
48
48
|
|
|
49
|
-
The file name and function
|
|
49
|
+
The file name, contract function and sync function form the adapter's identity:
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
52
|
# <content-dir>/adapters/mytool.sh
|
|
53
|
+
adapter_contract_mytool() {
|
|
54
|
+
local output="${1%/}"
|
|
55
|
+
adapter_contract_version 1
|
|
56
|
+
adapter_contract_owned "$output/rules"
|
|
57
|
+
adapter_contract_owned "$output/agents"
|
|
58
|
+
adapter_contract_owned "$output/skills"
|
|
59
|
+
adapter_contract_ignore "$output/rules/"
|
|
60
|
+
adapter_contract_ignore "$output/agents/"
|
|
61
|
+
adapter_contract_ignore "$output/skills/"
|
|
62
|
+
}
|
|
63
|
+
|
|
53
64
|
sync_to_mytool() {
|
|
54
65
|
local repo_root="$1"
|
|
55
66
|
local config_file="$2"
|
|
@@ -59,6 +70,25 @@ sync_to_mytool() {
|
|
|
59
70
|
}
|
|
60
71
|
```
|
|
61
72
|
|
|
73
|
+
The contract accepts the configured repo-relative output path and emits only
|
|
74
|
+
records through these helpers:
|
|
75
|
+
|
|
76
|
+
| Record | Meaning |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `adapter_contract_version 1` | Required interface version |
|
|
79
|
+
| `adapter_contract_requires <name>` | Another target that must be enabled for a full sync |
|
|
80
|
+
| `adapter_contract_owned <path>` | Path exclusively regenerated by this adapter |
|
|
81
|
+
| `adapter_contract_managed <path>` | Shared or marker-managed path modified by this adapter |
|
|
82
|
+
| `adapter_contract_legacy <path>` | Pre-Intelligence input preserved in the initial backup |
|
|
83
|
+
| `adapter_contract_preserve <path>` | Settings or state preserved in place and included in the initial backup |
|
|
84
|
+
| `adapter_contract_ignore <pattern>` | Exact `.gitignore` pattern managed on enable/init |
|
|
85
|
+
| `adapter_contract_include <pattern>` | Exact negated `.gitignore` pattern managed on enable/init |
|
|
86
|
+
|
|
87
|
+
All paths are repository-relative. The CLI refuses missing, malformed, unsafe,
|
|
88
|
+
or unsupported contracts before enabling or syncing an adapter. `owned` and
|
|
89
|
+
`managed` paths form the transactional write-set: if any adapter fails, the
|
|
90
|
+
engine restores every selected adapter path to its pre-sync state.
|
|
91
|
+
|
|
62
92
|
The engine calls:
|
|
63
93
|
|
|
64
94
|
```text
|
|
@@ -177,8 +207,8 @@ Call `finalize_output_file` on every text file after transformation. It expands
|
|
|
177
207
|
|
|
178
208
|
Adapters regenerate output, so cleanup is part of their public contract.
|
|
179
209
|
|
|
180
|
-
1. Delete only paths
|
|
181
|
-
2.
|
|
210
|
+
1. Delete only paths declared `owned`. Preserve sibling settings, commands, extensions, workflows and hand-authored files.
|
|
211
|
+
2. Keep `adapter_contract_<name>()` exactly aligned with every path `sync_to_<name>()` writes or deletes.
|
|
182
212
|
3. Use marker-based cleanup when generated and hand-authored files share a directory. The OpenCode adapter is the reference implementation.
|
|
183
213
|
4. Use `sync_open_skill_dirs` for `.agents/skills/`; multiple adapters share it.
|
|
184
214
|
5. Write only beneath the supplied `output_dir`, except for an explicitly shared standard path handled by a shared helper.
|
|
@@ -211,6 +241,13 @@ sync_mytool_rules() {
|
|
|
211
241
|
done < <(read_yaml_list "$config_file" "rules")
|
|
212
242
|
}
|
|
213
243
|
|
|
244
|
+
adapter_contract_mytool() {
|
|
245
|
+
local output="${1%/}"
|
|
246
|
+
adapter_contract_version 1
|
|
247
|
+
adapter_contract_owned "$output/rules"
|
|
248
|
+
adapter_contract_ignore "$output/rules/"
|
|
249
|
+
}
|
|
250
|
+
|
|
214
251
|
sync_to_mytool() {
|
|
215
252
|
local repo_root="$1" config_file="$2" output_dir="$3"
|
|
216
253
|
|
|
@@ -234,6 +271,8 @@ Verify:
|
|
|
234
271
|
- skill resources are present;
|
|
235
272
|
- no literal layout tokens remain;
|
|
236
273
|
- hand-authored siblings under the tool root survive;
|
|
274
|
+
- a deliberately failing later adapter restores all earlier output byte-for-byte;
|
|
275
|
+
- `intelligence status --check` accepts the contract and its Git policy;
|
|
237
276
|
- output paths cannot overlap sources or escape the repository;
|
|
238
277
|
- a second sync produces no Git diff.
|
|
239
278
|
|