Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Create `~/.config/opencode/opencode-synced.jsonc`:
"includeSessions": false,
"includePromptStash": false,
"includeModelFavorites": true,
"includeSkills": true,
"extraSecretPaths": [],
"extraConfigPaths": [],
}
Expand All @@ -89,6 +90,7 @@ Create `~/.config/opencode/opencode-synced.jsonc`:
- `~/.config/opencode/opencode.json` and `opencode.jsonc`
- `~/.config/opencode/AGENTS.md`
- `~/.config/opencode/agent/`, `command/`, `mode/`, `tool/`, `themes/`, `plugin/`
- `~/.config/opencode/skills/`
- `~/.local/state/opencode/model.json` (model favorites)
- Any extra paths in `extraConfigPaths` (allowlist, files or folders)

Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export const opencodeConfigSync: Plugin = async (ctx) => {
.boolean()
.optional()
.describe('Sync model favorites (state/model.json)'),
includeSkills: tool.schema.boolean().optional().describe('Sync skills directory'),
create: tool.schema.boolean().optional().describe('Create repo if missing'),
private: tool.schema.boolean().optional().describe('Create repo as private'),
extraSecretPaths: tool.schema.array(tool.schema.string()).optional(),
Expand All @@ -175,6 +176,7 @@ export const opencodeConfigSync: Plugin = async (ctx) => {
includeSessions: args.includeSessions,
includePromptStash: args.includePromptStash,
includeModelFavorites: args.includeModelFavorites,
includeSkills: args.includeSkills,
create: args.create,
private: args.private,
extraSecretPaths: args.extraSecretPaths,
Expand Down
5 changes: 5 additions & 0 deletions src/sync/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ describe('normalizeSyncConfig', () => {
expect(normalized.includeModelFavorites).toBe(true);
});

it('enables skills by default', () => {
const normalized = normalizeSyncConfig({});
expect(normalized.includeSkills).toBe(true);
});

it('defaults extra path lists when omitted', () => {
const normalized = normalizeSyncConfig({ includeSecrets: true });
expect(normalized.extraSecretPaths).toEqual([]);
Expand Down
4 changes: 4 additions & 0 deletions src/sync/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface SyncConfig {
includeSessions?: boolean;
includePromptStash?: boolean;
includeModelFavorites?: boolean;
includeSkills?: boolean;
secretsBackend?: SecretsBackendConfig;
extraSecretPaths?: string[];
extraConfigPaths?: string[];
Expand All @@ -43,6 +44,7 @@ export interface NormalizedSyncConfig extends SyncConfig {
includeSessions: boolean;
includePromptStash: boolean;
includeModelFavorites: boolean;
includeSkills: boolean;
secretsBackend?: SecretsBackendConfig;
extraSecretPaths: string[];
extraConfigPaths: string[];
Expand Down Expand Up @@ -106,12 +108,14 @@ export function normalizeSecretsBackend(
export function normalizeSyncConfig(config: SyncConfig): NormalizedSyncConfig {
const includeSecrets = Boolean(config.includeSecrets);
const includeModelFavorites = config.includeModelFavorites !== false;
const includeSkills = config.includeSkills !== false;
return {
includeSecrets,
includeMcpSecrets: includeSecrets ? Boolean(config.includeMcpSecrets) : false,
includeSessions: Boolean(config.includeSessions),
includePromptStash: Boolean(config.includePromptStash),
includeModelFavorites,
includeSkills,
secretsBackend: normalizeSecretsBackend(config.secretsBackend),
extraSecretPaths: Array.isArray(config.extraSecretPaths) ? config.extraSecretPaths : [],
extraConfigPaths: Array.isArray(config.extraConfigPaths) ? config.extraConfigPaths : [],
Expand Down
29 changes: 29 additions & 0 deletions src/sync/paths.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,33 @@ describe('buildSyncPlan', () => {

expect(disabledItem).toBeUndefined();
});

it('includes skills directory by default and allows disabling', () => {
const env = { HOME: '/home/test' } as NodeJS.ProcessEnv;
const locations = resolveSyncLocations(env, 'linux');
const config: SyncConfig = {
repo: { owner: 'acme', name: 'config' },
includeSecrets: false,
};

const plan = buildSyncPlan(normalizeSyncConfig(config), locations, '/repo', 'linux');
const skillsItem = plan.items.find((item) =>
item.localPath.endsWith('/.config/opencode/skills')
);

expect(skillsItem).toBeTruthy();
expect(skillsItem?.type).toBe('dir');

const disabledPlan = buildSyncPlan(
normalizeSyncConfig({ ...config, includeSkills: false }),
locations,
'/repo',
'linux'
);
const disabledItem = disabledPlan.items.find((item) =>
item.localPath.endsWith('/.config/opencode/skills')
);

expect(disabledItem).toBeUndefined();
});
});
11 changes: 11 additions & 0 deletions src/sync/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const DEFAULT_OVERRIDES_NAME = 'opencode-synced.overrides.jsonc';
const DEFAULT_STATE_NAME = 'sync-state.json';

const CONFIG_DIRS = ['agent', 'command', 'mode', 'tool', 'themes', 'plugin'];
const SKILLS_DIR = 'skills';
const SESSION_DIRS = ['storage/session', 'storage/message', 'storage/part', 'storage/session_diff'];
const PROMPT_STASH_FILES = ['prompt-stash.jsonl', 'prompt-history.jsonl'];
const MODEL_FAVORITES_FILE = 'model.json';
Expand Down Expand Up @@ -224,6 +225,16 @@ export function buildSyncPlan(
});
}

if (config.includeSkills !== false) {
items.push({
localPath: path.join(configRoot, SKILLS_DIR),
repoPath: path.join(repoConfigRoot, SKILLS_DIR),
type: 'dir',
isSecret: false,
isConfigFile: false,
});
}
Comment on lines +228 to +236
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The config parameter is of type NormalizedSyncConfig, where includeSkills is a boolean. The check config.includeSkills !== false is therefore equivalent to simply config.includeSkills. Using the simpler form improves readability.

For context, the same pattern is used for includeModelFavorites on line 218, which could also be simplified.

Suggested change
if (config.includeSkills !== false) {
items.push({
localPath: path.join(configRoot, SKILLS_DIR),
repoPath: path.join(repoConfigRoot, SKILLS_DIR),
type: 'dir',
isSecret: false,
isConfigFile: false,
});
}
if (config.includeSkills) {
items.push({
localPath: path.join(configRoot, SKILLS_DIR),
repoPath: path.join(repoConfigRoot, SKILLS_DIR),
type: 'dir',
isSecret: false,
isConfigFile: false,
});
}


if (config.includeSecrets) {
if (!usingSecretsBackend) {
items.push(
Expand Down
4 changes: 4 additions & 0 deletions src/sync/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ interface InitOptions {
includeSessions?: boolean;
includePromptStash?: boolean;
includeModelFavorites?: boolean;
includeSkills?: boolean;
create?: boolean;
private?: boolean;
extraSecretPaths?: string[];
Expand Down Expand Up @@ -317,6 +318,7 @@ export function createSyncService(ctx: SyncServiceContext): SyncService {
const includeSessions = config.includeSessions ? 'enabled' : 'disabled';
const includePromptStash = config.includePromptStash ? 'enabled' : 'disabled';
const includeModelFavorites = config.includeModelFavorites ? 'enabled' : 'disabled';
const includeSkills = config.includeSkills ? 'enabled' : 'disabled';
const secretsBackend = config.secretsBackend?.type ?? 'none';
const lastPull = state.lastPull ?? 'never';
const lastPush = state.lastPush ?? 'never';
Expand All @@ -340,6 +342,7 @@ export function createSyncService(ctx: SyncServiceContext): SyncService {
`Sessions: ${includeSessions}`,
`Prompt stash: ${includePromptStash}`,
`Model favorites: ${includeModelFavorites}`,
`Skills: ${includeSkills}`,
`Last pull: ${lastPull}`,
`Last push: ${lastPush}`,
`Working tree: ${changesLabel}`,
Expand Down Expand Up @@ -767,6 +770,7 @@ async function buildConfigFromInit($: Shell, options: InitOptions) {
includeSessions: options.includeSessions ?? false,
includePromptStash: options.includePromptStash ?? false,
includeModelFavorites: options.includeModelFavorites ?? true,
includeSkills: options.includeSkills ?? true,
extraSecretPaths: options.extraSecretPaths ?? [],
extraConfigPaths: options.extraConfigPaths ?? [],
localRepoPath: options.localRepoPath,
Expand Down
Loading