@delorenj/pjangler 1.3.0 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +72 -0
  2. package/dist/index.js +4510 -2485
  3. package/dist/mcp-server.js +4166 -1959
  4. package/dist/prompt.js +404 -0
  5. package/package.json +7 -5
  6. package/templates/commonproject/copier.yml +6 -1
  7. package/templates/commonproject/template/.mise/scripts/provision-packs.py +14 -50
  8. package/templates/hermes-agent/copier.yml +8 -11
  9. package/templates/hermes-agent/template/.runtime-scaffold/.gitignore.jinja +44 -0
  10. package/templates/hermes-agent/template/.scripts/01-config.sh +9 -0
  11. package/templates/hermes-agent/template/.scripts/05-fleet-env.sh +18 -28
  12. package/templates/hermes-agent/template/.scripts/10-hermes-profile.sh +68 -4
  13. package/templates/hermes-agent/template/.scripts/42-ticket-provider.sh +18 -1
  14. package/templates/hermes-agent/template/.scripts/70-systemd.sh +73 -43
  15. package/templates/hermes-agent/template/.scripts/80-registry.sh +6 -0
  16. package/templates/hermes-agent/template/.scripts/_lib.sh +62 -6
  17. package/templates/hermes-agent/template/.scripts/checkpoint.sh +29 -1
  18. package/templates/hermes-agent/template/.scripts/heartbeat.sh +13 -1
  19. package/templates/hermes-agent/template/.scripts/lib/fleet-env.sh +202 -0
  20. package/templates/hermes-agent/template/.scripts/lib/parse-fleet-env.py +734 -0
  21. package/templates/hermes-agent/template/.scripts/lifecycle.sh +126 -0
  22. package/templates/hermes-agent/template/.scripts/providers/plane.sh +1 -1
  23. package/templates/hermes-agent/template/SOUL.md.jinja +44 -8
  24. package/templates/hermes-agent/template/hermes.jinja +20 -8
  25. package/templates/hermes-agent/template/momo.jinja +177 -0
  26. package/templates/hermes-agent/template/role.yaml.jinja +19 -19
