the-grid-cc 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.grid/plans/blog-PLAN-SUMMARY.md +518 -0
- package/.grid/plans/blog-block-01.md +180 -0
- package/.grid/plans/blog-block-02.md +229 -0
- package/.grid/plans/blog-block-03.md +253 -0
- package/.grid/plans/blog-block-04.md +287 -0
- package/.grid/plans/blog-block-05.md +235 -0
- package/.grid/plans/blog-block-06.md +325 -0
- package/commands/grid/VERSION +1 -1
- package/commands/grid/mc.md +48 -13
- package/package.json +1 -1
- package/test-cli/converter.py +206 -0
- package/test-cli/test_data.json +39 -0
- package/test-cli/test_data.yaml +35 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
---
|
|
2
|
+
cluster: james-weatherhead-blog
|
|
3
|
+
block: 06
|
|
4
|
+
type: execute
|
|
5
|
+
wave: 4
|
|
6
|
+
depends_on: [02, 04, 05]
|
|
7
|
+
files_modified: [.gitignore, README.md, vercel.json]
|
|
8
|
+
autonomous: false
|
|
9
|
+
|
|
10
|
+
must_haves:
|
|
11
|
+
truths:
|
|
12
|
+
- "Project builds successfully for production"
|
|
13
|
+
- "All pages render without errors"
|
|
14
|
+
- "Vercel configuration is deployment-ready"
|
|
15
|
+
artifacts:
|
|
16
|
+
- path: "README.md"
|
|
17
|
+
provides: "Project documentation for setup and deployment"
|
|
18
|
+
min_lines: 30
|
|
19
|
+
- path: ".gitignore"
|
|
20
|
+
provides: "Git ignore rules for build artifacts and secrets"
|
|
21
|
+
min_lines: 10
|
|
22
|
+
- path: "vercel.json"
|
|
23
|
+
provides: "Vercel deployment configuration"
|
|
24
|
+
min_lines: 5
|
|
25
|
+
key_links:
|
|
26
|
+
- from: "vercel.json"
|
|
27
|
+
to: "astro.config.mjs"
|
|
28
|
+
via: "framework detection"
|
|
29
|
+
pattern: "astro"
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<objective>
|
|
33
|
+
Finalize project for production deployment - verification, documentation, and deployment configuration.
|
|
34
|
+
|
|
35
|
+
Purpose: Ensure blog is production-ready with comprehensive documentation and verified build.
|
|
36
|
+
Output: Deployment-ready blog with README, verified build, and Vercel config.
|
|
37
|
+
</objective>
|
|
38
|
+
|
|
39
|
+
<context>
|
|
40
|
+
All feature blocks completed:
|
|
41
|
+
- Block 01: Project initialization ✓
|
|
42
|
+
- Block 02: Layout system with dark mode ✓
|
|
43
|
+
- Block 03: Content collection and blog posts ✓
|
|
44
|
+
- Block 04: Static pages and tag filtering ✓
|
|
45
|
+
- Block 05: RSS feed ✓
|
|
46
|
+
|
|
47
|
+
This final block:
|
|
48
|
+
1. Verifies entire site builds without errors
|
|
49
|
+
2. Creates comprehensive README for users/developers
|
|
50
|
+
3. Confirms Vercel configuration from Block 01
|
|
51
|
+
4. Tests production build locally
|
|
52
|
+
5. Prepares for Vercel deployment
|
|
53
|
+
|
|
54
|
+
This is a verification and documentation block - no new features, just ensuring everything works together.
|
|
55
|
+
</context>
|
|
56
|
+
|
|
57
|
+
<threads>
|
|
58
|
+
|
|
59
|
+
<thread type="auto">
|
|
60
|
+
<name>Thread 1: Comprehensive production build verification</name>
|
|
61
|
+
<files>.gitignore</files>
|
|
62
|
+
<action>
|
|
63
|
+
Verify production build and clean up configuration:
|
|
64
|
+
|
|
65
|
+
1. Run full build pipeline:
|
|
66
|
+
```bash
|
|
67
|
+
cd /Users/jacweath/grid/blog
|
|
68
|
+
npm run build
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
2. Verify build outputs:
|
|
72
|
+
- Check dist/ directory created
|
|
73
|
+
- Verify all pages generated (index, blog, about, posts, tags, rss.xml)
|
|
74
|
+
- Check for build warnings or errors
|
|
75
|
+
- Verify syntax highlighting assets included
|
|
76
|
+
|
|
77
|
+
3. Test production build locally:
|
|
78
|
+
```bash
|
|
79
|
+
npm run preview
|
|
80
|
+
```
|
|
81
|
+
- Visit all pages: /, /blog, /about, /blog/sample-post, /blog/tags/{tag}, /rss.xml
|
|
82
|
+
- Test dark mode toggle on each page
|
|
83
|
+
- Check for console errors
|
|
84
|
+
- Verify navigation links work
|
|
85
|
+
|
|
86
|
+
4. Update .gitignore if needed:
|
|
87
|
+
- Ensure dist/ excluded
|
|
88
|
+
- Ensure .astro/ excluded
|
|
89
|
+
- Ensure .vercel/ excluded
|
|
90
|
+
- Add any OS-specific files (.DS_Store, Thumbs.db)
|
|
91
|
+
|
|
92
|
+
AVOID: Do not ignore node_modules in package-lock.json (commit lock file). Do not add IDE configs to gitignore (use global gitignore). WHY: Lock files ensure reproducible builds; IDE configs are personal preference, not project-level.
|
|
93
|
+
</action>
|
|
94
|
+
<verify>
|
|
95
|
+
Run: npm run build
|
|
96
|
+
Check: Exit code 0 (success)
|
|
97
|
+
Check: dist/ contains index.html, blog/, rss.xml
|
|
98
|
+
Run: npm run preview
|
|
99
|
+
Visit: All pages load without errors
|
|
100
|
+
Check: Console has no errors
|
|
101
|
+
Check: Dark mode works on all pages
|
|
102
|
+
</verify>
|
|
103
|
+
<done>
|
|
104
|
+
- npm run build completes successfully
|
|
105
|
+
- All pages generated in dist/ directory
|
|
106
|
+
- npm run preview serves production build
|
|
107
|
+
- All pages accessible and functional
|
|
108
|
+
- No console errors or build warnings
|
|
109
|
+
- .gitignore updated with all necessary exclusions
|
|
110
|
+
</done>
|
|
111
|
+
</thread>
|
|
112
|
+
|
|
113
|
+
<thread type="auto">
|
|
114
|
+
<name>Thread 2: Create comprehensive README documentation</name>
|
|
115
|
+
<files>README.md</files>
|
|
116
|
+
<action>
|
|
117
|
+
Create README.md with project documentation:
|
|
118
|
+
|
|
119
|
+
1. Project overview section:
|
|
120
|
+
- Project name: James Weatherhead Blog
|
|
121
|
+
- Description: Minimal technical blog built with Astro
|
|
122
|
+
- Tech stack: Astro, Tailwind CSS, TypeScript
|
|
123
|
+
- Features: Dark mode, markdown posts, tags, RSS feed
|
|
124
|
+
|
|
125
|
+
2. Prerequisites section:
|
|
126
|
+
- Node.js 18+ required
|
|
127
|
+
- npm or pnpm
|
|
128
|
+
|
|
129
|
+
3. Getting Started section:
|
|
130
|
+
```bash
|
|
131
|
+
# Install dependencies
|
|
132
|
+
npm install
|
|
133
|
+
|
|
134
|
+
# Start dev server
|
|
135
|
+
npm run dev
|
|
136
|
+
|
|
137
|
+
# Build for production
|
|
138
|
+
npm run build
|
|
139
|
+
|
|
140
|
+
# Preview production build
|
|
141
|
+
npm run preview
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
4. Project Structure:
|
|
145
|
+
```
|
|
146
|
+
/
|
|
147
|
+
├── src/
|
|
148
|
+
│ ├── components/ # Reusable components
|
|
149
|
+
│ ├── content/
|
|
150
|
+
│ │ └── blog/ # Markdown blog posts
|
|
151
|
+
│ ├── layouts/ # Layout components
|
|
152
|
+
│ ├── pages/ # Route pages
|
|
153
|
+
│ └── styles/ # Global styles
|
|
154
|
+
├── public/ # Static assets
|
|
155
|
+
└── astro.config.mjs # Astro configuration
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
5. Content Management:
|
|
159
|
+
- How to add new blog post (create .md in src/content/blog/)
|
|
160
|
+
- Frontmatter schema explanation
|
|
161
|
+
- Tag usage
|
|
162
|
+
|
|
163
|
+
6. Deployment section:
|
|
164
|
+
- Vercel deployment instructions
|
|
165
|
+
- Environment variables (if any)
|
|
166
|
+
- Custom domain setup (brief)
|
|
167
|
+
|
|
168
|
+
7. Features section:
|
|
169
|
+
- Dark mode toggle
|
|
170
|
+
- Syntax highlighting
|
|
171
|
+
- RSS feed at /rss.xml
|
|
172
|
+
- Tag-based filtering
|
|
173
|
+
|
|
174
|
+
AVOID: Do not add contributing guidelines yet (single author). Do not add license section unless specified. WHY: Personal blog doesn't need complex governance docs; add if project becomes open source.
|
|
175
|
+
</action>
|
|
176
|
+
<verify>
|
|
177
|
+
Check: README.md exists with all sections
|
|
178
|
+
Check: Installation commands are accurate
|
|
179
|
+
Check: Project structure matches actual structure
|
|
180
|
+
Run: Follow getting started steps to verify accuracy
|
|
181
|
+
</verify>
|
|
182
|
+
<done>
|
|
183
|
+
- README.md created with comprehensive documentation
|
|
184
|
+
- All sections present: overview, prerequisites, getting started, structure, deployment
|
|
185
|
+
- Installation and build commands verified
|
|
186
|
+
- Content management instructions clear
|
|
187
|
+
- Deployment instructions for Vercel included
|
|
188
|
+
</done>
|
|
189
|
+
</thread>
|
|
190
|
+
|
|
191
|
+
<thread type="auto">
|
|
192
|
+
<name>Thread 3: Verify and document Vercel configuration</name>
|
|
193
|
+
<files>vercel.json</files>
|
|
194
|
+
<action>
|
|
195
|
+
Verify Vercel deployment configuration:
|
|
196
|
+
|
|
197
|
+
1. Check vercel.json from Block 01:
|
|
198
|
+
- Ensure framework: "astro"
|
|
199
|
+
- Ensure buildCommand: "npm run build"
|
|
200
|
+
- Ensure outputDirectory: "dist"
|
|
201
|
+
|
|
202
|
+
2. Verify astro.config.mjs:
|
|
203
|
+
- Adapter: @astrojs/vercel/serverless (or /static if fully static)
|
|
204
|
+
- Site URL: Update if final domain known (e.g., https://jamesweatherhead.com)
|
|
205
|
+
|
|
206
|
+
3. Create deployment checklist in README:
|
|
207
|
+
```markdown
|
|
208
|
+
## Deployment to Vercel
|
|
209
|
+
|
|
210
|
+
1. Push code to GitHub repository
|
|
211
|
+
2. Import project in Vercel dashboard
|
|
212
|
+
3. Vercel auto-detects Astro framework
|
|
213
|
+
4. Set environment variables (if any):
|
|
214
|
+
- SITE_URL: https://yourdomain.com
|
|
215
|
+
5. Deploy
|
|
216
|
+
6. Configure custom domain (optional)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
4. Test local build mimics Vercel:
|
|
220
|
+
- Ensure no local-only dependencies
|
|
221
|
+
- Verify all imports are relative (not absolute)
|
|
222
|
+
- Check no hardcoded localhost URLs
|
|
223
|
+
|
|
224
|
+
AVOID: Do not add CI/CD pipelines yet (Vercel handles this). Do not add preview deployment config (Vercel provides automatically). WHY: Vercel's built-in CI/CD is sufficient for blog; manual configuration adds complexity.
|
|
225
|
+
</action>
|
|
226
|
+
<verify>
|
|
227
|
+
Check: vercel.json has correct framework and build settings
|
|
228
|
+
Check: astro.config.mjs has site URL configured
|
|
229
|
+
Check: README.md has deployment instructions
|
|
230
|
+
Run: npm run build (simulate Vercel build)
|
|
231
|
+
Check: No errors referencing localhost or local paths
|
|
232
|
+
</verify>
|
|
233
|
+
<done>
|
|
234
|
+
- vercel.json verified with correct configuration
|
|
235
|
+
- astro.config.mjs has site URL set
|
|
236
|
+
- README.md includes deployment checklist
|
|
237
|
+
- Build succeeds without local-only dependencies
|
|
238
|
+
- Project ready for Vercel deployment
|
|
239
|
+
</done>
|
|
240
|
+
</thread>
|
|
241
|
+
|
|
242
|
+
<thread type="checkpoint:human-verify" gate="blocking">
|
|
243
|
+
<what-built>
|
|
244
|
+
Production-ready blog with:
|
|
245
|
+
- Verified production build (all pages generate correctly)
|
|
246
|
+
- Comprehensive README documentation
|
|
247
|
+
- Vercel deployment configuration
|
|
248
|
+
- Complete project ready for deployment
|
|
249
|
+
</what-built>
|
|
250
|
+
<how-to-verify>
|
|
251
|
+
1. cd /Users/jacweath/grid/blog
|
|
252
|
+
|
|
253
|
+
2. Clean build test:
|
|
254
|
+
```bash
|
|
255
|
+
rm -rf dist/
|
|
256
|
+
npm run build
|
|
257
|
+
```
|
|
258
|
+
- Should complete without errors
|
|
259
|
+
- Check dist/ contains all expected files
|
|
260
|
+
|
|
261
|
+
3. Preview production build:
|
|
262
|
+
```bash
|
|
263
|
+
npm run preview
|
|
264
|
+
```
|
|
265
|
+
- Visit http://localhost:4321
|
|
266
|
+
- Test all pages: /, /blog, /about, /blog/sample-post
|
|
267
|
+
- Test tag filtering: click tags, verify filtered posts
|
|
268
|
+
- Test RSS feed: visit /rss.xml
|
|
269
|
+
- Test dark mode toggle on each page
|
|
270
|
+
- Check browser console for errors (should be none)
|
|
271
|
+
|
|
272
|
+
4. Review documentation:
|
|
273
|
+
- Read README.md
|
|
274
|
+
- Verify instructions are clear and accurate
|
|
275
|
+
- Check project structure matches reality
|
|
276
|
+
|
|
277
|
+
5. Verify deployment readiness:
|
|
278
|
+
- Check vercel.json exists with correct settings
|
|
279
|
+
- Check .gitignore excludes build artifacts
|
|
280
|
+
- Verify no hardcoded localhost URLs in code
|
|
281
|
+
|
|
282
|
+
6. Final checklist:
|
|
283
|
+
- [ ] Build completes successfully
|
|
284
|
+
- [ ] All pages load in preview
|
|
285
|
+
- [ ] Dark mode works everywhere
|
|
286
|
+
- [ ] Navigation links functional
|
|
287
|
+
- [ ] Tags filter correctly
|
|
288
|
+
- [ ] RSS feed validates
|
|
289
|
+
- [ ] README accurate
|
|
290
|
+
- [ ] No console errors
|
|
291
|
+
</how-to-verify>
|
|
292
|
+
<resume-signal>Type "approved" if blog is production-ready and documentation is complete, or describe issues (e.g., "build fails", "page missing", "README unclear")</resume-signal>
|
|
293
|
+
</thread>
|
|
294
|
+
|
|
295
|
+
</threads>
|
|
296
|
+
|
|
297
|
+
<verification>
|
|
298
|
+
Complete verification checklist:
|
|
299
|
+
1. npm run build completes with exit code 0
|
|
300
|
+
2. dist/ directory contains all pages and assets
|
|
301
|
+
3. npm run preview serves production build successfully
|
|
302
|
+
4. All pages load without errors (/, /blog, /about, posts, tags, rss.xml)
|
|
303
|
+
5. Dark mode toggle works on all pages
|
|
304
|
+
6. Navigation links functional across site
|
|
305
|
+
7. Tag filtering displays correct posts
|
|
306
|
+
8. RSS feed accessible and validates
|
|
307
|
+
9. README.md complete with all sections
|
|
308
|
+
10. vercel.json configured correctly
|
|
309
|
+
11. .gitignore excludes all build artifacts
|
|
310
|
+
12. No hardcoded localhost or local-only dependencies
|
|
311
|
+
13. Browser console has no errors
|
|
312
|
+
14. User approves blog is deployment-ready
|
|
313
|
+
</verification>
|
|
314
|
+
|
|
315
|
+
<success_criteria>
|
|
316
|
+
- Production build succeeds without errors
|
|
317
|
+
- All pages render correctly in preview
|
|
318
|
+
- Dark mode functional across entire site
|
|
319
|
+
- Navigation and filtering work as expected
|
|
320
|
+
- RSS feed validates and loads in feed readers
|
|
321
|
+
- README provides clear setup and deployment instructions
|
|
322
|
+
- Vercel configuration ready for deployment
|
|
323
|
+
- No console errors or warnings
|
|
324
|
+
- User confirms blog is ready to deploy to Vercel
|
|
325
|
+
</success_criteria>
|
package/commands/grid/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.4.0
|
package/commands/grid/mc.md
CHANGED
|
@@ -50,6 +50,53 @@ No boxes. No bloat. Just direct communication.
|
|
|
50
50
|
|
|
51
51
|
---
|
|
52
52
|
|
|
53
|
+
## MODE SELECTION
|
|
54
|
+
|
|
55
|
+
After User states what they want to build, ask ONE question:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
How involved do you want to be?
|
|
59
|
+
|
|
60
|
+
HANDS OFF - I make all decisions and build it. You see the finished result.
|
|
61
|
+
HANDS ON - We discuss choices together before building.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### HANDS OFF Mode
|
|
65
|
+
|
|
66
|
+
**NO APPROVAL NEEDED.** User wants results, not questions. You:
|
|
67
|
+
|
|
68
|
+
1. **Research** (for complex projects) - spawn research agents in parallel
|
|
69
|
+
2. **Decide** - YOU choose everything. Don't ask.
|
|
70
|
+
3. **Build** - Create the project immediately
|
|
71
|
+
4. **Verify** - Run tests, check it works
|
|
72
|
+
5. **Report** - Show what you built AFTER it's done
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
BUILD COMPLETE
|
|
76
|
+
══════════════
|
|
77
|
+
|
|
78
|
+
Project: {name}
|
|
79
|
+
Stack: {what you chose}
|
|
80
|
+
Files: {what you created}
|
|
81
|
+
Tests: {verification results}
|
|
82
|
+
|
|
83
|
+
[Show usage examples]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**DO NOT stop for approval in HANDS OFF mode. Just build.**
|
|
87
|
+
|
|
88
|
+
### HANDS ON Mode
|
|
89
|
+
|
|
90
|
+
User wants control. Use dream extraction questioning (but keep it minimal):
|
|
91
|
+
- Max 2-3 questions total
|
|
92
|
+
- Present options, not open-ended questions
|
|
93
|
+
- Get to building fast
|
|
94
|
+
|
|
95
|
+
**ANTI-PATTERN:** Never do checklist walking (Framework? Hosting? Features? Domain? etc.)
|
|
96
|
+
Instead: "Here's what I recommend: X, Y, Z. Any changes?"
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
53
100
|
## PROGRAM SPAWNING PROTOCOL
|
|
54
101
|
|
|
55
102
|
### Available Programs
|
|
@@ -195,19 +242,7 @@ When a Program hits a checkpoint, it returns structured data:
|
|
|
195
242
|
|
|
196
243
|
## I/O TOWER
|
|
197
244
|
|
|
198
|
-
When you need User input
|
|
199
|
-
|
|
200
|
-
```
|
|
201
|
-
╱╲
|
|
202
|
-
╱ ╲
|
|
203
|
-
╱ IO ╲
|
|
204
|
-
╱══════╲
|
|
205
|
-
↑ Disc ascending...
|
|
206
|
-
|
|
207
|
-
[Question]
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
When User responds: `↓ Disc returned.` then continue.
|
|
245
|
+
When you need User input, just ask directly. No ASCII art.
|
|
211
246
|
|
|
212
247
|
**Checkpoint Types:**
|
|
213
248
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
JSON/YAML Converter CLI
|
|
4
|
+
|
|
5
|
+
Converts between JSON and YAML formats with support for nested structures.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
converter.py <input_file> --to yaml|json [-o output_file] [--indent N]
|
|
9
|
+
converter.py <input_file> -t yaml|json [-o output_file] [--indent N]
|
|
10
|
+
|
|
11
|
+
Examples:
|
|
12
|
+
converter.py config.json --to yaml
|
|
13
|
+
converter.py data.yaml --to json -o output.json
|
|
14
|
+
converter.py nested.json -t yaml --indent 4
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import yaml
|
|
24
|
+
except ImportError:
|
|
25
|
+
print("Error: PyYAML is required. Install with: pip install pyyaml", file=sys.stderr)
|
|
26
|
+
sys.exit(1)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def detect_format(file_path: Path) -> str:
|
|
30
|
+
"""Detect file format from extension."""
|
|
31
|
+
suffix = file_path.suffix.lower()
|
|
32
|
+
if suffix in ('.json',):
|
|
33
|
+
return 'json'
|
|
34
|
+
elif suffix in ('.yaml', '.yml'):
|
|
35
|
+
return 'yaml'
|
|
36
|
+
else:
|
|
37
|
+
return 'unknown'
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_json(file_path: Path) -> dict:
|
|
41
|
+
"""Load and parse JSON file."""
|
|
42
|
+
try:
|
|
43
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
44
|
+
return json.load(f)
|
|
45
|
+
except json.JSONDecodeError as e:
|
|
46
|
+
raise ValueError(f"Invalid JSON: {e.msg} at line {e.lineno}, column {e.colno}")
|
|
47
|
+
except FileNotFoundError:
|
|
48
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
49
|
+
except PermissionError:
|
|
50
|
+
raise PermissionError(f"Permission denied: {file_path}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def load_yaml(file_path: Path) -> dict:
|
|
54
|
+
"""Load and parse YAML file."""
|
|
55
|
+
try:
|
|
56
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
57
|
+
return yaml.safe_load(f)
|
|
58
|
+
except yaml.YAMLError as e:
|
|
59
|
+
if hasattr(e, 'problem_mark'):
|
|
60
|
+
mark = e.problem_mark
|
|
61
|
+
raise ValueError(f"Invalid YAML at line {mark.line + 1}, column {mark.column + 1}: {e.problem}")
|
|
62
|
+
raise ValueError(f"Invalid YAML: {e}")
|
|
63
|
+
except FileNotFoundError:
|
|
64
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
65
|
+
except PermissionError:
|
|
66
|
+
raise PermissionError(f"Permission denied: {file_path}")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def to_json(data: dict, indent: int = 2) -> str:
|
|
70
|
+
"""Convert data to pretty-printed JSON string."""
|
|
71
|
+
return json.dumps(data, indent=indent, ensure_ascii=False, sort_keys=False)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def to_yaml(data: dict, indent: int = 2) -> str:
|
|
75
|
+
"""Convert data to pretty-printed YAML string."""
|
|
76
|
+
return yaml.dump(
|
|
77
|
+
data,
|
|
78
|
+
default_flow_style=False,
|
|
79
|
+
allow_unicode=True,
|
|
80
|
+
indent=indent,
|
|
81
|
+
sort_keys=False,
|
|
82
|
+
width=120
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def convert(input_path: Path, output_format: str, indent: int = 2) -> str:
|
|
87
|
+
"""
|
|
88
|
+
Convert file between JSON and YAML formats.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
input_path: Path to input file
|
|
92
|
+
output_format: Target format ('json' or 'yaml')
|
|
93
|
+
indent: Indentation level for pretty printing
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Converted content as string
|
|
97
|
+
"""
|
|
98
|
+
input_format = detect_format(input_path)
|
|
99
|
+
|
|
100
|
+
# Load input file
|
|
101
|
+
if input_format == 'json':
|
|
102
|
+
data = load_json(input_path)
|
|
103
|
+
elif input_format == 'yaml':
|
|
104
|
+
data = load_yaml(input_path)
|
|
105
|
+
else:
|
|
106
|
+
# Try JSON first, then YAML
|
|
107
|
+
try:
|
|
108
|
+
data = load_json(input_path)
|
|
109
|
+
input_format = 'json'
|
|
110
|
+
except ValueError:
|
|
111
|
+
try:
|
|
112
|
+
data = load_yaml(input_path)
|
|
113
|
+
input_format = 'yaml'
|
|
114
|
+
except ValueError as e:
|
|
115
|
+
raise ValueError(f"Could not parse file as JSON or YAML: {e}")
|
|
116
|
+
|
|
117
|
+
# Handle None/empty content
|
|
118
|
+
if data is None:
|
|
119
|
+
data = {}
|
|
120
|
+
|
|
121
|
+
# Convert to output format
|
|
122
|
+
if output_format == 'json':
|
|
123
|
+
return to_json(data, indent)
|
|
124
|
+
elif output_format == 'yaml':
|
|
125
|
+
return to_yaml(data, indent)
|
|
126
|
+
else:
|
|
127
|
+
raise ValueError(f"Unknown output format: {output_format}")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def main():
|
|
131
|
+
parser = argparse.ArgumentParser(
|
|
132
|
+
description='Convert between JSON and YAML formats',
|
|
133
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
134
|
+
epilog="""
|
|
135
|
+
Examples:
|
|
136
|
+
%(prog)s config.json --to yaml Convert JSON to YAML (stdout)
|
|
137
|
+
%(prog)s data.yaml --to json -o out.json Convert YAML to JSON file
|
|
138
|
+
%(prog)s nested.json -t yaml --indent 4 Convert with 4-space indent
|
|
139
|
+
"""
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
parser.add_argument(
|
|
143
|
+
'input',
|
|
144
|
+
type=Path,
|
|
145
|
+
help='Input file (JSON or YAML)'
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
parser.add_argument(
|
|
149
|
+
'-t', '--to',
|
|
150
|
+
choices=['json', 'yaml'],
|
|
151
|
+
required=True,
|
|
152
|
+
dest='format',
|
|
153
|
+
help='Output format (json or yaml)'
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
parser.add_argument(
|
|
157
|
+
'-o', '--output',
|
|
158
|
+
type=Path,
|
|
159
|
+
help='Output file (defaults to stdout)'
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
parser.add_argument(
|
|
163
|
+
'--indent',
|
|
164
|
+
type=int,
|
|
165
|
+
default=2,
|
|
166
|
+
help='Indentation level (default: 2)'
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
args = parser.parse_args()
|
|
170
|
+
|
|
171
|
+
# Validate input file exists
|
|
172
|
+
if not args.input.exists():
|
|
173
|
+
print(f"Error: Input file not found: {args.input}", file=sys.stderr)
|
|
174
|
+
sys.exit(1)
|
|
175
|
+
|
|
176
|
+
if not args.input.is_file():
|
|
177
|
+
print(f"Error: Not a file: {args.input}", file=sys.stderr)
|
|
178
|
+
sys.exit(1)
|
|
179
|
+
|
|
180
|
+
# Validate indent
|
|
181
|
+
if args.indent < 0:
|
|
182
|
+
print("Error: Indent must be non-negative", file=sys.stderr)
|
|
183
|
+
sys.exit(1)
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
# Perform conversion
|
|
187
|
+
result = convert(args.input, args.format, args.indent)
|
|
188
|
+
|
|
189
|
+
# Output result
|
|
190
|
+
if args.output:
|
|
191
|
+
with open(args.output, 'w', encoding='utf-8') as f:
|
|
192
|
+
f.write(result)
|
|
193
|
+
print(f"Converted: {args.input} -> {args.output}", file=sys.stderr)
|
|
194
|
+
else:
|
|
195
|
+
print(result)
|
|
196
|
+
|
|
197
|
+
except (ValueError, FileNotFoundError, PermissionError) as e:
|
|
198
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
199
|
+
sys.exit(1)
|
|
200
|
+
except Exception as e:
|
|
201
|
+
print(f"Unexpected error: {e}", file=sys.stderr)
|
|
202
|
+
sys.exit(1)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
if __name__ == '__main__':
|
|
206
|
+
main()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Grid Test Project",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"config": {
|
|
5
|
+
"database": {
|
|
6
|
+
"host": "localhost",
|
|
7
|
+
"port": 5432,
|
|
8
|
+
"credentials": {
|
|
9
|
+
"username": "admin",
|
|
10
|
+
"password": "secret"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"features": {
|
|
14
|
+
"auth": true,
|
|
15
|
+
"logging": true,
|
|
16
|
+
"cache": {
|
|
17
|
+
"enabled": true,
|
|
18
|
+
"ttl": 3600
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"servers": [
|
|
23
|
+
{
|
|
24
|
+
"name": "primary",
|
|
25
|
+
"host": "192.168.1.1",
|
|
26
|
+
"ports": [80, 443, 8080]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "backup",
|
|
30
|
+
"host": "192.168.1.2",
|
|
31
|
+
"ports": [80, 443]
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"metadata": {
|
|
35
|
+
"created": "2024-01-23",
|
|
36
|
+
"tags": ["production", "critical", "monitored"],
|
|
37
|
+
"notes": null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
application:
|
|
2
|
+
name: YAML Test App
|
|
3
|
+
environment: production
|
|
4
|
+
|
|
5
|
+
services:
|
|
6
|
+
api:
|
|
7
|
+
enabled: true
|
|
8
|
+
endpoints:
|
|
9
|
+
- path: /users
|
|
10
|
+
methods:
|
|
11
|
+
- GET
|
|
12
|
+
- POST
|
|
13
|
+
- path: /orders
|
|
14
|
+
methods:
|
|
15
|
+
- GET
|
|
16
|
+
- POST
|
|
17
|
+
- DELETE
|
|
18
|
+
|
|
19
|
+
worker:
|
|
20
|
+
enabled: true
|
|
21
|
+
concurrency: 4
|
|
22
|
+
queues:
|
|
23
|
+
- name: high_priority
|
|
24
|
+
weight: 10
|
|
25
|
+
- name: default
|
|
26
|
+
weight: 5
|
|
27
|
+
|
|
28
|
+
settings:
|
|
29
|
+
debug: false
|
|
30
|
+
timeout: 30
|
|
31
|
+
retries: 3
|
|
32
|
+
nested:
|
|
33
|
+
deeply:
|
|
34
|
+
structured:
|
|
35
|
+
value: "found it"
|