@ccpluginizer/ccpluginizer 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -10
- package/bin/ccpluginizer +7 -1
- package/dist/index.js +338 -53
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -2,39 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
> CLI for pluginizing non-plugin Claude Code repos.
|
|
4
4
|
|
|
5
|
-
Generate
|
|
5
|
+
Generate and validate [ccpluginizer marketplace](https://github.com/lifebugz/ccpluginizer) entries from any GitHub repo with Claude Code-compatible content (skills, agents, commands, hooks, MCP servers).
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
Works with either runtime. Pick whichever you have.
|
|
10
|
+
|
|
11
|
+
With Bun:
|
|
12
|
+
|
|
9
13
|
```bash
|
|
10
14
|
bun add -g @ccpluginizer/ccpluginizer
|
|
15
|
+
# or one-shot:
|
|
16
|
+
bunx @ccpluginizer/ccpluginizer scan <owner/repo>
|
|
11
17
|
```
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
With npm or Node:
|
|
14
20
|
|
|
15
21
|
```bash
|
|
16
|
-
|
|
22
|
+
npm install -g @ccpluginizer/ccpluginizer
|
|
23
|
+
# or one-shot:
|
|
24
|
+
npx @ccpluginizer/ccpluginizer scan <owner/repo>
|
|
17
25
|
```
|
|
18
26
|
|
|
27
|
+
At runtime the CLI prefers Bun when it's around (it starts faster), and falls back to Node otherwise. You don't need to configure anything.
|
|
28
|
+
|
|
19
29
|
## Usage
|
|
20
30
|
|
|
21
31
|
```bash
|
|
22
32
|
ccpluginizer scan <owner/repo> # Generate a marketplace entry
|
|
23
|
-
ccpluginizer submit <owner/repo> # Open a PR to add the repo to the catalog
|
|
24
33
|
ccpluginizer validate <entry.json> # Validate an entry against the schema
|
|
25
34
|
```
|
|
26
35
|
|
|
36
|
+
To add a repo to the catalog, run `scan`, commit the JSON to `entries/<name>.json` in the catalog repo, and open a PR. See the catalog's [CONTRIBUTING.md](https://github.com/lifebugz/ccpluginizer/blob/main/CONTRIBUTING.md).
|
|
37
|
+
|
|
27
38
|
`<owner/repo>` accepts either GitHub shorthand (`elysiajs/skills`) or a full URL (`https://github.com/elysiajs/skills`).
|
|
28
39
|
|
|
29
40
|
## How it works
|
|
30
41
|
|
|
31
|
-
ccpluginizer detects skills, agents, commands, hooks, and MCP servers in the source repo, then synthesizes a marketplace entry that uses Claude Code's `strict: false` mode to point at the source.
|
|
32
|
-
|
|
33
|
-
Three detection layers:
|
|
42
|
+
ccpluginizer detects skills, agents, commands, hooks, and MCP servers in the source repo, then synthesizes a marketplace entry that uses Claude Code's `strict: false` mode to point at the source. The catalog never holds a copy of the source itself.
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
2. **Manifest metadata** — `.claude-plugin/manifest.json` or `.ccpluginizer.json` marker file.
|
|
37
|
-
3. **Heuristic fallback** — looks for `SKILL.md` files with YAML frontmatter, `commands/*.md`, etc.
|
|
44
|
+
Detection runs in three passes. The first looks at convention paths like `.claude/skills/`, `.claude/agents/`, and `.claude/commands/`. The second reads `.claude-plugin/manifest.json` or a `.ccpluginizer.json` marker file if the repo has one. The third is a heuristic fallback for repos that follow neither convention. It scans for `SKILL.md` files with YAML frontmatter, `commands/*.md`, and similar patterns.
|
|
38
45
|
|
|
39
46
|
## Repository
|
|
40
47
|
|
package/bin/ccpluginizer
CHANGED
|
@@ -8,4 +8,10 @@ while [ -L "$script" ]; do
|
|
|
8
8
|
*) script="$(dirname "$script")/$link" ;;
|
|
9
9
|
esac
|
|
10
10
|
done
|
|
11
|
-
|
|
11
|
+
dist="$(cd "$(dirname "$script")" && pwd)/../dist/index.js"
|
|
12
|
+
# Prefer bun when available (bunx users); fall back to node (npx/npm users).
|
|
13
|
+
# The dist bundle is built with --target node so both runtimes work.
|
|
14
|
+
if command -v bun >/dev/null 2>&1; then
|
|
15
|
+
exec bun "$dist" "$@"
|
|
16
|
+
fi
|
|
17
|
+
exec node "$dist" "$@"
|
package/dist/index.js
CHANGED
|
@@ -589,13 +589,324 @@ class R {
|
|
|
589
589
|
}
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
+
// ../../node_modules/.bun/@crustjs+core@0.0.16+7524df1edfed9f02/node_modules/@crustjs/core/dist/index.js
|
|
593
|
+
init_chunk_vt64gs69();
|
|
594
|
+
function T2(j) {
|
|
595
|
+
return { meta: { name: j }, localFlags: {}, effectiveFlags: {}, args: undefined, subCommands: {}, plugins: [], preRun: undefined, run: undefined, postRun: undefined };
|
|
596
|
+
}
|
|
597
|
+
function z2(j, q2) {
|
|
598
|
+
let J = {};
|
|
599
|
+
for (let [Q, $] of Object.entries(j))
|
|
600
|
+
if ($.inherit === true)
|
|
601
|
+
J[Q] = $;
|
|
602
|
+
for (let [Q, $] of Object.entries(q2))
|
|
603
|
+
J[Q] = $;
|
|
604
|
+
return J;
|
|
605
|
+
}
|
|
606
|
+
function D2(j, q2) {
|
|
607
|
+
let J = [j.meta.name], Q = j, $ = q2;
|
|
608
|
+
while ($.length > 0) {
|
|
609
|
+
let K = Q.subCommands;
|
|
610
|
+
if (!K || Object.keys(K).length === 0)
|
|
611
|
+
break;
|
|
612
|
+
let Z = $[0];
|
|
613
|
+
if (!Z || Z.startsWith("-"))
|
|
614
|
+
break;
|
|
615
|
+
if (Z in K && K[Z]) {
|
|
616
|
+
Q = K[Z], J.push(Z), $ = $.slice(1);
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
if (Q.run)
|
|
620
|
+
break;
|
|
621
|
+
let H = Object.keys(K);
|
|
622
|
+
throw new W("COMMAND_NOT_FOUND", `Unknown command "${Z}".`, { input: Z, available: H, commandPath: [...J], parentCommand: Q });
|
|
623
|
+
}
|
|
624
|
+
return { command: Q, argv: $, commandPath: J };
|
|
625
|
+
}
|
|
626
|
+
function N2(j) {
|
|
627
|
+
for (let [q2, J] of Object.entries(j)) {
|
|
628
|
+
if (q2.startsWith("no-")) {
|
|
629
|
+
let Q = q2.slice(3);
|
|
630
|
+
throw new W("DEFINITION", `Flag "--${q2}" must not use "no-" prefix; define "${Q}" and negate with "--no-${Q}"`);
|
|
631
|
+
}
|
|
632
|
+
if (J.short?.startsWith("no-"))
|
|
633
|
+
throw new W("DEFINITION", `Short alias "-${J.short}" on "--${q2}" must not use "no-" prefix (reserved for negation)`);
|
|
634
|
+
if (J.aliases) {
|
|
635
|
+
for (let Q of J.aliases)
|
|
636
|
+
if (Q.startsWith("no-"))
|
|
637
|
+
throw new W("DEFINITION", `Alias "--${Q}" on "--${q2}" must not use "no-" prefix (reserved for negation)`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
var w2 = "CRUST_INTERNAL_VALIDATE_ONLY";
|
|
642
|
+
var P3 = 130;
|
|
643
|
+
var v2 = "__CRUST_VALIDATE_RESULT__";
|
|
644
|
+
function x2() {
|
|
645
|
+
let j = new Map;
|
|
646
|
+
return { get(q2) {
|
|
647
|
+
return j.get(q2);
|
|
648
|
+
}, has(q2) {
|
|
649
|
+
return j.has(q2);
|
|
650
|
+
}, set(q2, J) {
|
|
651
|
+
j.set(q2, J);
|
|
652
|
+
}, delete(q2) {
|
|
653
|
+
return j.delete(q2);
|
|
654
|
+
} };
|
|
655
|
+
}
|
|
656
|
+
function B2(j) {
|
|
657
|
+
if (!(j instanceof Error))
|
|
658
|
+
return false;
|
|
659
|
+
return j.name === "CancelledError";
|
|
660
|
+
}
|
|
661
|
+
function b3(j, q2) {
|
|
662
|
+
j.effectiveFlags = z2(q2, j.localFlags);
|
|
663
|
+
for (let J of Object.values(j.subCommands))
|
|
664
|
+
b3(J, j.effectiveFlags);
|
|
665
|
+
}
|
|
666
|
+
function y2(j) {
|
|
667
|
+
return { addFlag(q2, J, Q) {
|
|
668
|
+
if (J in q2.effectiveFlags)
|
|
669
|
+
j?.push(`Plugin flag "--${J}" on "${q2.meta.name}" overrides existing flag`);
|
|
670
|
+
q2.effectiveFlags[J] = Q;
|
|
671
|
+
}, addSubCommand(q2, J, Q) {
|
|
672
|
+
if (!J.trim())
|
|
673
|
+
throw new W("DEFINITION", "addSubCommand: name must be a non-empty string");
|
|
674
|
+
if (q2.subCommands[J]) {
|
|
675
|
+
j?.push(`Plugin subcommand "${J}" on "${q2.meta.name}" skipped (already exists)`);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
b3(Q, q2.effectiveFlags), q2.subCommands[J] = Q;
|
|
679
|
+
} };
|
|
680
|
+
}
|
|
681
|
+
async function A3(j, q2, J) {
|
|
682
|
+
for (let Q of j) {
|
|
683
|
+
if (!Q.setup)
|
|
684
|
+
continue;
|
|
685
|
+
await Q.setup(q2, J);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
async function F2(j, q2, J) {
|
|
689
|
+
let Q = j.map((Z) => Z.middleware).filter((Z) => Boolean(Z)), $ = -1, K = async (Z) => {
|
|
690
|
+
if (Z <= $)
|
|
691
|
+
throw new W("DEFINITION", "Plugin middleware called next() multiple times");
|
|
692
|
+
if ($ = Z, Z === Q.length) {
|
|
693
|
+
await J();
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
let H = Q[Z];
|
|
697
|
+
if (!H)
|
|
698
|
+
throw new W("DEFINITION", "Plugin middleware stack is invalid");
|
|
699
|
+
await H(q2, () => K(Z + 1));
|
|
700
|
+
};
|
|
701
|
+
await K(0);
|
|
702
|
+
}
|
|
703
|
+
function I3(j) {
|
|
704
|
+
let q2 = [...j.plugins];
|
|
705
|
+
for (let J of Object.values(j.subCommands))
|
|
706
|
+
q2.push(...I3(J));
|
|
707
|
+
return q2;
|
|
708
|
+
}
|
|
709
|
+
function k2(j) {
|
|
710
|
+
let q2 = {};
|
|
711
|
+
for (let [J, Q] of Object.entries(j))
|
|
712
|
+
q2[J] = { ...Q, aliases: Q.aliases ? [...Q.aliases] : undefined };
|
|
713
|
+
return q2;
|
|
714
|
+
}
|
|
715
|
+
function h3(j) {
|
|
716
|
+
let q2 = {};
|
|
717
|
+
for (let [J, Q] of Object.entries(j.subCommands))
|
|
718
|
+
q2[J] = h3(Q);
|
|
719
|
+
return { meta: { ...j.meta }, localFlags: k2(j.localFlags), effectiveFlags: k2(j.effectiveFlags), args: j.args ? j.args.map((J) => ({ ...J })) : undefined, subCommands: q2, plugins: [...j.plugins], preRun: j.preRun, run: j.run, postRun: j.postRun };
|
|
720
|
+
}
|
|
721
|
+
function _4(j) {
|
|
722
|
+
if (Object.freeze(j), Object.freeze(j.localFlags), Object.freeze(j.effectiveFlags), Object.freeze(j.meta), Object.freeze(j.plugins), j.args)
|
|
723
|
+
Object.freeze(j.args);
|
|
724
|
+
for (let q2 of Object.values(j.subCommands))
|
|
725
|
+
_4(q2);
|
|
726
|
+
Object.freeze(j.subCommands);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
class R2 {
|
|
730
|
+
_node;
|
|
731
|
+
_inheritedFlags;
|
|
732
|
+
constructor(j) {
|
|
733
|
+
if (!j.trim())
|
|
734
|
+
throw new W("DEFINITION", "meta.name must be a non-empty string");
|
|
735
|
+
this._node = T2(j), this._inheritedFlags = {};
|
|
736
|
+
}
|
|
737
|
+
static _createChild(j, q2) {
|
|
738
|
+
let J = new R2(j);
|
|
739
|
+
return J._inheritedFlags = q2, J;
|
|
740
|
+
}
|
|
741
|
+
_clone(j) {
|
|
742
|
+
let q2 = Object.create(Object.getPrototypeOf(this)), J = { ...this._node, localFlags: { ...this._node.localFlags }, effectiveFlags: { ...this._node.effectiveFlags }, subCommands: { ...this._node.subCommands }, plugins: [...this._node.plugins], meta: { ...this._node.meta }, args: this._node.args ? [...this._node.args] : undefined, ...j };
|
|
743
|
+
return q2._node = J, q2._inheritedFlags = this._inheritedFlags, q2;
|
|
744
|
+
}
|
|
745
|
+
meta(j) {
|
|
746
|
+
return this._clone({ meta: { ...this._node.meta, ...j } });
|
|
747
|
+
}
|
|
748
|
+
flags(j) {
|
|
749
|
+
N2(j);
|
|
750
|
+
let q2 = {};
|
|
751
|
+
for (let [J, Q] of Object.entries(j))
|
|
752
|
+
q2[J] = { ...Q };
|
|
753
|
+
return this._clone({ localFlags: q2, effectiveFlags: z2(this._inheritedFlags, q2) });
|
|
754
|
+
}
|
|
755
|
+
args(j) {
|
|
756
|
+
let q2 = j.map((J) => ({ ...J }));
|
|
757
|
+
return this._clone({ args: q2 });
|
|
758
|
+
}
|
|
759
|
+
run(j) {
|
|
760
|
+
return this._clone({ run: j });
|
|
761
|
+
}
|
|
762
|
+
preRun(j) {
|
|
763
|
+
return this._clone({ preRun: j });
|
|
764
|
+
}
|
|
765
|
+
postRun(j) {
|
|
766
|
+
return this._clone({ postRun: j });
|
|
767
|
+
}
|
|
768
|
+
use(j) {
|
|
769
|
+
return this._clone({ plugins: [...this._node.plugins, j] });
|
|
770
|
+
}
|
|
771
|
+
sub(j) {
|
|
772
|
+
if (!j.trim())
|
|
773
|
+
throw new W("DEFINITION", "Subcommand name must be a non-empty string");
|
|
774
|
+
let q2 = z2(this._inheritedFlags, this._node.localFlags);
|
|
775
|
+
return R2._createChild(j, q2);
|
|
776
|
+
}
|
|
777
|
+
command(j, q2) {
|
|
778
|
+
if (typeof j === "string") {
|
|
779
|
+
let K = j;
|
|
780
|
+
if (!q2)
|
|
781
|
+
throw new W("DEFINITION", "command(name, cb) requires a callback");
|
|
782
|
+
if (!K.trim())
|
|
783
|
+
throw new W("DEFINITION", "Subcommand name must be a non-empty string");
|
|
784
|
+
if (this._node.subCommands[K])
|
|
785
|
+
throw new W("DEFINITION", `Subcommand "${K}" is already registered`);
|
|
786
|
+
let Z = z2(this._inheritedFlags, this._node.localFlags), H = R2._createChild(K, Z), X2 = q2(H), W2 = { ...X2._node, effectiveFlags: z2(X2._inheritedFlags, X2._node.localFlags) };
|
|
787
|
+
return this._clone({ subCommands: { ...this._node.subCommands, [K]: W2 } });
|
|
788
|
+
}
|
|
789
|
+
let J = j, Q = J._node.meta.name;
|
|
790
|
+
if (!Q.trim())
|
|
791
|
+
throw new W("DEFINITION", "Subcommand name must be a non-empty string");
|
|
792
|
+
if (this._node.subCommands[Q])
|
|
793
|
+
throw new W("DEFINITION", `Subcommand "${Q}" is already registered`);
|
|
794
|
+
let $ = { ...J._node, effectiveFlags: z2(J._inheritedFlags, J._node.localFlags) };
|
|
795
|
+
return this._clone({ subCommands: { ...this._node.subCommands, [Q]: $ } });
|
|
796
|
+
}
|
|
797
|
+
async prepareCommandTree(j) {
|
|
798
|
+
let q2 = j?.argv ?? [], J = h3(this._node), Q = I3(J), $ = [], K = x2(), Z = { argv: [...q2], rootCommand: J, state: K }, H = y2($);
|
|
799
|
+
try {
|
|
800
|
+
await A3(Q, Z, H);
|
|
801
|
+
} catch (W2) {
|
|
802
|
+
if (B2(W2))
|
|
803
|
+
throw W2;
|
|
804
|
+
if (W2 instanceof W)
|
|
805
|
+
throw W2;
|
|
806
|
+
if (W2 instanceof Error)
|
|
807
|
+
throw W2;
|
|
808
|
+
throw new W("DEFINITION", String(W2));
|
|
809
|
+
}
|
|
810
|
+
_4(J);
|
|
811
|
+
let { validateCommandTree: X2 } = await Promise.resolve().then(() => (init_chunk_5apf3vnv(), exports_chunk_5apf3vnv));
|
|
812
|
+
return X2(J), { root: J, warnings: $ };
|
|
813
|
+
}
|
|
814
|
+
async execute(j) {
|
|
815
|
+
let q2 = j?.argv ?? process.argv.slice(2), J = this._node, Q = I3(J), $ = [], K = x2(), Z = { argv: [...q2], rootCommand: J, state: K }, H = y2($);
|
|
816
|
+
try {
|
|
817
|
+
await A3(Q, Z, H);
|
|
818
|
+
} catch (W2) {
|
|
819
|
+
if (B2(W2)) {
|
|
820
|
+
process.exitCode = P3;
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (W2 instanceof W) {
|
|
824
|
+
console.error(`Error: ${W2.message}`), process.exitCode = 1;
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
let U2 = W2 instanceof Error ? W2.message : String(W2);
|
|
828
|
+
console.error(`Error: ${U2}`), process.exitCode = 1;
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
if (_4(J), process.env[w2] === "1") {
|
|
832
|
+
let W2 = (async () => {
|
|
833
|
+
try {
|
|
834
|
+
let { validateCommandTree: U2 } = await Promise.resolve().then(() => (init_chunk_5apf3vnv(), exports_chunk_5apf3vnv));
|
|
835
|
+
U2(J);
|
|
836
|
+
for (let Y of $)
|
|
837
|
+
console.warn(`Warning: ${Y}`);
|
|
838
|
+
return { ok: true };
|
|
839
|
+
} catch (U2) {
|
|
840
|
+
let Y = U2 instanceof Error ? U2.message : String(U2);
|
|
841
|
+
return console.error(Y), process.exitCode = 1, { ok: false, error: U2 };
|
|
842
|
+
}
|
|
843
|
+
})();
|
|
844
|
+
return globalThis[v2] = W2, await W2, process.exit(process.exitCode ?? 0);
|
|
845
|
+
}
|
|
846
|
+
for (let W2 of $)
|
|
847
|
+
console.warn(`Warning: ${W2}`);
|
|
848
|
+
let X2 = { argv: [...q2], rootCommand: J, state: K, route: null, input: null };
|
|
849
|
+
try {
|
|
850
|
+
let W2, U2;
|
|
851
|
+
try {
|
|
852
|
+
let Y = D2(J, [...q2]);
|
|
853
|
+
X2.route = Y, W2 = Y.command, U2 = b(W2, Y.argv), X2.input = U2;
|
|
854
|
+
} catch (Y) {
|
|
855
|
+
await F2(Q, X2, async () => {
|
|
856
|
+
throw Y;
|
|
857
|
+
});
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
await F2(Q, X2, async () => {
|
|
861
|
+
if (E(W2, U2), !W2.run)
|
|
862
|
+
return;
|
|
863
|
+
let Y = { args: U2.args, flags: U2.flags, rawArgs: U2.rawArgs, command: W2 }, V;
|
|
864
|
+
try {
|
|
865
|
+
if (W2.preRun)
|
|
866
|
+
await W2.preRun(Y);
|
|
867
|
+
await W2.run(Y);
|
|
868
|
+
} catch (M3) {
|
|
869
|
+
V = M3;
|
|
870
|
+
}
|
|
871
|
+
if (W2.postRun)
|
|
872
|
+
try {
|
|
873
|
+
await W2.postRun(Y);
|
|
874
|
+
} catch (M3) {
|
|
875
|
+
if (!V)
|
|
876
|
+
V = M3;
|
|
877
|
+
else
|
|
878
|
+
console.error(`Error in postRun: ${M3 instanceof Error ? M3.message : String(M3)}`);
|
|
879
|
+
}
|
|
880
|
+
if (V)
|
|
881
|
+
throw V;
|
|
882
|
+
});
|
|
883
|
+
} catch (W2) {
|
|
884
|
+
if (B2(W2)) {
|
|
885
|
+
process.exitCode = P3;
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (W2 instanceof W) {
|
|
889
|
+
console.error(`Error: ${W2.message}`), process.exitCode = 1;
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
if (W2 instanceof Error) {
|
|
893
|
+
let U2 = new W("EXECUTION", W2.message).withCause(W2);
|
|
894
|
+
console.error(`Error: ${U2.message}`), process.exitCode = 1;
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
console.error(`Error: ${String(W2)}`), process.exitCode = 1;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
592
902
|
// src/commands/scan.ts
|
|
593
|
-
import { writeFileSync } from "fs";
|
|
903
|
+
import { writeFileSync } from "node:fs";
|
|
594
904
|
|
|
595
905
|
// src/sources/github.ts
|
|
596
|
-
import {
|
|
597
|
-
import {
|
|
598
|
-
import {
|
|
906
|
+
import { spawnSync } from "node:child_process";
|
|
907
|
+
import { mkdtempSync } from "node:fs";
|
|
908
|
+
import { tmpdir } from "node:os";
|
|
909
|
+
import { join } from "node:path";
|
|
599
910
|
|
|
600
911
|
// src/errors.ts
|
|
601
912
|
class CcpluginizerError extends Error {
|
|
@@ -639,19 +950,17 @@ class SourceCloneError extends CcpluginizerError {
|
|
|
639
950
|
}
|
|
640
951
|
|
|
641
952
|
// src/sources/github.ts
|
|
642
|
-
|
|
953
|
+
function resolveGithub(repo) {
|
|
643
954
|
const dest = mkdtempSync(join(tmpdir(), `ccp-${repo.replace("/", "-")}-`));
|
|
644
955
|
const url = `https://github.com/${repo}.git`;
|
|
645
|
-
const
|
|
646
|
-
|
|
647
|
-
|
|
956
|
+
const result = spawnSync("git", ["clone", "--depth=1", url, dest], {
|
|
957
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
958
|
+
encoding: "utf8"
|
|
648
959
|
});
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const stderr = await new Response(proc.stderr).text();
|
|
652
|
-
throw new SourceCloneError(repo, stderr);
|
|
960
|
+
if (result.status !== 0) {
|
|
961
|
+
throw new SourceCloneError(repo, result.stderr);
|
|
653
962
|
}
|
|
654
|
-
return dest;
|
|
963
|
+
return Promise.resolve(dest);
|
|
655
964
|
}
|
|
656
965
|
|
|
657
966
|
// src/sources/index.ts
|
|
@@ -690,8 +999,8 @@ async function resolveSource(input) {
|
|
|
690
999
|
}
|
|
691
1000
|
|
|
692
1001
|
// src/detector/marketplaceGuard.ts
|
|
693
|
-
import { existsSync } from "fs";
|
|
694
|
-
import { join as join2 } from "path";
|
|
1002
|
+
import { existsSync } from "node:fs";
|
|
1003
|
+
import { join as join2 } from "node:path";
|
|
695
1004
|
function checkMarketplaceGuard(repoRoot) {
|
|
696
1005
|
const marketplaceFile = join2(repoRoot, ".claude-plugin", "marketplace.json");
|
|
697
1006
|
if (existsSync(marketplaceFile)) {
|
|
@@ -700,8 +1009,8 @@ function checkMarketplaceGuard(repoRoot) {
|
|
|
700
1009
|
}
|
|
701
1010
|
|
|
702
1011
|
// src/detector/markerFile.ts
|
|
703
|
-
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
704
|
-
import { join as join3 } from "path";
|
|
1012
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
1013
|
+
import { join as join3 } from "node:path";
|
|
705
1014
|
|
|
706
1015
|
// ../../node_modules/.bun/valibot@1.3.1+7524df1edfed9f02/node_modules/valibot/dist/index.mjs
|
|
707
1016
|
var store$4;
|
|
@@ -1413,8 +1722,8 @@ function detectMarkerFile(repoRoot) {
|
|
|
1413
1722
|
}
|
|
1414
1723
|
|
|
1415
1724
|
// src/detector/conventions.ts
|
|
1416
|
-
import { existsSync as existsSync3, readdirSync, statSync } from "fs";
|
|
1417
|
-
import { join as join4 } from "path";
|
|
1725
|
+
import { existsSync as existsSync3, readdirSync, statSync } from "node:fs";
|
|
1726
|
+
import { join as join4 } from "node:path";
|
|
1418
1727
|
var FOLDER_KINDS = [
|
|
1419
1728
|
{ folder: "skills", kind: "skills" },
|
|
1420
1729
|
{ folder: "agents", kind: "agents" },
|
|
@@ -1481,8 +1790,8 @@ function mergeByKind(findings) {
|
|
|
1481
1790
|
}
|
|
1482
1791
|
|
|
1483
1792
|
// src/detector/nonStandardManifest.ts
|
|
1484
|
-
import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
1485
|
-
import { join as join5 } from "path";
|
|
1793
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
|
|
1794
|
+
import { join as join5 } from "node:path";
|
|
1486
1795
|
|
|
1487
1796
|
// src/schemas/nonStandardManifest.ts
|
|
1488
1797
|
var NonStandardManifestSchema = object({
|
|
@@ -1535,8 +1844,8 @@ function detectNonStandardManifest(repoRoot) {
|
|
|
1535
1844
|
}
|
|
1536
1845
|
|
|
1537
1846
|
// src/detector/contentSniff.ts
|
|
1538
|
-
import { readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
1539
|
-
import { join as join6, dirname, relative } from "path";
|
|
1847
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
1848
|
+
import { join as join6, dirname, relative } from "node:path";
|
|
1540
1849
|
|
|
1541
1850
|
// src/schemas/frontmatter.ts
|
|
1542
1851
|
var SkillFrontmatterSchema = object({
|
|
@@ -1654,8 +1963,8 @@ function coerceYamlValue(raw) {
|
|
|
1654
1963
|
}
|
|
1655
1964
|
|
|
1656
1965
|
// src/detector/normalize.ts
|
|
1657
|
-
import { existsSync as existsSync5 } from "fs";
|
|
1658
|
-
import { join as join7 } from "path";
|
|
1966
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
1967
|
+
import { join as join7 } from "node:path";
|
|
1659
1968
|
function normalizePath(input) {
|
|
1660
1969
|
if (input.split("/").includes("..")) {
|
|
1661
1970
|
throw new PathNormalizationError(input, "path traversal (..) not allowed");
|
|
@@ -1820,7 +2129,7 @@ function defaultEntryName(sourceRepo) {
|
|
|
1820
2129
|
}
|
|
1821
2130
|
|
|
1822
2131
|
// src/commands/scan.ts
|
|
1823
|
-
var scanCommand = new
|
|
2132
|
+
var scanCommand = new R2("scan").meta({ description: "Scan a non-plugin repo and emit a marketplace entry" }).args([{ name: "repo", type: "string", required: true, description: "owner/repo, URL, or local path" }]).flags({
|
|
1824
2133
|
output: { type: "string", short: "o", description: "Write entry JSON to file" }
|
|
1825
2134
|
}).run(async ({ args, flags }) => {
|
|
1826
2135
|
const repoPath = await resolveSource(args.repo);
|
|
@@ -1836,7 +2145,7 @@ var scanCommand = new R("scan").meta({ description: "Scan a non-plugin repo and
|
|
|
1836
2145
|
});
|
|
1837
2146
|
|
|
1838
2147
|
// src/commands/validate.ts
|
|
1839
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
2148
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
1840
2149
|
|
|
1841
2150
|
// src/schemas/marketplaceEntry.ts
|
|
1842
2151
|
var PathString = pipe(string(), startsWith("./"));
|
|
@@ -1882,7 +2191,7 @@ var MarketplaceEntrySchema = object({
|
|
|
1882
2191
|
});
|
|
1883
2192
|
|
|
1884
2193
|
// src/commands/validate.ts
|
|
1885
|
-
var validateCommand = new
|
|
2194
|
+
var validateCommand = new R2("validate").meta({ description: "Validate a marketplace entry JSON file against the schema" }).args([{ name: "entryFile", type: "string", required: true, description: "Path to entry JSON file" }]).run(({ args }) => {
|
|
1886
2195
|
const raw = readFileSync4(args.entryFile, "utf8");
|
|
1887
2196
|
const parsed = JSON.parse(raw);
|
|
1888
2197
|
const result = safeParse(MarketplaceEntrySchema, parsed);
|
|
@@ -1896,30 +2205,6 @@ var validateCommand = new R("validate").meta({ description: "Validate a marketpl
|
|
|
1896
2205
|
console.log("OK");
|
|
1897
2206
|
});
|
|
1898
2207
|
|
|
1899
|
-
// src/commands/submit.ts
|
|
1900
|
-
import { writeFileSync as writeFileSync2, mkdtempSync as mkdtempSync2 } from "fs";
|
|
1901
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
1902
|
-
import { join as join8 } from "path";
|
|
1903
|
-
var submitCommand = new R("submit").meta({ description: "Generate an entry and open a PR against ccpluginizer/marketplace" }).args([{ name: "repo", type: "string", required: true, description: "owner/repo to pluginize" }]).flags({
|
|
1904
|
-
dryRun: { type: "boolean", short: "n", description: "Print the PR plan without opening it" }
|
|
1905
|
-
}).run(async ({ args, flags }) => {
|
|
1906
|
-
const repoPath = await resolveSource(args.repo);
|
|
1907
|
-
const sourceRepo = inferSourceRepo(args.repo);
|
|
1908
|
-
const entry = synthesizeEntry({ repoRoot: repoPath, sourceRepo });
|
|
1909
|
-
const tmpFile = join8(mkdtempSync2(join8(tmpdir2(), "ccp-submit-")), `${entry.name}.json`);
|
|
1910
|
-
writeFileSync2(tmpFile, JSON.stringify(entry, null, 2) + `
|
|
1911
|
-
`, "utf8");
|
|
1912
|
-
if (flags.dryRun === true) {
|
|
1913
|
-
console.log(`Would submit:
|
|
1914
|
-
entry: ${tmpFile}
|
|
1915
|
-
to: ccpluginizer/marketplace`);
|
|
1916
|
-
console.log(JSON.stringify(entry, null, 2));
|
|
1917
|
-
return;
|
|
1918
|
-
}
|
|
1919
|
-
console.log(`Generated entry at ${tmpFile}`);
|
|
1920
|
-
console.log("Run with --dryRun to preview, or follow the manual PR workflow in CONTRIBUTING.md.");
|
|
1921
|
-
});
|
|
1922
|
-
|
|
1923
2208
|
// src/index.ts
|
|
1924
|
-
var app = new R("ccpluginizer").meta({ description: "Pluginize non-plugin Claude Code repos" }).command(scanCommand).command(validateCommand)
|
|
2209
|
+
var app = new R("ccpluginizer").meta({ description: "Pluginize non-plugin Claude Code repos" }).command(scanCommand).command(validateCommand);
|
|
1925
2210
|
await app.execute();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ccpluginizer/ccpluginizer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI for pluginizing non-plugin Claude Code repos",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,11 +34,12 @@
|
|
|
34
34
|
"typecheck": "tsc --noEmit",
|
|
35
35
|
"lint": "eslint . --max-warnings 0",
|
|
36
36
|
"test": "bun test",
|
|
37
|
-
"build": "bun build src/index.ts --outdir dist --target
|
|
37
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm",
|
|
38
38
|
"dev": "bun run src/index.ts",
|
|
39
39
|
"prepublishOnly": "bun run build"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
42
|
+
"node": ">=20",
|
|
42
43
|
"bun": ">=1.0.0"
|
|
43
44
|
},
|
|
44
45
|
"publishConfig": {
|