agent-knowledge-cli 0.1.2__py3-none-any.whl
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.
- agent_knowledge/__init__.py +3 -0
- agent_knowledge/__main__.py +3 -0
- agent_knowledge/assets/__init__.py +0 -0
- agent_knowledge/assets/claude/global.md +44 -0
- agent_knowledge/assets/claude/project-template.md +46 -0
- agent_knowledge/assets/claude/scripts/install.sh +85 -0
- agent_knowledge/assets/commands/doctor.md +21 -0
- agent_knowledge/assets/commands/global-knowledge-sync.md +27 -0
- agent_knowledge/assets/commands/graphify-sync.md +26 -0
- agent_knowledge/assets/commands/knowledge-sync.md +26 -0
- agent_knowledge/assets/commands/ship.md +29 -0
- agent_knowledge/assets/rules/generate-architecture-doc.mdc +87 -0
- agent_knowledge/assets/rules/history-backfill.mdc +67 -0
- agent_knowledge/assets/rules/memory-bootstrap.mdc +53 -0
- agent_knowledge/assets/rules/memory-writeback.mdc +90 -0
- agent_knowledge/assets/rules/shared-memory.mdc +102 -0
- agent_knowledge/assets/rules/workflow-orchestration.mdc +93 -0
- agent_knowledge/assets/rules-global/action-first.mdc +26 -0
- agent_knowledge/assets/rules-global/no-icons-emojis.mdc +16 -0
- agent_knowledge/assets/rules-global/no-unsolicited-docs.mdc +20 -0
- agent_knowledge/assets/scripts/bootstrap-memory-tree.sh +389 -0
- agent_knowledge/assets/scripts/compact-memory.sh +191 -0
- agent_knowledge/assets/scripts/doctor.sh +137 -0
- agent_knowledge/assets/scripts/global-knowledge-sync.sh +372 -0
- agent_knowledge/assets/scripts/graphify-sync.sh +397 -0
- agent_knowledge/assets/scripts/import-agent-history.sh +706 -0
- agent_knowledge/assets/scripts/install-project-links.sh +258 -0
- agent_knowledge/assets/scripts/lib/knowledge-common.sh +875 -0
- agent_knowledge/assets/scripts/measure-token-savings.py +540 -0
- agent_knowledge/assets/scripts/ship.sh +256 -0
- agent_knowledge/assets/scripts/update-knowledge.sh +341 -0
- agent_knowledge/assets/scripts/validate-knowledge.sh +265 -0
- agent_knowledge/assets/skills/decision-recording/SKILL.md +124 -0
- agent_knowledge/assets/skills/history-backfill/SKILL.md +115 -0
- agent_knowledge/assets/skills/memory-compaction/SKILL.md +115 -0
- agent_knowledge/assets/skills/memory-management/SKILL.md +134 -0
- agent_knowledge/assets/skills/project-ontology-bootstrap/SKILL.md +173 -0
- agent_knowledge/assets/skills/session-management/SKILL.md +116 -0
- agent_knowledge/assets/skills-cursor/create-rule/SKILL.md +164 -0
- agent_knowledge/assets/skills-cursor/create-skill/SKILL.md +498 -0
- agent_knowledge/assets/skills-cursor/create-subagent/SKILL.md +225 -0
- agent_knowledge/assets/skills-cursor/migrate-to-skills/SKILL.md +134 -0
- agent_knowledge/assets/skills-cursor/shell/SKILL.md +24 -0
- agent_knowledge/assets/skills-cursor/update-cursor-settings/SKILL.md +122 -0
- agent_knowledge/assets/templates/dashboards/project-overview.template.md +24 -0
- agent_knowledge/assets/templates/dashboards/session-rollup.template.md +23 -0
- agent_knowledge/assets/templates/hooks/hooks.json.template +11 -0
- agent_knowledge/assets/templates/integrations/claude/CLAUDE.md +7 -0
- agent_knowledge/assets/templates/integrations/codex/AGENTS.md +7 -0
- agent_knowledge/assets/templates/integrations/cursor/agent-knowledge.mdc +11 -0
- agent_knowledge/assets/templates/integrations/cursor/hooks.json +11 -0
- agent_knowledge/assets/templates/memory/MEMORY.root.template.md +36 -0
- agent_knowledge/assets/templates/memory/branch.template.md +33 -0
- agent_knowledge/assets/templates/memory/decision.template.md +33 -0
- agent_knowledge/assets/templates/memory/profile.hybrid.yaml +16 -0
- agent_knowledge/assets/templates/memory/profile.ml-platform.yaml +18 -0
- agent_knowledge/assets/templates/memory/profile.robotics.yaml +19 -0
- agent_knowledge/assets/templates/memory/profile.web-app.yaml +16 -0
- agent_knowledge/assets/templates/portfolio/.obsidian/README.md +21 -0
- agent_knowledge/assets/templates/portfolio/.obsidian/app.json +5 -0
- agent_knowledge/assets/templates/portfolio/.obsidian/core-plugins.json +7 -0
- agent_knowledge/assets/templates/project/.agent-project.yaml +36 -0
- agent_knowledge/assets/templates/project/.agentknowledgeignore +10 -0
- agent_knowledge/assets/templates/project/AGENTS.md +87 -0
- agent_knowledge/assets/templates/project/agent-knowledge/.obsidian/README.md +23 -0
- agent_knowledge/assets/templates/project/agent-knowledge/.obsidian/app.json +5 -0
- agent_knowledge/assets/templates/project/agent-knowledge/.obsidian/core-plugins.json +7 -0
- agent_knowledge/assets/templates/project/agent-knowledge/Evidence/README.md +34 -0
- agent_knowledge/assets/templates/project/agent-knowledge/Evidence/imports/README.md +29 -0
- agent_knowledge/assets/templates/project/agent-knowledge/Evidence/raw/README.md +25 -0
- agent_knowledge/assets/templates/project/agent-knowledge/Memory/MEMORY.md +37 -0
- agent_knowledge/assets/templates/project/agent-knowledge/Memory/decisions/decisions.md +31 -0
- agent_knowledge/assets/templates/project/agent-knowledge/Outputs/README.md +24 -0
- agent_knowledge/assets/templates/project/agent-knowledge/STATUS.md +43 -0
- agent_knowledge/assets/templates/project/agent-knowledge/Sessions/README.md +21 -0
- agent_knowledge/assets/templates/project/agent-knowledge/Templates/README.md +19 -0
- agent_knowledge/assets/templates/project/gitignore.agent-knowledge +13 -0
- agent_knowledge/cli.py +457 -0
- agent_knowledge/runtime/__init__.py +0 -0
- agent_knowledge/runtime/integrations.py +154 -0
- agent_knowledge/runtime/paths.py +46 -0
- agent_knowledge/runtime/shell.py +22 -0
- agent_knowledge/runtime/sync.py +255 -0
- agent_knowledge_cli-0.1.2.dist-info/METADATA +155 -0
- agent_knowledge_cli-0.1.2.dist-info/RECORD +88 -0
- agent_knowledge_cli-0.1.2.dist-info/WHEEL +4 -0
- agent_knowledge_cli-0.1.2.dist-info/entry_points.txt +2 -0
- agent_knowledge_cli-0.1.2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Compact and clean memory notes conservatively.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
# shellcheck source=/dev/null
|
|
10
|
+
. "$SCRIPT_DIR/lib/knowledge-common.sh"
|
|
11
|
+
|
|
12
|
+
usage() {
|
|
13
|
+
cat <<'EOF'
|
|
14
|
+
Usage:
|
|
15
|
+
scripts/compact-memory.sh [project-dir]
|
|
16
|
+
scripts/compact-memory.sh --project <dir> [--dry-run] [--json] [--summary-file <file>]
|
|
17
|
+
EOF
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
TARGET_PROJECT_ARG="."
|
|
21
|
+
POSITIONAL=()
|
|
22
|
+
COMPACTED=()
|
|
23
|
+
WARNINGS=()
|
|
24
|
+
|
|
25
|
+
while [ "$#" -gt 0 ]; do
|
|
26
|
+
if kc_parse_common_flag "$@" ; then
|
|
27
|
+
shift
|
|
28
|
+
continue
|
|
29
|
+
fi
|
|
30
|
+
flag_status=$?
|
|
31
|
+
if [ "$flag_status" -eq 2 ]; then
|
|
32
|
+
shift 2
|
|
33
|
+
continue
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
case "$1" in
|
|
37
|
+
--project)
|
|
38
|
+
TARGET_PROJECT_ARG="${2:-.}"
|
|
39
|
+
shift 2
|
|
40
|
+
;;
|
|
41
|
+
*)
|
|
42
|
+
POSITIONAL+=("$1")
|
|
43
|
+
shift
|
|
44
|
+
;;
|
|
45
|
+
esac
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
if [ "$SHOW_HELP" -eq 1 ]; then
|
|
49
|
+
usage
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
if [ ${#POSITIONAL[@]} -ge 1 ]; then
|
|
54
|
+
TARGET_PROJECT_ARG="${POSITIONAL[0]}"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
kc_load_project_context "$TARGET_PROJECT_ARG"
|
|
58
|
+
[ -d "$MEMORY_DIR" ] || kc_fail "No memory tree at: $MEMORY_DIR"
|
|
59
|
+
|
|
60
|
+
compact_recent_changes() {
|
|
61
|
+
local file="$1"
|
|
62
|
+
local limit="$2"
|
|
63
|
+
local label="$3"
|
|
64
|
+
local tmp_file
|
|
65
|
+
|
|
66
|
+
tmp_file="$(mktemp)"
|
|
67
|
+
awk -v limit="$limit" '
|
|
68
|
+
BEGIN {
|
|
69
|
+
in_recent = 0
|
|
70
|
+
count = 0
|
|
71
|
+
}
|
|
72
|
+
{
|
|
73
|
+
if ($0 == "## Recent Changes") {
|
|
74
|
+
in_recent = 1
|
|
75
|
+
delete seen
|
|
76
|
+
print
|
|
77
|
+
next
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (in_recent && /^## /) {
|
|
81
|
+
in_recent = 0
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (in_recent) {
|
|
85
|
+
if ($0 ~ /^- /) {
|
|
86
|
+
if (!($0 in seen) && count < limit) {
|
|
87
|
+
seen[$0] = 1
|
|
88
|
+
kept[++count] = $0
|
|
89
|
+
}
|
|
90
|
+
next
|
|
91
|
+
}
|
|
92
|
+
if ($0 ~ /^[[:space:]]*$/) {
|
|
93
|
+
next
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
print
|
|
98
|
+
|
|
99
|
+
if (!in_recent) {
|
|
100
|
+
next
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
END {
|
|
104
|
+
if (count > 0) {
|
|
105
|
+
for (i = 1; i <= count; i++) {
|
|
106
|
+
print kept[i]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
' "$file" > "$tmp_file"
|
|
111
|
+
|
|
112
|
+
kc_apply_temp_file "$tmp_file" "$file" "$label"
|
|
113
|
+
case "$KC_LAST_ACTION" in
|
|
114
|
+
updated|would-update)
|
|
115
|
+
COMPACTED+=("$label")
|
|
116
|
+
;;
|
|
117
|
+
esac
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
health_check_note() {
|
|
121
|
+
local file="$1"
|
|
122
|
+
local rel="$2"
|
|
123
|
+
local lines
|
|
124
|
+
|
|
125
|
+
lines="$(wc -l < "$file" 2>/dev/null || echo 0)"
|
|
126
|
+
if [ "$lines" -gt 220 ]; then
|
|
127
|
+
WARNINGS+=("$rel is still over 220 lines after compaction")
|
|
128
|
+
elif [ "$lines" -gt 180 ]; then
|
|
129
|
+
WARNINGS+=("$rel is still over 180 lines after compaction")
|
|
130
|
+
fi
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
kc_log "Compacting memory: $MEMORY_DIR"
|
|
134
|
+
|
|
135
|
+
memory_files="$(find "$MEMORY_DIR" -name "*.md" | sort)"
|
|
136
|
+
while IFS= read -r file; do
|
|
137
|
+
[ -n "$file" ] || continue
|
|
138
|
+
rel="$(kc_rel_knowledge_path "$file")"
|
|
139
|
+
limit=12
|
|
140
|
+
case "$file" in
|
|
141
|
+
"$MEMORY_ROOT")
|
|
142
|
+
limit=8
|
|
143
|
+
;;
|
|
144
|
+
*/decisions/*)
|
|
145
|
+
limit=8
|
|
146
|
+
;;
|
|
147
|
+
esac
|
|
148
|
+
compact_recent_changes "$file" "$limit" "$rel"
|
|
149
|
+
done <<EOF
|
|
150
|
+
$memory_files
|
|
151
|
+
EOF
|
|
152
|
+
|
|
153
|
+
while IFS= read -r file; do
|
|
154
|
+
[ -n "$file" ] || continue
|
|
155
|
+
rel="$(kc_rel_knowledge_path "$file")"
|
|
156
|
+
health_check_note "$file" "$rel"
|
|
157
|
+
done <<EOF
|
|
158
|
+
$memory_files
|
|
159
|
+
EOF
|
|
160
|
+
|
|
161
|
+
kc_status_load
|
|
162
|
+
if [ "${DRY_RUN:-0}" -eq 0 ] && [ ${#COMPACTED[@]} -gt 0 ]; then
|
|
163
|
+
STATUS_LAST_COMPACTION="$(kc_now_utc)"
|
|
164
|
+
fi
|
|
165
|
+
STATUS_WARNING_LINES="$(printf '%s\n' "${WARNINGS[@]+"${WARNINGS[@]}"}")"
|
|
166
|
+
kc_status_write "$STATUS_WARNING_LINES"
|
|
167
|
+
|
|
168
|
+
json_summary="{"
|
|
169
|
+
json_summary="$json_summary\"script\":\"compact-memory\","
|
|
170
|
+
json_summary="$json_summary\"project_root\":\"$(kc_json_escape "$TARGET_PROJECT")\","
|
|
171
|
+
json_summary="$json_summary\"dry_run\":$(kc_json_bool "$DRY_RUN"),"
|
|
172
|
+
json_summary="$json_summary\"compacted\":$(kc_json_array "${COMPACTED[@]+"${COMPACTED[@]}"}"),"
|
|
173
|
+
json_summary="$json_summary\"warnings\":$(kc_json_array "${WARNINGS[@]+"${WARNINGS[@]}"}")"
|
|
174
|
+
json_summary="$json_summary}"
|
|
175
|
+
kc_write_json_output "$json_summary"
|
|
176
|
+
|
|
177
|
+
if [ "$JSON_MODE" -ne 1 ]; then
|
|
178
|
+
kc_log ""
|
|
179
|
+
if [ ${#COMPACTED[@]} -gt 0 ]; then
|
|
180
|
+
kc_log "Compacted notes:"
|
|
181
|
+
printf ' %s\n' "${COMPACTED[@]+"${COMPACTED[@]}"}"
|
|
182
|
+
else
|
|
183
|
+
kc_log "No compaction changes were needed."
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
if [ ${#WARNINGS[@]} -gt 0 ]; then
|
|
187
|
+
kc_log ""
|
|
188
|
+
kc_log "Warnings:"
|
|
189
|
+
printf ' %s\n' "${WARNINGS[@]+"${WARNINGS[@]}"}"
|
|
190
|
+
fi
|
|
191
|
+
fi
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Quick troubleshooting entrypoint for the project knowledge system.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
# shellcheck source=/dev/null
|
|
10
|
+
. "$SCRIPT_DIR/lib/knowledge-common.sh"
|
|
11
|
+
|
|
12
|
+
usage() {
|
|
13
|
+
cat <<'EOF'
|
|
14
|
+
Usage:
|
|
15
|
+
scripts/doctor.sh [project-dir]
|
|
16
|
+
scripts/doctor.sh --project <dir> [--json] [--summary-file <file>] [--dry-run]
|
|
17
|
+
EOF
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
TARGET_PROJECT_ARG="."
|
|
21
|
+
POSITIONAL=()
|
|
22
|
+
WARNINGS=()
|
|
23
|
+
|
|
24
|
+
while [ "$#" -gt 0 ]; do
|
|
25
|
+
if kc_parse_common_flag "$@" ; then
|
|
26
|
+
shift
|
|
27
|
+
continue
|
|
28
|
+
fi
|
|
29
|
+
flag_status=$?
|
|
30
|
+
if [ "$flag_status" -eq 2 ]; then
|
|
31
|
+
shift 2
|
|
32
|
+
continue
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
case "$1" in
|
|
36
|
+
--project)
|
|
37
|
+
TARGET_PROJECT_ARG="${2:-.}"
|
|
38
|
+
shift 2
|
|
39
|
+
;;
|
|
40
|
+
*)
|
|
41
|
+
POSITIONAL+=("$1")
|
|
42
|
+
shift
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
if [ "$SHOW_HELP" -eq 1 ]; then
|
|
48
|
+
usage
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
if [ ${#POSITIONAL[@]} -ge 1 ]; then
|
|
53
|
+
TARGET_PROJECT_ARG="${POSITIONAL[0]}"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
kc_load_project_context "$TARGET_PROJECT_ARG"
|
|
57
|
+
|
|
58
|
+
validation_status="ok"
|
|
59
|
+
doctor_validate_args=(--project "$TARGET_PROJECT")
|
|
60
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
61
|
+
doctor_validate_args+=(--dry-run)
|
|
62
|
+
fi
|
|
63
|
+
if ! kc_run_child_script "$SCRIPT_DIR/validate-knowledge.sh" "${doctor_validate_args[@]}"; then
|
|
64
|
+
validation_status="error"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
if [ -n "$FRAMEWORK_REPO" ] && [ ! -d "$FRAMEWORK_REPO" ]; then
|
|
68
|
+
WARNINGS+=("Framework repo path from .agent-project.yaml does not exist: $FRAMEWORK_REPO")
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ ! -f "$TARGET_PROJECT/AGENTS.md" ]; then
|
|
72
|
+
WARNINGS+=("AGENTS.md is missing from the project repo.")
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [ -f "$TARGET_PROJECT/.cursor/hooks.json" ]; then
|
|
76
|
+
WARNINGS+=("Repo-local hooks are installed; review them if sync behavior is surprising.")
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
if [ ! -L "$KNOWLEDGE_POINTER_PATH" ]; then
|
|
80
|
+
if kc_is_windows_like; then
|
|
81
|
+
WARNINGS+=("agent-knowledge is not reported as a symlink. If this is a junction, verify it still resolves to the external knowledge folder.")
|
|
82
|
+
else
|
|
83
|
+
WARNINGS+=("agent-knowledge is not a symlink. Canonical external source-of-truth mode is not active.")
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# Detect integrations
|
|
88
|
+
INTEGRATIONS=()
|
|
89
|
+
[ -d "$TARGET_PROJECT/.cursor" ] && INTEGRATIONS+=("cursor")
|
|
90
|
+
[ -d "$TARGET_PROJECT/.claude" ] || [ -f "$TARGET_PROJECT/CLAUDE.md" ] && INTEGRATIONS+=("claude")
|
|
91
|
+
[ -d "$TARGET_PROJECT/.codex" ] && INTEGRATIONS+=("codex")
|
|
92
|
+
|
|
93
|
+
# Read onboarding state
|
|
94
|
+
ONBOARDING_STATUS="$(kc_yaml_leaf_value "$STATUS_FILE" "onboarding" 2>/dev/null || echo "unknown")"
|
|
95
|
+
|
|
96
|
+
doctor_result="ok"
|
|
97
|
+
if [ "$validation_status" = "error" ]; then
|
|
98
|
+
doctor_result="error"
|
|
99
|
+
elif [ ${#WARNINGS[@]} -gt 0 ]; then
|
|
100
|
+
doctor_result="warn"
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
kc_status_load
|
|
104
|
+
if [ "${DRY_RUN:-0}" -eq 0 ]; then
|
|
105
|
+
STATUS_LAST_DOCTOR="$(kc_now_utc)"
|
|
106
|
+
STATUS_LAST_DOCTOR_RESULT="$doctor_result"
|
|
107
|
+
fi
|
|
108
|
+
STATUS_WARNING_LINES="$(printf '%s\n' "${WARNINGS[@]+"${WARNINGS[@]}"}")"
|
|
109
|
+
kc_status_write "$STATUS_WARNING_LINES"
|
|
110
|
+
|
|
111
|
+
json_summary="{"
|
|
112
|
+
json_summary="$json_summary\"script\":\"doctor\","
|
|
113
|
+
json_summary="$json_summary\"project_root\":\"$(kc_json_escape "$TARGET_PROJECT")\","
|
|
114
|
+
json_summary="$json_summary\"result\":\"$(kc_json_escape "$doctor_result")\","
|
|
115
|
+
json_summary="$json_summary\"dry_run\":$(kc_json_bool "$DRY_RUN"),"
|
|
116
|
+
json_summary="$json_summary\"onboarding\":\"$(kc_json_escape "$ONBOARDING_STATUS")\","
|
|
117
|
+
json_summary="$json_summary\"integrations\":$(kc_json_array "${INTEGRATIONS[@]+"${INTEGRATIONS[@]}"}"),"
|
|
118
|
+
json_summary="$json_summary\"warnings\":$(kc_json_array "${WARNINGS[@]+"${WARNINGS[@]}"}")"
|
|
119
|
+
json_summary="$json_summary}"
|
|
120
|
+
kc_write_json_output "$json_summary"
|
|
121
|
+
|
|
122
|
+
if [ "$JSON_MODE" -ne 1 ]; then
|
|
123
|
+
kc_log "Doctor result: $doctor_result"
|
|
124
|
+
kc_log " project: $TARGET_PROJECT"
|
|
125
|
+
kc_log " pointer: $KNOWLEDGE_POINTER_PATH"
|
|
126
|
+
kc_log " real knowledge path: $KNOWLEDGE_REAL_DIR"
|
|
127
|
+
kc_log " onboarding: $ONBOARDING_STATUS"
|
|
128
|
+
kc_log " integrations: ${INTEGRATIONS[*]+"${INTEGRATIONS[*]}"}"
|
|
129
|
+
if [ ${#WARNINGS[@]} -gt 0 ]; then
|
|
130
|
+
kc_log "Warnings:"
|
|
131
|
+
printf ' %s\n' "${WARNINGS[@]+"${WARNINGS[@]}"}"
|
|
132
|
+
fi
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
if [ "$doctor_result" = "error" ]; then
|
|
136
|
+
exit 1
|
|
137
|
+
fi
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Build or update a project-scoped tooling knowledge base from safe allowlisted sources.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
# shellcheck source=/dev/null
|
|
10
|
+
. "$SCRIPT_DIR/lib/knowledge-common.sh"
|
|
11
|
+
|
|
12
|
+
usage() {
|
|
13
|
+
cat <<'EOF'
|
|
14
|
+
Usage:
|
|
15
|
+
scripts/global-knowledge-sync.sh [project-dir]
|
|
16
|
+
scripts/global-knowledge-sync.sh --project <dir> [--dry-run] [--json] [--summary-file <file>]
|
|
17
|
+
EOF
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
TARGET_PROJECT_ARG="."
|
|
21
|
+
POSITIONAL=()
|
|
22
|
+
SCANNED=()
|
|
23
|
+
SKIPPED=()
|
|
24
|
+
UPDATED=()
|
|
25
|
+
CACHED=()
|
|
26
|
+
WARNINGS=()
|
|
27
|
+
GENERATED_AT="$(kc_now_utc)"
|
|
28
|
+
|
|
29
|
+
while [ "$#" -gt 0 ]; do
|
|
30
|
+
if kc_parse_common_flag "$@" ; then
|
|
31
|
+
shift
|
|
32
|
+
continue
|
|
33
|
+
fi
|
|
34
|
+
flag_status=$?
|
|
35
|
+
if [ "$flag_status" -eq 2 ]; then
|
|
36
|
+
shift 2
|
|
37
|
+
continue
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
case "$1" in
|
|
41
|
+
--project)
|
|
42
|
+
TARGET_PROJECT_ARG="${2:-.}"
|
|
43
|
+
shift 2
|
|
44
|
+
;;
|
|
45
|
+
*)
|
|
46
|
+
POSITIONAL+=("$1")
|
|
47
|
+
shift
|
|
48
|
+
;;
|
|
49
|
+
esac
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
if [ "$SHOW_HELP" -eq 1 ]; then
|
|
53
|
+
usage
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
if [ ${#POSITIONAL[@]} -ge 1 ]; then
|
|
58
|
+
TARGET_PROJECT_ARG="${POSITIONAL[0]}"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
kc_load_project_context "$TARGET_PROJECT_ARG"
|
|
62
|
+
kc_require_knowledge_pointer
|
|
63
|
+
|
|
64
|
+
TOOLING_EVIDENCE_DIR="$EVIDENCE_TOOLING_DIR"
|
|
65
|
+
TOOLING_MEMORY_DIR="$MEMORY_DIR/tooling"
|
|
66
|
+
TOOLING_INDEX="$TOOLING_MEMORY_DIR/INDEX.md"
|
|
67
|
+
|
|
68
|
+
kc_ensure_dir "$TOOLING_EVIDENCE_DIR" "agent-knowledge/Evidence/tooling"
|
|
69
|
+
kc_ensure_dir "$TOOLING_MEMORY_DIR" "agent-knowledge/Memory/tooling"
|
|
70
|
+
kc_ensure_dir "$EVIDENCE_CACHE_DIR" "agent-knowledge/Evidence/.cache"
|
|
71
|
+
|
|
72
|
+
is_allowlisted_tool_file() {
|
|
73
|
+
local path="$1"
|
|
74
|
+
case "$path" in
|
|
75
|
+
"$HOME/.claude/settings.json"|"$HOME/.claude/CLAUDE.md"|"$HOME/.codex/config.toml"|"$HOME/.cursor/settings.json")
|
|
76
|
+
return 0
|
|
77
|
+
;;
|
|
78
|
+
"$HOME/.claude/agents/"*|"$HOME/.cursor/rules/"*|"$HOME/.cursor/snippets/"*)
|
|
79
|
+
return 0
|
|
80
|
+
;;
|
|
81
|
+
esac
|
|
82
|
+
return 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
is_safe_tool_file() {
|
|
86
|
+
local path="$1"
|
|
87
|
+
case "$path" in
|
|
88
|
+
*session*|*oauth*|*token*|*auth*|*cache*|*.db|*.sqlite|*.sqlite3)
|
|
89
|
+
return 1
|
|
90
|
+
;;
|
|
91
|
+
*)
|
|
92
|
+
return 0
|
|
93
|
+
;;
|
|
94
|
+
esac
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
redact_tool_file() {
|
|
98
|
+
local src="$1"
|
|
99
|
+
sed -E \
|
|
100
|
+
-e 's/([Pp]assword[[:space:]]*[:=][[:space:]]*).*/\1[REDACTED]/g' \
|
|
101
|
+
-e 's/([Tt]oken[[:space:]]*[:=][[:space:]]*).*/\1[REDACTED]/g' \
|
|
102
|
+
-e 's/([Ss]ecret[[:space:]]*[:=][[:space:]]*).*/\1[REDACTED]/g' \
|
|
103
|
+
-e 's/([Aa]uthorization[[:space:]]*[:=][[:space:]]*).*/\1[REDACTED]/g' \
|
|
104
|
+
-e 's/([Cc]ookie[[:space:]]*[:=][[:space:]]*).*/\1[REDACTED]/g' \
|
|
105
|
+
-e 's/([Oo]auth[^:=]*[[:space:]]*[:=][[:space:]]*).*/\1[REDACTED]/g' \
|
|
106
|
+
-e 's/([Aa]pi[_-]?[Kk]ey[[:space:]]*[:=][[:space:]]*).*/\1[REDACTED]/g' \
|
|
107
|
+
-e 's/([Rr]efresh[_-]?[Tt]oken[[:space:]]*[:=][[:space:]]*).*/\1[REDACTED]/g' \
|
|
108
|
+
"$src"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
capture_tooling_evidence() {
|
|
112
|
+
local src="$1"
|
|
113
|
+
local family="$2"
|
|
114
|
+
local name="$3"
|
|
115
|
+
local dst="$TOOLING_EVIDENCE_DIR/$name"
|
|
116
|
+
local meta_dst="$dst.meta.json"
|
|
117
|
+
local signature=""
|
|
118
|
+
local tmp_file=""
|
|
119
|
+
local changed=0
|
|
120
|
+
|
|
121
|
+
signature="$(kc_signature_from_paths "$src")"
|
|
122
|
+
if kc_cache_is_current "global-tooling" "$name" "$signature" "$dst" "$meta_dst"; then
|
|
123
|
+
CACHED+=("agent-knowledge/Evidence/tooling/$name")
|
|
124
|
+
SCANNED+=("$src")
|
|
125
|
+
TOOLING_FAMILIES="${TOOLING_FAMILIES}${family}"$'\n'
|
|
126
|
+
TOOLING_FILES="${TOOLING_FILES}${family}|$name|$src"$'\n'
|
|
127
|
+
return 0
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
tmp_file="$(mktemp)"
|
|
131
|
+
{
|
|
132
|
+
printf '# Tooling Evidence\n'
|
|
133
|
+
printf '# Source: %s\n' "$src"
|
|
134
|
+
printf '# Kind: tooling-import\n'
|
|
135
|
+
printf '# Confidence: EXTRACTED\n'
|
|
136
|
+
printf '# Generated: %s\n\n' "$GENERATED_AT"
|
|
137
|
+
redact_tool_file "$src"
|
|
138
|
+
} > "$tmp_file"
|
|
139
|
+
|
|
140
|
+
kc_apply_temp_file "$tmp_file" "$dst" "agent-knowledge/Evidence/tooling/$name"
|
|
141
|
+
case "$KC_LAST_ACTION" in
|
|
142
|
+
created|updated|would-create|would-update)
|
|
143
|
+
changed=1
|
|
144
|
+
;;
|
|
145
|
+
esac
|
|
146
|
+
|
|
147
|
+
kc_write_metadata_json "$meta_dst" "agent-knowledge/Evidence/tooling/$name.meta.json" "$src" "tooling-import" "EXTRACTED" "$GENERATED_AT" "$src" "Redacted allowlisted tooling surface captured for project-scoped reference."
|
|
148
|
+
case "$KC_LAST_ACTION" in
|
|
149
|
+
created|updated|would-create|would-update)
|
|
150
|
+
changed=1
|
|
151
|
+
;;
|
|
152
|
+
esac
|
|
153
|
+
|
|
154
|
+
kc_cache_store "global-tooling" "$name" "$signature"
|
|
155
|
+
|
|
156
|
+
if [ "$changed" -eq 1 ]; then
|
|
157
|
+
UPDATED+=("agent-knowledge/Evidence/tooling/$name")
|
|
158
|
+
fi
|
|
159
|
+
SCANNED+=("$src")
|
|
160
|
+
TOOLING_FAMILIES="${TOOLING_FAMILIES}${family}"$'\n'
|
|
161
|
+
TOOLING_FILES="${TOOLING_FILES}${family}|$name|$src"$'\n'
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
write_tooling_note() {
|
|
165
|
+
local family="$1"
|
|
166
|
+
local title="$2"
|
|
167
|
+
local purpose="$3"
|
|
168
|
+
local file_name="$TOOLING_MEMORY_DIR/$family.md"
|
|
169
|
+
local entries=""
|
|
170
|
+
local line=""
|
|
171
|
+
local signature=""
|
|
172
|
+
local tmp_file=""
|
|
173
|
+
|
|
174
|
+
while IFS= read -r line; do
|
|
175
|
+
[ -n "$line" ] || continue
|
|
176
|
+
case "$line" in
|
|
177
|
+
"$family|"*)
|
|
178
|
+
name="$(printf '%s' "$line" | cut -d'|' -f2)"
|
|
179
|
+
src="$(printf '%s' "$line" | cut -d'|' -f3-)"
|
|
180
|
+
entries="${entries}- [../../Evidence/tooling/$name](../../Evidence/tooling/$name) - Redacted import from \`$src\`."$'\n'
|
|
181
|
+
;;
|
|
182
|
+
esac
|
|
183
|
+
done <<EOF
|
|
184
|
+
$TOOLING_FILES
|
|
185
|
+
EOF
|
|
186
|
+
|
|
187
|
+
[ -n "$entries" ] || entries="- No allowlisted sources were captured."
|
|
188
|
+
signature="$(kc_signature_from_lines "$(printf '%s\n---\n%s\n---\n%s\n' "$family" "$title" "$entries")")"
|
|
189
|
+
if kc_cache_is_current "global-tooling-note" "$family" "$signature" "$file_name"; then
|
|
190
|
+
CACHED+=("agent-knowledge/Memory/tooling/$family.md")
|
|
191
|
+
return 0
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
tmp_file="$(mktemp)"
|
|
195
|
+
{
|
|
196
|
+
printf '%s\n' '---'
|
|
197
|
+
printf 'note_type: tooling-memory\n'
|
|
198
|
+
printf 'project: %s\n' "$PROJECT_NAME"
|
|
199
|
+
printf 'profile: %s\n' "$PROJECT_PROFILE"
|
|
200
|
+
printf 'area: tooling-%s\n' "$family"
|
|
201
|
+
printf 'status: active\n'
|
|
202
|
+
printf 'last_updated: %s\n' "$(kc_today)"
|
|
203
|
+
printf '%s\n\n' '---'
|
|
204
|
+
printf '# %s\n\n' "$title"
|
|
205
|
+
printf '## Purpose\n\n- %s\n\n' "$purpose"
|
|
206
|
+
printf '## Current State\n\n- Tooling knowledge is derived only from allowlisted local configuration surfaces.\n- Sensitive lines are redacted before evidence is written.\n\n'
|
|
207
|
+
printf '## Recent Changes\n\n- %s - Refreshed tooling evidence for `%s`.\n\n' "$(kc_today)" "$family"
|
|
208
|
+
printf '## Decisions\n\n- No tooling-specific decisions recorded yet.\n\n'
|
|
209
|
+
printf '## Open Questions\n\n- Which parts of this tooling setup are stable enough to rely on across sessions?\n\n'
|
|
210
|
+
printf '## Subtopics\n\n%s\n' "$entries"
|
|
211
|
+
} > "$tmp_file"
|
|
212
|
+
|
|
213
|
+
kc_apply_temp_file "$tmp_file" "$file_name" "agent-knowledge/Memory/tooling/$family.md"
|
|
214
|
+
case "$KC_LAST_ACTION" in
|
|
215
|
+
created|updated|would-create|would-update)
|
|
216
|
+
UPDATED+=("agent-knowledge/Memory/tooling/$family.md")
|
|
217
|
+
;;
|
|
218
|
+
esac
|
|
219
|
+
kc_cache_store "global-tooling-note" "$family" "$signature"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
write_tooling_index() {
|
|
223
|
+
local families=""
|
|
224
|
+
local family=""
|
|
225
|
+
local bullets=""
|
|
226
|
+
local signature=""
|
|
227
|
+
local tmp_file=""
|
|
228
|
+
|
|
229
|
+
families="$(printf '%s\n' "$TOOLING_FAMILIES" | awk 'NF && !seen[$0]++ { print $0 }')"
|
|
230
|
+
while IFS= read -r family; do
|
|
231
|
+
[ -n "$family" ] || continue
|
|
232
|
+
bullets="${bullets}- [${family}.md](${family}.md) - Redacted ${family} tooling summary."$'\n'
|
|
233
|
+
done <<EOF
|
|
234
|
+
$families
|
|
235
|
+
EOF
|
|
236
|
+
|
|
237
|
+
[ -n "$bullets" ] || bullets="- No allowlisted tooling sources were found."
|
|
238
|
+
signature="$(kc_signature_from_lines "$bullets")"
|
|
239
|
+
if kc_cache_is_current "global-tooling-note" "index" "$signature" "$TOOLING_INDEX"; then
|
|
240
|
+
CACHED+=("agent-knowledge/Memory/tooling/INDEX.md")
|
|
241
|
+
return 0
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
tmp_file="$(mktemp)"
|
|
245
|
+
{
|
|
246
|
+
printf '%s\n' '---'
|
|
247
|
+
printf 'note_type: tooling-index\n'
|
|
248
|
+
printf 'project: %s\n' "$PROJECT_NAME"
|
|
249
|
+
printf 'status: active\n'
|
|
250
|
+
printf 'last_updated: %s\n' "$(kc_today)"
|
|
251
|
+
printf '%s\n\n' '---'
|
|
252
|
+
printf '# Tooling\n\n'
|
|
253
|
+
printf '## Purpose\n\n- Curated summary of safe local tooling surfaces relevant to this project.\n\n'
|
|
254
|
+
printf '## Current State\n\n- Evidence is redacted and stored under [../../Evidence/tooling](../../Evidence/tooling).\n\n'
|
|
255
|
+
printf '## Recent Changes\n\n- %s - Refreshed tooling knowledge from allowlisted local sources.\n\n' "$(kc_today)"
|
|
256
|
+
printf '## Decisions\n\n- No tooling decisions recorded yet.\n\n'
|
|
257
|
+
printf '## Open Questions\n\n- Which global tool defaults should be copied into project-specific durable memory?\n\n'
|
|
258
|
+
printf '## Subtopics\n\n%s\n' "$bullets"
|
|
259
|
+
} > "$tmp_file"
|
|
260
|
+
|
|
261
|
+
kc_apply_temp_file "$tmp_file" "$TOOLING_INDEX" "agent-knowledge/Memory/tooling/INDEX.md"
|
|
262
|
+
case "$KC_LAST_ACTION" in
|
|
263
|
+
created|updated|would-create|would-update)
|
|
264
|
+
UPDATED+=("agent-knowledge/Memory/tooling/INDEX.md")
|
|
265
|
+
;;
|
|
266
|
+
esac
|
|
267
|
+
kc_cache_store "global-tooling-note" "index" "$signature"
|
|
268
|
+
kc_append_unique_bullet "$MEMORY_ROOT" "Subtopics" "- [Tooling](tooling/INDEX.md) - Redacted local tooling setup relevant to this project." "agent-knowledge/Memory/MEMORY.md"
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
TOOLING_FAMILIES=""
|
|
272
|
+
TOOLING_FILES=""
|
|
273
|
+
|
|
274
|
+
for path in \
|
|
275
|
+
"$HOME/.claude/settings.json" \
|
|
276
|
+
"$HOME/.claude/CLAUDE.md" \
|
|
277
|
+
"$HOME/.codex/config.toml" \
|
|
278
|
+
"$HOME/.cursor/settings.json"; do
|
|
279
|
+
if [ -f "$path" ] && is_allowlisted_tool_file "$path"; then
|
|
280
|
+
if is_safe_tool_file "$path"; then
|
|
281
|
+
case "$path" in
|
|
282
|
+
"$HOME/.claude/"*)
|
|
283
|
+
capture_tooling_evidence "$path" "claude" "$(basename "$path" | tr '[:upper:]' '[:lower:]')"
|
|
284
|
+
;;
|
|
285
|
+
"$HOME/.codex/"*)
|
|
286
|
+
capture_tooling_evidence "$path" "codex" "$(basename "$path" | tr '[:upper:]' '[:lower:]')"
|
|
287
|
+
;;
|
|
288
|
+
"$HOME/.cursor/"*)
|
|
289
|
+
capture_tooling_evidence "$path" "cursor" "$(basename "$path" | tr '[:upper:]' '[:lower:]')"
|
|
290
|
+
;;
|
|
291
|
+
esac
|
|
292
|
+
else
|
|
293
|
+
SKIPPED+=("$path")
|
|
294
|
+
fi
|
|
295
|
+
fi
|
|
296
|
+
done
|
|
297
|
+
|
|
298
|
+
for dir in "$HOME/.claude/agents" "$HOME/.cursor/rules" "$HOME/.cursor/snippets"; do
|
|
299
|
+
[ -d "$dir" ] || continue
|
|
300
|
+
while IFS= read -r path; do
|
|
301
|
+
[ -n "$path" ] || continue
|
|
302
|
+
if ! is_allowlisted_tool_file "$path"; then
|
|
303
|
+
continue
|
|
304
|
+
fi
|
|
305
|
+
if ! is_safe_tool_file "$path"; then
|
|
306
|
+
SKIPPED+=("$path")
|
|
307
|
+
continue
|
|
308
|
+
fi
|
|
309
|
+
case "$dir" in
|
|
310
|
+
"$HOME/.claude/agents")
|
|
311
|
+
capture_tooling_evidence "$path" "claude" "agents-$(basename "$path")"
|
|
312
|
+
;;
|
|
313
|
+
"$HOME/.cursor/"*)
|
|
314
|
+
capture_tooling_evidence "$path" "cursor" "$(basename "$dir")-$(basename "$path")"
|
|
315
|
+
;;
|
|
316
|
+
esac
|
|
317
|
+
done <<EOF
|
|
318
|
+
$(find "$dir" -type f \( -name "*.md" -o -name "*.json" -o -name "*.yaml" -o -name "*.yml" -o -name "*.toml" -o -name "*.txt" \) 2>/dev/null | sort)
|
|
319
|
+
EOF
|
|
320
|
+
done
|
|
321
|
+
|
|
322
|
+
if [ -z "$TOOLING_FILES" ]; then
|
|
323
|
+
WARNINGS+=("No allowlisted local tooling sources were found.")
|
|
324
|
+
else
|
|
325
|
+
if printf '%s\n' "$TOOLING_FAMILIES" | grep -q '^claude$'; then
|
|
326
|
+
write_tooling_note "claude" "Claude Tooling" "Redacted Claude settings and prompt surfaces that may affect project work."
|
|
327
|
+
fi
|
|
328
|
+
if printf '%s\n' "$TOOLING_FAMILIES" | grep -q '^codex$'; then
|
|
329
|
+
write_tooling_note "codex" "Codex Tooling" "Redacted Codex configuration surfaces that may affect project work."
|
|
330
|
+
fi
|
|
331
|
+
if printf '%s\n' "$TOOLING_FAMILIES" | grep -q '^cursor$'; then
|
|
332
|
+
write_tooling_note "cursor" "Cursor Tooling" "Redacted Cursor customization surfaces that may affect project work."
|
|
333
|
+
fi
|
|
334
|
+
write_tooling_index
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
kc_status_load
|
|
338
|
+
if [ "${DRY_RUN:-0}" -eq 0 ] && [ ${#UPDATED[@]} -gt 0 ]; then
|
|
339
|
+
STATUS_LAST_GLOBAL_SYNC="$(kc_now_utc)"
|
|
340
|
+
fi
|
|
341
|
+
STATUS_WARNING_LINES="$(printf '%s\n' "${WARNINGS[@]+"${WARNINGS[@]}"}")"
|
|
342
|
+
kc_status_write "$STATUS_WARNING_LINES"
|
|
343
|
+
|
|
344
|
+
json_summary="{"
|
|
345
|
+
json_summary="$json_summary\"script\":\"global-knowledge-sync\","
|
|
346
|
+
json_summary="$json_summary\"project_root\":\"$(kc_json_escape "$TARGET_PROJECT")\","
|
|
347
|
+
json_summary="$json_summary\"dry_run\":$(kc_json_bool "$DRY_RUN"),"
|
|
348
|
+
json_summary="$json_summary\"scanned\":$(kc_json_array "${SCANNED[@]+"${SCANNED[@]}"}"),"
|
|
349
|
+
json_summary="$json_summary\"skipped\":$(kc_json_array "${SKIPPED[@]+"${SKIPPED[@]}"}"),"
|
|
350
|
+
json_summary="$json_summary\"updated\":$(kc_json_array "${UPDATED[@]+"${UPDATED[@]}"}"),"
|
|
351
|
+
json_summary="$json_summary\"cached\":$(kc_json_array "${CACHED[@]+"${CACHED[@]}"}"),"
|
|
352
|
+
json_summary="$json_summary\"warnings\":$(kc_json_array "${WARNINGS[@]+"${WARNINGS[@]}"}")"
|
|
353
|
+
json_summary="$json_summary}"
|
|
354
|
+
kc_write_json_output "$json_summary"
|
|
355
|
+
|
|
356
|
+
if [ "$JSON_MODE" -ne 1 ]; then
|
|
357
|
+
kc_log "Global tooling sync: $TARGET_PROJECT"
|
|
358
|
+
if [ ${#SCANNED[@]} -gt 0 ]; then
|
|
359
|
+
kc_log "Scanned:"
|
|
360
|
+
printf ' %s\n' "${SCANNED[@]+"${SCANNED[@]}"}"
|
|
361
|
+
fi
|
|
362
|
+
if [ ${#CACHED[@]} -gt 0 ]; then
|
|
363
|
+
kc_log ""
|
|
364
|
+
kc_log "Cached:"
|
|
365
|
+
printf ' %s\n' "${CACHED[@]+"${CACHED[@]}"}"
|
|
366
|
+
fi
|
|
367
|
+
if [ ${#SKIPPED[@]} -gt 0 ]; then
|
|
368
|
+
kc_log ""
|
|
369
|
+
kc_log "Skipped for safety:"
|
|
370
|
+
printf ' %s\n' "${SKIPPED[@]+"${SKIPPED[@]}"}"
|
|
371
|
+
fi
|
|
372
|
+
fi
|