llm-wb 0.1.0-beta.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.
Files changed (170) hide show
  1. package/.agentic/00.chat/README.md +78 -0
  2. package/.agentic/00.chat/checklists/before-commit.md +195 -0
  3. package/.agentic/00.chat/checklists/llm-workbench-public-beta.md +94 -0
  4. package/.agentic/00.chat/commands/README.md +108 -0
  5. package/.agentic/00.chat/migration-plan.md +132 -0
  6. package/.agentic/00.chat/skills/session-summary.md +48 -0
  7. package/.agentic/00.chat/standards/llm-workbench-public-beta-contract.md +216 -0
  8. package/.agentic/00.chat/standards/main-refresh-conflict-types.md +358 -0
  9. package/.agentic/00.chat/workflows/README.md +40 -0
  10. package/.agentic/00.chat/workflows/bootstrap-chat-workbench-repo.md +212 -0
  11. package/.agentic/00.chat/workflows/chat-cleanup.md +102 -0
  12. package/.agentic/00.chat/workflows/chat-commit.md +56 -0
  13. package/.agentic/00.chat/workflows/chat-promote-to-main.md +169 -0
  14. package/.agentic/00.chat/workflows/chat-refresh-from-main.md +242 -0
  15. package/.agentic/00.chat/workflows/chat-reporting.md +69 -0
  16. package/.agentic/00.chat/workflows/chat-start.md +173 -0
  17. package/.agentic/00.chat/workflows/chat-upstream-reusable-lesson.md +123 -0
  18. package/.agentic/shared/standards/README.md +32 -0
  19. package/.agentic/shared/standards/upstream-repo-bootstrap.md +131 -0
  20. package/.agentic/shared/workflows/README.md +35 -0
  21. package/.agentic/shared/workflows/capability-resolution-workflow.md +189 -0
  22. package/.agentic/shared/workflows/change-shared-process.md +92 -0
  23. package/.cursor/rules/llm-workbench.mdc +17 -0
  24. package/.github/copilot-instructions.md +16 -0
  25. package/AGENTS.md +63 -0
  26. package/CLAUDE.md +16 -0
  27. package/CONTRIBUTING.md +57 -0
  28. package/LICENSE +21 -0
  29. package/LLM_WORKBENCH.md +17 -0
  30. package/README.md +98 -0
  31. package/SECURITY.md +44 -0
  32. package/bin/llm-workbench.js +672 -0
  33. package/docs/00.chat/README.md +47 -0
  34. package/docs/00.chat/llm-workbench-acceptance-matrix.md +55 -0
  35. package/docs/00.chat/script-layout.md +107 -0
  36. package/docs/adapting-to-your-repo.md +29 -0
  37. package/docs/concepts.md +38 -0
  38. package/docs/install.md +114 -0
  39. package/docs/public-beta-contract.md +45 -0
  40. package/docs/workflows.md +103 -0
  41. package/examples/minimal-repo/README.md +13 -0
  42. package/package.json +93 -0
  43. package/scripts/00.chat/README.md +46 -0
  44. package/scripts/00.chat/bootstrap/README.md +35 -0
  45. package/scripts/00.chat/bootstrap/audit-chat-bootstrap-file-set/README.md +39 -0
  46. package/scripts/00.chat/bootstrap/audit-chat-bootstrap-file-set/script.sh +213 -0
  47. package/scripts/00.chat/closeout/README.md +30 -0
  48. package/scripts/00.chat/closeout/build-closeout-prompt/README.md +35 -0
  49. package/scripts/00.chat/closeout/build-closeout-prompt/script.sh +124 -0
  50. package/scripts/00.chat/command/README.md +31 -0
  51. package/scripts/00.chat/command/close/README.md +30 -0
  52. package/scripts/00.chat/command/close/script.sh +25 -0
  53. package/scripts/00.chat/command/dispatcher/README.md +46 -0
  54. package/scripts/00.chat/command/dispatcher/script.sh +91 -0
  55. package/scripts/00.chat/command/dispatcher/smoke-test.sh +168 -0
  56. package/scripts/00.chat/command/new/README.md +32 -0
  57. package/scripts/00.chat/command/new/script.sh +28 -0
  58. package/scripts/00.chat/command/open-window/README.md +38 -0
  59. package/scripts/00.chat/command/open-window/script.sh +25 -0
  60. package/scripts/00.chat/command/package-scripts/README.md +34 -0
  61. package/scripts/00.chat/command/package-scripts/smoke-test.sh +113 -0
  62. package/scripts/00.chat/git/README.md +30 -0
  63. package/scripts/00.chat/git/cleanup-empty-chat-branches/README.md +36 -0
  64. package/scripts/00.chat/git/cleanup-empty-chat-branches/script.sh +243 -0
  65. package/scripts/00.chat/git/cleanup-empty-chat-branches/smoke-test.sh +136 -0
  66. package/scripts/00.chat/local-merge/README.md +30 -0
  67. package/scripts/00.chat/local-merge/list-active-chat-branches/README.md +29 -0
  68. package/scripts/00.chat/local-merge/list-active-chat-branches/script.sh +109 -0
  69. package/scripts/00.chat/local-merge/report-chat-branch-overlaps/README.md +29 -0
  70. package/scripts/00.chat/local-merge/report-chat-branch-overlaps/script.sh +142 -0
  71. package/scripts/00.chat/local-merge/verify-chat-ready-to-merge-local-main/README.md +33 -0
  72. package/scripts/00.chat/local-merge/verify-chat-ready-to-merge-local-main/script.sh +345 -0
  73. package/scripts/00.chat/local-merge/verify-chat-ready-to-merge-local-main/smoke-test.sh +244 -0
  74. package/scripts/00.chat/main-refresh/README.md +39 -0
  75. package/scripts/00.chat/main-refresh/apply-rehearsed-refresh/README.md +32 -0
  76. package/scripts/00.chat/main-refresh/apply-rehearsed-refresh/script.sh +198 -0
  77. package/scripts/00.chat/main-refresh/check-chat-is-current-with-main/README.md +30 -0
  78. package/scripts/00.chat/main-refresh/check-chat-is-current-with-main/script.sh +121 -0
  79. package/scripts/00.chat/main-refresh/classify-conflict/README.md +39 -0
  80. package/scripts/00.chat/main-refresh/classify-conflict/script.sh +169 -0
  81. package/scripts/00.chat/main-refresh/classify-conflict/smoke-test.sh +137 -0
  82. package/scripts/00.chat/main-refresh/classify-refresh-readiness/README.md +35 -0
  83. package/scripts/00.chat/main-refresh/classify-refresh-readiness/script.sh +171 -0
  84. package/scripts/00.chat/main-refresh/classify-refresh-readiness/smoke-test.sh +132 -0
  85. package/scripts/00.chat/main-refresh/rehearse-refresh-from-main/README.md +34 -0
  86. package/scripts/00.chat/main-refresh/rehearse-refresh-from-main/script.sh +124 -0
  87. package/scripts/00.chat/main-refresh/rehearse-refresh-from-main/smoke-test.sh +257 -0
  88. package/scripts/00.chat/main-refresh/show-main-update-status/README.md +31 -0
  89. package/scripts/00.chat/main-refresh/show-main-update-status/script.sh +73 -0
  90. package/scripts/00.chat/main-refresh/verify-conflict-audit/README.md +37 -0
  91. package/scripts/00.chat/main-refresh/verify-conflict-audit/script.sh +154 -0
  92. package/scripts/00.chat/main-refresh/verify-conflict-audit/smoke-test.sh +99 -0
  93. package/scripts/00.chat/metrics/README.md +35 -0
  94. package/scripts/00.chat/metrics/data/chat-pricing.json +107 -0
  95. package/scripts/00.chat/metrics/data/chat-pricing.schema.json +63 -0
  96. package/scripts/00.chat/metrics/estimate-chat-cost/README.md +40 -0
  97. package/scripts/00.chat/metrics/estimate-chat-cost/script.js +130 -0
  98. package/scripts/00.chat/migration/README.md +30 -0
  99. package/scripts/00.chat/migration/audit-chat-layer-migration/README.md +33 -0
  100. package/scripts/00.chat/migration/audit-chat-layer-migration/script.sh +127 -0
  101. package/scripts/00.chat/recovery/README.md +30 -0
  102. package/scripts/00.chat/recovery/import-active-paths-to-chat-worktree/README.md +76 -0
  103. package/scripts/00.chat/recovery/import-active-paths-to-chat-worktree/script.sh +212 -0
  104. package/scripts/00.chat/recovery/import-active-paths-to-chat-worktree/smoke-test.sh +162 -0
  105. package/scripts/00.chat/reporting/README.md +30 -0
  106. package/scripts/00.chat/reporting/generate-commit-log-summary/README.md +35 -0
  107. package/scripts/00.chat/reporting/generate-commit-log-summary/script.sh +299 -0
  108. package/scripts/00.chat/reporting/generate-commit-log-summary/smoke-test.sh +93 -0
  109. package/scripts/00.chat/reporting/report-chat-workspaces/README.md +32 -0
  110. package/scripts/00.chat/reporting/report-chat-workspaces/script.sh +82 -0
  111. package/scripts/00.chat/session-log/README.md +33 -0
  112. package/scripts/00.chat/session-log/check-commit-prerequisites/README.md +89 -0
  113. package/scripts/00.chat/session-log/check-commit-prerequisites/script.sh +121 -0
  114. package/scripts/00.chat/session-log/check-commit-prerequisites/smoke-test.sh +119 -0
  115. package/scripts/00.chat/session-log/check-commitlog-deletions/README.md +90 -0
  116. package/scripts/00.chat/session-log/check-commitlog-deletions/script.sh +131 -0
  117. package/scripts/00.chat/session-log/check-commitlog-deletions/smoke-test.sh +123 -0
  118. package/scripts/00.chat/session-log/checkpoint-chat-session-log/README.md +98 -0
  119. package/scripts/00.chat/session-log/checkpoint-chat-session-log/script.sh +126 -0
  120. package/scripts/00.chat/session-log/paths/README.md +38 -0
  121. package/scripts/00.chat/session-log/paths/lib.sh +133 -0
  122. package/scripts/00.chat/session-log/prepare-chat-session-before-commit/README.md +90 -0
  123. package/scripts/00.chat/session-log/prepare-chat-session-before-commit/script.sh +145 -0
  124. package/scripts/00.chat/session-log/read-current-chat-log/README.md +44 -0
  125. package/scripts/00.chat/session-log/read-current-chat-log/script.sh +92 -0
  126. package/scripts/00.chat/session-log/read-current-chat-log/smoke-test.sh +127 -0
  127. package/scripts/00.chat/session-log/record-chat-commit/README.md +133 -0
  128. package/scripts/00.chat/session-log/record-chat-commit/script.sh +394 -0
  129. package/scripts/00.chat/session-log/record-chat-commit/smoke-test.sh +227 -0
  130. package/scripts/00.chat/session-log/record-main-refresh-conflict/README.md +34 -0
  131. package/scripts/00.chat/session-log/record-main-refresh-conflict/script.sh +239 -0
  132. package/scripts/00.chat/session-log/rename-current-chat-log-folder/README.md +32 -0
  133. package/scripts/00.chat/session-log/rename-current-chat-log-folder/script.sh +112 -0
  134. package/scripts/00.chat/session-log/update-chat-log/README.md +32 -0
  135. package/scripts/00.chat/session-log/update-chat-log/script.sh +294 -0
  136. package/scripts/00.chat/startup/README.md +37 -0
  137. package/scripts/00.chat/startup/auto-start-missing-session/README.md +113 -0
  138. package/scripts/00.chat/startup/auto-start-missing-session/script.sh +54 -0
  139. package/scripts/00.chat/startup/resolve-current-chat-session/README.md +57 -0
  140. package/scripts/00.chat/startup/resolve-current-chat-session/script.sh +47 -0
  141. package/scripts/00.chat/startup/resolve-current-chat-session/smoke-test.sh +130 -0
  142. package/scripts/00.chat/startup/start-chat-session/README.md +197 -0
  143. package/scripts/00.chat/startup/start-chat-session/script.sh +330 -0
  144. package/scripts/00.chat/startup/start-chat-session/smoke-test.sh +182 -0
  145. package/scripts/00.chat/startup/start-new-chat/README.md +31 -0
  146. package/scripts/00.chat/startup/start-new-chat/script.sh +29 -0
  147. package/scripts/00.chat/transcript/README.md +36 -0
  148. package/scripts/00.chat/transcript/discover-codex-session-log/README.md +32 -0
  149. package/scripts/00.chat/transcript/discover-codex-session-log/script.sh +106 -0
  150. package/scripts/00.chat/transcript/register-codex-session-log/README.md +32 -0
  151. package/scripts/00.chat/transcript/register-codex-session-log/script.sh +115 -0
  152. package/scripts/00.chat/worktree/README.md +32 -0
  153. package/scripts/00.chat/worktree/check-write-location/README.md +87 -0
  154. package/scripts/00.chat/worktree/check-write-location/script.sh +95 -0
  155. package/scripts/00.chat/worktree/dirty-worktree-check/README.md +77 -0
  156. package/scripts/00.chat/worktree/dirty-worktree-check/script.sh +93 -0
  157. package/scripts/00.chat/worktree/ensure-chat-worktree/README.md +33 -0
  158. package/scripts/00.chat/worktree/ensure-chat-worktree/script.sh +132 -0
  159. package/scripts/00.chat/worktree/open-window/README.md +34 -0
  160. package/scripts/00.chat/worktree/open-window/script.sh +131 -0
  161. package/scripts/00.chat/worktree/paths/README.md +32 -0
  162. package/scripts/00.chat/worktree/paths/lib.sh +71 -0
  163. package/scripts/01.harness/artifact-metadata/check-headers/script.sh +522 -0
  164. package/scripts/01.harness/artifact-metadata/check-headers/smoke-test.sh +48 -0
  165. package/scripts/01.harness/check-deterministic-process-drift.sh +416 -0
  166. package/scripts/01.harness/check-governed-script-command-drift.sh +184 -0
  167. package/scripts/01.harness/run-governed-script.sh +178 -0
  168. package/scripts/install.sh +503 -0
  169. package/scripts/uninstall.sh +199 -0
  170. package/tests/smoke-test-install.sh +70 -0
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # agentic-artifact:
5
+ # schema: agentic-artifact/v2
6
+ # id: harness.script.run-governed-script
7
+ # version: 1
8
+ # status: active
9
+ # layer: 01.harness
10
+ # domain: governance
11
+ # disciplines:
12
+ # - agentic
13
+ # kind: script
14
+ # purpose: Run only explicitly governed repository scripts with approval-sensitive routing.
15
+ # portability:
16
+ # class: required
17
+ # targets:
18
+ # - llm-workbench
19
+ # used_by:
20
+ # - id: harness.standards.governed-script-permissions
21
+ # - id: chat.workflows.chat-start
22
+ # path: .agentic/00.chat/workflows/chat-start.md
23
+ # effects:
24
+ # - read-only
25
+
26
+ usage() {
27
+ cat <<'EOF'
28
+ Usage:
29
+ run-governed-script.sh [--approved-action] <script> [args...]
30
+ run-governed-script.sh --list
31
+
32
+ Runs only explicitly governed repository scripts.
33
+
34
+ Use --approved-action only after the current chat has explicit approval for the
35
+ action class governed by the active workflow.
36
+ EOF
37
+ }
38
+
39
+ has_artifact_header() {
40
+ sed -n '1,80p' "$1" | grep -Fq "agentic-artifact:"
41
+ }
42
+
43
+ metadata_has_line() {
44
+ local path="$1"
45
+ local value="$2"
46
+
47
+ sed -n '1,140p' "$path" | grep -Eq "^[#[:space:]]*-[[:space:]]*$value[[:space:]]*$"
48
+ }
49
+
50
+ has_never_persistent_effect() {
51
+ local path="$1"
52
+
53
+ case "$path" in
54
+ scripts/00.chat/main-refresh/apply-rehearsed-refresh/script.sh)
55
+ return 1
56
+ ;;
57
+ esac
58
+
59
+ metadata_has_line "$path" "destructive" ||
60
+ metadata_has_line "$path" "push" ||
61
+ metadata_has_line "$path" "history-rewrite" ||
62
+ metadata_has_line "$path" "overwrites" ||
63
+ metadata_has_line "$path" "cloud" ||
64
+ metadata_has_line "$path" "database"
65
+ }
66
+
67
+ classify_script() {
68
+ local path="$1"
69
+
70
+ case "$path" in
71
+ */lib.sh)
72
+ echo "ERROR: governed library files must not be invoked directly: $path" >&2
73
+ return 1
74
+ ;;
75
+ esac
76
+
77
+ case "$path" in
78
+ scripts/[0-9][0-9].*/*.sh|scripts/[0-9][0-9].*/*/script.sh|scripts/[0-9][0-9].*/*/smoke-test.sh|\
79
+ scripts/[0-9][0-9].*/*/*/script.sh|scripts/[0-9][0-9].*/*/*/smoke-test.sh)
80
+ ;;
81
+ scripts/shared/*.sh|scripts/shared/*/*.sh)
82
+ echo "ERROR: retired shared script path must not be invoked directly: $path" >&2
83
+ return 1
84
+ ;;
85
+ *)
86
+ echo "ERROR: refused script outside canonical governed script paths: $path" >&2
87
+ return 1
88
+ ;;
89
+ esac
90
+
91
+ if [ ! -f "$path" ]; then
92
+ echo "ERROR: governed script does not exist: $path" >&2
93
+ return 1
94
+ fi
95
+
96
+ if ! has_artifact_header "$path"; then
97
+ echo "ERROR: script is missing governed agentic-artifact metadata: $path" >&2
98
+ return 1
99
+ fi
100
+
101
+ if has_never_persistent_effect "$path"; then
102
+ echo "ERROR: script has effects that are never persistent-auto-approved: $path" >&2
103
+ return 1
104
+ fi
105
+
106
+ if metadata_has_line "$path" "read-only" &&
107
+ ! metadata_has_line "$path" "writes-files" &&
108
+ ! metadata_has_line "$path" "commits" &&
109
+ ! metadata_has_line "$path" "network"; then
110
+ printf '%s\n' "always"
111
+ return 0
112
+ fi
113
+
114
+ printf '%s\n' "approved"
115
+ }
116
+
117
+ APPROVED_ACTION="no"
118
+
119
+ if [ $# -eq 0 ]; then
120
+ usage >&2
121
+ exit 2
122
+ fi
123
+
124
+ case "$1" in
125
+ --approved-action)
126
+ APPROVED_ACTION="yes"
127
+ shift
128
+ ;;
129
+ --list)
130
+ REPO_ROOT="$(git rev-parse --show-toplevel)"
131
+ cd "$REPO_ROOT"
132
+ git ls-files 'scripts/*.sh' 'scripts/*/*.sh' 'scripts/*/*/*.sh' 'scripts/*/*/*/*.sh' |
133
+ while IFS= read -r script_path; do
134
+ run_class="$(classify_script "$script_path" 2>/dev/null || true)"
135
+ if [ -n "$run_class" ]; then
136
+ printf '%s %s\n' "$run_class" "$script_path"
137
+ fi
138
+ done
139
+ exit 0
140
+ ;;
141
+ -h|--help)
142
+ usage
143
+ exit 0
144
+ ;;
145
+ esac
146
+
147
+ if [ $# -eq 0 ]; then
148
+ usage >&2
149
+ exit 2
150
+ fi
151
+
152
+ SCRIPT_PATH="$1"
153
+ shift
154
+
155
+ case "$SCRIPT_PATH" in
156
+ /*|*../*|../*|*"/.."|*".."|*"
157
+ "*)
158
+ echo "ERROR: refused non-repository script path: $SCRIPT_PATH" >&2
159
+ exit 1
160
+ ;;
161
+ esac
162
+
163
+ REPO_ROOT="$(git rev-parse --show-toplevel)"
164
+ cd "$REPO_ROOT"
165
+
166
+ RUN_CLASS="$(classify_script "$SCRIPT_PATH")"
167
+
168
+ if [ "$RUN_CLASS" = "approved" ] && [ "$APPROVED_ACTION" != "yes" ]; then
169
+ echo "ERROR: approval-sensitive script requires --approved-action: $SCRIPT_PATH" >&2
170
+ exit 1
171
+ fi
172
+
173
+ if [ ! -f "$SCRIPT_PATH" ]; then
174
+ echo "ERROR: governed script does not exist: $SCRIPT_PATH" >&2
175
+ exit 1
176
+ fi
177
+
178
+ exec bash "$SCRIPT_PATH" "$@"
@@ -0,0 +1,503 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # agentic-artifact:
5
+ # schema: agentic-artifact/v2
6
+ # id: chat.script.install
7
+ # version: 1
8
+ # status: active
9
+ # layer: 00.chat
10
+ # domain: install
11
+ # disciplines:
12
+ # - agentic
13
+ # kind: script
14
+ # purpose: Install the llm-workbench chat harness into a target Git repository.
15
+ # portability:
16
+ # class: required
17
+ # targets:
18
+ # - llm-workbench
19
+ # used_by:
20
+ # - id: llm-workbench.docs.install
21
+ # effects:
22
+ # - writes-files
23
+ # - commits
24
+
25
+ usage() {
26
+ cat <<'EOF'
27
+ Usage:
28
+ scripts/install.sh [--dry-run|--apply] [--init-commit] <target-git-repo>
29
+
30
+ Installs the llm-workbench chat harness into a target Git repository.
31
+
32
+ The installer always plans first. Apply mode refuses to write when the plan has
33
+ conflicts. Existing package.json files are merged by adding only workbench-owned
34
+ chat:* scripts. Existing assistant instruction files are patched with a managed
35
+ llm-workbench block instead of being overwritten.
36
+ EOF
37
+ }
38
+
39
+ MODE=""
40
+ INIT_COMMIT="no"
41
+ TARGET_REPO=""
42
+
43
+ while [ $# -gt 0 ]; do
44
+ case "$1" in
45
+ --dry-run)
46
+ [ -z "$MODE" ] || { echo "ERROR: choose one mode." >&2; exit 2; }
47
+ MODE="dry-run"
48
+ shift
49
+ ;;
50
+ --apply)
51
+ [ -z "$MODE" ] || { echo "ERROR: choose one mode." >&2; exit 2; }
52
+ MODE="apply"
53
+ shift
54
+ ;;
55
+ --init-commit)
56
+ INIT_COMMIT="yes"
57
+ shift
58
+ ;;
59
+ -h|--help)
60
+ usage
61
+ exit 0
62
+ ;;
63
+ -*)
64
+ echo "ERROR: unknown argument: $1" >&2
65
+ usage >&2
66
+ exit 2
67
+ ;;
68
+ *)
69
+ [ -z "$TARGET_REPO" ] || { echo "ERROR: target repo specified twice." >&2; exit 2; }
70
+ TARGET_REPO="$1"
71
+ shift
72
+ ;;
73
+ esac
74
+ done
75
+
76
+ MODE="${MODE:-apply}"
77
+
78
+ if [ -z "$TARGET_REPO" ]; then
79
+ usage >&2
80
+ exit 2
81
+ fi
82
+
83
+ if [ ! -d "$TARGET_REPO/.git" ]; then
84
+ echo "ERROR: target is not a Git repo: $TARGET_REPO" >&2
85
+ exit 1
86
+ fi
87
+
88
+ if [ "$INIT_COMMIT" = "yes" ] && [ "$MODE" = "apply" ]; then
89
+ if git -C "$TARGET_REPO" rev-parse --verify HEAD >/dev/null 2>&1; then
90
+ echo "ERROR: --init-commit is only for repos with no existing HEAD." >&2
91
+ exit 2
92
+ fi
93
+ fi
94
+
95
+ SOURCE_REPO="$(cd "$(dirname "$0")/.." && pwd)"
96
+ TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/llm-workbench-install-plan.XXXXXX")"
97
+ PLAN_PATHS="$TMP_DIR/planned-paths.txt"
98
+ FILE_ACTIONS="$TMP_DIR/file-actions.tsv"
99
+ PACKAGE_OUTPUT="$TMP_DIR/package-output.txt"
100
+ MANIFEST_OUTPUT="$TMP_DIR/install-manifest.tsv"
101
+ MANIFEST_PATH="$TARGET_REPO/.llm-workbench/install-manifest.tsv"
102
+
103
+ cleanup() {
104
+ rm -rf "$TMP_DIR"
105
+ }
106
+ trap cleanup EXIT
107
+
108
+ : > "$PLAN_PATHS"
109
+ : > "$FILE_ACTIONS"
110
+ : > "$MANIFEST_OUTPUT"
111
+
112
+ CREATE_COUNT=0
113
+ SAME_COUNT=0
114
+ PATCH_COUNT=0
115
+ CONFLICT_COUNT=0
116
+ PACKAGE_CONFLICTS="no"
117
+
118
+ managed_block() {
119
+ cat <<'EOF'
120
+
121
+ <!-- llm-workbench:start -->
122
+ ## llm-workbench
123
+
124
+ Follow `.agentic/00.chat/workflows/chat-start.md` at the start of each chat.
125
+ Use `commitLogs/<session>/README.md` as the first source of truth for chat
126
+ lifecycle, branch, worktree, context-packet references, commits, and metrics.
127
+
128
+ Do not assign the whole chat a durable layer, mode, or workflow. When a prompt
129
+ needs layer, mode, workflow, corpus, or rule context, use the current user
130
+ request, this repo's assistant instructions, and any repo-provided context
131
+ router if one exists.
132
+
133
+ Default mode after governed chat-start bootstrap is read-only until the user
134
+ explicitly grants write permission for task files.
135
+ <!-- llm-workbench:end -->
136
+ EOF
137
+ }
138
+
139
+ record_path() {
140
+ printf '%s\n' "$1" >> "$PLAN_PATHS"
141
+ }
142
+
143
+ plan_file() {
144
+ local source="$1"
145
+ local relative_path="$2"
146
+ local target="$TARGET_REPO/$relative_path"
147
+
148
+ record_path "$relative_path"
149
+
150
+ if [ -e "$target" ]; then
151
+ if cmp -s "$source" "$target"; then
152
+ echo "SAME $relative_path"
153
+ printf 'SAME\t%s\t%s\n' "$source" "$relative_path" >> "$FILE_ACTIONS"
154
+ SAME_COUNT=$((SAME_COUNT + 1))
155
+ else
156
+ echo "CONFLICT $relative_path"
157
+ printf 'CONFLICT\t%s\t%s\n' "$source" "$relative_path" >> "$FILE_ACTIONS"
158
+ CONFLICT_COUNT=$((CONFLICT_COUNT + 1))
159
+ fi
160
+ else
161
+ echo "CREATE $relative_path"
162
+ printf 'CREATE\t%s\t%s\n' "$source" "$relative_path" >> "$FILE_ACTIONS"
163
+ CREATE_COUNT=$((CREATE_COUNT + 1))
164
+ fi
165
+ }
166
+
167
+ plan_instruction_file() {
168
+ local source="$1"
169
+ local relative_path="$2"
170
+ local target="$TARGET_REPO/$relative_path"
171
+
172
+ [ -f "$source" ] || return 0
173
+ record_path "$relative_path"
174
+
175
+ if [ ! -e "$target" ]; then
176
+ echo "CREATE $relative_path"
177
+ printf 'CREATE\t%s\t%s\n' "$source" "$relative_path" >> "$FILE_ACTIONS"
178
+ CREATE_COUNT=$((CREATE_COUNT + 1))
179
+ return 0
180
+ fi
181
+
182
+ if cmp -s "$source" "$target"; then
183
+ echo "SAME $relative_path"
184
+ printf 'SAME\t%s\t%s\n' "$source" "$relative_path" >> "$FILE_ACTIONS"
185
+ SAME_COUNT=$((SAME_COUNT + 1))
186
+ return 0
187
+ fi
188
+
189
+ if grep -q 'llm-workbench:start' "$target"; then
190
+ echo "SAME_BLOCK $relative_path"
191
+ printf 'SAME_BLOCK\t%s\t%s\n' "$source" "$relative_path" >> "$FILE_ACTIONS"
192
+ SAME_COUNT=$((SAME_COUNT + 1))
193
+ else
194
+ echo "PATCH_BLOCK $relative_path"
195
+ printf 'PATCH_BLOCK\t%s\t%s\n' "$source" "$relative_path" >> "$FILE_ACTIONS"
196
+ PATCH_COUNT=$((PATCH_COUNT + 1))
197
+ fi
198
+ }
199
+
200
+ plan_tree() {
201
+ local tree="$1"
202
+ local file
203
+ local relative_path
204
+
205
+ [ -d "$SOURCE_REPO/$tree" ] || return 0
206
+
207
+ while IFS= read -r file; do
208
+ relative_path="${file#$SOURCE_REPO/}"
209
+ case "$relative_path" in
210
+ scripts/00.chat/upstream/*)
211
+ continue
212
+ ;;
213
+ esac
214
+ plan_file "$file" "$relative_path"
215
+ done < <(find "$SOURCE_REPO/$tree" -type f | sort)
216
+ }
217
+
218
+ plan_selected_file() {
219
+ local path="$1"
220
+
221
+ [ -f "$SOURCE_REPO/$path" ] || return 0
222
+ plan_file "$SOURCE_REPO/$path" "$path"
223
+ }
224
+
225
+ plan_public_harness_scripts() {
226
+ plan_selected_file "scripts/01.harness/run-governed-script.sh"
227
+ plan_selected_file "scripts/01.harness/check-deterministic-process-drift.sh"
228
+ plan_selected_file "scripts/01.harness/check-governed-script-command-drift.sh"
229
+ plan_selected_file "scripts/01.harness/artifact-metadata/check-headers/script.sh"
230
+ plan_selected_file "scripts/01.harness/artifact-metadata/check-headers/smoke-test.sh"
231
+ }
232
+
233
+ plan_package_json() {
234
+ local target_package="$TARGET_REPO/package.json"
235
+ local template_package="$SOURCE_REPO/package.json"
236
+
237
+ record_path "package.json"
238
+
239
+ if [ ! -f "$target_package" ]; then
240
+ echo "CREATE package.json"
241
+ printf 'CREATE_PACKAGE\t%s\tpackage.json\n' "$template_package" >> "$FILE_ACTIONS"
242
+ CREATE_COUNT=$((CREATE_COUNT + 1))
243
+ return 0
244
+ fi
245
+
246
+ printf 'MERGE_PACKAGE\t%s\tpackage.json\n' "$template_package" >> "$FILE_ACTIONS"
247
+
248
+ if ! node - "$target_package" "$template_package" > "$PACKAGE_OUTPUT" <<'NODE'
249
+ const fs = require('fs');
250
+ const targetPath = process.argv[2];
251
+ const templatePath = process.argv[3];
252
+
253
+ let target;
254
+ let template;
255
+
256
+ try {
257
+ target = JSON.parse(fs.readFileSync(targetPath, 'utf8'));
258
+ } catch (error) {
259
+ console.log(`CONFLICT package.json invalid-json ${error.message}`);
260
+ process.exit(1);
261
+ }
262
+
263
+ try {
264
+ template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
265
+ } catch (error) {
266
+ console.log(`CONFLICT package.json template-invalid-json ${error.message}`);
267
+ process.exit(1);
268
+ }
269
+
270
+ const actualScripts = target.scripts || {};
271
+ const expectedScripts = template.scripts || {};
272
+ let conflicts = 0;
273
+
274
+ for (const [name, expected] of Object.entries(expectedScripts)) {
275
+ const actual = actualScripts[name];
276
+ if (!name.startsWith('chat:') && name !== 'chat') {
277
+ continue;
278
+ }
279
+ if (actual === undefined) {
280
+ console.log(`PACKAGE_ADD_SCRIPT ${name} ${expected}`);
281
+ } else if (actual === expected) {
282
+ console.log(`PACKAGE_SAME_SCRIPT ${name}`);
283
+ } else {
284
+ console.log(`PACKAGE_CONFLICT_SCRIPT ${name} actual=${actual} expected=${expected}`);
285
+ conflicts += 1;
286
+ }
287
+ }
288
+
289
+ for (const name of Object.keys(actualScripts).sort()) {
290
+ if (expectedScripts[name] === undefined || (!name.startsWith('chat:') && name !== 'chat')) {
291
+ console.log(`PACKAGE_PRESERVE_SCRIPT ${name} ${actualScripts[name]}`);
292
+ }
293
+ }
294
+
295
+ process.exit(conflicts > 0 ? 1 : 0);
296
+ NODE
297
+ then
298
+ PACKAGE_CONFLICTS="yes"
299
+ CONFLICT_COUNT=$((CONFLICT_COUNT + 1))
300
+ fi
301
+
302
+ cat "$PACKAGE_OUTPUT"
303
+ }
304
+
305
+ print_header() {
306
+ local head
307
+ local branch
308
+
309
+ head="$(git -C "$TARGET_REPO" rev-parse --verify HEAD 2>/dev/null || true)"
310
+ branch="$(git -C "$TARGET_REPO" branch --show-current 2>/dev/null || true)"
311
+
312
+ echo "llm-workbench install ${MODE}"
313
+ echo
314
+ echo "Source repo: $SOURCE_REPO"
315
+ echo "Target repo: $TARGET_REPO"
316
+ echo "Target branch: ${branch:-<none>}"
317
+ echo "Target HEAD: ${head:-<unborn>}"
318
+ echo "Target OS: ${LLM_WORKBENCH_TARGET_OS:-auto}"
319
+ echo
320
+ }
321
+
322
+ print_header
323
+
324
+ echo "Package plan:"
325
+ plan_package_json
326
+ echo
327
+
328
+ echo "File plan:"
329
+ plan_instruction_file "$SOURCE_REPO/AGENTS.md" "AGENTS.md"
330
+ plan_instruction_file "$SOURCE_REPO/CLAUDE.md" "CLAUDE.md"
331
+ plan_instruction_file "$SOURCE_REPO/.github/copilot-instructions.md" ".github/copilot-instructions.md"
332
+ plan_instruction_file "$SOURCE_REPO/.cursor/rules/llm-workbench.mdc" ".cursor/rules/llm-workbench.mdc"
333
+ plan_instruction_file "$SOURCE_REPO/LLM_WORKBENCH.md" "LLM_WORKBENCH.md"
334
+ plan_tree "bin"
335
+ plan_tree ".agentic/00.chat"
336
+ plan_tree ".agentic/shared"
337
+ plan_tree "scripts/00.chat"
338
+ plan_public_harness_scripts
339
+ echo
340
+
341
+ echo "Excluded source paths:"
342
+ echo "EXCLUDE commitLogs/"
343
+ echo "EXCLUDE .agentic/01.harness/"
344
+ echo "EXCLUDE scripts/00.chat/upstream/"
345
+ echo "EXCLUDE docs/"
346
+ echo "EXCLUDE public repo templates, local transcripts, and local worktree paths"
347
+ echo
348
+
349
+ echo "Summary:"
350
+ echo "create: $CREATE_COUNT"
351
+ echo "same: $SAME_COUNT"
352
+ echo "patch: $PATCH_COUNT"
353
+ echo "conflicts: $CONFLICT_COUNT"
354
+ echo "package_conflicts: $PACKAGE_CONFLICTS"
355
+ echo "mode: $MODE"
356
+
357
+ if [ "$CONFLICT_COUNT" -gt 0 ]; then
358
+ exit 1
359
+ fi
360
+
361
+ copy_file() {
362
+ local source="$1"
363
+ local relative_path="$2"
364
+ local target="$TARGET_REPO/$relative_path"
365
+
366
+ mkdir -p "$(dirname "$target")"
367
+ cp "$source" "$target"
368
+ }
369
+
370
+ record_manifest() {
371
+ local kind="$1"
372
+ local value="$2"
373
+
374
+ printf '%s\t%s\n' "$kind" "$value" >> "$MANIFEST_OUTPUT"
375
+ }
376
+
377
+ patch_instruction_file() {
378
+ local relative_path="$1"
379
+ local target="$TARGET_REPO/$relative_path"
380
+
381
+ managed_block >> "$target"
382
+ }
383
+
384
+ merge_package_json() {
385
+ local target_package="$TARGET_REPO/package.json"
386
+ local template_package="$SOURCE_REPO/package.json"
387
+
388
+ node - "$target_package" "$template_package" <<'NODE'
389
+ const fs = require('fs');
390
+ const targetPath = process.argv[2];
391
+ const templatePath = process.argv[3];
392
+
393
+ const target = JSON.parse(fs.readFileSync(targetPath, 'utf8'));
394
+ const template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
395
+
396
+ target.scripts = target.scripts || {};
397
+ for (const [name, expected] of Object.entries(template.scripts || {})) {
398
+ if (!name.startsWith('chat:') && name !== 'chat') {
399
+ continue;
400
+ }
401
+ if (target.scripts[name] === undefined || target.scripts[name] === expected) {
402
+ target.scripts[name] = expected;
403
+ } else {
404
+ throw new Error(`conflicting script during apply: ${name}`);
405
+ }
406
+ }
407
+
408
+ fs.writeFileSync(targetPath, `${JSON.stringify(target, null, 2)}\n`);
409
+ NODE
410
+ }
411
+
412
+ record_added_package_scripts() {
413
+ sed -n 's/^PACKAGE_ADD_SCRIPT \([^ ]*\) .*/\1/p' "$PACKAGE_OUTPUT" \
414
+ | while IFS= read -r script_name; do
415
+ [ -n "$script_name" ] || continue
416
+ record_manifest "package-script" "$script_name"
417
+ done
418
+ }
419
+
420
+ apply_plan() {
421
+ local action
422
+ local source
423
+ local relative_path
424
+
425
+ echo
426
+ echo "Applying clean plan..."
427
+
428
+ while IFS=$'\t' read -r action source relative_path; do
429
+ case "$action" in
430
+ CREATE)
431
+ copy_file "$source" "$relative_path"
432
+ record_manifest "create" "$relative_path"
433
+ echo "APPLIED_CREATE $relative_path"
434
+ ;;
435
+ CREATE_PACKAGE)
436
+ copy_file "$source" "$relative_path"
437
+ record_manifest "create" "$relative_path"
438
+ echo "APPLIED_CREATE package.json"
439
+ ;;
440
+ MERGE_PACKAGE)
441
+ merge_package_json
442
+ record_added_package_scripts
443
+ echo "APPLIED_MERGE package.json"
444
+ ;;
445
+ PATCH_BLOCK)
446
+ patch_instruction_file "$relative_path"
447
+ record_manifest "patch-block" "$relative_path"
448
+ echo "APPLIED_PATCH_BLOCK $relative_path"
449
+ ;;
450
+ SAME|SAME_BLOCK)
451
+ :
452
+ ;;
453
+ *)
454
+ echo "ERROR: unexpected action in clean plan: $action" >&2
455
+ exit 1
456
+ ;;
457
+ esac
458
+ done < "$FILE_ACTIONS"
459
+
460
+ echo "Apply completed."
461
+
462
+ mkdir -p "$(dirname "$MANIFEST_PATH")"
463
+ cp "$MANIFEST_OUTPUT" "$MANIFEST_PATH"
464
+ echo "Wrote install manifest: ${MANIFEST_PATH#$TARGET_REPO/}"
465
+ }
466
+
467
+ if [ "$MODE" = "apply" ]; then
468
+ apply_plan
469
+ fi
470
+
471
+ stage_install_paths() {
472
+ local kind
473
+ local value
474
+ local staged_package_json="no"
475
+
476
+ while IFS=$'\t' read -r kind value; do
477
+ case "$kind" in
478
+ create|patch-block)
479
+ git -C "$TARGET_REPO" add -- "$value"
480
+ ;;
481
+ package-script)
482
+ if [ "$staged_package_json" = "no" ]; then
483
+ git -C "$TARGET_REPO" add -- package.json
484
+ staged_package_json="yes"
485
+ fi
486
+ ;;
487
+ esac
488
+ done < "$MANIFEST_OUTPUT"
489
+
490
+ git -C "$TARGET_REPO" add -- .llm-workbench/install-manifest.tsv
491
+ }
492
+
493
+ if [ "$INIT_COMMIT" = "yes" ] && [ "$MODE" = "apply" ]; then
494
+ echo
495
+ echo "Creating install commit..."
496
+ stage_install_paths
497
+ if ! git -C "$TARGET_REPO" diff --cached --quiet; then
498
+ git -C "$TARGET_REPO" commit -m "Install llm-workbench harness"
499
+ echo "Install commit created."
500
+ else
501
+ echo "No install changes to commit."
502
+ fi
503
+ fi