Compare commits

..

5 Commits

13 changed files with 192 additions and 309 deletions

View File

@@ -884,25 +884,6 @@ describe("GitMasterConfigSchema", () => {
//#then
expect(result.success).toBe(false)
})
test("accepts shell-safe git_env_prefix", () => {
const config = { git_env_prefix: "MY_HOOK=active" }
const result = GitMasterConfigSchema.safeParse(config)
expect(result.success).toBe(true)
if (result.success) {
expect(result.data.git_env_prefix).toBe("MY_HOOK=active")
}
})
test("rejects git_env_prefix with shell metacharacters", () => {
const config = { git_env_prefix: "A=1; rm -rf /" }
const result = GitMasterConfigSchema.safeParse(config)
expect(result.success).toBe(false)
})
})
describe("skills schema", () => {

View File

@@ -10,7 +10,6 @@ export * from "./schema/commands"
export * from "./schema/dynamic-context-pruning"
export * from "./schema/experimental"
export * from "./schema/fallback-models"
export * from "./schema/git-env-prefix"
export * from "./schema/git-master"
export * from "./schema/hooks"
export * from "./schema/notification"

View File

@@ -1,28 +0,0 @@
import { z } from "zod"
const GIT_ENV_ASSIGNMENT_PATTERN =
/^(?:[A-Za-z_][A-Za-z0-9_]*=[A-Za-z0-9_-]*)(?: [A-Za-z_][A-Za-z0-9_]*=[A-Za-z0-9_-]*)*$/
export const GIT_ENV_PREFIX_VALIDATION_MESSAGE =
'git_env_prefix must be empty or use shell-safe env assignments like "GIT_MASTER=1"'
export function isValidGitEnvPrefix(value: string): boolean {
if (value === "") {
return true
}
return GIT_ENV_ASSIGNMENT_PATTERN.test(value)
}
export function assertValidGitEnvPrefix(value: string): string {
if (!isValidGitEnvPrefix(value)) {
throw new Error(GIT_ENV_PREFIX_VALIDATION_MESSAGE)
}
return value
}
export const GitEnvPrefixSchema = z
.string()
.refine(isValidGitEnvPrefix, { message: GIT_ENV_PREFIX_VALIDATION_MESSAGE })
.default("GIT_MASTER=1")

View File

@@ -1,14 +1,10 @@
import { z } from "zod"
import { GitEnvPrefixSchema } from "./git-env-prefix"
export const GitMasterConfigSchema = z.object({
/** Add "Ultraworked with Sisyphus" footer to commit messages (default: true). Can be boolean or custom string. */
commit_footer: z.union([z.boolean(), z.string()]).default(true),
/** Add "Co-authored-by: Sisyphus" trailer to commit messages (default: true) */
include_co_authored_by: z.boolean().default(true),
/** Environment variable prefix for all git commands (default: "GIT_MASTER=1"). Set to "" to disable. Allows custom git hooks to detect git-master skill usage. */
git_env_prefix: GitEnvPrefixSchema,
})
export type GitMasterConfig = z.infer<typeof GitMasterConfigSchema>

View File

@@ -0,0 +1,108 @@
/// <reference types="bun-types" />
import { describe, it, expect } from "bun:test"
import { mapClaudeModelToOpenCode } from "./claude-model-mapper"
describe("mapClaudeModelToOpenCode", () => {
describe("#given undefined or empty input", () => {
it("#when called with undefined #then returns undefined", () => {
expect(mapClaudeModelToOpenCode(undefined)).toBeUndefined()
})
it("#when called with empty string #then returns undefined", () => {
expect(mapClaudeModelToOpenCode("")).toBeUndefined()
})
it("#when called with whitespace-only string #then returns undefined", () => {
expect(mapClaudeModelToOpenCode(" ")).toBeUndefined()
})
})
describe("#given Claude Code alias", () => {
it("#when called with sonnet #then maps to anthropic claude-sonnet-4-6 object", () => {
expect(mapClaudeModelToOpenCode("sonnet")).toEqual({ providerID: "anthropic", modelID: "claude-sonnet-4-6" })
})
it("#when called with opus #then maps to anthropic claude-opus-4-6 object", () => {
expect(mapClaudeModelToOpenCode("opus")).toEqual({ providerID: "anthropic", modelID: "claude-opus-4-6" })
})
it("#when called with haiku #then maps to anthropic claude-haiku-4-5 object", () => {
expect(mapClaudeModelToOpenCode("haiku")).toEqual({ providerID: "anthropic", modelID: "claude-haiku-4-5" })
})
it("#when called with Sonnet (capitalized) #then maps case-insensitively to object", () => {
expect(mapClaudeModelToOpenCode("Sonnet")).toEqual({ providerID: "anthropic", modelID: "claude-sonnet-4-6" })
})
})
describe("#given inherit", () => {
it("#when called with inherit #then returns undefined", () => {
expect(mapClaudeModelToOpenCode("inherit")).toBeUndefined()
})
})
describe("#given bare Claude model name", () => {
it("#when called with claude-sonnet-4-5-20250514 #then adds anthropic object format", () => {
expect(mapClaudeModelToOpenCode("claude-sonnet-4-5-20250514")).toEqual({ providerID: "anthropic", modelID: "claude-sonnet-4-5-20250514" })
})
it("#when called with claude-opus-4-6 #then adds anthropic object format", () => {
expect(mapClaudeModelToOpenCode("claude-opus-4-6")).toEqual({ providerID: "anthropic", modelID: "claude-opus-4-6" })
})
it("#when called with claude-haiku-4-5-20251001 #then adds anthropic object format", () => {
expect(mapClaudeModelToOpenCode("claude-haiku-4-5-20251001")).toEqual({ providerID: "anthropic", modelID: "claude-haiku-4-5-20251001" })
})
it("#when called with claude-3-5-sonnet-20241022 #then adds anthropic object format", () => {
expect(mapClaudeModelToOpenCode("claude-3-5-sonnet-20241022")).toEqual({ providerID: "anthropic", modelID: "claude-3-5-sonnet-20241022" })
})
})
describe("#given model with dot version numbers", () => {
it("#when called with claude-3.5-sonnet #then normalizes dots and returns object format", () => {
expect(mapClaudeModelToOpenCode("claude-3.5-sonnet")).toEqual({ providerID: "anthropic", modelID: "claude-3-5-sonnet" })
})
it("#when called with claude-3.5-sonnet-20241022 #then normalizes dots and returns object format", () => {
expect(mapClaudeModelToOpenCode("claude-3.5-sonnet-20241022")).toEqual({ providerID: "anthropic", modelID: "claude-3-5-sonnet-20241022" })
})
})
describe("#given model already in provider/model format", () => {
it("#when called with anthropic/claude-sonnet-4-6 #then splits into object format", () => {
expect(mapClaudeModelToOpenCode("anthropic/claude-sonnet-4-6")).toEqual({ providerID: "anthropic", modelID: "claude-sonnet-4-6" })
})
it("#when called with openai/gpt-5.2 #then splits into object format", () => {
expect(mapClaudeModelToOpenCode("openai/gpt-5.2")).toEqual({ providerID: "openai", modelID: "gpt-5.2" })
})
})
describe("#given non-Claude bare model", () => {
it("#when called with gpt-5.2 #then returns undefined", () => {
expect(mapClaudeModelToOpenCode("gpt-5.2")).toBeUndefined()
})
it("#when called with gemini-3-flash #then returns undefined", () => {
expect(mapClaudeModelToOpenCode("gemini-3-flash")).toBeUndefined()
})
})
describe("#given prototype property name", () => {
it("#when called with constructor #then returns undefined", () => {
expect(mapClaudeModelToOpenCode("constructor")).toBeUndefined()
})
it("#when called with toString #then returns undefined", () => {
expect(mapClaudeModelToOpenCode("toString")).toBeUndefined()
})
})
describe("#given model with leading/trailing whitespace", () => {
it("#when called with padded string #then trims before returning object format", () => {
expect(mapClaudeModelToOpenCode(" claude-sonnet-4-6 ")).toEqual({ providerID: "anthropic", modelID: "claude-sonnet-4-6" })
})
})
})

View File

@@ -0,0 +1,39 @@
import { normalizeModelFormat } from "../../shared/model-format-normalizer"
import { normalizeModelID } from "../../shared/model-normalization"
const ANTHROPIC_PREFIX = "anthropic/"
const CLAUDE_CODE_ALIAS_MAP = new Map<string, string>([
["sonnet", `${ANTHROPIC_PREFIX}claude-sonnet-4-6`],
["opus", `${ANTHROPIC_PREFIX}claude-opus-4-6`],
["haiku", `${ANTHROPIC_PREFIX}claude-haiku-4-5`],
])
function mapClaudeModelString(model: string | undefined): string | undefined {
if (!model) return undefined
const trimmed = model.trim()
if (trimmed.length === 0) return undefined
if (trimmed === "inherit") return undefined
const aliasResult = CLAUDE_CODE_ALIAS_MAP.get(trimmed.toLowerCase())
if (aliasResult) return aliasResult
if (trimmed.includes("/")) return trimmed
const normalized = normalizeModelID(trimmed)
if (normalized.startsWith("claude-")) {
return `${ANTHROPIC_PREFIX}${normalized}`
}
return undefined
}
export function mapClaudeModelToOpenCode(
model: string | undefined
): { providerID: string; modelID: string } | undefined {
const mappedModel = mapClaudeModelString(model)
return mappedModel ? normalizeModelFormat(mappedModel) : undefined
}

View File

@@ -1,10 +1,10 @@
import { existsSync, readdirSync, readFileSync } from "fs"
import { join, basename } from "path"
import type { AgentConfig } from "@opencode-ai/sdk"
import { parseFrontmatter } from "../../shared/frontmatter"
import { isMarkdownFile } from "../../shared/file-utils"
import { getClaudeConfigDir } from "../../shared"
import type { AgentScope, AgentFrontmatter, LoadedAgent } from "./types"
import type { AgentScope, AgentFrontmatter, ClaudeCodeAgentConfig, LoadedAgent } from "./types"
import { mapClaudeModelToOpenCode } from "./claude-model-mapper"
function parseToolsConfig(toolsStr?: string): Record<string, boolean> | undefined {
if (!toolsStr) return undefined
@@ -42,10 +42,13 @@ function loadAgentsFromDir(agentsDir: string, scope: AgentScope): LoadedAgent[]
const formattedDescription = `(${scope}) ${originalDescription}`
const config: AgentConfig = {
const mappedModelOverride = mapClaudeModelToOpenCode(data.model)
const config: ClaudeCodeAgentConfig = {
description: formattedDescription,
mode: "subagent",
prompt: body.trim(),
...(mappedModelOverride ? { model: mappedModelOverride } : {}),
}
const toolsConfig = parseToolsConfig(data.tools)
@@ -67,22 +70,22 @@ function loadAgentsFromDir(agentsDir: string, scope: AgentScope): LoadedAgent[]
return agents
}
export function loadUserAgents(): Record<string, AgentConfig> {
export function loadUserAgents(): Record<string, ClaudeCodeAgentConfig> {
const userAgentsDir = join(getClaudeConfigDir(), "agents")
const agents = loadAgentsFromDir(userAgentsDir, "user")
const result: Record<string, AgentConfig> = {}
const result: Record<string, ClaudeCodeAgentConfig> = {}
for (const agent of agents) {
result[agent.name] = agent.config
}
return result
}
export function loadProjectAgents(directory?: string): Record<string, AgentConfig> {
export function loadProjectAgents(directory?: string): Record<string, ClaudeCodeAgentConfig> {
const projectAgentsDir = join(directory ?? process.cwd(), ".claude", "agents")
const agents = loadAgentsFromDir(projectAgentsDir, "project")
const result: Record<string, AgentConfig> = {}
const result: Record<string, ClaudeCodeAgentConfig> = {}
for (const agent of agents) {
result[agent.name] = agent.config
}

View File

@@ -2,6 +2,10 @@ import type { AgentConfig } from "@opencode-ai/sdk"
export type AgentScope = "user" | "project"
export type ClaudeCodeAgentConfig = Omit<AgentConfig, "model"> & {
model?: string | { providerID: string; modelID: string }
}
export interface AgentFrontmatter {
name?: string
description?: string
@@ -12,6 +16,6 @@ export interface AgentFrontmatter {
export interface LoadedAgent {
name: string
path: string
config: AgentConfig
config: ClaudeCodeAgentConfig
scope: AgentScope
}

View File

@@ -1,10 +1,10 @@
import { existsSync, readdirSync, readFileSync } from "fs"
import { basename, join } from "path"
import type { AgentConfig } from "@opencode-ai/sdk"
import { parseFrontmatter } from "../../shared/frontmatter"
import { isMarkdownFile } from "../../shared/file-utils"
import { log } from "../../shared/logger"
import type { AgentFrontmatter } from "../claude-code-agent-loader/types"
import type { AgentFrontmatter, ClaudeCodeAgentConfig } from "../claude-code-agent-loader/types"
import { mapClaudeModelToOpenCode } from "../claude-code-agent-loader/claude-model-mapper"
import type { LoadedPlugin } from "./types"
function parseToolsConfig(toolsStr?: string): Record<string, boolean> | undefined {
@@ -24,8 +24,8 @@ function parseToolsConfig(toolsStr?: string): Record<string, boolean> | undefine
return result
}
export function loadPluginAgents(plugins: LoadedPlugin[]): Record<string, AgentConfig> {
const agents: Record<string, AgentConfig> = {}
export function loadPluginAgents(plugins: LoadedPlugin[]): Record<string, ClaudeCodeAgentConfig> {
const agents: Record<string, ClaudeCodeAgentConfig> = {}
for (const plugin of plugins) {
if (!plugin.agentsDir || !existsSync(plugin.agentsDir)) continue
@@ -46,10 +46,13 @@ export function loadPluginAgents(plugins: LoadedPlugin[]): Record<string, AgentC
const originalDescription = data.description || ""
const formattedDescription = `(plugin: ${plugin.name}) ${originalDescription}`
const config: AgentConfig = {
const mappedModelOverride = mapClaudeModelToOpenCode(data.model)
const config: ClaudeCodeAgentConfig = {
description: formattedDescription,
mode: "subagent",
prompt: body.trim(),
...(mappedModelOverride ? { model: mappedModelOverride } : {}),
}
const toolsConfig = parseToolsConfig(data.tools)

View File

@@ -1,7 +1,7 @@
import { log } from "../../shared/logger"
import type { AgentConfig } from "@opencode-ai/sdk"
import type { CommandDefinition } from "../claude-code-command-loader/types"
import type { McpServerConfig } from "../claude-code-mcp-loader/types"
import type { ClaudeCodeAgentConfig } from "../claude-code-agent-loader/types"
import type { HooksConfig, LoadedPlugin, PluginLoadError, PluginLoaderOptions } from "./types"
import { discoverInstalledPlugins } from "./discovery"
import { loadPluginCommands } from "./command-loader"
@@ -20,7 +20,7 @@ export { loadPluginHooksConfigs } from "./hook-loader"
export interface PluginComponentsResult {
commands: Record<string, CommandDefinition>
skills: Record<string, CommandDefinition>
agents: Record<string, AgentConfig>
agents: Record<string, ClaudeCodeAgentConfig>
mcpServers: Record<string, McpServerConfig>
hooksConfigs: HooksConfig[]
plugins: LoadedPlugin[]

View File

@@ -1,155 +0,0 @@
/// <reference types="bun-types" />
import { describe, it, expect } from "bun:test"
import { injectGitMasterConfig } from "./git-master-template-injection"
const SAMPLE_TEMPLATE = [
"# Git Master Agent",
"",
"## MODE DETECTION (FIRST STEP)",
"",
"Analyze the request.",
"",
"```bash",
"git status",
"git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null",
"MERGE_BASE=$(git merge-base HEAD main)",
"GIT_SEQUENCE_EDITOR=: git rebase -i --autosquash $MERGE_BASE",
"```",
"",
"```",
"</execution>",
].join("\n")
describe("#given git_env_prefix config", () => {
describe("#when default config (GIT_MASTER=1)", () => {
it("#then injects env prefix section before MODE DETECTION", () => {
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: false,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
})
expect(result).toContain("## GIT COMMAND PREFIX (MANDATORY)")
expect(result).toContain("GIT_MASTER=1 git status")
expect(result).toContain("GIT_MASTER=1 git commit")
expect(result).toContain("GIT_MASTER=1 git push")
expect(result).toContain("EVERY git command MUST be prefixed with `GIT_MASTER=1`")
const prefixIndex = result.indexOf("## GIT COMMAND PREFIX")
const modeIndex = result.indexOf("## MODE DETECTION")
expect(prefixIndex).toBeLessThan(modeIndex)
})
})
describe("#when git_env_prefix is empty string", () => {
it("#then does NOT inject env prefix section", () => {
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: false,
include_co_authored_by: false,
git_env_prefix: "",
})
expect(result).not.toContain("## GIT COMMAND PREFIX")
expect(result).not.toContain("GIT_MASTER=1")
expect(result).not.toContain("git_env_prefix")
})
})
describe("#when git_env_prefix is custom value", () => {
it("#then injects custom prefix in section", () => {
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: false,
include_co_authored_by: false,
git_env_prefix: "MY_HOOK=active",
})
expect(result).toContain("MY_HOOK=active git status")
expect(result).toContain("MY_HOOK=active git commit")
expect(result).not.toContain("GIT_MASTER=1")
})
})
describe("#when git_env_prefix contains shell metacharacters", () => {
it("#then rejects the malicious value", () => {
expect(() =>
injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: false,
include_co_authored_by: false,
git_env_prefix: "A=1; rm -rf /",
})
).toThrow('git_env_prefix must be empty or use shell-safe env assignments like "GIT_MASTER=1"')
})
})
describe("#when no config provided", () => {
it("#then uses default GIT_MASTER=1 prefix", () => {
const result = injectGitMasterConfig(SAMPLE_TEMPLATE)
expect(result).toContain("GIT_MASTER=1 git status")
expect(result).toContain("## GIT COMMAND PREFIX (MANDATORY)")
})
})
})
describe("#given git_env_prefix with commit footer", () => {
describe("#when both env prefix and footer are enabled", () => {
it("#then commit examples include the env prefix", () => {
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: true,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
})
expect(result).toContain("GIT_MASTER=1 git commit")
expect(result).toContain("Ultraworked with [Sisyphus]")
})
})
describe("#when the template already contains bare git commands in bash blocks", () => {
it("#then prefixes every git invocation in the final output", () => {
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: false,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
})
expect(result).toContain("GIT_MASTER=1 git status")
expect(result).toContain(
"GIT_MASTER=1 git merge-base HEAD main 2>/dev/null || GIT_MASTER=1 git merge-base HEAD master 2>/dev/null"
)
expect(result).toContain("MERGE_BASE=$(GIT_MASTER=1 git merge-base HEAD main)")
expect(result).toContain(
"GIT_SEQUENCE_EDITOR=: GIT_MASTER=1 git rebase -i --autosquash $MERGE_BASE"
)
})
})
describe("#when env prefix disabled but footer enabled", () => {
it("#then commit examples have no env prefix", () => {
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: true,
include_co_authored_by: false,
git_env_prefix: "",
})
expect(result).not.toContain("GIT_MASTER=1 git commit")
expect(result).toContain("git commit -m")
expect(result).toContain("Ultraworked with [Sisyphus]")
})
})
describe("#when both env prefix and co-author are enabled", () => {
it("#then commit example includes prefix, footer, and co-author", () => {
const result = injectGitMasterConfig(SAMPLE_TEMPLATE, {
commit_footer: true,
include_co_authored_by: true,
git_env_prefix: "GIT_MASTER=1",
})
expect(result).toContain("GIT_MASTER=1 git commit")
expect(result).toContain("Ultraworked with [Sisyphus]")
expect(result).toContain("Co-authored-by: Sisyphus")
})
})
})

