refactor(linkedin)!: rename plugin linkedin-thought-leadership → linkedin-studio (v3.0.0)
BREAKING CHANGE: the marketplace slug, the agent namespace (linkedin-studio:<agent>), and the runtime state-file path (~/.claude/linkedin-studio.local.md) all change. Reinstall required; existing state migrated in place (post metrics, streak, history preserved). The /linkedin:* commands are unchanged — the command namespace is set per-command in frontmatter and was always independent of the plugin slug. Functionality is byte-identical to v2.4.0; this release is pure identity. - dir + manifests: plugins/linkedin-studio + plugin.json + root marketplace.json - agent namespace updated in commands/newsletter.md (only functional invoker) - state path updated in 4 hook scripts + topic-rotation prompt + state template - catch-all skill dir renamed skills/linkedin-studio (5 functional skills unchanged) - docs + version bump to 3.0.0 across README badge, CHANGELOG, root README/CLAUDE.md - historical records (CHANGELOG past entries, docs/ build artifacts, config-audit v5.0.0 snapshots) intentionally retain the old slug Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9df3de795c
commit
b6bb61246b
196 changed files with 164 additions and 138 deletions
102
plugins/linkedin-studio/hooks/scripts/clipboard-helper.mjs
Normal file
102
plugins/linkedin-studio/hooks/scripts/clipboard-helper.mjs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/env node
|
||||
// Cross-platform clipboard helper for linkedin-studio plugin
|
||||
// Copies text to system clipboard using platform-native commands.
|
||||
// Standalone: reads stdin and copies it. Import: export { copyToClipboard, clipboardAvailable }
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
import { dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const PLATFORM_COMMANDS = {
|
||||
darwin: { copy: 'pbcopy', check: 'which pbcopy' },
|
||||
win32: { copy: 'clip', check: 'where clip' },
|
||||
linux: { copy: 'xclip -selection clipboard', check: 'which xclip' },
|
||||
};
|
||||
|
||||
const LINUX_FALLBACK = { copy: 'xsel --clipboard --input', check: 'which xsel' };
|
||||
|
||||
/**
|
||||
* Check if clipboard is available on this platform.
|
||||
* @returns {{ available: boolean, platform: string }}
|
||||
*/
|
||||
export function clipboardAvailable() {
|
||||
const platform = process.platform;
|
||||
const commands = PLATFORM_COMMANDS[platform];
|
||||
|
||||
if (!commands) {
|
||||
return { available: false, platform };
|
||||
}
|
||||
|
||||
try {
|
||||
execSync(commands.check, { stdio: 'ignore' });
|
||||
return { available: true, platform };
|
||||
} catch {
|
||||
// Linux fallback: try xsel if xclip not found
|
||||
if (platform === 'linux') {
|
||||
try {
|
||||
execSync(LINUX_FALLBACK.check, { stdio: 'ignore' });
|
||||
return { available: true, platform };
|
||||
} catch {
|
||||
return { available: false, platform };
|
||||
}
|
||||
}
|
||||
return { available: false, platform };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy text to the system clipboard.
|
||||
* Never throws — always returns a result object.
|
||||
* @param {string} text - The text to copy
|
||||
* @returns {{ success: boolean, platform: string }}
|
||||
*/
|
||||
export function copyToClipboard(text) {
|
||||
const platform = process.platform;
|
||||
|
||||
if (typeof text !== 'string') {
|
||||
return { success: false, platform };
|
||||
}
|
||||
|
||||
const commands = PLATFORM_COMMANDS[platform];
|
||||
if (!commands) {
|
||||
return { success: false, platform };
|
||||
}
|
||||
|
||||
// Determine which copy command to use
|
||||
let copyCmd = commands.copy;
|
||||
if (platform === 'linux') {
|
||||
try {
|
||||
execSync(commands.check, { stdio: 'ignore' });
|
||||
} catch {
|
||||
try {
|
||||
execSync(LINUX_FALLBACK.check, { stdio: 'ignore' });
|
||||
copyCmd = LINUX_FALLBACK.copy;
|
||||
} catch {
|
||||
return { success: false, platform };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
execSync(copyCmd, { input: text, stdio: ['pipe', 'ignore', 'ignore'] });
|
||||
return { success: true, platform };
|
||||
} catch {
|
||||
return { success: false, platform };
|
||||
}
|
||||
}
|
||||
|
||||
// Standalone execution: read stdin and copy
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
let input = '';
|
||||
process.stdin.setEncoding('utf-8');
|
||||
process.stdin.on('data', (chunk) => { input += chunk; });
|
||||
process.stdin.on('end', () => {
|
||||
const result = copyToClipboard(input);
|
||||
if (result.success) {
|
||||
process.stdout.write('COPIED\n');
|
||||
} else {
|
||||
process.stdout.write(`FAILED:${result.platform}\n`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue