infernoflow 0.32.7 → 0.32.9

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 (78) hide show
  1. package/dist/bin/infernoflow.mjs +84 -255
  2. package/dist/lib/adopters/angular.mjs +1 -128
  3. package/dist/lib/adopters/css.mjs +1 -111
  4. package/dist/lib/adopters/react.mjs +1 -104
  5. package/dist/lib/ai/ideDetection.mjs +1 -31
  6. package/dist/lib/ai/localProvider.mjs +1 -88
  7. package/dist/lib/ai/providerRouter.mjs +2 -295
  8. package/dist/lib/commands/adopt.mjs +20 -869
  9. package/dist/lib/commands/adoptWizard.mjs +9 -320
  10. package/dist/lib/commands/agent.mjs +5 -191
  11. package/dist/lib/commands/ai.mjs +2 -407
  12. package/dist/lib/commands/audit.mjs +13 -300
  13. package/dist/lib/commands/changelog.mjs +26 -594
  14. package/dist/lib/commands/check.mjs +3 -184
  15. package/dist/lib/commands/ci.mjs +3 -208
  16. package/dist/lib/commands/claudeMd.mjs +25 -130
  17. package/dist/lib/commands/cloud.mjs +5 -521
  18. package/dist/lib/commands/context.mjs +31 -287
  19. package/dist/lib/commands/coverage.mjs +2 -282
  20. package/dist/lib/commands/dashboard.mjs +123 -635
  21. package/dist/lib/commands/demo.mjs +8 -465
  22. package/dist/lib/commands/diff.mjs +5 -274
  23. package/dist/lib/commands/docGate.mjs +2 -81
  24. package/dist/lib/commands/doctor.mjs +3 -321
  25. package/dist/lib/commands/explain.mjs +8 -438
  26. package/dist/lib/commands/export.mjs +10 -239
  27. package/dist/lib/commands/generateSkills.mjs +38 -163
  28. package/dist/lib/commands/graph.mjs +203 -320
  29. package/dist/lib/commands/health.mjs +2 -309
  30. package/dist/lib/commands/impact.mjs +2 -325
  31. package/dist/lib/commands/implement.mjs +7 -103
  32. package/dist/lib/commands/init.mjs +23 -475
  33. package/dist/lib/commands/installCursorHooks.mjs +1 -36
  34. package/dist/lib/commands/installVsCodeCopilotHooks.mjs +1 -37
  35. package/dist/lib/commands/link.mjs +2 -342
  36. package/dist/lib/commands/monorepo.mjs +4 -428
  37. package/dist/lib/commands/notify.mjs +4 -258
  38. package/dist/lib/commands/onboard.mjs +4 -296
  39. package/dist/lib/commands/prComment.mjs +2 -361
  40. package/dist/lib/commands/prImpact.mjs +2 -157
  41. package/dist/lib/commands/publish.mjs +15 -316
  42. package/dist/lib/commands/report.mjs +28 -272
  43. package/dist/lib/commands/review.mjs +9 -223
  44. package/dist/lib/commands/run.mjs +8 -336
  45. package/dist/lib/commands/scaffold.mjs +54 -419
  46. package/dist/lib/commands/scan.mjs +5 -558
  47. package/dist/lib/commands/scout.mjs +2 -291
  48. package/dist/lib/commands/setup.mjs +5 -310
  49. package/dist/lib/commands/share.mjs +13 -196
  50. package/dist/lib/commands/snapshot.mjs +3 -383
  51. package/dist/lib/commands/stability.mjs +2 -293
  52. package/dist/lib/commands/status.mjs +4 -172
  53. package/dist/lib/commands/suggest.mjs +21 -563
  54. package/dist/lib/commands/syncAuto.mjs +1 -96
  55. package/dist/lib/commands/synthesize.mjs +10 -228
  56. package/dist/lib/commands/teamSync.mjs +2 -388
  57. package/dist/lib/commands/test.mjs +6 -363
  58. package/dist/lib/commands/version.mjs +2 -282
  59. package/dist/lib/commands/vibe.mjs +7 -357
  60. package/dist/lib/commands/watch.mjs +4 -203
  61. package/dist/lib/commands/why.mjs +4 -358
  62. package/dist/lib/cursorHooksInstall.mjs +1 -60
  63. package/dist/lib/draftToolingInstall.mjs +7 -68
  64. package/dist/lib/git/detect-drift.mjs +4 -208
  65. package/dist/lib/learning/adapt.mjs +6 -101
  66. package/dist/lib/learning/observe.mjs +1 -119
  67. package/dist/lib/learning/patternDetector.mjs +1 -298
  68. package/dist/lib/learning/profile.mjs +2 -279
  69. package/dist/lib/learning/skillSynthesizer.mjs +24 -145
  70. package/dist/lib/templates/index.mjs +1 -131
  71. package/dist/lib/ui/errors.mjs +1 -142
  72. package/dist/lib/ui/output.mjs +6 -72
  73. package/dist/lib/ui/prompts.mjs +6 -147
  74. package/dist/lib/vsCodeCopilotHooksInstall.mjs +1 -42
  75. package/dist/templates/cursor/inferno-mcp-server.mjs +29 -0
  76. package/dist/templates/github-app/GITHUB_APP.md +67 -0
  77. package/dist/templates/github-app/app-manifest.json +20 -0
  78. package/package.json +1 -1