View File

@@ -1,88 +1,14 @@
import { assertValidGitEnvPrefix, type GitMasterConfig } from "../../config/schema"
const BASH_CODE_BLOCK_PATTERN = /```bash\r?\n([\s\S]*?)```/g
const LEADING_GIT_COMMAND_PATTERN = /^([ \t]*(?:[A-Za-z_][A-Za-z0-9_]*=[^ \t]+\s+)*)git(?=[ \t]|$)/gm
const INLINE_GIT_COMMAND_PATTERN = /([;&|()][ \t]*)git(?=[ \t]|$)/g
import type { GitMasterConfig } from "../../config/schema"
export function injectGitMasterConfig(template: string, config?: GitMasterConfig): string {
const commitFooter = config?.commit_footer ?? true
const includeCoAuthoredBy = config?.include_co_authored_by ?? true
const gitEnvPrefix = assertValidGitEnvPrefix(config?.git_env_prefix ?? "GIT_MASTER=1")
let result = gitEnvPrefix ? injectGitEnvPrefix(template, gitEnvPrefix) : template
if (commitFooter || includeCoAuthoredBy) {
const injection = buildCommitFooterInjection(commitFooter, includeCoAuthoredBy, gitEnvPrefix)
const insertionPoint = result.indexOf("```\n</execution>")
result =
insertionPoint !== -1
? result.slice(0, insertionPoint) +
"```\n\n" +
injection +
"\n</execution>" +
result.slice(insertionPoint + "```\n</execution>".length)
: result + "\n\n" + injection
if (!commitFooter && !includeCoAuthoredBy) {
return template
}
return gitEnvPrefix ? prefixGitCommandsInBashCodeBlocks(result, gitEnvPrefix) : result
}
function injectGitEnvPrefix(template: string, prefix: string): string {
const envPrefixSection = [
"## GIT COMMAND PREFIX (MANDATORY)",
"",
`<git_env_prefix>`,
`**EVERY git command MUST be prefixed with \`${prefix}\`.**`,
"",
"This allows custom git hooks to detect when git-master skill is active.",
"",
"```bash",
`${prefix} git status`,
`${prefix} git add <files>`,
`${prefix} git commit -m "message"`,
`${prefix} git push`,
`${prefix} git rebase ...`,
`${prefix} git log ...`,
"```",
"",
"**NO EXCEPTIONS. Every `git` invocation must include this prefix.**",
`</git_env_prefix>`,
].join("\n")
const modeDetectionMarker = "## MODE DETECTION (FIRST STEP)"
const markerIndex = template.indexOf(modeDetectionMarker)
if (markerIndex !== -1) {
return (
template.slice(0, markerIndex) +
envPrefixSection +
"\n\n---\n\n" +
template.slice(markerIndex)
)
}
return envPrefixSection + "\n\n---\n\n" + template
}
function prefixGitCommandsInBashCodeBlocks(template: string, prefix: string): string {
return template.replace(BASH_CODE_BLOCK_PATTERN, (block, codeBlock: string) => {
return block.replace(codeBlock, prefixGitCommandsInCodeBlock(codeBlock, prefix))
})
}
function prefixGitCommandsInCodeBlock(codeBlock: string, prefix: string): string {
return codeBlock
.replace(LEADING_GIT_COMMAND_PATTERN, `$1${prefix} git`)
.replace(INLINE_GIT_COMMAND_PATTERN, `$1${prefix} git`)
}
function buildCommitFooterInjection(
commitFooter: boolean | string,
includeCoAuthoredBy: boolean,
gitEnvPrefix: string,
): string {
const sections: string[] = []
const cmdPrefix = gitEnvPrefix ? `${gitEnvPrefix} ` : ""
sections.push("### 5.5 Commit Footer & Co-Author")
sections.push("")
@@ -117,7 +43,7 @@ function buildCommitFooterInjection(
sections.push("**Example (both enabled):**")
sections.push("```bash")
sections.push(
`${cmdPrefix}git commit -m "{Commit Message}" -m "${footerText}" -m "Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>"`
`git commit -m "{Commit Message}" -m "${footerText}" -m "Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>"`
)
sections.push("```")
} else if (commitFooter) {
@@ -127,16 +53,29 @@ function buildCommitFooterInjection(
: "Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)"
sections.push("**Example:**")
sections.push("```bash")
sections.push(`${cmdPrefix}git commit -m "{Commit Message}" -m "${footerText}"`)
sections.push(`git commit -m "{Commit Message}" -m "${footerText}"`)
sections.push("```")
} else if (includeCoAuthoredBy) {
sections.push("**Example:**")
sections.push("```bash")
sections.push(
`${cmdPrefix}git commit -m "{Commit Message}" -m "Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>"`
"git commit -m \"{Commit Message}\" -m \"Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>\""
)
sections.push("```")
}
return sections.join("\n")
const injection = sections.join("\n")
const insertionPoint = template.indexOf("```\n</execution>")
if (insertionPoint !== -1) {
return (
template.slice(0, insertionPoint) +
"```\n\n" +
injection +
"\n</execution>" +
template.slice(insertionPoint + "```\n</execution>".length)
)
}
return template + "\n\n" + injection
}

View File

@@ -228,7 +228,6 @@ describe("resolveMultipleSkillsAsync", () => {
gitMasterConfig: {
commit_footer: false,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
},
}
@@ -250,7 +249,6 @@ describe("resolveMultipleSkillsAsync", () => {
gitMasterConfig: {
commit_footer: true,
include_co_authored_by: true,
git_env_prefix: "GIT_MASTER=1",
},
}
@@ -271,7 +269,6 @@ describe("resolveMultipleSkillsAsync", () => {
gitMasterConfig: {
commit_footer: true,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
},
}
@@ -305,7 +302,6 @@ describe("resolveMultipleSkillsAsync", () => {
gitMasterConfig: {
commit_footer: false,
include_co_authored_by: true,
git_env_prefix: "GIT_MASTER=1",
},
}
@@ -326,7 +322,6 @@ describe("resolveMultipleSkillsAsync", () => {
gitMasterConfig: {
commit_footer: customFooter,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
},
}
@@ -346,7 +341,6 @@ describe("resolveMultipleSkillsAsync", () => {
gitMasterConfig: {
commit_footer: true,
include_co_authored_by: false,
git_env_prefix: "GIT_MASTER=1",
},
}