@kennethsolomon/shipkit 1.0.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.
Files changed (117) hide show
  1. package/README.md +321 -0
  2. package/bin/shipkit.js +146 -0
  3. package/commands/sk/brainstorm.md +63 -0
  4. package/commands/sk/branch.md +35 -0
  5. package/commands/sk/config.md +96 -0
  6. package/commands/sk/execute-plan.md +85 -0
  7. package/commands/sk/features.md +238 -0
  8. package/commands/sk/finish-feature.md +154 -0
  9. package/commands/sk/help.md +103 -0
  10. package/commands/sk/hotfix.md +61 -0
  11. package/commands/sk/plan.md +30 -0
  12. package/commands/sk/release.md +72 -0
  13. package/commands/sk/security-check.md +188 -0
  14. package/commands/sk/set-profile.md +71 -0
  15. package/commands/sk/status.md +25 -0
  16. package/commands/sk/update-task.md +35 -0
  17. package/commands/sk/write-plan.md +72 -0
  18. package/package.json +23 -0
  19. package/skills/sk:accessibility/LICENSE.txt +177 -0
  20. package/skills/sk:accessibility/SKILL.md +150 -0
  21. package/skills/sk:api-design/LICENSE.txt +177 -0
  22. package/skills/sk:api-design/SKILL.md +158 -0
  23. package/skills/sk:brainstorming/SKILL.md +124 -0
  24. package/skills/sk:debug/SKILL.md +252 -0
  25. package/skills/sk:debug/debug_conductor.py +177 -0
  26. package/skills/sk:debug/lib/__init__.py +1 -0
  27. package/skills/sk:debug/lib/bug_gatherer.py +55 -0
  28. package/skills/sk:debug/lib/context_reader.py +139 -0
  29. package/skills/sk:debug/lib/findings_writer.py +76 -0
  30. package/skills/sk:debug/lib/lessons_writer.py +165 -0
  31. package/skills/sk:debug/lib/step_runner.py +326 -0
  32. package/skills/sk:features/SKILL.md +238 -0
  33. package/skills/sk:frontend-design/LICENSE.txt +177 -0
  34. package/skills/sk:frontend-design/SKILL.md +191 -0
  35. package/skills/sk:laravel-init/SKILL.md +37 -0
  36. package/skills/sk:laravel-new/SKILL.md +68 -0
  37. package/skills/sk:lint/SKILL.md +113 -0
  38. package/skills/sk:perf/LICENSE.txt +177 -0
  39. package/skills/sk:perf/SKILL.md +188 -0
  40. package/skills/sk:release/SKILL.md +113 -0
  41. package/skills/sk:release/references/android-checklist.md +269 -0
  42. package/skills/sk:release/references/ios-checklist.md +339 -0
  43. package/skills/sk:release/release.sh +378 -0
  44. package/skills/sk:review/SKILL.md +346 -0
  45. package/skills/sk:review/references/security-checklist.md +223 -0
  46. package/skills/sk:schema-migrate/SKILL.md +125 -0
  47. package/skills/sk:schema-migrate/orms/drizzle.md +546 -0
  48. package/skills/sk:schema-migrate/orms/laravel.md +367 -0
  49. package/skills/sk:schema-migrate/orms/prisma.md +357 -0
  50. package/skills/sk:schema-migrate/orms/rails.md +351 -0
  51. package/skills/sk:schema-migrate/orms/sqlalchemy.md +385 -0
  52. package/skills/sk:schema-migrate/references/detection.md +110 -0
  53. package/skills/sk:setup-claude/SKILL.md +365 -0
  54. package/skills/sk:setup-claude/references/detection.md +6 -0
  55. package/skills/sk:setup-claude/references/templates.md +11 -0
  56. package/skills/sk:setup-claude/scripts/apply_setup_claude.py +443 -0
  57. package/skills/sk:setup-claude/scripts/detect_arch_changes.py +437 -0
  58. package/skills/sk:setup-claude/templates/.claude/docs/arch-changelog-guide.md.template +6 -0
  59. package/skills/sk:setup-claude/templates/.claude/docs/changelog-guide.md.template +12 -0
  60. package/skills/sk:setup-claude/templates/CHANGELOG.md.template +21 -0
  61. package/skills/sk:setup-claude/templates/CLAUDE.md.template +299 -0
  62. package/skills/sk:setup-claude/templates/arch-changelog-guide.md.template +3 -0
  63. package/skills/sk:setup-claude/templates/changelog-guide.md.template +3 -0
  64. package/skills/sk:setup-claude/templates/commands/brainstorm.md.template +74 -0
  65. package/skills/sk:setup-claude/templates/commands/execute-plan.md.template +57 -0
  66. package/skills/sk:setup-claude/templates/commands/features.md.template +238 -0
  67. package/skills/sk:setup-claude/templates/commands/finish-feature.md.template +155 -0
  68. package/skills/sk:setup-claude/templates/commands/plan.md.template +30 -0
  69. package/skills/sk:setup-claude/templates/commands/re-setup.md.template +38 -0
  70. package/skills/sk:setup-claude/templates/commands/release.md.template +74 -0
  71. package/skills/sk:setup-claude/templates/commands/security-check.md.template +172 -0
  72. package/skills/sk:setup-claude/templates/commands/status.md.template +17 -0
  73. package/skills/sk:setup-claude/templates/commands/write-plan.md.template +34 -0
  74. package/skills/sk:setup-claude/templates/finish-feature.md.template +3 -0
  75. package/skills/sk:setup-claude/templates/plan.md.template +3 -0
  76. package/skills/sk:setup-claude/templates/status.md.template +3 -0
  77. package/skills/sk:setup-claude/templates/tasks/findings.md.template +19 -0
  78. package/skills/sk:setup-claude/templates/tasks/lessons.md.template +26 -0
  79. package/skills/sk:setup-claude/templates/tasks/progress.md.template +20 -0
  80. package/skills/sk:setup-claude/templates/tasks/security-findings.md.template +5 -0
  81. package/skills/sk:setup-claude/templates/tasks/todo.md.template +26 -0
  82. package/skills/sk:setup-claude/templates/tasks/workflow-status.md.template +31 -0
  83. package/skills/sk:setup-claude/templates/tasks-findings.md.template +3 -0
  84. package/skills/sk:setup-claude/templates/tasks-lessons.md.template +3 -0
  85. package/skills/sk:setup-claude/templates/tasks-progress.md.template +3 -0
  86. package/skills/sk:setup-claude/templates/tasks-todo.md.template +3 -0
  87. package/skills/sk:setup-claude/tests/test_apply_setup_claude.py +193 -0
  88. package/skills/sk:setup-optimizer/SKILL.md +184 -0
  89. package/skills/sk:setup-optimizer/lib/__init__.py +24 -0
  90. package/skills/sk:setup-optimizer/lib/detect.py +205 -0
  91. package/skills/sk:setup-optimizer/lib/discover.py +221 -0
  92. package/skills/sk:setup-optimizer/lib/enrich.py +163 -0
  93. package/skills/sk:setup-optimizer/lib/merge.py +277 -0
  94. package/skills/sk:setup-optimizer/lib/sidecar.py +129 -0
  95. package/skills/sk:setup-optimizer/optimize_claude.py +174 -0
  96. package/skills/sk:setup-optimizer/templates/CLAUDE.md.template +105 -0
  97. package/skills/sk:skill-creator/LICENSE.txt +202 -0
  98. package/skills/sk:skill-creator/SKILL.md +479 -0
  99. package/skills/sk:skill-creator/agents/analyzer.md +274 -0
  100. package/skills/sk:skill-creator/agents/comparator.md +202 -0
  101. package/skills/sk:skill-creator/agents/grader.md +223 -0
  102. package/skills/sk:skill-creator/assets/eval_review.html +146 -0
  103. package/skills/sk:skill-creator/eval-viewer/generate_review.py +471 -0
  104. package/skills/sk:skill-creator/eval-viewer/viewer.html +1325 -0
  105. package/skills/sk:skill-creator/references/schemas.md +430 -0
  106. package/skills/sk:skill-creator/scripts/aggregate_benchmark.py +401 -0
  107. package/skills/sk:skill-creator/scripts/generate_report.py +326 -0
  108. package/skills/sk:skill-creator/scripts/improve_description.py +248 -0
  109. package/skills/sk:skill-creator/scripts/package_skill.py +136 -0
  110. package/skills/sk:skill-creator/scripts/quick_validate.py +103 -0
  111. package/skills/sk:skill-creator/scripts/run_eval.py +310 -0
  112. package/skills/sk:skill-creator/scripts/run_loop.py +332 -0
  113. package/skills/sk:skill-creator/scripts/utils.py +47 -0
  114. package/skills/sk:smart-commit/SKILL.md +175 -0
  115. package/skills/sk:test/SKILL.md +171 -0
  116. package/skills/sk:write-tests/SKILL.md +195 -0
  117. package/skills/sk:write-tests/references/patterns.md +209 -0