@@ -1,104 +1,30 @@
1
- /**
2
- * lib/learning/skillSynthesizer.mjs
3
- *
4
- * Converts approved candidates into actual files on disk:
5
- *
6
- * AGENTS → inferno/agents/<name>.json
7
- * (runnable via `infernoflow agent run <name>`)
8
- *
9
- * SKILLS → inferno/skills/<name>.md
10
- * + appends to .cursor/rules/infernoflow-auto.mdc
11
- * + appends to CLAUDE.md (if it exists)
12
- */
13
-
14
- import * as fs from "node:fs";
15
- import * as path from "node:path";
16
-
17
- // ── Agent file writer ─────────────────────────────────────────────────────────
18
-
19
- /**
20
- * Write an agent definition to inferno/agents/<name>.json.
21
- * Returns the file path written.
22
- */
23
- export function writeAgentFile(infernoDir, candidate) {
24
- const agentsDir = path.join(infernoDir, "agents");
25
- fs.mkdirSync(agentsDir, { recursive: true });
26
-
27
- const agentDef = {
28
- id: candidate.id,
29
- name: candidate.name,
30
- description: candidate.description,
31
- steps: candidate.steps.map(cmd => ({ command: cmd, args: [] })),
32
- createdAt: new Date().toISOString(),
33
- source: "auto-synthesized",
34
- frequency: candidate.frequency,
35
- confidence: candidate.confidence,
36
- };
37
-
38
- const filePath = path.join(agentsDir, `${candidate.name}.json`);
39
- fs.writeFileSync(filePath, JSON.stringify(agentDef, null, 2) + "\n", "utf8");
40
- return filePath;
41
- }
42
-
43
- // ── Skill file writers ────────────────────────────────────────────────────────
44
-
45
- /**
46
- * Write a skill markdown file to inferno/skills/<name>.md.
47
- */
48
- export function writeSkillFile(infernoDir, candidate) {
49
- const skillsDir = path.join(infernoDir, "skills");
50
- fs.mkdirSync(skillsDir, { recursive: true });
51
-
52
- const examples = (candidate.examples || [])
53
- .map(e => ` - "${e}"`)
54
- .join("\n");
55
-
56
- const steps = (candidate.steps || [])
57
- .map((s, i) => `${i + 1}. Run \`infernoflow ${s}\``)
58
- .join("\n");
59
-
60
- const content = `# Skill: ${candidate.name}
61
-
62
- > Auto-synthesized from ${candidate.frequency} observed sessions (confidence: ${Math.round(candidate.confidence * 100)}%)
1
+ import*as r from"node:fs";import*as u from"node:path";function c(s,e){const n=u.join(s,"agents");r.mkdirSync(n,{recursive:!0});const t={id:e.id,name:e.name,description:e.description,steps:e.steps.map(i=>({command:i,args:[]})),createdAt:new Date().toISOString(),source:"auto-synthesized",frequency:e.frequency,confidence:e.confidence},o=u.join(n,`${e.name}.json`);return r.writeFileSync(o,JSON.stringify(t,null,2)+`
2
+ `,"utf8"),o}function a(s,e){const n=u.join(s,"skills");r.mkdirSync(n,{recursive:!0});const t=(e.examples||[]).map(f=>` - "${f}"`).join(`
3
+ `),o=(e.steps||[]).map((f,p)=>`${p+1}. Run \`infernoflow ${f}\``).join(`
4
+ `),i=`# Skill: ${e.name}
5
+
6
+ > Auto-synthesized from ${e.frequency} observed sessions (confidence: ${Math.round(e.confidence*100)}%)
63
7
 
64
8
  ## Trigger pattern
65
9
 
66
- \`${candidate.trigger}\`
10
+ \`${e.trigger}\`
67
11
 
68
12
  ## Example tasks this applies to
69
13
 
70
- ${examples || " (none recorded)"}
14
+ ${t||" (none recorded)"}
71
15
 
72
16
  ## Recommended workflow
73
17
 
74
18
  When working on a task matching the pattern above:
75
19
 
76
- ${steps}
20
+ ${o}
77
21
 
78
22
  ## Notes
79
23
 
80
24
  - This skill was automatically generated by \`infernoflow synthesize\`
81
- - Review and edit as needed it is yours to own
25
+ - Review and edit as needed \u2014 it is yours to own
82
26
  - Re-run \`infernoflow synthesize\` to update as your patterns evolve
83
- `;
84
-
85
- const filePath = path.join(skillsDir, `${candidate.name}.md`);
86
- fs.writeFileSync(filePath, content, "utf8");
87
- return filePath;
88
- }
89
-
90
- /**
91
- * Append a skill rule to .cursor/rules/infernoflow-auto.mdc.
92
- * Creates the file if it doesn't exist.
93
- */
94
- export function appendToCursorRules(cwd, candidate) {
95
- const rulesDir = path.join(cwd, ".cursor", "rules");
96
- fs.mkdirSync(rulesDir, { recursive: true });
97
-
98
- const filePath = path.join(rulesDir, "infernoflow-auto.mdc");
99
-
100
- const header = fs.existsSync(filePath) ? "" :
101
- `---
27
+ `,l=u.join(n,`${e.name}.md`);return r.writeFileSync(l,i,"utf8"),l}function m(s,e){const n=u.join(s,".cursor","rules");r.mkdirSync(n,{recursive:!0});const t=u.join(n,"infernoflow-auto.mdc"),o=r.existsSync(t)?"":`---
102
28
  description: Auto-generated infernoflow workflow rules
103
29
  globs: ["**/*"]
104
30
  alwaysApply: true
@@ -107,67 +33,20 @@ alwaysApply: true
107
33
  # infernoflow Auto-Generated Workflow Rules
108
34
 
109
35
  These rules are synthesized from your observed development patterns.
110
- Do not edit manually re-run \`infernoflow synthesize\` to regenerate.
111
-
112
- `;
36
+ Do not edit manually \u2014 re-run \`infernoflow synthesize\` to regenerate.
113
37
 
114
- const rule = `
115
- ## ${candidate.name}
38
+ `,i=`
39
+ ## ${e.name}
116
40
 
117
- Pattern: \`${candidate.trigger}\`
41
+ Pattern: \`${e.trigger}\`
118
42
 
119
43
  When working on tasks matching this pattern:
120
- ${(candidate.steps || []).map((s, i) => `${i + 1}. Run \`infernoflow ${s}\``).join("\n")}
121
-
122
- `;
123
-
124
- fs.appendFileSync(filePath, header + rule, "utf8");
125
- return filePath;
126
- }
127
-
128
- /**
129
- * Append a skill tip to CLAUDE.md if it exists.
130
- */
131
- export function appendToClaudeMd(cwd, candidate) {
132
- const claudeMdPath = path.join(cwd, "CLAUDE.md");
133
- if (!fs.existsSync(claudeMdPath)) return null;
134
-
135
- const existing = fs.readFileSync(claudeMdPath, "utf8");
136
- const marker = "## infernoflow Auto-Skills";
137
-
138
- const tip = `\n- **${candidate.name}**: For tasks like \`${candidate.trigger}\`, run: ${(candidate.steps || []).map(s => `\`infernoflow ${s}\``).join(" → ")}`;
139
-
140
- if (!existing.includes(marker)) {
141
- fs.appendFileSync(claudeMdPath, `\n\n${marker}\n${tip}\n`, "utf8");
142
- } else {
143
- // Append after the marker section
144
- const updated = existing.replace(marker, marker + tip);
145
- fs.writeFileSync(claudeMdPath, updated, "utf8");
146
- }
147
-
148
- return claudeMdPath;
149
- }
150
-
151
- // ── Main synthesize function ──────────────────────────────────────────────────
152
-
153
- /**
154
- * Write all files for an approved candidate.
155
- * Returns { filePaths: string[] } — list of files written.
156
- */
157
- export function synthesizeCandidate(cwd, infernoDir, candidate) {
158
- const filePaths = [];
159
-
160
- if (candidate.type === "agent") {
161
- filePaths.push(writeAgentFile(infernoDir, candidate));
162
- } else {
163
- // skill
164
- filePaths.push(writeSkillFile(infernoDir, candidate));
165
- try { filePaths.push(appendToCursorRules(cwd, candidate)); } catch {}
166
- try {
167
- const p = appendToClaudeMd(cwd, candidate);
168
- if (p) filePaths.push(p);
169
- } catch {}
170
- }
171
-
172
- return { filePaths };
173
- }
44
+ ${(e.steps||[]).map((l,f)=>`${f+1}. Run \`infernoflow ${l}\``).join(`
45
+ `)}
46
+
47
+ `;return r.appendFileSync(t,o+i,"utf8"),t}function y(s,e){const n=u.join(s,"CLAUDE.md");if(!r.existsSync(n))return null;const t=r.readFileSync(n,"utf8"),o="## infernoflow Auto-Skills",i=`
48
+ - **${e.name}**: For tasks like \`${e.trigger}\`, run: ${(e.steps||[]).map(l=>`\`infernoflow ${l}\``).join(" \u2192 ")}`;if(!t.includes(o))r.appendFileSync(n,`
49
+
50
+ ${o}
51
+ ${i}
52
+ `,"utf8");else{const l=t.replace(o,o+i);r.writeFileSync(n,l,"utf8")}return n}function h(s,e,n){const t=[];if(n.type==="agent")t.push(c(e,n));else{t.push(a(e,n));try{t.push(m(s,n))}catch{}try{const o=y(s,n);o&&t.push(o)}catch{}}return{filePaths:t}}export{y as appendToClaudeMd,m as appendToCursorRules,h as synthesizeCandidate,c as writeAgentFile,a as writeSkillFile};
@@ -1,131 +1 @@
1
- /**
2
- * infernoflow project templates
3
- *
4
- * Named starters for `infernoflow init --template <name>`.
5
- * Each template provides:
6
- * - capabilities : pre-populated capability list
7
- * - scenarios : one starter scenario per capability
8
- * - contextHint : first-session CONTEXT.md guidance
9
- * - scripts : suggested package.json scripts to add
10
- */
11
-
12
- export const TEMPLATES = {
13
-
14
- // ── REST API ────────────────────────────────────────────────────────────────
15
- "rest-api": {
16
- description: "Express / Fastify / Hono REST API",
17
- capabilities: [
18
- { id: "list-items", description: "GET /items — paginated list with filtering and sorting" },
19
- { id: "get-item", description: "GET /items/:id — fetch a single resource by ID" },
20
- { id: "create-item", description: "POST /items — create a new resource, validate input" },
21
- { id: "update-item", description: "PUT /items/:id — full update of an existing resource" },
22
- { id: "delete-item", description: "DELETE /items/:id — soft or hard delete" },
23
- { id: "authenticate", description: "POST /auth/login — exchange credentials for a JWT token" },
24
- { id: "refresh-token", description: "POST /auth/refresh — extend session with a new token" },
25
- { id: "health-check", description: "GET /health — liveness probe for load balancers" },
26
- { id: "paginate", description: "Cursor or offset pagination applied across list endpoints" },
27
- { id: "validate-input", description: "Schema validation on all incoming request bodies" },
28
- ],
29
- contextHint: "Building a REST API. Focus on route handlers, middleware, and input validation.",
30
- scripts: {
31
- "dev": "node src/index.js",
32
- "start": "node dist/index.js",
33
- },
34
- },
35
-
36
- // ── Next.js ─────────────────────────────────────────────────────────────────
37
- "nextjs": {
38
- description: "Next.js full-stack app (App Router or Pages Router)",
39
- capabilities: [
40
- { id: "render-home-page", description: "/ — server-rendered landing page with SEO metadata" },
41
- { id: "render-dashboard", description: "/dashboard — authenticated user dashboard" },
42
- { id: "api-authenticate", description: "POST /api/auth/login — issue session cookie or JWT" },
43
- { id: "api-list-resources", description: "GET /api/resources — paginated list, auth-protected" },
44
- { id: "api-create-resource", description: "POST /api/resources — create and persist a resource" },
45
- { id: "server-side-props", description: "Fetch user-specific data server-side before render" },
46
- { id: "static-generation", description: "Pre-render marketing pages at build time" },
47
- { id: "image-optimization", description: "next/image usage for responsive, lazy-loaded images" },
48
- { id: "middleware-auth", description: "Edge middleware to protect dashboard routes" },
49
- { id: "error-boundary", description: "Global error.tsx / _error.tsx user-facing error page" },
50
- ],
51
- contextHint: "Building a Next.js app. Use Server Components by default; Client Components only when needed.",
52
- scripts: {
53
- "dev": "next dev",
54
- "build": "next build",
55
- "start": "next start",
56
- },
57
- },
58
-
59
- // ── CLI tool ────────────────────────────────────────────────────────────────
60
- "cli": {
61
- description: "Node.js / Python CLI tool",
62
- capabilities: [
63
- { id: "parse-args", description: "Parse CLI arguments and flags, show help on --help" },
64
- { id: "validate-config", description: "Load and validate config file or env vars on startup" },
65
- { id: "main-command", description: "Primary command — the core action the CLI performs" },
66
- { id: "output-formatter", description: "Format output as text, JSON, or table based on --format flag" },
67
- { id: "error-handler", description: "Friendly error messages with exit codes, no raw stack traces" },
68
- { id: "progress-display", description: "Spinner or progress bar for long-running operations" },
69
- { id: "update-check", description: "Check npm/PyPI for a newer version on startup" },
70
- { id: "plugin-loader", description: "Discover and load user-installed plugins at runtime" },
71
- ],
72
- contextHint: "Building a CLI tool. Prioritise UX: helpful errors, --json flag, zero-config defaults.",
73
- scripts: {
74
- "start": "node bin/cli.js",
75
- "link": "npm link",
76
- },
77
- },
78
-
79
- // ── GraphQL ─────────────────────────────────────────────────────────────────
80
- "graphql": {
81
- description: "GraphQL API (Apollo, Pothos, or similar)",
82
- capabilities: [
83
- { id: "query-viewer", description: "query { viewer } — return the authenticated user" },
84
- { id: "query-list", description: "query { items(first: N, after: cursor) } — paginated list" },
85
- { id: "query-node", description: "query { node(id) } — global object lookup by ID" },
86
- { id: "mutation-create", description: "mutation { createItem(input) } — create and return new object" },
87
- { id: "mutation-update", description: "mutation { updateItem(id, input) } — update fields" },
88
- { id: "mutation-delete", description: "mutation { deleteItem(id) } — remove and return deleted" },
89
- { id: "subscription-event", description: "subscription { itemUpdated } — real-time push via WebSocket" },
90
- { id: "data-loader", description: "Batch and cache DB calls with DataLoader to avoid N+1" },
91
- { id: "auth-directive", description: "@auth directive — enforce authentication on resolvers" },
92
- { id: "error-formatting", description: "Consistent GraphQL error shape with extensions and codes" },
93
- ],
94
- contextHint: "Building a GraphQL API. Use DataLoader for all relations, @auth directive on protected resolvers.",
95
- scripts: {
96
- "dev": "ts-node src/server.ts",
97
- "codegen": "graphql-codegen",
98
- },
99
- },
100
-
101
- // ── Monorepo ────────────────────────────────────────────────────────────────
102
- "monorepo": {
103
- description: "Monorepo workspace (nx / turborepo / pnpm workspaces)",
104
- capabilities: [
105
- { id: "build-all", description: "Build all packages in dependency order" },
106
- { id: "test-all", description: "Run tests across all packages in parallel" },
107
- { id: "lint-all", description: "Lint all packages with shared ESLint / Prettier config" },
108
- { id: "publish-packages", description: "Bump versions and publish changed packages to npm" },
109
- { id: "shared-ui-library", description: "packages/ui — shared React component library" },
110
- { id: "shared-utils", description: "packages/utils — shared utility functions and types" },
111
- { id: "web-app", description: "apps/web — primary Next.js or React web application" },
112
- { id: "api-service", description: "apps/api — backend service consumed by apps" },
113
- { id: "cache-builds", description: "Remote build cache via Turborepo or Nx Cloud" },
114
- { id: "affected-only", description: "Run tasks only for packages affected by a git change" },
115
- ],
116
- contextHint: "Working in a monorepo. Changes in packages/* may affect apps/*. Check affected packages before building.",
117
- scripts: {
118
- "build": "turbo build",
119
- "dev": "turbo dev",
120
- "test": "turbo test",
121
- },
122
- },
123
- };
124
-
125
- export function listTemplates() {
126
- return Object.entries(TEMPLATES).map(([name, t]) => ({ name, description: t.description }));
127
- }
128
-
129
- export function getTemplate(name) {
130
- return TEMPLATES[name] || null;
131
- }
1
+ const i={"rest-api":{description:"Express / Fastify / Hono REST API",capabilities:[{id:"list-items",description:"GET /items \u2014 paginated list with filtering and sorting"},{id:"get-item",description:"GET /items/:id \u2014 fetch a single resource by ID"},{id:"create-item",description:"POST /items \u2014 create a new resource, validate input"},{id:"update-item",description:"PUT /items/:id \u2014 full update of an existing resource"},{id:"delete-item",description:"DELETE /items/:id \u2014 soft or hard delete"},{id:"authenticate",description:"POST /auth/login \u2014 exchange credentials for a JWT token"},{id:"refresh-token",description:"POST /auth/refresh \u2014 extend session with a new token"},{id:"health-check",description:"GET /health \u2014 liveness probe for load balancers"},{id:"paginate",description:"Cursor or offset pagination applied across list endpoints"},{id:"validate-input",description:"Schema validation on all incoming request bodies"}],contextHint:"Building a REST API. Focus on route handlers, middleware, and input validation.",scripts:{dev:"node src/index.js",start:"node dist/index.js"}},nextjs:{description:"Next.js full-stack app (App Router or Pages Router)",capabilities:[{id:"render-home-page",description:"/ \u2014 server-rendered landing page with SEO metadata"},{id:"render-dashboard",description:"/dashboard \u2014 authenticated user dashboard"},{id:"api-authenticate",description:"POST /api/auth/login \u2014 issue session cookie or JWT"},{id:"api-list-resources",description:"GET /api/resources \u2014 paginated list, auth-protected"},{id:"api-create-resource",description:"POST /api/resources \u2014 create and persist a resource"},{id:"server-side-props",description:"Fetch user-specific data server-side before render"},{id:"static-generation",description:"Pre-render marketing pages at build time"},{id:"image-optimization",description:"next/image usage for responsive, lazy-loaded images"},{id:"middleware-auth",description:"Edge middleware to protect dashboard routes"},{id:"error-boundary",description:"Global error.tsx / _error.tsx user-facing error page"}],contextHint:"Building a Next.js app. Use Server Components by default; Client Components only when needed.",scripts:{dev:"next dev",build:"next build",start:"next start"}},cli:{description:"Node.js / Python CLI tool",capabilities:[{id:"parse-args",description:"Parse CLI arguments and flags, show help on --help"},{id:"validate-config",description:"Load and validate config file or env vars on startup"},{id:"main-command",description:"Primary command \u2014 the core action the CLI performs"},{id:"output-formatter",description:"Format output as text, JSON, or table based on --format flag"},{id:"error-handler",description:"Friendly error messages with exit codes, no raw stack traces"},{id:"progress-display",description:"Spinner or progress bar for long-running operations"},{id:"update-check",description:"Check npm/PyPI for a newer version on startup"},{id:"plugin-loader",description:"Discover and load user-installed plugins at runtime"}],contextHint:"Building a CLI tool. Prioritise UX: helpful errors, --json flag, zero-config defaults.",scripts:{start:"node bin/cli.js",link:"npm link"}},graphql:{description:"GraphQL API (Apollo, Pothos, or similar)",capabilities:[{id:"query-viewer",description:"query { viewer } \u2014 return the authenticated user"},{id:"query-list",description:"query { items(first: N, after: cursor) } \u2014 paginated list"},{id:"query-node",description:"query { node(id) } \u2014 global object lookup by ID"},{id:"mutation-create",description:"mutation { createItem(input) } \u2014 create and return new object"},{id:"mutation-update",description:"mutation { updateItem(id, input) } \u2014 update fields"},{id:"mutation-delete",description:"mutation { deleteItem(id) } \u2014 remove and return deleted"},{id:"subscription-event",description:"subscription { itemUpdated } \u2014 real-time push via WebSocket"},{id:"data-loader",description:"Batch and cache DB calls with DataLoader to avoid N+1"},{id:"auth-directive",description:"@auth directive \u2014 enforce authentication on resolvers"},{id:"error-formatting",description:"Consistent GraphQL error shape with extensions and codes"}],contextHint:"Building a GraphQL API. Use DataLoader for all relations, @auth directive on protected resolvers.",scripts:{dev:"ts-node src/server.ts",codegen:"graphql-codegen"}},monorepo:{description:"Monorepo workspace (nx / turborepo / pnpm workspaces)",capabilities:[{id:"build-all",description:"Build all packages in dependency order"},{id:"test-all",description:"Run tests across all packages in parallel"},{id:"lint-all",description:"Lint all packages with shared ESLint / Prettier config"},{id:"publish-packages",description:"Bump versions and publish changed packages to npm"},{id:"shared-ui-library",description:"packages/ui \u2014 shared React component library"},{id:"shared-utils",description:"packages/utils \u2014 shared utility functions and types"},{id:"web-app",description:"apps/web \u2014 primary Next.js or React web application"},{id:"api-service",description:"apps/api \u2014 backend service consumed by apps"},{id:"cache-builds",description:"Remote build cache via Turborepo or Nx Cloud"},{id:"affected-only",description:"Run tasks only for packages affected by a git change"}],contextHint:"Working in a monorepo. Changes in packages/* may affect apps/*. Check affected packages before building.",scripts:{build:"turbo build",dev:"turbo dev",test:"turbo test"}}};function r(){return Object.entries(i).map(([e,t])=>({name:e,description:t.description}))}function a(e){return i[e]||null}export{i as TEMPLATES,a as getTemplate,r as listTemplates};
@@ -1,142 +1 @@
1
- /**
2
- * Shared error handling utilities for infernoflow commands.
3
- *
4
- * Provides consistent, friendly error messages with actionable hints.
5
- */
6
-
7
- import { warn, info, bold, cyan, gray, red } from "./output.mjs";
8
-
9
- // ── Common error types ────────────────────────────────────────────────────────
10
-
11
- export const ERR = {
12
- NO_INFERNO_DIR: "inferno/ directory not found.",
13
- NO_CONTRACT: "No contract.json or capabilities.json found.",
14
- NO_GIT: "This directory is not a git repository.",
15
- NO_NETWORK: "Network request failed.",
16
- INVALID_JSON: "Invalid JSON in response.",
17
- PERMISSION: "Permission denied.",
18
- TIMEOUT: "Command timed out.",
19
- };
20
-
21
- // ── Error formatter ───────────────────────────────────────────────────────────
22
-
23
- export function fatalError(message, hint, jsonMode = false) {
24
- if (jsonMode) {
25
- console.log(JSON.stringify({ ok: false, error: message, hint: hint || undefined }));
26
- } else {
27
- console.error();
28
- console.error(` ${red("✗")} ${bold(message)}`);
29
- if (hint) console.error(` ${gray(hint)}`);
30
- console.error();
31
- }
32
- process.exit(1);
33
- }
34
-
35
- export function softWarn(message, hint, jsonMode = false) {
36
- if (!jsonMode) {
37
- warn(message);
38
- if (hint) console.error(` ${gray(hint)}`);
39
- }
40
- }
41
-
42
- // ── Pre-flight checks ─────────────────────────────────────────────────────────
43
-
44
- import * as fs from "node:fs";
45
- import * as path from "node:path";
46
- import { execSync } from "node:child_process";
47
-
48
- /**
49
- * Ensure inferno/ exists. Exits with a friendly message if not.
50
- */
51
- export function requireInfernoDir(cwd, jsonMode = false) {
52
- const infernoDir = path.join(cwd, "inferno");
53
- if (!fs.existsSync(infernoDir)) {
54
- fatalError(
55
- ERR.NO_INFERNO_DIR,
56
- "Run: infernoflow init (or: infernoflow setup for full setup)",
57
- jsonMode
58
- );
59
- }
60
- return infernoDir;
61
- }
62
-
63
- /**
64
- * Ensure a git repo exists. Exits with a friendly message if not.
65
- */
66
- export function requireGitRepo(cwd, jsonMode = false) {
67
- try {
68
- execSync("git rev-parse --git-dir", { cwd, stdio: "ignore" });
69
- } catch {
70
- fatalError(
71
- ERR.NO_GIT,
72
- "Run: git init && git add . && git commit -m 'init'",
73
- jsonMode
74
- );
75
- }
76
- }
77
-
78
- /**
79
- * Read and parse a JSON file, with a friendly error on failure.
80
- */
81
- export function readJsonFile(filePath, label, jsonMode = false) {
82
- if (!fs.existsSync(filePath)) return null;
83
- try {
84
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
85
- } catch (err) {
86
- fatalError(
87
- `Could not parse ${label}: ${err.message}`,
88
- `Check the file for syntax errors: ${filePath}`,
89
- jsonMode
90
- );
91
- }
92
- }
93
-
94
- /**
95
- * Read contract.json or capabilities.json, whichever exists.
96
- */
97
- export function readContract(infernoDir, jsonMode = false) {
98
- for (const f of ["contract.json", "capabilities.json"]) {
99
- const p = path.join(infernoDir, f);
100
- const data = readJsonFile(p, f, jsonMode);
101
- if (data) return data;
102
- }
103
- fatalError(ERR.NO_CONTRACT, "Run: infernoflow init", jsonMode);
104
- }
105
-
106
- /**
107
- * Parse a semver string. Returns { major, minor, patch } or null.
108
- */
109
- export function parseSemver(v) {
110
- const m = String(v || "").match(/^(\d+)\.(\d+)\.(\d+)/);
111
- if (!m) return null;
112
- return { major: +m[1], minor: +m[2], patch: +m[3], raw: m[0] };
113
- }
114
-
115
- /**
116
- * Validate a URL string. Returns true if valid http/https URL.
117
- */
118
- export function isValidUrl(str) {
119
- try {
120
- const u = new URL(str);
121
- return u.protocol === "http:" || u.protocol === "https:";
122
- } catch { return false; }
123
- }
124
-
125
- /**
126
- * Wrap an async command handler with top-level error catching.
127
- * Prints a friendly message instead of a raw stack trace.
128
- */
129
- export function withErrorHandling(fn, jsonMode = false) {
130
- return fn().catch(err => {
131
- if (jsonMode) {
132
- console.log(JSON.stringify({ ok: false, error: err.message }));
133
- } else {
134
- console.error();
135
- console.error(` ${red("✗")} ${bold("Unexpected error:")} ${err.message}`);
136
- if (process.env.DEBUG) console.error(err.stack);
137
- else console.error(` ${gray("Set DEBUG=1 for a full stack trace.")}`);
138
- console.error();
139
- }
140
- process.exit(1);
141
- });
142
- }
1
+ import{warn as p,bold as a,gray as i,red as l}from"./output.mjs";const s={NO_INFERNO_DIR:"inferno/ directory not found.",NO_CONTRACT:"No contract.json or capabilities.json found.",NO_GIT:"This directory is not a git repository.",NO_NETWORK:"Network request failed.",INVALID_JSON:"Invalid JSON in response.",PERMISSION:"Permission denied.",TIMEOUT:"Command timed out."};function n(o,r,e=!1){e?console.log(JSON.stringify({ok:!1,error:o,hint:r||void 0})):(console.error(),console.error(` ${l("\u2717")} ${a(o)}`),r&&console.error(` ${i(r)}`),console.error()),process.exit(1)}function g(o,r,e=!1){e||(p(o),r&&console.error(` ${i(r)}`))}import*as c from"node:fs";import*as u from"node:path";import{execSync as d}from"node:child_process";function R(o,r=!1){const e=u.join(o,"inferno");return c.existsSync(e)||n(s.NO_INFERNO_DIR,"Run: infernoflow init (or: infernoflow setup for full setup)",r),e}function S(o,r=!1){try{d("git rev-parse --git-dir",{cwd:o,stdio:"ignore"})}catch{n(s.NO_GIT,"Run: git init && git add . && git commit -m 'init'",r)}}function N(o,r,e=!1){if(!c.existsSync(o))return null;try{return JSON.parse(c.readFileSync(o,"utf8"))}catch(t){n(`Could not parse ${r}: ${t.message}`,`Check the file for syntax errors: ${o}`,e)}}function I(o,r=!1){for(const e of["contract.json","capabilities.json"]){const t=u.join(o,e),f=N(t,e,r);if(f)return f}n(s.NO_CONTRACT,"Run: infernoflow init",r)}function $(o){const r=String(o||"").match(/^(\d+)\.(\d+)\.(\d+)/);return r?{major:+r[1],minor:+r[2],patch:+r[3],raw:r[0]}:null}function E(o){try{const r=new URL(o);return r.protocol==="http:"||r.protocol==="https:"}catch{return!1}}function T(o,r=!1){return o().catch(e=>{r?console.log(JSON.stringify({ok:!1,error:e.message})):(console.error(),console.error(` ${l("\u2717")} ${a("Unexpected error:")} ${e.message}`),process.env.DEBUG?console.error(e.stack):console.error(` ${i("Set DEBUG=1 for a full stack trace.")}`),console.error()),process.exit(1)})}export{s as ERR,n as fatalError,E as isValidUrl,$ as parseSemver,I as readContract,N as readJsonFile,S as requireGitRepo,R as requireInfernoDir,g as softWarn,T as withErrorHandling};
@@ -1,72 +1,6 @@
1
- // Zero-dependency color/output utilities using ANSI codes
2
-
3
- const c = {
4
- reset: "\x1b[0m",
5
- bold: "\x1b[1m",
6
- red: "\x1b[31m",
7
- green: "\x1b[32m",
8
- yellow: "\x1b[33m",
9
- blue: "\x1b[34m",
10
- cyan: "\x1b[36m",
11
- white: "\x1b[37m",
12
- gray: "\x1b[90m",
13
- orange: "\x1b[38;5;208m",
14
- };
15
-
16
- const noColor = !process.stdout.isTTY || process.env.NO_COLOR;
17
-
18
- function paint(code, text) {
19
- if (noColor) return text;
20
- return `${code}${text}${c.reset}`;
21
- }
22
-
23
- export const bold = t => paint(c.bold, t);
24
- export const red = t => paint(c.red, t);
25
- export const green = t => paint(c.green, t);
26
- export const yellow = t => paint(c.yellow, t);
27
- export const cyan = t => paint(c.cyan, t);
28
- export const gray = t => paint(c.gray, t);
29
- export const white = t => paint(c.white, t);
30
- export const orange = t => paint(c.orange, t);
31
- export const boldRed = t => paint(c.bold + c.red, t);
32
- export const boldGreen = t => paint(c.bold + c.green, t);
33
- export const boldYellow = t => paint(c.bold + c.yellow, t);
34
- export const boldOrange = t => paint(c.bold + c.orange, t);
35
-
36
- function strip(str) {
37
- return str.replace(/\x1b\[[0-9;]*m/g, "");
38
- }
39
-
40
- export function header(text) {
41
- const title = boldOrange("🔥 infernoflow") + gray(" — " + text);
42
- console.log("\n" + title);
43
- console.log(gray("─".repeat(50)));
44
- }
45
-
46
- export function ok(msg) { console.log(" " + green("✔") + " " + msg); }
47
- export function fail(msg, hint) {
48
- console.log(" " + red("✘") + " " + red(msg));
49
- if (hint) console.log(" " + gray("→ " + hint));
50
- }
51
- export function warn(msg) { console.log(" " + yellow("⚠") + " " + yellow(msg)); }
52
- export function info(msg) { console.log(" " + cyan("ℹ") + " " + msg); }
53
- export function section(title) { console.log("\n" + bold(white(title))); }
54
-
55
- export function done(msg) {
56
- console.log("\n" + boldGreen("✨ " + msg) + "\n");
57
- }
58
-
59
- export function nextSteps(steps) {
60
- console.log(bold("Next steps:"));
61
- steps.forEach((s, i) => {
62
- console.log(" " + gray((i + 1) + ".") + " " + s);
63
- });
64
- console.log();
65
- }
66
-
67
- export function errorAndExit(msg, hint) {
68
- console.error("\n" + boldRed("Error: ") + red(msg));
69
- if (hint) console.error(gray(" → " + hint));
70
- console.error();
71
- process.exit(1);
72
- }
1
+ const e={reset:"\x1B[0m",bold:"\x1B[1m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m",white:"\x1B[37m",gray:"\x1B[90m",orange:"\x1B[38;5;208m"},p=!process.stdout.isTTY||process.env.NO_COLOR;function r(o,n){return p?n:`${o}${n}${e.reset}`}const c=o=>r(e.bold,o),l=o=>r(e.red,o),g=o=>r(e.green,o),s=o=>r(e.yellow,o),i=o=>r(e.cyan,o),t=o=>r(e.gray,o),b=o=>r(e.white,o),u=o=>r(e.orange,o),d=o=>r(e.bold+e.red,o),f=o=>r(e.bold+e.green,o),m=o=>r(e.bold+e.yellow,o),a=o=>r(e.bold+e.orange,o);function w(o){return o.replace(/\x1b\[[0-9;]*m/g,"")}function y(o){const n=a("\u{1F525} infernoflow")+t(" \u2014 "+o);console.log(`
2
+ `+n),console.log(t("\u2500".repeat(50)))}function O(o){console.log(" "+g("\u2714")+" "+o)}function h(o,n){console.log(" "+l("\u2718")+" "+l(o)),n&&console.log(" "+t("\u2192 "+n))}function E(o){console.log(" "+s("\u26A0")+" "+s(o))}function $(o){console.log(" "+i("\u2139")+" "+o)}function C(o){console.log(`
3
+ `+c(b(o)))}function N(o){console.log(`
4
+ `+f("\u2728 "+o)+`
5
+ `)}function R(o){console.log(c("Next steps:")),o.forEach((n,x)=>{console.log(" "+t(x+1+".")+" "+n)}),console.log()}function T(o,n){console.error(`
6
+ `+d("Error: ")+l(o)),n&&console.error(t(" \u2192 "+n)),console.error(),process.exit(1)}export{c as bold,f as boldGreen,a as boldOrange,d as boldRed,m as boldYellow,i as cyan,N as done,T as errorAndExit,h as fail,t as gray,g as green,y as header,$ as info,R as nextSteps,O as ok,u as orange,l as red,C as section,E as warn,b as white,s as yellow};