Compare commits

...

4 Commits

Author SHA1 Message Date
jkunz c38e94bcf3 v2.18.1
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-05-10 14:41:49 +00:00
jkunz b9b51f29d1 fix(config): use inherited stdio for opencode handoff 2026-05-10 14:41:08 +00:00
jkunz a3ad48368d v2.18.0
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-05-10 13:43:05 +00:00
jkunz c10b764c0a feat(config): add opencode config fix 2026-05-10 13:42:57 +00:00
6 changed files with 193 additions and 31 deletions
+12
View File
@@ -3,6 +3,18 @@
## Pending
## 2026-05-10 - 2.18.1
### Fixes
- Run `gitzone config fix` opencode handoff with inherited terminal I/O.
## 2026-05-10 - 2.18.0
### Features
- Add `gitzone config fix` to invoke opencode for configuration repair.
## 2026-05-10 - 2.17.0
### Features
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@git.zone/cli",
"private": false,
"version": "2.17.0",
"version": "2.18.1",
"description": "A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
@@ -87,7 +87,7 @@
"@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartscaf": "^4.0.21",
"@push.rocks/smartshell": "^3.3.7",
"@push.rocks/smartshell": "^3.5.0",
"@push.rocks/smartunique": "^3.0.9",
"@push.rocks/smartupdate": "^2.0.6",
"prettier": "^3.8.1"
+35 -23
View File
@@ -81,8 +81,8 @@ importers:
specifier: ^4.0.21
version: 4.0.21
'@push.rocks/smartshell':
specifier: ^3.3.7
version: 3.3.7
specifier: ^3.5.0
version: 3.5.0
'@push.rocks/smartunique':
specifier: ^3.0.9
version: 3.0.9
@@ -1063,6 +1063,9 @@ packages:
'@push.rocks/smartdelay@3.0.5':
resolution: {integrity: sha512-mUuI7kj2f7ztjpic96FvRIlf2RsKBa5arw81AHNsndbxO6asRcxuWL8dTVxouEIK8YsBUlj0AsrCkHhMbLQdHw==}
'@push.rocks/smartdelay@3.1.0':
resolution: {integrity: sha512-59xveBMbWmbFhh/rqhQnYG/klg/VONG9hV8+RQ7ftqsNRkcmUT+VM5etAbODgAUvsF4lxK+xVR0tbZOo0kGhRQ==}
'@push.rocks/smartdiff@1.1.0':
resolution: {integrity: sha512-AAz/unmko0C+g+60odOoK32PE3Ci3YLoB+zfg1LGLyVRCthcdzjqa1C2Km0MfG7IyJQKPdj8J5HPubtpm3ZeaQ==}
@@ -1192,6 +1195,9 @@ packages:
'@push.rocks/smartpromise@4.2.3':
resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==}
'@push.rocks/smartpromise@4.2.4':
resolution: {integrity: sha512-8FUyYt94hOIY9mqHjitn4h69u0jbEtTF2RKKw2DpiTVFjpDTk9gXbVHZ/V+xEcBrN4mrzdQES0OiDmkNPoddEQ==}
'@push.rocks/smartpuppeteer@2.0.5':
resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==}
@@ -1222,8 +1228,8 @@ packages:
'@push.rocks/smartserve@2.0.1':
resolution: {integrity: sha512-YQb2qexfCzCqOlLWBBXKMg6xG4zahCPAxomz/KEKAwHtW6wMTtuHKSTSkRTQ0vl9jssLMAmRz2OyafiL9XGJXQ==}
'@push.rocks/smartshell@3.3.7':
resolution: {integrity: sha512-b3st2+FjHUVhZZRlXfw93+SQA0UMVlURqe55uVpWdjJX7jeGXTTeszuYygtiR99zC5iZ8WZhGDct3N2L1qc/qw==}
'@push.rocks/smartshell@3.5.0':
resolution: {integrity: sha512-Hx9TVvC/AWxZsnm1GDb+W4Fe58nf1FkKbSBABUgkxct4XRYugBI2z9Twnjm3R9vdRry8oy0enfR9NPVhisGaGA==}
'@push.rocks/smartspawn@3.0.3':
resolution: {integrity: sha512-DyrGPV69wwOiJgKkyruk5hS3UEGZ99xFAqBE9O2nM8VXCRLbbty3xt1Ug5Z092ZZmJYaaGMSnMw3ijyZJFCT0Q==}
@@ -3917,9 +3923,9 @@ packages:
engines: {node: '>= 8'}
hasBin: true
which@6.0.1:
resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==}
engines: {node: ^20.17.0 || >=22.9.0}
which@7.0.0:
resolution: {integrity: sha512-RancgH2dmbLdHl6LRhEqvklWMgl/Hdnun0Y90KhBOLkMefg8Qa7/Zel8Sm+8HEcP6DEjzsWzpkuBQEZok58isA==}
engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0}
hasBin: true
wordwrap@1.0.0:
@@ -4802,7 +4808,7 @@ snapshots:
'@push.rocks/smartlog': 3.2.1
'@push.rocks/smartlog-destination-local': 9.0.2
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
'@push.rocks/smarttime': 4.2.3
typedoc: 0.28.17(typescript@5.9.3)
typescript: 5.9.3
@@ -4832,7 +4838,7 @@ snapshots:
'@push.rocks/smartnpm': 2.0.6
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartrequest': 5.0.1
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
transitivePeerDependencies:
- '@nuxt/kit'
- aws-crt
@@ -4845,7 +4851,7 @@ snapshots:
'@git.zone/tsrun@2.0.1':
dependencies:
'@push.rocks/smartfile': 13.1.2
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
tsx: 4.21.0
'@git.zone/tstest@3.3.2(socks@2.8.7)(typescript@5.9.3)':
@@ -4870,7 +4876,7 @@ snapshots:
'@push.rocks/smartrequest': 5.0.1
'@push.rocks/smarts3': 5.3.0
'@push.rocks/smartserve': 2.0.1
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
'@push.rocks/smarttime': 4.2.3
'@push.rocks/smartwatch': 6.3.0
'@types/ws': 8.18.1
@@ -5590,7 +5596,7 @@ snapshots:
'@push.rocks/smartai': 2.0.0(typescript@5.9.3)(ws@8.19.0)(zod@3.25.76)
'@push.rocks/smartfs': 1.5.0
'@push.rocks/smartrequest': 5.0.1
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
ai: 6.0.116(zod@3.25.76)
zod: 3.25.76
transitivePeerDependencies:
@@ -5775,6 +5781,10 @@ snapshots:
dependencies:
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartdelay@3.1.0':
dependencies:
'@push.rocks/smartpromise': 4.2.4
'@push.rocks/smartdiff@1.1.0':
dependencies:
diff: 8.0.3
@@ -5818,7 +5828,7 @@ snapshots:
'@push.rocks/smartexit@2.0.3':
dependencies:
'@push.rocks/lik': 6.3.1
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartpromise': 4.2.4
'@push.rocks/smartexpect@2.5.0':
dependencies:
@@ -5897,7 +5907,7 @@ snapshots:
'@push.rocks/smartfile': 11.2.7
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
'@push.rocks/smartstring': 4.1.0
'@push.rocks/smarttime': 4.2.3
'@types/diff': 8.0.0
@@ -5968,7 +5978,7 @@ snapshots:
'@push.rocks/smartmustache': 3.0.2
'@push.rocks/smartpnpm': 1.0.6
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
'@tsclass/tsclass': 4.4.4
transitivePeerDependencies:
- supports-color
@@ -6159,14 +6169,16 @@ snapshots:
'@push.rocks/smartpnpm@1.0.6':
dependencies:
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
'@push.rocks/smartpromise@4.2.3': {}
'@push.rocks/smartpromise@4.2.4': {}
'@push.rocks/smartpuppeteer@2.0.5(typescript@5.9.3)':
dependencies:
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
puppeteer: 24.35.0(typescript@5.9.3)
tree-kill: 1.2.2
transitivePeerDependencies:
@@ -6233,7 +6245,7 @@ snapshots:
'@push.rocks/smartinteract': 2.0.16
'@push.rocks/smartobject': 1.0.12
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartshell': 3.3.7
'@push.rocks/smartshell': 3.5.0
'@push.rocks/smartyaml': 3.0.4
'@push.rocks/smartserve@2.0.1':
@@ -6249,13 +6261,13 @@ snapshots:
- bufferutil
- utf-8-validate
'@push.rocks/smartshell@3.3.7':
'@push.rocks/smartshell@3.5.0':
dependencies:
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartdelay': 3.1.0
'@push.rocks/smartexit': 2.0.3
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartpromise': 4.2.4
'@types/which': 3.0.4
which: 6.0.1
which: 7.0.0
'@push.rocks/smartspawn@3.0.3':
dependencies:
@@ -9513,7 +9525,7 @@ snapshots:
dependencies:
isexe: 2.0.0
which@6.0.1:
which@7.0.0:
dependencies:
isexe: 4.0.0
+3
View File
@@ -266,6 +266,9 @@ gitzone config release
# Validate schema, legacy keys, release targets, registries, and npm auth
gitzone config doctor
# Use opencode to repair configuration issues found by doctor
gitzone config fix
# Read the npm release target registries
gitzone config get release.targets.npm.registries
+1 -1
View File
@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@git.zone/cli',
version: '2.17.0',
version: '2.18.1',
description: 'A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.'
}
+140 -5
View File
@@ -117,6 +117,9 @@ export const run = async (argvArg: any) => {
case "doctor":
await handleDoctor(mode);
break;
case "fix":
await handleFix(argvArg, mode);
break;
case "migrate":
await handleMigrate(value, mode);
break;
@@ -165,6 +168,7 @@ async function handleInteractiveMenu(): Promise<void> {
{ name: "Configure release workflow", value: "release" },
{ name: "Configure services", value: "services" },
{ name: "Validate configuration (doctor)", value: "doctor" },
{ name: "Fix configuration with opencode", value: "fix" },
{ name: "Add an npm target registry", value: "add" },
{ name: "Remove an npm target registry", value: "remove" },
{ name: "Clear npm target registries", value: "clear" },
@@ -213,6 +217,9 @@ async function handleInteractiveMenu(): Promise<void> {
case "doctor":
await handleDoctor(defaultCliMode);
break;
case "fix":
await handleFix({ _: ["config", "fix"] }, defaultCliMode);
break;
case "help":
showHelp();
break;
@@ -890,6 +897,86 @@ async function handleRelease(mode: ICliMode): Promise<void> {
}
async function handleDoctor(mode: ICliMode): Promise<void> {
const findings = await collectDoctorFindings();
printDoctorResult(findings, mode);
}
async function handleFix(argvArg: any, mode: ICliMode): Promise<void> {
if (mode.json) {
printJson({
ok: false,
error: "JSON output is not supported for `gitzone config fix`. Use `gitzone config doctor --json` for machine-readable diagnostics.",
});
process.exitCode = 1;
return;
}
const findings = await collectDoctorFindings();
const counts = countDoctorFindings(findings);
const extraInstructions = (argvArg._?.slice(2).join(" ") || "").trim();
const force = Boolean(argvArg.force);
if (counts.error === 0 && counts.warn === 0 && !extraInstructions && !force) {
plugins.logger.log(
"success",
"Configuration doctor found no issues. Use `gitzone config fix --force` to run opencode anyway.",
);
return;
}
if (!mode.yes) {
if (!mode.interactive) {
throw new Error("Config fix requires an interactive terminal or `-y` to run opencode non-interactively.");
}
const confirmed = await plugins.smartinteract.SmartInteract.getCliConfirmation(
`Run opencode to fix .smartconfig.json? (${counts.error} error, ${counts.warn} warning)`,
true,
);
if (!confirmed) {
plugins.logger.log("info", "Config fix cancelled.");
return;
}
}
const opencodeArgs = [
"run",
"--title",
"gitzone config fix",
"--dir",
process.cwd(),
];
if (mode.yes) {
opencodeArgs.push("--dangerously-skip-permissions");
}
opencodeArgs.push(buildConfigFixPrompt(findings, extraInstructions));
plugins.logger.log("info", "Starting opencode configuration fix...");
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: "bash",
sourceFilePaths: [],
});
let result: plugins.smartshell.IExecResult;
try {
result = await smartshellInstance.execSpawn("opencode", opencodeArgs, {
stdio: "inherit",
});
} catch (error) {
throw new Error(`Failed to run opencode: ${error instanceof Error ? error.message : String(error)}`);
}
if (result.exitCode !== 0) {
plugins.logger.log("error", `opencode exited with code ${result.exitCode}`);
process.exitCode = result.exitCode || 1;
return;
}
await formatSmartconfigWithDiff(mode);
const finalFindings = await collectDoctorFindings();
printDoctorResult(finalFindings, mode);
}
async function collectDoctorFindings(): Promise<IDoctorFinding[]> {
const findings: IDoctorFinding[] = [];
const smartconfigPath = getSmartconfigPath();
const smartconfigExists = await plugins.smartfs.file(smartconfigPath).exists();
@@ -900,7 +987,7 @@ async function handleDoctor(mode: ICliMode): Promise<void> {
message: ".smartconfig.json does not exist",
fix: "Run `gitzone config project` to create project basics.",
});
return printDoctorResult(findings, mode);
return findings;
}
let smartconfigData: Record<string, any>;
@@ -912,7 +999,7 @@ async function handleDoctor(mode: ICliMode): Promise<void> {
message: ".smartconfig.json is not valid JSON",
fix: error instanceof Error ? error.message : String(error),
});
return printDoctorResult(findings, mode);
return findings;
}
const cliConfig = getCliConfigValueFromData(smartconfigData, "") || {};
@@ -958,7 +1045,7 @@ async function handleDoctor(mode: ICliMode): Promise<void> {
validateCommitConfig(cliConfig.commit || {}, findings);
await validateReleaseConfig(cliConfig.release || {}, findings);
printDoctorResult(findings, mode);
return findings;
}
/**
@@ -1274,14 +1361,56 @@ function getDefaultEnabledTargets(currentTargets: Record<string, any>): string[]
return enabledTargets;
}
function printDoctorResult(findings: IDoctorFinding[], mode: ICliMode): void {
const counts = findings.reduce(
function countDoctorFindings(
findings: IDoctorFinding[],
): Record<TDoctorFindingLevel, number> {
return findings.reduce(
(accumulator, finding) => {
accumulator[finding.level] += 1;
return accumulator;
},
{ ok: 0, warn: 0, error: 0 } as Record<TDoctorFindingLevel, number>,
);
}
function buildConfigFixPrompt(
findings: IDoctorFinding[],
extraInstructions: string,
): string {
const promptParts = [
"Other /c-* commands can be found at ~/.config/opencode/commands/*",
"# gitzone config fix",
"",
`Working directory: ${process.cwd()}`,
"",
"Repair the project configuration so `gitzone config doctor --json` passes.",
"",
"Rules:",
"- Read `.smartconfig.json`, `package.json`, and nearby project metadata before editing.",
"- Keep gitzone CLI config under `@git.zone/cli` in `.smartconfig.json`.",
`- Use schemaVersion ${CURRENT_GITZONE_CLI_SCHEMA_VERSION} for ` +
"`@git.zone/cli`.",
"- Use target-based release config: `release.targets.git`, `release.targets.npm`, and `release.targets.docker`.",
"- Keep npm registries only at `@git.zone/cli.release.targets.npm.registries`.",
"- Do not add runtime legacy compatibility code. If legacy config exists, migrate it explicitly.",
"- Do not commit, release, install dependencies, or modify unrelated files.",
"- Use pnpm commands only if commands are needed.",
"- Run `gitzone config doctor --json` after changes and keep fixing until no errors remain.",
"- Run `git diff --check` after changes to catch whitespace problems.",
"",
"Current doctor findings:",
JSON.stringify(findings, null, 2),
];
if (extraInstructions) {
promptParts.push("", "Additional user instructions:", extraInstructions);
}
return promptParts.join("\n");
}
function printDoctorResult(findings: IDoctorFinding[], mode: ICliMode): void {
const counts = countDoctorFindings(findings);
if (mode.json) {
printJson({
@@ -1666,6 +1795,7 @@ export function showHelp(mode?: ICliMode): void {
{ name: "cli", description: "Configure CLI behavior interactively" },
{ name: "release", description: "Configure release workflow interactively" },
{ name: "doctor", description: "Validate .smartconfig.json" },
{ name: "fix [instructions]", description: "Use opencode to repair .smartconfig.json" },
{ name: "get <path>", description: "Read a single config value" },
{ name: "set <path> <value>", description: "Write a config value" },
{ name: "unset <path>", description: "Delete a config value" },
@@ -1689,6 +1819,8 @@ export function showHelp(mode?: ICliMode): void {
"gitzone config show --json",
"gitzone config project",
"gitzone config doctor --json",
"gitzone config fix",
"gitzone config fix -y",
"gitzone config get release.targets.npm.accessLevel",
"gitzone config set cli.interactive false",
"gitzone config set cli.output json",
@@ -1708,6 +1840,7 @@ export function showHelp(mode?: ICliMode): void {
console.log(" cli Configure CLI behavior interactively");
console.log(" release Configure release workflow interactively");
console.log(" doctor Validate .smartconfig.json");
console.log(" fix [instructions] Use opencode to repair .smartconfig.json");
console.log(" get <path> Read a single config value");
console.log(" set <path> <value> Write a config value");
console.log(" unset <path> Delete a config value");
@@ -1730,6 +1863,8 @@ export function showHelp(mode?: ICliMode): void {
console.log(" gitzone config cli");
console.log(" gitzone config release");
console.log(" gitzone config doctor --json");
console.log(" gitzone config fix");
console.log(" gitzone config fix -y");
console.log(" gitzone config get release.targets.npm.accessLevel");
console.log(" gitzone config set cli.interactive false");
console.log(" gitzone config set cli.output json");