project-iris 0.0.12 → 0.0.14

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 (189) hide show
  1. package/README.md +214 -323
  2. package/bin/cli.js +21 -0
  3. package/flows/aidlc/README.md +372 -0
  4. package/flows/aidlc/agents/construction-agent.md +79 -0
  5. package/flows/aidlc/agents/inception-agent.md +97 -0
  6. package/flows/aidlc/agents/master-agent.md +61 -0
  7. package/flows/aidlc/agents/operations-agent.md +89 -0
  8. package/flows/aidlc/commands/construction-agent.md +63 -0
  9. package/flows/aidlc/commands/inception-agent.md +55 -0
  10. package/flows/aidlc/commands/master-agent.md +47 -0
  11. package/flows/aidlc/commands/operations-agent.md +77 -0
  12. package/flows/aidlc/context-config.yaml +67 -0
  13. package/flows/aidlc/memory-bank.yaml +104 -0
  14. package/flows/aidlc/quick-start.md +322 -0
  15. package/flows/aidlc/skills/construction/bolt-list.md +163 -0
  16. package/flows/aidlc/skills/construction/bolt-replan.md +345 -0
  17. package/flows/aidlc/skills/construction/bolt-start.md +442 -0
  18. package/flows/aidlc/skills/construction/bolt-status.md +185 -0
  19. package/flows/aidlc/skills/construction/navigator.md +196 -0
  20. package/flows/aidlc/skills/inception/bolt-plan.md +372 -0
  21. package/flows/aidlc/skills/inception/context.md +171 -0
  22. package/flows/aidlc/skills/inception/intent-create.md +211 -0
  23. package/flows/aidlc/skills/inception/intent-list.md +124 -0
  24. package/flows/aidlc/skills/inception/navigator.md +207 -0
  25. package/flows/aidlc/skills/inception/requirements.md +227 -0
  26. package/flows/aidlc/skills/inception/review.md +248 -0
  27. package/flows/aidlc/skills/inception/story-create.md +304 -0
  28. package/flows/aidlc/skills/inception/units.md +278 -0
  29. package/flows/aidlc/skills/master/analyze-context.md +239 -0
  30. package/flows/aidlc/skills/master/answer-question.md +141 -0
  31. package/flows/aidlc/skills/master/explain-flow.md +158 -0
  32. package/flows/aidlc/skills/master/project-init.md +281 -0
  33. package/flows/aidlc/skills/master/route-request.md +126 -0
  34. package/flows/aidlc/skills/operations/build.md +237 -0
  35. package/flows/aidlc/skills/operations/deploy.md +259 -0
  36. package/flows/aidlc/skills/operations/monitor.md +265 -0
  37. package/flows/aidlc/skills/operations/navigator.md +209 -0
  38. package/flows/aidlc/skills/operations/verify.md +224 -0
  39. package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt.md +3 -3
  40. package/{dist → flows/aidlc}/templates/construction/bolt-types/spike-bolt.md +2 -2
  41. package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
  42. package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
  43. package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
  44. package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
  45. package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
  46. package/flows/aidlc/templates/inception/project/README.md +55 -0
  47. package/flows/aidlc/templates/standards/catalog.yaml +345 -0
  48. package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
  49. package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
  50. package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
  51. package/lib/InstallerFactory.js +36 -0
  52. package/lib/analytics/env-detector.js +92 -0
  53. package/lib/analytics/index.js +22 -0
  54. package/lib/analytics/machine-id.js +33 -0
  55. package/lib/analytics/tracker.js +232 -0
  56. package/lib/cli-utils.js +342 -0
  57. package/lib/constants.js +32 -0
  58. package/lib/installer.js +402 -0
  59. package/lib/installers/AntigravityInstaller.js +22 -0
  60. package/lib/installers/ClaudeInstaller.js +85 -0
  61. package/lib/installers/ClineInstaller.js +21 -0
  62. package/lib/installers/CodexInstaller.js +21 -0
  63. package/lib/installers/CopilotInstaller.js +113 -0
  64. package/lib/installers/CursorInstaller.js +63 -0
  65. package/lib/installers/GeminiInstaller.js +75 -0
  66. package/lib/installers/KiroInstaller.js +22 -0
  67. package/lib/installers/OpenCodeInstaller.js +22 -0
  68. package/lib/installers/RooInstaller.js +22 -0
  69. package/lib/installers/ToolInstaller.js +73 -0
  70. package/lib/installers/WindsurfInstaller.js +22 -0
  71. package/lib/markdown-validator.ts +175 -0
  72. package/lib/yaml-validator.ts +99 -0
  73. package/package.json +105 -32
  74. package/scripts/artifact-validator.js +594 -0
  75. package/scripts/bolt-complete.js +606 -0
  76. package/scripts/status-integrity.js +598 -0
  77. package/dist/bridge/agent-runner.js +0 -190
  78. package/dist/bridge/connector-factory.js +0 -31
  79. package/dist/bridge/connectors/antigravity-connector.js +0 -18
  80. package/dist/bridge/connectors/cursor-connector.js +0 -31
  81. package/dist/bridge/connectors/in-process-connector.js +0 -29
  82. package/dist/bridge/connectors/vscode-connector.js +0 -31
  83. package/dist/bridge/connectors/windsurf-connector.js +0 -23
  84. package/dist/bridge/filesystem-connector.js +0 -110
  85. package/dist/bridge/helper.js +0 -203
  86. package/dist/bridge/types.js +0 -10
  87. package/dist/cli.js +0 -40
  88. package/dist/commands/ask.js +0 -259
  89. package/dist/commands/bridge.js +0 -88
  90. package/dist/commands/create.js +0 -25
  91. package/dist/commands/develop.js +0 -141
  92. package/dist/commands/doctor.js +0 -102
  93. package/dist/commands/flow.js +0 -301
  94. package/dist/commands/framework.js +0 -273
  95. package/dist/commands/generate.js +0 -59
  96. package/dist/commands/install.js +0 -100
  97. package/dist/commands/pack.js +0 -33
  98. package/dist/commands/phase.js +0 -38
  99. package/dist/commands/run.js +0 -199
  100. package/dist/commands/status.js +0 -114
  101. package/dist/commands/uninstall.js +0 -14
  102. package/dist/commands/use.js +0 -20
  103. package/dist/commands/validate.js +0 -102
  104. package/dist/framework/framework-loader.js +0 -97
  105. package/dist/framework/framework-paths.js +0 -48
  106. package/dist/framework/framework-types.js +0 -15
  107. package/dist/iris/artifact-checker.js +0 -78
  108. package/dist/iris/artifacts/config.js +0 -68
  109. package/dist/iris/artifacts/generator.js +0 -88
  110. package/dist/iris/artifacts/types.js +0 -1
  111. package/dist/iris/bundle.js +0 -44
  112. package/dist/iris/doctrine/collector.js +0 -124
  113. package/dist/iris/fixer.js +0 -149
  114. package/dist/iris/flows/manifest.js +0 -124
  115. package/dist/iris/framework-context.js +0 -49
  116. package/dist/iris/framework-manager.js +0 -215
  117. package/dist/iris/fs/atomic.js +0 -22
  118. package/dist/iris/guard.js +0 -38
  119. package/dist/iris/importers/index.js +0 -9
  120. package/dist/iris/importers/types.js +0 -8
  121. package/dist/iris/importers/writer.js +0 -139
  122. package/dist/iris/include.js +0 -49
  123. package/dist/iris/installer.js +0 -334
  124. package/dist/iris/interactive/env.js +0 -21
  125. package/dist/iris/interactive/intent-interview.js +0 -345
  126. package/dist/iris/interactive/intent-schema.js +0 -28
  127. package/dist/iris/interactive/interview-io.js +0 -22
  128. package/dist/iris/interview/config.js +0 -71
  129. package/dist/iris/interview/types.js +0 -16
  130. package/dist/iris/interview/utils.js +0 -38
  131. package/dist/iris/manifest.js +0 -54
  132. package/dist/iris/packer.js +0 -325
  133. package/dist/iris/parsers/unit-parser.js +0 -43
  134. package/dist/iris/paths.js +0 -18
  135. package/dist/iris/policy.js +0 -133
  136. package/dist/iris/proc.js +0 -56
  137. package/dist/iris/report.js +0 -53
  138. package/dist/iris/resolver.js +0 -66
  139. package/dist/iris/router.js +0 -114
  140. package/dist/iris/routes.js +0 -189
  141. package/dist/iris/run-state.js +0 -146
  142. package/dist/iris/state.js +0 -113
  143. package/dist/iris/templates.js +0 -70
  144. package/dist/iris/tmp.js +0 -24
  145. package/dist/iris/uninstaller.js +0 -181
  146. package/dist/iris/utils/interpolate.js +0 -42
  147. package/dist/iris/validator.js +0 -391
  148. package/dist/iris/workflow/config.js +0 -51
  149. package/dist/iris/workflow/engine.js +0 -129
  150. package/dist/iris/workflow/steps.js +0 -448
  151. package/dist/iris/workflow/types.js +0 -1
  152. package/dist/iris_bundle/frameworks/iris-core/framework.yaml +0 -9
  153. package/dist/iris_bundle/frameworks/iris-core/memory/memory-bank.yaml +0 -1
  154. package/dist/iris_bundle/frameworks/iris-core/policy.yaml +0 -7
  155. package/dist/iris_bundle/frameworks/iris-core/templates/config/memory-bank.yaml +0 -1
  156. package/dist/iris_bundle/frameworks/iris-core/templates/construction/bolt-types/spike-bolt.md +0 -240
  157. package/dist/lib.js +0 -96
  158. package/dist/templates/construction/bolt-template.md +0 -226
  159. package/dist/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -49
  160. package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -55
  161. package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -67
  162. package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -62
  163. package/dist/templates/construction/bolt-types/ddd-construction-bolt.md +0 -528
  164. package/dist/templates/construction/bolt-types/simple-construction-bolt.md +0 -347
  165. package/dist/templates/inception/requirements-template.md +0 -144
  166. package/dist/templates/inception/stories-template.md +0 -38
  167. package/dist/templates/inception/story-template.md +0 -147
  168. package/dist/templates/inception/system-context-template.md +0 -29
  169. package/dist/templates/inception/unit-brief-template.md +0 -177
  170. package/dist/templates/inception/units-template.md +0 -52
  171. package/dist/utils/exit-codes.js +0 -7
  172. package/dist/utils/logo.js +0 -17
  173. package/dist/workflows/bolt-execution.js +0 -238
  174. package/dist/workflows/bolt-plan.js +0 -221
  175. package/dist/workflows/intent-inception.js +0 -285
  176. package/dist/workflows/memory-bank-generator.js +0 -180
  177. package/dist/workflows/reporting.js +0 -74
  178. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-template.md +0 -0
  179. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -0
  180. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -0
  181. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -0
  182. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -0
  183. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/simple-construction-bolt.md +0 -0
  184. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/requirements-template.md +0 -0
  185. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/stories-template.md +0 -0
  186. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/story-template.md +0 -0
  187. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/system-context-template.md +0 -0
  188. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/unit-brief-template.md +0 -0
  189. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/units-template.md +0 -0
