@ainova-systems/intelligence 0.11.3 → 0.11.4

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.
@@ -23,6 +23,9 @@
23
23
  # - Converted to .opencode/agents/<name>.md markdown agents
24
24
  # (mode: subagent) with frontmatter description/model/permission. The
25
25
  # body is the source agent's prompt, used directly as the system prompt.
26
+ #
27
+ # Every per-file loop batches its work into one awk process (see the batched
28
+ # helpers in lib/common.sh): process spawns dominate sync time on Windows.
26
29
 
27
30
  source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
28
31
 
@@ -37,11 +40,6 @@ adapter_contract_opencode() {
37
40
  adapter_contract_ignore "$output/agents/"
38
41
  }
39
42
 
40
- # Strip frontmatter, return body only.
41
- # Direct call to the shared `strip_frontmatter` helper in lib/common.sh —
42
- # duplicating the awk block here would fork frontmatter parsing, which the
43
- # convention keeps in one place.
44
-
45
43
  sync_opencode_skills() {
46
44
  local repo_root="$1"
47
45
  local config_file="$2"
@@ -74,38 +72,47 @@ sync_opencode_commands() {
74
72
  local commands_dir="$output_dir/commands"
75
73
  mkdir -p "$commands_dir"
76
74
 
77
- # Marker-based cleanup: leave user-authored commands alone.
75
+ # Marker-based cleanup: leave user-authored commands alone. One grep -l
76
+ # finds every generated file; one rm removes them.
78
77
  local f
79
- if [ -d "$commands_dir" ]; then
80
- for f in "$commands_dir"/*.md; do
81
- [ -f "$f" ] || continue
82
- if grep -qF "$OPENCODE_GEN_MARKER" "$f" 2>/dev/null; then
83
- rm -f "$f"
84
- fi
85
- done
78
+ local -a existing=() generated=()
79
+ for f in "$commands_dir"/*.md; do
80
+ [ -f "$f" ] && existing+=("$f")
81
+ done
82
+ if [ "${#existing[@]}" -gt 0 ]; then
83
+ while IFS= read -r f; do
84
+ [ -n "$f" ] && generated+=("$f")
85
+ done < <(grep -lF "$OPENCODE_GEN_MARKER" "${existing[@]}" 2>/dev/null || true)
86
+ [ "${#generated[@]}" -gt 0 ] && rm -f "${generated[@]}"
86
87
  fi
87
88
 
88
- local count=0
89
+ local src d skill_name
90
+ local -a skill_mds=()
91
+ load_yaml_list "$config_file" "skills"
92
+ local list="$IS_YAML_LIST"
89
93
  while IFS= read -r src; do
90
94
  [ -z "$src" ] && continue
91
- local dir
92
- dir="$(resolve_source_dir "$repo_root" "$src")"
95
+ local dir="$repo_root/$src"
93
96
  [ -d "$dir" ] || continue
94
97
  for d in "$dir"/*/; do
95
98
  [ -d "$d" ] || continue
96
- local skill_name
97
- skill_name="$(basename "$d")"
98
- local skill_md="$d/SKILL.md"
99
- [ -f "$skill_md" ] || continue
100
-
101
- local description argument_hint
102
- description="$(get_frontmatter_value "description" "$skill_md")"
103
- argument_hint="$(get_frontmatter_value "argument-hint" "$skill_md")"
99
+ [ -f "$d/SKILL.md" ] || continue
100
+ skill_mds+=("${d%/}/SKILL.md")
101
+ done
102
+ done <<< "$list"
104
103
 
105
- local out_file="$commands_dir/$skill_name.md"
104
+ local count=0
105
+ if [ "${#skill_mds[@]}" -gt 0 ]; then
106
+ local path description argument_hint out_file
107
+ local -a out_files=()
108
+ while IFS=$'\x1f' read -r path description argument_hint; do
109
+ [ -n "$path" ] || continue
110
+ skill_name="${path%/SKILL.md}"; skill_name="${skill_name##*/}"
111
+ out_file="$commands_dir/$skill_name.md"
112
+ yaml_dq_escape_var "$description"
106
113
  {
107
114
  echo "---"
108
- echo "description: \"$(yaml_dq_escape "$description")\""
115
+ echo "description: \"$IS_YAML_ESCAPED\""
109
116
  echo "---"
110
117
  echo ""
111
118
  echo "$OPENCODE_GEN_MARKER"
@@ -120,11 +127,12 @@ sync_opencode_commands() {
120
127
  echo "Forward any provided arguments to the skill: \$ARGUMENTS"
121
128
  fi
122
129
  } > "$out_file"
123
- finalize_output_file "$out_file"
130
+ out_files+=("$out_file")
124
131
  count=$((count + 1))
125
132
  echo " command: /$skill_name"
126
- done
127
- done < <(read_yaml_list "$config_file" "skills")
133
+ done < <(frontmatter_index "description,argument-hint" "${skill_mds[@]}")
134
+ [ "${#out_files[@]}" -gt 0 ] && finalize_output_files "${out_files[@]}"
135
+ fi
128
136
 
129
137
  echo " -> Commands: $count"
130
138
  }
@@ -137,24 +145,29 @@ sync_opencode_agents() {
137
145
  local agents_dir="$output_dir/agents"
138
146
  mkdir -p "$agents_dir"
139
147
 
140
- local count=0
148
+ local src f
149
+ local -a files=()
150
+ load_yaml_list "$config_file" "agents"
151
+ local list="$IS_YAML_LIST"
141
152
  while IFS= read -r src; do
142
153
  [ -z "$src" ] && continue
143
- local dir
144
- dir="$(resolve_source_dir "$repo_root" "$src")"
154
+ local dir="$repo_root/$src"
145
155
  [ -d "$dir" ] || continue
146
156
  for f in "$dir"/*.md; do
147
157
  [ -f "$f" ] || continue
158
+ files+=("$f")
159
+ done
160
+ done <<< "$list"
148
161
 
149
- local name tier access description body
150
- name="$(basename "$f" .md)"
151
- tier="$(get_frontmatter_value "tier" "$f")"
152
- access="$(get_frontmatter_value "access" "$f")"
153
- description="$(get_frontmatter_value "description" "$f")"
154
- body="$(strip_frontmatter "$f")"
162
+ local count=0
163
+ if [ "${#files[@]}" -gt 0 ]; then
164
+ load_model_tiers "$config_file" "opencode"
155
165
 
156
- local model edit_perm bash_perm
157
- model="$(get_model "$config_file" "opencode" "$tier")"
166
+ local path tier access description name edit_perm bash_perm spec="" report="" LS=$'\x1e'
167
+ while IFS=$'\x1f' read -r path tier access description; do
168
+ [ -n "$path" ] || continue
169
+ name="${path##*/}"; name="${name%.md}"
170
+ resolve_model_var "$tier"
158
171
 
159
172
  # Map source `access:` to opencode permission keys.
160
173
  # `edit` gates write/edit/apply_patch; `bash` gates shell.
@@ -163,28 +176,31 @@ sync_opencode_agents() {
163
176
  *) edit_perm="allow"; bash_perm="allow" ;;
164
177
  esac
165
178
 
166
- local out_file="$agents_dir/$name.md"
167
- {
168
- echo "---"
169
- echo "description: \"$(yaml_dq_escape "$description")\""
170
- echo "mode: subagent"
171
- if [ -n "$model" ]; then
172
- echo "model: \"$(yaml_dq_escape "$model")\""
173
- fi
174
- echo "permission:"
175
- echo " edit: $edit_perm"
176
- echo " bash: $bash_perm"
177
- echo "---"
178
- echo ""
179
- echo "<!-- Generated by intelligence-sync. Do not edit manually. -->"
180
- echo ""
181
- printf '%s\n' "$body"
182
- } > "$out_file"
183
- finalize_output_file "$out_file"
179
+ local header
180
+ yaml_dq_escape_var "$description"
181
+ header="${LS}---"
182
+ header+="${LS}description: \"$IS_YAML_ESCAPED\""
183
+ header+="${LS}mode: subagent"
184
+ if [ -n "$IS_MODEL" ]; then
185
+ yaml_dq_escape_var "$IS_MODEL"
186
+ header+="${LS}model: \"$IS_YAML_ESCAPED\""
187
+ fi
188
+ header+="${LS}permission:"
189
+ header+="${LS} edit: $edit_perm"
190
+ header+="${LS} bash: $bash_perm"
191
+ header+="${LS}---"
192
+ header+="${LS}"
193
+ header+="${LS}<!-- Generated by intelligence-sync. Do not edit manually. -->"
194
+ header+="${LS}"
195
+
196
+ spec+="$path"$'\x1f'"$agents_dir/$name.md"$'\x1f'"strip"$'\x1f'"1"$'\x1f'"none"$'\x1f'"$header"$'\x1f\n'
197
+ report+=" agent: $name.md"$'\n'
184
198
  count=$((count + 1))
185
- echo " agent: $name.md"
186
- done
187
- done < <(read_yaml_list "$config_file" "agents")
199
+ done < <(frontmatter_index "tier,access,description" "${files[@]}")
200
+
201
+ emit_wrapped_bodies "$spec"
202
+ printf '%s' "$report"
203
+ fi
188
204
 
189
205
  echo " -> Agents: $count"
190
206
  }
@@ -12,6 +12,9 @@
12
12
  # Agents:
13
13
  # - Converted to .pi/prompts/intelligence-agent-<name>.md prompt templates
14
14
  # so users can invoke a persona explicitly via `/intelligence-agent-<name>`.
15
+ #
16
+ # Every per-file loop batches its work into one awk process (see the batched
17
+ # helpers in lib/common.sh): process spawns dominate sync time on Windows.
15
18
 
16
19
  source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
17
20
 
@@ -29,40 +32,55 @@ adapter_contract_pi() {
29
32
  adapter_contract_ignore "$output/prompts/intelligence-agent-*.md"
30
33
  }
31
34
 
32
- pi_ts_escape() {
35
+ # Fork-free TS string escape: sets IS_PI_TS_ESCAPED.
36
+ pi_ts_escape_var() {
33
37
  local s="$1"
34
38
  s="${s//\\/\\\\}"
35
39
  s="${s//\"/\\\"}"
36
40
  s="${s//$'\r'/}"
37
41
  s="${s//$'\n'/ }"
38
- printf '%s' "$s"
42
+ IS_PI_TS_ESCAPED="$s"
39
43
  }
40
44
 
41
- pi_rule_paths() {
42
- local file="$1"
45
+ pi_ts_escape() {
46
+ pi_ts_escape_var "$1"
47
+ printf '%s' "$IS_PI_TS_ESCAPED"
48
+ }
49
+
50
+ # pi_rule_paths_rows <file>... — batch form of the old per-file `paths:`
51
+ # reader: one row per file in argument order, the path then each pattern,
52
+ # \x1f-separated. Patterns come from the frontmatter `paths:` block only,
53
+ # with one leading/trailing quote character stripped, exactly like the
54
+ # per-file reader did.
55
+ pi_rule_paths_rows() {
56
+ [ "$#" -gt 0 ] || return 0
43
57
  awk '
58
+ BEGIN { US = sprintf("%c", 31) }
59
+ function store() { if (cur != "") R[cur] = row }
60
+ FNR == 1 { store(); cur = FILENAME; row = cur; done_f = 0; in_fm = 0; in_paths = 0 }
44
61
  { sub(/\r$/, "") }
45
- NR == 1 && $0 != "---" { exit }
46
- NR == 1 { in_fm = 1; next }
47
- in_fm && $0 == "---" { exit }
62
+ done_f { next }
63
+ FNR == 1 && $0 != "---" { done_f = 1; next }
64
+ FNR == 1 { in_fm = 1; next }
65
+ in_fm && $0 == "---" { done_f = 1; next }
48
66
  !in_fm { next }
49
-
50
67
  /^paths:[[:space:]]*$/ { in_paths = 1; next }
51
68
  in_paths && /^ - / {
52
69
  val = $0
53
70
  sub(/^ - /, "", val)
54
71
  gsub(/^["\047]|["\047]$/, "", val)
55
- print val
72
+ row = row US val
56
73
  next
57
74
  }
58
- in_paths && !/^ - / && !/^[[:space:]]*$/ { exit }
59
- ' "$file"
60
- }
61
-
62
- pi_agent_body() {
63
- # Thin alias around the shared helper in lib/common.sh; kept so older call
64
- # sites (and any future adapter logic) read symmetrically with sync_pi_*.
65
- strip_frontmatter "$1"
75
+ in_paths && !/^ - / && !/^[[:space:]]*$/ { done_f = 1 }
76
+ END {
77
+ store()
78
+ for (i = 1; i < ARGC; i++) {
79
+ if (ARGV[i] in R) print R[ARGV[i]]
80
+ else print ARGV[i]
81
+ }
82
+ }
83
+ ' "$@"
66
84
  }
67
85
 
68
86
  sync_pi_rules() {
@@ -78,43 +96,60 @@ sync_pi_rules() {
78
96
 
79
97
  mkdir -p "$rules_root" "$(dirname "$extension_file")"
80
98
 
99
+ local src f
100
+ local -a files=()
101
+ load_yaml_list "$config_file" "rules"
102
+ local list="$IS_YAML_LIST"
81
103
  while IFS= read -r src; do
82
104
  [ -z "$src" ] && continue
83
- local dir
84
- dir="$(resolve_source_dir "$repo_root" "$src")"
105
+ local dir="$repo_root/$src"
85
106
  [ -d "$dir" ] || continue
86
107
  for f in "$dir"/*.md; do
87
108
  [ -f "$f" ] || continue
88
- local hp
89
- hp=$(has_paths "$f")
109
+ files+=("$f")
110
+ done
111
+ done <<< "$list"
112
+
113
+ local -a scoped=()
114
+ if [ "${#files[@]}" -gt 0 ]; then
115
+ local path hp
116
+ while IFS=$'\x1f' read -r path hp; do
117
+ [ -n "$path" ] || continue
90
118
  [ "$hp" -eq 0 ] && continue
119
+ scoped+=("$path")
120
+ done < <(frontmatter_index "paths#" "${files[@]}")
121
+ fi
122
+
123
+ if [ "${#scoped[@]}" -gt 0 ]; then
124
+ finalize_copy_files "$rules_root" "${scoped[@]}"
91
125
 
92
- local base rel_rule_path patterns_js display sep display_sep
93
- base="$(basename "$f")"
126
+ local -a row_arr
127
+ local base rel_rule_path patterns_js display sep display_sep pat i
128
+ while IFS=$'\x1f' read -r -a row_arr; do
129
+ [ "${#row_arr[@]}" -gt 0 ] || continue
130
+ base="${row_arr[0]##*/}"
94
131
  rel_rule_path="$output_rel/intelligence-sync/rules/$base"
95
132
  patterns_js=""
96
133
  display=""
97
134
  sep=""
98
135
  display_sep=""
99
-
100
- cp "$f" "$rules_root/$base"
101
- finalize_output_file "$rules_root/$base"
102
-
103
- while IFS= read -r pat; do
136
+ i=1
137
+ while [ "$i" -lt "${#row_arr[@]}" ]; do
138
+ pat="${row_arr[$i]}"
139
+ i=$((i + 1))
104
140
  [ -z "$pat" ] && continue
105
- local pat_escaped
106
- pat_escaped="$(pi_ts_escape "$pat")"
107
- patterns_js+="$sep\"$pat_escaped\""
141
+ pi_ts_escape_var "$pat"
142
+ patterns_js+="$sep\"$IS_PI_TS_ESCAPED\""
108
143
  display+="$display_sep$pat"
109
144
  sep=", "
110
145
  display_sep=", "
111
- done < <(pi_rule_paths "$f")
112
-
113
- manifest+=" { path: \"$(pi_ts_escape "$rel_rule_path")\", patterns: [$patterns_js] },"$'\n'
146
+ done
147
+ pi_ts_escape_var "$rel_rule_path"
148
+ manifest+=" { path: \"$IS_PI_TS_ESCAPED\", patterns: [$patterns_js] },"$'\n'
114
149
  count=$((count + 1))
115
150
  echo " rule: $base (scoped${display:+ — $display})"
116
- done
117
- done < <(read_yaml_list "$config_file" "rules")
151
+ done < <(pi_rule_paths_rows "${scoped[@]}")
152
+ fi
118
153
 
119
154
  if [ "$count" -gt 0 ]; then
120
155
  {
@@ -186,64 +221,67 @@ sync_pi_agents() {
186
221
 
187
222
  mkdir -p "$prompts_dir"
188
223
 
224
+ local src f
225
+ local -a files=()
226
+ load_yaml_list "$config_file" "agents"
227
+ local list="$IS_YAML_LIST"
189
228
  while IFS= read -r src; do
190
229
  [ -z "$src" ] && continue
191
- local dir
192
- dir="$(resolve_source_dir "$repo_root" "$src")"
230
+ local dir="$repo_root/$src"
193
231
  [ -d "$dir" ] || continue
194
232
  for f in "$dir"/*.md; do
195
233
  [ -f "$f" ] || continue
234
+ files+=("$f")
235
+ done
236
+ done <<< "$list"
196
237
 
197
- local name rel desc access template_desc body prompt_file access_note
198
- name="$(basename "$f" .md)"
199
- rel="$(repo_rel_link "$repo_root" "$f")"
200
- desc="$(get_frontmatter_value "description" "$f")"
201
- access="$(get_frontmatter_value "access" "$f")"
202
- body="$(pi_agent_body "$f")"
203
- prompt_file="$prompts_dir/intelligence-agent-$name.md"
238
+ if [ "${#files[@]}" -gt 0 ]; then
239
+ local path desc access name template_desc spec="" report="" LS=$'\x1e'
240
+ while IFS=$'\x1f' read -r path desc access; do
241
+ [ -n "$path" ] || continue
242
+ name="${path##*/}"; name="${name%.md}"
243
+ repo_rel_link_var "$repo_root" "$path"
204
244
 
205
245
  template_desc="Use $name persona"
206
246
  [ -n "$desc" ] && template_desc="$template_desc — $desc"
207
247
 
208
- access_note=""
248
+ local header
249
+ yaml_dq_escape_var "$template_desc"
250
+ header="${LS}---"
251
+ header+="${LS}description: \"$IS_YAML_ESCAPED\""
252
+ header+="${LS}argument-hint: \"[task]\""
253
+ header+="${LS}---"
254
+ header+="${LS}"
255
+ header+="${LS}<!-- Generated by intelligence-sync. Do not edit manually. -->"
256
+ header+="${LS}"
257
+ header+="${LS}Adopt the \`$name\` agent persona for this task."
258
+ header+="${LS}"
259
+ header+="${LS}Source: \`${IS_REPO_REL:-$name (remote pack)}\`"
260
+ if [ -n "$desc" ]; then
261
+ header+="${LS}Use this persona when: $desc"
262
+ header+="${LS}"
263
+ fi
209
264
  if [ "$access" = "readonly" ]; then
210
- # Bash 3.2 (the macOS system shell) misparses an apostrophe in
211
- # a quoted heredoc nested inside command substitution.
212
- access_note="## Access Mode
213
-
214
- Default to read-only analysis. Do not change files or run mutating commands unless the user explicitly asks you to override this agent's normal restriction.
215
- "
265
+ header+="${LS}## Access Mode"
266
+ header+="${LS}"
267
+ header+="${LS}Default to read-only analysis. Do not change files or run mutating commands unless the user explicitly asks you to override this agent's normal restriction."
268
+ header+="${LS}"
216
269
  fi
270
+ header+="${LS}## Agent Instructions"
271
+ header+="${LS}"
272
+
273
+ local tail
274
+ tail="${LS}"
275
+ tail+="${LS}User task: \$@"
217
276
 
218
- {
219
- echo "---"
220
- echo "description: \"$(yaml_dq_escape "$template_desc")\""
221
- echo "argument-hint: \"[task]\""
222
- echo "---"
223
- echo ""
224
- echo "<!-- Generated by intelligence-sync. Do not edit manually. -->"
225
- echo ""
226
- printf 'Adopt the `%s` agent persona for this task.\n' "$name"
227
- echo ""
228
- printf 'Source: `%s`\n' "${rel:-$name (remote pack)}"
229
- if [ -n "$desc" ]; then
230
- echo "Use this persona when: $desc"
231
- echo ""
232
- fi
233
- if [ -n "$access_note" ]; then
234
- printf '%s\n' "$access_note"
235
- fi
236
- echo "## Agent Instructions"
237
- echo ""
238
- printf '%s\n' "$body"
239
- echo ""
240
- echo "User task: \$@"
241
- } > "$prompt_file"
242
- finalize_output_file "$prompt_file"
277
+ spec+="$path"$'\x1f'"$prompts_dir/intelligence-agent-$name.md"$'\x1f'"strip"$'\x1f'"1"$'\x1f'"none"$'\x1f'"$header"$'\x1f'"$tail"$'\n'
278
+ report+=" agent: intelligence-agent-$name.md"$'\n'
243
279
  count=$((count + 1))
244
- echo " agent: intelligence-agent-$name.md"
245
- done
246
- done < <(read_yaml_list "$config_file" "agents")
280
+ done < <(frontmatter_index "description,access" "${files[@]}")
281
+
282
+ emit_wrapped_bodies "$spec"
283
+ printf '%s' "$report"
284
+ fi
247
285
 
248
286
  echo " -> Agents: $count prompt template(s)"
249
287
  }