code-brick 0.1.0 → 0.2.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 +85 -0
- package/dist/index.js +226 -6
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -312,6 +312,77 @@ import 'package:go_router/go_router.dart';
|
|
|
312
312
|
|
|
313
313
|
The command auto-detects the project name from `pubspec.yaml`, `package.json`, or `pyproject.toml`.
|
|
314
314
|
|
|
315
|
+
### `brick export <name|index>`
|
|
316
|
+
|
|
317
|
+
Export a template as a shareable `.brick` file. Perfect for sharing templates with teammates or between machines.
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
# Export to current directory
|
|
321
|
+
brick export nestjs-auth
|
|
322
|
+
|
|
323
|
+
# By index
|
|
324
|
+
brick export 0
|
|
325
|
+
|
|
326
|
+
# Specify output path
|
|
327
|
+
brick export 0 --output ~/Desktop/my-auth.brick
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Options:**
|
|
331
|
+
|
|
332
|
+
- `-o, --output <path>` — Output file path (default: `./<name>.brick`)
|
|
333
|
+
|
|
334
|
+
**Example output:**
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
┌ 🧱 Exporting template
|
|
338
|
+
│ ● Template: nestjs-auth
|
|
339
|
+
│ ● Files: 5 files
|
|
340
|
+
│ ● Output: /Users/you/nestjs-auth.brick
|
|
341
|
+
│
|
|
342
|
+
│ ◇ Export complete!
|
|
343
|
+
│ Size: 12.34 KB
|
|
344
|
+
│
|
|
345
|
+
│ Share this file and import with:
|
|
346
|
+
│ brick import nestjs-auth.brick
|
|
347
|
+
└
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### `brick import <file>`
|
|
351
|
+
|
|
352
|
+
Import a template from a `.brick` file.
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
# Import from .brick file
|
|
356
|
+
brick import nestjs-auth.brick
|
|
357
|
+
|
|
358
|
+
# Import with custom name
|
|
359
|
+
brick import nestjs-auth.brick --name my-auth-v2
|
|
360
|
+
|
|
361
|
+
# Force overwrite existing template
|
|
362
|
+
brick import nestjs-auth.brick --force
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**Options:**
|
|
366
|
+
|
|
367
|
+
- `-n, --name <name>` — Custom name for the imported template
|
|
368
|
+
- `-f, --force` — Overwrite existing template without prompting
|
|
369
|
+
|
|
370
|
+
**Example output:**
|
|
371
|
+
|
|
372
|
+
```
|
|
373
|
+
┌ 🧱 Importing template
|
|
374
|
+
│ ● File: /Users/you/nestjs-auth.brick
|
|
375
|
+
│ ● Template: nestjs-auth
|
|
376
|
+
│ ● Description: JWT authentication module
|
|
377
|
+
│ ● Files: 5 files
|
|
378
|
+
│
|
|
379
|
+
│ ◇ Import complete!
|
|
380
|
+
│
|
|
381
|
+
│ View structure: brick tree nestjs-auth
|
|
382
|
+
│ Apply template: brick apply nestjs-auth
|
|
383
|
+
└
|
|
384
|
+
```
|
|
385
|
+
|
|
315
386
|
## Smart Ignore System
|
|
316
387
|
|
|
317
388
|
Brick **automatically ignores** common dependency directories, build outputs, and generated files across all frameworks. This keeps your templates clean and portable.
|
|
@@ -449,6 +520,20 @@ brick clean 2 # Remove local imports
|
|
|
449
520
|
brick delete 1 --force
|
|
450
521
|
```
|
|
451
522
|
|
|
523
|
+
### Share Templates with Your Team
|
|
524
|
+
|
|
525
|
+
```bash
|
|
526
|
+
# Export a template to share
|
|
527
|
+
brick export nestjs-auth
|
|
528
|
+
# Creates: nestjs-auth.brick (shareable file)
|
|
529
|
+
|
|
530
|
+
# Send the .brick file to your teammate, then they run:
|
|
531
|
+
brick import nestjs-auth.brick
|
|
532
|
+
|
|
533
|
+
# Or import with a custom name
|
|
534
|
+
brick import nestjs-auth.brick --name company-auth
|
|
535
|
+
```
|
|
536
|
+
|
|
452
537
|
## Contributing
|
|
453
538
|
|
|
454
539
|
Contributions are welcome! Please open an issue or submit a pull request.
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import pc15 from "picocolors";
|
|
6
6
|
|
|
7
7
|
// src/commands/init.ts
|
|
8
8
|
import * as p from "@clack/prompts";
|
|
@@ -518,16 +518,16 @@ async function saveCommand(name, sourcePath, options) {
|
|
|
518
518
|
}
|
|
519
519
|
let files;
|
|
520
520
|
if (options.include) {
|
|
521
|
-
const patterns = options.include.split(",").map((
|
|
521
|
+
const patterns = options.include.split(",").map((p13) => p13.trim());
|
|
522
522
|
files = await fg(patterns, {
|
|
523
523
|
cwd: resolvedPath,
|
|
524
524
|
dot: false,
|
|
525
|
-
ignore: options.exclude?.split(",").map((
|
|
525
|
+
ignore: options.exclude?.split(",").map((p13) => p13.trim()) || []
|
|
526
526
|
});
|
|
527
527
|
} else {
|
|
528
528
|
files = await getFilesRecursive(resolvedPath);
|
|
529
529
|
if (options.exclude) {
|
|
530
|
-
const excludePatterns = options.exclude.split(",").map((
|
|
530
|
+
const excludePatterns = options.exclude.split(",").map((p13) => p13.trim());
|
|
531
531
|
const excluded = await fg(excludePatterns, {
|
|
532
532
|
cwd: resolvedPath,
|
|
533
533
|
dot: false
|
|
@@ -1811,11 +1811,229 @@ async function cleanCommand(nameOrIndex, options) {
|
|
|
1811
1811
|
p11.outro("Template cleaned successfully! \u2713");
|
|
1812
1812
|
}
|
|
1813
1813
|
|
|
1814
|
+
// src/commands/export.ts
|
|
1815
|
+
import fs8 from "fs-extra";
|
|
1816
|
+
import path8 from "path";
|
|
1817
|
+
import archiver from "archiver";
|
|
1818
|
+
import pc13 from "picocolors";
|
|
1819
|
+
async function exportCommand(templateNameOrIndex, options) {
|
|
1820
|
+
if (!await isInitialized()) {
|
|
1821
|
+
console.log(pc13.red("CodeBrick is not initialized. Run: brick init"));
|
|
1822
|
+
process.exit(1);
|
|
1823
|
+
}
|
|
1824
|
+
const templateName = await resolveTemplateName(templateNameOrIndex);
|
|
1825
|
+
if (!templateName) {
|
|
1826
|
+
console.log(pc13.red(`Template '${templateNameOrIndex}' not found`));
|
|
1827
|
+
process.exit(1);
|
|
1828
|
+
}
|
|
1829
|
+
const template = await getTemplate(templateName);
|
|
1830
|
+
if (!template) {
|
|
1831
|
+
console.log(pc13.red(`Template '${templateName}' not found`));
|
|
1832
|
+
process.exit(1);
|
|
1833
|
+
}
|
|
1834
|
+
if (template.type !== "local") {
|
|
1835
|
+
console.log(
|
|
1836
|
+
pc13.red("Only local templates can be exported. Remote templates must be pulled first.")
|
|
1837
|
+
);
|
|
1838
|
+
process.exit(1);
|
|
1839
|
+
}
|
|
1840
|
+
const templatePath = getTemplatePath(templateName);
|
|
1841
|
+
if (!await fs8.pathExists(templatePath)) {
|
|
1842
|
+
console.log(pc13.red(`Template files not found at: ${templatePath}`));
|
|
1843
|
+
process.exit(1);
|
|
1844
|
+
}
|
|
1845
|
+
const metadata = await loadTemplateMetadata(templateName);
|
|
1846
|
+
if (!metadata) {
|
|
1847
|
+
console.log(pc13.red("Template metadata not found"));
|
|
1848
|
+
process.exit(1);
|
|
1849
|
+
}
|
|
1850
|
+
const outputFileName = `${templateName}.brick`;
|
|
1851
|
+
const outputPath = options.output ? path8.resolve(options.output) : path8.join(process.cwd(), outputFileName);
|
|
1852
|
+
await fs8.ensureDir(path8.dirname(outputPath));
|
|
1853
|
+
console.log(pc13.cyan("\u250C \u{1F9F1} Exporting template"));
|
|
1854
|
+
console.log(pc13.gray(`\u2502 \u25CF Template: ${pc13.white(templateName)}`));
|
|
1855
|
+
console.log(pc13.gray(`\u2502 \u25CF Files: ${pc13.white(metadata.files.length.toString())} files`));
|
|
1856
|
+
console.log(pc13.gray(`\u2502 \u25CF Output: ${pc13.white(outputPath)}`));
|
|
1857
|
+
try {
|
|
1858
|
+
await createBrickArchive(templatePath, outputPath, metadata);
|
|
1859
|
+
const stats = await fs8.stat(outputPath);
|
|
1860
|
+
const sizeKB = (stats.size / 1024).toFixed(2);
|
|
1861
|
+
console.log(pc13.gray("\u2502"));
|
|
1862
|
+
console.log(pc13.green(`\u2502 \u25C7 Export complete!`));
|
|
1863
|
+
console.log(pc13.gray(`\u2502 Size: ${pc13.white(sizeKB + " KB")}`));
|
|
1864
|
+
console.log(pc13.gray("\u2502"));
|
|
1865
|
+
console.log(pc13.gray(`\u2502 Share this file and import with:`));
|
|
1866
|
+
console.log(pc13.cyan(`\u2502 brick import ${outputFileName}`));
|
|
1867
|
+
console.log(pc13.cyan("\u2514"));
|
|
1868
|
+
console.log();
|
|
1869
|
+
console.log(pc13.green(`\u2713 Template '${templateName}' exported to ${outputPath}`));
|
|
1870
|
+
} catch (error) {
|
|
1871
|
+
console.log(pc13.red(`\u2502 \u2717 Export failed: ${error.message}`));
|
|
1872
|
+
console.log(pc13.cyan("\u2514"));
|
|
1873
|
+
process.exit(1);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
async function createBrickArchive(templatePath, outputPath, metadata) {
|
|
1877
|
+
return new Promise((resolve, reject) => {
|
|
1878
|
+
const output = fs8.createWriteStream(outputPath);
|
|
1879
|
+
const archive = archiver("tar", {
|
|
1880
|
+
gzip: true,
|
|
1881
|
+
gzipOptions: { level: 9 }
|
|
1882
|
+
});
|
|
1883
|
+
output.on("close", () => resolve());
|
|
1884
|
+
output.on("error", (err) => reject(err));
|
|
1885
|
+
archive.on("error", (err) => reject(err));
|
|
1886
|
+
archive.on("warning", (err) => {
|
|
1887
|
+
if (err.code !== "ENOENT") {
|
|
1888
|
+
reject(err);
|
|
1889
|
+
}
|
|
1890
|
+
});
|
|
1891
|
+
archive.pipe(output);
|
|
1892
|
+
archive.directory(templatePath, "template", (entry) => {
|
|
1893
|
+
if (entry.name === "brick.json") {
|
|
1894
|
+
return false;
|
|
1895
|
+
}
|
|
1896
|
+
return entry;
|
|
1897
|
+
});
|
|
1898
|
+
const metadataBuffer = Buffer.from(JSON.stringify(metadata, null, 2));
|
|
1899
|
+
archive.append(metadataBuffer, { name: "brick.json" });
|
|
1900
|
+
archive.finalize();
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
// src/commands/import.ts
|
|
1905
|
+
import fs9 from "fs-extra";
|
|
1906
|
+
import path9 from "path";
|
|
1907
|
+
import * as tar from "tar";
|
|
1908
|
+
import pc14 from "picocolors";
|
|
1909
|
+
import * as p12 from "@clack/prompts";
|
|
1910
|
+
async function importCommand(filePath, options) {
|
|
1911
|
+
if (!await isInitialized()) {
|
|
1912
|
+
console.log(pc14.red("CodeBrick is not initialized. Run: brick init"));
|
|
1913
|
+
process.exit(1);
|
|
1914
|
+
}
|
|
1915
|
+
const absolutePath = path9.resolve(filePath);
|
|
1916
|
+
if (!await fs9.pathExists(absolutePath)) {
|
|
1917
|
+
console.log(pc14.red(`File not found: ${absolutePath}`));
|
|
1918
|
+
process.exit(1);
|
|
1919
|
+
}
|
|
1920
|
+
if (!absolutePath.endsWith(".brick")) {
|
|
1921
|
+
console.log(pc14.yellow("\u26A0 Warning: File does not have .brick extension"));
|
|
1922
|
+
}
|
|
1923
|
+
console.log(pc14.cyan("\u250C \u{1F9F1} Importing template"));
|
|
1924
|
+
console.log(pc14.gray(`\u2502 \u25CF File: ${pc14.white(absolutePath)}`));
|
|
1925
|
+
try {
|
|
1926
|
+
const tempDir = path9.join(
|
|
1927
|
+
process.cwd(),
|
|
1928
|
+
`.brick-import-${Date.now()}`
|
|
1929
|
+
);
|
|
1930
|
+
await fs9.ensureDir(tempDir);
|
|
1931
|
+
try {
|
|
1932
|
+
await tar.extract({
|
|
1933
|
+
file: absolutePath,
|
|
1934
|
+
cwd: tempDir
|
|
1935
|
+
});
|
|
1936
|
+
const metadataPath = path9.join(tempDir, "brick.json");
|
|
1937
|
+
if (!await fs9.pathExists(metadataPath)) {
|
|
1938
|
+
throw new Error("Invalid .brick file: missing brick.json metadata");
|
|
1939
|
+
}
|
|
1940
|
+
const metadata = await fs9.readJson(metadataPath);
|
|
1941
|
+
let templateName = options.name || metadata.name;
|
|
1942
|
+
console.log(pc14.gray(`\u2502 \u25CF Template: ${pc14.white(templateName)}`));
|
|
1943
|
+
console.log(pc14.gray(`\u2502 \u25CF Description: ${pc14.white(metadata.description || "No description")}`));
|
|
1944
|
+
console.log(pc14.gray(`\u2502 \u25CF Files: ${pc14.white(metadata.files.length.toString())} files`));
|
|
1945
|
+
if (await templateExists(templateName)) {
|
|
1946
|
+
if (options.force) {
|
|
1947
|
+
console.log(pc14.yellow(`\u2502 \u26A0 Overwriting existing template '${templateName}'`));
|
|
1948
|
+
} else {
|
|
1949
|
+
console.log(pc14.gray("\u2502"));
|
|
1950
|
+
const shouldOverwrite = await p12.confirm({
|
|
1951
|
+
message: `Template '${templateName}' already exists. Overwrite?`
|
|
1952
|
+
});
|
|
1953
|
+
if (p12.isCancel(shouldOverwrite) || !shouldOverwrite) {
|
|
1954
|
+
const newName = await p12.text({
|
|
1955
|
+
message: "Enter a new name for the template:",
|
|
1956
|
+
placeholder: `${templateName}-imported`,
|
|
1957
|
+
validate: (value) => {
|
|
1958
|
+
if (!value) return "Name is required";
|
|
1959
|
+
if (!/^[a-z0-9-_]+$/i.test(value)) {
|
|
1960
|
+
return "Name can only contain letters, numbers, hyphens, and underscores";
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1964
|
+
if (p12.isCancel(newName)) {
|
|
1965
|
+
console.log(pc14.yellow("\u2502 Import cancelled"));
|
|
1966
|
+
console.log(pc14.cyan("\u2514"));
|
|
1967
|
+
await fs9.remove(tempDir);
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
templateName = newName;
|
|
1971
|
+
console.log(pc14.gray(`\u2502 \u25CF New name: ${pc14.white(templateName)}`));
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
const templateDir = getTemplatePath(templateName);
|
|
1976
|
+
const sourceTemplateDir = path9.join(tempDir, "template");
|
|
1977
|
+
if (await fs9.pathExists(templateDir)) {
|
|
1978
|
+
await fs9.remove(templateDir);
|
|
1979
|
+
}
|
|
1980
|
+
if (await fs9.pathExists(sourceTemplateDir)) {
|
|
1981
|
+
await fs9.copy(sourceTemplateDir, templateDir);
|
|
1982
|
+
} else {
|
|
1983
|
+
await fs9.ensureDir(templateDir);
|
|
1984
|
+
const entries = await fs9.readdir(tempDir);
|
|
1985
|
+
for (const entry of entries) {
|
|
1986
|
+
if (entry !== "brick.json") {
|
|
1987
|
+
await fs9.copy(
|
|
1988
|
+
path9.join(tempDir, entry),
|
|
1989
|
+
path9.join(templateDir, entry)
|
|
1990
|
+
);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1995
|
+
const updatedMetadata = {
|
|
1996
|
+
...metadata,
|
|
1997
|
+
name: templateName,
|
|
1998
|
+
updatedAt: now,
|
|
1999
|
+
source: {
|
|
2000
|
+
origin: "local",
|
|
2001
|
+
path: `imported from ${path9.basename(absolutePath)}`
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
2004
|
+
await saveTemplateMetadata(templateName, updatedMetadata);
|
|
2005
|
+
const storeEntry = {
|
|
2006
|
+
type: "local",
|
|
2007
|
+
path: templateDir,
|
|
2008
|
+
description: metadata.description || "",
|
|
2009
|
+
tags: metadata.tags || [],
|
|
2010
|
+
createdAt: metadata.createdAt || now,
|
|
2011
|
+
updatedAt: now
|
|
2012
|
+
};
|
|
2013
|
+
await addTemplateToStore(templateName, storeEntry);
|
|
2014
|
+
console.log(pc14.gray("\u2502"));
|
|
2015
|
+
console.log(pc14.green(`\u2502 \u25C7 Import complete!`));
|
|
2016
|
+
console.log(pc14.gray("\u2502"));
|
|
2017
|
+
console.log(pc14.gray(`\u2502 View structure: ${pc14.cyan(`brick tree ${templateName}`)}`));
|
|
2018
|
+
console.log(pc14.gray(`\u2502 Apply template: ${pc14.cyan(`brick apply ${templateName}`)}`));
|
|
2019
|
+
console.log(pc14.cyan("\u2514"));
|
|
2020
|
+
console.log();
|
|
2021
|
+
console.log(pc14.green(`\u2713 Template '${templateName}' imported successfully!`));
|
|
2022
|
+
} finally {
|
|
2023
|
+
await fs9.remove(tempDir);
|
|
2024
|
+
}
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
console.log(pc14.red(`\u2502 \u2717 Import failed: ${error.message}`));
|
|
2027
|
+
console.log(pc14.cyan("\u2514"));
|
|
2028
|
+
process.exit(1);
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
|
|
1814
2032
|
// src/index.ts
|
|
1815
2033
|
var program = new Command();
|
|
1816
2034
|
program.name("brick").description(
|
|
1817
|
-
|
|
1818
|
-
).version("0.
|
|
2035
|
+
pc15.cyan("\u{1F9F1} CodeBrick") + " - A framework-agnostic CLI for managing reusable code templates"
|
|
2036
|
+
).version("0.2.0");
|
|
1819
2037
|
program.command("init").description("Initialize CodeBrick in the system").action(initCommand);
|
|
1820
2038
|
program.command("save <name> [path]").description("Create a new LOCAL template from files").option("-d, --description <desc>", "Template description").option("-t, --tags <tags>", "Comma-separated tags").option("--include <patterns>", "Glob patterns to include").option("--exclude <patterns>", "Glob patterns to exclude").option("--detect-deps", "Auto-detect dependencies from imports").action(saveCommand);
|
|
1821
2039
|
program.command("list").alias("ls").description("List all saved templates").option("--local", "Show only local templates").option("--remote", "Show only remote templates").option("--tag <tag>", "Filter by tag").option("--detailed", "Show detailed view").action(listCommand);
|
|
@@ -1827,4 +2045,6 @@ program.command("remove-file <name> <files...>").description("Remove files from
|
|
|
1827
2045
|
program.command("delete <name>").alias("rm").description("Delete a template entirely").option("-f, --force", "Skip confirmation").action(deleteCommand);
|
|
1828
2046
|
program.command("size [name]").description("Show template size(s) - all templates if no name provided").action(sizeCommand);
|
|
1829
2047
|
program.command("clean <name>").description("Remove local/project-specific imports from template files").option("-p, --pattern <regex>", "Custom regex pattern to match imports to remove").option("--dry-run", "Preview changes without modifying files").option("--no-keep-external", "Also remove external package imports").action(cleanCommand);
|
|
2048
|
+
program.command("export <name>").description("Export a template as a shareable .brick file").option("-o, --output <path>", "Output file path (default: ./<name>.brick)").action(exportCommand);
|
|
2049
|
+
program.command("import <file>").description("Import a template from a .brick file").option("-n, --name <name>", "Custom name for the imported template").option("-f, --force", "Overwrite existing template without prompting").action(importCommand);
|
|
1830
2050
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-brick",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A framework-agnostic CLI tool for managing reusable code templates. Save, manage, and apply code snippets across projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -39,14 +39,18 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@clack/prompts": "^0.7.0",
|
|
42
|
+
"archiver": "^7.0.1",
|
|
42
43
|
"commander": "^12.0.0",
|
|
44
|
+
"fast-glob": "^3.3.2",
|
|
43
45
|
"fs-extra": "^11.2.0",
|
|
44
46
|
"picocolors": "^1.0.0",
|
|
45
|
-
"
|
|
47
|
+
"tar": "^7.5.2"
|
|
46
48
|
},
|
|
47
49
|
"devDependencies": {
|
|
50
|
+
"@types/archiver": "^7.0.0",
|
|
48
51
|
"@types/fs-extra": "^11.0.4",
|
|
49
52
|
"@types/node": "^20.11.0",
|
|
53
|
+
"@types/tar": "^6.1.13",
|
|
50
54
|
"tsup": "^8.0.1",
|
|
51
55
|
"typescript": "^5.3.3"
|
|
52
56
|
},
|