@searls/turbocommit 0.10.2 → 0.12.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 +6 -0
- package/lib/git.js +11 -1
- package/lib/redact.js +156 -0
- package/lib/run.js +15 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -136,6 +136,12 @@ Here's every property with its default:
|
|
|
136
136
|
// "Name <email>" → custom trailer value
|
|
137
137
|
"coauthor": true,
|
|
138
138
|
|
|
139
|
+
// Auto-push after each turbocommit.
|
|
140
|
+
// Only pushes when fast-forward is possible (never forces).
|
|
141
|
+
// true → push after each commit
|
|
142
|
+
// false → don't push (default)
|
|
143
|
+
"push": false,
|
|
144
|
+
|
|
139
145
|
"title": {
|
|
140
146
|
// "agent" (default) → run a title agent to generate the headline
|
|
141
147
|
// "transcript" → extract the first prompt as the headline
|
package/lib/git.js
CHANGED
|
@@ -56,6 +56,15 @@ function currentBranch (cwd) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function pushClean (cwd) {
|
|
60
|
+
try {
|
|
61
|
+
git('push', { cwd })
|
|
62
|
+
return true
|
|
63
|
+
} catch {
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
function esc (s) {
|
|
60
69
|
return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`')
|
|
61
70
|
}
|
|
@@ -66,5 +75,6 @@ module.exports = {
|
|
|
66
75
|
hasChanges,
|
|
67
76
|
addAndCommit,
|
|
68
77
|
hasCommits,
|
|
69
|
-
currentBranch
|
|
78
|
+
currentBranch,
|
|
79
|
+
pushClean
|
|
70
80
|
}
|
package/lib/redact.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
const SAFE_NAMES = new Set([
|
|
2
|
+
// Shell / OS
|
|
3
|
+
'HOME', 'USER', 'SHELL', 'PATH', 'PWD', 'OLDPWD', 'LANG', 'TERM',
|
|
4
|
+
'EDITOR', 'VISUAL', 'PAGER', 'HOSTNAME', 'LOGNAME', 'DISPLAY', 'TZ',
|
|
5
|
+
'TMPDIR', 'SHLVL', '_', 'BROWSER', 'COMMAND_MODE', 'COLUMNS', 'LINES',
|
|
6
|
+
'MANPATH', 'INFOPATH',
|
|
7
|
+
|
|
8
|
+
// Terminal
|
|
9
|
+
'COLORTERM', 'TERM_PROGRAM', 'TERM_PROGRAM_VERSION', 'TERM_SESSION_ID',
|
|
10
|
+
'ITERM_SESSION_ID', 'ITERM_PROFILE', 'APPLE_TERMINAL_ID',
|
|
11
|
+
|
|
12
|
+
// Locale
|
|
13
|
+
'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY',
|
|
14
|
+
'LC_NUMERIC', 'LC_TIME',
|
|
15
|
+
|
|
16
|
+
// XDG
|
|
17
|
+
'XDG_DATA_HOME', 'XDG_CONFIG_HOME', 'XDG_STATE_HOME', 'XDG_CACHE_HOME',
|
|
18
|
+
'XDG_RUNTIME_DIR', 'XDG_DATA_DIRS', 'XDG_CONFIG_DIRS', 'XDG_SESSION_TYPE',
|
|
19
|
+
'XDG_CURRENT_DESKTOP', 'XDG_SESSION_CLASS', 'XDG_SESSION_ID', 'XDG_SEAT',
|
|
20
|
+
'XDG_VTNR', 'XDG_MENU_PREFIX',
|
|
21
|
+
|
|
22
|
+
// Dev environment
|
|
23
|
+
'NODE_ENV', 'RAILS_ENV', 'RACK_ENV', 'GO_ENV', 'MIX_ENV', 'FLASK_ENV',
|
|
24
|
+
'DEBUG', 'VERBOSE', 'LOG_LEVEL', 'PORT', 'HOST', 'CI',
|
|
25
|
+
'NVM_DIR', 'NVM_BIN', 'NVM_INC', 'NVM_CD_FLAGS',
|
|
26
|
+
'VOLTA_HOME', 'HOMEBREW_PREFIX', 'HOMEBREW_CELLAR', 'HOMEBREW_REPOSITORY',
|
|
27
|
+
'HOMEBREW_SHELLENV_PREFIX',
|
|
28
|
+
|
|
29
|
+
// SSH / GPG (socket paths, not keys)
|
|
30
|
+
'SSH_AUTH_SOCK', 'SSH_AGENT_PID', 'GPG_AGENT_INFO', 'GPG_TTY',
|
|
31
|
+
|
|
32
|
+
// Git
|
|
33
|
+
'GIT_EDITOR', 'GIT_PAGER', 'GIT_TERMINAL_PROMPT'
|
|
34
|
+
])
|
|
35
|
+
|
|
36
|
+
// Values that are common English words, config toggles, or well-known
|
|
37
|
+
// non-secret defaults. Checked case-insensitively.
|
|
38
|
+
const SAFE_VALUES = new Set([
|
|
39
|
+
// Boolean-like
|
|
40
|
+
'true', 'false', 'yes', 'no', 'on', 'off',
|
|
41
|
+
|
|
42
|
+
// Environment modes
|
|
43
|
+
'development', 'production', 'staging', 'test', 'testing',
|
|
44
|
+
'dev', 'prod', 'local', 'ci', 'qa', 'sandbox', 'preview',
|
|
45
|
+
'canary', 'demo', 'benchmark', 'profile', 'uat', 'integration',
|
|
46
|
+
'preprod', 'pre-production', 'stage', 'deployment',
|
|
47
|
+
|
|
48
|
+
// Log levels
|
|
49
|
+
'trace', 'debug', 'info', 'warn', 'warning', 'error', 'fatal',
|
|
50
|
+
'critical', 'notice', 'verbose', 'silent', 'quiet', 'off',
|
|
51
|
+
|
|
52
|
+
// Common toggles/modes
|
|
53
|
+
'enabled', 'disabled', 'always', 'never', 'auto', 'default',
|
|
54
|
+
'manual', 'none', 'null', 'undefined', 'inherit', 'unset',
|
|
55
|
+
'strict', 'lax', 'permissive', 'enforcing', 'noninteractive',
|
|
56
|
+
|
|
57
|
+
// Editors, pagers, browsers (bare names)
|
|
58
|
+
'vim', 'vi', 'nano', 'emacs', 'nvim', 'code', 'subl', 'mate',
|
|
59
|
+
'micro', 'ed', 'pico', 'gedit', 'kate', 'hx', 'kak', 'kakoune',
|
|
60
|
+
'joe', 'emacsclient', 'less', 'more', 'most', 'cat',
|
|
61
|
+
'open', 'firefox', 'chromium', 'links', 'lynx', 'w3m', 'safari', 'opera',
|
|
62
|
+
|
|
63
|
+
// Common English words that appear as env values
|
|
64
|
+
'allow', 'deny', 'block', 'drop', 'accept', 'reject',
|
|
65
|
+
'public', 'private', 'master', 'main', 'release',
|
|
66
|
+
'read', 'write', 'root', 'latest', 'stable', 'edge',
|
|
67
|
+
'screen', 'random', 'node', 'ruby', 'python', 'go', 'java',
|
|
68
|
+
'rust', 'php', 'slim', 'alpine',
|
|
69
|
+
'localhost',
|
|
70
|
+
|
|
71
|
+
// Platform/architecture identifiers
|
|
72
|
+
'linux', 'darwin', 'win32', 'windows', 'freebsd', 'openbsd',
|
|
73
|
+
'netbsd', 'sunos', 'aix', 'android', 'cygwin', 'msys',
|
|
74
|
+
'x86_64', 'amd64', 'x64', 'arm64', 'aarch64', 'i386', 'i686',
|
|
75
|
+
'ia32', 'armv7l', 'arm', 'ppc64le', 's390x', 'riscv64',
|
|
76
|
+
|
|
77
|
+
// TERM values
|
|
78
|
+
'xterm', 'xterm-256color', 'xterm-color', 'xterm-16color',
|
|
79
|
+
'screen', 'screen-256color', 'tmux', 'tmux-256color',
|
|
80
|
+
'linux', 'vt100', 'vt220', 'dumb', 'ansi', 'rxvt',
|
|
81
|
+
'rxvt-unicode', 'rxvt-unicode-256color', 'alacritty', 'kitty',
|
|
82
|
+
'wezterm', 'eterm-color', 'foot', 'foot-extra',
|
|
83
|
+
'gnome-256color', 'konsole-256color', 'st-256color',
|
|
84
|
+
'putty', 'putty-256color',
|
|
85
|
+
|
|
86
|
+
// COLORTERM
|
|
87
|
+
'truecolor', '24bit',
|
|
88
|
+
|
|
89
|
+
// TERM_PROGRAM
|
|
90
|
+
'apple_terminal', 'iterm.app', 'vscode', 'hyper',
|
|
91
|
+
|
|
92
|
+
// Docker/CI identifiers
|
|
93
|
+
'bullseye', 'bookworm', 'jammy', 'lts',
|
|
94
|
+
'travis-ci', 'circleci', 'codeship', 'github-actions',
|
|
95
|
+
'gitlab-ci', 'jenkins', 'buildkite',
|
|
96
|
+
|
|
97
|
+
// System users
|
|
98
|
+
'nobody', 'www-data', 'daemon', 'nonroot', 'appuser',
|
|
99
|
+
'nginx', 'postgres', 'mysql', 'redis',
|
|
100
|
+
|
|
101
|
+
// XDG session types
|
|
102
|
+
'x11', 'wayland', 'tty',
|
|
103
|
+
|
|
104
|
+
// Metasyntactic / genuinely-never-a-credential values
|
|
105
|
+
'todo', 'fixme', 'tbd', 'n/a',
|
|
106
|
+
'xxx', 'yyy', 'zzz', 'foo', 'bar', 'baz',
|
|
107
|
+
'lorem', 'temp', 'tmp', 'blah'
|
|
108
|
+
])
|
|
109
|
+
|
|
110
|
+
// Pattern-based rules for values that match known non-secret formats
|
|
111
|
+
const SAFE_VALUE_PATTERNS = [
|
|
112
|
+
/^\d+$/, // pure numeric
|
|
113
|
+
/^(.)\1+$/, // repeated single char (xxxx, ****, ....)
|
|
114
|
+
/^\d+\.\d+\.\d+(-[\w.]+)?$/, // semver (18.17.1, 1.0.0-beta.1)
|
|
115
|
+
/^[a-z]{2}_[A-Z]{2}([.\w-]*)?$/, // locale (en_US.UTF-8)
|
|
116
|
+
/^(C|POSIX)(\.UTF-8)?$/, // C/POSIX locale
|
|
117
|
+
/^[A-Z][a-z]+\/[A-Za-z_]+$/, // timezone (America/New_York)
|
|
118
|
+
/^(UTC|Etc\/UTC|US\/\w+)$/, // timezone shorthand
|
|
119
|
+
/^\/[a-z][\w/.-]*$/, // unix path (/usr/local/bin)
|
|
120
|
+
/^(https?|redis|mongodb|postgres(ql)?|mysql|amqp|smtp|memcached|elasticsearch):\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0|::1)/, // localhost URLs
|
|
121
|
+
/^(xterm|screen|tmux|linux|vt\d+|dumb|ansi|rxvt|alacritty|kitty|wezterm|foot)(-[\w]+)*$/, // TERM identifiers
|
|
122
|
+
/^(localhost)?:\d+(\.\d+)?$/, // X11 DISPLAY (:0, :0.0)
|
|
123
|
+
/^wayland-\d+$/, // Wayland display
|
|
124
|
+
/^sqlite:(\/\/\/)?(:\w+:|[\w./]+)$/ // sqlite URLs
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
function isSafeValue (value) {
|
|
128
|
+
if (!value || value.length <= 3) return true
|
|
129
|
+
if (SAFE_VALUES.has(value.toLowerCase())) return true
|
|
130
|
+
for (const pattern of SAFE_VALUE_PATTERNS) {
|
|
131
|
+
if (pattern.test(value)) return true
|
|
132
|
+
}
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function buildRedactions (env) {
|
|
137
|
+
env = env || process.env
|
|
138
|
+
const entries = []
|
|
139
|
+
for (const [name, value] of Object.entries(env)) {
|
|
140
|
+
if (SAFE_NAMES.has(name)) continue
|
|
141
|
+
if (isSafeValue(value)) continue
|
|
142
|
+
entries.push({ name, value })
|
|
143
|
+
}
|
|
144
|
+
entries.sort((a, b) => b.value.length - a.value.length)
|
|
145
|
+
return entries
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function redact (text, redactions) {
|
|
149
|
+
if (!text) return text
|
|
150
|
+
for (const { name, value } of redactions) {
|
|
151
|
+
text = text.replaceAll(value, '[REDACTED:' + name + ']')
|
|
152
|
+
}
|
|
153
|
+
return text
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = { buildRedactions, redact }
|
package/lib/run.js
CHANGED
|
@@ -4,10 +4,11 @@ const path = require('path')
|
|
|
4
4
|
const { loadJson, mergeConfig } = require('./io')
|
|
5
5
|
const { parseTranscript, formatBody, formatTitleTranscript, extractHeadline, extractModel } = require('./transcript')
|
|
6
6
|
const { runTitleAgent, runBodyAgent } = require('./agent')
|
|
7
|
-
const { gitRoot, hasChanges, addAndCommit, hasCommits, currentBranch } = require('./git')
|
|
7
|
+
const { gitRoot, hasChanges, addAndCommit, hasCommits, currentBranch, pushClean } = require('./git')
|
|
8
8
|
const { logEvent } = require('./log')
|
|
9
9
|
const { wrapText } = require('./wrap')
|
|
10
10
|
const { hasTrackedModifications, cleanupTracking } = require('./track')
|
|
11
|
+
const { redact, buildRedactions } = require('./redact')
|
|
11
12
|
const { getAncestors, savePending, collectPending, cleanupConsumed, cleanupStale, readWatermark, saveWatermark, resolveParentCommit } = require('./session')
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -187,9 +188,20 @@ function run (input) {
|
|
|
187
188
|
const coauthor = resolveCoauthor(config, hookInput.transcript_path, root)
|
|
188
189
|
const tag = coauthor ? '\n\n' + coauthor : ''
|
|
189
190
|
|
|
190
|
-
const
|
|
191
|
+
const redactions = buildRedactions()
|
|
192
|
+
const safeHeadline = redact(headline, redactions)
|
|
193
|
+
const safeBody = redact(wrappedBody + tag, redactions)
|
|
194
|
+
const sha = addAndCommit(root, safeHeadline, safeBody)
|
|
191
195
|
|
|
192
|
-
logEvent('success', { project, branch, context, title:
|
|
196
|
+
logEvent('success', { project, branch, context, title: safeHeadline })
|
|
197
|
+
|
|
198
|
+
if (config.push === true) {
|
|
199
|
+
if (pushClean(root)) {
|
|
200
|
+
logEvent('push', { project, branch })
|
|
201
|
+
} else {
|
|
202
|
+
logEvent('push-fail', { project, branch })
|
|
203
|
+
}
|
|
204
|
+
}
|
|
193
205
|
|
|
194
206
|
// Post-commit: save watermark and cleanup
|
|
195
207
|
if (sessionId) {
|