-
Notifications
You must be signed in to change notification settings - Fork 8
t1304: Soft TTSR — wire rules into OpenCode plugin hooks #2139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
266cd4a
8908f63
6812212
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1328,6 +1328,353 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Tool definitions extracted to tools.mjs — imported at top of file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // --------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Phase 5: Soft TTSR — Rule Enforcement via Plugin Hooks (t1304) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // --------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // "Soft TTSR" (Text-to-Speech Rules) provides preventative enforcement of | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // coding standards without stream-level interception (which OpenCode doesn't | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // expose). Three hooks work together: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1. system.transform — inject active rules into system prompt (preventative) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2. messages.transform — scan prior assistant outputs for violations, inject | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // correction context into message history (corrective) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 3. text.complete — detect violations post-hoc and flag them (observational) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Rules are data-driven: each rule is an object with id, description, a regex | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // pattern to detect violations, and a correction message. Rules can be loaded | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // from a config file or use the built-in defaults. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Path to optional user-defined TTSR rules file. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * JSON array of rule objects: { id, description, pattern, correction, severity } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @type {string} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const TTSR_RULES_PATH = join(AGENTS_DIR, "configs", "ttsr-rules.json"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Built-in TTSR rules — enforced by default. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Each rule has: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - id: unique identifier | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - description: human-readable explanation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - pattern: regex string to detect violations in assistant output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - correction: message injected when violation is detected | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - severity: "error" | "warn" | "info" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - systemPrompt: instruction injected into system prompt (preventative) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @type {Array<{id: string, description: string, pattern: string, correction: string, severity: string, systemPrompt: string}>} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const BUILTIN_TTSR_RULES = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "no-glob-for-discovery", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: "Use git ls-files or fd instead of Glob/find for file discovery", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern: "(?:mcp_glob|Glob tool|use.*\\bGlob\\b.*to find|I'll use Glob)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| correction: "Use `git ls-files` or `fd` for file discovery, not Glob. Glob is a last resort when Bash is unavailable.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| severity: "warn", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| systemPrompt: "File discovery: use `git ls-files '<pattern>'` for git-tracked files, `fd` for untracked. NEVER use Glob/find as primary discovery.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "no-cat-for-reading", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: "Use Read tool instead of cat/head/tail for file reading", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern: "(?:^|\\s)cat\\s+['\"]?[/~\\w]|\\bhead\\s+-n|\\btail\\s+-n", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| correction: "Use the Read tool for file reading, not cat/head/tail. These are Bash commands that waste context.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| severity: "info", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| systemPrompt: "Use the Read tool for file reading. Avoid cat/head/tail in Bash — they waste context tokens.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "read-before-edit", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: "Always Read a file before Edit or Write operations", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern: "(?:I'll edit|Let me edit|I'll write to|Let me write)(?:(?!I'll read|let me read|I've read|already read).){0,200}$", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| correction: "ALWAYS Read a file before Edit/Write. These tools fail without a prior Read in this conversation.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| severity: "error", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| systemPrompt: "ALWAYS Read a file before Edit or Write. These tools FAIL without a prior Read in this conversation.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "no-credentials-in-output", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: "Never expose credentials, API keys, or secrets in output", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern: "(?:api[_-]?key|secret|password|token)\\s*[:=]\\s*['\"][A-Za-z0-9+/=_-]{16,}['\"]", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| correction: "SECURITY: Never expose credentials in output. Use `aidevops secret set NAME` for secure storage.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| severity: "error", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| systemPrompt: "NEVER expose credentials, API keys, or secrets in output or logs.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "pre-edit-check", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: "Run pre-edit-check.sh before modifying files", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern: "(?:I'll (?:create|modify|edit|write)|Let me (?:create|modify|edit|write)).*(?:on main|on master)\\b", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| correction: "Run pre-edit-check.sh before modifying files. NEVER edit on main/master branch.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| severity: "error", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| systemPrompt: "Before ANY file modification: run pre-edit-check.sh. NEVER edit on main/master.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "shell-explicit-returns", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: "Shell functions must have explicit return statements", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern: "(?:function\\s+\\w+|\\w+\\s*\\(\\)\\s*\\{)(?:(?!return\\s+[0-9]).){50,}\\}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| correction: "Shell functions must have explicit `return 0` or `return 1` statements (SonarCloud S7682).", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| severity: "warn", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| systemPrompt: "Shell scripts: every function must have an explicit `return 0` or `return 1`.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "shell-local-params", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: "Use local var=\"$1\" pattern in shell functions", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern: "\\$[1-9](?!.*local\\s+\\w+=.*\\$[1-9])", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| correction: "Use `local var=\"$1\"` pattern — never use positional parameters directly (SonarCloud S7679).", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| severity: "warn", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| systemPrompt: "Shell scripts: use `local var=\"$1\"` — never use $1 directly in function bodies.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Cached loaded rules (built-in + user-defined). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @type {Array<object> | null} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let _ttsrRules = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Load TTSR rules: built-in defaults merged with optional user-defined rules. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * User rules can override built-in rules by matching id. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {Array<{id: string, description: string, pattern: string, correction: string, severity: string, systemPrompt: string}>} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function loadTtsrRules() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (_ttsrRules !== null) return _ttsrRules; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _ttsrRules = [...BUILTIN_TTSR_RULES]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userContent = readIfExists(TTSR_RULES_PATH); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (userContent) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userRules = JSON.parse(userContent); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (Array.isArray(userRules)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const rule of userRules) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!rule.id || !rule.pattern) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const existingIdx = _ttsrRules.findIndex((r) => r.id === rule.id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (existingIdx >= 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _ttsrRules[existingIdx] = { ..._ttsrRules[existingIdx], ...rule }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _ttsrRules.push(rule); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("[aidevops] Failed to parse TTSR rules file — using built-in rules only"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1456
to
+1458
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return _ttsrRules; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Check text against a single TTSR rule. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} text - Text to check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {object} rule - TTSR rule object | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {{ matched: boolean, matches: string[] }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function checkRule(text, rule) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const regex = new RegExp(rule.pattern, "gim"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 1472 in .agents/plugins/opencode-aidevops/index.mjs
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const matches = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let match; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while ((match = regex.exec(text)) !== null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| matches.push(match[0].substring(0, 120)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (matches.length >= 3) break; // Cap matches to avoid noise | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { matched: matches.length > 0, matches }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { matched: false, matches: [] }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1480
to
+1482
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The } catch (e) {
console.error(`[aidevops] Invalid regex in TTSR rule '${rule.id}':`, e);
return { matched: false, matches: [] };
} |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1470
to
+1483
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, locate and examine the file
fd -p "index.mjs" | head -5Repository: marcusquinn/aidevops Length of output: 107 🏁 Script executed: # Read the checkRule function and surrounding context
sed -n '1450,1500p' .agents/plugins/opencode-aidevops/index.mjsRepository: marcusquinn/aidevops Length of output: 1382 🏁 Script executed: # Search for where checkRule is called to understand the text parameter
rg "checkRule\(" .agents/plugins/opencode-aidevops/index.mjs -B 2 -A 2Repository: marcusquinn/aidevops Length of output: 373 🏁 Script executed: # Search for rule validation logic
rg "ttsr-rules" .agents/plugins/opencode-aidevops/index.mjs -B 2 -A 5Repository: marcusquinn/aidevops Length of output: 255 🏁 Script executed: # Check for Codacy configuration files
fd -p "codacy|eslint|\.codacy" -type fRepository: marcusquinn/aidevops Length of output: 234 🏁 Script executed: # Search for scanForViolations calls to understand text source
rg "scanForViolations\(" .agents/plugins/opencode-aidevops/index.mjs -B 3 -A 3Repository: marcusquinn/aidevops Length of output: 727 🏁 Script executed: # Look at the full loadTtsrRules function to understand validation
sed -n '1400,1470p' .agents/plugins/opencode-aidevops/index.mjs | head -100Repository: marcusquinn/aidevops Length of output: 2746 🏁 Script executed: # Check for Codacy or ESLint configuration
fd -p "\.codacyrc|\.eslintrc|codacy.yaml|\.eslint" -type fRepository: marcusquinn/aidevops Length of output: 234 🏁 Script executed: # Look at the built-in rules to understand pattern types
rg "_ttsrRules\s*=" .agents/plugins/opencode-aidevops/index.mjs -A 30 | head -60Repository: marcusquinn/aidevops Length of output: 1346 🏁 Script executed: # Search for any lint configuration files more broadly
find . -name ".*rc" -o -name ".codacy*" -o -name "*.config.js" 2>/dev/null | grep -E "(eslint|codacy|lint)" | head -20Repository: marcusquinn/aidevops Length of output: 87 🏁 Script executed: # Check if there's a package.json with eslintConfig
fd "package.json" -x grep -l "eslintConfig\|codacy" {} \;Repository: marcusquinn/aidevops Length of output: 46 🏁 Script executed: # Examine the shell-explicit-returns pattern more carefully - it's in the built-in rules
sed -n '1300,1350p' .agents/plugins/opencode-aidevops/index.mjsRepository: marcusquinn/aidevops Length of output: 2198 🏁 Script executed: # Check the context around where scanForViolations is called - what's the typical text size?
rg "extractTextFromParts\(" .agents/plugins/opencode-aidevops/index.mjs -B 5 -A 10 | head -40Repository: marcusquinn/aidevops Length of output: 974 🏁 Script executed: # Look at what extractTextFromParts returns
rg "function extractTextFromParts" .agents/plugins/opencode-aidevops/index.mjs -A 15Repository: marcusquinn/aidevops Length of output: 628 🏁 Script executed: # Check the actual Codacy configuration
cat .codacy.yml 2>/dev/null || echo "File not readable"Repository: marcusquinn/aidevops Length of output: 1231 🏁 Script executed: # Look at the shell-explicit-returns pattern specifically - it looks complex
rg "shell-explicit-returns" .agents/plugins/opencode-aidevops/index.mjs -A 2 -B 1Repository: marcusquinn/aidevops Length of output: 265 🏁 Script executed: # Search for any test or validation of the checkRule function
rg "checkRule" . --type js -B 2 -A 2Repository: marcusquinn/aidevops Length of output: 46 🏁 Script executed: # Check if there's documentation about regex patterns or safety
rg "ReDoS|catastrophic|backtrack|timeout" . 2>/dev/null | head -20Repository: marcusquinn/aidevops Length of output: 2286 🏁 Script executed: # Look for any limits or caps already in place for text processing
rg "slice\(0," .agents/plugins/opencode-aidevops/index.mjs | head -10Repository: marcusquinn/aidevops Length of output: 195 🏁 Script executed: # Check if there's an ESLint configuration with specific rules about dynamic RegExp
find . -name ".eslintrc*" -o -name "eslint.config.*" 2>/dev/nullRepository: marcusquinn/aidevops Length of output: 46 🏁 Script executed: # Check package.json for any ESLint or security-related dependencies
fd "package.json" -x cat {} \; | grep -A 20 -B 5 "eslint\|security" | head -50Repository: marcusquinn/aidevops Length of output: 46 🏁 Script executed: # Verify the exact line numbers of the checkRule function
rg -n "function checkRule" .agents/plugins/opencode-aidevops/index.mjsRepository: marcusquinn/aidevops Length of output: 101 🏁 Script executed: # Check if there are any actual tests or CI logs that mention Codacy failures
fd "\.yml$|\.yaml$" -path "*/.github/*" -x cat {} \; 2>/dev/null | head -100Repository: marcusquinn/aidevops Length of output: 46 ReDoS vulnerability: dynamic
LLM outputs passed to Capping input length before the exec loop eliminates this risk without external dependencies: 🛡️ Proposed fix — cap input before the exec loop function checkRule(text, rule) {
try {
+ // Cap input to guard against ReDoS from user-supplied or slow built-in patterns.
+ const safeText = text.length > 50_000 ? text.slice(0, 50_000) : text;
const regex = new RegExp(rule.pattern, "gim");
const matches = [];
let match;
- while ((match = regex.exec(text)) !== null) {
+ while ((match = regex.exec(safeText)) !== null) {
matches.push(match[0].substring(0, 120));
if (matches.length >= 3) break;
}
return { matched: matches.length > 0, matches };
} catch {
return { matched: false, matches: [] };
}
}📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Check: Codacy Static Code Analysis[warning] 1472-1472: .agents/plugins/opencode-aidevops/index.mjs#L1472 [failure] 1472-1472: .agents/plugins/opencode-aidevops/index.mjs#L1472 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Scan text for all TTSR rule violations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} text - Text to scan | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {Array<{rule: object, matches: string[]}>} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function scanForViolations(text) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const rules = loadTtsrRules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const violations = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const rule of rules) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = checkRule(text, rule); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (result.matched) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| violations.push({ rule, matches: result.matches }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return violations; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Hook: experimental.chat.system.transform (t1304) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Injects active TTSR rules into the system prompt as preventative guidance. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * This runs before every LLM call, ensuring the model is aware of rules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * before generating output. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {object} _input - { sessionID?: string, model: Model } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {object} output - { system: string[] } (mutable) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function systemTransformHook(_input, output) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const rules = loadTtsrRules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (rules.length === 0) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ruleLines = rules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter((r) => r.systemPrompt) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map((r) => `- ${r.systemPrompt}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (ruleLines.length === 0) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output.system.push( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "## aidevops Quality Rules (enforced)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "The following rules are actively enforced. Violations will be flagged.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...ruleLines, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ].join("\n"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Extract text content from a Part array. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Only extracts text from TextPart objects (type === "text"). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {Array<object>} parts - Array of Part objects | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {string} Concatenated text content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function extractTextFromParts(parts) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!Array.isArray(parts)) return ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return parts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter((p) => p && p.type === "text" && typeof p.text === "string") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map((p) => p.text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .join("\n"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Hook: experimental.chat.messages.transform (t1304) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Scans previous assistant outputs for rule violations and injects | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * correction context into the message history. This provides corrective | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * feedback so the model learns from its own violations within the session. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Strategy: scan the last N assistant messages (not all — performance). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * If violations are found, append a synthetic correction message to the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * message history so the model sees the feedback before generating. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {object} _input - {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {object} output - { messages: { info: Message, parts: Part[] }[] } (mutable) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function messagesTransformHook(_input, output) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!output.messages || output.messages.length === 0) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Scan the last 3 assistant messages for violations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const scanWindow = 3; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const assistantMessages = output.messages | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter((m) => m.info && m.info.role === "assistant") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .slice(-scanWindow); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (assistantMessages.length === 0) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const allViolations = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const msg of assistantMessages) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const text = extractTextFromParts(msg.parts); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!text) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const violations = scanForViolations(text); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const v of violations) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Deduplicate by rule id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!allViolations.some((av) => av.rule.id === v.rule.id)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allViolations.push(v); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (allViolations.length === 0) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Build correction context | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const corrections = allViolations.map((v) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const severity = v.rule.severity === "error" ? "ERROR" : "WARNING"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return `[${severity}] ${v.rule.id}: ${v.rule.correction}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const correctionText = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "[aidevops TTSR] Rule violations detected in recent output:", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...corrections, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Apply these corrections in your next response.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ].join("\n"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Inject as a synthetic user message at the end of the history | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // so the model sees the correction before generating its next response | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const correctionId = `ttsr-correction-${Date.now()}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const sessionID = output.messages[0]?.info?.sessionID || ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output.messages.push({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| info: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: correctionId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessionID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role: "user", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| time: { created: Date.now() }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parentID: "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1604
to
+1614
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Synthetic correction message borrows
✏️ Proposed fix- const sessionID = output.messages[0]?.info?.sessionID || "";
+ const sessionID = output.messages[output.messages.length - 1]?.info?.sessionID || "";🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parts: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: `${correctionId}-part`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessionID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messageID: correctionId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "text", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: correctionText, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| synthetic: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| qualityLog( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "INFO", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `TTSR messages.transform: injected ${allViolations.length} correction(s): ${allViolations.map((v) => v.rule.id).join(", ")}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Hook: experimental.text.complete (t1304) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Detects rule violations post-hoc in completed text parts and flags them. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * This is observational — it logs violations but does not modify the output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * (the text has already been shown to the user). The log data feeds into | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * quality metrics and pattern tracking. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {object} input - { sessionID: string, messageID: string, partID: string } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {object} output - { text: string } (mutable) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function textCompleteHook(input, output) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!output.text) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const violations = scanForViolations(output.text); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (violations.length === 0) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Log violations for observability | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const v of violations) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| qualityLog( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| v.rule.severity === "error" ? "ERROR" : "WARN", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `TTSR violation [${v.rule.id}]: ${v.rule.description} (session: ${input.sessionID}, message: ${input.messageID})`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Append violation markers as comments at the end of the text | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // so the model can see them in subsequent turns | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const markers = violations.map((v) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const severity = v.rule.severity === "error" ? "ERROR" : "WARN"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return `<!-- TTSR:${severity}:${v.rule.id} — ${v.rule.correction} -->`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output.text = output.text + "\n" + markers.join("\n"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1634
to
+1665
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JSDoc contradicts the implementation, and HTML markers are unconditionally appended to all text types. Line 1639 states "does not modify the output (the text has already been shown to the user)" — but lines 1660-1665 clearly mutate Additionally, HTML comment markers are appended regardless of content type. If the LLM's text part contains raw shell, JSON, or a bare code block with no surrounding prose, those ✏️ Proposed JSDoc + guard fix- * Detects rule violations post-hoc in completed text parts and flags them.
- * This is observational — it logs violations but does not modify the output
- * (the text has already been shown to the user). The log data feeds into
- * quality metrics and pattern tracking.
+ * Detects rule violations post-hoc in completed text parts, logs them, and
+ * appends HTML comment markers to the stored text for subsequent model turns.
+ * NOTE: output.text IS mutated; markers are invisible in markdown-rendered UIs
+ * but may appear as literal text in plain-text or code-only responses.- output.text = output.text + "\n" + markers.join("\n");
+ // Only append markers when the text contains markdown prose (not bare code/JSON).
+ const looksLikeMarkdown = /[#*_`[\]]/.test(output.text);
+ if (looksLikeMarkdown) {
+ output.text = output.text + "\n" + markers.join("\n");
+ }🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Record pattern for tracking | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const patternTracker = join(SCRIPTS_DIR, "pattern-tracker-helper.sh"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (existsSync(patternTracker)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ruleIds = violations.map((v) => v.rule.id).join(","); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `bash "${patternTracker}" record "TTSR_VIOLATION" "rules: ${ruleIds}" --tag "ttsr" 2>/dev/null`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of `bash "${patternTracker}" record "TTSR_VIOLATION" "rules: ${ruleIds}" --tag "ttsr"`,References
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 5000, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // --------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Main Plugin Export | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // --------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1341,7 +1688,10 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 3. Quality hooks — full pre-commit pipeline (ShellCheck, return statements, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * positional params, secrets scan, markdown lint) on Write/Edit operations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 4. Shell environment — aidevops paths and variables | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 5. Compaction context — preserves operational state across context resets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 5. Soft TTSR — preventative rule enforcement via system prompt injection, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * corrective feedback via message history scanning, and post-hoc violation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * detection via text completion hooks (t1304) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 6. Compaction context — preserves operational state across context resets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * MCP registration (Phase 2, t008.2): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - Registers all known MCP servers from a data-driven registry | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1371,7 +1721,12 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Phase 4: Shell environment | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "shell.env": shellEnvHook, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Compaction context | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Phase 5: Soft TTSR — rule enforcement (t1304) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "experimental.chat.system.transform": systemTransformHook, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "experimental.chat.messages.transform": messagesTransformHook, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "experimental.text.complete": textCompleteHook, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Compaction context (includes OMOC state when detected) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "experimental.session.compacting": async (input, output) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| compactingHook(input, output, directory), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regular expression for the
shell-local-paramsrule appears to have a logical flaw. The negative lookahead(?!.*local\s+\w+=.*\$[1-9])checks for the absence of alocalassignment after the use of a positional parameter ($1,$2, etc.). To correctly enforce thelocal var="$1"pattern, as per the repository style guide (line 11), the check should verify that alocalassignment has occurred before the parameter is used within its function scope. This regex is likely to produce false negatives (failing to flag incorrect usage) and may not be reliable. Consider refining the pattern to more accurately reflect the rule's intent.References
local var="$1"pattern in functions. The current regex does not correctly enforce this. (link)