@jetrabbits/agentic 0.1.0 → 0.2.0
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/CHANGELOG.md +12 -0
- package/Makefile +31 -3
- package/README.md +23 -5
- package/agentic +754 -110
- package/docs/agentic-lifecycle.md +1 -1
- package/docs/agentic-usage.md +47 -2
- package/package.json +2 -1
package/agentic
CHANGED
|
@@ -15,7 +15,7 @@ APP_REPO_LINK="https://github.com/sawrus/agent-guides"
|
|
|
15
15
|
PROJECT_MANIFEST_NAME=".agentic.json"
|
|
16
16
|
|
|
17
17
|
DEFAULT_AGENT_OS="default"
|
|
18
|
-
STATIC_AGENT_OS=(
|
|
18
|
+
STATIC_AGENT_OS=(opencode codex claude antigravity cursor kilocode)
|
|
19
19
|
INSTALL_DIRS=(rules skills workflows prompts)
|
|
20
20
|
THEME_CHOICES=(auto dark light)
|
|
21
21
|
|
|
@@ -55,6 +55,12 @@ SKIPPED_MANAGED_PATHS=()
|
|
|
55
55
|
WARNINGS=()
|
|
56
56
|
|
|
57
57
|
CONTEXT7_API_KEY="${CONTEXT7_API_KEY:-}"
|
|
58
|
+
AGENTIC_ENABLE_CONTEXT7="${AGENTIC_ENABLE_CONTEXT7:-}"
|
|
59
|
+
AGENTIC_DOCTOR="${AGENTIC_DOCTOR:-1}"
|
|
60
|
+
AGENTIC_DOCTOR_KEEP_TMP="${AGENTIC_DOCTOR_KEEP_TMP:-0}"
|
|
61
|
+
|
|
62
|
+
RUN_LOG_ACTIVE=false
|
|
63
|
+
RUN_LOG_FILE=""
|
|
58
64
|
|
|
59
65
|
COLOR_RESET=""
|
|
60
66
|
COLOR_HEADER=""
|
|
@@ -67,7 +73,7 @@ FZF_COLOR_ARGS=()
|
|
|
67
73
|
|
|
68
74
|
usage() {
|
|
69
75
|
cat <<USAGE
|
|
70
|
-
$APP_TITLE
|
|
76
|
+
$APP_TITLE $(app_version_label)
|
|
71
77
|
|
|
72
78
|
Usage:
|
|
73
79
|
$SCRIPT_NAME list [agentos|areas|specs --area <name>]
|
|
@@ -75,6 +81,7 @@ Usage:
|
|
|
75
81
|
$SCRIPT_NAME tui [--theme auto|dark|light]
|
|
76
82
|
$SCRIPT_NAME upgrade
|
|
77
83
|
$SCRIPT_NAME self-install [--bin-dir <dir>] [--force] [--install-fzf] [--dry-run]
|
|
84
|
+
$SCRIPT_NAME --version
|
|
78
85
|
|
|
79
86
|
Behavior:
|
|
80
87
|
- No arguments in interactive terminal: runs TUI mode
|
|
@@ -87,11 +94,13 @@ Options:
|
|
|
87
94
|
--areas Comma-separated area list (example: software)
|
|
88
95
|
--specializations Comma-separated specializations in area.spec format (example: software.backend,software.frontend)
|
|
89
96
|
--theme Interface theme: auto|dark|light (default: config value or auto)
|
|
97
|
+
--no-doctor Skip real agent smoke checks after install
|
|
90
98
|
--bin-dir Installation directory for self-install (default: ~/.local/bin)
|
|
91
99
|
--force Overwrite existing binary for self-install
|
|
92
100
|
--install-fzf During self-install, try to auto-install fzf (optional)
|
|
93
101
|
--dry-run Show actions without writing files
|
|
94
102
|
-h, --help Show this help
|
|
103
|
+
-V, --version Show agentic version
|
|
95
104
|
|
|
96
105
|
Examples:
|
|
97
106
|
$SCRIPT_NAME list agentos
|
|
@@ -102,6 +111,31 @@ Examples:
|
|
|
102
111
|
USAGE
|
|
103
112
|
}
|
|
104
113
|
|
|
114
|
+
read_package_version() {
|
|
115
|
+
local package_file="$1"
|
|
116
|
+
[[ -f "$package_file" ]] || return 1
|
|
117
|
+
|
|
118
|
+
sed -n 's/^[[:space:]]*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$package_file" | head -n 1
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
app_version() {
|
|
122
|
+
local version=""
|
|
123
|
+
local candidate
|
|
124
|
+
for candidate in "$SCRIPT_DIR/package.json" "$REPO_ROOT/package.json" "$APP_REPO_DIR/package.json"; do
|
|
125
|
+
[[ -n "$candidate" ]] || continue
|
|
126
|
+
version="$(read_package_version "$candidate" || true)"
|
|
127
|
+
if [[ -n "$version" ]]; then
|
|
128
|
+
printf '%s\n' "$version"
|
|
129
|
+
return
|
|
130
|
+
fi
|
|
131
|
+
done
|
|
132
|
+
printf 'unknown\n'
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
app_version_label() {
|
|
136
|
+
printf 'v%s\n' "$(app_version)"
|
|
137
|
+
}
|
|
138
|
+
|
|
105
139
|
is_interactive_terminal() {
|
|
106
140
|
if [[ "${AGENTIC_FORCE_INTERACTIVE:-${AGENTOS_FORCE_INTERACTIVE:-}}" == "1" ]]; then
|
|
107
141
|
return 0
|
|
@@ -217,16 +251,116 @@ set_theme_colors() {
|
|
|
217
251
|
}
|
|
218
252
|
|
|
219
253
|
log() {
|
|
220
|
-
|
|
254
|
+
emit_log_line stdout "[agentic]" "$1" "$COLOR_INFO"
|
|
221
255
|
}
|
|
222
256
|
|
|
223
257
|
warn() {
|
|
224
|
-
|
|
258
|
+
emit_log_line stdout "[agentic][warn]" "$1" "$COLOR_WARN"
|
|
225
259
|
WARNINGS+=("$1")
|
|
226
260
|
}
|
|
227
261
|
|
|
228
262
|
error() {
|
|
229
|
-
|
|
263
|
+
emit_log_line stderr "[agentic][error]" "$1" "$COLOR_ERROR"
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
timestamp_now() {
|
|
267
|
+
date '+%Y-%m-%d %H:%M:%S'
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
init_run_logging() {
|
|
271
|
+
if [[ "$RUN_LOG_ACTIVE" == true ]]; then
|
|
272
|
+
return
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
local base_dir="${TMPDIR:-/tmp}"
|
|
276
|
+
local stamp
|
|
277
|
+
stamp="$(date '+%Y%m%d-%H%M%S')"
|
|
278
|
+
RUN_LOG_FILE="$(mktemp "$base_dir/agentic-$stamp.XXXXXX")"
|
|
279
|
+
RUN_LOG_ACTIVE=true
|
|
280
|
+
log "Run log initialized: $RUN_LOG_FILE"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
write_run_log_line() {
|
|
284
|
+
local line="$1"
|
|
285
|
+
if [[ "$RUN_LOG_ACTIVE" == true && -n "$RUN_LOG_FILE" ]]; then
|
|
286
|
+
printf '%s\n' "$line" >> "$RUN_LOG_FILE"
|
|
287
|
+
fi
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
emit_log_line() {
|
|
291
|
+
local stream="$1"
|
|
292
|
+
local tag="$2"
|
|
293
|
+
local message="$3"
|
|
294
|
+
local color="${4:-}"
|
|
295
|
+
|
|
296
|
+
local line plain_line ts
|
|
297
|
+
if [[ "$RUN_LOG_ACTIVE" == true ]]; then
|
|
298
|
+
ts="$(timestamp_now)"
|
|
299
|
+
plain_line="$ts $tag $message"
|
|
300
|
+
if [[ -n "$color" ]]; then
|
|
301
|
+
line="$ts ${color}${tag}${COLOR_RESET} $message"
|
|
302
|
+
else
|
|
303
|
+
line="$plain_line"
|
|
304
|
+
fi
|
|
305
|
+
else
|
|
306
|
+
plain_line="$tag $message"
|
|
307
|
+
if [[ -n "$color" ]]; then
|
|
308
|
+
line="${color}${tag}${COLOR_RESET} $message"
|
|
309
|
+
else
|
|
310
|
+
line="$plain_line"
|
|
311
|
+
fi
|
|
312
|
+
fi
|
|
313
|
+
|
|
314
|
+
if [[ "$stream" == "stderr" ]]; then
|
|
315
|
+
printf '%s\n' "$line" >&2
|
|
316
|
+
else
|
|
317
|
+
printf '%s\n' "$line"
|
|
318
|
+
fi
|
|
319
|
+
write_run_log_line "$plain_line"
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
out() {
|
|
323
|
+
local message="${1:-}"
|
|
324
|
+
local color="${2:-}"
|
|
325
|
+
local line plain_line ts
|
|
326
|
+
|
|
327
|
+
if [[ -z "$message" ]]; then
|
|
328
|
+
printf '\n'
|
|
329
|
+
write_run_log_line ""
|
|
330
|
+
return
|
|
331
|
+
fi
|
|
332
|
+
|
|
333
|
+
if [[ "$RUN_LOG_ACTIVE" == true ]]; then
|
|
334
|
+
ts="$(timestamp_now)"
|
|
335
|
+
plain_line="$ts $message"
|
|
336
|
+
if [[ -n "$color" ]]; then
|
|
337
|
+
line="$ts ${color}${message}${COLOR_RESET}"
|
|
338
|
+
else
|
|
339
|
+
line="$plain_line"
|
|
340
|
+
fi
|
|
341
|
+
else
|
|
342
|
+
plain_line="$message"
|
|
343
|
+
if [[ -n "$color" ]]; then
|
|
344
|
+
line="${color}${message}${COLOR_RESET}"
|
|
345
|
+
else
|
|
346
|
+
line="$plain_line"
|
|
347
|
+
fi
|
|
348
|
+
fi
|
|
349
|
+
|
|
350
|
+
printf '%s\n' "$line"
|
|
351
|
+
write_run_log_line "$plain_line"
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
log_file_block() {
|
|
355
|
+
local label="$1"
|
|
356
|
+
local path="$2"
|
|
357
|
+
[[ "$RUN_LOG_ACTIVE" == true && -n "$RUN_LOG_FILE" && -f "$path" ]] || return 0
|
|
358
|
+
|
|
359
|
+
write_run_log_line "$(timestamp_now) --- $label output begin ---"
|
|
360
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
361
|
+
write_run_log_line "$(timestamp_now) $line"
|
|
362
|
+
done < "$path"
|
|
363
|
+
write_run_log_line "$(timestamp_now) --- $label output end ---"
|
|
230
364
|
}
|
|
231
365
|
|
|
232
366
|
unique_append() {
|
|
@@ -519,6 +653,42 @@ ensure_python_available() {
|
|
|
519
653
|
fi
|
|
520
654
|
}
|
|
521
655
|
|
|
656
|
+
pip_command() {
|
|
657
|
+
if command -v pip >/dev/null 2>&1; then
|
|
658
|
+
printf '%s\n' "pip"
|
|
659
|
+
return 0
|
|
660
|
+
fi
|
|
661
|
+
if command -v pip3 >/dev/null 2>&1; then
|
|
662
|
+
printf '%s\n' "pip3"
|
|
663
|
+
return 0
|
|
664
|
+
fi
|
|
665
|
+
if command -v python3 >/dev/null 2>&1 && python3 -m pip --version >/dev/null 2>&1; then
|
|
666
|
+
printf '%s\n' "python3 -m pip"
|
|
667
|
+
return 0
|
|
668
|
+
fi
|
|
669
|
+
return 1
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
ensure_pip_available() {
|
|
673
|
+
if ! pip_command >/dev/null; then
|
|
674
|
+
error "pip is required to run agentic install/tui. Install pip for Python 3 and make 'pip3', 'pip', or 'python3 -m pip' available."
|
|
675
|
+
exit 1
|
|
676
|
+
fi
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
ensure_hash_available() {
|
|
680
|
+
if ! command -v shasum >/dev/null 2>&1 && ! command -v sha256sum >/dev/null 2>&1; then
|
|
681
|
+
error "shasum or sha256sum is required to track managed files"
|
|
682
|
+
exit 1
|
|
683
|
+
fi
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
ensure_agentic_runtime_requirements() {
|
|
687
|
+
ensure_python_available
|
|
688
|
+
ensure_pip_available
|
|
689
|
+
ensure_hash_available
|
|
690
|
+
}
|
|
691
|
+
|
|
522
692
|
selected_agent_os_contains() {
|
|
523
693
|
local expected="$1"
|
|
524
694
|
local agent
|
|
@@ -547,8 +717,7 @@ hash_file() {
|
|
|
547
717
|
elif command -v sha256sum >/dev/null 2>&1; then
|
|
548
718
|
sha256sum "$path" | awk '{print $1}'
|
|
549
719
|
else
|
|
550
|
-
|
|
551
|
-
exit 1
|
|
720
|
+
ensure_hash_available
|
|
552
721
|
fi
|
|
553
722
|
}
|
|
554
723
|
|
|
@@ -634,12 +803,15 @@ register_managed_file() {
|
|
|
634
803
|
local dest="$1"
|
|
635
804
|
local source_ref="$2"
|
|
636
805
|
local marker="$3"
|
|
806
|
+
local copied="${4:-true}"
|
|
637
807
|
local rel
|
|
638
808
|
rel="$(project_rel_path "$dest")"
|
|
639
809
|
local digest
|
|
640
810
|
digest="$(hash_file "$dest")"
|
|
641
811
|
MANAGED_RECORDS+=("$rel|$source_ref|$digest|$marker")
|
|
642
|
-
|
|
812
|
+
if [[ "$copied" == true ]]; then
|
|
813
|
+
unique_append "$dest" COPIED_PATHS
|
|
814
|
+
fi
|
|
643
815
|
}
|
|
644
816
|
|
|
645
817
|
record_agentic_event() {
|
|
@@ -679,8 +851,10 @@ write_file_with_agentic_marker() {
|
|
|
679
851
|
can_write_managed_file "$dest" || return 0
|
|
680
852
|
|
|
681
853
|
ensure_dir "$(dirname -- "$dest")"
|
|
682
|
-
|
|
854
|
+
local write_status
|
|
855
|
+
write_status="$(python3 - "$src" "$dest" "$source_ref" "$APP_REPO_LINK" "$(app_version_label)" <<'PY'
|
|
683
856
|
import json
|
|
857
|
+
import re
|
|
684
858
|
import sys
|
|
685
859
|
from pathlib import Path
|
|
686
860
|
|
|
@@ -688,6 +862,7 @@ src = Path(sys.argv[1])
|
|
|
688
862
|
dest = Path(sys.argv[2])
|
|
689
863
|
source_ref = sys.argv[3]
|
|
690
864
|
repo = sys.argv[4]
|
|
865
|
+
version = sys.argv[5]
|
|
691
866
|
text = src.read_text(encoding="utf-8")
|
|
692
867
|
suffix = dest.suffix.lower()
|
|
693
868
|
marker = f"Generated by agentic; source: {source_ref}; repository: {repo}"
|
|
@@ -697,12 +872,28 @@ def yaml_quote(value: str) -> str:
|
|
|
697
872
|
return json.dumps(value, ensure_ascii=False)
|
|
698
873
|
|
|
699
874
|
|
|
875
|
+
def existing_created_by() -> str:
|
|
876
|
+
if not dest.exists():
|
|
877
|
+
return version
|
|
878
|
+
try:
|
|
879
|
+
old = dest.read_text(encoding="utf-8")
|
|
880
|
+
except Exception:
|
|
881
|
+
return version
|
|
882
|
+
match = re.search(r"(?m)^ created_by:\s*(.+?)\s*$", old)
|
|
883
|
+
if not match:
|
|
884
|
+
return version
|
|
885
|
+
return match.group(1).strip().strip('"')
|
|
886
|
+
|
|
887
|
+
|
|
700
888
|
def markdown_with_marker(body: str) -> str:
|
|
889
|
+
created_by = existing_created_by()
|
|
701
890
|
block = (
|
|
702
891
|
"agentic:\n"
|
|
703
892
|
" generated_by: agentic\n"
|
|
704
893
|
f" source: {yaml_quote(source_ref)}\n"
|
|
705
894
|
f" repository: {yaml_quote(repo)}\n"
|
|
895
|
+
f" created_by: {yaml_quote(created_by)}\n"
|
|
896
|
+
f" updated_by: {yaml_quote(version)}\n"
|
|
706
897
|
)
|
|
707
898
|
if body.startswith("---\n"):
|
|
708
899
|
end = body.find("\n---", 4)
|
|
@@ -739,9 +930,23 @@ elif suffix in {".sh", ".toml", ".py", ".yml", ".yaml"}:
|
|
|
739
930
|
else:
|
|
740
931
|
output = commented(text, "#")
|
|
741
932
|
|
|
933
|
+
if dest.exists():
|
|
934
|
+
try:
|
|
935
|
+
if dest.read_text(encoding="utf-8") == output:
|
|
936
|
+
print("unchanged")
|
|
937
|
+
raise SystemExit(0)
|
|
938
|
+
except UnicodeDecodeError:
|
|
939
|
+
pass
|
|
940
|
+
|
|
742
941
|
dest.write_text(output, encoding="utf-8")
|
|
942
|
+
print("written")
|
|
743
943
|
PY
|
|
744
|
-
|
|
944
|
+
)"
|
|
945
|
+
if [[ "$write_status" == "unchanged" ]]; then
|
|
946
|
+
register_managed_file "$dest" "$source_ref" "internal" false
|
|
947
|
+
else
|
|
948
|
+
register_managed_file "$dest" "$source_ref" "internal"
|
|
949
|
+
fi
|
|
745
950
|
}
|
|
746
951
|
|
|
747
952
|
write_agentic_manifest() {
|
|
@@ -775,7 +980,8 @@ write_agentic_manifest() {
|
|
|
775
980
|
specs_csv="${SELECTED_SPECS[*]}"
|
|
776
981
|
IFS="$old_ifs"
|
|
777
982
|
|
|
778
|
-
|
|
983
|
+
local manifest_status
|
|
984
|
+
manifest_status="$(python3 - "$manifest" "$records_file" "$skipped_file" "$APP_REPO_LINK" "$REPO_ROOT" "$agent_os_csv" "$areas_csv" "$specs_csv" "$(app_version_label)" <<'PY'
|
|
779
985
|
import json
|
|
780
986
|
import sys
|
|
781
987
|
from datetime import datetime, timezone
|
|
@@ -789,37 +995,55 @@ repo_root = sys.argv[5]
|
|
|
789
995
|
agent_os = [x for x in sys.argv[6].split(",") if x]
|
|
790
996
|
areas = [x for x in sys.argv[7].split(",") if x]
|
|
791
997
|
specs = [x for x in sys.argv[8].split(",") if x]
|
|
998
|
+
app_version = sys.argv[9]
|
|
792
999
|
now = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
|
793
1000
|
|
|
794
1001
|
existing = {}
|
|
795
1002
|
created_at = now
|
|
1003
|
+
old_data = None
|
|
796
1004
|
if manifest.exists():
|
|
797
1005
|
try:
|
|
798
1006
|
old = json.loads(manifest.read_text(encoding="utf-8"))
|
|
1007
|
+
old_data = old
|
|
799
1008
|
created_at = old.get("created_at", created_at)
|
|
800
1009
|
for item in old.get("managed_files", []):
|
|
801
1010
|
if item.get("path"):
|
|
802
1011
|
existing[item["path"]] = item
|
|
803
1012
|
except Exception:
|
|
804
1013
|
existing = {}
|
|
1014
|
+
original_existing = json.loads(json.dumps(existing))
|
|
805
1015
|
|
|
806
1016
|
for line in records_file.read_text(encoding="utf-8").splitlines():
|
|
807
1017
|
if not line:
|
|
808
1018
|
continue
|
|
809
1019
|
path, source, digest, marker = (line.split("|", 3) + ["", "", "", ""])[:4]
|
|
1020
|
+
old_item = original_existing.get(path, {})
|
|
1021
|
+
old_updated_at = old_item.get("updated_at", now)
|
|
1022
|
+
if (
|
|
1023
|
+
old_item.get("source") == source
|
|
1024
|
+
and old_item.get("content_hash") == digest
|
|
1025
|
+
and old_item.get("marker") == marker
|
|
1026
|
+
):
|
|
1027
|
+
item_updated_at = old_updated_at
|
|
1028
|
+
else:
|
|
1029
|
+
item_updated_at = now
|
|
810
1030
|
existing[path] = {
|
|
811
1031
|
"path": path,
|
|
812
1032
|
"source": source,
|
|
813
1033
|
"content_hash": digest,
|
|
814
1034
|
"marker": marker,
|
|
815
|
-
"updated_at":
|
|
1035
|
+
"updated_at": item_updated_at,
|
|
816
1036
|
}
|
|
817
1037
|
|
|
818
1038
|
skipped = [x for x in skipped_file.read_text(encoding="utf-8").splitlines() if x]
|
|
1039
|
+
old_agentic = old_data.get("_agentic", {}) if isinstance(old_data, dict) else {}
|
|
1040
|
+
created_by = old_agentic.get("created_by", app_version)
|
|
819
1041
|
data = {
|
|
820
1042
|
"_agentic": {
|
|
821
1043
|
"generated_by": "agentic",
|
|
822
1044
|
"repository": repo_link,
|
|
1045
|
+
"created_by": created_by,
|
|
1046
|
+
"updated_by": app_version,
|
|
823
1047
|
},
|
|
824
1048
|
"version": 1,
|
|
825
1049
|
"created_at": created_at,
|
|
@@ -834,9 +1058,26 @@ data = {
|
|
|
834
1058
|
"managed_files": sorted(existing.values(), key=lambda x: x["path"]),
|
|
835
1059
|
"skipped_files": skipped,
|
|
836
1060
|
}
|
|
1061
|
+
|
|
1062
|
+
if old_data is not None:
|
|
1063
|
+
old_compare = json.loads(json.dumps(old_data))
|
|
1064
|
+
new_compare = json.loads(json.dumps(data))
|
|
1065
|
+
for payload in (old_compare, new_compare):
|
|
1066
|
+
payload.pop("updated_at", None)
|
|
1067
|
+
if isinstance(payload.get("_agentic"), dict):
|
|
1068
|
+
payload["_agentic"].pop("updated_by", None)
|
|
1069
|
+
if old_compare == new_compare:
|
|
1070
|
+
print("unchanged")
|
|
1071
|
+
raise SystemExit(0)
|
|
1072
|
+
|
|
837
1073
|
manifest.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
1074
|
+
print("written")
|
|
838
1075
|
PY
|
|
1076
|
+
)"
|
|
839
1077
|
rm -f "$records_file" "$skipped_file"
|
|
1078
|
+
if [[ "$manifest_status" == "unchanged" ]]; then
|
|
1079
|
+
return
|
|
1080
|
+
fi
|
|
840
1081
|
unique_append "$manifest" COPIED_PATHS
|
|
841
1082
|
}
|
|
842
1083
|
|
|
@@ -976,9 +1217,10 @@ copy_dir_contents() {
|
|
|
976
1217
|
|
|
977
1218
|
local event kind value events_file
|
|
978
1219
|
events_file="$(mktemp "${TMPDIR:-/tmp}/agentic-copy-events.XXXXXX")"
|
|
979
|
-
python3 - "$src" "$dest" "$REPO_ROOT" "$PROJECT_DIR" "$(project_manifest_path)" "$APP_REPO_LINK" > "$events_file" <<'PY'
|
|
1220
|
+
python3 - "$src" "$dest" "$REPO_ROOT" "$PROJECT_DIR" "$(project_manifest_path)" "$APP_REPO_LINK" "$(app_version_label)" > "$events_file" <<'PY'
|
|
980
1221
|
import hashlib
|
|
981
1222
|
import json
|
|
1223
|
+
import re
|
|
982
1224
|
import sys
|
|
983
1225
|
from pathlib import Path
|
|
984
1226
|
|
|
@@ -988,6 +1230,7 @@ repo_root = Path(sys.argv[3])
|
|
|
988
1230
|
project_dir = Path(sys.argv[4])
|
|
989
1231
|
manifest = Path(sys.argv[5])
|
|
990
1232
|
repo = sys.argv[6]
|
|
1233
|
+
version = sys.argv[7]
|
|
991
1234
|
|
|
992
1235
|
|
|
993
1236
|
def emit(kind: str, value: str) -> None:
|
|
@@ -1010,7 +1253,7 @@ if manifest.exists():
|
|
|
1010
1253
|
for item in data.get("managed_files", []):
|
|
1011
1254
|
rel = item.get("path")
|
|
1012
1255
|
if rel:
|
|
1013
|
-
managed[rel] = item
|
|
1256
|
+
managed[rel] = item
|
|
1014
1257
|
except Exception:
|
|
1015
1258
|
managed = {}
|
|
1016
1259
|
|
|
@@ -1033,12 +1276,28 @@ def yaml_quote(value: str) -> str:
|
|
|
1033
1276
|
return json.dumps(value, ensure_ascii=False)
|
|
1034
1277
|
|
|
1035
1278
|
|
|
1036
|
-
def
|
|
1279
|
+
def existing_created_by(target: Path) -> str:
|
|
1280
|
+
if not target.exists():
|
|
1281
|
+
return version
|
|
1282
|
+
try:
|
|
1283
|
+
old = target.read_text(encoding="utf-8")
|
|
1284
|
+
except Exception:
|
|
1285
|
+
return version
|
|
1286
|
+
match = re.search(r"(?m)^ created_by:\s*(.+?)\s*$", old)
|
|
1287
|
+
if not match:
|
|
1288
|
+
return version
|
|
1289
|
+
return match.group(1).strip().strip('"')
|
|
1290
|
+
|
|
1291
|
+
|
|
1292
|
+
def markdown_with_marker(body: str, source_ref: str, target: Path) -> str:
|
|
1293
|
+
created_by = existing_created_by(target)
|
|
1037
1294
|
block = (
|
|
1038
1295
|
"agentic:\n"
|
|
1039
1296
|
" generated_by: agentic\n"
|
|
1040
1297
|
f" source: {yaml_quote(source_ref)}\n"
|
|
1041
1298
|
f" repository: {yaml_quote(repo)}\n"
|
|
1299
|
+
f" created_by: {yaml_quote(created_by)}\n"
|
|
1300
|
+
f" updated_by: {yaml_quote(version)}\n"
|
|
1042
1301
|
)
|
|
1043
1302
|
if body.startswith("---\n"):
|
|
1044
1303
|
end = body.find("\n---", 4)
|
|
@@ -1061,7 +1320,7 @@ def add_marker(file_path: Path, target: Path, source_ref: str) -> str:
|
|
|
1061
1320
|
text = file_path.read_text(encoding="utf-8")
|
|
1062
1321
|
suffix = target.suffix.lower()
|
|
1063
1322
|
if suffix == ".md":
|
|
1064
|
-
return markdown_with_marker(text, source_ref)
|
|
1323
|
+
return markdown_with_marker(text, source_ref, target)
|
|
1065
1324
|
if suffix == ".json":
|
|
1066
1325
|
data = json.loads(text)
|
|
1067
1326
|
if not isinstance(data, dict):
|
|
@@ -1087,15 +1346,27 @@ for file_path in sorted(p for p in src.rglob("*") if p.is_file()):
|
|
|
1087
1346
|
emit("WARN", f"Skipping unmanaged target on rerun: {project_rel}")
|
|
1088
1347
|
emit("SKIP", project_rel)
|
|
1089
1348
|
continue
|
|
1090
|
-
|
|
1349
|
+
managed_item = managed.get(project_rel, {})
|
|
1350
|
+
if managed_item.get("marker") == "config":
|
|
1351
|
+
continue
|
|
1352
|
+
expected_hash = managed_item.get("content_hash", "")
|
|
1091
1353
|
if target.exists() and expected_hash and sha256(target) != expected_hash:
|
|
1092
1354
|
emit("WARN", f"Skipping user-modified managed file: {project_rel}")
|
|
1093
1355
|
emit("SKIP", project_rel)
|
|
1094
1356
|
continue
|
|
1095
1357
|
|
|
1358
|
+
output = add_marker(file_path, target, source_ref)
|
|
1096
1359
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
1097
1360
|
emit("DIR", str(target.parent))
|
|
1098
|
-
target.
|
|
1361
|
+
if target.exists():
|
|
1362
|
+
try:
|
|
1363
|
+
if target.read_text(encoding="utf-8") == output:
|
|
1364
|
+
digest = sha256(target)
|
|
1365
|
+
emit("RECORD", f"{project_rel}|{source_ref}|{digest}|internal")
|
|
1366
|
+
continue
|
|
1367
|
+
except UnicodeDecodeError:
|
|
1368
|
+
pass
|
|
1369
|
+
target.write_text(output, encoding="utf-8")
|
|
1099
1370
|
digest = sha256(target)
|
|
1100
1371
|
emit("RECORD", f"{project_rel}|{source_ref}|{digest}|internal")
|
|
1101
1372
|
emit("COPIED", str(target))
|
|
@@ -1146,7 +1417,8 @@ write_json_file_with_agentic_metadata() {
|
|
|
1146
1417
|
|
|
1147
1418
|
can_write_managed_file "$dest" || return 0
|
|
1148
1419
|
ensure_dir "$(dirname -- "$dest")"
|
|
1149
|
-
|
|
1420
|
+
local write_status
|
|
1421
|
+
write_status="$(python3 - "$dest" "$source_ref" "$APP_REPO_LINK" "$CONTEXT7_API_KEY" "$python_body" "$(app_version_label)" <<'PY'
|
|
1150
1422
|
import json
|
|
1151
1423
|
import sys
|
|
1152
1424
|
from pathlib import Path
|
|
@@ -1156,11 +1428,15 @@ source_ref = sys.argv[2]
|
|
|
1156
1428
|
repo = sys.argv[3]
|
|
1157
1429
|
context7_api_key = sys.argv[4]
|
|
1158
1430
|
body = sys.argv[5]
|
|
1431
|
+
version = sys.argv[6]
|
|
1159
1432
|
|
|
1160
1433
|
data = {}
|
|
1434
|
+
created_by = version
|
|
1161
1435
|
if path.exists():
|
|
1162
1436
|
try:
|
|
1163
1437
|
data = json.loads(path.read_text(encoding="utf-8"))
|
|
1438
|
+
if isinstance(data, dict):
|
|
1439
|
+
created_by = data.get("_agentic", {}).get("created_by", version)
|
|
1164
1440
|
except Exception:
|
|
1165
1441
|
data = {}
|
|
1166
1442
|
if not isinstance(data, dict):
|
|
@@ -1172,9 +1448,28 @@ namespace = {
|
|
|
1172
1448
|
}
|
|
1173
1449
|
exec(body, namespace)
|
|
1174
1450
|
data = namespace["data"]
|
|
1451
|
+
metadata = data.setdefault("_agentic", {})
|
|
1452
|
+
metadata["generated_by"] = "agentic"
|
|
1453
|
+
metadata["repository"] = repo
|
|
1454
|
+
metadata["created_by"] = created_by
|
|
1455
|
+
metadata["updated_by"] = version
|
|
1456
|
+
output = json.dumps(data, indent=2, ensure_ascii=False) + "\n"
|
|
1457
|
+
if path.exists():
|
|
1458
|
+
try:
|
|
1459
|
+
if path.read_text(encoding="utf-8") == output:
|
|
1460
|
+
print("unchanged")
|
|
1461
|
+
raise SystemExit(0)
|
|
1462
|
+
except UnicodeDecodeError:
|
|
1463
|
+
pass
|
|
1175
1464
|
path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
1465
|
+
print("written")
|
|
1176
1466
|
PY
|
|
1177
|
-
|
|
1467
|
+
)"
|
|
1468
|
+
if [[ "$write_status" == "unchanged" ]]; then
|
|
1469
|
+
register_managed_file "$dest" "$source_ref" "internal" false
|
|
1470
|
+
else
|
|
1471
|
+
register_managed_file "$dest" "$source_ref" "internal"
|
|
1472
|
+
fi
|
|
1178
1473
|
}
|
|
1179
1474
|
|
|
1180
1475
|
write_json_config_file() {
|
|
@@ -1190,7 +1485,8 @@ write_json_config_file() {
|
|
|
1190
1485
|
|
|
1191
1486
|
can_write_managed_file "$dest" || return 0
|
|
1192
1487
|
ensure_dir "$(dirname -- "$dest")"
|
|
1193
|
-
|
|
1488
|
+
local write_status
|
|
1489
|
+
write_status="$(python3 - "$dest" "$CONTEXT7_API_KEY" "$python_body" <<'PY'
|
|
1194
1490
|
import json
|
|
1195
1491
|
import sys
|
|
1196
1492
|
from pathlib import Path
|
|
@@ -1214,9 +1510,62 @@ namespace = {
|
|
|
1214
1510
|
}
|
|
1215
1511
|
exec(body, namespace)
|
|
1216
1512
|
data = namespace["data"]
|
|
1217
|
-
|
|
1513
|
+
output = json.dumps(data, indent=2, ensure_ascii=False) + "\n"
|
|
1514
|
+
if path.exists():
|
|
1515
|
+
try:
|
|
1516
|
+
if path.read_text(encoding="utf-8") == output:
|
|
1517
|
+
print("unchanged")
|
|
1518
|
+
raise SystemExit(0)
|
|
1519
|
+
except UnicodeDecodeError:
|
|
1520
|
+
pass
|
|
1521
|
+
path.write_text(output, encoding="utf-8")
|
|
1522
|
+
print("written")
|
|
1218
1523
|
PY
|
|
1219
|
-
|
|
1524
|
+
)"
|
|
1525
|
+
if [[ "$write_status" == "unchanged" ]]; then
|
|
1526
|
+
register_managed_file "$dest" "$source_ref" "config" false
|
|
1527
|
+
else
|
|
1528
|
+
register_managed_file "$dest" "$source_ref" "config"
|
|
1529
|
+
fi
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
write_text_config_file() {
|
|
1533
|
+
local dest="$1"
|
|
1534
|
+
local source_ref="$2"
|
|
1535
|
+
local content="$3"
|
|
1536
|
+
|
|
1537
|
+
if [[ "$DRY_RUN" == true ]]; then
|
|
1538
|
+
log "DRY-RUN write text config file $dest"
|
|
1539
|
+
unique_append "$dest" COPIED_PATHS
|
|
1540
|
+
return
|
|
1541
|
+
fi
|
|
1542
|
+
|
|
1543
|
+
can_write_managed_file "$dest" || return 0
|
|
1544
|
+
ensure_dir "$(dirname -- "$dest")"
|
|
1545
|
+
|
|
1546
|
+
local write_status
|
|
1547
|
+
write_status="$(python3 - "$dest" "$content" <<'PY'
|
|
1548
|
+
import sys
|
|
1549
|
+
from pathlib import Path
|
|
1550
|
+
|
|
1551
|
+
path = Path(sys.argv[1])
|
|
1552
|
+
content = sys.argv[2]
|
|
1553
|
+
if path.exists():
|
|
1554
|
+
try:
|
|
1555
|
+
if path.read_text(encoding="utf-8") == content:
|
|
1556
|
+
print("unchanged")
|
|
1557
|
+
raise SystemExit(0)
|
|
1558
|
+
except UnicodeDecodeError:
|
|
1559
|
+
pass
|
|
1560
|
+
path.write_text(content, encoding="utf-8")
|
|
1561
|
+
print("written")
|
|
1562
|
+
PY
|
|
1563
|
+
)"
|
|
1564
|
+
if [[ "$write_status" == "unchanged" ]]; then
|
|
1565
|
+
register_managed_file "$dest" "$source_ref" "config" false
|
|
1566
|
+
else
|
|
1567
|
+
register_managed_file "$dest" "$source_ref" "config"
|
|
1568
|
+
fi
|
|
1220
1569
|
}
|
|
1221
1570
|
|
|
1222
1571
|
write_context7_opencode_config() {
|
|
@@ -1250,22 +1599,32 @@ if context7_api_key:
|
|
|
1250
1599
|
context7["headers"] = {"CONTEXT7_API_KEY": context7_api_key}
|
|
1251
1600
|
mcp["context7"] = context7
|
|
1252
1601
|
'
|
|
1253
|
-
|
|
1602
|
+
write_json_config_file "$dest" "generated:context7-opencode-legacy-config" "$body"
|
|
1254
1603
|
}
|
|
1255
1604
|
|
|
1256
1605
|
write_context7_codex_config() {
|
|
1257
1606
|
local dest="$PROJECT_DIR/.codex/config.toml"
|
|
1258
|
-
local
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1607
|
+
local body
|
|
1608
|
+
body="$(python3 - "$dest" "$CONTEXT7_API_KEY" <<'PY'
|
|
1609
|
+
import re
|
|
1610
|
+
import sys
|
|
1611
|
+
from pathlib import Path
|
|
1612
|
+
|
|
1613
|
+
path = Path(sys.argv[1])
|
|
1614
|
+
api_key = sys.argv[2]
|
|
1615
|
+
text = path.read_text(encoding="utf-8") if path.exists() else ""
|
|
1616
|
+
text = re.sub(r"(?ms)^\[mcp_servers\.context7\]\n.*?(?=^\[|\Z)", "", text).strip()
|
|
1617
|
+
block = '[mcp_servers.context7]\nurl = "https://mcp.context7.com/mcp"\n'
|
|
1618
|
+
if api_key:
|
|
1619
|
+
escaped = api_key.replace("\\", "\\\\").replace('"', '\\"')
|
|
1620
|
+
block += f'http_headers = {{ "CONTEXT7_API_KEY" = "{escaped}" }}\n'
|
|
1621
|
+
if text:
|
|
1622
|
+
print(block + "\n" + text.rstrip() + "\n", end="")
|
|
1623
|
+
else:
|
|
1624
|
+
print(block, end="")
|
|
1625
|
+
PY
|
|
1626
|
+
)"
|
|
1627
|
+
write_text_config_file "$dest" "generated:context7-codex-config" "$body"
|
|
1269
1628
|
}
|
|
1270
1629
|
|
|
1271
1630
|
write_context7_claude_config() {
|
|
@@ -1350,7 +1709,7 @@ write_mempalace_opencode_config() {
|
|
|
1350
1709
|
local body
|
|
1351
1710
|
body='
|
|
1352
1711
|
mcp = data.setdefault("mcp", {})
|
|
1353
|
-
mcp["mempalace"] = {"type": "local", "command": ["mempalace-mcp"
|
|
1712
|
+
mcp["mempalace"] = {"type": "local", "command": ["mempalace-mcp"]}
|
|
1354
1713
|
'
|
|
1355
1714
|
write_json_config_file "$dest" "generated:mempalace-opencode-config" "$body"
|
|
1356
1715
|
}
|
|
@@ -1359,18 +1718,21 @@ write_mempalace_codex_config() {
|
|
|
1359
1718
|
local dest="$PROJECT_DIR/.codex/config.toml"
|
|
1360
1719
|
local body
|
|
1361
1720
|
body="$(python3 - "$dest" <<'PYCODE'
|
|
1362
|
-
import
|
|
1721
|
+
import re
|
|
1722
|
+
import pathlib
|
|
1723
|
+
import sys
|
|
1724
|
+
|
|
1363
1725
|
path = pathlib.Path(sys.argv[1])
|
|
1364
1726
|
text = path.read_text(encoding='utf-8') if path.exists() else ''
|
|
1365
|
-
block = "[mcp_servers.mempalace]\ncommand = \"mempalace-mcp\"\
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
print(
|
|
1727
|
+
block = "[mcp_servers.mempalace]\ncommand = \"mempalace-mcp\"\n"
|
|
1728
|
+
text = re.sub(r"(?ms)^\[mcp_servers\.mempalace\]\n.*?(?=^\[|\Z)", "", text).strip()
|
|
1729
|
+
if text:
|
|
1730
|
+
print(text.rstrip() + "\n\n" + block, end="")
|
|
1731
|
+
else:
|
|
1732
|
+
print(block, end="")
|
|
1371
1733
|
PYCODE
|
|
1372
1734
|
)"
|
|
1373
|
-
|
|
1735
|
+
write_text_config_file "$dest" "generated:mempalace-codex-config" "$body"
|
|
1374
1736
|
}
|
|
1375
1737
|
|
|
1376
1738
|
write_mempalace_generic_json_config() {
|
|
@@ -1379,26 +1741,23 @@ write_mempalace_generic_json_config() {
|
|
|
1379
1741
|
local body
|
|
1380
1742
|
body='
|
|
1381
1743
|
servers = data.setdefault("mcpServers", {})
|
|
1382
|
-
servers["mempalace"] = {"command": "mempalace-mcp"
|
|
1744
|
+
servers["mempalace"] = {"command": "mempalace-mcp"}
|
|
1383
1745
|
'
|
|
1384
1746
|
write_json_config_file "$dest" "$marker" "$body"
|
|
1385
1747
|
}
|
|
1386
1748
|
|
|
1387
1749
|
print_mempalace_project_setup_instructions() {
|
|
1388
1750
|
log "MemPalace setup instructions for target project: $PROJECT_DIR"
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
5) Verify in your IDE/agent that MemPalace MCP tools are connected.
|
|
1400
|
-
Note: Ollama at localhost:11434 is optional; MemPalace can run heuristics-only without it.
|
|
1401
|
-
EOF
|
|
1751
|
+
out "1) Ensure Python is installed and available in PATH."
|
|
1752
|
+
out "2) Install MemPalace:"
|
|
1753
|
+
out " pip install mempalace"
|
|
1754
|
+
out "3) Initialize project-local MemPalace cache:"
|
|
1755
|
+
out " mempalace init \"$PROJECT_DIR\" --yes --auto-mine"
|
|
1756
|
+
out "4) Index existing project memory:"
|
|
1757
|
+
out " # optional if --auto-mine was skipped"
|
|
1758
|
+
out " mempalace mine \"$PROJECT_DIR\""
|
|
1759
|
+
out "5) Verify in your IDE/agent that MemPalace MCP tools are connected."
|
|
1760
|
+
out "Note: Ollama at localhost:11434 is optional; MemPalace can run heuristics-only without it."
|
|
1402
1761
|
}
|
|
1403
1762
|
|
|
1404
1763
|
setup_mempalace_for_agentic_opencode() {
|
|
@@ -1413,33 +1772,33 @@ setup_mempalace_for_agentic_opencode() {
|
|
|
1413
1772
|
log "$step_prefix [1/4] Python check passed"
|
|
1414
1773
|
|
|
1415
1774
|
log "$step_prefix [2/4] Checking pip availability"
|
|
1416
|
-
|
|
1775
|
+
local pip_bin
|
|
1776
|
+
if ! pip_bin="$(pip_command)"; then
|
|
1417
1777
|
warn "pip is not available. Install pip for Python 3, then run: pip install mempalace"
|
|
1418
1778
|
return 1
|
|
1419
1779
|
fi
|
|
1420
1780
|
log "$step_prefix [2/4] pip check passed"
|
|
1421
1781
|
|
|
1422
1782
|
log "$step_prefix [3/4] Installing mempalace package"
|
|
1423
|
-
if
|
|
1424
|
-
log "MemPalace package installed via '
|
|
1783
|
+
if $pip_bin install mempalace >/dev/null 2>&1; then
|
|
1784
|
+
log "MemPalace package installed via '$pip_bin install mempalace'"
|
|
1425
1785
|
else
|
|
1426
1786
|
warn "Unable to auto-install mempalace via pip; continuing with manual setup instructions"
|
|
1427
1787
|
print_mempalace_project_setup_instructions
|
|
1428
1788
|
return 1
|
|
1429
1789
|
fi
|
|
1430
1790
|
|
|
1431
|
-
log "$step_prefix [4/4] Initializing project memory at $PROJECT_DIR
|
|
1791
|
+
log "$step_prefix [4/4] Initializing project memory at $PROJECT_DIR"
|
|
1432
1792
|
if command -v mempalace >/dev/null 2>&1; then
|
|
1433
|
-
|
|
1434
|
-
if mempalace init "$PROJECT_DIR/.mempalace" --yes --auto-mine >/dev/null 2>&1; then
|
|
1793
|
+
if mempalace init "$PROJECT_DIR" --yes --auto-mine >/dev/null 2>&1; then
|
|
1435
1794
|
log "MemPalace init completed"
|
|
1436
1795
|
else
|
|
1437
|
-
warn "Failed: mempalace init \"$PROJECT_DIR
|
|
1796
|
+
warn "Failed: mempalace init \"$PROJECT_DIR\" --yes --auto-mine"
|
|
1438
1797
|
fi
|
|
1439
|
-
if mempalace mine "$PROJECT_DIR
|
|
1798
|
+
if mempalace mine "$PROJECT_DIR" >/dev/null 2>&1; then
|
|
1440
1799
|
log "MemPalace mine completed"
|
|
1441
1800
|
else
|
|
1442
|
-
warn "Failed: mempalace mine \"$PROJECT_DIR
|
|
1801
|
+
warn "Failed: mempalace mine \"$PROJECT_DIR\""
|
|
1443
1802
|
fi
|
|
1444
1803
|
log "$step_prefix [4/4] Initialization step finished"
|
|
1445
1804
|
else
|
|
@@ -1455,6 +1814,7 @@ configure_mempalace_if_needed() {
|
|
|
1455
1814
|
&& ! selected_agent_os_contains "claude" \
|
|
1456
1815
|
&& ! selected_agent_os_contains "cursor" \
|
|
1457
1816
|
&& ! selected_agent_os_contains "gemini" \
|
|
1817
|
+
&& ! selected_agent_os_contains "kilocode" \
|
|
1458
1818
|
&& ! selected_agent_os_contains "antigravity"; then
|
|
1459
1819
|
return
|
|
1460
1820
|
fi
|
|
@@ -1464,8 +1824,8 @@ configure_mempalace_if_needed() {
|
|
|
1464
1824
|
enable_mempalace="$(trim "${AGENTIC_ENABLE_MEMPALACE}")"
|
|
1465
1825
|
elif is_interactive_terminal && [[ -z "${AGENTIC_TEST_SOURCE_AGENTIC:-}" ]]; then
|
|
1466
1826
|
read -r -p "Enable MemPalace MCP memory integration? [y/N]: " enable_mempalace
|
|
1467
|
-
enable_mempalace="$(trim "${enable_mempalace:-
|
|
1468
|
-
if [[ -z "$enable_mempalace" ]]; then enable_mempalace="
|
|
1827
|
+
enable_mempalace="$(trim "${enable_mempalace:-n}")"
|
|
1828
|
+
if [[ -z "$enable_mempalace" ]]; then enable_mempalace="n"; fi
|
|
1469
1829
|
fi
|
|
1470
1830
|
if [[ "$enable_mempalace" =~ ^[Nn]$ ]]; then
|
|
1471
1831
|
log "Skipped MemPalace MCP configuration"
|
|
@@ -1479,11 +1839,7 @@ configure_mempalace_if_needed() {
|
|
|
1479
1839
|
fi
|
|
1480
1840
|
|
|
1481
1841
|
if command -v mempalace-mcp >/dev/null 2>&1; then
|
|
1482
|
-
|
|
1483
|
-
log "MemPalace MCP runtime check succeeded via 'mempalace-mcp'"
|
|
1484
|
-
else
|
|
1485
|
-
warn "MemPalace MCP runtime check failed; continuing without runtime validation"
|
|
1486
|
-
fi
|
|
1842
|
+
log "MemPalace MCP binary found: mempalace-mcp"
|
|
1487
1843
|
else
|
|
1488
1844
|
warn "mempalace-mcp is unavailable; install/repair MemPalace and re-run setup"
|
|
1489
1845
|
fi
|
|
@@ -1502,6 +1858,9 @@ configure_mempalace_if_needed() {
|
|
|
1502
1858
|
if selected_agent_os_contains "gemini"; then
|
|
1503
1859
|
write_mempalace_generic_json_config "$PROJECT_DIR/.gemini/settings.json" "generated:mempalace-gemini-config"
|
|
1504
1860
|
fi
|
|
1861
|
+
if selected_agent_os_contains "kilocode"; then
|
|
1862
|
+
write_mempalace_generic_json_config "$PROJECT_DIR/.kilocode/mcp.json" "generated:mempalace-kilocode-config"
|
|
1863
|
+
fi
|
|
1505
1864
|
if selected_agent_os_contains "antigravity"; then
|
|
1506
1865
|
write_mempalace_generic_json_config "$HOME/.gemini/antigravity/mcp_config.json" "generated:mempalace-antigravity-config"
|
|
1507
1866
|
fi
|
|
@@ -1518,10 +1877,17 @@ configure_context7_if_needed() {
|
|
|
1518
1877
|
return
|
|
1519
1878
|
fi
|
|
1520
1879
|
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
read -r -p "Enable Context7 MCP configuration? [y/N]: " enable_context7
|
|
1880
|
+
local enable_context7="${AGENTIC_ENABLE_CONTEXT7:-}"
|
|
1881
|
+
if [[ -n "$enable_context7" ]]; then
|
|
1524
1882
|
enable_context7="$(trim "$enable_context7")"
|
|
1883
|
+
fi
|
|
1884
|
+
|
|
1885
|
+
if is_interactive_terminal; then
|
|
1886
|
+
local answer
|
|
1887
|
+
if [[ -z "$enable_context7" ]]; then
|
|
1888
|
+
read -r -p "Enable Context7 MCP configuration? [y/N]: " enable_context7
|
|
1889
|
+
enable_context7="$(trim "$enable_context7")"
|
|
1890
|
+
fi
|
|
1525
1891
|
if [[ ! "$enable_context7" =~ ^[Yy]$ ]]; then
|
|
1526
1892
|
log "Context7 MCP configuration disabled"
|
|
1527
1893
|
return
|
|
@@ -1603,9 +1969,34 @@ configure_opencode_plugins_if_needed() {
|
|
|
1603
1969
|
return
|
|
1604
1970
|
fi
|
|
1605
1971
|
|
|
1606
|
-
local
|
|
1607
|
-
|
|
1608
|
-
|
|
1972
|
+
local plugin_options=("telegram-opencode-notifier" "llm-quota-checker")
|
|
1973
|
+
local selected_plugins=()
|
|
1974
|
+
local use_fzf_plugins=false
|
|
1975
|
+
if fzf_available; then
|
|
1976
|
+
use_fzf_plugins=true
|
|
1977
|
+
elif ensure_fzf_or_fallback; then
|
|
1978
|
+
use_fzf_plugins=true
|
|
1979
|
+
fi
|
|
1980
|
+
|
|
1981
|
+
if [[ "$use_fzf_plugins" == true ]]; then
|
|
1982
|
+
readlines selected_plugins < <(choose_multi_fzf_strict "Select optional OpenCode plugin(s):" "${plugin_options[@]}")
|
|
1983
|
+
else
|
|
1984
|
+
local selected_plugins_output
|
|
1985
|
+
selected_plugins_output="$(choose_multi_by_index "Select optional OpenCode plugin(s):" "${plugin_options[@]}")"
|
|
1986
|
+
readlines selected_plugins <<< "$selected_plugins_output"
|
|
1987
|
+
fi
|
|
1988
|
+
|
|
1989
|
+
local enable_telegram="n" telegram_token telegram_chat enable_model_checker="n"
|
|
1990
|
+
local selected_plugin
|
|
1991
|
+
for selected_plugin in "${selected_plugins[@]}"; do
|
|
1992
|
+
selected_plugin="$(trim "$selected_plugin")"
|
|
1993
|
+
[[ -z "$selected_plugin" ]] && continue
|
|
1994
|
+
case "$selected_plugin" in
|
|
1995
|
+
telegram-opencode-notifier) enable_telegram="y" ;;
|
|
1996
|
+
llm-quota-checker) enable_model_checker="y" ;;
|
|
1997
|
+
esac
|
|
1998
|
+
done
|
|
1999
|
+
|
|
1609
2000
|
telegram_token=""
|
|
1610
2001
|
telegram_chat=""
|
|
1611
2002
|
if [[ "$enable_telegram" =~ ^[Yy]$ ]]; then
|
|
@@ -1615,9 +2006,6 @@ configure_opencode_plugins_if_needed() {
|
|
|
1615
2006
|
telegram_chat="$(trim "$telegram_chat")"
|
|
1616
2007
|
fi
|
|
1617
2008
|
|
|
1618
|
-
read -r -p "Enable OpenCode model checker plugin? [y/N]: " enable_model_checker
|
|
1619
|
-
enable_model_checker="$(trim "$enable_model_checker")"
|
|
1620
|
-
|
|
1621
2009
|
python3 - "$OPENCODE_PLUGIN_CONFIG_FILE" "$telegram_token" "$telegram_chat" "$enable_model_checker" <<'PY'
|
|
1622
2010
|
import json
|
|
1623
2011
|
import sys
|
|
@@ -1744,7 +2132,7 @@ build_header() {
|
|
|
1744
2132
|
{
|
|
1745
2133
|
echo "# Agentic Project Guidelines"
|
|
1746
2134
|
echo
|
|
1747
|
-
echo "Generated by $SCRIPT_NAME
|
|
2135
|
+
echo "Generated by $SCRIPT_NAME."
|
|
1748
2136
|
echo
|
|
1749
2137
|
echo "## Installation Context"
|
|
1750
2138
|
echo "- Agent OS targets: ${SELECTED_AGENT_OS[*]}"
|
|
@@ -1911,37 +2299,47 @@ validate_inputs() {
|
|
|
1911
2299
|
}
|
|
1912
2300
|
|
|
1913
2301
|
print_report() {
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
2302
|
+
out
|
|
2303
|
+
out "=== Installation report ===" "$COLOR_HEADER"
|
|
2304
|
+
out "Agentic version: $(app_version_label)"
|
|
2305
|
+
out "Project dir: $PROJECT_DIR"
|
|
2306
|
+
out "Knowledge base repo: $REPO_ROOT"
|
|
2307
|
+
out "Config file: $APP_CONFIG_FILE"
|
|
2308
|
+
out "Agent OS targets: ${SELECTED_AGENT_OS[*]}"
|
|
2309
|
+
out "Areas: ${SELECTED_AREAS[*]}"
|
|
2310
|
+
out "Specializations: ${SELECTED_SPECS[*]}"
|
|
2311
|
+
|
|
2312
|
+
out
|
|
2313
|
+
out "Created directories:"
|
|
1925
2314
|
if [[ "${#CREATED_PATHS[@]}" -eq 0 ]]; then
|
|
1926
|
-
|
|
2315
|
+
out "- (none)"
|
|
1927
2316
|
else
|
|
1928
|
-
|
|
2317
|
+
local created_path
|
|
2318
|
+
for created_path in "${CREATED_PATHS[@]}"; do
|
|
2319
|
+
out "- $created_path"
|
|
2320
|
+
done
|
|
1929
2321
|
fi
|
|
1930
2322
|
|
|
1931
|
-
|
|
1932
|
-
|
|
2323
|
+
out
|
|
2324
|
+
out "Copied/generated paths:"
|
|
1933
2325
|
if [[ "${#COPIED_PATHS[@]}" -eq 0 ]]; then
|
|
1934
|
-
|
|
2326
|
+
out "- (none)"
|
|
1935
2327
|
else
|
|
1936
|
-
|
|
2328
|
+
local copied_path
|
|
2329
|
+
for copied_path in "${COPIED_PATHS[@]}"; do
|
|
2330
|
+
out "- $copied_path"
|
|
2331
|
+
done
|
|
1937
2332
|
fi
|
|
1938
2333
|
|
|
1939
|
-
|
|
1940
|
-
|
|
2334
|
+
out
|
|
2335
|
+
out "Warnings:"
|
|
1941
2336
|
if [[ "${#WARNINGS[@]}" -eq 0 ]]; then
|
|
1942
|
-
|
|
2337
|
+
out "- (none)"
|
|
1943
2338
|
else
|
|
1944
|
-
|
|
2339
|
+
local warning
|
|
2340
|
+
for warning in "${WARNINGS[@]}"; do
|
|
2341
|
+
out "- $warning"
|
|
2342
|
+
done
|
|
1945
2343
|
fi
|
|
1946
2344
|
}
|
|
1947
2345
|
|
|
@@ -2004,14 +2402,201 @@ print_missing_agent_binary_guides() {
|
|
|
2004
2402
|
return
|
|
2005
2403
|
fi
|
|
2006
2404
|
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2405
|
+
out
|
|
2406
|
+
out "=== Agent binary setup recommendations ===" "$COLOR_HEADER"
|
|
2407
|
+
local missing_line
|
|
2408
|
+
for missing_line in "${missing_lines[@]}"; do
|
|
2409
|
+
out "$missing_line"
|
|
2410
|
+
done
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
changelog_file_path() {
|
|
2414
|
+
local candidate
|
|
2415
|
+
for candidate in "$SCRIPT_DIR/CHANGELOG.md" "$REPO_ROOT/CHANGELOG.md" "$APP_REPO_DIR/CHANGELOG.md"; do
|
|
2416
|
+
[[ -f "$candidate" ]] || continue
|
|
2417
|
+
printf '%s\n' "$candidate"
|
|
2418
|
+
return 0
|
|
2419
|
+
done
|
|
2420
|
+
return 1
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
print_current_changelog() {
|
|
2424
|
+
local changelog version
|
|
2425
|
+
changelog="$(changelog_file_path || true)"
|
|
2426
|
+
version="$(app_version_label)"
|
|
2427
|
+
if [[ -z "$changelog" ]]; then
|
|
2428
|
+
warn "CHANGELOG.md not found; skipping changelog output"
|
|
2429
|
+
return
|
|
2430
|
+
fi
|
|
2431
|
+
|
|
2432
|
+
local section_file
|
|
2433
|
+
section_file="$(mktemp "${TMPDIR:-/tmp}/agentic-changelog.XXXXXX")"
|
|
2434
|
+
awk -v wanted="## $version" '
|
|
2435
|
+
$0 == wanted { in_section = 1; print; next }
|
|
2436
|
+
in_section && /^## / { exit }
|
|
2437
|
+
in_section { print }
|
|
2438
|
+
' "$changelog" > "$section_file"
|
|
2439
|
+
|
|
2440
|
+
if [[ ! -s "$section_file" ]]; then
|
|
2441
|
+
rm -f "$section_file"
|
|
2442
|
+
warn "No changelog section found for $version"
|
|
2443
|
+
return
|
|
2444
|
+
fi
|
|
2445
|
+
|
|
2446
|
+
out
|
|
2447
|
+
out "=== Changelog $version ===" "$COLOR_HEADER"
|
|
2448
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
2449
|
+
if [[ "$line" == "## $version" ]]; then
|
|
2450
|
+
continue
|
|
2451
|
+
fi
|
|
2452
|
+
out "$line"
|
|
2453
|
+
done < "$section_file"
|
|
2454
|
+
rm -f "$section_file"
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
doctor_agent_supported() {
|
|
2458
|
+
case "$1" in
|
|
2459
|
+
codex|opencode|claude|gemini) return 0 ;;
|
|
2460
|
+
*) return 1 ;;
|
|
2461
|
+
esac
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
doctor_enabled() {
|
|
2465
|
+
[[ "$DRY_RUN" != true ]] || return 1
|
|
2466
|
+
[[ "${AGENTIC_DOCTOR:-1}" != "0" ]] || return 1
|
|
2467
|
+
[[ "${AGENTIC_TEST_SOURCE_AGENTIC:-}" != "1" ]] || return 1
|
|
2468
|
+
return 0
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
doctor_prompt() {
|
|
2472
|
+
printf '%s\n' "/develop-feature напиши hello world python"
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
doctor_output_has_fatal_patterns() {
|
|
2476
|
+
local output_file="$1"
|
|
2477
|
+
grep -Eiq 'MCP.*(error|failed|failure|connection|connect|startup)|plugin.*(error|failed|failure)|auth.*(required|failed)|login required|permission.*(denied|required)|SyntaxError|Traceback|Invalid regular expression flags|An unexpected critical error occurred|FatalError|RuntimeError|EPERM|EACCES|panic:' "$output_file"
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
doctor_copy_project() {
|
|
2481
|
+
local dest="$1"
|
|
2482
|
+
mkdir -p "$dest"
|
|
2483
|
+
if [[ -d "$PROJECT_DIR" ]]; then
|
|
2484
|
+
cp -R "$PROJECT_DIR/." "$dest/"
|
|
2485
|
+
fi
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
run_doctor_command() {
|
|
2489
|
+
local agent_os="$1"
|
|
2490
|
+
local work_dir="$2"
|
|
2491
|
+
local output_file="$3"
|
|
2492
|
+
local prompt
|
|
2493
|
+
prompt="$(doctor_prompt)"
|
|
2494
|
+
|
|
2495
|
+
case "$agent_os" in
|
|
2496
|
+
codex)
|
|
2497
|
+
codex exec --skip-git-repo-check --full-auto -C "$work_dir" "$prompt" >"$output_file" 2>&1
|
|
2498
|
+
;;
|
|
2499
|
+
opencode)
|
|
2500
|
+
opencode run --dir "$work_dir" --dangerously-skip-permissions --format json --command develop-feature "напиши hello world python" >"$output_file" 2>&1
|
|
2501
|
+
;;
|
|
2502
|
+
claude)
|
|
2503
|
+
(cd "$work_dir" && claude -p --permission-mode bypassPermissions --output-format stream-json "$prompt") >"$output_file" 2>&1
|
|
2504
|
+
;;
|
|
2505
|
+
gemini)
|
|
2506
|
+
(cd "$work_dir" && gemini --prompt "$prompt") >"$output_file" 2>&1
|
|
2507
|
+
;;
|
|
2508
|
+
*)
|
|
2509
|
+
return 2
|
|
2510
|
+
;;
|
|
2511
|
+
esac
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
run_doctor_for_agent() {
|
|
2515
|
+
local agent_os="$1"
|
|
2516
|
+
local doctor_root="$2"
|
|
2517
|
+
local binary_name
|
|
2518
|
+
binary_name="$(get_agent_binary_name "$agent_os")"
|
|
2519
|
+
|
|
2520
|
+
if [[ -z "$binary_name" ]] || ! command -v "$binary_name" >/dev/null 2>&1; then
|
|
2521
|
+
out "❌ $agent_os: binary '$binary_name' is not installed"
|
|
2522
|
+
return 1
|
|
2523
|
+
fi
|
|
2524
|
+
|
|
2525
|
+
local work_dir output_file status
|
|
2526
|
+
work_dir="$doctor_root/$agent_os"
|
|
2527
|
+
output_file="$doctor_root/$agent_os.log"
|
|
2528
|
+
doctor_copy_project "$work_dir"
|
|
2529
|
+
|
|
2530
|
+
set +e
|
|
2531
|
+
run_doctor_command "$agent_os" "$work_dir" "$output_file"
|
|
2532
|
+
status=$?
|
|
2533
|
+
set -e
|
|
2534
|
+
|
|
2535
|
+
log_file_block "doctor $agent_os" "$output_file"
|
|
2536
|
+
|
|
2537
|
+
if [[ "$status" -ne 0 ]]; then
|
|
2538
|
+
out "❌ $agent_os: /develop-feature smoke failed (exit $status, log: $output_file)"
|
|
2539
|
+
return 1
|
|
2540
|
+
fi
|
|
2541
|
+
|
|
2542
|
+
if doctor_output_has_fatal_patterns "$output_file"; then
|
|
2543
|
+
out "❌ $agent_os: /develop-feature smoke reported integration errors (log: $output_file)"
|
|
2544
|
+
return 1
|
|
2545
|
+
fi
|
|
2546
|
+
|
|
2547
|
+
out "✅ $agent_os: /develop-feature smoke passed"
|
|
2548
|
+
return 0
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
run_agentic_doctor() {
|
|
2552
|
+
if ! doctor_enabled; then
|
|
2553
|
+
log "Agentic doctor skipped"
|
|
2554
|
+
return
|
|
2555
|
+
fi
|
|
2556
|
+
|
|
2557
|
+
local selected_doctor_agents=()
|
|
2558
|
+
local agent_os
|
|
2559
|
+
for agent_os in "${SELECTED_AGENT_OS[@]}"; do
|
|
2560
|
+
if doctor_agent_supported "$agent_os"; then
|
|
2561
|
+
selected_doctor_agents+=("$agent_os")
|
|
2562
|
+
fi
|
|
2563
|
+
done
|
|
2564
|
+
|
|
2565
|
+
if [[ "${#selected_doctor_agents[@]}" -eq 0 ]]; then
|
|
2566
|
+
log "Agentic doctor skipped: no supported real agentos selected"
|
|
2567
|
+
return
|
|
2568
|
+
fi
|
|
2569
|
+
|
|
2570
|
+
local doctor_root
|
|
2571
|
+
doctor_root="$(mktemp -d "${TMPDIR:-/tmp}/agentic-doctor.XXXXXX")"
|
|
2572
|
+
out
|
|
2573
|
+
out "=== Agentic doctor ===" "$COLOR_HEADER"
|
|
2574
|
+
out "Doctor temp root: $doctor_root"
|
|
2575
|
+
|
|
2576
|
+
local failures=0
|
|
2577
|
+
for agent_os in "${selected_doctor_agents[@]}"; do
|
|
2578
|
+
if ! run_doctor_for_agent "$agent_os" "$doctor_root"; then
|
|
2579
|
+
failures=$((failures + 1))
|
|
2580
|
+
fi
|
|
2581
|
+
done
|
|
2582
|
+
|
|
2583
|
+
if [[ "$AGENTIC_DOCTOR_KEEP_TMP" == "1" || "$failures" -gt 0 ]]; then
|
|
2584
|
+
out "Doctor temp root kept: $doctor_root"
|
|
2585
|
+
else
|
|
2586
|
+
rm -rf "$doctor_root"
|
|
2587
|
+
fi
|
|
2588
|
+
|
|
2589
|
+
if [[ "$failures" -gt 0 ]]; then
|
|
2590
|
+
warn "Agentic doctor completed with $failures failing check(s)"
|
|
2591
|
+
else
|
|
2592
|
+
log "Agentic doctor completed successfully"
|
|
2593
|
+
fi
|
|
2010
2594
|
}
|
|
2011
2595
|
|
|
2012
2596
|
run_install() {
|
|
2597
|
+
init_run_logging
|
|
2013
2598
|
ensure_repo_layout
|
|
2014
|
-
|
|
2599
|
+
ensure_agentic_runtime_requirements
|
|
2015
2600
|
normalize_selected_agent_os
|
|
2016
2601
|
validate_inputs
|
|
2017
2602
|
|
|
@@ -2025,6 +2610,9 @@ run_install() {
|
|
|
2025
2610
|
write_agentic_manifest "$PROJECT_DIR"
|
|
2026
2611
|
print_report
|
|
2027
2612
|
print_missing_agent_binary_guides
|
|
2613
|
+
print_current_changelog
|
|
2614
|
+
run_agentic_doctor
|
|
2615
|
+
out "Agentic log file: $RUN_LOG_FILE"
|
|
2028
2616
|
}
|
|
2029
2617
|
|
|
2030
2618
|
ascii_banner() {
|
|
@@ -2318,6 +2906,28 @@ choose_single_fzf() {
|
|
|
2318
2906
|
printf '%s\n' "${options[@]}" | fzf "${fzf_args[@]}"
|
|
2319
2907
|
}
|
|
2320
2908
|
|
|
2909
|
+
|
|
2910
|
+
choose_multi_fzf_strict() {
|
|
2911
|
+
local prompt="$1"
|
|
2912
|
+
shift
|
|
2913
|
+
local options=("$@")
|
|
2914
|
+
|
|
2915
|
+
if [[ "${#options[@]}" -eq 0 ]]; then
|
|
2916
|
+
return
|
|
2917
|
+
fi
|
|
2918
|
+
|
|
2919
|
+
local sentinel="<none>"
|
|
2920
|
+
local picked=()
|
|
2921
|
+
readlines picked < <(choose_multi_fzf "$prompt" "$sentinel" "${options[@]}")
|
|
2922
|
+
|
|
2923
|
+
local item
|
|
2924
|
+
for item in "${picked[@]}"; do
|
|
2925
|
+
item="$(trim "$item")"
|
|
2926
|
+
[[ -z "$item" || "$item" == "$sentinel" ]] && continue
|
|
2927
|
+
printf '%s\n' "$item"
|
|
2928
|
+
done
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2321
2931
|
choose_multi_fzf() {
|
|
2322
2932
|
local prompt="$1"
|
|
2323
2933
|
shift
|
|
@@ -2373,11 +2983,13 @@ run_tui() {
|
|
|
2373
2983
|
exit 1
|
|
2374
2984
|
fi
|
|
2375
2985
|
|
|
2986
|
+
ensure_agentic_runtime_requirements
|
|
2987
|
+
|
|
2376
2988
|
pick_theme_if_needed
|
|
2377
2989
|
set_theme_colors
|
|
2378
2990
|
|
|
2379
2991
|
ascii_banner
|
|
2380
|
-
echo "${COLOR_HEADER}$APP_TUI_TITLE${COLOR_RESET}"
|
|
2992
|
+
echo "${COLOR_HEADER}$APP_TUI_TITLE $(app_version_label)${COLOR_RESET}"
|
|
2381
2993
|
echo "${COLOR_DIM}Theme: $THEME (resolved: $ACTIVE_THEME)${COLOR_RESET}"
|
|
2382
2994
|
echo
|
|
2383
2995
|
|
|
@@ -2413,6 +3025,26 @@ run_tui() {
|
|
|
2413
3025
|
SELECTED_AGENT_OS=("${picked_agent_os[@]}")
|
|
2414
3026
|
fi
|
|
2415
3027
|
|
|
3028
|
+
local mcp_options=("context7" "mempalace")
|
|
3029
|
+
local picked_mcps=()
|
|
3030
|
+
if [[ "$use_fzf" == true ]]; then
|
|
3031
|
+
readlines picked_mcps < <(choose_multi_fzf_strict "Select optional MCP integration(s):" "${mcp_options[@]}")
|
|
3032
|
+
else
|
|
3033
|
+
local picked_mcps_output
|
|
3034
|
+
picked_mcps_output="$(choose_multi_by_index "Select optional MCP integration(s):" "<none>" "${mcp_options[@]}")"
|
|
3035
|
+
readlines picked_mcps <<< "$picked_mcps_output"
|
|
3036
|
+
fi
|
|
3037
|
+
|
|
3038
|
+
AGENTIC_ENABLE_CONTEXT7="n"
|
|
3039
|
+
AGENTIC_ENABLE_MEMPALACE="n"
|
|
3040
|
+
local picked_mcp
|
|
3041
|
+
for picked_mcp in "${picked_mcps[@]}"; do
|
|
3042
|
+
case "$picked_mcp" in
|
|
3043
|
+
context7) AGENTIC_ENABLE_CONTEXT7="y" ;;
|
|
3044
|
+
mempalace) AGENTIC_ENABLE_MEMPALACE="y" ;;
|
|
3045
|
+
esac
|
|
3046
|
+
done
|
|
3047
|
+
|
|
2416
3048
|
local areas=()
|
|
2417
3049
|
readlines areas < <(list_areas)
|
|
2418
3050
|
|
|
@@ -2704,6 +3336,10 @@ case "$COMMAND" in
|
|
|
2704
3336
|
DRY_RUN=true
|
|
2705
3337
|
shift
|
|
2706
3338
|
;;
|
|
3339
|
+
--no-doctor)
|
|
3340
|
+
AGENTIC_DOCTOR=0
|
|
3341
|
+
shift
|
|
3342
|
+
;;
|
|
2707
3343
|
-h|--help)
|
|
2708
3344
|
usage
|
|
2709
3345
|
exit 0
|
|
@@ -2745,6 +3381,10 @@ case "$COMMAND" in
|
|
|
2745
3381
|
DRY_RUN=true
|
|
2746
3382
|
shift
|
|
2747
3383
|
;;
|
|
3384
|
+
--no-doctor)
|
|
3385
|
+
AGENTIC_DOCTOR=0
|
|
3386
|
+
shift
|
|
3387
|
+
;;
|
|
2748
3388
|
-h|--help)
|
|
2749
3389
|
usage
|
|
2750
3390
|
exit 0
|
|
@@ -2831,6 +3471,10 @@ case "$COMMAND" in
|
|
|
2831
3471
|
usage
|
|
2832
3472
|
;;
|
|
2833
3473
|
|
|
3474
|
+
-V|--version|version)
|
|
3475
|
+
app_version_label
|
|
3476
|
+
;;
|
|
3477
|
+
|
|
2834
3478
|
*)
|
|
2835
3479
|
usage
|
|
2836
3480
|
exit 1
|