@atlashub/smartstack-cli 3.7.0 → 3.9.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 (133) hide show
  1. package/dist/index.js +365 -2
  2. package/dist/index.js.map +1 -1
  3. package/package.json +4 -2
  4. package/templates/agents/action.md +1 -0
  5. package/templates/agents/ba-writer.md +33 -0
  6. package/templates/agents/explore-codebase.md +1 -0
  7. package/templates/agents/explore-docs.md +1 -0
  8. package/templates/agents/fix-grammar.md +1 -0
  9. package/templates/agents/snipper.md +1 -0
  10. package/templates/skills/admin/SKILL.md +6 -0
  11. package/templates/skills/ai-prompt/SKILL.md +32 -136
  12. package/templates/skills/ai-prompt/steps/step-01-implementation.md +122 -0
  13. package/templates/skills/apex/SKILL.md +120 -0
  14. package/templates/skills/apex/_shared.md +86 -0
  15. package/templates/skills/apex/references/agent-teams-protocol.md +164 -0
  16. package/templates/skills/apex/references/smartstack-layers.md +173 -0
  17. package/templates/skills/apex/steps/step-00-init.md +156 -0
  18. package/templates/skills/apex/steps/step-01-analyze.md +169 -0
  19. package/templates/skills/apex/steps/step-02-plan.md +160 -0
  20. package/templates/skills/apex/steps/step-03-execute.md +166 -0
  21. package/templates/skills/apex/steps/step-04-validate.md +138 -0
  22. package/templates/skills/apex/steps/step-05-examine.md +124 -0
  23. package/templates/skills/apex/steps/step-06-resolve.md +105 -0
  24. package/templates/skills/apex/steps/step-07-tests.md +130 -0
  25. package/templates/skills/apex/steps/step-08-run-tests.md +115 -0
  26. package/templates/skills/application/SKILL.md +10 -0
  27. package/templates/skills/application/references/backend-controller-hierarchy.md +58 -0
  28. package/templates/skills/application/references/backend-entity-seeding.md +72 -0
  29. package/templates/skills/application/references/backend-verification.md +88 -0
  30. package/templates/skills/application/references/frontend-verification.md +111 -0
  31. package/templates/skills/application/references/nav-fallback-procedure.md +200 -0
  32. package/templates/skills/application/references/provider-template.md +134 -0
  33. package/templates/skills/application/references/test-frontend.md +73 -0
  34. package/templates/skills/application/references/test-prerequisites.md +72 -0
  35. package/templates/skills/application/steps/step-01-navigation.md +7 -198
  36. package/templates/skills/application/steps/step-03b-provider.md +4 -128
  37. package/templates/skills/application/steps/step-04-backend.md +20 -350
  38. package/templates/skills/application/steps/step-05-frontend.md +12 -101
  39. package/templates/skills/application/steps/step-07-tests.md +12 -132
  40. package/templates/skills/business-analyse/SKILL.md +11 -2
  41. package/templates/skills/business-analyse/html/ba-interactive.html +3214 -2246
  42. package/templates/skills/business-analyse/html/build-html.js +77 -0
  43. package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +130 -0
  44. package/templates/skills/business-analyse/html/src/scripts/02-navigation.js +22 -0
  45. package/templates/skills/business-analyse/html/src/scripts/03-render-cadrage.js +208 -0
  46. package/templates/skills/business-analyse/html/src/scripts/04-render-modules.js +211 -0
  47. package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +554 -0
  48. package/templates/skills/business-analyse/html/src/scripts/06-render-consolidation.js +110 -0
  49. package/templates/skills/business-analyse/html/src/scripts/07-render-handoff.js +90 -0
  50. package/templates/skills/business-analyse/html/src/scripts/08-editing.js +45 -0
  51. package/templates/skills/business-analyse/html/src/scripts/09-export.js +168 -0
  52. package/templates/skills/business-analyse/html/src/scripts/10-comments.js +171 -0
  53. package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +161 -0
  54. package/templates/skills/business-analyse/html/src/styles/01-variables.css +38 -0
  55. package/templates/skills/business-analyse/html/src/styles/02-layout.css +101 -0
  56. package/templates/skills/business-analyse/html/src/styles/03-navigation.css +62 -0
  57. package/templates/skills/business-analyse/html/src/styles/04-cards.css +196 -0
  58. package/templates/skills/business-analyse/html/src/styles/05-modules.css +325 -0
  59. package/templates/skills/business-analyse/html/src/styles/06-wireframes.css +230 -0
  60. package/templates/skills/business-analyse/html/src/styles/07-comments.css +184 -0
  61. package/templates/skills/business-analyse/html/src/styles/08-review-panel.css +241 -0
  62. package/templates/skills/business-analyse/html/src/template.html +623 -0
  63. package/templates/skills/business-analyse/references/cadrage-structure-cards.md +78 -0
  64. package/templates/skills/business-analyse/references/cadrage-vibe-coding.md +97 -0
  65. package/templates/skills/business-analyse/references/consolidation-structural-checks.md +92 -0
  66. package/templates/skills/business-analyse/references/deploy-data-build.md +121 -0
  67. package/templates/skills/business-analyse/references/deploy-modes.md +49 -0
  68. package/templates/skills/business-analyse/references/handoff-file-templates.md +119 -0
  69. package/templates/skills/business-analyse/references/handoff-mappings.md +81 -0
  70. package/templates/skills/business-analyse/references/html-data-mapping.md +10 -2
  71. package/templates/skills/business-analyse/references/init-schema-deployment.md +65 -0
  72. package/templates/skills/business-analyse/references/review-data-mapping.md +363 -0
  73. package/templates/skills/business-analyse/references/spec-auto-inference.md +57 -0
  74. package/templates/skills/business-analyse/references/ui-dashboard-spec.md +85 -0
  75. package/templates/skills/business-analyse/references/ui-resource-cards.md +110 -0
  76. package/templates/skills/business-analyse/references/validate-incremental-html.md +55 -0
  77. package/templates/skills/business-analyse/steps/step-00-init.md +35 -68
  78. package/templates/skills/business-analyse/steps/step-01-cadrage.md +5 -194
  79. package/templates/skills/business-analyse/steps/step-03a-data.md +6 -49
  80. package/templates/skills/business-analyse/steps/step-03b-ui.md +12 -178
  81. package/templates/skills/business-analyse/steps/step-03d-validate.md +3 -48
  82. package/templates/skills/business-analyse/steps/step-04-consolidation.md +9 -104
  83. package/templates/skills/business-analyse/steps/step-05a-handoff.md +25 -441
  84. package/templates/skills/business-analyse/steps/step-05b-deploy.md +19 -187
  85. package/templates/skills/business-analyse/steps/step-06-review.md +277 -0
  86. package/templates/skills/cc-agent/references/agent-behavior-patterns.md +95 -0
  87. package/templates/skills/cc-agent/steps/step-02-generate.md +5 -78
  88. package/templates/skills/check-version/SKILL.md +7 -0
  89. package/templates/skills/controller/references/controller-code-templates.md +159 -0
  90. package/templates/skills/controller/references/permission-sync-templates.md +152 -0
  91. package/templates/skills/controller/steps/step-03-generate.md +6 -158
  92. package/templates/skills/controller/steps/step-04-perms.md +5 -144
  93. package/templates/skills/debug/SKILL.md +7 -0
  94. package/templates/skills/explore/SKILL.md +6 -0
  95. package/templates/skills/feature-full/SKILL.md +39 -142
  96. package/templates/skills/feature-full/steps/step-01-implementation.md +120 -0
  97. package/templates/skills/gitflow/references/init-config-template.md +135 -0
  98. package/templates/skills/gitflow/references/init-name-normalization.md +103 -0
  99. package/templates/skills/gitflow/references/plan-template.md +69 -0
  100. package/templates/skills/gitflow/references/start-efcore-preflight.md +70 -0
  101. package/templates/skills/gitflow/references/start-local-config.md +110 -0
  102. package/templates/skills/gitflow/steps/step-init.md +18 -289
  103. package/templates/skills/gitflow/steps/step-plan.md +6 -63
  104. package/templates/skills/gitflow/steps/step-start.md +16 -126
  105. package/templates/skills/mcp/SKILL.md +9 -213
  106. package/templates/skills/mcp/steps/step-01-healthcheck.md +108 -0
  107. package/templates/skills/mcp/steps/step-02-tools.md +73 -0
  108. package/templates/skills/notification/SKILL.md +7 -0
  109. package/templates/skills/quick-search/SKILL.md +5 -0
  110. package/templates/skills/ralph-loop/SKILL.md +99 -381
  111. package/templates/skills/ralph-loop/references/category-rules.md +259 -0
  112. package/templates/skills/ralph-loop/references/compact-loop.md +182 -0
  113. package/templates/skills/ralph-loop/references/task-transform-legacy.md +259 -0
  114. package/templates/skills/ralph-loop/references/team-orchestration.md +189 -0
  115. package/templates/skills/ralph-loop/steps/step-00-init.md +111 -383
  116. package/templates/skills/ralph-loop/steps/step-01-task.md +79 -896
  117. package/templates/skills/ralph-loop/steps/step-02-execute.md +68 -680
  118. package/templates/skills/ralph-loop/steps/step-03-commit.md +47 -277
  119. package/templates/skills/ralph-loop/steps/step-04-check.md +124 -607
  120. package/templates/skills/ralph-loop/steps/step-05-report.md +68 -367
  121. package/templates/skills/refactor/SKILL.md +12 -176
  122. package/templates/skills/refactor/steps/step-01-discover.md +60 -0
  123. package/templates/skills/refactor/steps/step-02-execute.md +67 -0
  124. package/templates/skills/review-code/SKILL.md +19 -257
  125. package/templates/skills/review-code/steps/step-01-smartstack.md +96 -0
  126. package/templates/skills/review-code/steps/step-02-detailed-review.md +80 -0
  127. package/templates/skills/review-code/steps/step-03-react.md +44 -0
  128. package/templates/skills/ui-components/SKILL.md +7 -0
  129. package/templates/skills/utils/SKILL.md +6 -0
  130. package/templates/skills/validate/SKILL.md +6 -0
  131. package/templates/skills/validate-feature/SKILL.md +8 -0
  132. package/templates/skills/workflow/SKILL.md +40 -118
  133. package/templates/skills/workflow/steps/step-01-implementation.md +84 -0
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Build script for ba-interactive.html
5
+ *
6
+ * Assembles the HTML template from split source files:
7
+ * - src/template.html (shell with placeholders)
8
+ * - src/styles/*.css (concatenated in alphabetical order)
9
+ * - src/scripts/*.js (concatenated in alphabetical order)
10
+ *
11
+ * Output: ba-interactive.html (single file, compatible with file:// protocol)
12
+ *
13
+ * Preserves template placeholders: {{FEATURE_DATA}}, {{EMBEDDED_ARTIFACTS}}, {{APPLICATION_NAME}}, {{APPLICATION_ID}}
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+
19
+ const SRC_DIR = path.join(__dirname, 'src');
20
+ const STYLES_DIR = path.join(SRC_DIR, 'styles');
21
+ const SCRIPTS_DIR = path.join(SRC_DIR, 'scripts');
22
+ const TEMPLATE_FILE = path.join(SRC_DIR, 'template.html');
23
+ const OUTPUT_FILE = path.join(__dirname, 'ba-interactive.html');
24
+
25
+ function readSortedFiles(dir, extension) {
26
+ const files = fs.readdirSync(dir)
27
+ .filter(f => f.endsWith(extension))
28
+ .sort();
29
+
30
+ return files.map(f => {
31
+ const content = fs.readFileSync(path.join(dir, f), 'utf8');
32
+ return { name: f, content };
33
+ });
34
+ }
35
+
36
+ function build() {
37
+ console.log('[build-html] Starting build...');
38
+
39
+ // Read template
40
+ if (!fs.existsSync(TEMPLATE_FILE)) {
41
+ console.error('[build-html] ERROR: Template not found at', TEMPLATE_FILE);
42
+ process.exit(1);
43
+ }
44
+ let template = fs.readFileSync(TEMPLATE_FILE, 'utf8');
45
+
46
+ // Read and concatenate CSS files
47
+ const cssFiles = readSortedFiles(STYLES_DIR, '.css');
48
+ console.log(`[build-html] CSS files: ${cssFiles.map(f => f.name).join(', ')}`);
49
+ const css = cssFiles.map(f => `/* --- ${f.name} --- */\n${f.content}`).join('\n\n');
50
+
51
+ // Read and concatenate JS files
52
+ const jsFiles = readSortedFiles(SCRIPTS_DIR, '.js');
53
+ console.log(`[build-html] JS files: ${jsFiles.map(f => f.name).join(', ')}`);
54
+ const js = jsFiles.map(f => `/* --- ${f.name} --- */\n${f.content}`).join('\n\n');
55
+
56
+ // Inject into template
57
+ template = template.replace('<!-- CSS_PLACEHOLDER -->', css);
58
+ template = template.replace('<!-- JS_PLACEHOLDER -->', js);
59
+
60
+ // Verify placeholders are preserved
61
+ const requiredPlaceholders = ['{{FEATURE_DATA}}', '{{EMBEDDED_ARTIFACTS}}', '{{APPLICATION_NAME}}'];
62
+ const missing = requiredPlaceholders.filter(p => !template.includes(p));
63
+ if (missing.length > 0) {
64
+ console.error('[build-html] ERROR: Missing placeholders:', missing.join(', '));
65
+ process.exit(1);
66
+ }
67
+
68
+ // Write output
69
+ fs.writeFileSync(OUTPUT_FILE, template, 'utf8');
70
+
71
+ const sizeKB = Math.round(fs.statSync(OUTPUT_FILE).size / 1024);
72
+ console.log(`[build-html] Output: ${OUTPUT_FILE} (${sizeKB} KB)`);
73
+ console.log(`[build-html] CSS: ${cssFiles.length} files, JS: ${jsFiles.length} files`);
74
+ console.log('[build-html] Build complete.');
75
+ }
76
+
77
+ build();
@@ -0,0 +1,130 @@
1
+ /* ============================================
2
+ DATA STORE
3
+ ============================================ */
4
+ const APP_KEY = 'ba-{{APPLICATION_ID}}';
5
+ let data = {{FEATURE_DATA}};
6
+ const ORIGINAL_DATA = JSON.parse(JSON.stringify(data));
7
+ const EMBEDDED_ARTIFACTS = {{EMBEDDED_ARTIFACTS}};
8
+
9
+ // Initialize optional data structures
10
+ data.wireframeComments = data.wireframeComments || {};
11
+ data.specComments = data.specComments || {};
12
+ data.customRoles = data.customRoles || [];
13
+ data.customActions = data.customActions || [];
14
+ data.cadrage.criteria = data.cadrage.criteria || [];
15
+ data.cadrage.risks = data.cadrage.risks || [];
16
+ data.consolidation = data.consolidation || {};
17
+ data.consolidation.e2eFlows = data.consolidation.e2eFlows || [];
18
+
19
+ // Initialize comments array
20
+ data.comments = data.comments || [];
21
+
22
+ // Defensive mapping: globalScope (feature.json format) -> scope (HTML format)
23
+ // Handles cases where FEATURE_DATA contains raw globalScope instead of pre-mapped scope
24
+ if (!data.cadrage.scope || (!data.cadrage.scope.vital?.length && data.cadrage.globalScope)) {
25
+ const gs = data.cadrage.globalScope || {};
26
+ data.cadrage.scope = {
27
+ vital: (gs.mustHave || []).map(s => typeof s === 'string' ? { name: s, description: '' } : s),
28
+ important: (gs.shouldHave || []).map(s => typeof s === 'string' ? { name: s, description: '' } : s),
29
+ optional: (gs.couldHave || []).map(s => typeof s === 'string' ? { name: s, description: '' } : s),
30
+ excluded: (gs.outOfScope || []).map(s => typeof s === 'string' ? { name: s, description: '' } : s)
31
+ };
32
+ }
33
+ data.cadrage.scope = data.cadrage.scope || { vital: [], important: [], optional: [], excluded: [] };
34
+
35
+ // Defensive init: moduleSpecs (may be missing if LLM didn't follow mapping)
36
+ data.moduleSpecs = data.moduleSpecs || {};
37
+
38
+ // Vibe coding mode: hide non-relevant sections
39
+ const isVibeCoding = data.metadata?.vibeCoding === true;
40
+ if (isVibeCoding) {
41
+ document.querySelectorAll('[data-vibe-hide]').forEach(el => el.style.display = 'none');
42
+ }
43
+
44
+ /* ============================================
45
+ UTILITIES
46
+ ============================================ */
47
+ function showNotification(message) {
48
+ const el = document.getElementById('notification');
49
+ el.textContent = message;
50
+ el.classList.add('visible');
51
+ setTimeout(() => el.classList.remove('visible'), 2500);
52
+ }
53
+
54
+ function toggleForm(formId) {
55
+ document.getElementById(formId).classList.toggle('visible');
56
+ }
57
+
58
+ function clearForm(formId) {
59
+ document.querySelectorAll('#' + formId + ' input, #' + formId + ' textarea').forEach(el => el.value = '');
60
+ }
61
+
62
+ function getNestedValue(obj, path) {
63
+ return path.split('.').reduce((o, k) => (o || {})[k], obj);
64
+ }
65
+
66
+ function setNestedValue(obj, path, value) {
67
+ const keys = path.split('.');
68
+ let current = obj;
69
+ for (let i = 0; i < keys.length - 1; i++) {
70
+ if (!current[keys[i]]) current[keys[i]] = {};
71
+ current = current[keys[i]];
72
+ }
73
+ current[keys[keys.length - 1]] = value;
74
+ }
75
+
76
+ function updateCounts() {
77
+ document.getElementById('stakeholderCount').textContent = data.cadrage.stakeholders.length;
78
+ document.getElementById('moduleCount').textContent = data.modules.length;
79
+ updateModulesNav();
80
+ updateDepSelects();
81
+ }
82
+
83
+ function formatFrequency(f) {
84
+ return { daily: 'Quotidien', weekly: 'Hebdomadaire', monthly: 'Mensuel', occasional: 'Occasionnel' }[f] || f;
85
+ }
86
+
87
+ function formatAccess(a) {
88
+ return { admin: 'Administration', manager: 'Supervision', contributor: 'Contribution', viewer: 'Consultation' }[a] || a;
89
+ }
90
+
91
+ function formatPriority(p) {
92
+ return { vital: 'Indispensable', important: 'Important', optional: 'Optionnel', excluded: 'Hors perimetre' }[p] || p;
93
+ }
94
+
95
+ function formatLevel(l) {
96
+ return { high: 'Fort', medium: 'Moyen', low: 'Faible' }[l] || l;
97
+ }
98
+
99
+ function formatModuleType(t) {
100
+ return { 'data-centric': 'Donnees', 'workflow': 'Processus', 'reporting': 'Rapports', 'integration': 'Integration', 'full-module': 'Complet' }[t] || t;
101
+ }
102
+
103
+ function formatModulePriority(p) {
104
+ return { must: 'Indispensable', should: 'Important', could: 'Optionnel' }[p] || p;
105
+ }
106
+
107
+ /* ============================================
108
+ INITIALIZATION
109
+ ============================================ */
110
+ document.addEventListener('DOMContentLoaded', function() {
111
+ loadFromLocalStorage();
112
+ initEditableFields();
113
+ // Re-apply vibe hiding after DOM init (in case loadFromLocalStorage changed data)
114
+ if (isVibeCoding) {
115
+ document.querySelectorAll('[data-vibe-hide]').forEach(el => el.style.display = 'none');
116
+ }
117
+ renderStakeholders();
118
+ renderScope();
119
+ renderRisks();
120
+ renderCriteria();
121
+ renderModules();
122
+ renderDependencies();
123
+ renderAllModuleSpecs();
124
+ renderConsolidation();
125
+ renderHandoff();
126
+ renderE2EFlows();
127
+ updateCounts();
128
+ initInlineComments();
129
+ renderReviewPanel();
130
+ });
@@ -0,0 +1,22 @@
1
+ /* ============================================
2
+ NAVIGATION
3
+ ============================================ */
4
+ let currentSectionId = 'cadrage-problem';
5
+
6
+ function showSection(sectionId) {
7
+ currentSectionId = sectionId;
8
+ document.querySelectorAll('.section').forEach(s => s.style.display = 'none');
9
+ const section = document.getElementById(sectionId);
10
+ if (section) section.style.display = 'block';
11
+
12
+ document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
13
+ const navItem = document.querySelector('[data-section="' + sectionId + '"]');
14
+ if (navItem) navItem.classList.add('active');
15
+ }
16
+
17
+ function restoreCurrentSection() {
18
+ if (currentSectionId) {
19
+ const section = document.getElementById(currentSectionId);
20
+ if (section) section.style.display = 'block';
21
+ }
22
+ }
@@ -0,0 +1,208 @@
1
+ /* ============================================
2
+ EDITABLE FIELDS
3
+ ============================================ */
4
+ function initEditableFields() {
5
+ document.querySelectorAll('.editable[data-field]').forEach(el => {
6
+ const field = el.dataset.field;
7
+ const value = getNestedValue(data, 'cadrage.' + field);
8
+ if (value) el.textContent = value;
9
+
10
+ el.addEventListener('blur', function() {
11
+ setNestedValue(data, 'cadrage.' + field, this.textContent.trim());
12
+ autoSave();
13
+ });
14
+ });
15
+ }
16
+
17
+ /* ============================================
18
+ STAKEHOLDERS
19
+ ============================================ */
20
+ function addStakeholder() {
21
+ const role = document.getElementById('sh-role').value.trim();
22
+ if (!role) return;
23
+
24
+ data.cadrage.stakeholders.push({
25
+ role: role,
26
+ function: document.getElementById('sh-function').value.trim(),
27
+ tasks: document.getElementById('sh-tasks').value.split('\n').filter(t => t.trim()),
28
+ frequency: document.getElementById('sh-frequency').value,
29
+ access: document.getElementById('sh-access').value,
30
+ frustrations: document.getElementById('sh-frustrations').value.trim()
31
+ });
32
+
33
+ renderStakeholders();
34
+ toggleForm('addStakeholderForm');
35
+ clearForm('addStakeholderForm');
36
+ updateCounts();
37
+ autoSave();
38
+ }
39
+
40
+ function renderStakeholders() {
41
+ const grid = document.getElementById('stakeholderGrid');
42
+ grid.innerHTML = data.cadrage.stakeholders.map((s, i) => `
43
+ <div class="stakeholder-card">
44
+ <div style="display:flex;justify-content:space-between;align-items:start;">
45
+ <div class="stakeholder-role">${s.role}</div>
46
+ <button class="btn btn-sm" onclick="removeStakeholder(${i})" style="opacity:0.5;font-size:0.7rem;">Supprimer</button>
47
+ </div>
48
+ <div class="stakeholder-function">${s.function || ''}</div>
49
+ <ul class="stakeholder-tasks">
50
+ ${(s.tasks || []).map(t => '<li>' + t + '</li>').join('')}
51
+ </ul>
52
+ <div class="stakeholder-meta">
53
+ <span>${formatFrequency(s.frequency)}</span>
54
+ <span>${formatAccess(s.access)}</span>
55
+ </div>
56
+ ${s.frustrations ? '<div style="font-size:0.8rem;color:var(--warning);margin-top:0.5rem;font-style:italic;">' + s.frustrations + '</div>' : ''}
57
+ </div>
58
+ `).join('');
59
+ }
60
+
61
+ function removeStakeholder(index) {
62
+ data.cadrage.stakeholders.splice(index, 1);
63
+ renderStakeholders();
64
+ updateCounts();
65
+ autoSave();
66
+ }
67
+
68
+ /* ============================================
69
+ SCOPE ITEMS
70
+ ============================================ */
71
+ function addScopeItem(priority) {
72
+ const name = document.getElementById('scope-name-' + priority).value.trim();
73
+ if (!name) return;
74
+ const description = document.getElementById('scope-desc-' + priority).value.trim();
75
+
76
+ data.cadrage.scope[priority].push({ name, description });
77
+ renderScope();
78
+ toggleForm('addScopeForm-' + priority);
79
+ document.getElementById('scope-name-' + priority).value = '';
80
+ document.getElementById('scope-desc-' + priority).value = '';
81
+ autoSave();
82
+ }
83
+
84
+ function renderScope() {
85
+ ['vital', 'important', 'optional', 'excluded'].forEach(p => {
86
+ const container = document.getElementById('scope' + p.charAt(0).toUpperCase() + p.slice(1));
87
+ container.innerHTML = data.cadrage.scope[p].map((item, i) => `
88
+ <div class="uc-item">
89
+ <div class="uc-header">
90
+ <span class="priority priority-${p}">${formatPriority(p)}</span>
91
+ <span class="uc-title">${item.name}</span>
92
+ <div class="uc-actions">
93
+ <button class="btn btn-sm" onclick="removeScopeItem('${p}',${i})">Supprimer</button>
94
+ </div>
95
+ </div>
96
+ ${item.description ? '<div class="uc-detail">' + item.description + '</div>' : ''}
97
+ </div>
98
+ `).join('');
99
+ });
100
+ }
101
+
102
+ function removeScopeItem(priority, index) {
103
+ data.cadrage.scope[priority].splice(index, 1);
104
+ renderScope();
105
+ autoSave();
106
+ }
107
+
108
+ /* ============================================
109
+ ACCEPTANCE CRITERIA
110
+ ============================================ */
111
+ function addCriterion() {
112
+ const text = document.getElementById('criterion-text').value.trim();
113
+ if (!text) return;
114
+ if (!data.cadrage.criteria) data.cadrage.criteria = [];
115
+ data.cadrage.criteria.push({ text, validated: false });
116
+ renderCriteria();
117
+ toggleForm('addCriterionForm');
118
+ document.getElementById('criterion-text').value = '';
119
+ autoSave();
120
+ }
121
+
122
+ function removeCriterion(index) {
123
+ data.cadrage.criteria.splice(index, 1);
124
+ renderCriteria();
125
+ autoSave();
126
+ }
127
+
128
+ function toggleCriterion(index) {
129
+ data.cadrage.criteria[index].validated = !data.cadrage.criteria[index].validated;
130
+ renderCriteria();
131
+ autoSave();
132
+ }
133
+
134
+ function renderCriteria() {
135
+ const container = document.getElementById('criteriaList');
136
+ if (!container) return;
137
+ const criteria = data.cadrage.criteria || [];
138
+ container.innerHTML = criteria.map((c, i) => `
139
+ <div class="uc-item" style="display:flex;align-items:center;gap:0.75rem;">
140
+ <input type="checkbox" ${c.validated ? 'checked' : ''} onchange="toggleCriterion(${i})" style="cursor:pointer;width:18px;height:18px;flex-shrink:0;">
141
+ <span style="flex:1;${c.validated ? 'text-decoration:line-through;color:var(--text-muted);' : ''}">${c.text}</span>
142
+ <button class="btn btn-sm" onclick="removeCriterion(${i})" style="opacity:0.5;flex-shrink:0;">&#10005;</button>
143
+ </div>
144
+ `).join('');
145
+ }
146
+
147
+ /* ============================================
148
+ RISKS
149
+ ============================================ */
150
+ function addRisk() {
151
+ const desc = document.getElementById('risk-desc').value.trim();
152
+ if (!desc) return;
153
+
154
+ data.cadrage.risks.push({
155
+ description: desc,
156
+ probability: document.getElementById('risk-probability').value,
157
+ impact: document.getElementById('risk-impact').value,
158
+ mitigation: document.getElementById('risk-mitigation').value.trim()
159
+ });
160
+
161
+ renderRisks();
162
+ toggleForm('addRiskForm');
163
+ clearForm('addRiskForm');
164
+ autoSave();
165
+ }
166
+
167
+ function renderRisks() {
168
+ const list = document.getElementById('risksList');
169
+ list.innerHTML = data.cadrage.risks.map((r, i) => {
170
+ const level = (r.probability === 'high' && r.impact === 'high') ? 'critical'
171
+ : (r.probability === 'low' && r.impact === 'low') ? 'low' : 'medium';
172
+ return `
173
+ <div class="risk-item">
174
+ <div class="risk-level risk-${level}"></div>
175
+ <div>
176
+ <div class="risk-text">${r.description}</div>
177
+ ${r.mitigation ? '<div style="font-size:0.75rem;color:var(--text-muted);margin-top:0.25rem;">Prevention : ' + r.mitigation + '</div>' : ''}
178
+ </div>
179
+ <div class="risk-probability">${formatLevel(r.probability)}</div>
180
+ <div class="risk-impact">${formatLevel(r.impact)}</div>
181
+ </div>
182
+ `;
183
+ }).join('');
184
+ }
185
+
186
+ /* ============================================
187
+ PROCESS STEPS
188
+ ============================================ */
189
+ function addProcessStep() {
190
+ const label = prompt('Nom de l\'etape :');
191
+ if (!label) return;
192
+ if (!data.cadrage.current.steps) data.cadrage.current.steps = [];
193
+ data.cadrage.current.steps.push(label);
194
+ renderProcessFlow();
195
+ autoSave();
196
+ }
197
+
198
+ function renderProcessFlow() {
199
+ const container = document.getElementById('processFlow');
200
+ const steps = data.cadrage.current.steps || [];
201
+ container.innerHTML = steps.map((step, i) => `
202
+ <div class="process-step">
203
+ <div class="process-step-number">Etape ${i + 1}</div>
204
+ <div class="process-step-label">${step}</div>
205
+ </div>
206
+ ${i < steps.length - 1 ? '<div class="process-arrow">&#8594;</div>' : ''}
207
+ `).join('');
208
+ }
@@ -0,0 +1,211 @@
1
+ /* ============================================
2
+ MODULE MANAGEMENT
3
+ ============================================ */
4
+ function addModule() {
5
+ const name = document.getElementById('mod-name').value.trim();
6
+ if (!name) return;
7
+ const code = name.replace(/[^a-zA-Z0-9]/g, '');
8
+
9
+ data.modules.push({
10
+ code: code,
11
+ name: name,
12
+ description: document.getElementById('mod-desc').value.trim(),
13
+ featureType: document.getElementById('mod-type').value,
14
+ priority: document.getElementById('mod-priority').value,
15
+ entities: document.getElementById('mod-entities').value.split('\n').filter(e => e.trim()),
16
+ status: 'pending'
17
+ });
18
+
19
+ // Initialize module spec
20
+ if (!data.moduleSpecs[code]) {
21
+ data.moduleSpecs[code] = {
22
+ useCases: [],
23
+ businessRules: [],
24
+ entities: [],
25
+ permissions: [],
26
+ notes: ''
27
+ };
28
+ }
29
+
30
+ renderModules();
31
+ renderDependencies();
32
+ renderAllModuleSpecs();
33
+ toggleForm('addModuleForm');
34
+ clearForm('addModuleForm');
35
+ updateCounts();
36
+ autoSave();
37
+ }
38
+
39
+ function removeModule(index) {
40
+ if (!confirm('Supprimer ce domaine fonctionnel ?')) return;
41
+ const code = data.modules[index].code;
42
+ data.modules.splice(index, 1);
43
+ delete data.moduleSpecs[code];
44
+ data.dependencies = data.dependencies.filter(d => d.from !== code && d.to !== code);
45
+ renderModules();
46
+ renderDependencies();
47
+ renderAllModuleSpecs();
48
+ updateCounts();
49
+ autoSave();
50
+ }
51
+
52
+ function renderModules() {
53
+ const grid = document.getElementById('moduleGrid');
54
+ grid.innerHTML = data.modules.map((m, i) => `
55
+ <div class="module-card" onclick="showSection('module-spec-${m.code}')">
56
+ <button class="module-card-remove" onclick="event.stopPropagation();removeModule(${i})">&#10005;</button>
57
+ <div class="module-card-header">
58
+ <span class="module-card-code">${m.name}</span>
59
+ <span class="module-card-type">${formatModuleType(m.featureType)}</span>
60
+ </div>
61
+ <div class="module-card-desc">${m.description || ''}</div>
62
+ <div class="module-card-meta">
63
+ <span class="priority priority-${m.priority === 'must' ? 'vital' : m.priority === 'should' ? 'important' : 'optional'}">${formatModulePriority(m.priority)}</span>
64
+ <span>${(m.entities || []).length} donnees</span>
65
+ <span>${(data.moduleSpecs[m.code]?.useCases || []).length} cas d'utilisation</span>
66
+ </div>
67
+ </div>
68
+ `).join('') || '<p style="color:var(--text-muted);text-align:center;padding:2rem;grid-column:1/-1;">Aucun domaine fonctionnel defini. Cliquez sur le bouton ci-dessous pour commencer.</p>';
69
+ }
70
+
71
+ function updateModulesNav() {
72
+ const nav = document.getElementById('modulesNav');
73
+ const navItems = data.modules.map(m => `
74
+ <a class="nav-item" onclick="showSection('module-spec-${m.code}')" data-section="module-spec-${m.code}">
75
+ <span class="nav-icon">&#9679;</span> ${m.name}
76
+ <span class="nav-badge">${(data.moduleSpecs[m.code]?.useCases || []).length}</span>
77
+ </a>
78
+ `).join('');
79
+ nav.innerHTML = '<div class="nav-group-title">3. Specification</div>' + (navItems || '<div style="padding:0.3rem 1rem;font-size:0.8rem;color:var(--text-muted);font-style:italic;">Aucun domaine</div>');
80
+ }
81
+
82
+ /* ============================================
83
+ DEPENDENCY MANAGEMENT
84
+ ============================================ */
85
+ function updateDepSelects() {
86
+ const fromSel = document.getElementById('dep-from');
87
+ const toSel = document.getElementById('dep-to');
88
+ if (!fromSel || !toSel) return;
89
+ const opts = data.modules.map(m => `<option value="${m.code}">${m.name}</option>`).join('');
90
+ fromSel.innerHTML = opts;
91
+ toSel.innerHTML = opts;
92
+ }
93
+
94
+ function addDependency() {
95
+ const from = document.getElementById('dep-from').value;
96
+ const to = document.getElementById('dep-to').value;
97
+ if (!from || !to || from === to) return;
98
+ if (data.dependencies.some(d => d.from === from && d.to === to)) return;
99
+
100
+ data.dependencies.push({
101
+ from: from,
102
+ to: to,
103
+ description: document.getElementById('dep-desc').value.trim()
104
+ });
105
+ document.getElementById('dep-desc').value = '';
106
+
107
+ renderDependencies();
108
+ autoSave();
109
+ }
110
+
111
+ function removeDependency(index) {
112
+ data.dependencies.splice(index, 1);
113
+ renderDependencies();
114
+ autoSave();
115
+ }
116
+
117
+ function renderDependencies() {
118
+ // Render dependency list
119
+ const depList = document.getElementById('depList');
120
+ if (depList) {
121
+ depList.innerHTML = data.dependencies.map((d, i) => {
122
+ const fromName = data.modules.find(m => m.code === d.from)?.name || d.from;
123
+ const toName = data.modules.find(m => m.code === d.to)?.name || d.to;
124
+ return `
125
+ <div class="interaction-item">
126
+ <span style="font-weight:600;color:var(--text-bright);">${fromName}</span>
127
+ <span class="interaction-arrow">&#8594;</span>
128
+ <span style="font-weight:600;color:var(--text-bright);">${toName}</span>
129
+ <span style="flex:1;font-size:0.8rem;color:var(--text-muted);">${d.description || ''}</span>
130
+ <button class="btn btn-sm" onclick="removeDependency(${i})" style="opacity:0.5;">Supprimer</button>
131
+ </div>
132
+ `;
133
+ }).join('');
134
+ }
135
+
136
+ // Render graph
137
+ renderDepGraph();
138
+ // Render processing order
139
+ renderProcessingOrder();
140
+ }
141
+
142
+ function renderDepGraph() {
143
+ const graph = document.getElementById('depGraph');
144
+ if (!graph || data.modules.length === 0) return;
145
+
146
+ const layers = computeTopologicalLayers();
147
+ if (!layers) {
148
+ graph.innerHTML = '<p style="color:var(--error);text-align:center;padding:1rem;">Dependance circulaire detectee ! Corrigez les dependances.</p>';
149
+ return;
150
+ }
151
+
152
+ graph.innerHTML = layers.map((layer, i) => `
153
+ <div class="dep-layer">
154
+ <div class="dep-layer-label">Couche ${i + 1}</div>
155
+ <div class="dep-layer-modules">
156
+ ${layer.map(code => {
157
+ const m = data.modules.find(mod => mod.code === code);
158
+ return `<div class="dep-module">${m ? (m.name || m.code) : code}</div>`;
159
+ }).join('')}
160
+ </div>
161
+ </div>
162
+ ${i < layers.length - 1 ? '<div class="dep-arrow">&#8595;</div>' : ''}
163
+ `).join('');
164
+ }
165
+
166
+ function computeTopologicalLayers() {
167
+ const codes = data.modules.map(m => m.code);
168
+ const inDeg = {};
169
+ const adj = {};
170
+ codes.forEach(c => { inDeg[c] = 0; adj[c] = []; });
171
+ data.dependencies.forEach(d => {
172
+ if (inDeg[d.from] !== undefined && adj[d.to] !== undefined) {
173
+ adj[d.to].push(d.from);
174
+ inDeg[d.from]++;
175
+ }
176
+ });
177
+
178
+ const layers = [];
179
+ let remaining = new Set(codes);
180
+ while (remaining.size > 0) {
181
+ const layer = [];
182
+ remaining.forEach(c => {
183
+ if (inDeg[c] === 0) layer.push(c);
184
+ });
185
+ if (layer.length === 0) return null; // cycle
186
+ layers.push(layer);
187
+ layer.forEach(c => {
188
+ remaining.delete(c);
189
+ (adj[c] || []).forEach(dep => { inDeg[dep]--; });
190
+ });
191
+ }
192
+ return layers;
193
+ }
194
+
195
+ function renderProcessingOrder() {
196
+ const container = document.getElementById('processingOrder');
197
+ if (!container) return;
198
+ const layers = computeTopologicalLayers();
199
+ if (!layers) { container.innerHTML = ''; return; }
200
+ const order = layers.flat();
201
+ container.innerHTML = order.map((code, i) => {
202
+ const m = data.modules.find(mod => mod.code === code);
203
+ return `
204
+ <div class="process-step">
205
+ <div class="process-step-number">Etape ${i + 1}</div>
206
+ <div class="process-step-label">${m ? (m.name || m.code) : code}</div>
207
+ </div>
208
+ ${i < order.length - 1 ? '<div class="process-arrow">&#8594;</div>' : ''}
209
+ `;
210
+ }).join('');
211
+ }