agent-composer 0.3.1 → 0.4.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/README.md +495 -180
- package/composer.config.schema.json +206 -2
- package/dist/cli/cleanup.d.ts +24 -0
- package/dist/cli/cleanup.js +151 -0
- package/dist/cli/cleanup.js.map +1 -0
- package/dist/cli/doctor.d.ts +12 -0
- package/dist/cli/doctor.js +244 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/goal.d.ts +28 -0
- package/dist/cli/goal.js +251 -0
- package/dist/cli/goal.js.map +1 -0
- package/dist/cli/help.d.ts +3 -0
- package/dist/cli/help.js +31 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/init.d.ts +5 -0
- package/dist/cli/init.js +116 -21
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/initArgs.d.ts +16 -0
- package/dist/cli/initArgs.js +19 -0
- package/dist/cli/initArgs.js.map +1 -0
- package/dist/cli/installGitHook.d.ts +7 -0
- package/dist/cli/installGitHook.js +61 -0
- package/dist/cli/installGitHook.js.map +1 -0
- package/dist/cli/mode.d.ts +6 -0
- package/dist/cli/mode.js +25 -0
- package/dist/cli/mode.js.map +1 -0
- package/dist/cli/status.d.ts +105 -0
- package/dist/cli/status.js +400 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/config/env.d.ts +1 -1
- package/dist/config/modes.d.ts +10 -0
- package/dist/config/modes.js +26 -0
- package/dist/config/modes.js.map +1 -0
- package/dist/config/oracleRole.d.ts +10 -0
- package/dist/config/oracleRole.js +11 -0
- package/dist/config/oracleRole.js.map +1 -0
- package/dist/config/schema.d.ts +246 -0
- package/dist/config/schema.js +127 -2
- package/dist/config/schema.js.map +1 -1
- package/dist/evolve/reflection.d.ts +9 -0
- package/dist/evolve/reflection.js +14 -0
- package/dist/evolve/reflection.js.map +1 -1
- package/dist/evolve/runner.d.ts +2 -1
- package/dist/evolve/runner.js +2 -1
- package/dist/evolve/runner.js.map +1 -1
- package/dist/index.js +115 -6
- package/dist/index.js.map +1 -1
- package/dist/providers/AnthropicCompatibleProvider.d.ts +13 -1
- package/dist/providers/AnthropicCompatibleProvider.js +115 -9
- package/dist/providers/AnthropicCompatibleProvider.js.map +1 -1
- package/dist/providers/CLIProvider.d.ts +18 -0
- package/dist/providers/CLIProvider.js +265 -62
- package/dist/providers/CLIProvider.js.map +1 -1
- package/dist/providers/IProvider.d.ts +12 -0
- package/dist/providers/SpendGuardProvider.d.ts +32 -0
- package/dist/providers/SpendGuardProvider.js +98 -0
- package/dist/providers/SpendGuardProvider.js.map +1 -0
- package/dist/registry.d.ts +5 -2
- package/dist/registry.js +17 -2
- package/dist/registry.js.map +1 -1
- package/dist/server/activeRuns.d.ts +17 -0
- package/dist/server/activeRuns.js +114 -0
- package/dist/server/activeRuns.js.map +1 -0
- package/dist/server/codexLifecycleRunner.d.ts +29 -0
- package/dist/server/codexLifecycleRunner.js +188 -0
- package/dist/server/codexLifecycleRunner.js.map +1 -0
- package/dist/server/configMutation.d.ts +22 -0
- package/dist/server/configMutation.js +121 -0
- package/dist/server/configMutation.js.map +1 -0
- package/dist/server/handoffContext.d.ts +1 -0
- package/dist/server/handoffContext.js +12 -0
- package/dist/server/handoffContext.js.map +1 -0
- package/dist/server/progress.d.ts +24 -0
- package/dist/server/progress.js +109 -0
- package/dist/server/progress.js.map +1 -0
- package/dist/server/toolDescriptions.d.ts +60 -0
- package/dist/server/toolDescriptions.js +134 -0
- package/dist/server/toolDescriptions.js.map +1 -0
- package/dist/server.d.ts +19 -25
- package/dist/server.js +87 -377
- package/dist/server.js.map +1 -1
- package/dist/tools/audit.d.ts +2 -0
- package/dist/tools/audit.js +66 -0
- package/dist/tools/audit.js.map +1 -0
- package/dist/tools/code.d.ts +2 -0
- package/dist/tools/code.js +160 -0
- package/dist/tools/code.js.map +1 -0
- package/dist/tools/codexLifecycle.d.ts +2 -0
- package/dist/tools/codexLifecycle.js +206 -0
- package/dist/tools/codexLifecycle.js.map +1 -0
- package/dist/tools/config.d.ts +2 -0
- package/dist/tools/config.js +183 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/context.d.ts +31 -0
- package/dist/tools/context.js +2 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/goal.d.ts +2 -0
- package/dist/tools/goal.js +159 -0
- package/dist/tools/goal.js.map +1 -0
- package/dist/tools/handoff.d.ts +2 -0
- package/dist/tools/handoff.js +57 -0
- package/dist/tools/handoff.js.map +1 -0
- package/dist/tools/oracle.d.ts +2 -0
- package/dist/tools/oracle.js +248 -0
- package/dist/tools/oracle.js.map +1 -0
- package/dist/tools/research.d.ts +2 -0
- package/dist/tools/research.js +51 -0
- package/dist/tools/research.js.map +1 -0
- package/dist/tools/review.d.ts +2 -0
- package/dist/tools/review.js +233 -0
- package/dist/tools/review.js.map +1 -0
- package/dist/tools/route.d.ts +2 -0
- package/dist/tools/route.js +69 -0
- package/dist/tools/route.js.map +1 -0
- package/dist/tools/session.d.ts +2 -0
- package/dist/tools/session.js +37 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/status.d.ts +2 -0
- package/dist/tools/status.js +34 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/tools/workflow.d.ts +2 -0
- package/dist/tools/workflow.js +27 -0
- package/dist/tools/workflow.js.map +1 -0
- package/dist/util/applyFileBlocks.d.ts +18 -0
- package/dist/util/applyFileBlocks.js +163 -0
- package/dist/util/applyFileBlocks.js.map +1 -0
- package/dist/util/asyncControl.d.ts +14 -0
- package/dist/util/asyncControl.js +106 -0
- package/dist/util/asyncControl.js.map +1 -0
- package/dist/util/auditLog.d.ts +56 -0
- package/dist/util/auditLog.js +232 -0
- package/dist/util/auditLog.js.map +1 -0
- package/dist/util/codexLifecycle.d.ts +55 -0
- package/dist/util/codexLifecycle.js +102 -0
- package/dist/util/codexLifecycle.js.map +1 -0
- package/dist/util/codexLifecycleJob.d.ts +209 -0
- package/dist/util/codexLifecycleJob.js +360 -0
- package/dist/util/codexLifecycleJob.js.map +1 -0
- package/dist/util/composerDisabled.d.ts +6 -0
- package/dist/util/composerDisabled.js +27 -0
- package/dist/util/composerDisabled.js.map +1 -0
- package/dist/util/dispatchHint.d.ts +5 -3
- package/dist/util/dispatchHint.js +62 -2
- package/dist/util/dispatchHint.js.map +1 -1
- package/dist/util/goal.d.ts +132 -0
- package/dist/util/goal.js +616 -0
- package/dist/util/goal.js.map +1 -0
- package/dist/util/goalReport.d.ts +51 -0
- package/dist/util/goalReport.js +164 -0
- package/dist/util/goalReport.js.map +1 -0
- package/dist/util/jobPolling.d.ts +9 -0
- package/dist/util/jobPolling.js +17 -0
- package/dist/util/jobPolling.js.map +1 -0
- package/dist/util/oracleJob.d.ts +66 -0
- package/dist/util/oracleJob.js +295 -0
- package/dist/util/oracleJob.js.map +1 -0
- package/dist/util/oracleLock.d.ts +38 -0
- package/dist/util/oracleLock.js +182 -0
- package/dist/util/oracleLock.js.map +1 -0
- package/dist/util/reviewDiff.d.ts +8 -0
- package/dist/util/reviewDiff.js +29 -0
- package/dist/util/reviewDiff.js.map +1 -0
- package/dist/util/reviewJob.d.ts +57 -0
- package/dist/util/reviewJob.js +207 -0
- package/dist/util/reviewJob.js.map +1 -0
- package/dist/util/workflowPlan.d.ts +24 -0
- package/dist/util/workflowPlan.js +49 -0
- package/dist/util/workflowPlan.js.map +1 -0
- package/package.json +8 -1
- package/plugin/composer-mastermind/commands/evolve.md +4 -0
- package/plugin/composer-mastermind/hooks/boundary_guard.sh +43 -2
- package/plugin/composer-mastermind/hooks/codex_warm_review.sh +161 -9
- package/plugin/composer-mastermind/hooks/learn.sh +172 -32
- package/plugin/composer-mastermind/hooks/precommit_codex_review.sh +430 -62
- package/plugin/composer-mastermind/skills/composer-mastermind/SKILL.md +184 -4
- package/scripts/composer-oracle-router-safe.sh +47 -0
- package/scripts/composer-statusline-segment.mjs +40 -0
- package/scripts/oracle-codex-handoff-safe.sh +49 -0
- package/scripts/oracle-plan-mcp.sh +66 -0
- package/scripts/oracle-pro-safe.sh +471 -0
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# oracle-pro-safe.sh
|
|
5
|
+
# Safe ChatGPT Pro browser adapter for steipete/oracle.
|
|
6
|
+
# Key design: never assume optional/hidden Oracle flags. Probe them with a dry-run first,
|
|
7
|
+
# then include only flags accepted by the installed local binary.
|
|
8
|
+
|
|
9
|
+
usage() {
|
|
10
|
+
cat <<'USAGE'
|
|
11
|
+
Usage:
|
|
12
|
+
scripts/oracle-pro-safe.sh [options] -- "prompt"
|
|
13
|
+
scripts/oracle-pro-safe.sh [options] -p "prompt"
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--mode <auto|quick|standard|deep|plan|review|debug|research>
|
|
17
|
+
--file <path-or-glob> Repeatable; passed to oracle --file
|
|
18
|
+
--slug <slug> Output slug prefix
|
|
19
|
+
--dry-run Run Oracle dry-run/preview only
|
|
20
|
+
--no-context Do not auto-attach lightweight repo context
|
|
21
|
+
--no-thinking-flag Do not pass --browser-thinking-time even if supported
|
|
22
|
+
--no-manual-login Do not pass --browser-manual-login even if supported
|
|
23
|
+
--model <model> Override selected Oracle model
|
|
24
|
+
--thinking <level> Override browser thinking level: light|standard|extended|heavy
|
|
25
|
+
--research deep|off Override browser research mode
|
|
26
|
+
-p, --prompt <prompt> Prompt text
|
|
27
|
+
-h, --help
|
|
28
|
+
|
|
29
|
+
Environment overrides:
|
|
30
|
+
ORACLE_PRO_QUICK_MODEL default: gpt-5.2-instant
|
|
31
|
+
ORACLE_PRO_STANDARD_MODEL default: gpt-5.5
|
|
32
|
+
ORACLE_PRO_DEEP_MODEL default: gpt-5.5-pro
|
|
33
|
+
ORACLE_PRO_QUICK_THINKING default: light
|
|
34
|
+
ORACLE_PRO_STANDARD_THINKING default: standard
|
|
35
|
+
ORACLE_PRO_DEEP_THINKING default: extended
|
|
36
|
+
ORACLE_PRO_BROWSER_STRATEGY default: select
|
|
37
|
+
ORACLE_PRO_OUTPUT_DIR default: .composer/oracle/answers
|
|
38
|
+
ORACLE_PRO_CONTEXT_DIR default: .composer/oracle/context
|
|
39
|
+
ORACLE_PRO_TIMEOUT default: 20m
|
|
40
|
+
ORACLE_PRO_INPUT_TIMEOUT default: 60s
|
|
41
|
+
ORACLE_PRO_REATTACH_DELAY default: 30s
|
|
42
|
+
ORACLE_PRO_REATTACH_INTERVAL default: 2m
|
|
43
|
+
ORACLE_PRO_REATTACH_TIMEOUT default: 2m
|
|
44
|
+
ORACLE_PRO_ATTACHMENTS default: never (inline files; set to auto/bundle for large files)
|
|
45
|
+
|
|
46
|
+
Secret file protection:
|
|
47
|
+
--file paths matching known secret patterns (.env, *.pem, *.key, id_rsa, .aws/credentials,
|
|
48
|
+
*secret*, *token*, *credential*, *password*, etc.) are rejected before upload.
|
|
49
|
+
Set ORACLE_PRO_ALLOW_SECRET_FILES=1 to override (use with caution).
|
|
50
|
+
USAGE
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
log() { printf '[oracle-pro] %s\n' "$*" >&2; }
|
|
54
|
+
warn() { printf '[oracle-pro][warn] %s\n' "$*" >&2; }
|
|
55
|
+
die() { printf '[oracle-pro][error] %s\n' "$*" >&2; exit 1; }
|
|
56
|
+
|
|
57
|
+
node_major() {
|
|
58
|
+
{ "$1" --version 2>/dev/null || true; } | sed -E 's/^v?([0-9]+).*/\1/'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
is_bad_node_major() {
|
|
62
|
+
# Keep this in sync with ORACLE_BAD_NODE_MAJORS in src/cli/doctor.ts.
|
|
63
|
+
case "$1" in
|
|
64
|
+
26) return 0 ;;
|
|
65
|
+
*) return 1 ;;
|
|
66
|
+
esac
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
select_good_node() {
|
|
70
|
+
local node_bin major c version dir
|
|
71
|
+
if node_bin="$(command -v node 2>/dev/null)"; then
|
|
72
|
+
major="$(node_major "$node_bin")"
|
|
73
|
+
if [[ -n "$major" ]] && ! is_bad_node_major "$major"; then
|
|
74
|
+
return 0
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
shopt -u failglob nullglob
|
|
79
|
+
|
|
80
|
+
local candidates=(
|
|
81
|
+
"${ORACLE_NODE_BIN:-}"
|
|
82
|
+
/opt/homebrew/opt/node@24/bin/node
|
|
83
|
+
/usr/local/opt/node@24/bin/node
|
|
84
|
+
"$HOME"/.nvm/versions/node/v24*/bin/node
|
|
85
|
+
"$HOME"/.nvm/versions/node/v25*/bin/node
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
for c in "${candidates[@]}"; do
|
|
89
|
+
[[ -n "$c" && -x "$c" ]] || continue
|
|
90
|
+
major="$(node_major "$c")"
|
|
91
|
+
[[ -n "$major" ]] || continue
|
|
92
|
+
is_bad_node_major "$major" && continue
|
|
93
|
+
dir="$(dirname "$c")"
|
|
94
|
+
PATH="$dir:$PATH"
|
|
95
|
+
export PATH
|
|
96
|
+
version="$("$c" --version 2>/dev/null || true)"
|
|
97
|
+
log "pinned node $version from $dir (avoids undici setTypeOfService EINVAL)"
|
|
98
|
+
return 0
|
|
99
|
+
done
|
|
100
|
+
|
|
101
|
+
warn "no known-good node found; oracle may crash under bad node majors (undici setTypeOfService EINVAL)"
|
|
102
|
+
return 0
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
MODE="auto"
|
|
106
|
+
PROMPT=""
|
|
107
|
+
SLUG=""
|
|
108
|
+
DRY_RUN=0
|
|
109
|
+
AUTO_CONTEXT=1
|
|
110
|
+
USE_THINKING_FLAG=1
|
|
111
|
+
USE_MANUAL_LOGIN=1
|
|
112
|
+
MODEL_OVERRIDE=""
|
|
113
|
+
THINKING_OVERRIDE=""
|
|
114
|
+
RESEARCH_OVERRIDE=""
|
|
115
|
+
FILES=()
|
|
116
|
+
|
|
117
|
+
while [[ $# -gt 0 ]]; do
|
|
118
|
+
case "$1" in
|
|
119
|
+
--mode) MODE="${2:-}"; shift 2 ;;
|
|
120
|
+
--mode=*) MODE="${1#*=}"; shift ;;
|
|
121
|
+
--file|-f) FILES+=("${2:-}"); shift 2 ;;
|
|
122
|
+
--file=*) FILES+=("${1#*=}"); shift ;;
|
|
123
|
+
--slug) SLUG="${2:-}"; shift 2 ;;
|
|
124
|
+
--slug=*) SLUG="${1#*=}"; shift ;;
|
|
125
|
+
--dry-run|--preview) DRY_RUN=1; shift ;;
|
|
126
|
+
--no-context) AUTO_CONTEXT=0; shift ;;
|
|
127
|
+
--no-thinking-flag) USE_THINKING_FLAG=0; shift ;;
|
|
128
|
+
--no-manual-login) USE_MANUAL_LOGIN=0; shift ;;
|
|
129
|
+
--model|-m) MODEL_OVERRIDE="${2:-}"; shift 2 ;;
|
|
130
|
+
--model=*) MODEL_OVERRIDE="${1#*=}"; shift ;;
|
|
131
|
+
--thinking) THINKING_OVERRIDE="${2:-}"; shift 2 ;;
|
|
132
|
+
--thinking=*) THINKING_OVERRIDE="${1#*=}"; shift ;;
|
|
133
|
+
--research) RESEARCH_OVERRIDE="${2:-}"; shift 2 ;;
|
|
134
|
+
--research=*) RESEARCH_OVERRIDE="${1#*=}"; shift ;;
|
|
135
|
+
-p|--prompt) PROMPT="${2:-}"; shift 2 ;;
|
|
136
|
+
--prompt=*) PROMPT="${1#*=}"; shift ;;
|
|
137
|
+
-h|--help) usage; exit 0 ;;
|
|
138
|
+
--) shift; PROMPT="${*:-}"; break ;;
|
|
139
|
+
*)
|
|
140
|
+
if [[ -z "$PROMPT" ]]; then PROMPT="$1"; else PROMPT="$PROMPT $1"; fi
|
|
141
|
+
shift
|
|
142
|
+
;;
|
|
143
|
+
esac
|
|
144
|
+
done
|
|
145
|
+
|
|
146
|
+
[[ -n "$PROMPT" ]] || die "prompt is required"
|
|
147
|
+
command -v oracle >/dev/null 2>&1 || die "oracle not found in PATH"
|
|
148
|
+
select_good_node
|
|
149
|
+
|
|
150
|
+
case "$MODE" in
|
|
151
|
+
auto|quick|standard|deep|plan|review|debug|research) ;;
|
|
152
|
+
*) die "unknown mode: $MODE" ;;
|
|
153
|
+
esac
|
|
154
|
+
|
|
155
|
+
classify_mode() {
|
|
156
|
+
local text_lc
|
|
157
|
+
text_lc="$(printf '%s' "$PROMPT" | tr '[:upper:]' '[:lower:]')"
|
|
158
|
+
case "$text_lc" in
|
|
159
|
+
*'[oracle:quick]'*) echo quick; return ;;
|
|
160
|
+
*'[oracle:standard]'*) echo standard; return ;;
|
|
161
|
+
*'[oracle:deep]'*|*'[oracle:plan]'*) echo deep; return ;;
|
|
162
|
+
*'[oracle:review]'*) echo review; return ;;
|
|
163
|
+
*'[oracle:debug]'*) echo debug; return ;;
|
|
164
|
+
*'[oracle:research]'*) echo research; return ;;
|
|
165
|
+
esac
|
|
166
|
+
if [[ ${#PROMPT} -gt 2500 ]]; then echo deep; return; fi
|
|
167
|
+
if [[ "$text_lc" =~ (architecture|architectural|design|plan|planning|proposal|migration|refactor|roadmap|tradeoff|trade-off|spec|handoff|implementation[[:space:]]+plan) ]]; then echo deep; return; fi
|
|
168
|
+
if [[ "$text_lc" =~ (review|audit|regression|security|compatibility|api[[:space:]]+break|edge[[:space:]]+case|risk) ]]; then echo review; return; fi
|
|
169
|
+
if [[ "$text_lc" =~ (debug|root[ -]?cause|failing|failure|flaky|bug|stack[[:space:]]+trace|exception|crash|deadlock|race) ]]; then echo debug; return; fi
|
|
170
|
+
if [[ "$text_lc" =~ (research|compare[[:space:]]+options|survey|citations|latest|web) ]]; then echo research; return; fi
|
|
171
|
+
if [[ "$text_lc" =~ (quick|simple|small|syntax|command|explain) ]]; then echo quick; return; fi
|
|
172
|
+
echo standard
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if [[ "$MODE" == "auto" ]]; then
|
|
176
|
+
MODE="$(classify_mode)"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Dispatch table. Model is the primary selector. Thinking flag is additive when the local binary supports it.
|
|
180
|
+
QUICK_MODEL="${ORACLE_PRO_QUICK_MODEL:-gpt-5.2-instant}"
|
|
181
|
+
STANDARD_MODEL="${ORACLE_PRO_STANDARD_MODEL:-gpt-5.5}"
|
|
182
|
+
DEEP_MODEL="${ORACLE_PRO_DEEP_MODEL:-gpt-5.5-pro}"
|
|
183
|
+
QUICK_THINKING="${ORACLE_PRO_QUICK_THINKING:-light}"
|
|
184
|
+
STANDARD_THINKING="${ORACLE_PRO_STANDARD_THINKING:-standard}"
|
|
185
|
+
DEEP_THINKING="${ORACLE_PRO_DEEP_THINKING:-extended}"
|
|
186
|
+
RESEARCH_MODE="off"
|
|
187
|
+
DEFAULT_STRATEGY="select"
|
|
188
|
+
ATTACHMENTS_MODE="${ORACLE_PRO_ATTACHMENTS:-never}"
|
|
189
|
+
|
|
190
|
+
case "$MODE" in
|
|
191
|
+
quick) MODEL="$QUICK_MODEL"; THINKING="$QUICK_THINKING"; DEFAULT_STRATEGY="current" ;;
|
|
192
|
+
standard) MODEL="$STANDARD_MODEL"; THINKING="$STANDARD_THINKING"; DEFAULT_STRATEGY="current" ;;
|
|
193
|
+
deep|plan|review|debug) MODEL="$DEEP_MODEL"; THINKING="$DEEP_THINKING"; DEFAULT_STRATEGY="select" ;;
|
|
194
|
+
research) MODEL="$DEEP_MODEL"; THINKING="$DEEP_THINKING"; RESEARCH_MODE="deep"; DEFAULT_STRATEGY="select" ;;
|
|
195
|
+
esac
|
|
196
|
+
|
|
197
|
+
[[ -z "$MODEL_OVERRIDE" ]] || MODEL="$MODEL_OVERRIDE"
|
|
198
|
+
[[ -z "$THINKING_OVERRIDE" ]] || THINKING="$THINKING_OVERRIDE"
|
|
199
|
+
[[ -z "$RESEARCH_OVERRIDE" ]] || RESEARCH_MODE="$RESEARCH_OVERRIDE"
|
|
200
|
+
|
|
201
|
+
OUT_DIR="${ORACLE_PRO_OUTPUT_DIR:-.composer/oracle/answers}"
|
|
202
|
+
CTX_DIR="${ORACLE_PRO_CONTEXT_DIR:-.composer/oracle/context}"
|
|
203
|
+
mkdir -p "$OUT_DIR" "$CTX_DIR"
|
|
204
|
+
|
|
205
|
+
is_secret_file() {
|
|
206
|
+
# Returns 0 (true) if $1 looks like a secret/credential file we must not upload.
|
|
207
|
+
local p base lc
|
|
208
|
+
p="$1"
|
|
209
|
+
base="${p##*/}"
|
|
210
|
+
lc="$(printf '%s' "$base" | tr '[:upper:]' '[:lower:]')"
|
|
211
|
+
case "$lc" in
|
|
212
|
+
.env|.env.*|*.pem|*.key|*.p12|*.pfx|*.keystore|*.jks|*.kdbx|*.ppk|id_rsa|id_dsa|id_ecdsa|id_ed25519|.npmrc|.netrc|.pgpass)
|
|
213
|
+
return 0 ;;
|
|
214
|
+
*secret*|*token*|*credential*|*password*)
|
|
215
|
+
return 0 ;;
|
|
216
|
+
esac
|
|
217
|
+
case "$p" in
|
|
218
|
+
*/.ssh/*|*/.aws/credentials|*/.gnupg/*)
|
|
219
|
+
return 0 ;;
|
|
220
|
+
esac
|
|
221
|
+
return 1
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
safe_slug() {
|
|
225
|
+
local s="$1"
|
|
226
|
+
s="$(printf '%s' "$s" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9._-]+/-/g; s/^-+|-+$//g; s/-+/-/g')"
|
|
227
|
+
[[ -n "$s" ]] || s="oracle"
|
|
228
|
+
printf '%s' "${s:0:80}"
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if [[ -z "$SLUG" ]]; then
|
|
232
|
+
SLUG="$(date +%Y%m%d-%H%M%S)-${MODE}"
|
|
233
|
+
else
|
|
234
|
+
SLUG="$(date +%Y%m%d-%H%M%S)-$(safe_slug "$SLUG")"
|
|
235
|
+
fi
|
|
236
|
+
OUT_FILE="$OUT_DIR/$SLUG.md"
|
|
237
|
+
|
|
238
|
+
# Probe support for optional flags. A dry-run should parse options without touching Chrome.
|
|
239
|
+
# Cache per-version-ish in the context dir to avoid repeated probes.
|
|
240
|
+
ORACLE_VERSION="$(oracle --version 2>/dev/null | head -1 | tr -d '\r' || true)"
|
|
241
|
+
CACHE_DIR="$CTX_DIR/.flag-cache"
|
|
242
|
+
mkdir -p "$CACHE_DIR"
|
|
243
|
+
cache_key="$(printf '%s' "$(command -v oracle)::${ORACLE_VERSION}" | shasum 2>/dev/null | awk '{print $1}')"
|
|
244
|
+
[[ -n "$cache_key" ]] || cache_key="default"
|
|
245
|
+
|
|
246
|
+
supports_option() {
|
|
247
|
+
local flag="$1"
|
|
248
|
+
local value="${2-}"
|
|
249
|
+
local key="$CACHE_DIR/${cache_key}.$(printf '%s' "$flag" | tr -c 'a-zA-Z0-9_' '_')"
|
|
250
|
+
if [[ -f "$key" ]]; then
|
|
251
|
+
[[ "$(cat "$key")" == "yes" ]]
|
|
252
|
+
return
|
|
253
|
+
fi
|
|
254
|
+
local args=(--engine browser --dry-run summary -p "oracle flag probe")
|
|
255
|
+
if [[ -n "$value" ]]; then args+=("$flag" "$value"); else args+=("$flag"); fi
|
|
256
|
+
if oracle "${args[@]}" >/dev/null 2>"$key.err"; then
|
|
257
|
+
printf 'yes' > "$key"
|
|
258
|
+
return 0
|
|
259
|
+
fi
|
|
260
|
+
if grep -qiE 'unknown option|unknown argument|invalid option' "$key.err" 2>/dev/null; then
|
|
261
|
+
printf 'no' > "$key"
|
|
262
|
+
return 1
|
|
263
|
+
fi
|
|
264
|
+
# Conservative: if dry-run failed for some non-parse reason, do not use the optional flag.
|
|
265
|
+
printf 'no' > "$key"
|
|
266
|
+
return 1
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
add_supported_flag() {
|
|
270
|
+
# $1 is the target array name (historically a nameref); all callers use ARGS,
|
|
271
|
+
# so append to ARGS directly to stay compatible with Bash 3.2 (no `local -n`).
|
|
272
|
+
local flag="$2"
|
|
273
|
+
local value="${3-}"
|
|
274
|
+
if supports_option "$flag" "$value"; then
|
|
275
|
+
if [[ -n "$value" ]]; then ARGS+=("$flag" "$value"); else ARGS+=("$flag"); fi
|
|
276
|
+
else
|
|
277
|
+
warn "installed oracle does not accept $flag; skipping"
|
|
278
|
+
fi
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
# Auto context: small, local, non-secret evidence that helps ChatGPT plan/review without dumping the repo.
|
|
282
|
+
AUTO_FILES=()
|
|
283
|
+
write_context_file() {
|
|
284
|
+
local name="$1"
|
|
285
|
+
local content_cmd="$2"
|
|
286
|
+
local path="$CTX_DIR/$SLUG.$name"
|
|
287
|
+
bash -lc "$content_cmd" > "$path" 2>/dev/null || true
|
|
288
|
+
if [[ -s "$path" ]]; then AUTO_FILES+=("$path"); fi
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if [[ "$AUTO_CONTEXT" -eq 1 ]]; then
|
|
292
|
+
# Authority class B — current policy/context (safe as source-of-truth).
|
|
293
|
+
policy_files=(CLAUDE.md composer.config.json docs/STATUS.md)
|
|
294
|
+
# Authority class C — background/history (risky as source-of-truth; only for planning/research).
|
|
295
|
+
background_files=(README.md AGENTS.md)
|
|
296
|
+
# Project manifest files (dependency intent + language).
|
|
297
|
+
base_files=(package.json pyproject.toml Cargo.toml go.mod)
|
|
298
|
+
|
|
299
|
+
# Task-aware attach set: minimal for trivial modes, no stale background for review/debug.
|
|
300
|
+
attach_candidates=()
|
|
301
|
+
case "$MODE" in
|
|
302
|
+
quick|standard)
|
|
303
|
+
attach_candidates=(CLAUDE.md docs/STATUS.md)
|
|
304
|
+
;;
|
|
305
|
+
review|debug)
|
|
306
|
+
attach_candidates=("${policy_files[@]}" "${base_files[@]}")
|
|
307
|
+
;;
|
|
308
|
+
deep|plan|research|*)
|
|
309
|
+
attach_candidates=("${policy_files[@]}" "${background_files[@]}" "${base_files[@]}")
|
|
310
|
+
;;
|
|
311
|
+
esac
|
|
312
|
+
|
|
313
|
+
for candidate in "${attach_candidates[@]}"; do
|
|
314
|
+
[[ -f "$candidate" ]] && AUTO_FILES+=("$candidate")
|
|
315
|
+
done
|
|
316
|
+
|
|
317
|
+
if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
318
|
+
write_context_file "git-status.txt" "git status --short"
|
|
319
|
+
write_context_file "git-diff-stat.txt" "git diff --stat"
|
|
320
|
+
if [[ "$MODE" != "quick" && "$MODE" != "standard" ]]; then
|
|
321
|
+
write_context_file "git-diff.patch" "git diff -- . ':(exclude).env' ':(exclude).env.*' ':(exclude)*.pem' ':(exclude)*.key' ':(exclude)*.p12' | sed -E -e '/(api[_-]?key|secret|token|passwd|password|credential|authorization|client[_-]?secret|access[_-]?token|refresh[_-]?token|private[_-]?key)[^a-z0-9]{0,4}[:=]/Id' -e '/bearer[[:space:]]+[a-z0-9._-]{6,}/Id' | head -c 200000"
|
|
322
|
+
fi
|
|
323
|
+
# Exact installed top-level deps (package.json shows ranges, not the installed tree).
|
|
324
|
+
if [[ "$MODE" != "quick" && "$MODE" != "standard" ]] && [[ -f package.json ]]; then
|
|
325
|
+
write_context_file "deps.txt" "npm ls --depth=0 2>/dev/null || true"
|
|
326
|
+
fi
|
|
327
|
+
fi
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
ARGS=(--engine browser -m "$MODEL" --write-output "$OUT_FILE")
|
|
331
|
+
|
|
332
|
+
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
333
|
+
ARGS+=(--dry-run summary)
|
|
334
|
+
fi
|
|
335
|
+
|
|
336
|
+
# Official v0.13.0 accepts these, but many are hidden from --help; still probe to survive forks/older installs.
|
|
337
|
+
add_supported_flag ARGS --browser-model-strategy "${ORACLE_PRO_BROWSER_STRATEGY:-$DEFAULT_STRATEGY}"
|
|
338
|
+
|
|
339
|
+
if [[ "$USE_THINKING_FLAG" -eq 1 ]]; then
|
|
340
|
+
add_supported_flag ARGS --browser-thinking-time "$THINKING"
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
if [[ "$RESEARCH_MODE" == "deep" ]]; then
|
|
344
|
+
add_supported_flag ARGS --browser-research deep
|
|
345
|
+
fi
|
|
346
|
+
|
|
347
|
+
if [[ "$USE_MANUAL_LOGIN" -eq 1 ]]; then
|
|
348
|
+
add_supported_flag ARGS --browser-manual-login
|
|
349
|
+
fi
|
|
350
|
+
|
|
351
|
+
add_supported_flag ARGS --browser-timeout "${ORACLE_PRO_TIMEOUT:-20m}"
|
|
352
|
+
add_supported_flag ARGS --browser-input-timeout "${ORACLE_PRO_INPUT_TIMEOUT:-60s}"
|
|
353
|
+
add_supported_flag ARGS --browser-attachments "$ATTACHMENTS_MODE"
|
|
354
|
+
add_supported_flag ARGS --browser-auto-reattach-delay "${ORACLE_PRO_REATTACH_DELAY:-30s}"
|
|
355
|
+
add_supported_flag ARGS --browser-auto-reattach-interval "${ORACLE_PRO_REATTACH_INTERVAL:-2m}"
|
|
356
|
+
add_supported_flag ARGS --browser-auto-reattach-timeout "${ORACLE_PRO_REATTACH_TIMEOUT:-2m}"
|
|
357
|
+
add_supported_flag ARGS --heartbeat "${ORACLE_PRO_HEARTBEAT:-30}"
|
|
358
|
+
|
|
359
|
+
# Validate + collect the exact attachment set (user files first, then auto context).
|
|
360
|
+
ATTACH=()
|
|
361
|
+
for f in "${FILES[@]}"; do
|
|
362
|
+
[[ -n "$f" ]] || continue
|
|
363
|
+
if is_secret_file "$f" && [[ "${ORACLE_PRO_ALLOW_SECRET_FILES:-0}" != "1" ]]; then
|
|
364
|
+
die "refusing to upload potential secret file: $f (matches secret denylist). Rename/relocate it, or set ORACLE_PRO_ALLOW_SECRET_FILES=1 to override."
|
|
365
|
+
fi
|
|
366
|
+
ATTACH+=("$f")
|
|
367
|
+
done
|
|
368
|
+
for f in "${AUTO_FILES[@]}"; do
|
|
369
|
+
[[ -n "$f" ]] && ATTACH+=("$f")
|
|
370
|
+
done
|
|
371
|
+
|
|
372
|
+
# Snapshot manifest: authoritative identity of the repo state this call may discuss.
|
|
373
|
+
# Lets the model (and us) detect drift between turns and bounds it to current disk state.
|
|
374
|
+
captured_at="$(date +%Y-%m-%dT%H:%M:%S%z)"
|
|
375
|
+
repo_root="$(pwd)"
|
|
376
|
+
git_branch="n/a"; git_head="n/a"; dirty="false"; status_hash="n/a"; diff_hash="n/a"
|
|
377
|
+
if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
378
|
+
git_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo n/a)"
|
|
379
|
+
git_head="$(git rev-parse HEAD 2>/dev/null || echo n/a)"
|
|
380
|
+
[[ -n "$(git status --porcelain 2>/dev/null)" ]] && dirty="true"
|
|
381
|
+
status_hash="$(git status --short 2>/dev/null | shasum -a 256 2>/dev/null | awk '{print $1}')"
|
|
382
|
+
diff_hash="$(git diff 2>/dev/null | shasum -a 256 2>/dev/null | awk '{print $1}')"
|
|
383
|
+
fi
|
|
384
|
+
[[ -n "$status_hash" ]] || status_hash="n/a"
|
|
385
|
+
[[ -n "$diff_hash" ]] || diff_hash="n/a"
|
|
386
|
+
node_ver="$(node --version 2>/dev/null || echo n/a)"
|
|
387
|
+
npm_ver="$(npm --version 2>/dev/null || echo n/a)"
|
|
388
|
+
os_ver="$(uname -sr 2>/dev/null || echo n/a)"
|
|
389
|
+
arch_ver="$(uname -m 2>/dev/null || echo n/a)"
|
|
390
|
+
short_head="${git_head:0:12}"
|
|
391
|
+
repo_state_hash="$(printf '%s' "${git_head}${status_hash}${diff_hash}${node_ver}${npm_ver}" | shasum -a 256 2>/dev/null | awk '{print $1}')"
|
|
392
|
+
[[ -n "$repo_state_hash" ]] || repo_state_hash="n/a"
|
|
393
|
+
snapshot_id="${SLUG}-${short_head}-${repo_state_hash:0:8}"
|
|
394
|
+
|
|
395
|
+
MANIFEST_PATH="$CTX_DIR/$SLUG.manifest.json"
|
|
396
|
+
# JSON-escape a string value (backslash and double-quote only; inputs are paths/hashes/versions).
|
|
397
|
+
json_escape() { printf '%s' "${1-}" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'; }
|
|
398
|
+
{
|
|
399
|
+
printf '{\n'
|
|
400
|
+
printf ' "snapshotId": "%s",\n' "$(json_escape "$snapshot_id")"
|
|
401
|
+
printf ' "capturedAt": "%s",\n' "$(json_escape "$captured_at")"
|
|
402
|
+
printf ' "repoRoot": "%s",\n' "$(json_escape "$repo_root")"
|
|
403
|
+
printf ' "branch": "%s",\n' "$(json_escape "$git_branch")"
|
|
404
|
+
printf ' "head": "%s",\n' "$(json_escape "$git_head")"
|
|
405
|
+
printf ' "dirty": %s,\n' "$dirty"
|
|
406
|
+
printf ' "gitStatusHash": "%s",\n' "$(json_escape "$status_hash")"
|
|
407
|
+
printf ' "diffHash": "%s",\n' "$(json_escape "$diff_hash")"
|
|
408
|
+
printf ' "repoStateHash": "%s",\n' "$(json_escape "$repo_state_hash")"
|
|
409
|
+
printf ' "mode": "%s",\n' "$(json_escape "$MODE")"
|
|
410
|
+
printf ' "runtime": { "node": "%s", "npm": "%s", "os": "%s", "arch": "%s" },\n' \
|
|
411
|
+
"$(json_escape "$node_ver")" "$(json_escape "$npm_ver")" "$(json_escape "$os_ver")" "$(json_escape "$arch_ver")"
|
|
412
|
+
printf ' "attachments": [\n'
|
|
413
|
+
manifest_n=${#ATTACH[@]}
|
|
414
|
+
manifest_i=0
|
|
415
|
+
for f in "${ATTACH[@]}"; do
|
|
416
|
+
manifest_i=$((manifest_i + 1))
|
|
417
|
+
sha="$(shasum -a 256 "$f" 2>/dev/null | awk '{print $1}')"
|
|
418
|
+
bytes="$(wc -c < "$f" 2>/dev/null | tr -d ' ')"
|
|
419
|
+
sep=","
|
|
420
|
+
[[ "$manifest_i" -eq "$manifest_n" ]] && sep=""
|
|
421
|
+
printf ' { "path": "%s", "sha256": "%s", "bytes": %s }%s\n' \
|
|
422
|
+
"$(json_escape "$f")" "${sha:-}" "${bytes:-0}" "$sep"
|
|
423
|
+
done
|
|
424
|
+
printf ' ]\n'
|
|
425
|
+
printf '}\n'
|
|
426
|
+
} > "$MANIFEST_PATH" 2>/dev/null || warn "manifest generation failed (continuing without manifest)"
|
|
427
|
+
[[ -f "$MANIFEST_PATH" ]] && ATTACH+=("$MANIFEST_PATH")
|
|
428
|
+
|
|
429
|
+
# CONTEXT CONTRACT: bound the model to this snapshot; defeat stale memory/attachments.
|
|
430
|
+
CONTRACT="$(cat <<EOF
|
|
431
|
+
CONTEXT CONTRACT
|
|
432
|
+
Authoritative snapshot: ${snapshot_id}
|
|
433
|
+
Captured at: ${captured_at}
|
|
434
|
+
Branch / HEAD: ${git_branch} / ${git_head}
|
|
435
|
+
Dirty tree: ${dirty}
|
|
436
|
+
Repo-state hash: ${repo_state_hash}
|
|
437
|
+
Runtime: node=${node_ver} npm=${npm_ver} os=${os_ver} arch=${arch_ver}
|
|
438
|
+
Task: ${MODE}
|
|
439
|
+
Authority order:
|
|
440
|
+
A. Live source-of-truth: ${SLUG}.manifest.json, ${SLUG}.git-status.txt, ${SLUG}.git-diff.patch, ${SLUG}.deps.txt, targeted source/tests
|
|
441
|
+
B. Current policy/context: CLAUDE.md, composer.config.json, docs/STATUS.md, relevant ADRs
|
|
442
|
+
C. Background/history: README.md, AGENTS.md
|
|
443
|
+
Rules:
|
|
444
|
+
- Treat class A as authoritative for the local repo. If A conflicts with B or C, A wins.
|
|
445
|
+
- Ignore prior chat memory, project memory, and earlier attachments if they conflict with this snapshot.
|
|
446
|
+
- For each substantive claim, tag it [attached], [runtime], [web], or [inference].
|
|
447
|
+
- Cite attached claims with file path and line span.
|
|
448
|
+
- For current API/library claims not proven by attached files, verify on the web against primary docs.
|
|
449
|
+
- If evidence is insufficient, say: "unknown from provided context".
|
|
450
|
+
EOF
|
|
451
|
+
)"
|
|
452
|
+
PROMPT="${CONTRACT}
|
|
453
|
+
|
|
454
|
+
${PROMPT}"
|
|
455
|
+
|
|
456
|
+
# Emit all attachments (incl. the manifest) as --file inputs.
|
|
457
|
+
for f in "${ATTACH[@]}"; do
|
|
458
|
+
[[ -n "$f" ]] && ARGS+=(--file "$f")
|
|
459
|
+
done
|
|
460
|
+
|
|
461
|
+
log "oracle version: ${ORACLE_VERSION:-unknown}"
|
|
462
|
+
log "mode=$MODE model=$MODEL thinking=$THINKING research=$RESEARCH_MODE output=$OUT_FILE"
|
|
463
|
+
log "files: user=${#FILES[@]} auto=${#AUTO_FILES[@]} total=${#ATTACH[@]} snapshot=${snapshot_id}"
|
|
464
|
+
|
|
465
|
+
oracle "${ARGS[@]}" -p "$PROMPT"
|
|
466
|
+
|
|
467
|
+
# Maintain a stable latest file for downstream Codex/Composer commands.
|
|
468
|
+
if [[ -f "$OUT_FILE" ]]; then
|
|
469
|
+
cp "$OUT_FILE" "$OUT_DIR/latest.md"
|
|
470
|
+
printf '%s\n' "$OUT_FILE"
|
|
471
|
+
fi
|