@ainova-systems/intelligence 0.25.0 → 0.25.2

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/engine/ENGINE_SHA CHANGED
@@ -1 +1 @@
1
- faf0b3ae4ad1b07f08463caf9a332c1eb3a6dcc9
1
+ 8755eb016a151d36dfcf4fc85b9cddd226cdce11
package/engine/VERSION CHANGED
@@ -1 +1 @@
1
- 0.25.0
1
+ 0.25.2
@@ -18,19 +18,34 @@ normalize_file_to_lf() {
18
18
  #
19
19
  # The content directory is named by the project (`intelligence/`,
20
20
  # `Intelligence/`, a codename), so package-shipped artifacts cannot hardcode
21
- # it. Layout-sensitive paths and commands use tokens expanded at output time:
21
+ # it. Layout-sensitive paths and commands use tokens expanded at output time,
22
+ # in EVERY generated file, frontmatter and body alike, so a scoped rule reaches
23
+ # Claude's `paths:`, Cursor's `globs:` and Copilot's `applyTo:` already
24
+ # carrying the project's real folder name.
22
25
  #
23
- # <content-dir> -> the repo-relative project content dir (e.g. `Intelligence`)
24
- # <module> -> the repo-relative sync package store path
25
- #
26
- # A backslash in front keeps a token literal, and the source lint warns about a
27
- # token in a project source that reads as ordinary text (_IS_AWK_LINT).
28
- #
29
- # Values come from IS_CONTENT_REL / IS_MODULE_REL, which the CLI derives from
30
- # the detected layout (never hardcoded) and exports before any adapter runs.
31
- # Expansion happens in EVERY generated file, frontmatter and body alike, so a
32
- # scoped rule reaches Claude's `paths:`, Cursor's `globs:` and Copilot's
33
- # `applyTo:` already carrying the project's real folder name.
26
+ # IS_LAYOUT_TOKENS is the one declaration of them, a `<name> <variable>
27
+ # <default>` row per token: `<name>` renders as the value of <variable>, which
28
+ # the CLI derives from the detected layout (never hardcoded) and exports
29
+ # before any adapter runs, or as <default>, the rest of the row, while that
30
+ # variable is unset or empty. Expansion (fin_line), the backslash that keeps a
31
+ # token as written (fin_keep, token_keep_var) and the source lint that warns
32
+ # about a token reading as ordinary text (_IS_AWK_LINT) all derive from these
33
+ # rows, so a new token is one row here plus its entry in the reference tables
34
+ # (packages/sync/references/conventions.md and adapters.md). A name is
35
+ # lowercase letters, digits and `-`: it is spliced into awk text and a Bash
36
+ # pattern.
37
+ IS_LAYOUT_TOKENS=(
38
+ 'content-dir IS_CONTENT_REL intelligence'
39
+ 'module IS_MODULE_REL .intelligence/packages/@ainova-systems/sync'
40
+ 'manifest IS_MANIFEST_NAME intelligence.yaml'
41
+ 'sync-cmd IS_SYNC_CMD intelligence sync'
42
+ )
43
+ IS_LAYOUT_NAMES=()
44
+ _is_names=""
45
+ for _is_row in "${IS_LAYOUT_TOKENS[@]}"; do
46
+ IS_LAYOUT_NAMES+=("${_is_row%% *}")
47
+ _is_names="$_is_names${_is_names:+ }${_is_row%% *}"
48
+ done
34
49
 
35
50
  # Process spawns dominate sync time on Git Bash for Windows: one fork costs
36
51
  # tens of milliseconds against ~1ms on Linux, so a helper that runs awk per
@@ -42,8 +57,10 @@ normalize_file_to_lf() {
42
57
  #
43
58
  # fin_line() uses literal (index-based) substitution, not gsub: a regex
44
59
  # replacement would give `&` in a path its special meaning, and POSIX awk has
45
- # no way to pass a replacement string verbatim. Token values arrive via the
46
- # -v args produced by is_fin_awk_vars.
60
+ # no way to pass a replacement string verbatim. The token names are part of
61
+ # the program text, so fin_keep works in every program that includes this
62
+ # library; the values are read in BEGIN from the environment variables that
63
+ # is_fin_awk_vars fills and names in IS_FIN_V.
47
64
  IS_AWK_LIB='
48
65
  function is_repl(s, from, to, out, i) {
49
66
  out = ""
@@ -53,23 +70,35 @@ IS_AWK_LIB='
53
70
  }
54
71
  return out s
55
72
  }
73
+ # FIN_TOK[k] is the k-th token of IS_LAYOUT_TOKENS, FIN_VAL[k] its value,
74
+ # FIN_ESC[k] the token escaped and FIN_HOLD[k] what fin_keep turns that into.
75
+ BEGIN {
76
+ FIN_N = split("'"$_is_names"'", is_tn, " ")
77
+ for (is_tk = 1; is_tk <= FIN_N; is_tk++) {
78
+ FIN_TOK[is_tk] = "<" is_tn[is_tk] ">"
79
+ FIN_ESC[is_tk] = "\\" FIN_TOK[is_tk]
80
+ FIN_HOLD[is_tk] = "\034" is_tn[is_tk] ">"
81
+ if (FIN_ENV != "") FIN_VAL[is_tk] = ENVIRON[FIN_ENV is_tk]
82
+ }
83
+ }
56
84
  # A backslash keeps a token as written: `\<module>` renders as `<module>`.
57
85
  # The escaped token waits on a control character while the others expand.
58
86
  # A writer that doubles backslashes for YAML or TOML calls fin_keep (or
59
87
  # token_keep_var) first: once escaped, `\\<module>` no longer reads as one.
60
- function fin_keep(s) {
88
+ function fin_keep(s, k) {
61
89
  if (!index(s, "\\<")) return s
62
- s = is_repl(s, "\\<sync-cmd>", "\034sync-cmd>")
63
- s = is_repl(s, "\\<manifest>", "\034manifest>")
64
- s = is_repl(s, "\\<module>", "\034module>")
65
- return is_repl(s, "\\<content-dir>", "\034content-dir>")
90
+ for (k = 1; k <= FIN_N; k++) s = is_repl(s, FIN_ESC[k], FIN_HOLD[k])
91
+ return s
66
92
  }
67
- function fin_line(s) {
68
- s = fin_keep(s)
69
- s = is_repl(s, "<sync-cmd>", FIN_SC)
70
- s = is_repl(s, "<manifest>", FIN_MF)
71
- s = is_repl(s, "<module>", FIN_MOD)
72
- s = is_repl(s, "<content-dir>", FIN_CONTENT)
93
+ # Every line of every rendered file passes here, and most hold no `<`, so
94
+ # those skip the token loop. Tokens expand from the last declared to the
95
+ # first, the order the engine has always used: a value that itself holds a
96
+ # token renders as it always did.
97
+ function fin_line(s, k) {
98
+ if (index(s, "<")) {
99
+ s = fin_keep(s)
100
+ for (k = FIN_N; k > 0; k--) s = is_repl(s, FIN_TOK[k], FIN_VAL[k])
101
+ }
73
102
  if (index(s, "\034")) s = is_repl(s, "\034", "<")
74
103
  return s
75
104
  }
@@ -128,17 +157,28 @@ IS_AWK_LIB='
128
157
  return 1
129
158
  }
130
159
  '
131
-
132
- # is_fin_awk_vars — fill the global IS_FIN_V array with the -v bindings the
133
- # IS_AWK_LIB fin_line() function needs. Rebuilt on every call: the IS_* env
134
- # contract is exported after this file is sourced.
160
+ unset _is_row _is_names
161
+
162
+ # is_fin_awk_vars — export each token's value as IS_LAYOUT_VALUE_<k>, <k> its
163
+ # row in IS_LAYOUT_TOKENS, and set the global IS_FIN_V to the -v binding that
164
+ # points IS_AWK_LIB at those variables; a program run without IS_FIN_V
165
+ # expands every token to nothing, as it always did. The values travel through
166
+ # the environment, which awk reads verbatim: a -v value has its backslashes
167
+ # read as escapes, and BSD awk refuses one that holds a newline. One variable
168
+ # per token, so no byte a value holds can split it into two. Rebuilt on every
169
+ # call: the IS_* env contract is exported after this file is sourced, and a
170
+ # caller may set one variable for a single call.
135
171
  is_fin_awk_vars() {
136
- IS_FIN_V=(
137
- -v "FIN_SC=${IS_SYNC_CMD:-intelligence sync}"
138
- -v "FIN_MF=${IS_MANIFEST_NAME:-intelligence.yaml}"
139
- -v "FIN_MOD=${IS_MODULE_REL:-.intelligence/packages/@ainova-systems/sync}"
140
- -v "FIN_CONTENT=${IS_CONTENT_REL:-intelligence}"
141
- )
172
+ local row rest var def k=0
173
+ for row in "${IS_LAYOUT_TOKENS[@]}"; do
174
+ rest="${row#* }"
175
+ var="${rest%% *}"
176
+ def=""
177
+ case "$rest" in *' '*) def="${rest#* }" ;; esac
178
+ k=$((k + 1))
179
+ export "IS_LAYOUT_VALUE_$k=${!var:-$def}"
180
+ done
181
+ IS_FIN_V=(-v FIN_ENV=IS_LAYOUT_VALUE_)
142
182
  }
143
183
 
144
184
  # finalize_output_files <file>...
@@ -311,7 +351,7 @@ token_keep_var() {
311
351
  local s="$1" fs=$'\034' t
312
352
  case "$s" in
313
353
  *'\<'*)
314
- for t in sync-cmd manifest module content-dir; do
354
+ for t in "${IS_LAYOUT_NAMES[@]}"; do
315
355
  s="${s//\\<$t>/$fs$t>}"
316
356
  done
317
357
  ;;
@@ -1107,6 +1147,9 @@ enforce_authored_invocation_policies() {
1107
1147
  # warning names the source exactly once however many tools render it. Unlike
1108
1148
  # the lint notes it is an unindented `WARNING:` line, because `sync --compact`
1109
1149
  # (and so `init`) keeps only those (decision 0015).
1150
+ # This form takes no repository root, so its warnings name each file as the
1151
+ # caller passed it; the engine's pass, source_frontmatter_scan, names it
1152
+ # relative to the root.
1110
1153
  # Usage: lint_frontmatter_files "a.md" "b.md" ...
1111
1154
  lint_frontmatter_files() {
1112
1155
  [ "$#" -gt 0 ] || return 0
@@ -1115,7 +1158,10 @@ lint_frontmatter_files() {
1115
1158
 
1116
1159
  # The lint rules, shared with source_frontmatter_scan, which runs them in the
1117
1160
  # same pass that reads routing fields. Every rule may end with `next`, so a
1118
- # program that joins them puts its own rules first.
1161
+ # program that joins them puts its own rules first. Every warning names the
1162
+ # source as lint_src: relative to ENVIRON["IS_LINT_ROOT"] when the file lies
1163
+ # under it, so a warning reads the same from any checkout, from the private
1164
+ # copy `sync --check` renders in, and in the replay of an unchanged sync.
1119
1165
  _IS_AWK_LINT='
1120
1166
  BEGIN {
1121
1167
  nl = split(levels, LV, " ")
@@ -1124,19 +1170,17 @@ _IS_AWK_LINT='
1124
1170
  LEVEL[LV[li]] = 1
1125
1171
  allowed = allowed (li > 1 ? ", " : "") LV[li]
1126
1172
  }
1127
- tok_root = ENVIRON["IS_TOK_ROOT"]
1128
- split("<content-dir> <module> <manifest> <sync-cmd>", TOK, " ")
1129
- TOKV[1] = FIN_CONTENT; TOKV[2] = FIN_MOD; TOKV[3] = FIN_MF; TOKV[4] = FIN_SC
1173
+ lint_root = ENVIRON["IS_LINT_ROOT"]
1130
1174
  }
1131
1175
  # A layout token that cannot mean what it renders as: joined to a word
1132
1176
  # or a path around it, or an XML element closed on the same line. A
1133
1177
  # module-name placeholder `<module>_update_N` rendered as the package
1134
1178
  # path. Packages use tokens by design, so only project sources (under
1135
1179
  # the root, outside its package store) are read, and an escaped token
1136
- # (`\<module>`) is literal.
1180
+ # (`\<module>`) is literal. The tokens are FIN_TOK from IS_AWK_LIB.
1137
1181
  function tok_check(l, k, t, n, at, p, b, a) {
1138
- for (k = 1; k <= 4; k++) {
1139
- t = TOK[k]; n = length(t); at = 0
1182
+ for (k = 1; k <= FIN_N; k++) {
1183
+ t = FIN_TOK[k]; n = length(t); at = 0
1140
1184
  while ((p = index(substr(l, at + 1), t)) > 0) {
1141
1185
  at += p
1142
1186
  b = (at > 1) ? substr(l, at - 1, 1) : ""
@@ -1146,7 +1190,7 @@ _IS_AWK_LINT='
1146
1190
  if (b ~ /[[:alnum:]_]/ || b == "/" || a ~ /^([[:alnum:]_-]|[.][[:alnum:]_])/ \
1147
1191
  || index(l, "</" substr(t, 2))) {
1148
1192
  printf "WARNING: %s:%d %s is part of the text around it, but sync renders this layout token as %s; write \\%s to keep it as written\n", \
1149
- substr(FILENAME, length(tok_root) + 2), FNR, t, TOKV[k], t > "/dev/stderr"
1193
+ lint_src, FNR, t, FIN_VAL[k], t > "/dev/stderr"
1150
1194
  break
1151
1195
  }
1152
1196
  }
@@ -1154,8 +1198,9 @@ _IS_AWK_LINT='
1154
1198
  }
1155
1199
  FNR == 1 {
1156
1200
  in_fm = 0; done = 0; effort_seen = 0
1157
- tok_own = tok_root != "" && index(FILENAME, tok_root "/") == 1 \
1158
- && index(FILENAME, tok_root "/.intelligence/") != 1
1201
+ lint_under = lint_root != "" && index(FILENAME, lint_root "/") == 1
1202
+ lint_src = lint_under ? substr(FILENAME, length(lint_root) + 2) : FILENAME
1203
+ tok_own = lint_under && index(FILENAME, lint_root "/.intelligence/") != 1
1159
1204
  }
1160
1205
  { sub(/\r$/, "") }
1161
1206
  tok_own && index($0, ">") { tok_check($0) }
@@ -1166,11 +1211,11 @@ _IS_AWK_LINT='
1166
1211
  in_fm && substr($0, 1, 7) == "effort:" && !effort_seen++ {
1167
1212
  effort = fm_value_strip(substr($0, 8))
1168
1213
  if (effort != "" && !(effort in LEVEL)) {
1169
- printf "WARNING: %s:%d effort \"%s\" is not one of %s — ignored, so no tool receives an effort from this file\n", FILENAME, FNR, effort, allowed > "/dev/stderr"
1214
+ printf "WARNING: %s:%d effort \"%s\" is not one of %s — ignored, so no tool receives an effort from this file\n", lint_src, FNR, effort, allowed > "/dev/stderr"
1170
1215
  }
1171
1216
  }
1172
1217
  in_fm && /^\t/ {
1173
- printf "WARNING: %s:%d leading tab in frontmatter (use spaces)\n", FILENAME, FNR > "/dev/stderr"
1218
+ printf "WARNING: %s:%d leading tab in frontmatter (use spaces)\n", lint_src, FNR > "/dev/stderr"
1174
1219
  }
1175
1220
  in_fm && /^[a-zA-Z0-9_-]+:[[:space:]]+[^"\047|>[{]/ {
1176
1221
  value_start = index($0, ":") + 1
@@ -1178,10 +1223,10 @@ _IS_AWK_LINT='
1178
1223
  sub(/^[[:space:]]+/, "", value)
1179
1224
  if (value ~ /:[[:space:]]/ || value ~ /:$/) {
1180
1225
  col = index(value, ":") + value_start
1181
- printf "WARNING: %s:%d unquoted colon in value at column %d — wrap value in quotes\n", FILENAME, FNR, col > "/dev/stderr"
1226
+ printf "WARNING: %s:%d unquoted colon in value at column %d — wrap value in quotes\n", lint_src, FNR, col > "/dev/stderr"
1182
1227
  }
1183
1228
  if (value ~ /"/) {
1184
- printf "WARNING: %s:%d literal double quote in unquoted value — wrap value in single quotes or escape as \\\" so strict-YAML targets accept it\n", FILENAME, FNR > "/dev/stderr"
1229
+ printf "WARNING: %s:%d literal double quote in unquoted value — wrap value in single quotes or escape as \\\" so strict-YAML targets accept it\n", lint_src, FNR > "/dev/stderr"
1185
1230
  }
1186
1231
  }
1187
1232
  # Field-length limits. Both Claude Code and the Agent Skills standard
@@ -1200,7 +1245,7 @@ _IS_AWK_LINT='
1200
1245
  }
1201
1246
  limit = (key == "name") ? 64 : 1024
1202
1247
  if (length(val) > limit) {
1203
- printf "WARNING: %s:%d %s is %d chars — over the %d-char limit; the skill/agent will be REJECTED at load time\n", FILENAME, FNR, key, length(val), limit > "/dev/stderr"
1248
+ printf "WARNING: %s:%d %s is %d chars — over the %d-char limit; the skill/agent will be REJECTED at load time\n", lint_src, FNR, key, length(val), limit > "/dev/stderr"
1204
1249
  }
1205
1250
  }
1206
1251
  '
@@ -2542,13 +2587,14 @@ source_catalog_build() {
2542
2587
  # occurrence of a key wins, and a key with no value at all is not set
2543
2588
  # (`targets: []` is: it is the empty list). With <lint-root>, the repository
2544
2589
  # root, the same pass runs lint_frontmatter_files over every file and the
2545
- # layout-token check over the project's own; warnings go to stderr.
2590
+ # layout-token check over the project's own; warnings go to stderr and name
2591
+ # each source relative to that root.
2546
2592
  source_frontmatter_scan() {
2547
2593
  local do_lint=0 root="$1"
2548
2594
  [ -z "$root" ] || do_lint=1
2549
2595
  shift
2550
2596
  is_fin_awk_vars
2551
- IS_TOK_ROOT="$root" awk -v levels="$IS_EFFORT_LEVELS" -v do_lint="$do_lint" "${IS_FIN_V[@]}" "$IS_AWK_LIB"'
2597
+ IS_LINT_ROOT="$root" awk -v levels="$IS_EFFORT_LEVELS" -v do_lint="$do_lint" "${IS_FIN_V[@]}" "$IS_AWK_LIB"'
2552
2598
  BEGIN { SX_US = sprintf("%c", 31) }
2553
2599
  function sx_items(s, k, parts, m, j, v, q) {
2554
2600
  m = split(s, parts, ",")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainova-systems/intelligence",
3
- "version": "0.25.0",
3
+ "version": "0.25.2",
4
4
  "description": "Build, version and distribute AI agent intelligence across your organization — one CLI, versioned Intelligence Packages, and a sync engine for Claude Code, Cursor, Copilot, Codex, Pi and OpenCode.",
5
5
  "bin": {
6
6
  "intelligence": "bin/intelligence.js"