@@ -0,0 +1,202 @@
1
+ # shellcheck shell=bash
2
+ # Shared data-only fleet.env loader. This file is trusted template code; the
3
+ # fleet.env it reads is configuration data and is never evaluated by a shell.
4
+
5
+ if [[ "${__PJANGLER_FLEET_ENV_LIBRARY_LOADED:-0}" == "1" ]]; then
6
+ return 0
7
+ fi
8
+ __PJANGLER_FLEET_ENV_LIBRARY_LOADED=1
9
+
10
+ # fleet.env is shared configuration, not authority to inject code into Python,
11
+ # shell, Node, or dynamic-loader children. PATH remains intact so explicitly
12
+ # configured Hermes/PJangler/provider tools still resolve.
13
+ subprocess_injection_key_is_unsafe() {
14
+ local key="$1" loader_key="$1"
15
+ case "$key" in
16
+ PYTHONPATH|PYTHONHOME|PYTHONSTARTUP|PYTHONUSERBASE|\
17
+ BASH_ENV|ENV|BASHOPTS|SHELLOPTS|BASH_COMPAT|BASH_LOADABLES_PATH|\
18
+ BASH_XTRACEFD|PROMPT_COMMAND|PS0|PS1|PS2|PS3|PS4|\
19
+ NODE_OPTIONS|NODE_PATH|GLIBC_TUNABLES|BASH_FUNC_*|DYLD_*)
20
+ return 0
21
+ ;;
22
+ esac
23
+
24
+ # GNU and multilib loaders consume the same control stem with an optional
25
+ # _32/_64 ABI suffix. Do not delete unrelated application keys such as
26
+ # LD_SDK_KEY merely because they share the LD_ prefix.
27
+ case "$loader_key" in
28
+ LD_*_32|LD_*_64) loader_key="${loader_key%_*}" ;;
29
+ esac
30
+ case "$loader_key" in
31
+ LD_ASSUME_KERNEL|LD_AUDIT|LD_BIND_NOT|LD_BIND_NOW|LD_DEBUG|\
32
+ LD_DEBUG_OUTPUT|LD_DYNAMIC_WEAK|LD_HWCAP_MASK|LD_LIBRARY_PATH|\
33
+ LD_ORIGIN_PATH|LD_POINTER_GUARD|LD_PREFER_MAP_32BIT_EXEC|LD_PRELOAD|\
34
+ LD_PROFILE|LD_PROFILE_OUTPUT|LD_SHOW_AUXV|LD_TRACE_LOADED_OBJECTS|\
35
+ LD_TRACE_PRELINKING|LD_USE_LOAD_BIAS|LD_VERBOSE|LD_WARN)
36
+ return 0
37
+ ;;
38
+ esac
39
+ return 1
40
+ }
41
+
42
+ scrub_subprocess_interpreter_injection() {
43
+ local key declaration function_name
44
+
45
+ builtin set +x +v
46
+ while IFS= read -r key; do
47
+ if subprocess_injection_key_is_unsafe "$key"; then
48
+ case "$key" in
49
+ BASHOPTS|SHELLOPTS|BASH_XTRACEFD)
50
+ builtin export -n "$key" 2>/dev/null || true
51
+ ;;
52
+ *)
53
+ builtin unset -v "$key" 2>/dev/null || true
54
+ ;;
55
+ esac
56
+ fi
57
+ done < <(builtin compgen -A variable)
58
+
59
+ # Imported exported functions are live functions rather than ordinary
60
+ # BASH_FUNC_* variables. Remove the whole class before selecting any child.
61
+ while IFS= read -r declaration; do
62
+ function_name="${declaration##* }"
63
+ [[ -n "$function_name" ]] && builtin unset -f -- "$function_name"
64
+ done < <(builtin declare -Fx)
65
+
66
+ builtin export PYTHONNOUSERSITE=1 PYTHONSAFEPATH=1
67
+ }
68
+
69
+ # Apply staged records as one transaction. Existing variables always win. New
70
+ # variables are removed in reverse order if any assignment/export fails.
71
+ apply_fleet_environment_records() {
72
+ local -n __pjangler_fleet_keys_ref="$1"
73
+ local -n __pjangler_fleet_values_ref="$2"
74
+ local __pjangler_fleet_index __pjangler_fleet_key
75
+ local -a __pjangler_fleet_applied=()
76
+
77
+ if (( ${#__pjangler_fleet_keys_ref[@]} != ${#__pjangler_fleet_values_ref[@]} )); then
78
+ builtin printf 'fleet environment apply failed: mismatched staging arrays\n' >&2
79
+ return 1
80
+ fi
81
+
82
+ for ((__pjangler_fleet_index = 0; __pjangler_fleet_index < ${#__pjangler_fleet_keys_ref[@]}; __pjangler_fleet_index++)); do
83
+ __pjangler_fleet_key="${__pjangler_fleet_keys_ref[__pjangler_fleet_index]}"
84
+ if [[ ! "$__pjangler_fleet_key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] \
85
+ || subprocess_injection_key_is_unsafe "$__pjangler_fleet_key"; then
86
+ builtin printf 'fleet environment apply failed: rejected variable name\n' >&2
87
+ for ((__pjangler_fleet_index = ${#__pjangler_fleet_applied[@]} - 1; __pjangler_fleet_index >= 0; __pjangler_fleet_index--)); do
88
+ builtin unset -v "${__pjangler_fleet_applied[__pjangler_fleet_index]}" 2>/dev/null || true
89
+ done
90
+ return 1
91
+ fi
92
+
93
+ if builtin declare -p "$__pjangler_fleet_key" >/dev/null 2>&1; then
94
+ continue
95
+ fi
96
+ if ! builtin printf -v "$__pjangler_fleet_key" '%s' \
97
+ "${__pjangler_fleet_values_ref[__pjangler_fleet_index]}"; then
98
+ builtin printf 'fleet environment apply failed: assignment rejected\n' >&2
99
+ for ((__pjangler_fleet_index = ${#__pjangler_fleet_applied[@]} - 1; __pjangler_fleet_index >= 0; __pjangler_fleet_index--)); do
100
+ builtin unset -v "${__pjangler_fleet_applied[__pjangler_fleet_index]}" 2>/dev/null || true
101
+ done
102
+ return 1
103
+ fi
104
+ __pjangler_fleet_applied+=("$__pjangler_fleet_key")
105
+ if ! builtin export "$__pjangler_fleet_key"; then
106
+ builtin printf 'fleet environment apply failed: export rejected\n' >&2
107
+ for ((__pjangler_fleet_index = ${#__pjangler_fleet_applied[@]} - 1; __pjangler_fleet_index >= 0; __pjangler_fleet_index--)); do
108
+ builtin unset -v "${__pjangler_fleet_applied[__pjangler_fleet_index]}" 2>/dev/null || true
109
+ done
110
+ return 1
111
+ fi
112
+ done
113
+ }
114
+
115
+ # Consume a parser child through a complete, double-NUL-terminated protocol.
116
+ # Nothing reaches this shell until child status and the entire frame validate.
117
+ import_fleet_environment_stream() {
118
+ local __pjangler_fleet_fd="$1" __pjangler_fleet_pid="$2"
119
+ local __pjangler_fleet_count __pjangler_fleet_index
120
+ local __pjangler_fleet_record __pjangler_fleet_key __pjangler_fleet_value
121
+ local -a __pjangler_fleet_records=() __pjangler_fleet_keys=() __pjangler_fleet_values=()
122
+ local -A __pjangler_fleet_seen=()
123
+
124
+ builtin mapfile -d '' -t -u "$__pjangler_fleet_fd" __pjangler_fleet_records || true
125
+ exec {__pjangler_fleet_fd}<&-
126
+ if ! builtin wait "$__pjangler_fleet_pid"; then
127
+ builtin printf 'fleet environment frame rejected: parser child failed\n' >&2
128
+ return 1
129
+ fi
130
+
131
+ __pjangler_fleet_count="${#__pjangler_fleet_records[@]}"
132
+ if (( __pjangler_fleet_count < 3 )) \
133
+ || [[ "${__pjangler_fleet_records[0]}" != "PJANGLER_FLEET_ENV_V1" ]] \
134
+ || [[ "${__pjangler_fleet_records[__pjangler_fleet_count - 2]}" != "PJANGLER_FLEET_ENV_END" ]] \
135
+ || [[ -n "${__pjangler_fleet_records[__pjangler_fleet_count - 1]}" ]]; then
136
+ builtin printf 'fleet environment frame rejected: incomplete framing\n' >&2
137
+ return 1
138
+ fi
139
+
140
+ for ((__pjangler_fleet_index = 1; __pjangler_fleet_index < __pjangler_fleet_count - 2; __pjangler_fleet_index++)); do
141
+ __pjangler_fleet_record="${__pjangler_fleet_records[__pjangler_fleet_index]}"
142
+ if [[ "$__pjangler_fleet_record" != *=* ]]; then
143
+ builtin printf 'fleet environment frame rejected: malformed record\n' >&2
144
+ return 1
145
+ fi
146
+ __pjangler_fleet_key="${__pjangler_fleet_record%%=*}"
147
+ __pjangler_fleet_value="${__pjangler_fleet_record#*=}"
148
+ if [[ ! "$__pjangler_fleet_key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] \
149
+ || subprocess_injection_key_is_unsafe "$__pjangler_fleet_key"; then
150
+ builtin printf 'fleet environment frame rejected: unsafe variable\n' >&2
151
+ return 1
152
+ fi
153
+ if [[ -n "${__pjangler_fleet_seen[$__pjangler_fleet_key]+present}" ]]; then
154
+ builtin printf 'fleet environment frame rejected: duplicate variable\n' >&2
155
+ return 1
156
+ fi
157
+ __pjangler_fleet_seen["$__pjangler_fleet_key"]=1
158
+ __pjangler_fleet_keys+=("$__pjangler_fleet_key")
159
+ __pjangler_fleet_values+=("$__pjangler_fleet_value")
160
+ done
161
+
162
+ apply_fleet_environment_records __pjangler_fleet_keys __pjangler_fleet_values
163
+ }
164
+
165
+ # fleet.env is parsed by an isolated interpreter. The parser path is explicit
166
+ # so every caller binds to its colocated, attested copy rather than PATH state.
167
+ import_fleet_environment() {
168
+ local __pjangler_fleet_path="$1" __pjangler_fleet_parser="$2"
169
+ local __pjangler_fleet_python __pjangler_fleet_fd __pjangler_fleet_pid
170
+
171
+ if [[ "$__pjangler_fleet_parser" != /* ]] \
172
+ || [[ ! -f "$__pjangler_fleet_parser" || -L "$__pjangler_fleet_parser" ]]; then
173
+ builtin printf 'fleet environment frame rejected: trusted parser is unavailable\n' >&2
174
+ return 1
175
+ fi
176
+ __pjangler_fleet_python="$(builtin type -P python3)" || {
177
+ builtin printf 'fleet environment frame rejected: python3 is unavailable\n' >&2
178
+ return 1
179
+ }
180
+
181
+ exec {__pjangler_fleet_fd}< <(
182
+ "$__pjangler_fleet_python" -I "$__pjangler_fleet_parser" "$__pjangler_fleet_path"
183
+ )
184
+ __pjangler_fleet_pid=$!
185
+ import_fleet_environment_stream "$__pjangler_fleet_fd" "$__pjangler_fleet_pid"
186
+ }
187
+
188
+ # Public loader used by rendered provisioners, launchers, and maintenance
189
+ # scripts. Missing configuration remains optional; every existing path,
190
+ # including symlinks and non-regular files, must pass the parser's file checks.
191
+ load_fleet_environment() {
192
+ local __pjangler_fleet_path="$1" __pjangler_fleet_parser="$2"
193
+ scrub_subprocess_interpreter_injection
194
+ if [[ ! -e "$__pjangler_fleet_path" && ! -L "$__pjangler_fleet_path" ]]; then
195
+ return 0
196
+ fi
197
+ if ! import_fleet_environment "$__pjangler_fleet_path" "$__pjangler_fleet_parser"; then
198
+ builtin printf 'fleet environment import failed: %s\n' "$__pjangler_fleet_path" >&2
199
+ return 1
200
+ fi
201
+ scrub_subprocess_interpreter_injection
202
+ }