fix: resolve installed OpenCode skill paths consistently

Carry forward PR #3115 ef475c79 and 780dfb78 onto current main. Preserve the newer hook-consent transforms during install and repair. Main already includes the source import fix from #3167.
This commit is contained in:
Yusuf Efe
2026-10-09 15:40:12 -04:00
committed by haelyra
parent ef648e0189
commit 0f34fdcc14
11 changed files with 342 additions and 41 deletions
+2 -20
View File
@@ -14,8 +14,6 @@ const { writeFileNoFollow: guardedWriteFile } = require('./install/guarded-write
const { withOpenCodeInstallLocks } = require('./install/opencode-install-lock');
const { isCodexUserConfig } = require('./install/codex-user-config');
const {
disableOpenCodeHookPluginRegistration,
getDisabledOpenCodePluginContent,
getRecordedHookConsent,
withHookConsent,
} = require('./install/hook-consent');
@@ -41,7 +39,7 @@ const {
updateSettingsAtomic,
validateManagedHooks,
} = require('./install/claude-settings');
const { adaptAntigravityAgent } = require('./install/antigravity-agent');
const { transformInstallContent } = require('./install/content-transform');
const { buildInstallIndex, rewriteRelativeLinks } = require('./install/link-rewrite');
const { getInstallTargetAdapter, listInstallTargetAdapters } = require('./install-targets/registry');
const { resolveInvocationEnvironment } = require('./invocation-environment');
@@ -232,24 +230,8 @@ function buildLinkIndexForOperations(operations, trustedRoot) {
return buildInstallIndex(mappings);
}
function transformCopyFileContent(operation, content) {
if (!operation.contentTransform) {
return content;
}
if (operation.contentTransform === 'antigravity-agent-frontmatter') {
return adaptAntigravityAgent(content, operation.sourceRelativePath);
}
if (operation.contentTransform === 'opencode-disable-ecc-hooks') {
return disableOpenCodeHookPluginRegistration(content, operation.sourceRelativePath);
}
if (operation.contentTransform === 'opencode-disable-plugin-entrypoint') {
return getDisabledOpenCodePluginContent();
}
throw new Error(`Unknown install content transform: ${operation.contentTransform}`);
}
function getExpectedCopyFileContent(operation, content, linkIndex) {
const transformed = transformCopyFileContent(operation, content);
const transformed = transformInstallContent(operation, content);
if (!linkIndex || !operation.sourceRelativePath || !isMarkdownPath(operation.destinationPath)) {
return transformed;
}
@@ -5,6 +5,8 @@ const path = require('path');
const {
buildValidationIssue,
createInstallTargetAdapter,
createManagedOperation,
isForeignPlatformPath,
} = require('./helpers');
const { resolveOpencodeConfigRoot } = require('../opencode-paths');
@@ -88,5 +90,27 @@ module.exports = createInstallTargetAdapter({
resolveRoot: resolveOpencodeConfigRoot,
installStatePathSegments: ['ecc-install-state.json'],
nativeRootRelativePath: '.opencode',
planOperations(input, adapter) {
const modules = Array.isArray(input.modules)
? input.modules
: (input.module ? [input.module] : []);
return modules.flatMap(module => (Array.isArray(module.paths) ? module.paths : [])
.filter(sourcePath => !isForeignPlatformPath(sourcePath, adapter.target))
.flatMap(sourcePath => {
const operation = adapter.createScaffoldOperation(module.id, sourcePath, input);
if (operation.sourceRelativePath !== '.opencode') {
return [operation];
}
// Keep the repository template's sibling skills path for direct use,
// but point home installs at the skills copied inside the config root.
return [operation, createManagedOperation({
moduleId: module.id,
sourceRelativePath: '.opencode/opencode.json',
destinationPath: path.join(operation.destinationPath, 'opencode.json'),
strategy: operation.strategy,
contentTransform: 'opencode-home-skills-path',
})];
}));
},
validate: defaultValidateOpencodeHome,
});
+1 -17
View File
@@ -51,28 +51,12 @@ const {
prepareExcludedPathsReconciliation,
} = require('./excluded-paths-reconciliation');
const { buildInstallIndex, rewriteRelativeLinks } = require('./link-rewrite');
const { adaptAntigravityAgent } = require('./antigravity-agent');
const { transformInstallContent } = require('./content-transform');
function isMarkdownPath(filePath) {
return /\.(md|mdx|markdown)$/i.test(String(filePath || ''));
}
function transformInstallContent(operation, content) {
if (!operation.contentTransform) {
return content;
}
if (operation.contentTransform === 'antigravity-agent-frontmatter') {
return adaptAntigravityAgent(content, operation.sourceRelativePath);
}
if (operation.contentTransform === 'opencode-disable-ecc-hooks') {
return disableOpenCodeHookPluginRegistration(content, operation.sourceRelativePath);
}
if (operation.contentTransform === 'opencode-disable-plugin-entrypoint') {
return getDisabledOpenCodePluginContent();
}
throw new Error(`Unknown install content transform: ${operation.contentTransform}`);
}
// Map every copy-file operation to { sourceRel, destRel } so relative links in
// namespaced markdown can be rewritten to the file's actual installed location
// (issue #2340). Returns null when the plan lacks the data needed to do so.
+37
View File
@@ -0,0 +1,37 @@
'use strict';
const { adaptAntigravityAgent } = require('./antigravity-agent');
const { disableOpenCodeHookPluginRegistration, getDisabledOpenCodePluginContent } = require('./hook-consent');
function transformInstallContent(operation, content) {
if (!operation.contentTransform) {
return content;
}
if (operation.contentTransform === 'antigravity-agent-frontmatter') {
return adaptAntigravityAgent(content, operation.sourceRelativePath);
}
if (operation.contentTransform === 'opencode-disable-ecc-hooks') {
return transformInstallContent({ ...operation, contentTransform: 'opencode-home-skills-path' },
disableOpenCodeHookPluginRegistration(content, operation.sourceRelativePath));
}
if (operation.contentTransform === 'opencode-disable-plugin-entrypoint') {
return getDisabledOpenCodePluginContent();
}
if (operation.contentTransform === 'opencode-home-skills-path') {
const config = JSON.parse(content);
if (!Array.isArray(config?.skills?.paths) || !config.skills.paths.includes('../skills')) {
return content;
}
const installedConfig = {
...config,
skills: {
...config.skills,
paths: config.skills.paths.map(skillPath => skillPath === '../skills' ? './skills' : skillPath),
},
};
return `${JSON.stringify(installedConfig, null, 2)}\n`;
}
throw new Error(`Unknown install content transform: ${operation.contentTransform}`);
}
module.exports = { transformInstallContent };