@@ -1,345 +0,0 @@
1
- import kleur from "kleur";
2
- import fs from "fs";
3
- import { writeJsonAtomic } from "../fs/atomic.js";
4
- import { InquirerIO } from "./interview-io.js";
5
- import { ensureDir } from "../../lib.js";
6
- import { getInboxPath, getHistoryDraftPath, getLatestDraftPath, getLegacyDraftPath } from "../paths.js";
7
- import { createEmptyDraft, validateDraft } from "./intent-schema.js";
8
- // --- Rubric Logic Moved to Schema/Shared or kept here? ---
9
- // The prompt asked for logic in schema.ts? No, prompt asked for SCHEMA definition in schema.ts.
10
- // Let's keep logic in schema.ts or here.
11
- // Actually, `calculateConfidence` is better in `intent-schema.ts` if we want to test it easily without inquirer deps.
12
- // But my previous `intent-schema.ts` didn't have `calculateConfidence`.
13
- // I should probably move `calculateConfidence` into `intent-schema.ts` OR just import the type and keep logic here.
14
- // To minimize changes to `intent-schema.ts` which I already wrote, I will keep `calculateConfidence` here
15
- // BUT I need to make sure I use the types correctly.
16
- // WAIT, I ALREADY WROTE `intent-schema.ts` WITHOUT `calculateConfidence`.
17
- // So I will implement `calculateConfidence` here in this file, extending the one in schema if needed or just standalone.
18
- // Re-defining locally to match the file I tried to write before
19
- // ACTUALLY, checking `intent-schema.ts` content from Step 193... it does NOT have calculateConfidence.
20
- // So I must implement it here.
21
- export function formatIntentDraft(draft) {
22
- return `
23
- # Intent: ${draft.goal}
24
-
25
- ## Context
26
- - **Target User:** ${draft.userType}
27
- - **Project Phase:** ${draft.projectPhase}
28
- - **Confidence:** ${draft.confidence.score}
29
-
30
- ## Success Criteria
31
- ${draft.successCriteria.length > 0 ? draft.successCriteria.map(c => `- ${c}`).join("\n") : "(None provided)"}
32
-
33
- ## Technical Constraints
34
- ${draft.constraints.map(c => `- ${c}`).join("\n")}
35
-
36
- ## Tooling Stack
37
- ${draft.tools.map(t => `- ${t}`).join("\n")}
38
-
39
- ## Additional Details
40
- ${draft.nonGoals.length > 0 ? `\n### Non-Goals\n${draft.nonGoals.map(x => `- ${x}`).join("\n")}` : ""}
41
- ${draft.acceptanceTests.length > 0 ? `\n### Acceptance Tests\n${draft.acceptanceTests.map(x => `- ${x}`).join("\n")}` : ""}
42
-
43
- ---
44
- *Generated via IRIS Interactive Interview*
45
- `.trim();
46
- }
47
- function calcConfidenceScore(draft) {
48
- let score = 0;
49
- const explanation = [];
50
- // 1. Goal Clarity
51
- if (draft.goal && draft.goal.split(" ").length > 3) {
52
- score += 0.2;
53
- explanation.push("+0.2 for clear goal statement");
54
- }
55
- else if (draft.goal) {
56
- score += 0.1;
57
- explanation.push("+0.1 for goal presence");
58
- }
59
- const successLen = draft.successCriteria?.length || 0;
60
- if (successLen > 0) {
61
- score += 0.2;
62
- explanation.push(`+0.2 for ${successLen} success criteria`);
63
- }
64
- const acceptLen = draft.acceptanceTests?.length || 0;
65
- if (acceptLen > 0) {
66
- score += 0.1;
67
- explanation.push(`+0.1 for ${acceptLen} acceptance tests`);
68
- }
69
- if (draft.constraints && draft.constraints.length > 0) {
70
- score += 0.1;
71
- explanation.push("+0.1 for identified constraints");
72
- }
73
- if (draft.tools && draft.tools.length > 0) {
74
- score += 0.1;
75
- explanation.push("+0.1 for tool selection");
76
- }
77
- if (draft.nonGoals && draft.nonGoals.length > 0) {
78
- score += 0.1;
79
- explanation.push("+0.1 for explicit non-goals");
80
- }
81
- if (draft.userType && draft.projectPhase) {
82
- score += 0.1;
83
- explanation.push("+0.1 for user context & phase");
84
- }
85
- score = Math.min(1.0, Math.max(0, Math.round(score * 10) / 10));
86
- return { score, explanation };
87
- }
88
- // Export for tests if needed, but the main export is runIntentInterview
89
- export { calcConfidenceScore as calculateConfidence };
90
- // --- Interview Logic ---
91
- export async function runIntentInterview(initialIntent, config, existingDraft, io = new InquirerIO()) {
92
- io.header("\n🎤 Interactive Intent Interview");
93
- if (config?.guidance?.systemNotes) {
94
- io.print(kleur.dim(config.guidance.systemNotes));
95
- }
96
- else {
97
- io.print(kleur.dim("Let's clarify your goals before we start building.\n"));
98
- }
99
- const draftPartial = existingDraft ? { ...existingDraft } : createEmptyDraft();
100
- // If we have an existing draft, we assume we are resuming/refining, so we don't overwrite unless intent is new?
101
- // Actually if initialIntent is provided on resume, it might be the "follow up prompt" context, which isn't a draft field.
102
- // But draftPartial.goal is the high level goal.
103
- if (!existingDraft && initialIntent) {
104
- draftPartial.goal = initialIntent;
105
- }
106
- // 1. Goal (Skip if exists)
107
- if (!draftPartial.goal) {
108
- const { goal } = await io.ask([
109
- {
110
- type: "input",
111
- name: "goal",
112
- message: config?.guidance?.starterQuestions?.[0] || "What are you trying to build?",
113
- validate: (input) => input.trim().length > 0 || "Please provide a goal."
114
- }
115
- ]);
116
- draftPartial.goal = goal;
117
- }
118
- else {
119
- // If resuming or passed in arg
120
- io.print(`${kleur.cyan("? ")}Goal: ${kleur.cyan(draftPartial.goal || "")}`);
121
- }
122
- // 2. User & Phase
123
- const context = await io.ask([
124
- {
125
- type: "list",
126
- name: "userType",
127
- message: "Who is the primary user?",
128
- choices: [
129
- "Individual Developer",
130
- "Team / Connectors",
131
- "Enterprise / Organization",
132
- "End Consumers"
133
- ]
134
- },
135
- {
136
- type: "list",
137
- name: "projectPhase",
138
- message: "What phase is this project in?",
139
- choices: [
140
- { name: "Inception (New Idea)", value: "Inception" },
141
- { name: "Construction (Active Dev)", value: "Construction" },
142
- { name: "Operations (Maintenance)", value: "Operations" }
143
- ]
144
- }
145
- ]);
146
- Object.assign(draftPartial, context);
147
- // 3. Success Criteria (Required)
148
- io.print("\n" + kleur.bold("Success Criteria"));
149
- // Loop until we have at least one criteria (Enforcement)
150
- while (true) {
151
- io.print(kleur.dim("What must be true for this to be considered done? (Enter empty line to finish)"));
152
- const criteria = [];
153
- if (draftPartial.successCriteria)
154
- criteria.push(...draftPartial.successCriteria);
155
- while (true) {
156
- const { item } = await io.ask([{
157
- type: "input",
158
- name: "item",
159
- message: `Criterion ${criteria.length + 1}:`
160
- }]);
161
- if (!item.trim())
162
- break;
163
- criteria.push(item.trim());
164
- }
165
- draftPartial.successCriteria = criteria;
166
- if (criteria.length === 0) {
167
- io.warn("⚠ At least one Success Criterion is required.");
168
- const { retry } = await io.ask([{
169
- type: "confirm",
170
- name: "retry",
171
- message: "Try adding criteria again?",
172
- default: true
173
- }]);
174
- if (!retry) {
175
- io.warn("Cannot proceed without success criteria. Interview cancelled.");
176
- return null;
177
- }
178
- }
179
- else {
180
- break;
181
- }
182
- }
183
- // 4. Constraints (Multi-select)
184
- const { constraints } = await io.ask([
185
- {
186
- type: "checkbox",
187
- name: "constraints",
188
- message: "What constraints matter most?",
189
- choices: [
190
- "Time (Speed to Market)",
191
- "Quality (Robustness)",
192
- "Compliance / Security",
193
- "Performance",
194
- "Cost / Budget",
195
- "Simplicity"
196
- ]
197
- }
198
- ]);
199
- draftPartial.constraints = constraints;
200
- // 5. Tools
201
- const { tools } = await io.ask([
202
- {
203
- type: "checkbox",
204
- name: "tools",
205
- message: "Which tools/platforms are involved?",
206
- choices: [
207
- { name: "Node.js / TypeScript", checked: true },
208
- "Python",
209
- "React / Frontend",
210
- "Docker / K8s",
211
- "AWS / Cloud",
212
- "Database (SQL/NoSQL)"
213
- ]
214
- }
215
- ]);
216
- draftPartial.tools = tools;
217
- // 6. Optional Fields
218
- const { addMore } = await io.ask([{
219
- type: "confirm",
220
- name: "addMore",
221
- message: "Add non-goals, risks, or acceptance tests? (Optional)",
222
- default: false
223
- }]);
224
- if (addMore) {
225
- // Non Goals
226
- io.print("\n" + kleur.bold("Non-Goals (What are we NOT doing?)"));
227
- while (true) {
228
- const { item } = await io.ask([{ type: "input", name: "item", message: ">" }]);
229
- if (!item.trim())
230
- break;
231
- if (!draftPartial.nonGoals)
232
- draftPartial.nonGoals = [];
233
- draftPartial.nonGoals.push(item.trim());
234
- }
235
- // Acceptance Tests
236
- io.print("\n" + kleur.bold("Acceptance Tests (How will we verify?)"));
237
- while (true) {
238
- const { item } = await io.ask([{ type: "input", name: "item", message: ">" }]);
239
- if (!item.trim())
240
- break;
241
- if (!draftPartial.acceptanceTests)
242
- draftPartial.acceptanceTests = [];
243
- draftPartial.acceptanceTests.push(item.trim());
244
- }
245
- }
246
- // Calculate Confidence
247
- const rubric = calcConfidenceScore(draftPartial);
248
- // Assemble final draft
249
- const draft = {
250
- ...createEmptyDraft(),
251
- ...draftPartial,
252
- confidence: rubric,
253
- // Ensure successCriteria is array
254
- successCriteria: draftPartial.successCriteria || [],
255
- createdAt: new Date().toISOString()
256
- };
257
- // Summary
258
- io.divider();
259
- io.header("📋 Intent Summary");
260
- io.print(`Goal: ${draft.goal}`);
261
- io.print(`Context: ${draft.userType} / ${draft.projectPhase}`);
262
- io.print(`Criteria: ${draft.successCriteria.length} items`);
263
- io.print(`Tools: ${draft.tools.join(", ")}`);
264
- io.print("");
265
- const scoreColor = draft.confidence.score > 0.7 ? kleur.green : draft.confidence.score > 0.4 ? kleur.yellow : kleur.red;
266
- io.print(`Confidence Score: ${scoreColor(draft.confidence.score.toFixed(2) + " / 1.0")}`);
267
- draft.confidence.explanation.forEach(exp => io.print(kleur.dim(` ${exp}`)));
268
- io.divider();
269
- // Validation check
270
- const val = validateDraft(draft);
271
- if (!val.valid) {
272
- io.warn(kleur.red("Draft validation failed:"));
273
- val.errors.forEach(e => io.print(`- ${e}`));
274
- io.print("Please restart or fix inputs.");
275
- return null;
276
- }
277
- const { confirm } = await io.ask([
278
- {
279
- type: "confirm",
280
- name: "confirm",
281
- message: "Do you want to generate intent artifacts now?",
282
- default: true
283
- }
284
- ]);
285
- if (!confirm) {
286
- io.warn("Interview cancelled. No artifacts generated.");
287
- return null;
288
- }
289
- return draft;
290
- }
291
- /**
292
- * Saves intent draft enforcing schema.
293
- * @param writeLegacy If true, also writes .iris/inbox/intent-draft.json (deprecated)
294
- */
295
- export function saveIntentDraft(root, draft, writeLegacy = false) {
296
- const inboxDir = getInboxPath(root);
297
- ensureDir(inboxDir);
298
- // Enforce Schema Defaults & Versioning
299
- const fullDraft = {
300
- ...createEmptyDraft(),
301
- ...draft,
302
- createdAt: draft.createdAt || new Date().toISOString()
303
- };
304
- // Recalculate confidence if missing
305
- if (!draft.confidence) {
306
- fullDraft.confidence = calcConfidenceScore(fullDraft);
307
- }
308
- // 1. Save History
309
- const historyFile = getHistoryDraftPath(root, new Date(fullDraft.createdAt));
310
- // 2. Save Latest (Canonical)
311
- const latestFile = getLatestDraftPath(root);
312
- writeJsonAtomic(historyFile, fullDraft);
313
- writeJsonAtomic(latestFile, fullDraft);
314
- // 3. Optional Legacy
315
- if (writeLegacy) {
316
- const legacyFile = getLegacyDraftPath(root);
317
- writeJsonAtomic(legacyFile, fullDraft);
318
- }
319
- }
320
- /**
321
- * Migrates legacy intent-draft.json to intent-draft.latest.json.
322
- */
323
- export function migrateLegacyDraft(root) {
324
- const legacyFile = getLegacyDraftPath(root);
325
- const latestFile = getLatestDraftPath(root);
326
- if (fs.existsSync(legacyFile) && !fs.existsSync(latestFile)) {
327
- try {
328
- const content = JSON.parse(fs.readFileSync(legacyFile, "utf-8"));
329
- // Enforce schema by saving via normalized function
330
- // Treat content as partial
331
- if (!content.successCriteria) {
332
- content.successCriteria = ["(Migrated from legacy draft)"];
333
- }
334
- // Re-save (generates latest + history)
335
- // Do NOT write legacy again (avoid loop), so writeLegacy=false
336
- saveIntentDraft(root, content, false);
337
- return true;
338
- }
339
- catch (e) {
340
- console.error(kleur.red(`Failed to migrate legacy draft: ${e}`));
341
- return false;
342
- }
343
- }
344
- return false;
345
- }
@@ -1,28 +0,0 @@
1
- export const CURRENT_SCHEMA_VERSION = "1";
2
- export function createEmptyDraft() {
3
- return {
4
- version: CURRENT_SCHEMA_VERSION,
5
- createdAt: new Date().toISOString(),
6
- goal: "",
7
- userType: "",
8
- projectPhase: "",
9
- constraints: [],
10
- tools: [],
11
- successCriteria: [],
12
- nonGoals: [],
13
- assumptions: [],
14
- risks: [],
15
- acceptanceTests: [],
16
- confidence: { score: 0, explanation: [] }
17
- };
18
- }
19
- export function validateDraft(draft) {
20
- const errors = [];
21
- if (!draft.goal)
22
- errors.push("Goal is required");
23
- if (!draft.successCriteria || draft.successCriteria.length === 0) {
24
- errors.push("At least one Success Criterion is required");
25
- }
26
- // We can add more strict checks here if needed
27
- return { valid: errors.length === 0, errors };
28
- }
@@ -1,22 +0,0 @@
1
- import inquirer from "inquirer";
2
- import kleur from "kleur";
3
- export class InquirerIO {
4
- async ask(questions) {
5
- // map Question interface to inquirer question
6
- // Inquirer types are slightly different, but compatible enough for pass-through usually
7
- const answers = await inquirer.prompt(questions);
8
- return answers;
9
- }
10
- print(message) {
11
- console.log(message);
12
- }
13
- header(text) {
14
- console.log(kleur.bold(text));
15
- }
16
- divider() {
17
- console.log(kleur.dim("=========================================="));
18
- }
19
- warn(message) {
20
- console.log(kleur.yellow(message));
21
- }
22
- }
@@ -1,71 +0,0 @@
1
- import fs from "fs";
2
- import yaml from "js-yaml";
3
- import kleur from "kleur";
4
- import { KNOWN_INTENT_FIELDS } from "./types.js";
5
- export const DEFAULT_INTERVIEW_CONFIG = {
6
- schemaVersion: 1,
7
- required: ["successCriteria", "user", "problem"],
8
- optional: ["nonGoals", "risks", "acceptanceTests"],
9
- guidance: {
10
- systemNotes: "You are a Product Manager / Business Analyst. Ask clarifying questions.",
11
- starterQuestions: [
12
- "Who is the user and what problem are we solving?",
13
- "What does success look like (measurable)?",
14
- "What are the constraints (time, budget, tech, legal)?"
15
- ],
16
- redFlags: ["Ambiguous scope", "No success metric"]
17
- },
18
- weights: {
19
- successCriteria: 3,
20
- risks: 2
21
- },
22
- fieldPrompts: {
23
- successCriteria: "What does success look like? How will we measure it?",
24
- user: "Who is the specific user for this feature?",
25
- problem: "What core problem are we solving?",
26
- risks: "Are there any risks or non-goals we should state?",
27
- constraints: "Are there any technical or budget constraints?"
28
- }
29
- };
30
- export function loadEffectiveInterviewConfig(framework) {
31
- if (!framework || !framework.files.interview || !fs.existsSync(framework.files.interview)) {
32
- return DEFAULT_INTERVIEW_CONFIG;
33
- }
34
- try {
35
- const content = fs.readFileSync(framework.files.interview, "utf8");
36
- const raw = yaml.load(content);
37
- // 1. Schema Version Check
38
- if (raw.schemaVersion !== 1) {
39
- console.error(kleur.yellow(`IRIS_WARNING: Unsupported interview schema version ${raw.schemaVersion}. Using default.`));
40
- return DEFAULT_INTERVIEW_CONFIG;
41
- }
42
- // 2. Strict Field Validation
43
- const allFields = [...(raw.required || []), ...(raw.optional || [])];
44
- const unknownFields = allFields.filter(f => !KNOWN_INTENT_FIELDS.has(f));
45
- if (unknownFields.length > 0) {
46
- console.error(kleur.yellow(`IRIS_WARNING: Invalid interview configuration. Unknown fields: ${unknownFields.join(", ")}. Using default.`));
47
- return DEFAULT_INTERVIEW_CONFIG;
48
- }
49
- // 3. Merge with minimal defaults (or just return raw if we trust it completely?)
50
- // The plan says "If invalid/missing return DEFAULT".
51
- // But if valid, we return the parsed object.
52
- // Should we merge fieldPrompts if missing?
53
- // Best to provide safety defaults for prompts if not overridden.
54
- return {
55
- schemaVersion: raw.schemaVersion,
56
- required: raw.required || [],
57
- optional: raw.optional || [],
58
- guidance: {
59
- systemNotes: raw.guidance?.systemNotes || DEFAULT_INTERVIEW_CONFIG.guidance.systemNotes,
60
- starterQuestions: raw.guidance?.starterQuestions || DEFAULT_INTERVIEW_CONFIG.guidance.starterQuestions,
61
- redFlags: raw.guidance?.redFlags || DEFAULT_INTERVIEW_CONFIG.guidance.redFlags
62
- },
63
- weights: raw.weights || {},
64
- fieldPrompts: { ...DEFAULT_INTERVIEW_CONFIG.fieldPrompts, ...raw.fieldPrompts }
65
- };
66
- }
67
- catch (e) {
68
- console.error(kleur.yellow(`IRIS_WARNING: Failed to parse interview.yaml: ${e.message}. Using default.`));
69
- return DEFAULT_INTERVIEW_CONFIG;
70
- }
71
- }
@@ -1,16 +0,0 @@
1
- export const KNOWN_INTENT_FIELDS = new Set([
2
- 'successCriteria',
3
- 'user',
4
- 'problem',
5
- 'constraints',
6
- 'nonGoals',
7
- 'risks',
8
- 'acceptanceTests',
9
- 'context',
10
- 'context.user',
11
- 'context.phase',
12
- 'visuals',
13
- 'name', // Sometimes inferred, but valid
14
- 'description', // Basic
15
- 'features', // High level
16
- ]);
@@ -1,38 +0,0 @@
1
- /**
2
- * Access a value in an object via dot-notation.
3
- * Safe traversal: returns undefined if path breaks.
4
- */
5
- export function getByPath(obj, path) {
6
- if (!obj)
7
- return undefined;
8
- const parts = path.split('.');
9
- let current = obj;
10
- for (const part of parts) {
11
- if (current === null || current === undefined)
12
- return undefined;
13
- current = current[part];
14
- }
15
- return current;
16
- }
17
- /**
18
- * Checks if a value is "missing" according to strict interview rules.
19
- * Missing = null, undefined, empty string "", whitespace-only string, or empty array [].
20
- */
21
- export function isMissing(value) {
22
- if (value === null || value === undefined)
23
- return true;
24
- if (typeof value === 'string')
25
- return value.trim().length === 0;
26
- if (Array.isArray(value))
27
- return value.length === 0;
28
- // Objects? We typically check leaf nodes, but an empty object {} usually counts as present for 'context'
29
- // UNLESS the requirement is specifically context.user.
30
- // So for generic objects, existence is enough.
31
- return false;
32
- }
33
- /**
34
- * Returns a list of required fields that are missing in the draft.
35
- */
36
- export function getMissingFields(draft, requiredFields) {
37
- return requiredFields.filter(field => isMissing(getByPath(draft, field)));
38
- }
@@ -1,54 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import yaml from "js-yaml";
4
- import { writeFile } from "../lib.js";
5
- const MANIFEST_PATH = path.join(process.cwd(), ".iris/manifest.yaml");
6
- export function loadManifest() {
7
- if (!fs.existsSync(MANIFEST_PATH))
8
- return null;
9
- try {
10
- const doc = yaml.load(fs.readFileSync(MANIFEST_PATH, "utf8"));
11
- // Basic migration/defaulting if needed
12
- return {
13
- version: 1,
14
- installed_at: new Date().toISOString(),
15
- tools_selected: [],
16
- paths_installed: [],
17
- created_dirs: [],
18
- ...doc
19
- };
20
- }
21
- catch (e) {
22
- console.error("Failed to parse manifest:", e);
23
- return null;
24
- }
25
- }
26
- export function saveManifest(manifest) {
27
- writeFile(MANIFEST_PATH, yaml.dump(manifest));
28
- }
29
- export function updateManifest(updates) {
30
- const current = loadManifest() || {
31
- version: 1,
32
- installed_at: new Date().toISOString(),
33
- tools_selected: [],
34
- paths_installed: [],
35
- created_dirs: [],
36
- };
37
- const updated = {
38
- ...current,
39
- ...updates,
40
- installed_at: new Date().toISOString(), // Always update timestamp
41
- tools_selected: Array.from(new Set([...current.tools_selected, ...(updates.tools_selected || [])])),
42
- paths_installed: mergePaths(current.paths_installed, updates.paths_installed || []),
43
- created_dirs: Array.from(new Set([...current.created_dirs, ...(updates.created_dirs || [])])),
44
- };
45
- saveManifest(updated);
46
- }
47
- function mergePaths(current, updates) {
48
- // Map current paths by path string for easy update
49
- const map = new Map();
50
- current.forEach(p => map.set(p.path, p));
51
- // Apply updates
52
- updates.forEach(p => map.set(p.path, p));
53
- return Array.from(map.values());
54
- }