@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,110 @@
1
+ /* ============================================
2
+ CONSOLIDATION
3
+ ============================================ */
4
+ function renderConsolidation() {
5
+ renderConsolInteractions();
6
+ renderConsolPermissions();
7
+ }
8
+
9
+ function renderConsolInteractions() {
10
+ const container = document.getElementById('consolInteractions');
11
+ if (!container || data.dependencies.length === 0) return;
12
+
13
+ container.innerHTML = data.dependencies.map(d => {
14
+ const fromName = data.modules.find(m => m.code === d.from)?.name || d.from;
15
+ const toName = data.modules.find(m => m.code === d.to)?.name || d.to;
16
+ return `
17
+ <div class="interaction-item">
18
+ <span style="font-weight:600;color:var(--text-bright);">${fromName}</span>
19
+ <span class="interaction-arrow">&#8594;</span>
20
+ <span style="font-weight:600;color:var(--text-bright);">${toName}</span>
21
+ <span class="interaction-type">Dependance</span>
22
+ <span style="flex:1;font-size:0.8rem;color:var(--text-muted);">${d.description || ''}</span>
23
+ </div>`;
24
+ }).join('');
25
+ }
26
+
27
+ function renderConsolPermissions() {
28
+ const container = document.getElementById('consolPermissions');
29
+ if (!container || data.modules.length === 0) return;
30
+
31
+ const roles = getPermRoles();
32
+
33
+ let html = '<table class="mock-table" style="background:var(--bg-card);border-radius:8px;overflow:hidden;">';
34
+ html += '<thead><tr><th>Profil</th>';
35
+ data.modules.forEach(m => { html += `<th style="text-align:center;">${m.name || m.code}</th>`; });
36
+ html += '</tr></thead><tbody>';
37
+
38
+ roles.forEach(role => {
39
+ html += `<tr><td style="font-weight:500;color:var(--text-bright);">${role}</td>`;
40
+ data.modules.forEach(m => {
41
+ const allPerms = data.moduleSpecs[m.code]?.permissions || [];
42
+ const hasWildcard = allPerms.includes(role + '|*');
43
+ const perms = hasWildcard
44
+ ? getPermActions()
45
+ : allPerms.filter(p => p.startsWith(role + '|'));
46
+ const count = perms.length;
47
+ const color = count > 4 ? 'var(--success)' : count > 2 ? 'var(--warning)' : count > 0 ? 'var(--text-muted)' : 'var(--border)';
48
+ const label = hasWildcard ? 'Tous' : (count > 0 ? count + ' droits' : 'Aucun');
49
+ html += `<td style="text-align:center;color:${color};font-weight:600;">${label}</td>`;
50
+ });
51
+ html += '</tr>';
52
+ });
53
+
54
+ html += '</tbody></table>';
55
+ container.innerHTML = html;
56
+ }
57
+
58
+ /* ============================================
59
+ E2E FLOWS
60
+ ============================================ */
61
+ function addE2EFlow() {
62
+ const name = document.getElementById('flow-name').value.trim();
63
+ if (!name) return;
64
+
65
+ const steps = document.getElementById('flow-steps').value.split('\n').filter(l => l.trim()).map(l => {
66
+ const parts = l.split(' - ');
67
+ return { module: parts[0]?.trim() || '', action: parts.slice(1).join(' - ').trim() || l.trim() };
68
+ });
69
+
70
+ data.consolidation.e2eFlows.push({
71
+ name: name,
72
+ steps: steps,
73
+ actors: document.getElementById('flow-actors').value.trim()
74
+ });
75
+
76
+ renderE2EFlows();
77
+ toggleForm('addFlowForm');
78
+ clearForm('addFlowForm');
79
+ autoSave();
80
+ }
81
+
82
+ function removeE2EFlow(index) {
83
+ data.consolidation.e2eFlows.splice(index, 1);
84
+ renderE2EFlows();
85
+ autoSave();
86
+ }
87
+
88
+ function renderE2EFlows() {
89
+ const container = document.getElementById('e2eFlowsList');
90
+ if (!container) return;
91
+
92
+ container.innerHTML = data.consolidation.e2eFlows.map((flow, fi) => `
93
+ <div class="card" style="margin-bottom:1rem;">
94
+ <div class="card-header">
95
+ <span class="card-title">${flow.name}</span>
96
+ <button class="btn btn-sm" onclick="removeE2EFlow(${fi})" style="opacity:0.5;">Supprimer</button>
97
+ </div>
98
+ ${flow.actors ? `<div style="font-size:0.8rem;color:var(--text-muted);margin-bottom:0.5rem;">Intervenants : ${flow.actors}</div>` : ''}
99
+ <div class="e2e-flow">
100
+ ${flow.steps.map((s, i) => `
101
+ <div class="e2e-step">
102
+ <div class="e2e-step-module">${s.module}</div>
103
+ <div class="e2e-step-action">${s.action}</div>
104
+ </div>
105
+ ${i < flow.steps.length - 1 ? '<div class="process-arrow">&#8594;</div>' : ''}
106
+ `).join('')}
107
+ </div>
108
+ </div>
109
+ `).join('');
110
+ }
@@ -0,0 +1,90 @@
1
+ /* ============================================
2
+ HANDOFF / SYNTHESE
3
+ ============================================ */
4
+ function renderHandoff() {
5
+ renderHandoffStats();
6
+ renderHandoffModules();
7
+ renderCoverageMatrix();
8
+ }
9
+
10
+ function renderHandoffStats() {
11
+ const container = document.getElementById('handoffStats');
12
+ if (!container) return;
13
+
14
+ const totalUCs = data.modules.reduce((sum, m) => sum + (data.moduleSpecs[m.code]?.useCases || []).length, 0);
15
+ const totalBRs = data.modules.reduce((sum, m) => sum + (data.moduleSpecs[m.code]?.businessRules || []).length, 0);
16
+ const totalEnts = data.modules.reduce((sum, m) => sum + (data.moduleSpecs[m.code]?.entities || []).length, 0);
17
+ const totalStakeholders = data.cadrage.stakeholders.length;
18
+
19
+ container.innerHTML = `
20
+ <div class="stat-card"><div class="stat-value">${data.modules.length}</div><div class="stat-label">Domaines fonctionnels</div></div>
21
+ <div class="stat-card"><div class="stat-value">${totalUCs}</div><div class="stat-label">Cas d'utilisation</div></div>
22
+ <div class="stat-card"><div class="stat-value">${totalBRs}</div><div class="stat-label">Regles metier</div></div>
23
+ <div class="stat-card"><div class="stat-value">${totalEnts}</div><div class="stat-label">Types de donnees</div></div>
24
+ <div class="stat-card"><div class="stat-value">${totalStakeholders}</div><div class="stat-label">Profils utilisateurs</div></div>
25
+ <div class="stat-card"><div class="stat-value">${data.dependencies.length}</div><div class="stat-label">Dependances</div></div>
26
+ <div class="stat-card"><div class="stat-value">${data.consolidation.e2eFlows.length}</div><div class="stat-label">Parcours bout en bout</div></div>
27
+ <div class="stat-card"><div class="stat-value">${data.cadrage.risks.length}</div><div class="stat-label">Risques identifies</div></div>
28
+ `;
29
+ }
30
+
31
+ function renderHandoffModules() {
32
+ const container = document.getElementById('handoffModuleList');
33
+ if (!container) return;
34
+
35
+ const layers = computeTopologicalLayers();
36
+ const order = layers ? layers.flat() : data.modules.map(m => m.code);
37
+
38
+ container.innerHTML = order.map((code, i) => {
39
+ const m = data.modules.find(mod => mod.code === code);
40
+ if (!m) return '';
41
+ const spec = data.moduleSpecs[code] || {};
42
+ return `
43
+ <div class="card" style="margin-bottom:0.75rem;">
44
+ <div style="display:flex;align-items:center;gap:0.75rem;">
45
+ <div style="width:28px;height:28px;border-radius:50%;background:var(--primary);display:flex;align-items:center;justify-content:center;color:#fff;font-size:0.75rem;font-weight:700;flex-shrink:0;">${i + 1}</div>
46
+ <div style="flex:1;">
47
+ <div style="font-weight:600;color:var(--text-bright);">${m.name}</div>
48
+ <div style="font-size:0.8rem;color:var(--text-muted);">${m.description || ''}</div>
49
+ </div>
50
+ <div style="display:flex;gap:1rem;font-size:0.75rem;color:var(--text-muted);">
51
+ <span>${(spec.useCases || []).length} cas d'utilisation</span>
52
+ <span>${(spec.businessRules || []).length} regles</span>
53
+ <span>${(spec.entities || []).length} donnees</span>
54
+ </div>
55
+ <span class="priority priority-${m.priority === 'must' ? 'vital' : m.priority === 'should' ? 'important' : 'optional'}">${formatModulePriority(m.priority)}</span>
56
+ </div>
57
+ </div>`;
58
+ }).join('');
59
+ }
60
+
61
+ function renderCoverageMatrix() {
62
+ const container = document.getElementById('coverageMatrix');
63
+ if (!container) return;
64
+
65
+ const allScope = ['vital', 'important', 'optional'].flatMap(p =>
66
+ (data.cadrage.scope[p] || []).map(item => ({ ...item, priority: p }))
67
+ );
68
+
69
+ if (allScope.length === 0) {
70
+ container.innerHTML = '<p style="color:var(--text-muted);font-style:italic;">Aucun element de perimetre defini dans le cadrage.</p>';
71
+ return;
72
+ }
73
+
74
+ container.innerHTML = `
75
+ <table class="mock-table" style="background:var(--bg-card);border-radius:8px;overflow:hidden;">
76
+ <thead><tr><th>Besoin</th><th>Priorite</th><th>Domaine</th><th>Couvert</th></tr></thead>
77
+ <tbody>
78
+ ${allScope.map(item => {
79
+ const moduleName = data.modules.length > 0 ? data.modules[0].name : 'A definir';
80
+ return `
81
+ <tr>
82
+ <td>${item.name}</td>
83
+ <td><span class="priority priority-${item.priority}">${formatPriority(item.priority)}</span></td>
84
+ <td style="color:var(--text-muted);">${moduleName}</td>
85
+ <td style="text-align:center;color:var(--success);">&#10003;</td>
86
+ </tr>`;
87
+ }).join('')}
88
+ </tbody>
89
+ </table>`;
90
+ }
@@ -0,0 +1,45 @@
1
+ /* ============================================
2
+ PERSISTENCE
3
+ ============================================ */
4
+ function autoSave() {
5
+ data.metadata.lastModified = new Date().toISOString();
6
+ localStorage.setItem(APP_KEY, JSON.stringify(data));
7
+ }
8
+
9
+ function saveToLocalStorage() {
10
+ autoSave();
11
+ showNotification('Modifications sauvegardees');
12
+ }
13
+
14
+ function loadFromLocalStorage() {
15
+ const saved = localStorage.getItem(APP_KEY);
16
+ if (saved) {
17
+ try {
18
+ const parsed = JSON.parse(saved);
19
+ // Deep merge with defaults
20
+ data.metadata = { ...data.metadata, ...parsed.metadata };
21
+ data.cadrage = {
22
+ ...data.cadrage,
23
+ ...parsed.cadrage,
24
+ scope: { ...data.cadrage.scope, ...(parsed.cadrage?.scope || {}) }
25
+ };
26
+ data.modules = parsed.modules || [];
27
+ data.dependencies = parsed.dependencies || [];
28
+ data.moduleSpecs = parsed.moduleSpecs || {};
29
+ data.consolidation = { ...data.consolidation, ...(parsed.consolidation || {}) };
30
+ data.handoff = parsed.handoff || {};
31
+ data.wireframeComments = parsed.wireframeComments || {};
32
+ data.specComments = parsed.specComments || {};
33
+ data.customRoles = parsed.customRoles || [];
34
+ data.customActions = parsed.customActions || [];
35
+ data.comments = parsed.comments || [];
36
+
37
+ // Restore editable fields
38
+ document.querySelectorAll('.editable[data-field]').forEach(el => {
39
+ const value = getNestedValue(data, 'cadrage.' + el.dataset.field);
40
+ if (value) el.textContent = value;
41
+ });
42
+ renderProcessFlow();
43
+ } catch (e) { console.error('Error loading saved data:', e); }
44
+ }
45
+ }
@@ -0,0 +1,168 @@
1
+ /* ============================================
2
+ EXPORT JSON
3
+ ============================================ */
4
+ function exportJSON() {
5
+ // Collect all editable fields (cadrage)
6
+ document.querySelectorAll('.editable[data-field]').forEach(el => {
7
+ setNestedValue(data, 'cadrage.' + el.dataset.field, el.textContent.trim());
8
+ });
9
+
10
+ // Collect module editable fields
11
+ document.querySelectorAll('.editable[data-module-field]').forEach(el => {
12
+ const code = el.dataset.moduleCode;
13
+ const field = el.dataset.moduleField;
14
+ if (data.moduleSpecs[code]) {
15
+ data.moduleSpecs[code][field] = el.textContent.trim();
16
+ }
17
+ });
18
+
19
+ data.metadata.lastModified = new Date().toISOString();
20
+ data.metadata.exportedAt = new Date().toISOString();
21
+
22
+ // Build complete export with structured data
23
+ const exportData = {
24
+ metadata: data.metadata,
25
+ cadrage: data.cadrage,
26
+ modules: data.modules,
27
+ dependencies: data.dependencies,
28
+ moduleSpecifications: {},
29
+ consolidation: data.consolidation,
30
+ artifacts: EMBEDDED_ARTIFACTS,
31
+ wireframeComments: data.wireframeComments,
32
+ specComments: data.specComments,
33
+ customRoles: data.customRoles,
34
+ customActions: data.customActions,
35
+ comments: data.comments
36
+ };
37
+
38
+ // Structure module specs for export
39
+ data.modules.forEach(m => {
40
+ const spec = data.moduleSpecs[m.code] || {};
41
+ exportData.moduleSpecifications[m.code] = {
42
+ module: m,
43
+ useCases: (spec.useCases || []).map((uc, i) => ({
44
+ id: 'UC-' + String(i + 1).padStart(3, '0'),
45
+ ...uc
46
+ })),
47
+ businessRules: (spec.businessRules || []).map((br, i) => ({
48
+ id: 'BR-' + br.category.toUpperCase().substring(0, 4) + '-' + String(i + 1).padStart(3, '0'),
49
+ ...br
50
+ })),
51
+ entities: spec.entities || [],
52
+ permissions: spec.permissions || [],
53
+ notes: spec.notes || ''
54
+ };
55
+ });
56
+
57
+ const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
58
+ const url = URL.createObjectURL(blob);
59
+ const a = document.createElement('a');
60
+ a.href = url;
61
+ a.download = (data.metadata.applicationId || 'analyse') + '-export.json';
62
+ a.click();
63
+ URL.revokeObjectURL(url);
64
+ showNotification('Export JSON telecharge');
65
+ }
66
+
67
+ /* ============================================
68
+ SAVE REVIEW JSON (corrections for new version)
69
+ ============================================ */
70
+ function detectChanges(original, current) {
71
+ const changes = { cadrage: false, modulesAdded: [], modulesRemoved: [], modulesModified: [], commentsCount: 0 };
72
+
73
+ // Cadrage changes
74
+ if (JSON.stringify(original.cadrage) !== JSON.stringify(current.cadrage)) {
75
+ changes.cadrage = true;
76
+ }
77
+
78
+ // Module changes
79
+ const origCodes = (original.modules || []).map(m => m.code);
80
+ const currCodes = (current.modules || []).map(m => m.code);
81
+ changes.modulesAdded = currCodes.filter(c => !origCodes.includes(c));
82
+ changes.modulesRemoved = origCodes.filter(c => !currCodes.includes(c));
83
+ currCodes.filter(c => origCodes.includes(c)).forEach(code => {
84
+ const origSpec = JSON.stringify(original.moduleSpecs?.[code] || {});
85
+ const currSpec = JSON.stringify(current.moduleSpecs?.[code] || {});
86
+ if (origSpec !== currSpec) changes.modulesModified.push(code);
87
+ });
88
+
89
+ // Comments count
90
+ changes.commentsCount = (current.comments || []).length;
91
+
92
+ return changes;
93
+ }
94
+
95
+ function saveReviewJSON() {
96
+ // Collect all editable fields (same as exportJSON)
97
+ document.querySelectorAll('.editable[data-field]').forEach(el => {
98
+ setNestedValue(data, 'cadrage.' + el.dataset.field, el.textContent.trim());
99
+ });
100
+ document.querySelectorAll('.editable[data-module-field]').forEach(el => {
101
+ const code = el.dataset.moduleCode;
102
+ const field = el.dataset.moduleField;
103
+ if (data.moduleSpecs[code]) {
104
+ data.moduleSpecs[code][field] = el.textContent.trim();
105
+ }
106
+ });
107
+
108
+ const changes = detectChanges(ORIGINAL_DATA, data);
109
+ const hasChanges = changes.cadrage || changes.modulesAdded.length > 0
110
+ || changes.modulesRemoved.length > 0 || changes.modulesModified.length > 0
111
+ || changes.commentsCount > 0;
112
+
113
+ // Build review export with metadata envelope
114
+ const reviewData = {
115
+ _reviewMeta: {
116
+ sourceVersion: data.metadata.version || '1.0',
117
+ sourceApplicationId: data.metadata.applicationId || '',
118
+ exportedAt: new Date().toISOString(),
119
+ hasChanges: hasChanges,
120
+ changeSummary: {
121
+ cadrageModified: changes.cadrage,
122
+ modulesAdded: changes.modulesAdded,
123
+ modulesRemoved: changes.modulesRemoved,
124
+ modulesModified: changes.modulesModified,
125
+ commentsCount: changes.commentsCount
126
+ }
127
+ },
128
+ metadata: data.metadata,
129
+ cadrage: data.cadrage,
130
+ modules: data.modules,
131
+ dependencies: data.dependencies,
132
+ moduleSpecifications: {},
133
+ consolidation: data.consolidation,
134
+ wireframeComments: data.wireframeComments,
135
+ specComments: data.specComments,
136
+ customRoles: data.customRoles,
137
+ customActions: data.customActions,
138
+ comments: data.comments
139
+ };
140
+
141
+ // Structure module specs (same logic as exportJSON)
142
+ data.modules.forEach(m => {
143
+ const spec = data.moduleSpecs[m.code] || {};
144
+ reviewData.moduleSpecifications[m.code] = {
145
+ module: m,
146
+ useCases: (spec.useCases || []).map((uc, i) => ({
147
+ id: 'UC-' + String(i + 1).padStart(3, '0'),
148
+ ...uc
149
+ })),
150
+ businessRules: (spec.businessRules || []).map((br, i) => ({
151
+ id: 'BR-' + br.category.toUpperCase().substring(0, 4) + '-' + String(i + 1).padStart(3, '0'),
152
+ ...br
153
+ })),
154
+ entities: spec.entities || [],
155
+ permissions: spec.permissions || [],
156
+ notes: spec.notes || ''
157
+ };
158
+ });
159
+
160
+ const blob = new Blob([JSON.stringify(reviewData, null, 2)], { type: 'application/json' });
161
+ const url = URL.createObjectURL(blob);
162
+ const a = document.createElement('a');
163
+ a.href = url;
164
+ a.download = 'ba-review.json';
165
+ a.click();
166
+ URL.revokeObjectURL(url);
167
+ showNotification('ba-review.json telecharge — sauvegardez-le a cote du HTML');
168
+ }
@@ -0,0 +1,171 @@
1
+ /* ============================================
2
+ INLINE COMMENTS SYSTEM
3
+ ============================================ */
4
+
5
+ /**
6
+ * Comments are stored in data.comments[] with structure:
7
+ * { id, sectionId, cardIndex, author, timestamp, content, status, category }
8
+ *
9
+ * - sectionId: matches the section div id (e.g. "cadrage-problem", "module-spec-Clients")
10
+ * - cardIndex: index of the card within that section (0-based)
11
+ * - status: "to-review" | "validated"
12
+ * - category: "clarification" | "correction" | "suggestion"
13
+ */
14
+
15
+ function initInlineComments() {
16
+ // Add comment buttons under each card and uc-item (direct children of sections)
17
+ document.querySelectorAll('.section').forEach(section => {
18
+ const sectionId = section.id;
19
+ section.querySelectorAll(':scope > .card, :scope > .uc-item').forEach((card, index) => {
20
+ if (card.querySelector('.comment-btn-container')) return;
21
+ card.appendChild(createCommentUI(sectionId, index));
22
+ });
23
+ });
24
+ // Second pass: nested list containers in module specs (ucList-*, brList-*, entList-*)
25
+ document.querySelectorAll('[id^="ucList-"], [id^="brList-"], [id^="entList-"]').forEach(list => {
26
+ const listId = list.id;
27
+ list.querySelectorAll(':scope > .uc-item, :scope > .entity-block, :scope > div').forEach((item, index) => {
28
+ if (item.querySelector('.comment-btn-container')) return;
29
+ item.appendChild(createCommentUI(listId, index));
30
+ });
31
+ });
32
+ }
33
+
34
+ function createCommentUI(sectionId, cardIndex) {
35
+ const comments = getCommentsForCard(sectionId, cardIndex);
36
+ const count = comments.length;
37
+
38
+ const container = document.createElement('div');
39
+ container.className = 'comment-btn-container';
40
+ container.dataset.sectionId = sectionId;
41
+ container.dataset.cardIndex = cardIndex;
42
+
43
+ container.innerHTML = `
44
+ <button class="comment-toggle-btn" onclick="toggleCommentThread('${sectionId}', ${cardIndex})">
45
+ Commentaires <span class="comment-count ${count === 0 ? 'empty' : ''}">${count}</span>
46
+ </button>
47
+ <div class="comment-thread" id="comment-thread-${sectionId}-${cardIndex}">
48
+ <div class="comment-items" id="comment-items-${sectionId}-${cardIndex}">
49
+ ${renderCommentItems(sectionId, cardIndex)}
50
+ </div>
51
+ <div class="comment-add-form">
52
+ <textarea id="comment-text-${sectionId}-${cardIndex}" placeholder="Ajouter un commentaire..."></textarea>
53
+ <select id="comment-cat-${sectionId}-${cardIndex}">
54
+ <option value="clarification">Clarification</option>
55
+ <option value="correction">Correction</option>
56
+ <option value="suggestion">Suggestion</option>
57
+ </select>
58
+ <button onclick="addInlineComment('${sectionId}', ${cardIndex})">Ajouter</button>
59
+ </div>
60
+ </div>
61
+ `;
62
+
63
+ return container;
64
+ }
65
+
66
+ function getCommentsForCard(sectionId, cardIndex) {
67
+ return (data.comments || []).filter(c =>
68
+ c.sectionId === sectionId && c.cardIndex === cardIndex
69
+ );
70
+ }
71
+
72
+ function toggleCommentThread(sectionId, cardIndex) {
73
+ const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
74
+ if (thread) thread.classList.toggle('visible');
75
+ }
76
+
77
+ function renderCommentItems(sectionId, cardIndex) {
78
+ const comments = getCommentsForCard(sectionId, cardIndex);
79
+ if (comments.length === 0) {
80
+ return '<div style="font-size:0.8rem;color:var(--text-muted);padding:0.5rem 0;font-style:italic;">Aucun commentaire</div>';
81
+ }
82
+
83
+ return comments.map((c, i) => {
84
+ const initials = (c.author || 'U').substring(0, 2).toUpperCase();
85
+ const date = c.timestamp ? new Date(c.timestamp).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) : '';
86
+ const globalIndex = data.comments.indexOf(c);
87
+
88
+ return `
89
+ <div class="comment-item">
90
+ <div class="comment-avatar">${initials}</div>
91
+ <div class="comment-body">
92
+ <div class="comment-meta">
93
+ <span class="comment-author">${c.author || 'Utilisateur'}</span>
94
+ <span class="comment-date">${date}</span>
95
+ <span class="comment-category comment-category-${c.category}">${c.category}</span>
96
+ <span class="comment-status comment-status-${c.status}">${c.status === 'validated' ? 'Valide' : 'A revoir'}</span>
97
+ </div>
98
+ <div class="comment-text">${c.content}</div>
99
+ <div class="comment-actions">
100
+ <button class="comment-action-btn" onclick="toggleCommentStatus(${globalIndex})">${c.status === 'validated' ? 'Remettre a revoir' : 'Valider'}</button>
101
+ <button class="comment-action-btn" onclick="deleteComment(${globalIndex})" style="color:var(--error);">Supprimer</button>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ `;
106
+ }).join('');
107
+ }
108
+
109
+ function addInlineComment(sectionId, cardIndex) {
110
+ const textEl = document.getElementById('comment-text-' + sectionId + '-' + cardIndex);
111
+ const catEl = document.getElementById('comment-cat-' + sectionId + '-' + cardIndex);
112
+ const content = textEl.value.trim();
113
+ if (!content) return;
114
+
115
+ const comment = {
116
+ id: 'comment-' + Date.now(),
117
+ sectionId: sectionId,
118
+ cardIndex: cardIndex,
119
+ author: 'Utilisateur',
120
+ timestamp: new Date().toISOString(),
121
+ content: content,
122
+ status: 'to-review',
123
+ category: catEl.value
124
+ };
125
+
126
+ data.comments.push(comment);
127
+ textEl.value = '';
128
+
129
+ refreshCommentUI(sectionId, cardIndex);
130
+ renderReviewPanel();
131
+ autoSave();
132
+ }
133
+
134
+ function toggleCommentStatus(globalIndex) {
135
+ const comment = data.comments[globalIndex];
136
+ if (!comment) return;
137
+ comment.status = comment.status === 'validated' ? 'to-review' : 'validated';
138
+ refreshCommentUI(comment.sectionId, comment.cardIndex);
139
+ renderReviewPanel();
140
+ autoSave();
141
+ }
142
+
143
+ function deleteComment(globalIndex) {
144
+ const comment = data.comments[globalIndex];
145
+ if (!comment) return;
146
+ const sectionId = comment.sectionId;
147
+ const cardIndex = comment.cardIndex;
148
+ data.comments.splice(globalIndex, 1);
149
+ refreshCommentUI(sectionId, cardIndex);
150
+ renderReviewPanel();
151
+ autoSave();
152
+ }
153
+
154
+ function refreshCommentUI(sectionId, cardIndex) {
155
+ // Update comment items
156
+ const itemsContainer = document.getElementById('comment-items-' + sectionId + '-' + cardIndex);
157
+ if (itemsContainer) {
158
+ itemsContainer.innerHTML = renderCommentItems(sectionId, cardIndex);
159
+ }
160
+
161
+ // Update count badge
162
+ const count = getCommentsForCard(sectionId, cardIndex).length;
163
+ const container = document.querySelector(`.comment-btn-container[data-section-id="${sectionId}"][data-card-index="${cardIndex}"]`);
164
+ if (container) {
165
+ const badge = container.querySelector('.comment-count');
166
+ if (badge) {
167
+ badge.textContent = count;
168
+ badge.className = 'comment-count' + (count === 0 ? ' empty' : '');
169
+ }
170
+ }
171
+ }