@tidyfactor/design 1.5.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/.tidyfactor +52 -0
- package/AGENTS.md +75 -0
- package/CHANGELOG.md +210 -0
- package/LICENSE +17 -0
- package/README.ar.md +398 -0
- package/README.de.md +44 -0
- package/README.es.md +44 -0
- package/README.fa.md +44 -0
- package/README.fr.md +44 -0
- package/README.md +418 -0
- package/README.pt.md +44 -0
- package/README.zh.md +44 -0
- package/SKILL-REGISTRY.md +69 -0
- package/SKILL.md +46 -0
- package/VISION.md +43 -0
- package/assets/blog.png +0 -0
- package/assets/content_layout.png +0 -0
- package/assets/content_output.png +0 -0
- package/assets/dark.png +0 -0
- package/assets/dashboard_layout.png +0 -0
- package/assets/dashboard_output.png +0 -0
- package/assets/ecommerce_layout.png +0 -0
- package/assets/ecommerce_output.png +0 -0
- package/assets/github-social-preview-2.png +0 -0
- package/assets/github-social-preview.png +0 -0
- package/assets/hero-banner.png +0 -0
- package/assets/light.png +0 -0
- package/assets/media_layout.png +0 -0
- package/assets/media_output.png +0 -0
- package/assets/og-default.png +0 -0
- package/assets/video.png +0 -0
- package/bin/add-skill.js +31 -0
- package/bin/create-kit.js +372 -0
- package/bin/remove-skill.js +137 -0
- package/brand.json +279 -0
- package/memory/01-design-schools.md +85 -0
- package/memory/02-design-tokens.md +41 -0
- package/memory/03-narrative-conversion.md +47 -0
- package/memory/04-motion-principles.md +44 -0
- package/memory/05-component-anatomy.md +34 -0
- package/memory/06-quality-bar.md +83 -0
- package/memory/07-consistency-contract.md +33 -0
- package/memory/08-arabic-bilingual.md +54 -0
- package/memory/09-prototype-flow.md +36 -0
- package/memory/10-python-tooling.md +64 -0
- package/memory/11-brand-json-v2.md +86 -0
- package/memory/12-typography-matrix.md +31 -0
- package/memory/13-layout-archetypes.md +30 -0
- package/memory/14-nav-footer-catalog.md +26 -0
- package/memory/15-performance-budget.md +18 -0
- package/memory/16-design-movements-guide.md +1084 -0
- package/memory/17-storytelling-industries-ux.md +143 -0
- package/memory/18-design-decision-engine.md +81 -0
- package/package.json +64 -0
- package/references/commands/_template.md +27 -0
- package/references/commands/assets.md +35 -0
- package/references/commands/audit.md +15 -0
- package/references/commands/brand.md +85 -0
- package/references/commands/brief.md +107 -0
- package/references/commands/clone.md +26 -0
- package/references/commands/components.md +46 -0
- package/references/commands/dashboard.md +45 -0
- package/references/commands/deploy.md +45 -0
- package/references/commands/flow.md +44 -0
- package/references/commands/handoff.md +53 -0
- package/references/commands/i18n.md +64 -0
- package/references/commands/init.md +30 -0
- package/references/commands/layout.md +81 -0
- package/references/commands/motion.md +62 -0
- package/references/commands/nav-footer.md +54 -0
- package/references/commands/page.md +45 -0
- package/references/commands/palette.md +35 -0
- package/references/commands/perf.md +57 -0
- package/references/commands/retrofit.md +15 -0
- package/references/commands/school.md +41 -0
- package/references/commands/states.md +48 -0
- package/references/commands/study.md +98 -0
- package/references/commands/tokens.md +45 -0
- package/references/commands/typography.md +87 -0
- package/references/foundations/daisyui.md +44 -0
- package/references/foundations/hybrid.md +32 -0
- package/references/foundations/native.md +31 -0
- package/references/foundations/tailwind-utility.md +43 -0
- package/references/memory/01-design-schools.md +85 -0
- package/references/memory/02-design-tokens.md +41 -0
- package/references/memory/03-narrative-conversion.md +47 -0
- package/references/memory/04-motion-principles.md +44 -0
- package/references/memory/05-component-anatomy.md +34 -0
- package/references/memory/06-quality-bar.md +83 -0
- package/references/memory/07-consistency-contract.md +33 -0
- package/references/memory/08-arabic-bilingual.md +54 -0
- package/references/memory/09-prototype-flow.md +36 -0
- package/references/memory/10-python-tooling.md +64 -0
- package/references/memory/11-brand-json-v2.md +86 -0
- package/references/memory/12-typography-matrix.md +31 -0
- package/references/memory/13-layout-archetypes.md +30 -0
- package/references/memory/14-nav-footer-catalog.md +26 -0
- package/references/memory/15-performance-budget.md +18 -0
- package/references/memory/16-design-movements-guide.md +1084 -0
- package/references/memory/17-storytelling-industries-ux.md +143 -0
- package/references/memory/18-design-decision-engine.md +81 -0
- package/references/memory/architecture.md +39 -0
- package/references/memory/decision-points.md +50 -0
- package/references/memory/foundations.md +15 -0
- package/references/memory/quality-bar.md +20 -0
- package/references/tidyfactor-vision.md +66 -0
- package/references/workflow.md +49 -0
- package/references/workflows/audit-prototype.md +26 -0
- package/references/workflows/brief.md +30 -0
- package/references/workflows/clone-prototype.md +27 -0
- package/references/workflows/init-prototype.md +28 -0
- package/references/workflows/retrofit-prototype.md +25 -0
- package/scripts/__pycache__/_utils.cpython-312.pyc +0 -0
- package/scripts/_utils.py +60 -0
- package/scripts/build.py +194 -0
- package/scripts/check_alpha.py +52 -0
- package/scripts/extract_palette.py +139 -0
- package/scripts/generate_transitions.py +105 -0
- package/scripts/inspect_images.py +48 -0
- package/scripts/minify_assets.py +140 -0
- package/scripts/optimize_assets.py +91 -0
- package/scripts/optimize_images.py +176 -0
- package/scripts/prepare_images.py +67 -0
- package/scripts/remove_backgrounds.py +61 -0
- package/scripts/test_build.py +228 -0
- package/templates/design-system/base.css +75 -0
- package/templates/design-system/components.css +229 -0
- package/templates/design-system/interactions.js +31 -0
- package/templates/design-system/motion.js +42 -0
- package/templates/design-system/tokens.css +81 -0
- package/templates/design-system/utilities.css +47 -0
- package/templates/index.html +84 -0
- package/templates/proto-nav.js +56 -0
- package/tools/build-skill.js +126 -0
- package/tools/validate-skill.js +144 -0
- package/tools/validate_skill.py +121 -0
package/VISION.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# TidyFactor Vision
|
|
2
|
+
|
|
3
|
+
This skill is part of the **TidyFactor Ecosystem**.
|
|
4
|
+
|
|
5
|
+
The TidyFactor Vision is maintained as the single source of truth at the Skills-LAB level:
|
|
6
|
+
|
|
7
|
+
👉 **[Read the full TidyFactor Vision](../TidyFactor-VISION.md)**
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## TidyFactor-Design — Track Summary
|
|
12
|
+
|
|
13
|
+
**TidyFactor-Design** is the code-native interactive design & prototyping track within the TidyFactor ecosystem (an AI-era alternative to Figma).
|
|
14
|
+
|
|
15
|
+
It builds interactive, clickable, animated HTML/CSS/JS prototypes while structurally guaranteeing visual consistency across all screens — zero per-page CSS/JS, zero build step.
|
|
16
|
+
|
|
17
|
+
### How this track fits the TidyFactor vision
|
|
18
|
+
|
|
19
|
+
| TidyFactor Principle | How Design Delivers It |
|
|
20
|
+
|---|---|
|
|
21
|
+
| **Structure over Complexity** | Centralized `design-system/` directory — pages carry HTML markup ONLY |
|
|
22
|
+
| **Content over Code** | Design tokens (`tokens.css` & `brand.json`) define the system independently of presentation |
|
|
23
|
+
| **AI Native** | Full `AGENTS.md` + `SKILL.md` — designed to be driven by any AI coding agent |
|
|
24
|
+
| **Open by Design** | MIT Licensed, published on npm, zero proprietary file formats |
|
|
25
|
+
| **Convention over Configuration** | Pluggable CSS foundations (Native, Tailwind, daisyUI, Hybrid) chosen once per project |
|
|
26
|
+
| **Evolution over Replacement** | Prototypes evolve directly into production code without re-architecture |
|
|
27
|
+
|
|
28
|
+
### TidyFactor Ecosystem Relationship
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
TidyFactor Ecosystem
|
|
32
|
+
├── tidyfactor-html — Static content sites
|
|
33
|
+
├── tidyfactor-php — Full-stack server-rendered PHP
|
|
34
|
+
├── tidyfactor-js — Framework-free SPAs
|
|
35
|
+
├── tidyfactor-htmx — Server-driven interactivity
|
|
36
|
+
├── TidyFactor-Cinematic — Luxury scroll-driven landing pages
|
|
37
|
+
├── TidyFactor-Design — Interactive prototyping & design systems ← this track
|
|
38
|
+
└── ...
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Commercial partner:** [Alwkala](https://alwkala.com) — provides expertise, implementation, consulting, and long-term support.
|
|
42
|
+
**NPM package:** [`@tidyfactor/design`](https://www.npmjs.com/package/@tidyfactor/design)
|
|
43
|
+
**GitHub:** [alwkala/tidyfactor-design](https://github.com/alwkala/tidyfactor-design)
|
package/assets/blog.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/assets/dark.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/assets/light.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/assets/video.png
ADDED
|
Binary file
|
package/bin/add-skill.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bin/add-skill.js — CLI installer wrapper for tidyfactor-design
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
const targetDir = process.cwd();
|
|
10
|
+
const skillSource = path.resolve(__dirname, '..');
|
|
11
|
+
const agentSkillsDir = path.join(targetDir, '.agents', 'skills', 'tidyfactor-design');
|
|
12
|
+
|
|
13
|
+
fs.mkdirSync(agentSkillsDir, { recursive: true });
|
|
14
|
+
|
|
15
|
+
function copyRecursive(src, dest) {
|
|
16
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const srcPath = path.join(src, entry.name);
|
|
19
|
+
const destPath = path.join(dest, entry.name);
|
|
20
|
+
if (['.git', 'node_modules', 'dist'].includes(entry.name)) continue;
|
|
21
|
+
if (entry.isDirectory()) {
|
|
22
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
23
|
+
copyRecursive(srcPath, destPath);
|
|
24
|
+
} else {
|
|
25
|
+
fs.copyFileSync(srcPath, destPath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
copyRecursive(skillSource, agentSkillsDir);
|
|
31
|
+
console.log('✓ Successfully injected tidyfactor-design skill into .agents/skills/tidyfactor-design');
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
/* Lightweight Zero-Dependency ANSI formatting */
|
|
9
|
+
const chalk = {
|
|
10
|
+
cyan: (str) => `\x1b[36m${str}\x1b[0m`,
|
|
11
|
+
green: (str) => `\x1b[32m${str}\x1b[0m`,
|
|
12
|
+
yellow: (str) => `\x1b[33m${str}\x1b[0m`,
|
|
13
|
+
red: (str) => `\x1b[31m${str}\x1b[0m`,
|
|
14
|
+
bold: (str) => `\x1b[1m${str}\x1b[0m`,
|
|
15
|
+
dim: (str) => `\x1b[2m${str}\x1b[0m`,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
19
|
+
const pkg = require(path.join(PACKAGE_ROOT, 'package.json'));
|
|
20
|
+
|
|
21
|
+
/* Subcommand delegation */
|
|
22
|
+
const firstArg = process.argv[2];
|
|
23
|
+
const SUBCOMMANDS_ADD = new Set(['add-skill', 'add-design-skill', 'skill', 'install-skill']);
|
|
24
|
+
const SUBCOMMANDS_REMOVE = new Set(['remove-skill', 'remove-design-skill', 'uninstall-skill', 'clean-skill']);
|
|
25
|
+
|
|
26
|
+
if (firstArg && SUBCOMMANDS_ADD.has(firstArg.toLowerCase())) {
|
|
27
|
+
process.argv.splice(2, 1);
|
|
28
|
+
require('./add-skill.js');
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (firstArg && SUBCOMMANDS_REMOVE.has(firstArg.toLowerCase())) {
|
|
33
|
+
process.argv.splice(2, 1);
|
|
34
|
+
require('./remove-skill.js');
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const FOUNDATIONS = [
|
|
39
|
+
{
|
|
40
|
+
name: 'native',
|
|
41
|
+
emoji: '🎨',
|
|
42
|
+
label: 'Native CSS',
|
|
43
|
+
desc: 'Pure CSS custom properties & semantic component classes (zero dependencies)',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'tailwind',
|
|
47
|
+
emoji: '⚡',
|
|
48
|
+
label: 'Tailwind Utility',
|
|
49
|
+
desc: 'Tailwind CDN with utility-first class mapping',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'daisyui',
|
|
53
|
+
emoji: '🌼',
|
|
54
|
+
label: 'daisyUI Components',
|
|
55
|
+
desc: 'Tailwind CDN + daisyUI component library themed via tokens',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'hybrid',
|
|
59
|
+
emoji: '🔀',
|
|
60
|
+
label: 'Hybrid Foundation',
|
|
61
|
+
desc: 'daisyUI composite app widgets + Native CSS signature brand styling',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'shadcn',
|
|
65
|
+
emoji: '📦',
|
|
66
|
+
label: 'shadcn/ui Primitives',
|
|
67
|
+
desc: 'Tailwind v4 + Radix UI accessible primitive tokens & components',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'pico',
|
|
71
|
+
emoji: '🌱',
|
|
72
|
+
label: 'Pico CSS v2',
|
|
73
|
+
desc: 'Semantic classless/lightweight CSS foundation for minimalist sites',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'bootstrap',
|
|
77
|
+
emoji: '🅱️',
|
|
78
|
+
label: 'Bootstrap 5.3',
|
|
79
|
+
desc: 'Enterprise-ready CSS custom properties & native dark mode themes',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'alpine',
|
|
83
|
+
emoji: '🏔️',
|
|
84
|
+
label: 'Alpine + Tailwind',
|
|
85
|
+
desc: 'Alpine.js micro-interactions + Tailwind v4 utility engine',
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const SCHOOLS = [
|
|
90
|
+
'minimalist',
|
|
91
|
+
'brutalism',
|
|
92
|
+
'glassmorphism',
|
|
93
|
+
'neumorphism',
|
|
94
|
+
'swiss',
|
|
95
|
+
'luxury',
|
|
96
|
+
'bauhaus',
|
|
97
|
+
'industrial',
|
|
98
|
+
'bento',
|
|
99
|
+
'aurora',
|
|
100
|
+
'cyberpunk',
|
|
101
|
+
'claymorphism'
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const FOUNDATION_BY_NAME = new Map(FOUNDATIONS.map(f => [f.name, f]));
|
|
105
|
+
|
|
106
|
+
function isInteractiveTerminal() {
|
|
107
|
+
return (
|
|
108
|
+
Boolean(process.stdin.isTTY) &&
|
|
109
|
+
Boolean(process.stdout.isTTY) &&
|
|
110
|
+
!process.env.CI &&
|
|
111
|
+
!process.env.AGENT_MODE &&
|
|
112
|
+
process.env.NO_PROMPT !== '1'
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function parseCliArgs() {
|
|
117
|
+
const args = process.argv.slice(2);
|
|
118
|
+
const positionalArgs = [];
|
|
119
|
+
let flags = {
|
|
120
|
+
foundation: undefined,
|
|
121
|
+
school: undefined,
|
|
122
|
+
palette: undefined,
|
|
123
|
+
yes: false,
|
|
124
|
+
help: false,
|
|
125
|
+
version: false,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
for (const arg of args) {
|
|
129
|
+
if (arg === '--help' || arg === '-h') {
|
|
130
|
+
flags.help = true;
|
|
131
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
132
|
+
flags.version = true;
|
|
133
|
+
} else if (arg.startsWith('--foundation=')) {
|
|
134
|
+
flags.foundation = arg.split('=')[1];
|
|
135
|
+
} else if (arg.startsWith('--school=')) {
|
|
136
|
+
flags.school = arg.split('=')[1];
|
|
137
|
+
} else if (arg.startsWith('--palette=')) {
|
|
138
|
+
flags.palette = arg.split('=')[1];
|
|
139
|
+
} else if (arg === '--yes' || arg === '-y') {
|
|
140
|
+
flags.yes = true;
|
|
141
|
+
} else if (!arg.startsWith('--')) {
|
|
142
|
+
positionalArgs.push(arg);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const targetDirArg = positionalArgs.length > 0 ? positionalArgs.join(' ') : null;
|
|
147
|
+
|
|
148
|
+
return { targetDirArg, flags };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function printBanner() {
|
|
152
|
+
const v = `v${pkg.version}`;
|
|
153
|
+
console.log('');
|
|
154
|
+
console.log(chalk.cyan(' ╔═══════════════════════════════════════════════════════╗'));
|
|
155
|
+
console.log(chalk.cyan(' ║') + chalk.bold(' 🎨 TidyFactor Design CLI ') + chalk.dim(v.padEnd(7)) + ' ' + chalk.cyan('║'));
|
|
156
|
+
console.log(chalk.cyan(' ║') + chalk.dim(' Code-native UI design engine & anti-slop system suite ') + chalk.cyan('║'));
|
|
157
|
+
console.log(chalk.cyan(' ║') + chalk.green(' 🌐 RTL & Bilingual Ready (El Messiri / Tajawal) ') + chalk.cyan('║'));
|
|
158
|
+
console.log(chalk.cyan(' ╚═══════════════════════════════════════════════════════╝'));
|
|
159
|
+
console.log('');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function printHelp() {
|
|
163
|
+
printBanner();
|
|
164
|
+
console.log(` ${chalk.bold('Usage:')}`);
|
|
165
|
+
console.log(` $ npx @tidyfactor/cli-design [project-dir] [options]\n`);
|
|
166
|
+
console.log(` ${chalk.bold('Subcommands:')}`);
|
|
167
|
+
console.log(` ${chalk.cyan('add-skill')} Inject Agent Skill, rules & memory into an existing workspace\n`);
|
|
168
|
+
console.log(` ${chalk.bold('Options:')}`);
|
|
169
|
+
console.log(` ${chalk.cyan('--foundation=<name>')} Select CSS foundation (native|tailwind|daisyui|hybrid|shadcn|pico|bootstrap|alpine)`);
|
|
170
|
+
console.log(` ${chalk.cyan('--school=<name>')} Select design school (minimalist|brutalism|glassmorphism|swiss|luxury|bento|cyberpunk)`);
|
|
171
|
+
console.log(` ${chalk.cyan('--palette=<image>')} Extract brand color palette from logo/screenshot`);
|
|
172
|
+
console.log(` ${chalk.cyan('-y, --yes')} Accept all defaults non-interactively`);
|
|
173
|
+
console.log(` ${chalk.cyan('-v, --version')} Display version number`);
|
|
174
|
+
console.log(` ${chalk.cyan('-h, --help')} Display this help message\n`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function askQuestion(rl, query) {
|
|
178
|
+
return new Promise((resolve) => rl.question(query, (ans) => resolve(ans.trim())));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function runInteractiveWizard(targetDir, foundation, school, palette) {
|
|
182
|
+
const rl = readline.createInterface({
|
|
183
|
+
input: process.stdin,
|
|
184
|
+
output: process.stdout,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Handle SIGINT (Ctrl+C) gracefully
|
|
188
|
+
rl.on('SIGINT', () => {
|
|
189
|
+
console.log(chalk.red('\n\n ✖ Operation cancelled.\n'));
|
|
190
|
+
rl.close();
|
|
191
|
+
process.exit(0);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
console.log(chalk.cyan(' 🧙 Interactive TidyFactor Design Wizard\n'));
|
|
195
|
+
|
|
196
|
+
// 1. Target Directory Prompt
|
|
197
|
+
const dirAns = await askQuestion(rl, chalk.bold(` [1/4] Target Directory [${targetDir}]: `));
|
|
198
|
+
if (dirAns) {
|
|
199
|
+
targetDir = dirAns;
|
|
200
|
+
}
|
|
201
|
+
console.log(chalk.green(` ✓ Target Directory: ${targetDir}`));
|
|
202
|
+
|
|
203
|
+
// 2. CSS Foundation Prompt
|
|
204
|
+
console.log(chalk.bold('\n [2/4] Select CSS Foundation:'));
|
|
205
|
+
FOUNDATIONS.forEach((f, idx) => {
|
|
206
|
+
const defaultTag = f.name === foundation ? chalk.green(' (default)') : '';
|
|
207
|
+
console.log(` ${chalk.cyan(`[${idx + 1}]`)} ${f.emoji} ${f.label.padEnd(16)} — ${chalk.dim(f.desc)}${defaultTag}`);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
let validFound = false;
|
|
211
|
+
while (!validFound) {
|
|
212
|
+
const foundAns = await askQuestion(rl, chalk.bold(` Choose [1-${FOUNDATIONS.length}] (Enter for default): `));
|
|
213
|
+
if (!foundAns) {
|
|
214
|
+
validFound = true;
|
|
215
|
+
} else {
|
|
216
|
+
const foundIdx = parseInt(foundAns, 10) - 1;
|
|
217
|
+
if (!isNaN(foundIdx) && FOUNDATIONS[foundIdx]) {
|
|
218
|
+
foundation = FOUNDATIONS[foundIdx].name;
|
|
219
|
+
validFound = true;
|
|
220
|
+
} else {
|
|
221
|
+
console.log(chalk.yellow(` ⚠ Invalid choice. Please select a number between 1 and ${FOUNDATIONS.length}.`));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const chosenF = FOUNDATION_BY_NAME.get(foundation);
|
|
226
|
+
console.log(chalk.green(` ✓ Foundation Selected: ${chosenF.emoji} ${chosenF.label}`));
|
|
227
|
+
|
|
228
|
+
// 3. Design School Prompt
|
|
229
|
+
console.log(chalk.bold('\n [3/4] Select Visual Design School:'));
|
|
230
|
+
SCHOOLS.forEach((s, idx) => {
|
|
231
|
+
const defaultTag = s === school ? chalk.green(' (default)') : '';
|
|
232
|
+
console.log(` ${chalk.cyan(`[${idx + 1}]`.padEnd(5))} ${s.padEnd(16)}${defaultTag}`);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
let validSchool = false;
|
|
236
|
+
while (!validSchool) {
|
|
237
|
+
const schoolAns = await askQuestion(rl, chalk.bold(` Choose [1-${SCHOOLS.length}] (Enter for default): `));
|
|
238
|
+
if (!schoolAns) {
|
|
239
|
+
validSchool = true;
|
|
240
|
+
} else {
|
|
241
|
+
const schoolIdx = parseInt(schoolAns, 10) - 1;
|
|
242
|
+
if (!isNaN(schoolIdx) && SCHOOLS[schoolIdx]) {
|
|
243
|
+
school = SCHOOLS[schoolIdx];
|
|
244
|
+
validSchool = true;
|
|
245
|
+
} else {
|
|
246
|
+
console.log(chalk.yellow(` ⚠ Invalid choice. Please select a number between 1 and ${SCHOOLS.length}.`));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
console.log(chalk.green(` ✓ Design School Selected: ${school}`));
|
|
251
|
+
|
|
252
|
+
// 4. Color Palette Extraction Prompt
|
|
253
|
+
const palAns = await askQuestion(rl, chalk.bold(`\n [4/4] Path to brand logo/image to extract colors (optional, press Enter to skip): `));
|
|
254
|
+
if (palAns && fs.existsSync(palAns)) {
|
|
255
|
+
palette = palAns;
|
|
256
|
+
console.log(chalk.green(` ✓ Palette Image Locked: ${palette}`));
|
|
257
|
+
} else if (palAns) {
|
|
258
|
+
console.log(chalk.yellow(` ⚠ Image file "${palAns}" not found. Skipping extraction.`));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
rl.close();
|
|
262
|
+
console.log('');
|
|
263
|
+
return { targetDir, foundation, school, palette };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function copyDirectory(src, dest) {
|
|
267
|
+
if (!fs.existsSync(src)) return;
|
|
268
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
269
|
+
for (const item of fs.readdirSync(src)) {
|
|
270
|
+
const sPath = path.join(src, item);
|
|
271
|
+
const dPath = path.join(dest, item);
|
|
272
|
+
if (fs.statSync(sPath).isDirectory()) {
|
|
273
|
+
copyDirectory(sPath, dPath);
|
|
274
|
+
} else {
|
|
275
|
+
fs.copyFileSync(sPath, dPath);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function main() {
|
|
281
|
+
const { targetDirArg, flags } = parseCliArgs();
|
|
282
|
+
|
|
283
|
+
if (flags.version) {
|
|
284
|
+
console.log(`v${pkg.version}`);
|
|
285
|
+
process.exit(0);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (flags.help) {
|
|
289
|
+
printHelp();
|
|
290
|
+
process.exit(0);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
printBanner();
|
|
294
|
+
|
|
295
|
+
let targetDir = targetDirArg || '.';
|
|
296
|
+
let foundation = flags.foundation || 'native';
|
|
297
|
+
let school = flags.school || 'minimalist';
|
|
298
|
+
|
|
299
|
+
// Interactive Wizard Trigger if live interactive terminal & no skip flags passed
|
|
300
|
+
const isInteractive = isInteractiveTerminal() && !flags.yes && !flags.foundation && !flags.school;
|
|
301
|
+
if (isInteractive) {
|
|
302
|
+
const wizardRes = await runInteractiveWizard(targetDir, foundation, school, flags.palette);
|
|
303
|
+
targetDir = wizardRes.targetDir;
|
|
304
|
+
foundation = wizardRes.foundation;
|
|
305
|
+
school = wizardRes.school;
|
|
306
|
+
flags.palette = wizardRes.palette;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (!FOUNDATION_BY_NAME.has(foundation)) {
|
|
310
|
+
console.log(chalk.yellow(`⚠ Unknown foundation "${foundation}", falling back to "native".`));
|
|
311
|
+
foundation = 'native';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (!SCHOOLS.includes(school)) {
|
|
315
|
+
school = 'minimalist';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const chosenFound = FOUNDATION_BY_NAME.get(foundation);
|
|
319
|
+
const targetPath = path.resolve(process.cwd(), targetDir);
|
|
320
|
+
|
|
321
|
+
console.log(chalk.green(` 🚀 Scaffolding TidyFactor Design project in ${chalk.bold(targetPath)}`));
|
|
322
|
+
console.log(chalk.dim(` Foundation: ${chosenFound.emoji} ${chosenFound.label} (${chosenFound.name})`));
|
|
323
|
+
console.log(chalk.dim(` Design School: ${school}\n`));
|
|
324
|
+
|
|
325
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
326
|
+
|
|
327
|
+
// 1. Copy design system & prototype templates
|
|
328
|
+
const templatesDir = path.join(PACKAGE_ROOT, 'templates');
|
|
329
|
+
if (fs.existsSync(templatesDir)) {
|
|
330
|
+
copyDirectory(templatesDir, targetPath);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 2. Copy brand.json v2
|
|
334
|
+
const defaultBrand = path.join(PACKAGE_ROOT, 'brand.json');
|
|
335
|
+
const targetBrand = path.join(targetPath, 'brand.json');
|
|
336
|
+
if (fs.existsSync(defaultBrand) && !fs.existsSync(targetBrand)) {
|
|
337
|
+
fs.copyFileSync(defaultBrand, targetBrand);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// 3. Auto-inject AI Agent Skill & Rules (.agents, .claude-skill, memory, AGENTS.md)
|
|
341
|
+
console.log(chalk.cyan(' 🤖 Auto-injecting AI Agent Skill & Design Decision Engine...'));
|
|
342
|
+
copyDirectory(path.join(PACKAGE_ROOT, '.agents'), path.join(targetPath, '.agents'));
|
|
343
|
+
copyDirectory(path.join(PACKAGE_ROOT, '.claude-skill'), path.join(targetPath, '.claude-skill'));
|
|
344
|
+
copyDirectory(path.join(PACKAGE_ROOT, 'memory'), path.join(targetPath, 'memory'));
|
|
345
|
+
|
|
346
|
+
const agentsMdSrc = path.join(PACKAGE_ROOT, 'AGENTS.md');
|
|
347
|
+
const agentsMdDest = path.join(targetPath, 'AGENTS.md');
|
|
348
|
+
if (fs.existsSync(agentsMdSrc) && !fs.existsSync(agentsMdDest)) {
|
|
349
|
+
fs.copyFileSync(agentsMdSrc, agentsMdDest);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// 4. Handle --palette extraction if provided
|
|
353
|
+
if (flags.palette && fs.existsSync(flags.palette)) {
|
|
354
|
+
console.log(chalk.cyan(` 🎨 Extracting color palette from ${flags.palette}...`));
|
|
355
|
+
const extractScript = path.join(PACKAGE_ROOT, 'scripts', 'extract_palette.py');
|
|
356
|
+
try {
|
|
357
|
+
execSync(`python "${extractScript}" "${flags.palette}" --json "${targetBrand}"`, { stdio: 'inherit' });
|
|
358
|
+
} catch (err) {
|
|
359
|
+
console.log(chalk.yellow('⚠ Palette extraction skipped (Python/Pillow not found).'));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
console.log(chalk.green('\n ✅ Project & AI Skill scaffolded successfully!'));
|
|
364
|
+
console.log('');
|
|
365
|
+
console.log(` ${chalk.bold('Next Steps:')}`);
|
|
366
|
+
if (targetDir !== '.') {
|
|
367
|
+
console.log(` $ cd "${targetDir}"`);
|
|
368
|
+
}
|
|
369
|
+
console.log(` $ python -m http.server 8123 # Preview interactive prototype in browser\n`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
main();
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
/* Lightweight Zero-Dependency ANSI formatting */
|
|
7
|
+
const chalk = {
|
|
8
|
+
cyan: (str) => `\x1b[36m${str}\x1b[0m`,
|
|
9
|
+
green: (str) => `\x1b[32m${str}\x1b[0m`,
|
|
10
|
+
yellow: (str) => `\x1b[33m${str}\x1b[0m`,
|
|
11
|
+
red: (str) => `\x1b[31m${str}\x1b[0m`,
|
|
12
|
+
bold: (str) => `\x1b[1m${str}\x1b[0m`,
|
|
13
|
+
dim: (str) => `\x1b[2m${str}\x1b[0m`,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
17
|
+
const pkg = require(path.join(PACKAGE_ROOT, 'package.json'));
|
|
18
|
+
|
|
19
|
+
function parseCliArgs() {
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
let flags = {
|
|
22
|
+
help: false,
|
|
23
|
+
version: false,
|
|
24
|
+
yes: false,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
for (const arg of args) {
|
|
28
|
+
if (arg === '--help' || arg === '-h') {
|
|
29
|
+
flags.help = true;
|
|
30
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
31
|
+
flags.version = true;
|
|
32
|
+
} else if (arg === '--yes' || arg === '-y') {
|
|
33
|
+
flags.yes = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return flags;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function printBanner() {
|
|
41
|
+
const v = `v${pkg.version}`;
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log(chalk.cyan(' ╔═══════════════════════════════════════════════════════╗'));
|
|
44
|
+
console.log(chalk.cyan(' ║') + chalk.bold(' 🗑️ TidyFactor Design Skill Uninstaller ') + chalk.dim(v.padEnd(7)) + ' ' + chalk.cyan('║'));
|
|
45
|
+
console.log(chalk.cyan(' ║') + chalk.dim(' Safely remove injected AI agent skill & rules ') + chalk.cyan('║'));
|
|
46
|
+
console.log(chalk.cyan(' ╚═══════════════════════════════════════════════════════╝'));
|
|
47
|
+
console.log('');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function printHelp() {
|
|
51
|
+
printBanner();
|
|
52
|
+
console.log(` ${chalk.bold('Usage:')}`);
|
|
53
|
+
console.log(` $ npx @tidyfactor/cli-design remove-skill [options]\n`);
|
|
54
|
+
console.log(` ${chalk.bold('Options:')}`);
|
|
55
|
+
console.log(` ${chalk.cyan('-y, --yes')} Accept uninstallation steps non-interactively`);
|
|
56
|
+
console.log(` ${chalk.cyan('-v, --version')} Display uninstaller version number`);
|
|
57
|
+
console.log(` ${chalk.cyan('-h, --help')} Display this help message\n`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function printSuccessSummary(removedItems) {
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log(chalk.yellow(' ╔═══════════════════════════════════════════════════════╗'));
|
|
63
|
+
console.log(chalk.yellow(' ║') + chalk.bold(' 🗑️ Design Skill Removed Successfully! ') + chalk.yellow('║'));
|
|
64
|
+
console.log(chalk.yellow(' ╠═══════════════════════════════════════════════════════╣'));
|
|
65
|
+
removedItems.forEach(item => {
|
|
66
|
+
console.log(chalk.yellow(' ║') + ` ✓ ${chalk.cyan(item.padEnd(52))}` + chalk.yellow('║'));
|
|
67
|
+
});
|
|
68
|
+
console.log(chalk.yellow(' ╚═══════════════════════════════════════════════════════╝'));
|
|
69
|
+
console.log('');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function rmrf(p) {
|
|
73
|
+
if (fs.existsSync(p)) {
|
|
74
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function main() {
|
|
81
|
+
const flags = parseCliArgs();
|
|
82
|
+
|
|
83
|
+
if (flags.version) {
|
|
84
|
+
console.log(`v${pkg.version}`);
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (flags.help) {
|
|
89
|
+
printHelp();
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
printBanner();
|
|
94
|
+
|
|
95
|
+
const cwd = process.cwd();
|
|
96
|
+
const removedItems = [];
|
|
97
|
+
|
|
98
|
+
console.log(chalk.cyan(' 🧹 Removing TidyFactor Design Agent Skill files...'));
|
|
99
|
+
|
|
100
|
+
// 1. Remove .agents/skills/tidyfactor-design
|
|
101
|
+
const agentSkillPath = path.join(cwd, '.agents', 'skills', 'tidyfactor-design');
|
|
102
|
+
if (rmrf(agentSkillPath)) {
|
|
103
|
+
removedItems.push('.agents/skills/tidyfactor-design/');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 2. Remove .claude-skill
|
|
107
|
+
const claudeSkillPath = path.join(cwd, '.claude-skill');
|
|
108
|
+
if (rmrf(claudeSkillPath)) {
|
|
109
|
+
removedItems.push('.claude-skill/');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 3. Remove memory reference folder
|
|
113
|
+
const memoryPath = path.join(cwd, 'memory');
|
|
114
|
+
if (rmrf(memoryPath)) {
|
|
115
|
+
removedItems.push('memory/');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 4. Remove AGENTS.md if it was generated by tidyfactor-design
|
|
119
|
+
const agentsMdPath = path.join(cwd, 'AGENTS.md');
|
|
120
|
+
if (fs.existsSync(agentsMdPath)) {
|
|
121
|
+
try {
|
|
122
|
+
const content = fs.readFileSync(agentsMdPath, 'utf-8');
|
|
123
|
+
if (content.includes('TidyFactor Design System') || content.includes('tidyfactor-design')) {
|
|
124
|
+
fs.unlinkSync(agentsMdPath);
|
|
125
|
+
removedItems.push('AGENTS.md');
|
|
126
|
+
}
|
|
127
|
+
} catch (e) {}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (removedItems.length === 0) {
|
|
131
|
+
console.log(chalk.yellow(' ⚠ No installed TidyFactor Design skill files found in this workspace.\n'));
|
|
132
|
+
} else {
|
|
133
|
+
printSuccessSummary(removedItems);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
main();
|