@@ -0,0 +1,378 @@
1
+ #!/bin/bash
2
+
3
+ # Release automation script - generic for any project
4
+ # Usage: /release
5
+ # This script:
6
+ # 1. Auto-detects project info (name, version, GitHub URL)
7
+ # 2. Prompts for version number
8
+ # 3. Updates CHANGELOG.md with [Unreleased] → [Version]
9
+ # 4. Updates version in CLAUDE.md
10
+ # 5. Creates git tag
11
+ # 6. Pushes tag to GitHub
12
+
13
+ set -e # Exit on error
14
+
15
+ # Colors for output
16
+ RED='\033[0;31m'
17
+ GREEN='\033[0;32m'
18
+ YELLOW='\033[1;33m'
19
+ BLUE='\033[0;34m'
20
+ NC='\033[0m' # No Color
21
+
22
+ # ============================================================================
23
+ # HELPER FUNCTIONS
24
+ # ============================================================================
25
+
26
+ detect_project_name() {
27
+ # Try multiple sources in order of preference
28
+
29
+ # 1. Check CLAUDE.md for project name
30
+ if [ -f "CLAUDE.md" ]; then
31
+ local name=$(grep -E "^# " CLAUDE.md | head -1 | sed 's/^# //')
32
+ if [ -n "$name" ] && [ "$name" != "CLAUDE.md" ]; then
33
+ echo "$name"
34
+ return 0
35
+ fi
36
+ fi
37
+
38
+ # 2. Check package.json (Node projects)
39
+ if [ -f "package.json" ]; then
40
+ local name=$(grep '"name"' package.json | head -1 | sed 's/.*"name"\s*:\s*"\([^"]*\)".*/\1/')
41
+ if [ -n "$name" ]; then
42
+ echo "$name"
43
+ return 0
44
+ fi
45
+ fi
46
+
47
+ # 3. Check pyproject.toml (Python projects)
48
+ if [ -f "pyproject.toml" ]; then
49
+ local name=$(grep -E "^name\s*=" pyproject.toml | head -1 | sed 's/.*=\s*"\([^"]*\)".*/\1/')
50
+ if [ -n "$name" ]; then
51
+ echo "$name"
52
+ return 0
53
+ fi
54
+ fi
55
+
56
+ # 4. Check Cargo.toml (Rust projects)
57
+ if [ -f "Cargo.toml" ]; then
58
+ local name=$(grep -E "^name\s*=" Cargo.toml | head -1 | sed 's/.*=\s*"\([^"]*\)".*/\1/')
59
+ if [ -n "$name" ]; then
60
+ echo "$name"
61
+ return 0
62
+ fi
63
+ fi
64
+
65
+ # 5. Fall back to directory name
66
+ basename "$(pwd)"
67
+ }
68
+
69
+ detect_github_url() {
70
+ # Get GitHub URL from git remote origin
71
+ if git remote -v 2>/dev/null | grep -q "origin"; then
72
+ local url=$(git remote get-url origin 2>/dev/null || echo "")
73
+ # Convert SSH to HTTPS if needed
74
+ url=$(echo "$url" | sed 's/git@github.com:/https:\/\/github.com\//' | sed 's/\.git$//')
75
+ if [ -n "$url" ]; then
76
+ echo "$url"
77
+ return 0
78
+ fi
79
+ fi
80
+ echo ""
81
+ }
82
+
83
+ detect_current_version() {
84
+ # Try to find version in CLAUDE.md
85
+ if [ -f "CLAUDE.md" ]; then
86
+ local version=$(grep -E "^Version:" CLAUDE.md | head -1 | awk '{print $NF}')
87
+ if [ -n "$version" ]; then
88
+ echo "$version"
89
+ return 0
90
+ fi
91
+ fi
92
+ echo ""
93
+ }
94
+
95
+ validate_version() {
96
+ local version=$1
97
+ # Validate semantic versioning format
98
+ if [[ $version =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$ ]]; then
99
+ return 0
100
+ fi
101
+ return 1
102
+ }
103
+
104
+ suggest_release_title() {
105
+ # Analyze [Unreleased] section and suggest a title
106
+ if ! [ -f "CHANGELOG.md" ]; then
107
+ echo ""
108
+ return 0
109
+ fi
110
+
111
+ local unreleased_content=$(sed -n '/## \[Unreleased\]/,/^---$/p' CHANGELOG.md 2>/dev/null || echo "")
112
+
113
+ # Suggest based on keywords
114
+ if echo "$unreleased_content" | grep -qi "security"; then
115
+ echo "Security & Stability Improvements"
116
+ elif echo "$unreleased_content" | grep -qi "performance\|optimize"; then
117
+ echo "Performance Optimization & Enhancements"
118
+ elif echo "$unreleased_content" | grep -qi "error\|fix\|bug\|stability"; then
119
+ echo "Stability & Bug Fixes"
120
+ elif echo "$unreleased_content" | grep -qi "breaking\|migration"; then
121
+ echo "Major Release & Breaking Changes"
122
+ else
123
+ echo "Features & Improvements"
124
+ fi
125
+ }
126
+
127
+ # ============================================================================
128
+ # MAIN SCRIPT
129
+ # ============================================================================
130
+
131
+ echo -e "${BLUE}📦 Release Automation${NC}"
132
+ echo ""
133
+
134
+ # Check if CHANGELOG.md exists
135
+ if [ ! -f "CHANGELOG.md" ]; then
136
+ echo -e "${RED}❌ CHANGELOG.md not found in current directory${NC}"
137
+ echo ""
138
+ echo "This project needs a CHANGELOG.md file to use the release script."
139
+ echo "See: https://keepachangelog.com/"
140
+ exit 1
141
+ fi
142
+
143
+ # Check if CHANGELOG.md has [Unreleased] section
144
+ if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then
145
+ echo -e "${RED}❌ No [Unreleased] section found in CHANGELOG.md${NC}"
146
+ exit 1
147
+ fi
148
+
149
+ # Auto-detect project info
150
+ PROJECT_NAME=$(detect_project_name)
151
+ CURRENT_VERSION=$(detect_current_version)
152
+ GITHUB_URL=$(detect_github_url)
153
+
154
+ echo -e "${YELLOW}Project: ${PROJECT_NAME}${NC}"
155
+ if [ -n "$CURRENT_VERSION" ]; then
156
+ echo -e "${YELLOW}Current version: ${CURRENT_VERSION}${NC}"
157
+ fi
158
+ echo ""
159
+
160
+ # Prompt for new version
161
+ while true; do
162
+ read -p "Enter new version (e.g., v0.2.0, 1.0.0-beta): " NEW_VERSION
163
+
164
+ if [ -z "$NEW_VERSION" ]; then
165
+ echo -e "${RED}❌ Version cannot be empty${NC}"
166
+ continue
167
+ fi
168
+
169
+ if validate_version "$NEW_VERSION"; then
170
+ break
171
+ else
172
+ echo -e "${RED}❌ Invalid version format. Use semantic versioning (e.g., v0.2.0, 1.0.0-beta)${NC}"
173
+ fi
174
+ done
175
+
176
+ # Normalize version (ensure starts with 'v')
177
+ if [[ ! $NEW_VERSION =~ ^v ]]; then
178
+ NEW_VERSION="v${NEW_VERSION}"
179
+ fi
180
+
181
+ echo -e "${GREEN}✅ Version: ${NEW_VERSION}${NC}"
182
+ echo ""
183
+
184
+ # Get today's date
185
+ TODAY=$(date +%Y-%m-%d)
186
+
187
+ # Suggest release title
188
+ echo -e "${BLUE}💭 Analyzing changes for title suggestion...${NC}"
189
+ SUGGESTED_TITLE=$(suggest_release_title)
190
+ echo -e "${YELLOW}Suggested title: ${SUGGESTED_TITLE}${NC}"
191
+
192
+ read -p "Use suggested title or enter your own (leave blank for suggestion): " RELEASE_TITLE
193
+ if [ -z "$RELEASE_TITLE" ]; then
194
+ RELEASE_TITLE="$SUGGESTED_TITLE"
195
+ fi
196
+ echo -e "${GREEN}✅ Release title: ${RELEASE_TITLE}${NC}"
197
+ echo ""
198
+
199
+ # Update CHANGELOG.md
200
+ echo -e "${BLUE}📝 Updating CHANGELOG.md...${NC}"
201
+
202
+ export NEW_VERSION TODAY
203
+ python3 << 'PYTHON_SCRIPT'
204
+ import os
205
+ import sys
206
+
207
+ new_version = os.environ['NEW_VERSION']
208
+ today = os.environ['TODAY']
209
+
210
+ with open('CHANGELOG.md', 'r') as f:
211
+ content = f.read()
212
+
213
+ # Find the unreleased section
214
+ unreleased_marker = "## [Unreleased]"
215
+ unreleased_index = content.find(unreleased_marker)
216
+
217
+ if unreleased_index == -1:
218
+ print("ERROR: Could not find [Unreleased] section")
219
+ sys.exit(1)
220
+
221
+ # Find the next section separator (---)
222
+ next_section = content.find("\n---\n", unreleased_index)
223
+ if next_section == -1:
224
+ print("ERROR: Could not find section separator (---)")
225
+ sys.exit(1)
226
+
227
+ # Extract the unreleased content (everything between marker and separator)
228
+ unreleased_content = content[unreleased_index + len(unreleased_marker):next_section]
229
+
230
+ # Create new changelog with fresh [Unreleased] section
231
+ header = """# Changelog
232
+
233
+ All notable changes to this project are documented in this file.
234
+
235
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
236
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
237
+
238
+ ## [Unreleased]
239
+
240
+ ### Added
241
+ - _Upcoming features and improvements will be listed here_
242
+
243
+ ### Changed
244
+ - _Behavioral changes will be listed here_
245
+
246
+ ### Deprecated
247
+ - _Features being phased out will be listed here_
248
+
249
+ ### Removed
250
+ - _Features being removed will be listed here_
251
+
252
+ ### Fixed
253
+ - _Bug fixes will be listed here_
254
+
255
+ ### Security
256
+ - _Security fixes will be listed here_
257
+
258
+ ---
259
+
260
+ ## [{new_version}] - {today}
261
+ {unreleased_content}
262
+
263
+ ---
264
+
265
+ """.format(new_version=new_version, today=today, unreleased_content=unreleased_content)
266
+
267
+ # Get rest of content (everything after the first separator)
268
+ rest_content = content[next_section + len("\n---\n"):]
269
+
270
+ # Build final content
271
+ final_content = header + rest_content
272
+
273
+ with open('CHANGELOG.md', 'w') as f:
274
+ f.write(final_content)
275
+
276
+ print("CHANGELOG.md updated successfully")
277
+ PYTHON_SCRIPT
278
+
279
+ if [ $? -ne 0 ]; then
280
+ echo -e "${RED}❌ Failed to update CHANGELOG.md${NC}"
281
+ exit 1
282
+ fi
283
+
284
+ echo -e "${GREEN}✅ CHANGELOG.md updated${NC}"
285
+
286
+ # Update version in CLAUDE.md (if it exists)
287
+ if [ -f "CLAUDE.md" ]; then
288
+ echo -e "${BLUE}📝 Updating version in CLAUDE.md...${NC}"
289
+ # Ensure version stored in CLAUDE.md always starts with exactly one 'v'
290
+ CLEAN_VERSION="${NEW_VERSION#v}" # strip leading 'v' if present
291
+
292
+ # Use sed with in-place editing (compatible with macOS and Linux)
293
+ if grep -q "^Version:" CLAUDE.md; then
294
+ sed -i '' "s/^Version: .*/Version: v${CLEAN_VERSION}/" CLAUDE.md 2>/dev/null || sed -i "s/^Version: .*/Version: v${CLEAN_VERSION}/" CLAUDE.md
295
+ else
296
+ # Add Version line after title if it doesn't exist
297
+ sed -i '' "2a\\
298
+ Version: v${CLEAN_VERSION}
299
+ " CLAUDE.md 2>/dev/null || sed -i "2a Version: v${CLEAN_VERSION}" CLAUDE.md
300
+ fi
301
+ echo -e "${GREEN}✅ CLAUDE.md version updated${NC}"
302
+ fi
303
+
304
+ # Stage changes
305
+ echo ""
306
+ echo -e "${BLUE}🔗 Staging changes...${NC}"
307
+ git add CHANGELOG.md 2>/dev/null
308
+ [ -f "CLAUDE.md" ] && git add CLAUDE.md 2>/dev/null
309
+
310
+ echo -e "${GREEN}✅ Changes staged${NC}"
311
+ git status 2>/dev/null || true
312
+
313
+ # Prompt before commit
314
+ echo ""
315
+ read -p "Commit these changes? (y/n) " -n 1 -r
316
+ echo
317
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
318
+ echo -e "${YELLOW}⏭️ Skipped commit${NC}"
319
+ exit 0
320
+ fi
321
+
322
+ # Create commit
323
+ echo -e "${BLUE}💬 Creating commit...${NC}"
324
+ git commit -m "chore: Release ${NEW_VERSION}
325
+
326
+ - Update CHANGELOG.md with release notes
327
+ - Update version in CLAUDE.md
328
+
329
+ Co-Authored-By: Release Automation <noreply@release.local>"
330
+
331
+ echo -e "${GREEN}✅ Commit created${NC}"
332
+
333
+ # Create and push tag
334
+ echo ""
335
+ echo -e "${BLUE}🏷️ Creating tag ${NEW_VERSION}...${NC}"
336
+ git tag -a "${NEW_VERSION}" -m "Release ${NEW_VERSION}"
337
+ echo -e "${GREEN}✅ Tag created${NC}"
338
+
339
+ # Prompt before push
340
+ echo ""
341
+ read -p "Push tag to GitHub? (y/n) " -n 1 -r
342
+ echo
343
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
344
+ echo -e "${YELLOW}⏭️ Skipped push. You can push manually with: git push origin ${NEW_VERSION}${NC}"
345
+ exit 0
346
+ fi
347
+
348
+ echo -e "${BLUE}🚀 Pushing to GitHub...${NC}"
349
+ if git push origin "${NEW_VERSION}" 2>/dev/null; then
350
+ echo -e "${GREEN}✅ Tag pushed to GitHub${NC}"
351
+ else
352
+ echo -e "${RED}❌ Failed to push tag to GitHub${NC}"
353
+ echo "You can push manually with: git push origin ${NEW_VERSION}"
354
+ exit 1
355
+ fi
356
+
357
+ echo ""
358
+ echo -e "${GREEN}✅ Release ${NEW_VERSION} completed!${NC}"
359
+ echo ""
360
+
361
+ # Show next steps
362
+ if [ -n "$GITHUB_URL" ]; then
363
+ echo -e "${BLUE}📋 Next steps:${NC}"
364
+ echo "1. Go to: ${GITHUB_URL}/releases/tag/${NEW_VERSION}"
365
+ echo "2. Click 'Create release from tag'"
366
+ echo "3. Use this as the release title:"
367
+ echo -e " ${YELLOW}${RELEASE_TITLE}${NC}"
368
+ echo "4. Copy the [${NEW_VERSION}] section from CHANGELOG.md as release notes"
369
+ echo "5. Publish!"
370
+ else
371
+ echo -e "${BLUE}📋 Next steps:${NC}"
372
+ echo "1. Go to your GitHub releases page"
373
+ echo "2. Create a new release from tag: ${NEW_VERSION}"
374
+ echo "3. Title: ${RELEASE_TITLE}"
375
+ echo "4. Notes: Copy the [${NEW_VERSION}] section from CHANGELOG.md"
376
+ fi
377
+
378
+ echo ""