@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
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Generated-output policy derived from each adapter's ownership contract.
|
|
3
|
+
|
|
4
|
+
gitignore_add_line() {
|
|
5
|
+
local root="$1" line="$2" file="$1/.gitignore"
|
|
6
|
+
if [ -f "$file" ] && grep -Fqx -- "$line" "$file"; then
|
|
7
|
+
return 0
|
|
8
|
+
fi
|
|
9
|
+
printf '%s\n' "$line" >> "$file"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
gitignore_path_is_ignored() {
|
|
13
|
+
local root="$1" path="$2"
|
|
14
|
+
git -C "$root" rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 2
|
|
15
|
+
git -C "$root" check-ignore -q --no-index -- "$path"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# Git cannot re-include a child of an excluded directory. Add explicit parent
|
|
19
|
+
# reinclusions, then make sure both parent and child negations occur after any
|
|
20
|
+
# pre-existing rule that still wins. The check-ignore probe also catches broad
|
|
21
|
+
# patterns such as an old `.claude/` or `.cursor/**`, not just exact matches.
|
|
22
|
+
gitignore_add_effective_include() {
|
|
23
|
+
local root="$1" value="${2#./}" dir parent="" part rc
|
|
24
|
+
dir="${value%/*}"
|
|
25
|
+
if [ "$dir" != "$value" ]; then
|
|
26
|
+
while IFS= read -r part; do
|
|
27
|
+
[ -n "$part" ] || continue
|
|
28
|
+
if [ -n "$parent" ]; then parent="$parent/$part"; else parent="$part"; fi
|
|
29
|
+
gitignore_add_line "$root" "!$parent/"
|
|
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
|
|
36
|
+
done < <(printf '%s\n' "$dir" | tr '/' '\n')
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
gitignore_add_line "$root" "!$value"
|
|
40
|
+
if gitignore_path_is_ignored "$root" "$value"; then
|
|
41
|
+
printf '!%s\n' "$value" >> "$root/.gitignore"
|
|
42
|
+
else
|
|
43
|
+
rc=$?
|
|
44
|
+
[ "$rc" -eq 1 ] || [ "$rc" -eq 2 ] || return "$rc"
|
|
45
|
+
fi
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ensure_gitignore_header() {
|
|
49
|
+
local root="$1" file="$1/.gitignore"
|
|
50
|
+
if [ ! -f "$file" ] || ! grep -Fqx '# Intelligence generated state and tool output' "$file"; then
|
|
51
|
+
if [ -f "$file" ] && [ -s "$file" ] && [ -n "$(tail -c 1 "$file" 2>/dev/null)" ]; then
|
|
52
|
+
printf '\n' >> "$file"
|
|
53
|
+
fi
|
|
54
|
+
printf '%s\n' '# Intelligence generated state and tool output' >> "$file"
|
|
55
|
+
fi
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ensure_base_gitignore() {
|
|
59
|
+
local root="$1"
|
|
60
|
+
ensure_gitignore_header "$root"
|
|
61
|
+
gitignore_add_line "$root" '.intelligence/'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ensure_target_gitignore() {
|
|
65
|
+
local root="$1" manifest="$2" target="$3" content_dir output kind value records
|
|
66
|
+
ensure_gitignore_header "$root"
|
|
67
|
+
content_dir="$(manifest_intelligence_dir "$manifest")"
|
|
68
|
+
output="$(get_target_output "$manifest" "$target")"
|
|
69
|
+
[ -n "$output" ] || output="$(default_target_output "$target")"
|
|
70
|
+
records="$(adapter_records_for "$root" "$content_dir" "$target" "$output")" \
|
|
71
|
+
|| die "adapter '$target' has an invalid ownership contract"
|
|
72
|
+
while IFS=$'\t' read -r kind value; do
|
|
73
|
+
case "$kind" in
|
|
74
|
+
ignore) gitignore_add_line "$root" "$value" ;;
|
|
75
|
+
include) gitignore_add_effective_include "$root" "$value" ;;
|
|
76
|
+
esac
|
|
77
|
+
done <<< "$records"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ensure_manifest_gitignore() {
|
|
81
|
+
local root="$1" manifest="$2" target
|
|
82
|
+
ensure_base_gitignore "$root"
|
|
83
|
+
while IFS= read -r target; do
|
|
84
|
+
[ -n "$target" ] || continue
|
|
85
|
+
if [ "$(is_target_enabled "$manifest" "$target")" = "1" ]; then
|
|
86
|
+
ensure_target_gitignore "$root" "$manifest" "$target"
|
|
87
|
+
fi
|
|
88
|
+
done < <(target_names "$manifest")
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Publishing/build tools do not share Git's ignore policy. In particular,
|
|
92
|
+
# .vscodeignore and .npmignore become the packager's own filter, while Docker
|
|
93
|
+
# always builds from its independent context filter. Only amend files the
|
|
94
|
+
# project already has: their presence is the explicit signal that this root is
|
|
95
|
+
# packaged or sent as a build context.
|
|
96
|
+
publisher_ignore_file_names() {
|
|
97
|
+
printf '%s\n' .vscodeignore .npmignore .dockerignore
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
publisher_ignore_add_line() {
|
|
101
|
+
local file="$1" line="$2"
|
|
102
|
+
if grep -Fqx -- "$line" "$file" 2>/dev/null; then
|
|
103
|
+
return 0
|
|
104
|
+
fi
|
|
105
|
+
printf '%s\n' "$line" >> "$file"
|
|
106
|
+
PUBLISH_IGNORE_FILE_CHANGED=1
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
publisher_ignore_add_path() {
|
|
110
|
+
local file="$1" path="${2#./}"
|
|
111
|
+
path="${path%/}"
|
|
112
|
+
[ -n "$path" ] || return 0
|
|
113
|
+
publisher_ignore_add_line "$file" "$path"
|
|
114
|
+
publisher_ignore_add_line "$file" "$path/**"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
ensure_manifest_publisher_ignores() {
|
|
118
|
+
local root="$1" manifest="$2" rel file content_dir target output records kind value
|
|
119
|
+
content_dir="$(manifest_intelligence_dir "$manifest")"
|
|
120
|
+
while IFS= read -r rel; do
|
|
121
|
+
[ -n "$rel" ] || continue
|
|
122
|
+
file="$root/$rel"
|
|
123
|
+
[ -f "$file" ] || continue
|
|
124
|
+
PUBLISH_IGNORE_FILE_CHANGED=0
|
|
125
|
+
if ! grep -Fqx '# Intelligence development context and generated output' "$file" 2>/dev/null; then
|
|
126
|
+
if [ -s "$file" ] && [ -n "$(tail -c 1 "$file" 2>/dev/null)" ]; then
|
|
127
|
+
printf '\n' >> "$file"
|
|
128
|
+
fi
|
|
129
|
+
printf '%s\n' '# Intelligence development context and generated output' >> "$file"
|
|
130
|
+
PUBLISH_IGNORE_FILE_CHANGED=1
|
|
131
|
+
fi
|
|
132
|
+
publisher_ignore_add_path "$file" '.intelligence'
|
|
133
|
+
publisher_ignore_add_line "$file" 'intelligence.yaml'
|
|
134
|
+
publisher_ignore_add_line "$file" 'intelligence.lock'
|
|
135
|
+
publisher_ignore_add_path "$file" "$content_dir"
|
|
136
|
+
while IFS= read -r target; do
|
|
137
|
+
[ -n "$target" ] || continue
|
|
138
|
+
[ "$(is_target_enabled "$manifest" "$target")" = "1" ] || continue
|
|
139
|
+
output="$(get_target_output "$manifest" "$target")"
|
|
140
|
+
[ -n "$output" ] || output="$(default_target_output "$target")"
|
|
141
|
+
publisher_ignore_add_path "$file" "$output"
|
|
142
|
+
records="$(adapter_records_for "$root" "$content_dir" "$target" "$output")" \
|
|
143
|
+
|| die "adapter '$target' has an invalid ownership contract"
|
|
144
|
+
while IFS=$'\t' read -r kind value; do
|
|
145
|
+
case "$kind" in
|
|
146
|
+
owned|managed|legacy) publisher_ignore_add_path "$file" "$value" ;;
|
|
147
|
+
esac
|
|
148
|
+
done <<< "$records"
|
|
149
|
+
done < <(target_names "$manifest")
|
|
150
|
+
if [ "$PUBLISH_IGNORE_FILE_CHANGED" -eq 1 ]; then
|
|
151
|
+
echo "packaging exclusions updated: $rel"
|
|
152
|
+
fi
|
|
153
|
+
done < <(publisher_ignore_file_names)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
managed_gitignore_patterns() {
|
|
157
|
+
local root="$1" manifest="$2" content_dir target output records kind value
|
|
158
|
+
content_dir="$(manifest_intelligence_dir "$manifest")"
|
|
159
|
+
printf '%s\n' '.intelligence/' "$content_dir/_backup/"
|
|
160
|
+
while IFS= read -r target; do
|
|
161
|
+
[ -n "$target" ] || continue
|
|
162
|
+
[ "$(is_target_enabled "$manifest" "$target")" = "1" ] || continue
|
|
163
|
+
output="$(get_target_output "$manifest" "$target")"
|
|
164
|
+
[ -n "$output" ] || output="$(default_target_output "$target")"
|
|
165
|
+
records="$(adapter_records_for "$root" "$content_dir" "$target" "$output")" \
|
|
166
|
+
|| return 1
|
|
167
|
+
while IFS=$'\t' read -r kind value; do
|
|
168
|
+
case "$kind" in
|
|
169
|
+
ignore) printf '%s\n' "$value" ;;
|
|
170
|
+
include) printf '!%s\n' "$value" ;;
|
|
171
|
+
esac
|
|
172
|
+
done <<< "$records"
|
|
173
|
+
done < <(target_names "$manifest")
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# Git does not apply ignore rules retroactively to files already in its index.
|
|
177
|
+
# Name each affected path that still exists and print a command which only
|
|
178
|
+
# untracks it. Quarantined legacy paths are already worktree deletions and must
|
|
179
|
+
# not receive a misleading "keep the local copy" instruction.
|
|
180
|
+
report_tracked_managed_ignores() {
|
|
181
|
+
local root="$1" manifest="$2" patterns tracked path quoted posix_quoted powershell_quoted announced=0
|
|
182
|
+
git -C "$root" rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 0
|
|
183
|
+
patterns="$(mktemp -t intelligence-gitignore-XXXXXX)"
|
|
184
|
+
tracked="$(mktemp -t intelligence-tracked-XXXXXX)"
|
|
185
|
+
managed_gitignore_patterns "$root" "$manifest" > "$patterns" || {
|
|
186
|
+
rm -f "$patterns" "$tracked"
|
|
187
|
+
return 0
|
|
188
|
+
}
|
|
189
|
+
git -C "$root" ls-files -z -ci --exclude-from="$patterns" > "$tracked" 2>/dev/null || true
|
|
190
|
+
rm -f "$patterns"
|
|
191
|
+
[ -s "$tracked" ] || {
|
|
192
|
+
rm -f "$tracked"
|
|
193
|
+
return 0
|
|
194
|
+
}
|
|
195
|
+
while IFS= read -r -d '' path; do
|
|
196
|
+
[ -n "$path" ] || continue
|
|
197
|
+
[ -e "$root/$path" ] || [ -L "$root/$path" ] || continue
|
|
198
|
+
if [ "$announced" -eq 0 ]; then
|
|
199
|
+
echo " Tracked files still bypass these .gitignore rules. Untrack them without deleting local copies:"
|
|
200
|
+
announced=1
|
|
201
|
+
fi
|
|
202
|
+
if [[ "$path" == *"'"* ]]; then
|
|
203
|
+
# macOS still ships Bash 3.2; use portable sed rather than newer
|
|
204
|
+
# parameter-replacement behavior for the embedded quote.
|
|
205
|
+
posix_quoted="$(printf '%s' "$path" | sed "s/'/'\\\\''/g")"
|
|
206
|
+
powershell_quoted="$(printf '%s' "$path" | sed "s/'/''/g")"
|
|
207
|
+
printf " POSIX: git rm --cached -- '%s'\n" "$posix_quoted"
|
|
208
|
+
printf " PowerShell: git rm --cached -- '%s'\n" "$powershell_quoted"
|
|
209
|
+
else
|
|
210
|
+
quoted="'$path'"
|
|
211
|
+
printf ' git rm --cached -- %s\n' "$quoted"
|
|
212
|
+
fi
|
|
213
|
+
done < "$tracked"
|
|
214
|
+
rm -f "$tracked"
|
|
215
|
+
}
|
package/cli/lib/manifest.sh
CHANGED
|
@@ -60,16 +60,38 @@ target_exists() {
|
|
|
60
60
|
' "$file"
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
# target_names <manifest> — declared target keys in manifest order.
|
|
64
|
+
target_names() {
|
|
65
|
+
local file="$1"
|
|
66
|
+
[ -f "$file" ] || return 0
|
|
67
|
+
awk '
|
|
68
|
+
{ sub(/\r$/, "") }
|
|
69
|
+
/^targets:[[:space:]]*$/ { in_targets = 1; next }
|
|
70
|
+
in_targets && /^[A-Za-z]/ { in_targets = 0 }
|
|
71
|
+
in_targets && /^ [a-z][a-z0-9_]*:/ {
|
|
72
|
+
line = $0
|
|
73
|
+
sub(/^ /, "", line)
|
|
74
|
+
sub(/:.*/, "", line)
|
|
75
|
+
print line
|
|
76
|
+
}
|
|
77
|
+
' "$file"
|
|
78
|
+
}
|
|
79
|
+
|
|
63
80
|
# target_set_enabled <manifest> <name> <true|false> <default-output>
|
|
64
81
|
# Transactionally update only `enabled`, preserving every other target field
|
|
65
82
|
# and comment. A missing target is added in the compact form used by init.
|
|
66
83
|
target_set_enabled() {
|
|
67
84
|
local file="$1" target="$2" enabled="$3" output="$4"
|
|
85
|
+
local target_missing=1
|
|
68
86
|
[ -f "$file" ] || die "no such file: $file"
|
|
69
87
|
case "$enabled" in true|false) ;; *) die "internal: invalid target state '$enabled'" ;; esac
|
|
88
|
+
target_exists "$file" "$target" && target_missing=0
|
|
70
89
|
output="$(_yq_esc "$output")"
|
|
71
|
-
_qmap_stage "$file" -v target="$target" -v enabled="$enabled" -v output="$output" '
|
|
90
|
+
_qmap_stage "$file" -v target="$target" -v enabled="$enabled" -v output="$output" -v target_missing="$target_missing" '
|
|
72
91
|
function entry() { return " " target ": { enabled: " enabled ", output: \"" output "\" }" }
|
|
92
|
+
function insert_missing_target() {
|
|
93
|
+
if (in_targets && target_missing && !target_seen) { print entry(); target_seen = 1 }
|
|
94
|
+
}
|
|
73
95
|
function add_enabled_to_inline(line, p) {
|
|
74
96
|
p = index(line, "{")
|
|
75
97
|
if (p > 0) return substr(line, 1, p) " enabled: " enabled "," substr(line, p + 1)
|
|
@@ -86,8 +108,20 @@ target_set_enabled() {
|
|
|
86
108
|
}
|
|
87
109
|
{ sub(/\r$/, "") }
|
|
88
110
|
/^targets:[[:space:]]*$/ { targets_seen = 1; in_targets = 1; print; next }
|
|
111
|
+
in_targets && target_missing && target_entry_seen && /^#/ {
|
|
112
|
+
flush_target()
|
|
113
|
+
insert_missing_target()
|
|
114
|
+
print
|
|
115
|
+
next
|
|
116
|
+
}
|
|
117
|
+
in_targets && target_missing && target_entry_seen && !in_target && /^[[:space:]]*$/ {
|
|
118
|
+
insert_missing_target()
|
|
119
|
+
print
|
|
120
|
+
next
|
|
121
|
+
}
|
|
89
122
|
in_targets && /^[A-Za-z]/ { flush_targets() }
|
|
90
123
|
in_targets && /^ [A-Za-z]/ {
|
|
124
|
+
target_entry_seen = 1
|
|
91
125
|
flush_target()
|
|
92
126
|
if ($0 ~ "^ " target ":[[:space:]]*") {
|
|
93
127
|
target_seen = 1
|
|
@@ -0,0 +1,137 @@
|
|
|
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_records() {
|
|
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_onboarding_records "$root" "$content_dir" "$target" "$output"
|
|
11
|
+
done | awk -F '\t' 'NF == 2 && !seen[$1 SUBSEP $2]++'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
onboarding_source_paths() {
|
|
15
|
+
onboarding_source_records "$1" "$2" "$3" \
|
|
16
|
+
| awk -F '\t' 'NF == 2 && !seen[$2]++ { print $2 }'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
onboarding_legacy_paths() {
|
|
20
|
+
onboarding_source_records "$1" "$2" "$3" \
|
|
21
|
+
| awk -F '\t' '$1 == "legacy" && !seen[$2]++ { print $2 }'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
onboarding_source_count() {
|
|
25
|
+
local root="$1" content_dir="$2" targets="$3" rel count=0
|
|
26
|
+
while IFS= read -r rel; do
|
|
27
|
+
[ -e "$root/$rel" ] || [ -L "$root/$rel" ] || continue
|
|
28
|
+
count=$((count + 1))
|
|
29
|
+
done < <(onboarding_source_paths "$root" "$content_dir" "$targets")
|
|
30
|
+
printf '%s\n' "$count"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
preserve_onboarding_sources() {
|
|
34
|
+
local root="$1" content_dir="$2" targets="$3"
|
|
35
|
+
local rel src dest backup_rel backup stage target legacy_count=0
|
|
36
|
+
backup_rel="$content_dir/_backup"
|
|
37
|
+
backup="$root/$backup_rel"
|
|
38
|
+
ONBOARDING_BACKUP_COUNT="$(onboarding_source_count "$root" "$content_dir" "$targets")"
|
|
39
|
+
ONBOARDING_BACKUP_REL=""
|
|
40
|
+
ONBOARDING_LEGACY_COUNT=0
|
|
41
|
+
export ONBOARDING_BACKUP_COUNT ONBOARDING_BACKUP_REL ONBOARDING_LEGACY_COUNT
|
|
42
|
+
[ "$ONBOARDING_BACKUP_COUNT" -gt 0 ] || return 0
|
|
43
|
+
[ ! -e "$backup" ] || die "onboarding backup already exists at $backup_rel - review or move it before rerunning init"
|
|
44
|
+
|
|
45
|
+
mkdir -p "$root/$content_dir"
|
|
46
|
+
stage="$root/$content_dir/.backup-stage.$$"
|
|
47
|
+
[ ! -e "$stage" ] || die "temporary onboarding backup path already exists"
|
|
48
|
+
mkdir -p "$stage"
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
printf '# Intelligence initial-state backup contract v1\n'
|
|
52
|
+
printf 'state\tinitial-onboarding\n'
|
|
53
|
+
printf 'source\tpre-intelligence\n'
|
|
54
|
+
for target in $targets; do printf 'target\t%s\n' "$target"; done
|
|
55
|
+
} > "$stage/manifest.tsv"
|
|
56
|
+
|
|
57
|
+
while IFS= read -r rel; do
|
|
58
|
+
src="$root/$rel"
|
|
59
|
+
[ -e "$src" ] || [ -L "$src" ] || continue
|
|
60
|
+
dest="$stage/$rel"
|
|
61
|
+
mkdir -p "$(dirname "$dest")"
|
|
62
|
+
if ! cp -R "$src" "$dest"; then
|
|
63
|
+
rm -rf "$stage"
|
|
64
|
+
die "failed to preserve initial AI instruction path '$rel'"
|
|
65
|
+
fi
|
|
66
|
+
printf 'path\t%s\n' "$rel" >> "$stage/manifest.tsv"
|
|
67
|
+
done < <(onboarding_source_paths "$root" "$content_dir" "$targets")
|
|
68
|
+
|
|
69
|
+
while IFS= read -r rel; do
|
|
70
|
+
[ -e "$stage/$rel" ] || [ -L "$stage/$rel" ] || continue
|
|
71
|
+
printf 'legacy\t%s\n' "$rel" >> "$stage/manifest.tsv"
|
|
72
|
+
legacy_count=$((legacy_count + 1))
|
|
73
|
+
done < <(onboarding_legacy_paths "$root" "$content_dir" "$targets")
|
|
74
|
+
|
|
75
|
+
# This marker is deliberately outside manifest.tsv: the manifest is the
|
|
76
|
+
# immutable inventory, while the marker is short-lived transaction state.
|
|
77
|
+
[ "$legacy_count" -eq 0 ] || : > "$stage/.quarantine-pending"
|
|
78
|
+
|
|
79
|
+
mv "$stage" "$backup"
|
|
80
|
+
ensure_gitignore_header "$root"
|
|
81
|
+
gitignore_add_line "$root" "$backup_rel/"
|
|
82
|
+
ONBOARDING_BACKUP_REL="$backup_rel"
|
|
83
|
+
ONBOARDING_LEGACY_COUNT="$legacy_count"
|
|
84
|
+
export ONBOARDING_BACKUP_REL ONBOARDING_LEGACY_COUNT
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
onboarding_quarantine_is_pending() {
|
|
88
|
+
[ -f "$1/$2/_backup/.quarantine-pending" ]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
onboarding_backup_legacy_paths() {
|
|
92
|
+
local manifest="$1/$2/_backup/manifest.tsv"
|
|
93
|
+
[ -f "$manifest" ] || return 0
|
|
94
|
+
awk -F '\t' '$1 == "legacy" && NF == 2 && !seen[$2]++ { print $2 }' "$manifest"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
quarantine_onboarding_legacy_sources() {
|
|
98
|
+
local root="$1" content_dir="$2" rel src backup
|
|
99
|
+
onboarding_quarantine_is_pending "$root" "$content_dir" || return 0
|
|
100
|
+
backup="$root/$content_dir/_backup"
|
|
101
|
+
while IFS= read -r rel; do
|
|
102
|
+
[ -n "$rel" ] || continue
|
|
103
|
+
src="$root/$rel"
|
|
104
|
+
[ -e "$src" ] || [ -L "$src" ] || continue
|
|
105
|
+
[ -e "$backup/$rel" ] || [ -L "$backup/$rel" ] \
|
|
106
|
+
|| die "initial backup is missing legacy path '$rel'"
|
|
107
|
+
if [ -d "$src" ] && [ ! -L "$src" ]; then
|
|
108
|
+
rm -rf -- "$src"
|
|
109
|
+
else
|
|
110
|
+
rm -f -- "$src"
|
|
111
|
+
fi
|
|
112
|
+
done < <(onboarding_backup_legacy_paths "$root" "$content_dir")
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
restore_onboarding_legacy_sources() {
|
|
116
|
+
local root="$1" content_dir="$2" rel src dest backup
|
|
117
|
+
onboarding_quarantine_is_pending "$root" "$content_dir" || return 0
|
|
118
|
+
backup="$root/$content_dir/_backup"
|
|
119
|
+
while IFS= read -r rel; do
|
|
120
|
+
[ -n "$rel" ] || continue
|
|
121
|
+
src="$backup/$rel"
|
|
122
|
+
[ -e "$src" ] || [ -L "$src" ] || continue
|
|
123
|
+
dest="$root/$rel"
|
|
124
|
+
if [ -d "$dest" ] && [ ! -L "$dest" ]; then
|
|
125
|
+
rm -rf -- "$dest"
|
|
126
|
+
else
|
|
127
|
+
rm -f -- "$dest"
|
|
128
|
+
fi
|
|
129
|
+
mkdir -p "$(dirname "$dest")"
|
|
130
|
+
cp -R "$src" "$dest" || die "failed to restore legacy onboarding path '$rel'"
|
|
131
|
+
done < <(onboarding_backup_legacy_paths "$root" "$content_dir")
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
complete_onboarding_quarantine() {
|
|
135
|
+
local marker="$1/$2/_backup/.quarantine-pending"
|
|
136
|
+
[ ! -f "$marker" ] || rm -f -- "$marker"
|
|
137
|
+
}
|
package/engine/ENGINE_SHA
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
e62f3af481c9be3e9782b2f88115758c5cd21669
|
package/engine/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.11.
|
|
1
|
+
0.11.1
|
|
@@ -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,10 +26,21 @@
|
|
|
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
|
-
# duplicating the awk block here would
|
|
32
|
-
# convention
|
|
42
|
+
# duplicating the awk block here would fork frontmatter parsing, which the
|
|
43
|
+
# convention keeps in one place.
|
|
33
44
|
|
|
34
45
|
sync_opencode_skills() {
|
|
35
46
|
local repo_root="$1"
|
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
|
{
|