@ainova-systems/intelligence 0.11.0-rc.8 → 0.11.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 +4 -0
- package/cli/commands/adapter.sh +46 -6
- package/cli/commands/init.sh +138 -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} +33 -7
- package/cli/internal/check.sh +49 -20
- package/cli/internal/{migrate-v1.sh → convert-legacy.sh} +36 -34
- package/cli/internal/package-add.sh +9 -14
- 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 +31 -25
- package/cli/internal/restore.sh +5 -16
- package/cli/internal/target-state.sh +26 -18
- package/cli/lib/adapter-lifecycle.sh +33 -0
- package/cli/lib/cli-common.sh +28 -20
- package/cli/lib/gitignore.sh +174 -0
- package/cli/lib/manifest.sh +58 -1
- 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/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 +57 -22
- package/packages/sync/references/onboarding-migration.md +79 -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 +90 -38
- package/packages/sync/skills/intelligence-review-skills/SKILL.md +1 -1
- package/packages/sync/skills/intelligence-sync/SKILL.md +1 -1
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
|
}
|
|
@@ -139,14 +143,12 @@ bundled_engine_version() {
|
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
# --- The sync package's manifest/lock plumbing ---------------------------
|
|
142
|
-
# sync_pkg_entry <manifest> — write/refresh the
|
|
143
|
-
#
|
|
144
|
-
#
|
|
146
|
+
# sync_pkg_entry <manifest> — write/refresh only the requested exact pin.
|
|
147
|
+
# The built-in source lives in engine-package.yaml and resolved source state
|
|
148
|
+
# belongs exclusively to intelligence.lock.
|
|
145
149
|
sync_pkg_entry() {
|
|
146
150
|
local manifest="$1"
|
|
147
151
|
qmap_set "$manifest" "packages" "$SYNC_PKG_NAME" "version" "$(bundled_engine_version)"
|
|
148
|
-
qmap_set "$manifest" "packages" "$SYNC_PKG_NAME" "url" "$SYNC_PKG_URL"
|
|
149
|
-
qmap_set "$manifest" "packages" "$SYNC_PKG_NAME" "path" "$SYNC_PKG_PATH"
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
# sync_pkg_install <root> — materialize the package into the store and lock
|
|
@@ -178,7 +180,7 @@ export_engine_env() {
|
|
|
178
180
|
|
|
179
181
|
# --- Project lifecycle preflight -----------------------------------------
|
|
180
182
|
# Public commands are intentionally few. They share this state gate so a CLI
|
|
181
|
-
# installed at a newer engine version brings the current
|
|
183
|
+
# installed at a newer engine version brings the current Intelligence project forward
|
|
182
184
|
# before a mutating operation. The npm install itself cannot do that: it runs
|
|
183
185
|
# outside any project and does not know which repositories the user owns.
|
|
184
186
|
|
|
@@ -227,13 +229,19 @@ project_needs_upgrade() {
|
|
|
227
229
|
[ -d "$root/.intelligence/engine" ] && return 0
|
|
228
230
|
|
|
229
231
|
while IFS= read -r name; do
|
|
230
|
-
[ "$name"
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
[ "$
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
232
|
+
[ -n "$name" ] || continue
|
|
233
|
+
# Early RC manifests mixed requested intent with resolved source
|
|
234
|
+
# details. Source URL/path now live only in the required lockfile.
|
|
235
|
+
[ -n "$(qmap_field "$manifest" "packages" "$name" "url")" ] && return 0
|
|
236
|
+
[ -n "$(qmap_field "$manifest" "packages" "$name" "path")" ] && return 0
|
|
237
|
+
if [ "$name" = "$SYNC_PKG_NAME" ]; then
|
|
238
|
+
pinned="$(qmap_field "$manifest" "packages" "$name" "version")"
|
|
239
|
+
locked="$(qmap_field "$root/intelligence.lock" "packages" "$name" "resolved")"
|
|
240
|
+
[ "$pinned" = "$eng" ] || return 0
|
|
241
|
+
if [ -f "$root/intelligence.lock" ]; then
|
|
242
|
+
[ -n "$locked" ] || return 0
|
|
243
|
+
[ "${locked#v}" = "$eng" ] || return 0
|
|
244
|
+
fi
|
|
237
245
|
fi
|
|
238
246
|
done < <(qmap_keys "$manifest" "packages")
|
|
239
247
|
return 1
|
|
@@ -264,7 +272,7 @@ ensure_project_current() {
|
|
|
264
272
|
die "project lifecycle requires alignment (stamp ${stamp:-unstamped}, engine $eng) — run 'intelligence init --apply' locally, review and commit the diff"
|
|
265
273
|
fi
|
|
266
274
|
echo " project alignment: stamp ${stamp:-unstamped}, engine $eng"
|
|
267
|
-
bash "$CLI_DIR/internal/
|
|
275
|
+
bash "$CLI_DIR/internal/align-project.sh" --no-sync
|
|
268
276
|
}
|
|
269
277
|
|
|
270
278
|
project_store_missing() {
|
|
@@ -0,0 +1,174 @@
|
|
|
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
|
+
ensure_gitignore_header() {
|
|
13
|
+
local root="$1" file="$1/.gitignore"
|
|
14
|
+
if [ ! -f "$file" ] || ! grep -Fqx '# Intelligence generated state and tool output' "$file"; then
|
|
15
|
+
if [ -f "$file" ] && [ -s "$file" ] && [ -n "$(tail -c 1 "$file" 2>/dev/null)" ]; then
|
|
16
|
+
printf '\n' >> "$file"
|
|
17
|
+
fi
|
|
18
|
+
printf '%s\n' '# Intelligence generated state and tool output' >> "$file"
|
|
19
|
+
fi
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
ensure_base_gitignore() {
|
|
23
|
+
local root="$1"
|
|
24
|
+
ensure_gitignore_header "$root"
|
|
25
|
+
gitignore_add_line "$root" '.intelligence/'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
ensure_target_gitignore() {
|
|
29
|
+
local root="$1" manifest="$2" target="$3" content_dir output kind value records
|
|
30
|
+
ensure_gitignore_header "$root"
|
|
31
|
+
content_dir="$(manifest_intelligence_dir "$manifest")"
|
|
32
|
+
output="$(get_target_output "$manifest" "$target")"
|
|
33
|
+
[ -n "$output" ] || output="$(default_target_output "$target")"
|
|
34
|
+
records="$(adapter_records_for "$root" "$content_dir" "$target" "$output")" \
|
|
35
|
+
|| die "adapter '$target' has an invalid ownership contract"
|
|
36
|
+
while IFS=$'\t' read -r kind value; do
|
|
37
|
+
case "$kind" in
|
|
38
|
+
ignore) gitignore_add_line "$root" "$value" ;;
|
|
39
|
+
include) gitignore_add_line "$root" "!$value" ;;
|
|
40
|
+
esac
|
|
41
|
+
done <<< "$records"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
ensure_manifest_gitignore() {
|
|
45
|
+
local root="$1" manifest="$2" target
|
|
46
|
+
ensure_base_gitignore "$root"
|
|
47
|
+
while IFS= read -r target; do
|
|
48
|
+
[ -n "$target" ] || continue
|
|
49
|
+
if [ "$(is_target_enabled "$manifest" "$target")" = "1" ]; then
|
|
50
|
+
ensure_target_gitignore "$root" "$manifest" "$target"
|
|
51
|
+
fi
|
|
52
|
+
done < <(target_names "$manifest")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Publishing/build tools do not share Git's ignore policy. In particular,
|
|
56
|
+
# .vscodeignore and .npmignore become the packager's own filter, while Docker
|
|
57
|
+
# always builds from its independent context filter. Only amend files the
|
|
58
|
+
# project already has: their presence is the explicit signal that this root is
|
|
59
|
+
# packaged or sent as a build context.
|
|
60
|
+
publisher_ignore_file_names() {
|
|
61
|
+
printf '%s\n' .vscodeignore .npmignore .dockerignore
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
publisher_ignore_add_line() {
|
|
65
|
+
local file="$1" line="$2"
|
|
66
|
+
if grep -Fqx -- "$line" "$file" 2>/dev/null; then
|
|
67
|
+
return 0
|
|
68
|
+
fi
|
|
69
|
+
printf '%s\n' "$line" >> "$file"
|
|
70
|
+
PUBLISH_IGNORE_FILE_CHANGED=1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
publisher_ignore_add_path() {
|
|
74
|
+
local file="$1" path="${2#./}"
|
|
75
|
+
path="${path%/}"
|
|
76
|
+
[ -n "$path" ] || return 0
|
|
77
|
+
publisher_ignore_add_line "$file" "$path"
|
|
78
|
+
publisher_ignore_add_line "$file" "$path/**"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ensure_manifest_publisher_ignores() {
|
|
82
|
+
local root="$1" manifest="$2" rel file content_dir target output records kind value
|
|
83
|
+
content_dir="$(manifest_intelligence_dir "$manifest")"
|
|
84
|
+
while IFS= read -r rel; do
|
|
85
|
+
[ -n "$rel" ] || continue
|
|
86
|
+
file="$root/$rel"
|
|
87
|
+
[ -f "$file" ] || continue
|
|
88
|
+
PUBLISH_IGNORE_FILE_CHANGED=0
|
|
89
|
+
if ! grep -Fqx '# Intelligence development context and generated output' "$file" 2>/dev/null; then
|
|
90
|
+
if [ -s "$file" ] && [ -n "$(tail -c 1 "$file" 2>/dev/null)" ]; then
|
|
91
|
+
printf '\n' >> "$file"
|
|
92
|
+
fi
|
|
93
|
+
printf '%s\n' '# Intelligence development context and generated output' >> "$file"
|
|
94
|
+
PUBLISH_IGNORE_FILE_CHANGED=1
|
|
95
|
+
fi
|
|
96
|
+
publisher_ignore_add_path "$file" '.intelligence'
|
|
97
|
+
publisher_ignore_add_line "$file" 'intelligence.yaml'
|
|
98
|
+
publisher_ignore_add_line "$file" 'intelligence.lock'
|
|
99
|
+
publisher_ignore_add_path "$file" "$content_dir"
|
|
100
|
+
while IFS= read -r target; do
|
|
101
|
+
[ -n "$target" ] || continue
|
|
102
|
+
[ "$(is_target_enabled "$manifest" "$target")" = "1" ] || continue
|
|
103
|
+
output="$(get_target_output "$manifest" "$target")"
|
|
104
|
+
[ -n "$output" ] || output="$(default_target_output "$target")"
|
|
105
|
+
publisher_ignore_add_path "$file" "$output"
|
|
106
|
+
records="$(adapter_records_for "$root" "$content_dir" "$target" "$output")" \
|
|
107
|
+
|| die "adapter '$target' has an invalid ownership contract"
|
|
108
|
+
while IFS=$'\t' read -r kind value; do
|
|
109
|
+
case "$kind" in
|
|
110
|
+
owned|managed|legacy) publisher_ignore_add_path "$file" "$value" ;;
|
|
111
|
+
esac
|
|
112
|
+
done <<< "$records"
|
|
113
|
+
done < <(target_names "$manifest")
|
|
114
|
+
if [ "$PUBLISH_IGNORE_FILE_CHANGED" -eq 1 ]; then
|
|
115
|
+
echo "packaging exclusions updated: $rel"
|
|
116
|
+
fi
|
|
117
|
+
done < <(publisher_ignore_file_names)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
managed_gitignore_patterns() {
|
|
121
|
+
local root="$1" manifest="$2" content_dir target output records kind value
|
|
122
|
+
content_dir="$(manifest_intelligence_dir "$manifest")"
|
|
123
|
+
printf '%s\n' '.intelligence/' "$content_dir/_backup/"
|
|
124
|
+
while IFS= read -r target; do
|
|
125
|
+
[ -n "$target" ] || continue
|
|
126
|
+
[ "$(is_target_enabled "$manifest" "$target")" = "1" ] || continue
|
|
127
|
+
output="$(get_target_output "$manifest" "$target")"
|
|
128
|
+
[ -n "$output" ] || output="$(default_target_output "$target")"
|
|
129
|
+
records="$(adapter_records_for "$root" "$content_dir" "$target" "$output")" \
|
|
130
|
+
|| return 1
|
|
131
|
+
while IFS=$'\t' read -r kind value; do
|
|
132
|
+
case "$kind" in
|
|
133
|
+
ignore) printf '%s\n' "$value" ;;
|
|
134
|
+
include) printf '!%s\n' "$value" ;;
|
|
135
|
+
esac
|
|
136
|
+
done <<< "$records"
|
|
137
|
+
done < <(target_names "$manifest")
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Git does not apply ignore rules retroactively to files already in its index.
|
|
141
|
+
# Name each affected path and print a command which only untracks it; the local
|
|
142
|
+
# file remains available as migration input.
|
|
143
|
+
report_tracked_managed_ignores() {
|
|
144
|
+
local root="$1" manifest="$2" patterns tracked path quoted posix_quoted powershell_quoted
|
|
145
|
+
git -C "$root" rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 0
|
|
146
|
+
patterns="$(mktemp -t intelligence-gitignore-XXXXXX)"
|
|
147
|
+
tracked="$(mktemp -t intelligence-tracked-XXXXXX)"
|
|
148
|
+
managed_gitignore_patterns "$root" "$manifest" > "$patterns" || {
|
|
149
|
+
rm -f "$patterns" "$tracked"
|
|
150
|
+
return 0
|
|
151
|
+
}
|
|
152
|
+
git -C "$root" ls-files -z -ci --exclude-from="$patterns" > "$tracked" 2>/dev/null || true
|
|
153
|
+
rm -f "$patterns"
|
|
154
|
+
[ -s "$tracked" ] || {
|
|
155
|
+
rm -f "$tracked"
|
|
156
|
+
return 0
|
|
157
|
+
}
|
|
158
|
+
echo " Tracked files still bypass these .gitignore rules. Untrack them without deleting local copies:"
|
|
159
|
+
while IFS= read -r -d '' path; do
|
|
160
|
+
[ -n "$path" ] || continue
|
|
161
|
+
if [[ "$path" == *"'"* ]]; then
|
|
162
|
+
# macOS still ships Bash 3.2; use portable sed rather than newer
|
|
163
|
+
# parameter-replacement behavior for the embedded quote.
|
|
164
|
+
posix_quoted="$(printf '%s' "$path" | sed "s/'/'\\\\''/g")"
|
|
165
|
+
powershell_quoted="$(printf '%s' "$path" | sed "s/'/''/g")"
|
|
166
|
+
printf " POSIX: git rm --cached -- '%s'\n" "$posix_quoted"
|
|
167
|
+
printf " PowerShell: git rm --cached -- '%s'\n" "$powershell_quoted"
|
|
168
|
+
else
|
|
169
|
+
quoted="'$path'"
|
|
170
|
+
printf ' git rm --cached -- %s\n' "$quoted"
|
|
171
|
+
fi
|
|
172
|
+
done < "$tracked"
|
|
173
|
+
rm -f "$tracked"
|
|
174
|
+
}
|
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
|
|
@@ -301,6 +335,29 @@ qmap_delete_key() {
|
|
|
301
335
|
'
|
|
302
336
|
}
|
|
303
337
|
|
|
338
|
+
# qmap_delete_field <file> <block> <key> <field> — drop one field from a
|
|
339
|
+
# quoted-key entry without disturbing its other fields or surrounding YAML.
|
|
340
|
+
qmap_delete_field() {
|
|
341
|
+
local file="$1" block="$2" key="$3" field="$4"
|
|
342
|
+
[ -f "$file" ] || return 0
|
|
343
|
+
_qmap_stage "$file" -v block="$block" -v key="$key" -v field="$field" '
|
|
344
|
+
{ sub(/\r$/, "") }
|
|
345
|
+
$0 ~ "^" block ":[ \t]*$" { inb = 1; print; next }
|
|
346
|
+
inb && /^[^ #]/ { inb = 0; ink = 0 }
|
|
347
|
+
inb && /^ "/ {
|
|
348
|
+
s = substr($0, 4); q = index(s, "\"")
|
|
349
|
+
ink = (q > 0 && substr(s, 1, q - 1) == key)
|
|
350
|
+
print; next
|
|
351
|
+
}
|
|
352
|
+
inb && ink && /^ [A-Za-z_]/ {
|
|
353
|
+
line = $0; sub(/^ /, "", line)
|
|
354
|
+
c = index(line, ":")
|
|
355
|
+
if (c > 0 && substr(line, 1, c - 1) == field) next
|
|
356
|
+
}
|
|
357
|
+
{ print }
|
|
358
|
+
'
|
|
359
|
+
}
|
|
360
|
+
|
|
304
361
|
# --- registries: a trust LIST, not a scope map -----------------------------
|
|
305
362
|
# The block holds registry repo URLs in trust order. Two shapes are read so
|
|
306
363
|
# early manifests keep working: the list form (`- "url"`) and the retired
|
|
@@ -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
|
+
88a9c74ddb8bee338671c7badbebd07669b668c0
|
|
@@ -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"
|