@0xdevabir/enhance 0.1.1 → 0.1.2

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 (2) hide show
  1. package/dist/index.js +173 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -602,7 +602,7 @@ function truncateLines(content, maxLines) {
602
602
  }
603
603
 
604
604
  // src/enhancer/injectors.ts
605
- function getInjections(stack, intent, config) {
605
+ function getFallbackInjections(stack, intent, config) {
606
606
  const rules = [];
607
607
  if (stack.language === "typescript") {
608
608
  rules.push("Use TypeScript throughout \u2014 no `any`, prefer `unknown` with type guards");
@@ -680,6 +680,31 @@ function getInjections(stack, intent, config) {
680
680
  return rules;
681
681
  }
682
682
 
683
+ // src/enhancer/angles.ts
684
+ var ANGLE_LABELS = {
685
+ completeness: "Completeness",
686
+ production: "Production Safety",
687
+ dx: "Developer Experience",
688
+ alternative: "Alternative Approach"
689
+ };
690
+ var ANGLE_ORDER = ["completeness", "production", "dx", "alternative"];
691
+ function getAngle(iteration) {
692
+ const idx = Math.min(iteration, ANGLE_ORDER.length - 1);
693
+ return ANGLE_ORDER[idx];
694
+ }
695
+ function getAngleFocus(angle) {
696
+ switch (angle) {
697
+ case "completeness":
698
+ return 'Cover every feature, sub-case, and edge case. Assume the reviewer will ask "what about X?" for every possible X. Nothing should be missing.';
699
+ case "production":
700
+ return "Focus on what breaks at 3am: failure modes, error handling, observability, security vulnerabilities, race conditions, data consistency, and retry semantics.";
701
+ case "dx":
702
+ return "Focus on implementation guidance: the correct step-by-step order, what confuses junior developers, integration pitfalls, what to verify first, and common mistakes.";
703
+ case "alternative":
704
+ return "Propose a meaningfully different architecture or pattern than the obvious approach. Explain the trade-off. Challenge the default solution.";
705
+ }
706
+ }
707
+
683
708
  // src/enhancer/template.ts
684
709
  function buildEnhancedPrompt(opts) {
685
710
  const { intent } = opts;
@@ -692,9 +717,30 @@ function buildEnhancedPrompt(opts) {
692
717
  return buildFeaturePrompt(opts);
693
718
  }
694
719
  }
720
+ function buildVersionHeader(opts) {
721
+ const { angle, iteration } = opts;
722
+ if (angle === void 0) return null;
723
+ const v = (iteration ?? 0) + 1;
724
+ const label = ANGLE_LABELS[angle];
725
+ return `\u{1F4CD} v${v} \u2014 ${label} angle`;
726
+ }
727
+ function buildAssumptionsSection(assumptions) {
728
+ if (!assumptions.length) return null;
729
+ return `## Assumptions
730
+
731
+ ${assumptions.map((a) => `- ${a}`).join("\n")}`;
732
+ }
733
+ function buildGotchasSection(gotchas) {
734
+ if (!gotchas.length) return null;
735
+ return `## Watch out for
736
+
737
+ ${gotchas.map((g) => `- ${g}`).join("\n")}`;
738
+ }
695
739
  function buildSimplePrompt(opts) {
696
- const { stack, intent, contextFiles, gitContext, injections, projectInstructions } = opts;
740
+ const { stack, intent, contextFiles, gitContext, injections, projectInstructions, assumptions = [], gotchas = [] } = opts;
697
741
  const sections = [];
742
+ const versionHeader = buildVersionHeader(opts);
743
+ if (versionHeader) sections.push(versionHeader);
698
744
  if (projectInstructions) {
699
745
  sections.push(`## Project Instructions
700
746
 
@@ -710,6 +756,10 @@ ${buildTaskDescription(intent)}`);
710
756
  sections.push(`## Requirements
711
757
 
712
758
  ${rules.map((r) => `- ${r}`).join("\n")}`);
759
+ const assumptionsSection = buildAssumptionsSection(assumptions);
760
+ if (assumptionsSection) sections.push(assumptionsSection);
761
+ const gotchasSection = buildGotchasSection(gotchas);
762
+ if (gotchasSection) sections.push(gotchasSection);
713
763
  if (gitContext) {
714
764
  sections.push(`## Recent Changes
715
765
 
@@ -726,8 +776,10 @@ ${buildOutputFormat(intent)}`);
726
776
  return sections.join("\n\n---\n\n");
727
777
  }
728
778
  function buildFeaturePrompt(opts) {
729
- const { stack, structure, intent, contextFiles, gitContext, injections, projectInstructions } = opts;
779
+ const { stack, structure, intent, contextFiles, gitContext, injections, projectInstructions, assumptions = [], gotchas = [] } = opts;
730
780
  const sections = [];
781
+ const versionHeader = buildVersionHeader(opts);
782
+ if (versionHeader) sections.push(versionHeader);
731
783
  if (projectInstructions) {
732
784
  sections.push(`## Project Instructions
733
785
 
@@ -752,6 +804,10 @@ ${buildTaskDescription(intent)}`);
752
804
  sections.push(`## Requirements
753
805
 
754
806
  ${requirementLines}`);
807
+ const assumptionsSection = buildAssumptionsSection(assumptions);
808
+ if (assumptionsSection) sections.push(assumptionsSection);
809
+ const gotchasSection = buildGotchasSection(gotchas);
810
+ if (gotchasSection) sections.push(gotchasSection);
755
811
  if (gitContext) {
756
812
  sections.push(`## Recent Changes
757
813
 
@@ -768,8 +824,10 @@ ${buildOutputFormat(intent)}`);
768
824
  return sections.join("\n\n---\n\n");
769
825
  }
770
826
  function buildSystemPrompt(opts) {
771
- const { stack, structure, intent, contextFiles, gitContext, injections, projectInstructions } = opts;
827
+ const { stack, structure, intent, contextFiles, gitContext, injections, projectInstructions, assumptions = [], gotchas = [] } = opts;
772
828
  const sections = [];
829
+ const versionHeader = buildVersionHeader(opts);
830
+ if (versionHeader) sections.push(versionHeader);
773
831
  if (projectInstructions) {
774
832
  sections.push(`## Project Instructions
775
833
 
@@ -794,6 +852,10 @@ ${buildTaskDescription(intent)}
794
852
  sections.push(`## Requirements
795
853
 
796
854
  ${requirementLines}`);
855
+ const assumptionsSection = buildAssumptionsSection(assumptions);
856
+ if (assumptionsSection) sections.push(assumptionsSection);
857
+ const gotchasSection = buildGotchasSection(gotchas);
858
+ if (gotchasSection) sections.push(gotchasSection);
797
859
  if (gitContext) {
798
860
  sections.push(`## Recent Changes
799
861
 
@@ -886,10 +948,97 @@ function capitalize(s) {
886
948
  return map[s] ?? s.charAt(0).toUpperCase() + s.slice(1);
887
949
  }
888
950
 
951
+ // src/enhancer/domain-requirements.ts
952
+ import Anthropic from "@anthropic-ai/sdk";
953
+ async function generateDomainEnhancements(rawPrompt, intent, stack, angle, apiKey) {
954
+ const client = new Anthropic({ apiKey });
955
+ const stackSummary = [
956
+ stack.language === "typescript" ? "TypeScript" : "JavaScript",
957
+ ...stack.frameworks,
958
+ stack.orm ? `ORM:${stack.orm}` : null,
959
+ stack.uiLibrary ? `UI:${stack.uiLibrary}` : null
960
+ ].filter(Boolean).join(", ");
961
+ const system = `You are a senior software engineer specializing in ${intent.feature} systems.
962
+ Generate implementation requirements, assumptions, and gotchas for a specific task.
963
+
964
+ CRITICAL RULES for requirements:
965
+ 1. Every requirement must be SPECIFIC to this exact task \u2014 not generic boilerplate
966
+ 2. Ask: "Would this sentence appear in a prompt about a completely different feature?" If yes \u2192 discard
967
+ 3. Angle focus: ${getAngleFocus(angle)}
968
+
969
+ REJECTED examples (too generic \u2014 never include):
970
+ - "Use TypeScript throughout \u2014 no any"
971
+ - "Handle errors explicitly"
972
+ - "Keep components small and focused"
973
+ - "Follow best practices"
974
+
975
+ GOOD examples (specific, expert-level):
976
+ - "Stripe webhooks fire multiple times \u2014 use idempotency keys keyed on event.id to prevent double-processing"
977
+ - "Store payment_intent_id not charge_id \u2014 charge IDs change on retries"
978
+ - "JWT refresh tokens must be rotated on every use to prevent token theft via stolen refresh token reuse"
979
+
980
+ Return ONLY valid JSON \u2014 no markdown, no prose:
981
+ {
982
+ "requirements": ["...", "..."], // 10-13 specific items
983
+ "assumptions": ["Assumed ...", "Assumed ..."], // 3-5 items starting with "Assumed"
984
+ "gotchas": ["gotcha description", "..."] // 2-3 non-obvious things that WILL bite
985
+ }`;
986
+ const user = `Task: "${rawPrompt}"
987
+ Domain: ${intent.feature}
988
+ Stack: ${stackSummary}
989
+ Action: ${intent.action} | Entity: ${intent.entity}
990
+
991
+ Generate requirements, assumptions, and gotchas. JSON only.`;
992
+ const response = await client.messages.create({
993
+ model: "claude-haiku-4-5-20251001",
994
+ max_tokens: 1536,
995
+ system,
996
+ messages: [{ role: "user", content: user }]
997
+ });
998
+ const text = response.content[0]?.type === "text" ? response.content[0].text : "{}";
999
+ try {
1000
+ const parsed = JSON.parse(text);
1001
+ if (Array.isArray(parsed.requirements) && parsed.requirements.length > 0) {
1002
+ return {
1003
+ requirements: parsed.requirements,
1004
+ assumptions: Array.isArray(parsed.assumptions) ? parsed.assumptions : [],
1005
+ gotchas: Array.isArray(parsed.gotchas) ? parsed.gotchas : []
1006
+ };
1007
+ }
1008
+ throw new Error("empty requirements");
1009
+ } catch {
1010
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
1011
+ if (jsonMatch) {
1012
+ const parsed = JSON.parse(jsonMatch[0]);
1013
+ return {
1014
+ requirements: Array.isArray(parsed.requirements) ? parsed.requirements : [],
1015
+ assumptions: Array.isArray(parsed.assumptions) ? parsed.assumptions : [],
1016
+ gotchas: Array.isArray(parsed.gotchas) ? parsed.gotchas : []
1017
+ };
1018
+ }
1019
+ throw new Error("Failed to parse domain enhancements from AI response");
1020
+ }
1021
+ }
1022
+
889
1023
  // src/enhancer/index.ts
890
- function enhance(rawPrompt, scan, contextFiles, projectInstructions = "", gitContext = "", intentOverride, config) {
1024
+ async function enhance(rawPrompt, scan, contextFiles, projectInstructions = "", gitContext = "", intentOverride, config, iteration = 0, _previousPrompt) {
891
1025
  const intent = intentOverride ?? analyzeIntent(rawPrompt);
892
- const injections = getInjections(scan.stack, intent, config);
1026
+ const angle = getAngle(iteration);
1027
+ let injections;
1028
+ let assumptions = [];
1029
+ let gotchas = [];
1030
+ if (config?.apiKey) {
1031
+ try {
1032
+ const domain = await generateDomainEnhancements(rawPrompt, intent, scan.stack, angle, config.apiKey);
1033
+ injections = domain.requirements;
1034
+ assumptions = domain.assumptions;
1035
+ gotchas = domain.gotchas;
1036
+ } catch {
1037
+ injections = getFallbackInjections(scan.stack, intent, config);
1038
+ }
1039
+ } else {
1040
+ injections = getFallbackInjections(scan.stack, intent, config);
1041
+ }
893
1042
  const enhanced = buildEnhancedPrompt({
894
1043
  stack: scan.stack,
895
1044
  structure: scan.structure,
@@ -897,15 +1046,19 @@ function enhance(rawPrompt, scan, contextFiles, projectInstructions = "", gitCon
897
1046
  contextFiles,
898
1047
  injections,
899
1048
  projectInstructions,
900
- gitContext
1049
+ gitContext,
1050
+ angle,
1051
+ assumptions,
1052
+ gotchas,
1053
+ iteration
901
1054
  });
902
- return { original: rawPrompt, enhanced, intent, stack: scan.stack };
1055
+ return { original: rawPrompt, enhanced, intent, stack: scan.stack, iteration, angle: ANGLE_LABELS[angle] };
903
1056
  }
904
1057
 
905
1058
  // src/enhancer/planner.ts
906
- import Anthropic from "@anthropic-ai/sdk";
1059
+ import Anthropic2 from "@anthropic-ai/sdk";
907
1060
  async function decomposeToPlan(rawPrompt, intent, stack, apiKey) {
908
- const client = new Anthropic({ apiKey });
1061
+ const client = new Anthropic2({ apiKey });
909
1062
  const stackSummary = `${stack.frameworks.join(", ")} / ${stack.language}`;
910
1063
  const systemPrompt = `You are a software architect. Break complex development tasks into sequential, focused implementation steps.
911
1064
  Each step should be small enough to implement in a single AI session.
@@ -952,7 +1105,7 @@ Rules:
952
1105
  }
953
1106
 
954
1107
  // src/providers/claude.ts
955
- import Anthropic2 from "@anthropic-ai/sdk";
1108
+ import Anthropic3 from "@anthropic-ai/sdk";
956
1109
  var MODEL_BY_COMPLEXITY = {
957
1110
  simple: "claude-haiku-4-5-20251001",
958
1111
  feature: "claude-sonnet-4-6",
@@ -969,7 +1122,7 @@ var ClaudeProvider = class {
969
1122
  "ANTHROPIC_API_KEY environment variable is not set."
970
1123
  );
971
1124
  }
972
- this.client = new Anthropic2({ apiKey: config.apiKey });
1125
+ this.client = new Anthropic3({ apiKey: config.apiKey });
973
1126
  this.model = config.model;
974
1127
  }
975
1128
  async send(prompt, options) {
@@ -1230,7 +1383,7 @@ Examples:
1230
1383
  enhance "fix the auth bug" --dry-run
1231
1384
  enhance "create dashboard" --confirm --verbose
1232
1385
  enhance "refactor user service" --provider codex
1233
- enhance "add auth" --action add --feature auth`).option("-p, --provider <name>", "AI provider: claude | codex | opencode").option("--dry-run", "Print enhanced prompt without sending to AI").option("--print-prompt", "Print enhanced prompt before sending to AI").option("--preview", "Color-coded prompt preview with per-section token counts").option("--verbose", "Show detected stack, intent, and context stats").option("--confirm", "Show detected intent and ask for approval before enhancing").option("--plan", "Decompose complex task into sequential sub-prompts (uses AI)").option("--action <action>", "Override detected action (create|fix|refactor|explain|add|delete)").option("--entity <entity>", "Override detected entity (page|component|api|hook|util|config|style)").option("--feature <feature>", "Override detected feature (auth|payment|user|upload|...)").action(async (rawPrompt, opts) => {
1386
+ enhance "add auth" --action add --feature auth`).option("-p, --provider <name>", "AI provider: claude | codex | opencode").option("--dry-run", "Print enhanced prompt without sending to AI").option("--print-prompt", "Print enhanced prompt before sending to AI").option("--preview", "Color-coded prompt preview with per-section token counts").option("--verbose", "Show detected stack, intent, and context stats").option("--confirm", "Show detected intent and ask for approval before enhancing").option("--plan", "Decompose complex task into sequential sub-prompts (uses AI)").option("--iteration <n>", "Angle iteration: 0=completeness, 1=production, 2=dx, 3=alternative", "0").option("--action <action>", "Override detected action (create|fix|refactor|explain|add|delete)").option("--entity <entity>", "Override detected entity (page|component|api|hook|util|config|style)").option("--feature <feature>", "Override detected feature (auth|payment|user|upload|...)").action(async (rawPrompt, opts) => {
1234
1387
  if (!rawPrompt) {
1235
1388
  program.help();
1236
1389
  return;
@@ -1301,8 +1454,14 @@ Examples:
1301
1454
  spinner.start("Enhancing prompt...");
1302
1455
  }
1303
1456
  spinner.text = "Enhancing prompt...";
1304
- const enhanced = enhance(rawPrompt, scan, contextFiles, projectInstructions, gitContext, intent, config);
1457
+ const iteration = Math.max(0, parseInt(opts.iteration ?? "0", 10) || 0);
1458
+ const enhanced = await enhance(rawPrompt, scan, contextFiles, projectInstructions, gitContext, intent, config, iteration);
1305
1459
  spinner.stop();
1460
+ if (enhanced.angle) {
1461
+ console.log(chalk3.dim(`
1462
+ \u{1F4CD} v${iteration + 1} \u2014 ${enhanced.angle} angle
1463
+ `));
1464
+ }
1306
1465
  if (opts.preview) {
1307
1466
  printColoredPreview(enhanced.enhanced);
1308
1467
  if (opts.dryRun) process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xdevabir/enhance",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "AI prompt middleware — intelligently rewrites developer prompts before sending to coding agents",
5
5
  "bin": {
6
6
  "enhance": "./bin/enhance.js"