@ainova-systems/intelligence 0.11.0-rc.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +46 -0
  3. package/bin/intelligence.js +59 -0
  4. package/cli/commands/add.sh +133 -0
  5. package/cli/commands/doctor.sh +100 -0
  6. package/cli/commands/init.sh +96 -0
  7. package/cli/commands/install.sh +84 -0
  8. package/cli/commands/list.sh +28 -0
  9. package/cli/commands/migrate.sh +251 -0
  10. package/cli/commands/registry.sh +51 -0
  11. package/cli/commands/remove.sh +39 -0
  12. package/cli/commands/status.sh +34 -0
  13. package/cli/commands/sync.sh +22 -0
  14. package/cli/commands/update.sh +59 -0
  15. package/cli/commands/upgrade.sh +27 -0
  16. package/cli/intelligence +71 -0
  17. package/cli/lib/cli-common.sh +149 -0
  18. package/cli/lib/lockfile.sh +97 -0
  19. package/cli/lib/manifest.sh +211 -0
  20. package/cli/lib/registry.sh +141 -0
  21. package/cli/lib/semver.sh +127 -0
  22. package/engine/INIT.md +498 -0
  23. package/engine/agents/intelligence-architect.md +53 -0
  24. package/engine/agents/intelligence-operator.md +49 -0
  25. package/engine/docs/ADAPTERS.md +212 -0
  26. package/engine/docs/CLI.md +91 -0
  27. package/engine/docs/CONVENTIONS.md +440 -0
  28. package/engine/rules/intelligence-authoring.md +114 -0
  29. package/engine/scripts/VERSION +1 -0
  30. package/engine/scripts/adapters/_template.sh +86 -0
  31. package/engine/scripts/adapters/agents.sh +299 -0
  32. package/engine/scripts/adapters/claude.sh +136 -0
  33. package/engine/scripts/adapters/codex.sh +118 -0
  34. package/engine/scripts/adapters/copilot.sh +193 -0
  35. package/engine/scripts/adapters/cursor.sh +146 -0
  36. package/engine/scripts/adapters/opencode.sh +200 -0
  37. package/engine/scripts/adapters/pi.sh +256 -0
  38. package/engine/scripts/lib/common.sh +1602 -0
  39. package/engine/scripts/lib/layout.sh +51 -0
  40. package/engine/scripts/lib/migrations.sh +708 -0
  41. package/engine/scripts/sync.sh +311 -0
  42. package/engine/scripts/update.sh +237 -0
  43. package/engine/skills/intelligence-add-agent/SKILL.md +62 -0
  44. package/engine/skills/intelligence-add-rule/SKILL.md +54 -0
  45. package/engine/skills/intelligence-add-skill/SKILL.md +53 -0
  46. package/engine/skills/intelligence-extract-skill/SKILL.md +47 -0
  47. package/engine/skills/intelligence-install-adapter/SKILL.md +31 -0
  48. package/engine/skills/intelligence-learn-from-context/SKILL.md +69 -0
  49. package/engine/skills/intelligence-review-skills/SKILL.md +86 -0
  50. package/engine/skills/intelligence-sync/SKILL.md +18 -0
  51. package/engine/skills/intelligence-uninstall-adapter/SKILL.md +42 -0
  52. package/engine/skills/intelligence-update/SKILL.md +159 -0
  53. package/package.json +39 -0
  54. package/registry/index.yaml +15 -0
