@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,193 @@
1
+ #!/bin/bash
2
+ # intelligence-sync: GitHub Copilot adapter
3
+ # Transforms source prompts to .github/ format
4
+ #
5
+ # Rules:
6
+ # - Path-scoped (with `paths:`) -> .github/instructions/{name}.instructions.md (applyTo:)
7
+ # - Always-on (no `paths:`) -> SKIPPED here. Copilot reads AGENTS.md
8
+ # natively, and the `agents` adapter inlines always-on rule content
9
+ # into AGENTS.md. We also do not generate .github/copilot-instructions.md
10
+ # because Copilot bug copilot-cli#489 makes AGENTS.md ignored when
11
+ # copilot-instructions.md is present.
12
+ # Skills: copy skill directories in full to .github/skills/{name}/
13
+ # Agents: -> .github/agents/{name}.agent.md (description, tools, model)
14
+
15
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
16
+
17
+ # Sync rules to Copilot format. Only path-scoped rules are emitted —
18
+ # always-on rules live in AGENTS.md (read by Copilot natively).
19
+ sync_copilot_rules() {
20
+ local repo_root="$1"
21
+ local config_file="$2"
22
+ local output_dir="$3"
23
+
24
+ local instructions_dir="$output_dir/instructions"
25
+ mkdir -p "$instructions_dir"
26
+
27
+ local scoped_count=0
28
+
29
+ while IFS= read -r src; do
30
+ [ -z "$src" ] && continue
31
+ local dir
32
+ dir="$(resolve_source_dir "$repo_root" "$src")"
33
+ [ -d "$dir" ] || continue
34
+ for f in "$dir"/*.md; do
35
+ [ -f "$f" ] || continue
36
+ local hp
37
+ hp=$(has_paths "$f")
38
+
39
+ # Skip always-on rules: AGENTS.md carries them.
40
+ [ "$hp" -eq 0 ] && continue
41
+
42
+ local base
43
+ base="$(basename "$f" .md)"
44
+
45
+ local paths_line
46
+ paths_line=$(awk '
47
+ { sub(/\r$/, "") }
48
+ /^paths:/ { in_paths=1; next }
49
+ in_paths && /^ - / {
50
+ val = $0
51
+ sub(/^ - /, "", val)
52
+ gsub(/["\047]/, "", val)
53
+ printf "%s%s", sep, val
54
+ sep = ","
55
+ }
56
+ in_paths && !/^ - / && !/^$/ { exit }
57
+ END { print "" }
58
+ ' "$f")
59
+
60
+ {
61
+ echo "---"
62
+ echo "applyTo: \"$paths_line\""
63
+ echo "---"
64
+ echo ""
65
+ awk '
66
+ BEGIN { in_fm=0; past_fm=0 }
67
+ { sub(/\r$/, "") }
68
+ /^---$/ {
69
+ if (!past_fm) { in_fm = !in_fm; if (!in_fm) { past_fm=1 }; next }
70
+ }
71
+ past_fm || !in_fm { print }
72
+ ' "$f"
73
+ } > "$instructions_dir/$base.instructions.md"
74
+ finalize_output_file "$instructions_dir/$base.instructions.md"
75
+ scoped_count=$((scoped_count + 1))
76
+ echo " rule: $base.instructions.md (scoped)"
77
+ done
78
+ done < <(read_yaml_list "$config_file" "rules")
79
+
80
+ echo " rules: $scoped_count scoped"
81
+ }
82
+
83
+ # Sync skills to Copilot format (SKILL.md in .github/skills/)
84
+ sync_copilot_skills() {
85
+ local repo_root="$1"
86
+ local config_file="$2"
87
+ local output_dir="$3"
88
+
89
+ local count=0
90
+ while IFS= read -r src; do
91
+ [ -z "$src" ] && continue
92
+ local dir
93
+ dir="$(resolve_source_dir "$repo_root" "$src")"
94
+ [ -d "$dir" ] || continue
95
+ for d in "$dir"/*/; do
96
+ [ -d "$d" ] || continue
97
+ local skill_name
98
+ skill_name="$(basename "$d")"
99
+ [ -f "$d/SKILL.md" ] || continue
100
+ copy_skill_bundle "$d" "$output_dir/skills/$skill_name"
101
+ count=$((count + 1))
102
+ echo " skill: $skill_name"
103
+ done
104
+ done < <(read_yaml_list "$config_file" "skills")
105
+
106
+ echo " -> Skills: $count"
107
+ }
108
+
109
+ # Sync agents to Copilot format (.github/agents/{name}.agent.md)
110
+ sync_copilot_agents() {
111
+ local repo_root="$1"
112
+ local config_file="$2"
113
+ local output_dir="$3"
114
+
115
+ local count=0
116
+ while IFS= read -r src; do
117
+ [ -z "$src" ] && continue
118
+ local dir
119
+ dir="$(resolve_source_dir "$repo_root" "$src")"
120
+ [ -d "$dir" ] || continue
121
+ for f in "$dir"/*.md; do
122
+ [ -f "$f" ] || continue
123
+ local name
124
+ name="$(basename "$f" .md)"
125
+
126
+ local tier access description
127
+ tier=$(get_frontmatter_value "tier" "$f")
128
+ access=$(get_frontmatter_value "access" "$f")
129
+ description=$(get_frontmatter_value "description" "$f")
130
+
131
+ local model
132
+ model=$(get_model "$config_file" "copilot" "$tier")
133
+
134
+ # Build tools list based on access. Tool aliases per Copilot custom-agent
135
+ # spec: `read`, `search`, `edit`, `execute`, `agent`, `web`, `todo`, `*`.
136
+ # Omitting `tools:` grants all; restrict to read/search for readonly access.
137
+ local tools_line=""
138
+ if [ "$access" = "readonly" ]; then
139
+ tools_line="tools: [\"read\", \"search\"]"
140
+ fi
141
+
142
+ local description_escaped
143
+ description_escaped=$(yaml_dq_escape "$description")
144
+
145
+ # Generate .agent.md: replace tier/access with description/model/tools
146
+ {
147
+ echo "---"
148
+ echo "description: \"$description_escaped\""
149
+ echo "model: $model"
150
+ [ -n "$tools_line" ] && echo "$tools_line"
151
+ echo "---"
152
+ echo ""
153
+ # Strip original frontmatter, keep body
154
+ awk '
155
+ BEGIN { in_fm=0; past_fm=0 }
156
+ { sub(/\r$/, "") }
157
+ /^---$/ {
158
+ if (!past_fm) { in_fm = !in_fm; if (!in_fm) { past_fm=1 }; next }
159
+ }
160
+ past_fm { print }
161
+ ' "$f"
162
+ } > "$output_dir/agents/$name.agent.md"
163
+ finalize_output_file "$output_dir/agents/$name.agent.md"
164
+ count=$((count + 1))
165
+ echo " agent: $name.agent.md (tier=$tier -> model=$model)"
166
+ done
167
+ done < <(read_yaml_list "$config_file" "agents")
168
+
169
+ echo " -> Agents: $count"
170
+ }
171
+
172
+ # Main entry point for Copilot adapter
173
+ sync_to_copilot() {
174
+ local repo_root="$1"
175
+ local config_file="$2"
176
+ local output_dir="$3"
177
+
178
+ echo "=== GitHub Copilot ==="
179
+
180
+ # Clean generated content (preserve workflows, etc.)
181
+ rm -rf "$output_dir/instructions" "$output_dir/prompts"
182
+ if [ -d "$output_dir/skills" ]; then
183
+ find "$output_dir/skills" -mindepth 1 -maxdepth 1 -type d | while read -r d; do
184
+ rm -rf "$d"
185
+ done
186
+ fi
187
+ rm -rf "$output_dir/agents"
188
+ mkdir -p "$output_dir" "$output_dir/skills" "$output_dir/agents"
189
+
190
+ sync_copilot_rules "$repo_root" "$config_file" "$output_dir"
191
+ sync_copilot_skills "$repo_root" "$config_file" "$output_dir"
192
+ sync_copilot_agents "$repo_root" "$config_file" "$output_dir"
193
+ }
@@ -0,0 +1,146 @@
1
+ #!/bin/bash
2
+ # intelligence-sync: Cursor adapter
3
+ # Transforms source prompts to .cursor/ format
4
+ #
5
+ # Rules:
6
+ # - Path-scoped (with `paths:`) -> .cursor/rules/<name>.mdc with `globs:`
7
+ # - Always-on (no `paths:`) -> SKIPPED here. Cursor reads AGENTS.md
8
+ # natively for project-level context, and the `agents` adapter inlines
9
+ # always-on rule content into AGENTS.md. Generating .mdc copies of the
10
+ # same rules would cause double-loading and burn the context window.
11
+ # Skills: copy skill directories in full (SKILL.md + bundled resources)
12
+ # Agents: strip tier/access/tools/disallowedTools, add model + readonly:true
13
+
14
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
15
+
16
+ # Sync rules to Cursor format (.md -> .mdc, paths -> globs)
17
+ sync_cursor_rules() {
18
+ local repo_root="$1"
19
+ local config_file="$2"
20
+ local output_dir="$3"
21
+
22
+ while IFS= read -r src; do
23
+ [ -z "$src" ] && continue
24
+ local dir
25
+ dir="$(resolve_source_dir "$repo_root" "$src")"
26
+ [ -d "$dir" ] || continue
27
+ for f in "$dir"/*.md; do
28
+ [ -f "$f" ] || continue
29
+ local hp
30
+ hp=$(has_paths "$f")
31
+
32
+ # Skip always-on rules: AGENTS.md (canonical) carries them.
33
+ if [ "$hp" -eq 0 ]; then
34
+ continue
35
+ fi
36
+
37
+ local base
38
+ base="$(basename "$f" .md)"
39
+
40
+ # Path-scoped rule -> Auto Attached (paths -> globs)
41
+ awk '
42
+ {
43
+ sub(/\r$/, "")
44
+ sub(/^paths:/, "globs:")
45
+ }
46
+ NR==1 { print; print "alwaysApply: false"; next }
47
+ { print }
48
+ ' "$f" > "$output_dir/rules/$base.mdc"
49
+ finalize_output_file "$output_dir/rules/$base.mdc"
50
+ echo " rule: $base.mdc (scoped)"
51
+ done
52
+ done < <(read_yaml_list "$config_file" "rules")
53
+ }
54
+
55
+ # Sync skills to Cursor format (copy as-is)
56
+ sync_cursor_skills() {
57
+ local repo_root="$1"
58
+ local config_file="$2"
59
+ local output_dir="$3"
60
+
61
+ while IFS= read -r src; do
62
+ [ -z "$src" ] && continue
63
+ local dir
64
+ dir="$(resolve_source_dir "$repo_root" "$src")"
65
+ [ -d "$dir" ] || continue
66
+ for d in "$dir"/*/; do
67
+ [ -d "$d" ] || continue
68
+ local skill_name
69
+ skill_name="$(basename "$d")"
70
+ [ -f "$d/SKILL.md" ] || continue
71
+ copy_skill_bundle "$d" "$output_dir/skills/$skill_name"
72
+ echo " skill: $skill_name"
73
+ done
74
+ done < <(read_yaml_list "$config_file" "skills")
75
+ }
76
+
77
+ # Sync agents to Cursor format
78
+ sync_cursor_agents() {
79
+ local repo_root="$1"
80
+ local config_file="$2"
81
+ local output_dir="$3"
82
+
83
+ while IFS= read -r src; do
84
+ [ -z "$src" ] && continue
85
+ local dir
86
+ dir="$(resolve_source_dir "$repo_root" "$src")"
87
+ [ -d "$dir" ] || continue
88
+ for f in "$dir"/*.md; do
89
+ [ -f "$f" ] || continue
90
+ local name
91
+ name="$(basename "$f")"
92
+
93
+ local tier access
94
+ tier=$(get_frontmatter_value "tier" "$f")
95
+ access=$(get_frontmatter_value "access" "$f")
96
+
97
+ local cursor_model cursor_readonly
98
+ cursor_model=$(get_model "$config_file" "cursor" "$tier")
99
+ cursor_readonly=""
100
+ [ "$access" = "readonly" ] && cursor_readonly="readonly: true"
101
+
102
+ # Always emit model: line (even when value matches Cursor's default)
103
+ # so config is explicit and grep-able.
104
+ awk -v cm="$cursor_model" -v cr="$cursor_readonly" '
105
+ BEGIN { count=0 }
106
+ /^tier:/ { next }
107
+ /^access:/ { next }
108
+ /^tools:/ { next }
109
+ /^disallowedTools:/ { next }
110
+ {
111
+ sub(/\r$/, "")
112
+ }
113
+ /^---$/ { count++ }
114
+ count==2 && /^---$/ {
115
+ print "model: " cm
116
+ if (cr != "") print cr
117
+ }
118
+ { print }
119
+ ' "$f" > "$output_dir/agents/$name"
120
+ finalize_output_file "$output_dir/agents/$name"
121
+ echo " agent: $name (tier=$tier -> cursor)"
122
+ done
123
+ done < <(read_yaml_list "$config_file" "agents")
124
+ }
125
+
126
+ # Main entry point for Cursor adapter
127
+ sync_to_cursor() {
128
+ local repo_root="$1"
129
+ local config_file="$2"
130
+ local output_dir="$3"
131
+
132
+ echo "=== Cursor ==="
133
+
134
+ rm -rf "$output_dir/rules" "$output_dir/agents" "$output_dir/skills"
135
+ mkdir -p "$output_dir/rules" "$output_dir/skills" "$output_dir/agents"
136
+
137
+ sync_cursor_rules "$repo_root" "$config_file" "$output_dir"
138
+ sync_cursor_skills "$repo_root" "$config_file" "$output_dir"
139
+ sync_cursor_agents "$repo_root" "$config_file" "$output_dir"
140
+
141
+ local rules_count skills_count agents_count
142
+ rules_count=$(find "$output_dir/rules" -name "*.mdc" 2>/dev/null | wc -l)
143
+ skills_count=$(find "$output_dir/skills" -name "SKILL.md" 2>/dev/null | wc -l)
144
+ agents_count=$(find "$output_dir/agents" -name "*.md" 2>/dev/null | wc -l)
145
+ echo " -> Rules: $rules_count (.mdc), Skills: $skills_count, Agents: $agents_count"
146
+ }
@@ -0,0 +1,200 @@
1
+ #!/bin/bash
2
+ # intelligence-sync: opencode adapter
3
+ # Transforms source prompts to opencode's native resource model.
4
+ #
5
+ # Rules:
6
+ # - Always-on (no `paths:`) -> SKIPPED here. opencode reads AGENTS.md
7
+ # natively as its primary "Rules" surface (see opencode.ai/docs/rules).
8
+ # - Path-scoped (with `paths:`) -> SKIPPED. opencode has no first-class
9
+ # scoped-rule mechanism; users who want scoped rules can opt in via
10
+ # `instructions:` globs in opencode.json. We do not synthesize that.
11
+ # Skills:
12
+ # - Copied to .agents/skills/ (Agent Skills open standard; opencode
13
+ # discovers `.agents/skills/<name>/SKILL.md` natively).
14
+ # Commands:
15
+ # - For every synced skill, emit `.opencode/commands/<name>.md` so users can
16
+ # invoke the skill as `/<name> [args]` directly from the slash menu —
17
+ # replicating the Claude Code UX where every skill is also a slash
18
+ # command. The command body delegates to the `skill` tool and forwards
19
+ # `$ARGUMENTS` so any flags from the skill's `argument-hint` reach the
20
+ # skill script unchanged. Cleanup is marker-based: only files containing
21
+ # the generation marker are removed, so user-managed commands survive.
22
+ # Agents:
23
+ # - Converted to .opencode/agents/<name>.md markdown agents
24
+ # (mode: subagent) with frontmatter description/model/permission. The
25
+ # body is the source agent's prompt, used directly as the system prompt.
26
+
27
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
28
+
29
+ # Strip frontmatter, return body only.
30
+ # Direct call to the shared `strip_frontmatter` helper in lib/common.sh —
31
+ # duplicating the awk block here would violate the "all parsing in common.sh"
32
+ # convention (see CLAUDE.md / docs/CONVENTIONS.md).
33
+
34
+ sync_opencode_skills() {
35
+ local repo_root="$1"
36
+ local config_file="$2"
37
+ local _output_dir="$3"
38
+
39
+ # Skills -> .agents/skills/ (open standard; opencode reads it natively).
40
+ # `sync_open_skill_dirs` owns clean + populate of this shared dir.
41
+ local skills_dir="$repo_root/.agents/skills"
42
+ sync_open_skill_dirs "$repo_root" "$config_file" "$skills_dir"
43
+ }
44
+
45
+ # Marker line embedded in every generated command file. Cleanup grep's for
46
+ # this exact string before deleting, so any user-authored command without it
47
+ # survives a sync.
48
+ OPENCODE_GEN_MARKER="<!-- Generated by intelligence-sync. Do not edit manually. -->"
49
+
50
+ # Generate one slash command per synced skill.
51
+ # For each `<src>/<skill>/SKILL.md`, write `.opencode/commands/<skill>.md`
52
+ # with `description:` copied from the skill's `description` (YAML-escaped) and a body that tells the
53
+ # current agent to load the skill via the `skill` tool and pass `$ARGUMENTS`.
54
+ # Files marked with $OPENCODE_GEN_MARKER are removed before regeneration; any
55
+ # user-managed command without the marker is preserved. A user file that
56
+ # shares a name with a skill is overwritten by design — that 1:1 name
57
+ # binding is the whole point of the wrap.
58
+ sync_opencode_commands() {
59
+ local repo_root="$1"
60
+ local config_file="$2"
61
+ local output_dir="$3"
62
+
63
+ local commands_dir="$output_dir/commands"
64
+ mkdir -p "$commands_dir"
65
+
66
+ # Marker-based cleanup: leave user-authored commands alone.
67
+ local f
68
+ if [ -d "$commands_dir" ]; then
69
+ for f in "$commands_dir"/*.md; do
70
+ [ -f "$f" ] || continue
71
+ if grep -qF "$OPENCODE_GEN_MARKER" "$f" 2>/dev/null; then
72
+ rm -f "$f"
73
+ fi
74
+ done
75
+ fi
76
+
77
+ local count=0
78
+ while IFS= read -r src; do
79
+ [ -z "$src" ] && continue
80
+ local dir
81
+ dir="$(resolve_source_dir "$repo_root" "$src")"
82
+ [ -d "$dir" ] || continue
83
+ for d in "$dir"/*/; do
84
+ [ -d "$d" ] || continue
85
+ local skill_name
86
+ skill_name="$(basename "$d")"
87
+ local skill_md="$d/SKILL.md"
88
+ [ -f "$skill_md" ] || continue
89
+
90
+ local description argument_hint
91
+ description="$(get_frontmatter_value "description" "$skill_md")"
92
+ argument_hint="$(get_frontmatter_value "argument-hint" "$skill_md")"
93
+
94
+ local out_file="$commands_dir/$skill_name.md"
95
+ {
96
+ echo "---"
97
+ echo "description: \"$(yaml_dq_escape "$description")\""
98
+ echo "---"
99
+ echo ""
100
+ echo "$OPENCODE_GEN_MARKER"
101
+ echo ""
102
+ echo "Load and execute the \`$skill_name\` skill via the \`skill\` tool, then run it."
103
+ echo ""
104
+ if [ -n "$argument_hint" ]; then
105
+ echo "Pass these arguments to the skill: \$ARGUMENTS"
106
+ echo ""
107
+ echo "Argument hint from the source skill: \`$argument_hint\`"
108
+ else
109
+ echo "Forward any provided arguments to the skill: \$ARGUMENTS"
110
+ fi
111
+ } > "$out_file"
112
+ finalize_output_file "$out_file"
113
+ count=$((count + 1))
114
+ echo " command: /$skill_name"
115
+ done
116
+ done < <(read_yaml_list "$config_file" "skills")
117
+
118
+ echo " -> Commands: $count"
119
+ }
120
+
121
+ sync_opencode_agents() {
122
+ local repo_root="$1"
123
+ local config_file="$2"
124
+ local output_dir="$3"
125
+
126
+ local agents_dir="$output_dir/agents"
127
+ mkdir -p "$agents_dir"
128
+
129
+ local count=0
130
+ while IFS= read -r src; do
131
+ [ -z "$src" ] && continue
132
+ local dir
133
+ dir="$(resolve_source_dir "$repo_root" "$src")"
134
+ [ -d "$dir" ] || continue
135
+ for f in "$dir"/*.md; do
136
+ [ -f "$f" ] || continue
137
+
138
+ local name tier access description body
139
+ name="$(basename "$f" .md)"
140
+ tier="$(get_frontmatter_value "tier" "$f")"
141
+ access="$(get_frontmatter_value "access" "$f")"
142
+ description="$(get_frontmatter_value "description" "$f")"
143
+ body="$(strip_frontmatter "$f")"
144
+
145
+ local model edit_perm bash_perm
146
+ model="$(get_model "$config_file" "opencode" "$tier")"
147
+
148
+ # Map source `access:` to opencode permission keys.
149
+ # `edit` gates write/edit/apply_patch; `bash` gates shell.
150
+ case "$access" in
151
+ readonly) edit_perm="deny"; bash_perm="deny" ;;
152
+ *) edit_perm="allow"; bash_perm="allow" ;;
153
+ esac
154
+
155
+ local out_file="$agents_dir/$name.md"
156
+ {
157
+ echo "---"
158
+ echo "description: \"$(yaml_dq_escape "$description")\""
159
+ echo "mode: subagent"
160
+ if [ -n "$model" ]; then
161
+ echo "model: \"$(yaml_dq_escape "$model")\""
162
+ fi
163
+ echo "permission:"
164
+ echo " edit: $edit_perm"
165
+ echo " bash: $bash_perm"
166
+ echo "---"
167
+ echo ""
168
+ echo "<!-- Generated by intelligence-sync. Do not edit manually. -->"
169
+ echo ""
170
+ printf '%s\n' "$body"
171
+ } > "$out_file"
172
+ finalize_output_file "$out_file"
173
+ count=$((count + 1))
174
+ echo " agent: $name.md"
175
+ done
176
+ done < <(read_yaml_list "$config_file" "agents")
177
+
178
+ echo " -> Agents: $count"
179
+ }
180
+
181
+ sync_to_opencode() {
182
+ local repo_root="$1"
183
+ local config_file="$2"
184
+ local output_dir="$3"
185
+
186
+ echo "=== opencode ==="
187
+
188
+ # Adapter owns .opencode/agents/ entirely (so removed/renamed source
189
+ # agents don't linger). Other .opencode/* files (e.g. user-managed
190
+ # opencode.json) are left untouched. .opencode/commands/ is owned
191
+ # marker-by-marker — see sync_opencode_commands.
192
+ rm -rf "$output_dir/agents"
193
+ mkdir -p "$output_dir/agents"
194
+
195
+ sync_opencode_skills "$repo_root" "$config_file" "$output_dir"
196
+ sync_opencode_commands "$repo_root" "$config_file" "$output_dir"
197
+ sync_opencode_agents "$repo_root" "$config_file" "$output_dir"
198
+
199
+ echo " -> Rules: skipped (AGENTS.md carries always-on; opencode has no native path-scoped rules)"
200
+ }