@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.
- package/LICENSE +21 -0
- package/README.md +46 -0
- package/bin/intelligence.js +59 -0
- package/cli/commands/add.sh +133 -0
- package/cli/commands/doctor.sh +100 -0
- package/cli/commands/init.sh +96 -0
- package/cli/commands/install.sh +84 -0
- package/cli/commands/list.sh +28 -0
- package/cli/commands/migrate.sh +251 -0
- package/cli/commands/registry.sh +51 -0
- package/cli/commands/remove.sh +39 -0
- package/cli/commands/status.sh +34 -0
- package/cli/commands/sync.sh +22 -0
- package/cli/commands/update.sh +59 -0
- package/cli/commands/upgrade.sh +27 -0
- package/cli/intelligence +71 -0
- package/cli/lib/cli-common.sh +149 -0
- package/cli/lib/lockfile.sh +97 -0
- package/cli/lib/manifest.sh +211 -0
- package/cli/lib/registry.sh +141 -0
- package/cli/lib/semver.sh +127 -0
- package/engine/INIT.md +498 -0
- package/engine/agents/intelligence-architect.md +53 -0
- package/engine/agents/intelligence-operator.md +49 -0
- package/engine/docs/ADAPTERS.md +212 -0
- package/engine/docs/CLI.md +91 -0
- package/engine/docs/CONVENTIONS.md +440 -0
- package/engine/rules/intelligence-authoring.md +114 -0
- package/engine/scripts/VERSION +1 -0
- package/engine/scripts/adapters/_template.sh +86 -0
- package/engine/scripts/adapters/agents.sh +299 -0
- package/engine/scripts/adapters/claude.sh +136 -0
- package/engine/scripts/adapters/codex.sh +118 -0
- package/engine/scripts/adapters/copilot.sh +193 -0
- package/engine/scripts/adapters/cursor.sh +146 -0
- package/engine/scripts/adapters/opencode.sh +200 -0
- package/engine/scripts/adapters/pi.sh +256 -0
- package/engine/scripts/lib/common.sh +1602 -0
- package/engine/scripts/lib/layout.sh +51 -0
- package/engine/scripts/lib/migrations.sh +708 -0
- package/engine/scripts/sync.sh +311 -0
- package/engine/scripts/update.sh +237 -0
- package/engine/skills/intelligence-add-agent/SKILL.md +62 -0
- package/engine/skills/intelligence-add-rule/SKILL.md +54 -0
- package/engine/skills/intelligence-add-skill/SKILL.md +53 -0
- package/engine/skills/intelligence-extract-skill/SKILL.md +47 -0
- package/engine/skills/intelligence-install-adapter/SKILL.md +31 -0
- package/engine/skills/intelligence-learn-from-context/SKILL.md +69 -0
- package/engine/skills/intelligence-review-skills/SKILL.md +86 -0
- package/engine/skills/intelligence-sync/SKILL.md +18 -0
- package/engine/skills/intelligence-uninstall-adapter/SKILL.md +42 -0
- package/engine/skills/intelligence-update/SKILL.md +159 -0
- package/package.json +39 -0
- 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
|
+
}
|