@kood/claude-code 0.6.5 → 0.6.6
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/dist/index.js +248 -148
- package/package.json +1 -1
- package/templates/.claude/scripts/deploy/build-run.sh +36 -0
- package/templates/.claude/scripts/deploy/deploy-check.sh +38 -0
- package/templates/.claude/scripts/git/git-all.sh +57 -0
- package/templates/.claude/scripts/git/git-clean-check.sh +31 -0
- package/templates/.claude/scripts/git/git-commit.sh +51 -0
- package/templates/.claude/scripts/git/git-info.sh +34 -0
- package/templates/.claude/scripts/git/git-push.sh +50 -0
- package/templates/.claude/scripts/lint/lint-check.sh +56 -0
- package/templates/.claude/scripts/lint/lint-file.sh +41 -0
- package/templates/.claude/scripts/pm/pm-detect.sh +25 -0
- package/templates/.claude/scripts/pm/pm-run.sh +41 -0
- package/templates/.claude/scripts/version/version-bump.sh +54 -0
- package/templates/.claude/scripts/version/version-find.sh +49 -0
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ var banner = () => {
|
|
|
23
23
|
|
|
24
24
|
// src/commands/init.ts
|
|
25
25
|
import fs8 from "fs-extra";
|
|
26
|
+
import os from "os";
|
|
26
27
|
|
|
27
28
|
// src/features/templates/template-path-resolver.ts
|
|
28
29
|
import path2 from "path";
|
|
@@ -319,6 +320,7 @@ var createExtrasCopier = (extrasType) => {
|
|
|
319
320
|
var copyCommands = createExtrasCopier("commands");
|
|
320
321
|
var copyAgents = createExtrasCopier("agents");
|
|
321
322
|
var copyInstructions = createExtrasCopier("instructions");
|
|
323
|
+
var copyScripts = createExtrasCopier("scripts");
|
|
322
324
|
var getSkillsToInstall = async (skillsSrc, templates) => {
|
|
323
325
|
const metadataMap = await loadAllSkillMetadata(skillsSrc);
|
|
324
326
|
const isNonUITemplate = templates.some((t) => NON_UI_TEMPLATES.includes(t));
|
|
@@ -363,6 +365,7 @@ var checkExistingClaudeFiles = async (targetDir) => {
|
|
|
363
365
|
const commandsDir = path8.join(targetDir, ".claude", "commands");
|
|
364
366
|
const agentsDir = path8.join(targetDir, ".claude", "agents");
|
|
365
367
|
const instructionsDir = path8.join(targetDir, ".claude", "instructions");
|
|
368
|
+
const scriptsDir = path8.join(targetDir, ".claude", "scripts");
|
|
366
369
|
if (await fs6.pathExists(skillsDir)) {
|
|
367
370
|
existingFiles.push(".claude/skills/");
|
|
368
371
|
}
|
|
@@ -375,6 +378,9 @@ var checkExistingClaudeFiles = async (targetDir) => {
|
|
|
375
378
|
if (await fs6.pathExists(instructionsDir)) {
|
|
376
379
|
existingFiles.push(".claude/instructions/");
|
|
377
380
|
}
|
|
381
|
+
if (await fs6.pathExists(scriptsDir)) {
|
|
382
|
+
existingFiles.push(".claude/scripts/");
|
|
383
|
+
}
|
|
378
384
|
return existingFiles;
|
|
379
385
|
};
|
|
380
386
|
var checkAllExtrasExist = async (_templates) => {
|
|
@@ -383,13 +389,150 @@ var checkAllExtrasExist = async (_templates) => {
|
|
|
383
389
|
const commandsSrc = path8.join(claudeDir, "commands");
|
|
384
390
|
const agentsSrc = path8.join(claudeDir, "agents");
|
|
385
391
|
const instructionsSrc = path8.join(claudeDir, "instructions");
|
|
392
|
+
const scriptsSrc = path8.join(claudeDir, "scripts");
|
|
386
393
|
const hasSkills = await hasFiles(skillsSrc);
|
|
387
394
|
const hasCommands = await hasFiles(commandsSrc);
|
|
388
395
|
const hasAgents = await hasFiles(agentsSrc);
|
|
389
396
|
const hasInstructions = await hasFiles(instructionsSrc);
|
|
390
|
-
|
|
397
|
+
const hasScripts = await hasFiles(scriptsSrc);
|
|
398
|
+
return { hasSkills, hasCommands, hasAgents, hasInstructions, hasScripts };
|
|
391
399
|
};
|
|
392
400
|
|
|
401
|
+
// src/features/extras/extras-installer.ts
|
|
402
|
+
function logExistingFilesUpdate(existingClaudeFiles) {
|
|
403
|
+
if (existingClaudeFiles.length > 0) {
|
|
404
|
+
logger.info("Updating existing extras:");
|
|
405
|
+
existingClaudeFiles.forEach((f) => logger.step(f));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async function installSkillsIfNeeded(templates, targetDir, shouldInstall, hasSkills) {
|
|
409
|
+
if (!shouldInstall) {
|
|
410
|
+
return { files: 0, directories: 0 };
|
|
411
|
+
}
|
|
412
|
+
if (!hasSkills) {
|
|
413
|
+
logger.warn("No skills found in selected templates.");
|
|
414
|
+
return { files: 0, directories: 0 };
|
|
415
|
+
}
|
|
416
|
+
logger.blank();
|
|
417
|
+
logger.info("Installing skills...");
|
|
418
|
+
const skillsResult = await copySkills(templates, targetDir);
|
|
419
|
+
logger.success(
|
|
420
|
+
`Skills: ${skillsResult.files} files, ${skillsResult.directories} directories`
|
|
421
|
+
);
|
|
422
|
+
return skillsResult;
|
|
423
|
+
}
|
|
424
|
+
async function installCommandsIfNeeded(templates, targetDir, shouldInstall, hasCommands) {
|
|
425
|
+
if (!shouldInstall) {
|
|
426
|
+
return { files: 0, directories: 0 };
|
|
427
|
+
}
|
|
428
|
+
if (!hasCommands) {
|
|
429
|
+
logger.warn("No commands found in selected templates.");
|
|
430
|
+
return { files: 0, directories: 0 };
|
|
431
|
+
}
|
|
432
|
+
logger.blank();
|
|
433
|
+
logger.info("Installing commands...");
|
|
434
|
+
const commandsResult = await copyCommands(templates, targetDir);
|
|
435
|
+
logger.success(
|
|
436
|
+
`Commands: ${commandsResult.files} files, ${commandsResult.directories} directories`
|
|
437
|
+
);
|
|
438
|
+
return commandsResult;
|
|
439
|
+
}
|
|
440
|
+
async function installAgentsIfNeeded(templates, targetDir, shouldInstall, hasAgents) {
|
|
441
|
+
if (!shouldInstall) {
|
|
442
|
+
return { files: 0, directories: 0 };
|
|
443
|
+
}
|
|
444
|
+
if (!hasAgents) {
|
|
445
|
+
logger.warn("No agents found in selected templates.");
|
|
446
|
+
return { files: 0, directories: 0 };
|
|
447
|
+
}
|
|
448
|
+
logger.blank();
|
|
449
|
+
logger.info("Installing agents...");
|
|
450
|
+
const agentsResult = await copyAgents(templates, targetDir);
|
|
451
|
+
logger.success(
|
|
452
|
+
`Agents: ${agentsResult.files} files, ${agentsResult.directories} directories`
|
|
453
|
+
);
|
|
454
|
+
return agentsResult;
|
|
455
|
+
}
|
|
456
|
+
async function installScriptsIfNeeded(templates, targetDir, shouldInstall, hasScripts) {
|
|
457
|
+
if (!shouldInstall) {
|
|
458
|
+
return { files: 0, directories: 0 };
|
|
459
|
+
}
|
|
460
|
+
if (!hasScripts) {
|
|
461
|
+
logger.warn("No scripts found in selected templates.");
|
|
462
|
+
return { files: 0, directories: 0 };
|
|
463
|
+
}
|
|
464
|
+
logger.blank();
|
|
465
|
+
logger.info("Installing scripts...");
|
|
466
|
+
const scriptsResult = await copyScripts(templates, targetDir);
|
|
467
|
+
logger.success(
|
|
468
|
+
`Scripts: ${scriptsResult.files} files, ${scriptsResult.directories} directories`
|
|
469
|
+
);
|
|
470
|
+
return scriptsResult;
|
|
471
|
+
}
|
|
472
|
+
async function installInstructionsIfNeeded(templates, targetDir, shouldInstall, hasInstructions) {
|
|
473
|
+
if (!shouldInstall) {
|
|
474
|
+
return { files: 0, directories: 0 };
|
|
475
|
+
}
|
|
476
|
+
if (!hasInstructions) {
|
|
477
|
+
logger.warn("No instructions found in selected templates.");
|
|
478
|
+
return { files: 0, directories: 0 };
|
|
479
|
+
}
|
|
480
|
+
logger.blank();
|
|
481
|
+
logger.info("Installing instructions...");
|
|
482
|
+
const instructionsResult = await copyInstructions(templates, targetDir);
|
|
483
|
+
logger.success(
|
|
484
|
+
`Instructions: ${instructionsResult.files} files, ${instructionsResult.directories} directories`
|
|
485
|
+
);
|
|
486
|
+
return instructionsResult;
|
|
487
|
+
}
|
|
488
|
+
async function installExtras(templates, targetDir, flags, availability) {
|
|
489
|
+
const {
|
|
490
|
+
installSkills,
|
|
491
|
+
installCommands,
|
|
492
|
+
installAgents,
|
|
493
|
+
installInstructions,
|
|
494
|
+
installScripts
|
|
495
|
+
} = flags;
|
|
496
|
+
if (!installSkills && !installCommands && !installAgents && !installInstructions && !installScripts) {
|
|
497
|
+
return { files: 0, directories: 0 };
|
|
498
|
+
}
|
|
499
|
+
const existingClaudeFiles = await checkExistingClaudeFiles(targetDir);
|
|
500
|
+
logExistingFilesUpdate(existingClaudeFiles);
|
|
501
|
+
const skillsResult = await installSkillsIfNeeded(
|
|
502
|
+
templates,
|
|
503
|
+
targetDir,
|
|
504
|
+
installSkills,
|
|
505
|
+
availability.hasSkills
|
|
506
|
+
);
|
|
507
|
+
const commandsResult = await installCommandsIfNeeded(
|
|
508
|
+
templates,
|
|
509
|
+
targetDir,
|
|
510
|
+
installCommands,
|
|
511
|
+
availability.hasCommands
|
|
512
|
+
);
|
|
513
|
+
const agentsResult = await installAgentsIfNeeded(
|
|
514
|
+
templates,
|
|
515
|
+
targetDir,
|
|
516
|
+
installAgents,
|
|
517
|
+
availability.hasAgents
|
|
518
|
+
);
|
|
519
|
+
const instructionsResult = await installInstructionsIfNeeded(
|
|
520
|
+
templates,
|
|
521
|
+
targetDir,
|
|
522
|
+
installInstructions,
|
|
523
|
+
availability.hasInstructions
|
|
524
|
+
);
|
|
525
|
+
const scriptsResult = await installScriptsIfNeeded(
|
|
526
|
+
templates,
|
|
527
|
+
targetDir,
|
|
528
|
+
installScripts,
|
|
529
|
+
availability.hasScripts
|
|
530
|
+
);
|
|
531
|
+
const totalFiles = skillsResult.files + commandsResult.files + agentsResult.files + instructionsResult.files + scriptsResult.files;
|
|
532
|
+
const totalDirectories = skillsResult.directories + commandsResult.directories + agentsResult.directories + instructionsResult.directories + scriptsResult.directories;
|
|
533
|
+
return { files: totalFiles, directories: totalDirectories };
|
|
534
|
+
}
|
|
535
|
+
|
|
393
536
|
// src/shared/prompts/prompt-helpers.ts
|
|
394
537
|
import prompts from "prompts";
|
|
395
538
|
async function promptConfirm(message, initial = false) {
|
|
@@ -401,6 +544,15 @@ async function promptConfirm(message, initial = false) {
|
|
|
401
544
|
});
|
|
402
545
|
return { confirmed: response.confirmed ?? false };
|
|
403
546
|
}
|
|
547
|
+
async function promptSelect(message, choices) {
|
|
548
|
+
const response = await prompts({
|
|
549
|
+
type: "select",
|
|
550
|
+
name: "value",
|
|
551
|
+
message,
|
|
552
|
+
choices
|
|
553
|
+
});
|
|
554
|
+
return { value: response.value };
|
|
555
|
+
}
|
|
404
556
|
async function promptMultiselect(message, choices, options) {
|
|
405
557
|
const response = await prompts({
|
|
406
558
|
type: "multiselect",
|
|
@@ -461,17 +613,20 @@ async function promptExtrasSelection(options) {
|
|
|
461
613
|
commands,
|
|
462
614
|
agents,
|
|
463
615
|
instructions,
|
|
616
|
+
scripts,
|
|
464
617
|
hasSkills,
|
|
465
618
|
hasCommands,
|
|
466
619
|
hasAgents,
|
|
467
|
-
hasInstructions
|
|
620
|
+
hasInstructions,
|
|
621
|
+
hasScripts
|
|
468
622
|
} = options;
|
|
469
623
|
let installSkills = skills ?? false;
|
|
470
624
|
let installCommands = commands ?? false;
|
|
471
625
|
const installAgents = agents ?? hasAgents;
|
|
472
626
|
const installInstructions = instructions ?? hasInstructions;
|
|
473
|
-
const
|
|
474
|
-
|
|
627
|
+
const installScripts = scripts ?? hasScripts;
|
|
628
|
+
const noOptionsProvided = skills === void 0 && commands === void 0 && agents === void 0 && instructions === void 0 && scripts === void 0;
|
|
629
|
+
if (noOptionsProvided && (hasSkills || hasCommands || hasAgents || hasInstructions || hasScripts)) {
|
|
475
630
|
logger.blank();
|
|
476
631
|
if (hasSkills) {
|
|
477
632
|
const result = await promptConfirm(
|
|
@@ -492,126 +647,41 @@ async function promptExtrasSelection(options) {
|
|
|
492
647
|
installSkills,
|
|
493
648
|
installCommands,
|
|
494
649
|
installAgents,
|
|
495
|
-
installInstructions
|
|
650
|
+
installInstructions,
|
|
651
|
+
installScripts
|
|
496
652
|
};
|
|
497
653
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
"Overwrite existing .claude files?",
|
|
509
|
-
false
|
|
510
|
-
);
|
|
511
|
-
return response.confirmed;
|
|
512
|
-
}
|
|
513
|
-
async function installSkillsIfNeeded(templates, targetDir, shouldInstall, hasSkills) {
|
|
514
|
-
if (!shouldInstall) {
|
|
515
|
-
return { files: 0, directories: 0 };
|
|
516
|
-
}
|
|
517
|
-
if (!hasSkills) {
|
|
518
|
-
logger.warn("No skills found in selected templates.");
|
|
519
|
-
return { files: 0, directories: 0 };
|
|
520
|
-
}
|
|
521
|
-
logger.blank();
|
|
522
|
-
logger.info("Installing skills...");
|
|
523
|
-
const skillsResult = await copySkills(templates, targetDir);
|
|
524
|
-
logger.success(
|
|
525
|
-
`Skills: ${skillsResult.files} files, ${skillsResult.directories} directories`
|
|
526
|
-
);
|
|
527
|
-
return skillsResult;
|
|
528
|
-
}
|
|
529
|
-
async function installCommandsIfNeeded(templates, targetDir, shouldInstall, hasCommands) {
|
|
530
|
-
if (!shouldInstall) {
|
|
531
|
-
return { files: 0, directories: 0 };
|
|
532
|
-
}
|
|
533
|
-
if (!hasCommands) {
|
|
534
|
-
logger.warn("No commands found in selected templates.");
|
|
535
|
-
return { files: 0, directories: 0 };
|
|
536
|
-
}
|
|
537
|
-
logger.blank();
|
|
538
|
-
logger.info("Installing commands...");
|
|
539
|
-
const commandsResult = await copyCommands(templates, targetDir);
|
|
540
|
-
logger.success(
|
|
541
|
-
`Commands: ${commandsResult.files} files, ${commandsResult.directories} directories`
|
|
542
|
-
);
|
|
543
|
-
return commandsResult;
|
|
544
|
-
}
|
|
545
|
-
async function installAgentsIfNeeded(templates, targetDir, shouldInstall, hasAgents) {
|
|
546
|
-
if (!shouldInstall) {
|
|
547
|
-
return { files: 0, directories: 0 };
|
|
548
|
-
}
|
|
549
|
-
if (!hasAgents) {
|
|
550
|
-
logger.warn("No agents found in selected templates.");
|
|
551
|
-
return { files: 0, directories: 0 };
|
|
552
|
-
}
|
|
553
|
-
logger.blank();
|
|
554
|
-
logger.info("Installing agents...");
|
|
555
|
-
const agentsResult = await copyAgents(templates, targetDir);
|
|
556
|
-
logger.success(
|
|
557
|
-
`Agents: ${agentsResult.files} files, ${agentsResult.directories} directories`
|
|
558
|
-
);
|
|
559
|
-
return agentsResult;
|
|
560
|
-
}
|
|
561
|
-
async function installInstructionsIfNeeded(templates, targetDir, shouldInstall, hasInstructions) {
|
|
562
|
-
if (!shouldInstall) {
|
|
563
|
-
return { files: 0, directories: 0 };
|
|
564
|
-
}
|
|
565
|
-
if (!hasInstructions) {
|
|
566
|
-
logger.warn("No instructions found in selected templates.");
|
|
567
|
-
return { files: 0, directories: 0 };
|
|
654
|
+
async function promptScopeSelection(options) {
|
|
655
|
+
const { providedScope } = options;
|
|
656
|
+
if (providedScope) {
|
|
657
|
+
if (providedScope !== "project" && providedScope !== "user") {
|
|
658
|
+
logger.error(
|
|
659
|
+
`Invalid scope: "${providedScope}". Use "project" or "user".`
|
|
660
|
+
);
|
|
661
|
+
process.exit(1);
|
|
662
|
+
}
|
|
663
|
+
return { scope: providedScope };
|
|
568
664
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
665
|
+
const response = await promptSelect(
|
|
666
|
+
"Select installation scope:",
|
|
667
|
+
[
|
|
668
|
+
{
|
|
669
|
+
title: "Project",
|
|
670
|
+
description: "Install to current project (.claude/)",
|
|
671
|
+
value: "project"
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
title: "User",
|
|
675
|
+
description: "Install to user home (~/.claude/)",
|
|
676
|
+
value: "user"
|
|
677
|
+
}
|
|
678
|
+
]
|
|
574
679
|
);
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const { installSkills, installCommands, installAgents, installInstructions } = flags;
|
|
579
|
-
if (!installSkills && !installCommands && !installAgents && !installInstructions) {
|
|
580
|
-
return { files: 0, directories: 0 };
|
|
581
|
-
}
|
|
582
|
-
const existingClaudeFiles = await checkExistingClaudeFiles(targetDir);
|
|
583
|
-
const shouldProceed = await handleDuplicateFiles(existingClaudeFiles, force);
|
|
584
|
-
if (!shouldProceed) {
|
|
585
|
-
logger.info("Skipping extras installation.");
|
|
586
|
-
return { files: 0, directories: 0 };
|
|
680
|
+
if (!response.value) {
|
|
681
|
+
logger.info("Operation cancelled.");
|
|
682
|
+
process.exit(0);
|
|
587
683
|
}
|
|
588
|
-
|
|
589
|
-
templates,
|
|
590
|
-
targetDir,
|
|
591
|
-
installSkills,
|
|
592
|
-
availability.hasSkills
|
|
593
|
-
);
|
|
594
|
-
const commandsResult = await installCommandsIfNeeded(
|
|
595
|
-
templates,
|
|
596
|
-
targetDir,
|
|
597
|
-
installCommands,
|
|
598
|
-
availability.hasCommands
|
|
599
|
-
);
|
|
600
|
-
const agentsResult = await installAgentsIfNeeded(
|
|
601
|
-
templates,
|
|
602
|
-
targetDir,
|
|
603
|
-
installAgents,
|
|
604
|
-
availability.hasAgents
|
|
605
|
-
);
|
|
606
|
-
const instructionsResult = await installInstructionsIfNeeded(
|
|
607
|
-
templates,
|
|
608
|
-
targetDir,
|
|
609
|
-
installInstructions,
|
|
610
|
-
availability.hasInstructions
|
|
611
|
-
);
|
|
612
|
-
const totalFiles = skillsResult.files + commandsResult.files + agentsResult.files + instructionsResult.files;
|
|
613
|
-
const totalDirectories = skillsResult.directories + commandsResult.directories + agentsResult.directories + instructionsResult.directories;
|
|
614
|
-
return { files: totalFiles, directories: totalDirectories };
|
|
684
|
+
return { scope: response.value };
|
|
615
685
|
}
|
|
616
686
|
|
|
617
687
|
// src/shared/gitignore-manager.ts
|
|
@@ -756,29 +826,38 @@ async function installTemplates(templates, targetDir) {
|
|
|
756
826
|
logger.success(`Total: ${totalFiles} files, ${totalDirectories} directories`);
|
|
757
827
|
return { files: totalFiles, directories: totalDirectories };
|
|
758
828
|
}
|
|
759
|
-
async function promptForExtrasInstallation(options, hasSkills, hasCommands, hasAgents, hasInstructions) {
|
|
829
|
+
async function promptForExtrasInstallation(options, hasSkills, hasCommands, hasAgents, hasInstructions, hasScripts) {
|
|
760
830
|
return await promptExtrasSelection({
|
|
761
831
|
skills: options.skills,
|
|
762
832
|
commands: options.commands,
|
|
763
833
|
agents: options.agents,
|
|
764
834
|
instructions: options.instructions,
|
|
835
|
+
scripts: options.scripts,
|
|
765
836
|
hasSkills,
|
|
766
837
|
hasCommands,
|
|
767
838
|
hasAgents,
|
|
768
|
-
hasInstructions
|
|
839
|
+
hasInstructions,
|
|
840
|
+
hasScripts
|
|
769
841
|
});
|
|
770
842
|
}
|
|
771
|
-
function showInstallationSummary(templates, flags, hasSkills, hasCommands, hasAgents, hasInstructions) {
|
|
772
|
-
const {
|
|
773
|
-
|
|
843
|
+
function showInstallationSummary(templates, flags, hasSkills, hasCommands, hasAgents, hasInstructions, hasScripts, scope) {
|
|
844
|
+
const {
|
|
845
|
+
installSkills,
|
|
846
|
+
installCommands,
|
|
847
|
+
installAgents,
|
|
848
|
+
installInstructions,
|
|
849
|
+
installScripts
|
|
850
|
+
} = flags;
|
|
851
|
+
const hasExtrasInstalled = installSkills && hasSkills || installCommands && hasCommands || installAgents && hasAgents || installInstructions && hasInstructions || installScripts && hasScripts;
|
|
774
852
|
if (templates.length === 0 && !hasExtrasInstalled) {
|
|
775
853
|
logger.blank();
|
|
776
854
|
logger.info("No templates or extras installed.");
|
|
777
855
|
logger.blank();
|
|
778
856
|
return;
|
|
779
857
|
}
|
|
858
|
+
const scopeLabel = scope === "user" ? "(user scope \u2192 ~/.claude/)" : "(project scope)";
|
|
780
859
|
logger.blank();
|
|
781
|
-
logger.success(
|
|
860
|
+
logger.success(`Claude Code documentation installed! ${scopeLabel}`);
|
|
782
861
|
if (templates.length > 0) {
|
|
783
862
|
logger.blank();
|
|
784
863
|
logger.info("Installed templates:");
|
|
@@ -799,6 +878,9 @@ function showInstallationSummary(templates, flags, hasSkills, hasCommands, hasAg
|
|
|
799
878
|
if (installInstructions && hasInstructions) {
|
|
800
879
|
logger.step("Instructions \u2192 .claude/instructions/");
|
|
801
880
|
}
|
|
881
|
+
if (installScripts && hasScripts) {
|
|
882
|
+
logger.step("Scripts \u2192 .claude/scripts/");
|
|
883
|
+
}
|
|
802
884
|
}
|
|
803
885
|
logger.blank();
|
|
804
886
|
logger.info("Next steps:");
|
|
@@ -812,48 +894,65 @@ function showInstallationSummary(templates, flags, hasSkills, hasCommands, hasAg
|
|
|
812
894
|
logger.blank();
|
|
813
895
|
}
|
|
814
896
|
var init = async (options) => {
|
|
815
|
-
const
|
|
816
|
-
await validateTargetDirectory(
|
|
897
|
+
const projectDir = options.cwd || process.cwd();
|
|
898
|
+
await validateTargetDirectory(projectDir);
|
|
899
|
+
const { scope } = await promptScopeSelection({
|
|
900
|
+
providedScope: options.scope
|
|
901
|
+
});
|
|
902
|
+
const targetDir = scope === "user" ? os.homedir() : projectDir;
|
|
903
|
+
const isUserScope = scope === "user";
|
|
904
|
+
if (isUserScope) {
|
|
905
|
+
logger.blank();
|
|
906
|
+
logger.info(`Installing to ~/.claude/ (user scope)`);
|
|
907
|
+
}
|
|
817
908
|
const availableTemplates = await listAvailableTemplates();
|
|
818
909
|
if (availableTemplates.length === 0) {
|
|
819
910
|
logger.error("No templates found. Package may be corrupted.");
|
|
820
911
|
process.exit(1);
|
|
821
912
|
}
|
|
822
|
-
|
|
823
|
-
if (
|
|
824
|
-
await
|
|
825
|
-
|
|
913
|
+
let templates = [];
|
|
914
|
+
if (!isUserScope) {
|
|
915
|
+
templates = await selectTemplates(options, availableTemplates);
|
|
916
|
+
if (templates.length > 0) {
|
|
917
|
+
await confirmOverwriteIfNeeded(targetDir, options.force ?? false);
|
|
918
|
+
await installTemplates(templates, targetDir);
|
|
919
|
+
}
|
|
826
920
|
}
|
|
827
921
|
const templatesToCheck = templates.length > 0 ? templates : availableTemplates;
|
|
828
|
-
const { hasSkills, hasCommands, hasAgents, hasInstructions } = await checkAllExtrasExist(templatesToCheck);
|
|
922
|
+
const { hasSkills, hasCommands, hasAgents, hasInstructions, hasScripts } = await checkAllExtrasExist(templatesToCheck);
|
|
829
923
|
const flags = await promptForExtrasInstallation(
|
|
830
924
|
options,
|
|
831
925
|
hasSkills,
|
|
832
926
|
hasCommands,
|
|
833
927
|
hasAgents,
|
|
834
|
-
hasInstructions
|
|
835
|
-
|
|
836
|
-
await installExtras(
|
|
837
|
-
templatesToCheck,
|
|
838
|
-
targetDir,
|
|
839
|
-
flags,
|
|
840
|
-
{ hasSkills, hasCommands, hasAgents, hasInstructions },
|
|
841
|
-
options.force ?? false
|
|
928
|
+
hasInstructions,
|
|
929
|
+
hasScripts
|
|
842
930
|
);
|
|
931
|
+
await installExtras(templatesToCheck, targetDir, flags, {
|
|
932
|
+
hasSkills,
|
|
933
|
+
hasCommands,
|
|
934
|
+
hasAgents,
|
|
935
|
+
hasInstructions,
|
|
936
|
+
hasScripts
|
|
937
|
+
});
|
|
843
938
|
showInstallationSummary(
|
|
844
939
|
templates,
|
|
845
940
|
flags,
|
|
846
941
|
hasSkills,
|
|
847
942
|
hasCommands,
|
|
848
943
|
hasAgents,
|
|
849
|
-
hasInstructions
|
|
944
|
+
hasInstructions,
|
|
945
|
+
hasScripts,
|
|
946
|
+
scope
|
|
850
947
|
);
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
948
|
+
if (!isUserScope) {
|
|
949
|
+
try {
|
|
950
|
+
await updateGitignore(targetDir);
|
|
951
|
+
} catch (error) {
|
|
952
|
+
logger.warn(
|
|
953
|
+
`Failed to update .gitignore: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
954
|
+
);
|
|
955
|
+
}
|
|
857
956
|
}
|
|
858
957
|
};
|
|
859
958
|
|
|
@@ -863,7 +962,7 @@ program.name("claude-code").description("Claude Code documentation installer for
|
|
|
863
962
|
program.option(
|
|
864
963
|
"-t, --template <names>",
|
|
865
964
|
"template names (comma-separated: tanstack-start,hono)"
|
|
866
|
-
).option("-f, --force", "overwrite existing files without prompting").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").option("-s, --skills", "install skills to .claude/skills/").option("-c, --commands", "install commands to .claude/commands/").option("-a, --agents", "install agents to .claude/agents/").action(async (options) => {
|
|
965
|
+
).option("-f, --force", "overwrite existing files without prompting").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").option("-s, --skills", "install skills to .claude/skills/").option("-c, --commands", "install commands to .claude/commands/").option("-a, --agents", "install agents to .claude/agents/").option("--scope <scope>", "installation scope (project|user)").action(async (options) => {
|
|
867
966
|
banner();
|
|
868
967
|
if (options.list) {
|
|
869
968
|
const templates = await listAvailableTemplates();
|
|
@@ -876,6 +975,7 @@ program.option(
|
|
|
876
975
|
templates: options.template?.split(",").map((t) => t.trim()),
|
|
877
976
|
force: options.force,
|
|
878
977
|
cwd: options.cwd,
|
|
978
|
+
scope: options.scope,
|
|
879
979
|
skills: options.skills,
|
|
880
980
|
commands: options.commands,
|
|
881
981
|
agents: options.agents
|
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# build-run.sh - Package manager 감지 후 build 실행
|
|
3
|
+
# Usage: ./build-run.sh
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PM_DIR="$SCRIPT_DIR/../pm"
|
|
9
|
+
|
|
10
|
+
# Package manager 감지
|
|
11
|
+
PM=$("$PM_DIR/pm-detect.sh")
|
|
12
|
+
|
|
13
|
+
echo "=== Building with $PM ==="
|
|
14
|
+
|
|
15
|
+
# Build 실행
|
|
16
|
+
case "$PM" in
|
|
17
|
+
bun)
|
|
18
|
+
bun run build
|
|
19
|
+
;;
|
|
20
|
+
pnpm)
|
|
21
|
+
pnpm run build
|
|
22
|
+
;;
|
|
23
|
+
yarn)
|
|
24
|
+
yarn build
|
|
25
|
+
;;
|
|
26
|
+
npm)
|
|
27
|
+
npm run build
|
|
28
|
+
;;
|
|
29
|
+
*)
|
|
30
|
+
echo "Error: Unknown package manager: $PM" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
;;
|
|
33
|
+
esac
|
|
34
|
+
|
|
35
|
+
echo ""
|
|
36
|
+
echo "✓ Build completed successfully"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# deploy-check.sh - 배포 전 전체 검증 (tsc + eslint + build)
|
|
3
|
+
# Usage: ./deploy-check.sh
|
|
4
|
+
# 검사 실패 시 조기 종료
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
LINT_DIR="$SCRIPT_DIR/../lint"
|
|
10
|
+
|
|
11
|
+
echo "=========================================="
|
|
12
|
+
echo " Pre-Deploy Verification"
|
|
13
|
+
echo "=========================================="
|
|
14
|
+
|
|
15
|
+
# 스크립트 존재 검증
|
|
16
|
+
if [ ! -x "$LINT_DIR/lint-check.sh" ]; then
|
|
17
|
+
echo "Error: lint-check.sh not found or not executable" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
if [ ! -x "$SCRIPT_DIR/build-run.sh" ]; then
|
|
21
|
+
echo "Error: build-run.sh not found or not executable" >&2
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Step 1: Lint 검사 (tsc + eslint 병렬)
|
|
26
|
+
echo ""
|
|
27
|
+
echo "[1/2] Running lint checks..."
|
|
28
|
+
"$LINT_DIR/lint-check.sh"
|
|
29
|
+
|
|
30
|
+
# Step 2: Build
|
|
31
|
+
echo ""
|
|
32
|
+
echo "[2/2] Running build..."
|
|
33
|
+
"$SCRIPT_DIR/build-run.sh"
|
|
34
|
+
|
|
35
|
+
echo ""
|
|
36
|
+
echo "=========================================="
|
|
37
|
+
echo " ✓ All checks passed - Ready to deploy"
|
|
38
|
+
echo "=========================================="
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# git-all.sh - 모든 변경사항 add + commit + push (단순 케이스)
|
|
3
|
+
# Usage: ./git-all.sh "커밋 메시지"
|
|
4
|
+
# WARNING: git add -A 사용 - 민감 파일(.env 등) 포함 주의
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Git 저장소 확인
|
|
9
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
10
|
+
echo "Error: Not a git repository"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if [ -z "$1" ]; then
|
|
15
|
+
echo "Usage: $0 \"commit message\""
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
COMMIT_MSG="$1"
|
|
20
|
+
|
|
21
|
+
# 빈 메시지 체크
|
|
22
|
+
if [ -z "$(echo "$COMMIT_MSG" | xargs)" ]; then
|
|
23
|
+
echo "Error: Commit message cannot be empty" >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# 변경사항 확인
|
|
28
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
29
|
+
# 민감 파일 검증
|
|
30
|
+
SENSITIVE=$(git status --porcelain | grep -E '^\?\?.*\.(env|pem|key|secret|credentials)' || true)
|
|
31
|
+
if [ -n "$SENSITIVE" ]; then
|
|
32
|
+
echo "ERROR: Sensitive files detected:" >&2
|
|
33
|
+
echo "$SENSITIVE" >&2
|
|
34
|
+
echo "" >&2
|
|
35
|
+
echo "Please use git-commit.sh with specific files instead" >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
echo "=== Adding all changes ==="
|
|
40
|
+
git add -A
|
|
41
|
+
|
|
42
|
+
echo "=== Committing ==="
|
|
43
|
+
git commit -m "$COMMIT_MSG"
|
|
44
|
+
|
|
45
|
+
echo "=== Pushing ==="
|
|
46
|
+
BRANCH=$(git branch --show-current)
|
|
47
|
+
if ! git rev-parse --abbrev-ref "@{upstream}" >/dev/null 2>&1; then
|
|
48
|
+
git push -u origin "$BRANCH"
|
|
49
|
+
else
|
|
50
|
+
git push
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
echo "Done: All changes committed and pushed"
|
|
54
|
+
else
|
|
55
|
+
echo "No changes to commit"
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# git-clean-check.sh - Working directory clean 여부 확인
|
|
3
|
+
# Usage: ./git-clean-check.sh [--quiet]
|
|
4
|
+
# Exit 0: clean, Exit 1: dirty
|
|
5
|
+
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
# Git 저장소 확인
|
|
9
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
10
|
+
echo "Error: Not a git repository"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
QUIET=false
|
|
15
|
+
if [ "${1:-}" = "--quiet" ]; then
|
|
16
|
+
QUIET=true
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
20
|
+
if [ "$QUIET" = false ]; then
|
|
21
|
+
echo "Working directory is DIRTY"
|
|
22
|
+
echo ""
|
|
23
|
+
git status --short
|
|
24
|
+
fi
|
|
25
|
+
exit 1
|
|
26
|
+
else
|
|
27
|
+
if [ "$QUIET" = false ]; then
|
|
28
|
+
echo "Working directory is CLEAN"
|
|
29
|
+
fi
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# git-commit.sh - 커밋 실행
|
|
3
|
+
# Usage: ./git-commit.sh "커밋 메시지" [files...]
|
|
4
|
+
# files 미지정 시 staged files만 커밋
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Git 저장소 확인
|
|
9
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
10
|
+
echo "Error: Not a git repository"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if [ -z "$1" ]; then
|
|
15
|
+
echo "Usage: $0 \"commit message\" [files...]"
|
|
16
|
+
echo " files: 커밋할 파일들 (미지정 시 staged files만)"
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
COMMIT_MSG="$1"
|
|
21
|
+
|
|
22
|
+
# 빈 메시지 체크
|
|
23
|
+
if [ -z "$(echo "$COMMIT_MSG" | xargs)" ]; then
|
|
24
|
+
echo "Error: Commit message cannot be empty" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
shift
|
|
29
|
+
FILES_PROVIDED=$#
|
|
30
|
+
|
|
31
|
+
# 파일 지정된 경우 add
|
|
32
|
+
if [ $FILES_PROVIDED -gt 0 ]; then
|
|
33
|
+
git add "$@"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# staged 파일 확인
|
|
37
|
+
if git diff --cached --quiet; then
|
|
38
|
+
if [ $FILES_PROVIDED -eq 0 ]; then
|
|
39
|
+
echo "Error: No staged changes to commit"
|
|
40
|
+
echo "Tip: Stage files with 'git add' or pass files as arguments"
|
|
41
|
+
else
|
|
42
|
+
echo "Error: No changes in specified files"
|
|
43
|
+
echo "Tip: Check if files exist and have modifications"
|
|
44
|
+
fi
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# 커밋
|
|
49
|
+
git commit -m "$COMMIT_MSG"
|
|
50
|
+
|
|
51
|
+
echo "Committed successfully"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# git-info.sh - Git 상태 및 diff 요약 출력
|
|
3
|
+
# 에이전트가 분석할 정보를 빠르게 수집
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# Git 저장소 확인
|
|
8
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
9
|
+
echo "Error: Not a git repository"
|
|
10
|
+
exit 1
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
echo "=== Git Status ==="
|
|
14
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
15
|
+
git status --short
|
|
16
|
+
else
|
|
17
|
+
echo "(clean)"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
echo ""
|
|
21
|
+
echo "=== Changed Files ==="
|
|
22
|
+
git diff --stat
|
|
23
|
+
|
|
24
|
+
echo ""
|
|
25
|
+
echo "=== Staged Files ==="
|
|
26
|
+
git diff --cached --stat
|
|
27
|
+
|
|
28
|
+
echo ""
|
|
29
|
+
echo "=== Recent Commits (5) ==="
|
|
30
|
+
git log --oneline -5
|
|
31
|
+
|
|
32
|
+
echo ""
|
|
33
|
+
echo "=== Current Branch ==="
|
|
34
|
+
git branch --show-current
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# git-push.sh - 안전한 푸시 (상태 확인 후)
|
|
3
|
+
# Usage: ./git-push.sh [--force]
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# Git 저장소 확인
|
|
8
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
9
|
+
echo "Error: Not a git repository"
|
|
10
|
+
exit 1
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
# 현재 브랜치
|
|
14
|
+
BRANCH=$(git branch --show-current)
|
|
15
|
+
|
|
16
|
+
if [ -z "$BRANCH" ]; then
|
|
17
|
+
echo "Error: Not on any branch (detached HEAD)"
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Force push 처리
|
|
22
|
+
USE_FORCE=false
|
|
23
|
+
if [ "${1:-}" = "--force" ]; then
|
|
24
|
+
# main/master 보호
|
|
25
|
+
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
|
|
26
|
+
echo "ERROR: Cannot force push to $BRANCH"
|
|
27
|
+
echo "This operation is too dangerous on protected branches"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
USE_FORCE=true
|
|
31
|
+
echo "Warning: Force push enabled (with lease) to $BRANCH"
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# upstream 확인 및 push
|
|
35
|
+
if ! git rev-parse --abbrev-ref "@{upstream}" >/dev/null 2>&1; then
|
|
36
|
+
echo "Setting upstream for branch: $BRANCH"
|
|
37
|
+
if [ "$USE_FORCE" = true ]; then
|
|
38
|
+
git push -u origin "$BRANCH" --force-with-lease
|
|
39
|
+
else
|
|
40
|
+
git push -u origin "$BRANCH"
|
|
41
|
+
fi
|
|
42
|
+
else
|
|
43
|
+
if [ "$USE_FORCE" = true ]; then
|
|
44
|
+
git push --force-with-lease
|
|
45
|
+
else
|
|
46
|
+
git push
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo "Pushed to origin/$BRANCH"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lint-check.sh - tsc + eslint 병렬 실행
|
|
3
|
+
# Usage: ./lint-check.sh
|
|
4
|
+
# 두 검사를 병렬로 실행하고 결과를 수집
|
|
5
|
+
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
# 임시 파일
|
|
9
|
+
TSC_OUT=$(mktemp)
|
|
10
|
+
ESLINT_OUT=$(mktemp)
|
|
11
|
+
TSC_EXIT=0
|
|
12
|
+
ESLINT_EXIT=0
|
|
13
|
+
|
|
14
|
+
# Cleanup (ERR, INT, TERM도 처리)
|
|
15
|
+
trap "rm -f $TSC_OUT $ESLINT_OUT" EXIT ERR INT TERM
|
|
16
|
+
|
|
17
|
+
echo "=== Running TypeScript + ESLint in parallel ==="
|
|
18
|
+
|
|
19
|
+
# 병렬 실행
|
|
20
|
+
(npx tsc --noEmit > "$TSC_OUT" 2>&1) &
|
|
21
|
+
TSC_PID=$!
|
|
22
|
+
|
|
23
|
+
(npx eslint . > "$ESLINT_OUT" 2>&1) &
|
|
24
|
+
ESLINT_PID=$!
|
|
25
|
+
|
|
26
|
+
# 대기
|
|
27
|
+
wait $TSC_PID || TSC_EXIT=$?
|
|
28
|
+
wait $ESLINT_PID || ESLINT_EXIT=$?
|
|
29
|
+
|
|
30
|
+
# 결과 출력
|
|
31
|
+
echo ""
|
|
32
|
+
echo "=== TypeScript Results ==="
|
|
33
|
+
if [ $TSC_EXIT -eq 0 ]; then
|
|
34
|
+
echo "✓ No type errors"
|
|
35
|
+
else
|
|
36
|
+
cat "$TSC_OUT"
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
echo ""
|
|
40
|
+
echo "=== ESLint Results ==="
|
|
41
|
+
if [ $ESLINT_EXIT -eq 0 ]; then
|
|
42
|
+
echo "✓ No lint errors"
|
|
43
|
+
else
|
|
44
|
+
cat "$ESLINT_OUT"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
echo ""
|
|
48
|
+
echo "=== Summary ==="
|
|
49
|
+
if [ $TSC_EXIT -eq 0 ] && [ $ESLINT_EXIT -eq 0 ]; then
|
|
50
|
+
echo "✓ All checks passed"
|
|
51
|
+
exit 0
|
|
52
|
+
else
|
|
53
|
+
[ $TSC_EXIT -ne 0 ] && echo "✗ TypeScript: FAILED"
|
|
54
|
+
[ $ESLINT_EXIT -ne 0 ] && echo "✗ ESLint: FAILED"
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lint-file.sh - 특정 파일/디렉토리만 검사
|
|
3
|
+
# Usage: ./lint-file.sh <file|directory> [file2...]
|
|
4
|
+
# Example: ./lint-file.sh src/utils/api.ts
|
|
5
|
+
# ./lint-file.sh src/components/
|
|
6
|
+
|
|
7
|
+
set -uo pipefail
|
|
8
|
+
|
|
9
|
+
if [ $# -eq 0 ]; then
|
|
10
|
+
echo "Usage: $0 <file|directory> [file2...]"
|
|
11
|
+
echo "Example: $0 src/utils/api.ts"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
TSC_EXIT=0
|
|
16
|
+
ESLINT_EXIT=0
|
|
17
|
+
|
|
18
|
+
echo "=== Checking: $@ ==="
|
|
19
|
+
|
|
20
|
+
# TypeScript 검사 (전체 프로젝트 - tsc는 개별 파일 검사 미지원)
|
|
21
|
+
echo ""
|
|
22
|
+
echo "=== TypeScript ==="
|
|
23
|
+
echo "(Note: tsc checks entire project)"
|
|
24
|
+
npx tsc --noEmit || TSC_EXIT=$?
|
|
25
|
+
|
|
26
|
+
# ESLint 검사
|
|
27
|
+
echo ""
|
|
28
|
+
echo "=== ESLint ==="
|
|
29
|
+
npx eslint "$@" || ESLINT_EXIT=$?
|
|
30
|
+
|
|
31
|
+
# 결과
|
|
32
|
+
echo ""
|
|
33
|
+
echo "=== Summary ==="
|
|
34
|
+
if [ $TSC_EXIT -eq 0 ] && [ $ESLINT_EXIT -eq 0 ]; then
|
|
35
|
+
echo "✓ All checks passed"
|
|
36
|
+
exit 0
|
|
37
|
+
else
|
|
38
|
+
[ $TSC_EXIT -ne 0 ] && echo "✗ TypeScript: FAILED"
|
|
39
|
+
[ $ESLINT_EXIT -ne 0 ] && echo "✗ ESLint: FAILED"
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pm-detect.sh - Package manager 자동 감지
|
|
3
|
+
# Usage: ./pm-detect.sh
|
|
4
|
+
# Output: npm | yarn | pnpm | bun
|
|
5
|
+
# Exit 1 if no lock file found
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# Lock 파일 기반 감지
|
|
10
|
+
if [ -f "bun.lockb" ]; then
|
|
11
|
+
echo "bun"
|
|
12
|
+
elif [ -f "pnpm-lock.yaml" ]; then
|
|
13
|
+
echo "pnpm"
|
|
14
|
+
elif [ -f "yarn.lock" ]; then
|
|
15
|
+
echo "yarn"
|
|
16
|
+
elif [ -f "package-lock.json" ]; then
|
|
17
|
+
echo "npm"
|
|
18
|
+
elif [ -f "package.json" ]; then
|
|
19
|
+
# Lock 파일 없으면 기본 npm
|
|
20
|
+
echo "Warning: No lock file found, defaulting to npm" >&2
|
|
21
|
+
echo "npm"
|
|
22
|
+
else
|
|
23
|
+
echo "Error: No package.json found" >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pm-run.sh - 감지된 package manager로 스크립트 실행
|
|
3
|
+
# Usage: ./pm-run.sh <script> [args...]
|
|
4
|
+
# Example: ./pm-run.sh build
|
|
5
|
+
# ./pm-run.sh test --watch
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
|
|
11
|
+
if [ $# -eq 0 ] || [ -z "${1:-}" ]; then
|
|
12
|
+
echo "Usage: $0 <script> [args...]"
|
|
13
|
+
echo "Example: $0 build"
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
SCRIPT="$1"
|
|
18
|
+
shift
|
|
19
|
+
|
|
20
|
+
# Package manager 감지
|
|
21
|
+
PM=$("$SCRIPT_DIR/pm-detect.sh")
|
|
22
|
+
|
|
23
|
+
# 실행
|
|
24
|
+
case "$PM" in
|
|
25
|
+
bun)
|
|
26
|
+
bun run "$SCRIPT" "$@"
|
|
27
|
+
;;
|
|
28
|
+
pnpm)
|
|
29
|
+
pnpm run "$SCRIPT" "$@"
|
|
30
|
+
;;
|
|
31
|
+
yarn)
|
|
32
|
+
yarn "$SCRIPT" "$@"
|
|
33
|
+
;;
|
|
34
|
+
npm)
|
|
35
|
+
npm run "$SCRIPT" "$@"
|
|
36
|
+
;;
|
|
37
|
+
*)
|
|
38
|
+
echo "Error: Unknown package manager: $PM" >&2
|
|
39
|
+
exit 1
|
|
40
|
+
;;
|
|
41
|
+
esac
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# version-bump.sh - 버전 계산
|
|
3
|
+
# Usage: ./version-bump.sh <current_version> <bump_type>
|
|
4
|
+
# bump_type: +1 (patch), +minor, +major, or direct version (x.x.x)
|
|
5
|
+
# Example: ./version-bump.sh 1.2.3 +1 → 1.2.4
|
|
6
|
+
# ./version-bump.sh 1.2.3 +minor → 1.3.0
|
|
7
|
+
# ./version-bump.sh 1.2.3 +major → 2.0.0
|
|
8
|
+
# ./version-bump.sh 1.2.3 2.0.0 → 2.0.0
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
if [ $# -lt 2 ]; then
|
|
13
|
+
echo "Usage: $0 <current_version> <bump_type>"
|
|
14
|
+
echo "bump_type: +1, +minor, +major, or x.x.x"
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
CURRENT="$1"
|
|
19
|
+
BUMP="$2"
|
|
20
|
+
|
|
21
|
+
# 버전 형식 먼저 검증
|
|
22
|
+
if ! [[ "$CURRENT" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
23
|
+
echo "Error: Invalid version format: $CURRENT" >&2
|
|
24
|
+
echo "Expected: x.x.x (e.g., 1.2.3)" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# 버전 파싱
|
|
29
|
+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
|
|
30
|
+
|
|
31
|
+
# 새 버전 계산
|
|
32
|
+
case "$BUMP" in
|
|
33
|
+
+1|+patch)
|
|
34
|
+
NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))"
|
|
35
|
+
;;
|
|
36
|
+
+minor)
|
|
37
|
+
NEW_VERSION="$MAJOR.$((MINOR + 1)).0"
|
|
38
|
+
;;
|
|
39
|
+
+major)
|
|
40
|
+
NEW_VERSION="$((MAJOR + 1)).0.0"
|
|
41
|
+
;;
|
|
42
|
+
*)
|
|
43
|
+
# 직접 버전 지정 검증
|
|
44
|
+
if [[ "$BUMP" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
45
|
+
NEW_VERSION="$BUMP"
|
|
46
|
+
else
|
|
47
|
+
echo "Error: Invalid bump type: $BUMP"
|
|
48
|
+
echo "Use: +1, +minor, +major, or x.x.x"
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
|
|
54
|
+
echo "$NEW_VERSION"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# version-find.sh - 버전 파일 탐색
|
|
3
|
+
# Usage: ./version-find.sh
|
|
4
|
+
# package.json과 .version() 코드 파일을 병렬로 탐색
|
|
5
|
+
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
echo "=== Searching version files ==="
|
|
9
|
+
|
|
10
|
+
# 임시 파일
|
|
11
|
+
PKG_OUT=$(mktemp)
|
|
12
|
+
CODE_OUT=$(mktemp)
|
|
13
|
+
|
|
14
|
+
trap "rm -f $PKG_OUT $CODE_OUT" EXIT ERR INT TERM
|
|
15
|
+
|
|
16
|
+
# 병렬 탐색 (fd 우선, 없으면 find)
|
|
17
|
+
if command -v fd &>/dev/null; then
|
|
18
|
+
fd -t f 'package.json' -E node_modules > "$PKG_OUT" 2>&1 &
|
|
19
|
+
else
|
|
20
|
+
find . -name 'package.json' -not -path '*/node_modules/*' > "$PKG_OUT" 2>&1 &
|
|
21
|
+
fi
|
|
22
|
+
PID1=$!
|
|
23
|
+
|
|
24
|
+
(rg "\.version\(['\"]" --type ts --type js -l 2>/dev/null || true) > "$CODE_OUT" &
|
|
25
|
+
PID2=$!
|
|
26
|
+
|
|
27
|
+
wait $PID1
|
|
28
|
+
wait $PID2
|
|
29
|
+
|
|
30
|
+
# 결과 출력
|
|
31
|
+
echo ""
|
|
32
|
+
echo "=== package.json files ==="
|
|
33
|
+
if [ -s "$PKG_OUT" ]; then
|
|
34
|
+
cat "$PKG_OUT"
|
|
35
|
+
else
|
|
36
|
+
echo "(none found)"
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
echo ""
|
|
40
|
+
echo "=== Code files with .version() ==="
|
|
41
|
+
if [ -s "$CODE_OUT" ]; then
|
|
42
|
+
cat "$CODE_OUT"
|
|
43
|
+
else
|
|
44
|
+
echo "(none found)"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
echo ""
|
|
48
|
+
echo "=== All version files ==="
|
|
49
|
+
cat "$PKG_OUT" "$CODE_OUT" | sort -u
|