@@ -0,0 +1,86 @@
1
+ #!/bin/bash
2
+ # intelligence-sync: Adapter template
3
+ # Copy this file to create a new IDE adapter.
4
+ #
5
+ # This file is NOT executable as-is — `<name>` placeholders below would be
6
+ # parsed by bash as input redirection (`<` operator). Replace every
7
+ # occurrence with your adapter name before sourcing.
8
+ #
9
+ # Required:
10
+ # 1. Name the file: <ide-name>.sh (e.g., myide.sh)
11
+ # 2. Replace every `<name>` placeholder with your adapter name
12
+ # 3. Implement sync_to_<name>() function
13
+ # 4. Add target to config.yaml:
14
+ # targets:
15
+ # <name>: { enabled: true, output: ".<name>" }
16
+ #
17
+ # The sync_to_<name>() function receives:
18
+ # $1 = repo_root — absolute path to the project root
19
+ # $2 = config_file — absolute path to config.yaml
20
+ # $3 = output_dir — absolute path to the output directory (e.g., .myide/)
21
+ #
22
+ # Available library functions (from lib/common.sh):
23
+ # normalize_file_to_lf(file) — fix CRLF line endings
24
+ # lint_frontmatter(file) — warn about unquoted colons / leading tabs
25
+ # get_frontmatter_value(key, file) — extract YAML frontmatter value
26
+ # has_frontmatter(file) — check if file has --- header
27
+ # has_paths(file) — check if file has paths: field
28
+ # get_model(config, ide, tier) — resolve model from config or default
29
+ # get_model_default(ide, tier) — hardcoded default for ide:tier
30
+ # map_access_to_claude_tools(access) — full->"" (no tools list; inherits all), readonly->restricted
31
+ # map_access_to_claude_disallowed(access) — readonly->"Write, Edit", full->""
32
+ # read_yaml_list(config, section) — read list from config.yaml
33
+ # get_target_field(config, target, field) — read a target's config field
34
+
35
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
36
+
37
+ # Sync rules for <agent-name>
38
+ # Typical transformations:
39
+ # - Copy as-is (like Claude)
40
+ # - Convert paths: to globs: (like Cursor)
41
+ # - Merge into single file (like Copilot)
42
+ sync_<name>_rules() {
43
+ local repo_root="$1"
44
+ local config_file="$2"
45
+ local output_dir="$3"
46
+
47
+ # TODO: Implement rule sync logic
48
+ echo " rules: not implemented"
49
+ }
50
+
51
+ # Sync skills for <agent-name>
52
+ sync_<name>_skills() {
53
+ local repo_root="$1"
54
+ local config_file="$2"
55
+ local output_dir="$3"
56
+
57
+ # TODO: Implement skill sync logic
58
+ echo " skills: not implemented"
59
+ }
60
+
61
+ # Sync agents for <agent-name>
62
+ sync_<name>_agents() {
63
+ local repo_root="$1"
64
+ local config_file="$2"
65
+ local output_dir="$3"
66
+
67
+ # TODO: Implement agent sync logic
68
+ echo " agents: not implemented"
69
+ }
70
+
71
+ # Main entry point
72
+ sync_to_<name>() {
73
+ local repo_root="$1"
74
+ local config_file="$2"
75
+ local output_dir="$3"
76
+
77
+ echo "=== <Agent Name> ==="
78
+
79
+ # Clean and create output directories
80
+ rm -rf "$output_dir/rules" "$output_dir/agents" "$output_dir/skills"
81
+ mkdir -p "$output_dir/rules" "$output_dir/skills" "$output_dir/agents"
82
+
83
+ sync_<name>_rules "$repo_root" "$config_file" "$output_dir"
84
+ sync_<name>_skills "$repo_root" "$config_file" "$output_dir"
85
+ sync_<name>_agents "$repo_root" "$config_file" "$output_dir"
86
+ }
@@ -0,0 +1,299 @@
1
+ #!/bin/bash
2
+ # intelligence-sync: AGENTS.md adapter
3
+ # Generates a committed project-index document that lists all agents, skills,
4
+ # and rules discovered from intelligence/ sources.
5
+ #
6
+ # Output: AGENTS.md at repo root (or wherever targets.agents.output points)
7
+ # Header: static text from config.yaml targets.agents.header (block scalar)
8
+ # Body: auto-built tables/lists from frontmatter of rules/agents/skills
9
+ #
10
+ # Unlike other adapters, the output is a single committed markdown file
11
+ # meant to be read by both humans and LLMs. It must never be hand-edited —
12
+ # every sync regenerates it from scratch.
13
+
14
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
15
+
16
+ # Repo-relative paths to the umbrella folder and the engine module, DERIVED —
17
+ # never hardcoded. The umbrella is named by the project (`intelligence/`,
18
+ # `Intelligence/`, a codename) and the engine lives in a module inside it;
19
+ # detect_layout resolved both before any adapter ran. A literal path here would
20
+ # print a sync command that does not exist, and would be wrong outright on a
21
+ # case-sensitive filesystem — in the one document Cursor, Copilot and Codex all
22
+ # read as canonical.
23
+ # sync.sh exports both as layout tokens; recompute only when an adapter is
24
+ # driven directly (tests), never assume a folder name.
25
+ agents_md_umbrella_rel() {
26
+ local repo_root="$1" rel="${IS_UMBRELLA_REL:-}"
27
+ [ -n "$rel" ] || rel="$(repo_rel_dir "$repo_root" "${LS_UMBRELLA_DIR:-$repo_root/intelligence}")"
28
+ printf '%s' "${rel:-intelligence}"
29
+ }
30
+
31
+ agents_md_module_rel() {
32
+ local repo_root="$1" rel="${IS_MODULE_REL:-}"
33
+ [ -n "$rel" ] || rel="$(repo_rel_dir "$repo_root" "${LS_MODULE_DIR:-$repo_root/intelligence/sync}")"
34
+ printf '%s' "${rel:-intelligence/sync}"
35
+ }
36
+
37
+ # Append the static header block from config.yaml (or a fallback).
38
+ agents_md_append_header() {
39
+ local output="$1"
40
+ local config_file="$2"
41
+
42
+ local header
43
+ header=$(get_target_block "$config_file" "agents" "header")
44
+
45
+ if [ -n "$header" ]; then
46
+ printf '%s\n' "$header" >> "$output"
47
+ else
48
+ local project_name
49
+ project_name=$(get_project_name "$config_file")
50
+ echo "# ${project_name:-Project}" >> "$output"
51
+ fi
52
+ echo "" >> "$output"
53
+ }
54
+
55
+ agents_md_append_agents_table() {
56
+ local repo_root="$1"
57
+ local config_file="$2"
58
+ local output="$3"
59
+
60
+ local rows=""
61
+ local count=0
62
+
63
+ while IFS= read -r src; do
64
+ [ -z "$src" ] && continue
65
+ local dir
66
+ dir="$(resolve_source_dir "$repo_root" "$src")"
67
+ [ -d "$dir" ] || continue
68
+ # Byte-order (LC_ALL=C) sort so generated output is identical across
69
+ # platforms — bash glob order follows LC_COLLATE, which differs between
70
+ # Linux CI (UTF-8, ignores `-`) and Git Bash (C, byte order).
71
+ while IFS= read -r f; do
72
+ [ -f "$f" ] || continue
73
+ local name rel tier access desc
74
+ name="$(basename "$f" .md)"
75
+ rel="$(repo_rel_link "$repo_root" "$f")"
76
+ tier=$(get_frontmatter_value "tier" "$f")
77
+ access=$(get_frontmatter_value "access" "$f")
78
+ desc=$(get_frontmatter_value "description" "$f")
79
+ if [ -n "$rel" ]; then
80
+ rows+="| [$name]($rel) | ${tier:--} | ${access:--} | ${desc:--} |"$'\n'
81
+ else
82
+ rows+="| $name | ${tier:--} | ${access:--} | ${desc:--} |"$'\n'
83
+ fi
84
+ count=$((count + 1))
85
+ done < <(find "$dir" -maxdepth 1 -type f -name '*.md' -print | LC_ALL=C sort)
86
+ done < <(read_yaml_list "$config_file" "agents")
87
+
88
+ [ "$count" -eq 0 ] && return 0
89
+
90
+ {
91
+ echo "### Agents"
92
+ echo ""
93
+ echo "| Agent | Tier | Access | Description |"
94
+ echo "|-------|------|--------|-------------|"
95
+ printf '%s' "$rows"
96
+ echo ""
97
+ } >> "$output"
98
+
99
+ echo " agents: $count listed"
100
+ }
101
+
102
+ agents_md_append_skills_table() {
103
+ local repo_root="$1"
104
+ local config_file="$2"
105
+ local output="$3"
106
+
107
+ local rows=""
108
+ local count=0
109
+
110
+ while IFS= read -r src; do
111
+ [ -z "$src" ] && continue
112
+ local dir
113
+ dir="$(resolve_source_dir "$repo_root" "$src")"
114
+ [ -d "$dir" ] || continue
115
+ # Byte-order (LC_ALL=C) sort so generated output is identical across
116
+ # platforms — see the note in agents_md_append_agents_table.
117
+ while IFS= read -r skill_dir; do
118
+ [ -d "$skill_dir" ] || continue
119
+ local dirname
120
+ dirname="$(basename "$skill_dir")"
121
+ case "$dirname" in _*) continue ;; esac
122
+ local skill_file="${skill_dir%/}/SKILL.md"
123
+ [ -f "$skill_file" ] || continue
124
+ local rel desc
125
+ rel="$(repo_rel_link "$repo_root" "$skill_file")"
126
+ desc=$(get_frontmatter_value "description" "$skill_file")
127
+ if [ -n "$rel" ]; then
128
+ rows+="| [$dirname]($rel) | ${desc:--} |"$'\n'
129
+ else
130
+ rows+="| $dirname | ${desc:--} |"$'\n'
131
+ fi
132
+ count=$((count + 1))
133
+ done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d -print | LC_ALL=C sort)
134
+ done < <(read_yaml_list "$config_file" "skills")
135
+
136
+ [ "$count" -eq 0 ] && return 0
137
+
138
+ {
139
+ echo "### Skills"
140
+ echo ""
141
+ echo "| Skill | Description |"
142
+ echo "|-------|-------------|"
143
+ printf '%s' "$rows"
144
+ echo ""
145
+ } >> "$output"
146
+
147
+ echo " skills: $count listed"
148
+ }
149
+
150
+ agents_md_append_rules_list() {
151
+ local repo_root="$1"
152
+ local config_file="$2"
153
+ local output="$3"
154
+
155
+ local lines=""
156
+ local count=0
157
+ local global_rule_files=()
158
+
159
+ while IFS= read -r src; do
160
+ [ -z "$src" ] && continue
161
+ local dir
162
+ dir="$(resolve_source_dir "$repo_root" "$src")"
163
+ [ -d "$dir" ] || continue
164
+ # Byte-order (LC_ALL=C) sort so generated output — and the inline order
165
+ # of always-on rules below — is identical across platforms. See the
166
+ # note in agents_md_append_agents_table.
167
+ while IFS= read -r f; do
168
+ [ -f "$f" ] || continue
169
+ local name rel scope
170
+ name="$(basename "$f" .md)"
171
+ rel="$(repo_rel_link "$repo_root" "$f")"
172
+ scope="global"
173
+ if [ "$(has_paths "$f")" != "0" ]; then
174
+ scope="scoped"
175
+ else
176
+ global_rule_files+=("$f")
177
+ fi
178
+ if [ -n "$rel" ]; then
179
+ lines+="- [$name]($rel) ($scope)"$'\n'
180
+ else
181
+ lines+="- $name ($scope)"$'\n'
182
+ fi
183
+ count=$((count + 1))
184
+ done < <(find "$dir" -maxdepth 1 -type f -name '*.md' -print | LC_ALL=C sort)
185
+ done < <(read_yaml_list "$config_file" "rules")
186
+
187
+ [ "$count" -eq 0 ] && return 0
188
+
189
+ {
190
+ echo "### Rules"
191
+ echo ""
192
+ printf '%s' "$lines"
193
+ echo ""
194
+ } >> "$output"
195
+
196
+ echo " rules: $count listed"
197
+
198
+ # Always-on rules (no `paths:`) are inlined into AGENTS.md as canonical
199
+ # project context. Codex (only reads AGENTS.md) and Cursor/Copilot
200
+ # (read AGENTS.md natively) all pick up rule content from here.
201
+ # Path-scoped rules stay in tool-specific channels (.cursor/rules/,
202
+ # .github/instructions/) so monorepo scoping is preserved.
203
+ if [ "${#global_rule_files[@]}" -gt 0 ]; then
204
+ local umbrella_rel
205
+ umbrella_rel="$(agents_md_umbrella_rel "$repo_root")"
206
+ {
207
+ echo "---"
208
+ echo ""
209
+ echo "## Project Context"
210
+ echo ""
211
+ echo "<!-- Inlined from always-on rules in $umbrella_rel/rules/ -->"
212
+ echo ""
213
+ } >> "$output"
214
+ local rf
215
+ for rf in "${global_rule_files[@]}"; do
216
+ awk '
217
+ BEGIN { in_fm=0; past_fm=0 }
218
+ { sub(/\r$/, "") }
219
+ /^---$/ {
220
+ if (!past_fm) { in_fm = !in_fm; if (!in_fm) { past_fm=1 }; next }
221
+ }
222
+ past_fm || !in_fm { print }
223
+ ' "$rf" >> "$output"
224
+ echo "" >> "$output"
225
+ done
226
+ echo " rules: ${#global_rule_files[@]} global rule(s) inlined"
227
+ fi
228
+ }
229
+
230
+ # Main entry point for AGENTS.md adapter
231
+ sync_to_agents() {
232
+ local repo_root="$1"
233
+ local config_file="$2"
234
+ local output_dir="$3"
235
+
236
+ echo "=== AGENTS.md ==="
237
+
238
+ # output_dir points at the target file path (e.g., /repo/AGENTS.md).
239
+ # If it looks like a directory (trailing slash, existing dir, or no .md
240
+ # extension), append default filename.
241
+ local output_file="$output_dir"
242
+ if [ -d "$output_file" ] || [[ "$output_file" == */ ]] || [[ "$output_file" != *.md ]]; then
243
+ output_file="${output_file%/}/AGENTS.md"
244
+ fi
245
+
246
+ mkdir -p "$(dirname "$output_file")"
247
+
248
+ local umbrella_rel module_rel
249
+ umbrella_rel="$(agents_md_umbrella_rel "$repo_root")"
250
+ module_rel="$(agents_md_module_rel "$repo_root")"
251
+
252
+ # These two strings are written into a COMMITTED file. An absolute path here
253
+ # means the derivation failed, and a machine-specific path in AGENTS.md is
254
+ # worse than a failed sync — fail loudly instead.
255
+ case "$umbrella_rel$module_rel" in
256
+ /*|*:[\\/]*)
257
+ echo " ERROR: AGENTS.md paths did not resolve relative to the repo root:" >&2
258
+ echo " umbrella='$umbrella_rel' module='$module_rel' repo_root='$repo_root'" >&2
259
+ return 1
260
+ ;;
261
+ esac
262
+
263
+ # How a reader re-runs the sync: the CLI names its own command (IS_SYNC_CMD),
264
+ # a vendored setup gets the script invocation.
265
+ local sync_cmd="${IS_SYNC_CMD:-bash $module_rel/scripts/sync.sh}"
266
+
267
+ {
268
+ echo "<!-- Generated by intelligence-sync. Do not edit manually. -->"
269
+ echo "<!-- Source: $umbrella_rel/ | Sync: $sync_cmd -->"
270
+ echo ""
271
+ } > "$output_file"
272
+
273
+ agents_md_append_header "$output_file" "$config_file"
274
+
275
+ {
276
+ echo "## Intelligence"
277
+ echo ""
278
+ echo "Source of truth: \`$umbrella_rel/\` | Sync: \`$sync_cmd\`"
279
+ echo ""
280
+ } >> "$output_file"
281
+
282
+ agents_md_append_agents_table "$repo_root" "$config_file" "$output_file"
283
+ agents_md_append_skills_table "$repo_root" "$config_file" "$output_file"
284
+ agents_md_append_rules_list "$repo_root" "$config_file" "$output_file"
285
+
286
+ finalize_output_file "$output_file"
287
+
288
+ # Safety net: AGENTS.md is committed, so it must never carry an absolute or
289
+ # transient link target (remote-pack content lives under the run cache). If
290
+ # relativization ever fails, fail the sync loudly rather than write a
291
+ # machine-specific path into version control.
292
+ if grep -nE '\]\((/|[A-Za-z]:[\\/])' "$output_file" >/dev/null 2>&1; then
293
+ echo " ERROR: AGENTS.md has an absolute link target — relativization failed:" >&2
294
+ grep -nE '\]\((/|[A-Za-z]:[\\/])' "$output_file" >&2
295
+ return 1
296
+ fi
297
+
298
+ echo " -> ${output_file#$repo_root/}"
299
+ }
@@ -0,0 +1,136 @@
1
+ #!/bin/bash
2
+ # intelligence-sync: Claude Code adapter
3
+ # Transforms source prompts to .claude/ format
4
+ #
5
+ # Rules: copy as-is (paths: frontmatter preserved)
6
+ # Skills: copy skill directories in full (SKILL.md + bundled resources)
7
+ # Agents: tier -> model, access -> tools/disallowedTools
8
+
9
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
10
+
11
+ # Sync rules to Claude format (copy as-is, normalize LF)
12
+ sync_claude_rules() {
13
+ local repo_root="$1"
14
+ local config_file="$2"
15
+ local output_dir="$3"
16
+
17
+ while IFS= read -r src; do
18
+ [ -z "$src" ] && continue
19
+ local dir
20
+ dir="$(resolve_source_dir "$repo_root" "$src")"
21
+ [ -d "$dir" ] || continue
22
+ for f in "$dir"/*.md; do
23
+ [ -f "$f" ] || continue
24
+ cp "$f" "$output_dir/rules/"
25
+ finalize_output_file "$output_dir/rules/$(basename "$f")"
26
+ echo " rule: $(basename "$f")"
27
+ done
28
+ done < <(read_yaml_list "$config_file" "rules")
29
+ }
30
+
31
+ # Sync skills to Claude format (copy skill directories in full)
32
+ sync_claude_skills() {
33
+ local repo_root="$1"
34
+ local config_file="$2"
35
+ local output_dir="$3"
36
+
37
+ while IFS= read -r src; do
38
+ [ -z "$src" ] && continue
39
+ local dir
40
+ dir="$(resolve_source_dir "$repo_root" "$src")"
41
+ [ -d "$dir" ] || continue
42
+ for d in "$dir"/*/; do
43
+ [ -d "$d" ] || continue
44
+ local skill_name
45
+ skill_name="$(basename "$d")"
46
+ [ -f "$d/SKILL.md" ] || continue
47
+ copy_skill_bundle "$d" "$output_dir/skills/$skill_name"
48
+ echo " skill: $skill_name"
49
+ done
50
+ done < <(read_yaml_list "$config_file" "skills")
51
+ }
52
+
53
+ # Sync a single agent file to Claude format
54
+ sync_claude_agent() {
55
+ local src="$1"
56
+ local config_file="$2"
57
+ local output_dir="$3"
58
+ local name
59
+ name="$(basename "$src")"
60
+
61
+ local tier access
62
+ tier=$(get_frontmatter_value "tier" "$src")
63
+ access=$(get_frontmatter_value "access" "$src")
64
+
65
+ local model tools extra
66
+ model=$(get_model "$config_file" "claude" "$tier")
67
+ tools=$(map_access_to_claude_tools "$access")
68
+ extra=$(map_access_to_claude_disallowed "$access")
69
+
70
+ # Transform: remove tier/access, inject tools/model/disallowedTools before closing ---
71
+ awk -v model="$model" -v tools="$tools" -v extra="$extra" '
72
+ BEGIN { count=0 }
73
+ /^tier:/ { next }
74
+ /^access:/ { next }
75
+ {
76
+ sub(/\r$/, "")
77
+ }
78
+ /^---$/ { count++ }
79
+ count==2 && /^---$/ {
80
+ if (tools != "") print "tools: " tools
81
+ if (extra != "") print "disallowedTools: " extra
82
+ print "model: " model
83
+ }
84
+ { print }
85
+ ' "$src" > "$output_dir/agents/$name"
86
+ finalize_output_file "$output_dir/agents/$name"
87
+ echo " agent: $name (tier=$tier -> model=$model, access=$access)"
88
+ }
89
+
90
+ # Sync all agents to Claude format
91
+ sync_claude_agents() {
92
+ local repo_root="$1"
93
+ local config_file="$2"
94
+ local output_dir="$3"
95
+
96
+ while IFS= read -r src; do
97
+ [ -z "$src" ] && continue
98
+ local dir
99
+ dir="$(resolve_source_dir "$repo_root" "$src")"
100
+ [ -d "$dir" ] || continue
101
+ for f in "$dir"/*.md; do
102
+ [ -f "$f" ] || continue
103
+ sync_claude_agent "$f" "$config_file" "$output_dir"
104
+ done
105
+ done < <(read_yaml_list "$config_file" "agents")
106
+ }
107
+
108
+ # Main entry point for Claude adapter
109
+ sync_to_claude() {
110
+ local repo_root="$1"
111
+ local config_file="$2"
112
+ local output_dir="$3"
113
+
114
+ echo "=== Claude Code ==="
115
+
116
+ # Clean generated content only — preserve any project-level files
117
+ # the user manages directly (settings.json, settings.local.json,
118
+ # commands/, statusline.sh, etc.).
119
+ rm -rf "$output_dir/rules" "$output_dir/agents"
120
+ if [ -d "$output_dir/skills" ]; then
121
+ find "$output_dir/skills" -mindepth 1 -maxdepth 1 -type d | while read -r d; do
122
+ rm -rf "$d"
123
+ done
124
+ fi
125
+ mkdir -p "$output_dir/rules" "$output_dir/skills" "$output_dir/agents"
126
+
127
+ sync_claude_rules "$repo_root" "$config_file" "$output_dir"
128
+ sync_claude_skills "$repo_root" "$config_file" "$output_dir"
129
+ sync_claude_agents "$repo_root" "$config_file" "$output_dir"
130
+
131
+ local rules_count skills_count agents_count
132
+ rules_count=$(find "$output_dir/rules" -name "*.md" 2>/dev/null | wc -l)
133
+ skills_count=$(find "$output_dir/skills" -name "SKILL.md" 2>/dev/null | wc -l)
134
+ agents_count=$(find "$output_dir/agents" -name "*.md" 2>/dev/null | wc -l)
135
+ echo " -> Rules: $rules_count, Skills: $skills_count, Agents: $agents_count"
136
+ }
@@ -0,0 +1,118 @@
1
+ #!/bin/bash
2
+ # intelligence-sync: OpenAI Codex CLI adapter
3
+ # Transforms source prompts to Codex format.
4
+ #
5
+ # Codex reads AGENTS.md natively for project context — the `agents` adapter
6
+ # inlines always-on rules into AGENTS.md, so this adapter only emits skills
7
+ # and subagent definitions.
8
+ #
9
+ # Skills: copy skill directories in full to .agents/skills/{name}/ (Codex
10
+ # reads from $REPO_ROOT/.agents/skills exclusively per official docs)
11
+ # Agents: -> .codex/agents/{name}.toml (name, description, model,
12
+ # model_reasoning_effort, sandbox_mode, developer_instructions)
13
+
14
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
15
+
16
+ # Sync skills to Codex format (.agents/skills/{name}/SKILL.md)
17
+ sync_codex_skills() {
18
+ local repo_root="$1"
19
+ local config_file="$2"
20
+ local output_dir="$3"
21
+
22
+ sync_open_skill_dirs "$repo_root" "$config_file" "$output_dir"
23
+ }
24
+
25
+ # Sync agents to Codex format (.codex/agents/{name}.toml)
26
+ sync_codex_agents() {
27
+ local repo_root="$1"
28
+ local config_file="$2"
29
+ local output_dir="$3"
30
+
31
+ local count=0
32
+ while IFS= read -r src; do
33
+ [ -z "$src" ] && continue
34
+ local dir
35
+ dir="$(resolve_source_dir "$repo_root" "$src")"
36
+ [ -d "$dir" ] || continue
37
+ for f in "$dir"/*.md; do
38
+ [ -f "$f" ] || continue
39
+ local name
40
+ name="$(basename "$f" .md)"
41
+
42
+ local tier access description
43
+ tier=$(get_frontmatter_value "tier" "$f")
44
+ access=$(get_frontmatter_value "access" "$f")
45
+ description=$(get_frontmatter_value "description" "$f")
46
+
47
+ local model effort sandbox
48
+ model=$(get_model "$config_file" "codex" "$tier")
49
+ case "$tier" in
50
+ heavy) effort="high" ;;
51
+ standard) effort="medium" ;;
52
+ light) effort="low" ;;
53
+ *) effort="medium" ;;
54
+ esac
55
+ case "$access" in
56
+ readonly) sandbox="read-only" ;;
57
+ *) sandbox="workspace-write" ;;
58
+ esac
59
+
60
+ local body
61
+ body=$(awk '
62
+ BEGIN { in_fm=0; past_fm=0 }
63
+ { sub(/\r$/, "") }
64
+ /^---$/ {
65
+ if (!past_fm) { in_fm = !in_fm; if (!in_fm) { past_fm=1 }; next }
66
+ }
67
+ past_fm { print }
68
+ ' "$f")
69
+
70
+ # Escape backslashes used in Markdown paths, then neutralize any
71
+ # closing TOML triple-quote sequence in the agent body.
72
+ local body_safe
73
+ body_safe="${body//\\/\\\\}"
74
+ body_safe="${body_safe//\"\"\"/\"\"\\\"}"
75
+
76
+ local name_escaped description_escaped
77
+ name_escaped=$(toml_escape "$name")
78
+ description_escaped=$(toml_escape "$description")
79
+
80
+ {
81
+ echo "name = \"$name_escaped\""
82
+ echo "description = \"$description_escaped\""
83
+ echo "model = \"$model\""
84
+ echo "model_reasoning_effort = \"$effort\""
85
+ echo "sandbox_mode = \"$sandbox\""
86
+ echo ""
87
+ echo "developer_instructions = \"\"\""
88
+ echo "$body_safe"
89
+ echo "\"\"\""
90
+ } > "$output_dir/$name.toml"
91
+ finalize_output_file "$output_dir/$name.toml"
92
+ count=$((count + 1))
93
+ echo " agent: $name.toml"
94
+ done
95
+ done < <(read_yaml_list "$config_file" "agents")
96
+
97
+ echo " -> Agents: $count"
98
+ }
99
+
100
+ # Main entry point for Codex adapter
101
+ sync_to_codex() {
102
+ local repo_root="$1"
103
+ local config_file="$2"
104
+ local output_dir="$3"
105
+
106
+ echo "=== OpenAI Codex ==="
107
+
108
+ # Skills -> .agents/skills/ (Codex reads from this fixed path).
109
+ # `sync_open_skill_dirs` owns clean + populate of this shared dir.
110
+ local skills_dir="$repo_root/.agents/skills"
111
+ sync_codex_skills "$repo_root" "$config_file" "$skills_dir"
112
+
113
+ # Agents -> .codex/agents/
114
+ local agents_dir="$repo_root/.codex/agents"
115
+ rm -rf "$agents_dir"
116
+ mkdir -p "$agents_dir"
117
+ sync_codex_agents "$repo_root" "$config_file" "$agents_dir"
118
+ }