create-sdd-project 0.16.5 → 0.16.7
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/lib/doctor.js
CHANGED
|
@@ -70,6 +70,9 @@ function runDoctor(cwd) {
|
|
|
70
70
|
// 11. Autonomy/Skills Consistency
|
|
71
71
|
results.push(checkAutonomySkillConsistency(cwd, aiTools));
|
|
72
72
|
|
|
73
|
+
// 12. Gemini Settings Format
|
|
74
|
+
results.push(checkGeminiSettings(cwd, aiTools));
|
|
75
|
+
|
|
73
76
|
return results;
|
|
74
77
|
}
|
|
75
78
|
|
|
@@ -529,6 +532,87 @@ function checkAutonomySkillConsistency(cwd, aiTools) {
|
|
|
529
532
|
};
|
|
530
533
|
}
|
|
531
534
|
|
|
535
|
+
function checkGeminiSettings(cwd, aiTools) {
|
|
536
|
+
if (aiTools === 'claude') {
|
|
537
|
+
return {
|
|
538
|
+
status: PASS,
|
|
539
|
+
message: 'Gemini settings: N/A (Claude only)',
|
|
540
|
+
details: [],
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const settingsPath = path.join(cwd, '.gemini', 'settings.json');
|
|
545
|
+
if (!fs.existsSync(settingsPath)) {
|
|
546
|
+
return {
|
|
547
|
+
status: WARN,
|
|
548
|
+
message: 'Gemini settings: .gemini/settings.json missing',
|
|
549
|
+
details: ['Run: npx create-sdd-project --upgrade to recreate from template'],
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
let settings;
|
|
554
|
+
try {
|
|
555
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
556
|
+
} catch (e) {
|
|
557
|
+
return {
|
|
558
|
+
status: FAIL,
|
|
559
|
+
message: 'Gemini settings: invalid JSON',
|
|
560
|
+
details: [e.message],
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Detectably-broken formats only — do NOT enforce stricter rules than upstream Gemini.
|
|
565
|
+
if (typeof settings.model === 'string') {
|
|
566
|
+
return {
|
|
567
|
+
status: FAIL,
|
|
568
|
+
message: 'Gemini settings: obsolete model format (string instead of object)',
|
|
569
|
+
details: [
|
|
570
|
+
`Found: "model": "${settings.model}"`,
|
|
571
|
+
`Expected: "model": { "name": "${settings.model}" }`,
|
|
572
|
+
'Gemini CLI silently falls back to defaults when model is a string, ignoring this file.',
|
|
573
|
+
'Verified against Gemini CLI 0.34.0. Run: npx create-sdd-project --upgrade',
|
|
574
|
+
],
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (settings.model !== undefined && (
|
|
579
|
+
settings.model === null ||
|
|
580
|
+
Array.isArray(settings.model) ||
|
|
581
|
+
typeof settings.model !== 'object'
|
|
582
|
+
)) {
|
|
583
|
+
const got = Array.isArray(settings.model)
|
|
584
|
+
? 'array'
|
|
585
|
+
: settings.model === null
|
|
586
|
+
? 'null'
|
|
587
|
+
: typeof settings.model;
|
|
588
|
+
return {
|
|
589
|
+
status: FAIL,
|
|
590
|
+
message: 'Gemini settings: model field has invalid type',
|
|
591
|
+
details: [
|
|
592
|
+
'Expected: object with a "name" property (or absent)',
|
|
593
|
+
`Got: ${got}`,
|
|
594
|
+
'Run: npx create-sdd-project --upgrade',
|
|
595
|
+
],
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (typeof settings.model === 'object' && !settings.model.name) {
|
|
600
|
+
return {
|
|
601
|
+
status: WARN,
|
|
602
|
+
message: 'Gemini settings: model object is missing "name" property',
|
|
603
|
+
details: [
|
|
604
|
+
'Gemini accepts this but the model is unset. Add: { "name": "gemini-2.5-pro" }',
|
|
605
|
+
],
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return {
|
|
610
|
+
status: PASS,
|
|
611
|
+
message: 'Gemini settings: valid',
|
|
612
|
+
details: [],
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
532
616
|
module.exports = {
|
|
533
617
|
runDoctor,
|
|
534
618
|
printResults,
|
package/lib/upgrade-generator.js
CHANGED
|
@@ -255,17 +255,40 @@ function generateUpgrade(config) {
|
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
// Merge settings.json —
|
|
258
|
+
// Merge settings.json — strategy depends on the tool:
|
|
259
|
+
// .claude: template-owned (hooks from template), preserve user permissions/additionalDirectories
|
|
260
|
+
// .gemini: user-owned, only migrate the model field if it's in the obsolete string format
|
|
259
261
|
const settingsSrc = path.join(templateToolDir, 'settings.json');
|
|
260
262
|
const settingsDest = path.join(base, 'settings.json');
|
|
261
263
|
if (fs.existsSync(settingsSrc)) {
|
|
262
264
|
const templateSettings = JSON.parse(fs.readFileSync(settingsSrc, 'utf8'));
|
|
263
265
|
if (fs.existsSync(settingsDest)) {
|
|
264
266
|
const userSettings = JSON.parse(fs.readFileSync(settingsDest, 'utf8'));
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
267
|
+
let merged;
|
|
268
|
+
if (dir === '.claude') {
|
|
269
|
+
// Claude: take template, restore user's permissions/additionalDirectories
|
|
270
|
+
merged = { ...templateSettings };
|
|
271
|
+
if (userSettings.permissions) merged.permissions = userSettings.permissions;
|
|
272
|
+
if (userSettings.additionalDirectories) merged.additionalDirectories = userSettings.additionalDirectories;
|
|
273
|
+
} else {
|
|
274
|
+
// Gemini: preserve all user keys, only migrate model field
|
|
275
|
+
merged = { ...userSettings };
|
|
276
|
+
if (typeof userSettings.model === 'string') {
|
|
277
|
+
// Obsolete format: convert "model": "name" → "model": { "name": "name" }
|
|
278
|
+
merged.model = { name: userSettings.model };
|
|
279
|
+
} else if (
|
|
280
|
+
userSettings.model !== undefined && (
|
|
281
|
+
userSettings.model === null ||
|
|
282
|
+
Array.isArray(userSettings.model) ||
|
|
283
|
+
typeof userSettings.model !== 'object' ||
|
|
284
|
+
!userSettings.model.name
|
|
285
|
+
)
|
|
286
|
+
) {
|
|
287
|
+
// Malformed (null, array, primitive, object without name) → reset to template default
|
|
288
|
+
merged.model = { ...templateSettings.model };
|
|
289
|
+
}
|
|
290
|
+
// Otherwise (valid object with name, or absent): leave as-is
|
|
291
|
+
}
|
|
269
292
|
fs.writeFileSync(settingsDest, JSON.stringify(merged, null, 2) + '\n', 'utf8');
|
|
270
293
|
} else {
|
|
271
294
|
fs.copyFileSync(settingsSrc, settingsDest);
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"model": "gemini-2.5-pro",
|
|
2
|
+
"model": { "name": "gemini-2.5-pro" },
|
|
3
3
|
"temperature": 0.2,
|
|
4
4
|
"instructions": "Follow the development standards in ai-specs/specs/base-standards.mdc. Use the agent instructions in .gemini/agents/ for specialized tasks. Use the workflow skills in .gemini/skills/ for task orchestration. Custom commands are available in .gemini/commands/."
|
|
5
5
|
}
|
|
@@ -54,3 +54,32 @@ jobs:
|
|
|
54
54
|
|
|
55
55
|
- name: Build
|
|
56
56
|
run: npm run build --if-present
|
|
57
|
+
|
|
58
|
+
# ===========================================
|
|
59
|
+
# Scaling tips
|
|
60
|
+
# ===========================================
|
|
61
|
+
# When this project grows and you split CI into multiple jobs with path
|
|
62
|
+
# filters (e.g., test-api, test-frontend triggered only when relevant files
|
|
63
|
+
# change), enabling branch protection that requires those individual checks
|
|
64
|
+
# will create a deadlock for docs-only PRs: no code changed → no jobs run →
|
|
65
|
+
# required checks never report → merge blocked.
|
|
66
|
+
#
|
|
67
|
+
# Solution: add a rollup job that always runs and aggregates the others.
|
|
68
|
+
# Then configure branch protection to require ONLY the rollup, not the
|
|
69
|
+
# individual jobs:
|
|
70
|
+
#
|
|
71
|
+
# ci-success:
|
|
72
|
+
# runs-on: ubuntu-latest
|
|
73
|
+
# needs: [test-api, test-frontend] # list all jobs to aggregate
|
|
74
|
+
# if: always()
|
|
75
|
+
# steps:
|
|
76
|
+
# - name: All required jobs passed or were skipped
|
|
77
|
+
# run: |
|
|
78
|
+
# if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
|
|
79
|
+
# echo "One or more required jobs failed"
|
|
80
|
+
# exit 1
|
|
81
|
+
# fi
|
|
82
|
+
#
|
|
83
|
+
# The rollup passes when all dependencies pass OR were skipped due to path
|
|
84
|
+
# filters. This is the standard pattern for path-filtered CI + required
|
|
85
|
+
# checks. Avoid `|| true` and `continue-on-error` — they silence real failures.
|