avocavo 1.1.3 → 1.2.1
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.
- package/CHANGELOG.md +7 -0
- package/README.md +4 -4
- package/bin/avocavo.js +91 -6
- package/lib/api.js +11 -0
- package/lib/formatters.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the Avocavo CLI will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.2.1] - 2026-03-31
|
|
6
|
+
|
|
7
|
+
### ✨ Conversational Text Analysis
|
|
8
|
+
- **NEW**: Added `avocavo analyze <text>` command to seamlessly parse free-form, conversational meal text.
|
|
9
|
+
- **ENHANCED**: Implemented GPT-backed context inference to automatically resolve missing quantities (e.g., "butter" -> "1 pat butter") instead of reverting to 100g defaults.
|
|
10
|
+
- **IMPROVED**: Standardized JSON output structure to match the `/batch` endpoint schema for unified downstream processing.
|
|
11
|
+
|
|
5
12
|
## [1.1.1] - 2025-08-21
|
|
6
13
|
|
|
7
14
|
### ✨ UPC/Barcode Search Support
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🥑 Avocavo CLI
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Structured nutrition data API with USDA-based calculations where applicable. Consistent nutrition estimates for apps and workflows.
|
|
4
4
|
|
|
5
5
|
## 🚀 Quick Start
|
|
6
6
|
|
|
@@ -27,7 +27,7 @@ avocavo --help
|
|
|
27
27
|
- 🥗 **USDA Database** - Access to comprehensive nutrition data
|
|
28
28
|
- 🧮 **Batch Analysis** - Analyze multiple ingredients at once
|
|
29
29
|
- 📊 **Rich Output** - Beautiful tables and JSON formats
|
|
30
|
-
- 🔒 **SSL Security** - All connections use
|
|
30
|
+
- 🔒 **SSL Security** - All connections use secured HTTPS
|
|
31
31
|
|
|
32
32
|
## 📦 Installation
|
|
33
33
|
|
|
@@ -58,7 +58,7 @@ Analyze a single ingredient:
|
|
|
58
58
|
```bash
|
|
59
59
|
avocavo ingredient "1 cup brown rice"
|
|
60
60
|
avocavo ingredient "200g chicken breast"
|
|
61
|
-
avocavo ingredient "1 cup rice" -v # Include USDA
|
|
61
|
+
avocavo ingredient "1 cup rice" -v # Include USDA FDC source URL
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
### `avocavo recipe [options]`
|
|
@@ -97,7 +97,7 @@ Remove stored credentials.
|
|
|
97
97
|
|
|
98
98
|
## 🔒 Security
|
|
99
99
|
|
|
100
|
-
- **SSL Verification**: All API calls use
|
|
100
|
+
- **SSL Verification**: All API calls use secured HTTPS connections
|
|
101
101
|
- **Secure Storage**: Credentials stored in system keychain (not plaintext)
|
|
102
102
|
- **OAuth Flow**: Secure browser-based authentication
|
|
103
103
|
- **No Hardcoded Secrets**: All sensitive data handled securely
|
package/bin/avocavo.js
CHANGED
|
@@ -57,7 +57,7 @@ function secureReadFile(filePath) {
|
|
|
57
57
|
|
|
58
58
|
program
|
|
59
59
|
.name('avocavo')
|
|
60
|
-
.description('Avocavo Nutrition API CLI -
|
|
60
|
+
.description('Avocavo Nutrition API CLI - Structured nutrition data with USDA-based calculations and ingredient normalization')
|
|
61
61
|
.version(version);
|
|
62
62
|
|
|
63
63
|
// Global options
|
|
@@ -346,7 +346,7 @@ keysCmd
|
|
|
346
346
|
program
|
|
347
347
|
.command('ingredient <ingredient>')
|
|
348
348
|
.description('Analyze a single ingredient for nutrition data')
|
|
349
|
-
.option('-v, --verify', 'Include USDA
|
|
349
|
+
.option('-v, --verify', 'Include USDA FDC source URL')
|
|
350
350
|
.option('--verbose', 'Show detailed performance metrics and USDA info')
|
|
351
351
|
.option('--debug', 'Show all available data (same as --json)')
|
|
352
352
|
.option('--timing', 'Show processing time')
|
|
@@ -441,7 +441,7 @@ program
|
|
|
441
441
|
.option('-s, --servings <number>', 'Number of servings', '1')
|
|
442
442
|
.option('-i, --ingredients <ingredients...>', 'Recipe ingredients')
|
|
443
443
|
.option('-f, --file <file>', 'Read ingredients from file (one per line)')
|
|
444
|
-
.option('-v, --verify', 'Include USDA
|
|
444
|
+
.option('-v, --verify', 'Include USDA FDC source URLs for ingredients')
|
|
445
445
|
.option('--verbose', 'Show detailed performance metrics and USDA info')
|
|
446
446
|
.option('--debug', 'Show all available data (same as --json)')
|
|
447
447
|
.action(async (options) => {
|
|
@@ -529,7 +529,7 @@ program
|
|
|
529
529
|
|
|
530
530
|
// Clean default: just show count
|
|
531
531
|
const usdaCount = ingredientsWithUSDA.length;
|
|
532
|
-
console.log(chalk.gray(`🎯 USDA
|
|
532
|
+
console.log(chalk.gray(`🎯 USDA matched: ${usdaCount}/${ingredients.length} ingredients`));
|
|
533
533
|
|
|
534
534
|
// Detailed USDA info with --verbose or --verify
|
|
535
535
|
if (options.verbose || options.verify) {
|
|
@@ -641,7 +641,7 @@ program
|
|
|
641
641
|
// Show USDA verification details for successful ingredients
|
|
642
642
|
if (result.results.some(item => item.success && item.metadata?.usda_match)) {
|
|
643
643
|
console.log('');
|
|
644
|
-
console.log(chalk.bold('🔗 USDA
|
|
644
|
+
console.log(chalk.bold('🔗 USDA Sources:'));
|
|
645
645
|
result.results.forEach(item => {
|
|
646
646
|
if (item.success && item.metadata?.usda_match) {
|
|
647
647
|
const usda = item.metadata.usda_match;
|
|
@@ -663,6 +663,91 @@ program
|
|
|
663
663
|
}
|
|
664
664
|
});
|
|
665
665
|
|
|
666
|
+
// Free-text analyze command
|
|
667
|
+
program
|
|
668
|
+
.command('analyze <text>')
|
|
669
|
+
.description('Analyze free-form meal text — AI extracts and looks up each ingredient automatically')
|
|
670
|
+
.option('--verbose', 'Show detailed USDA info for each ingredient')
|
|
671
|
+
.action(async (text, options) => {
|
|
672
|
+
try {
|
|
673
|
+
const api = await getApiClient();
|
|
674
|
+
const globalOpts = program.opts();
|
|
675
|
+
const verbose = options.verbose || globalOpts.verbose;
|
|
676
|
+
|
|
677
|
+
console.log(chalk.cyan(`🤖 Analyzing: "${text}"...`));
|
|
678
|
+
|
|
679
|
+
const result = await api.analyzeText(text, verbose);
|
|
680
|
+
|
|
681
|
+
if (program.opts().json) {
|
|
682
|
+
console.log(JSON.stringify(result, null, 2));
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (result.success) {
|
|
687
|
+
// Show what the AI extracted
|
|
688
|
+
const extracted = result.extracted_ingredients || [];
|
|
689
|
+
if (extracted.length > 0) {
|
|
690
|
+
console.log('');
|
|
691
|
+
console.log(chalk.bold('🔍 Detected ingredients:'));
|
|
692
|
+
extracted.forEach(ing => console.log(chalk.gray(` • ${ing}`)));
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
console.log('');
|
|
696
|
+
console.log(chalk.green(`✅ Analysis complete — ${result.summary.successful}/${result.batch_size || extracted.length} items matched`));
|
|
697
|
+
console.log('');
|
|
698
|
+
|
|
699
|
+
// Results table (same style as batch)
|
|
700
|
+
const tableData = (result.results || []).map(item => [
|
|
701
|
+
item.success ? '✅' : '❌',
|
|
702
|
+
item.ingredient,
|
|
703
|
+
item.success ? `${item.nutrition.calories}` : 'N/A',
|
|
704
|
+
item.success ? `${item.nutrition.protein}g` : 'N/A',
|
|
705
|
+
item.success ? `${item.nutrition.total_fat}g` : 'N/A',
|
|
706
|
+
item.success ? `${item.nutrition.carbohydrates}g` : 'N/A',
|
|
707
|
+
item.success ? `${item.nutrition.fiber}g` : 'N/A',
|
|
708
|
+
item.success ? `${item.nutrition.sodium}mg` : 'N/A'
|
|
709
|
+
]);
|
|
710
|
+
|
|
711
|
+
if (tableData.length > 0) {
|
|
712
|
+
console.log(formatTable([
|
|
713
|
+
['Status', 'Ingredient', 'Calories', 'Protein', 'Fat', 'Carbs', 'Fiber', 'Sodium'],
|
|
714
|
+
...tableData
|
|
715
|
+
]));
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Total calories summary
|
|
719
|
+
const totalCal = (result.results || [])
|
|
720
|
+
.filter(i => i.success)
|
|
721
|
+
.reduce((sum, i) => sum + (i.nutrition?.calories || 0), 0);
|
|
722
|
+
if (totalCal > 0) {
|
|
723
|
+
console.log('');
|
|
724
|
+
console.log(chalk.bold(`🔥 Total Calories: ${chalk.yellow(totalCal.toFixed(1))} kcal`));
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// USDA sources (with --verbose)
|
|
728
|
+
if (verbose) {
|
|
729
|
+
const withUSDA = (result.results || []).filter(i => i.success && i.metadata?.usda_match);
|
|
730
|
+
if (withUSDA.length > 0) {
|
|
731
|
+
console.log('');
|
|
732
|
+
console.log(chalk.bold('🔗 USDA Sources:'));
|
|
733
|
+
withUSDA.forEach(item => {
|
|
734
|
+
const usda = item.metadata.usda_match;
|
|
735
|
+
console.log(chalk.gray(` ${item.ingredient} → ${usda.description} (FDC: ${usda.fdc_id})`));
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
} else {
|
|
741
|
+
console.log(chalk.red(`❌ ${result.error || 'Analysis failed'}`));
|
|
742
|
+
console.log(chalk.cyan('💡 Try rephrasing or use: avocavo batch --ingredients "item1" "item2"'));
|
|
743
|
+
process.exit(1);
|
|
744
|
+
}
|
|
745
|
+
} catch (error) {
|
|
746
|
+
console.error(chalk.red(`❌ Analyze error: ${error.message}`));
|
|
747
|
+
process.exit(1);
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
|
|
666
751
|
// UPC/Barcode search command
|
|
667
752
|
program
|
|
668
753
|
.command('upc <upc>')
|
|
@@ -1014,7 +1099,7 @@ Examples:
|
|
|
1014
1099
|
$ avocavo keys switch # Switch active API key (interactive)
|
|
1015
1100
|
$ avocavo keys delete # Delete API key (interactive)
|
|
1016
1101
|
$ avocavo ingredient "1 cup rice" # Analyze single ingredient
|
|
1017
|
-
$ avocavo ingredient "1 cup rice" -v # Include USDA
|
|
1102
|
+
$ avocavo ingredient "1 cup rice" -v # Include USDA FDC source URL
|
|
1018
1103
|
$ avocavo recipe -i "2 cups flour" "1 cup milk" -s 8 # Analyze recipe
|
|
1019
1104
|
$ avocavo batch -i "1 cup rice" "2 tbsp oil" "4 oz chicken" # Batch analysis
|
|
1020
1105
|
$ avocavo upc "041196912395" # Search UPC/barcode
|
package/lib/api.js
CHANGED
|
@@ -41,6 +41,17 @@ class NutritionAPI {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
async analyzeText(text, verbose = false) {
|
|
45
|
+
try {
|
|
46
|
+
const response = await this.client.post(`${this.baseUrl}/api/v2/nutrition/analyze`, {
|
|
47
|
+
text: text
|
|
48
|
+
});
|
|
49
|
+
return response.data;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw this.handleError(error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
async analyzeRecipe(ingredients, servings = 1, verbose = false) {
|
|
45
56
|
try {
|
|
46
57
|
const response = await this.client.post(`${this.baseUrl}/api/v2/nutrition/recipe`, {
|
package/lib/formatters.js
CHANGED
|
@@ -148,7 +148,7 @@ function formatUSDAMatch(usda, verificationUrl) {
|
|
|
148
148
|
lines.push(` 🔗 Verify: ${chalk.underline(verificationUrl)}`);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
return '\n' + chalk.gray('USDA
|
|
151
|
+
return '\n' + chalk.gray('USDA Source:') + '\n' + lines.join('\n');
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
function formatTable(data, options = {}) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "avocavo",
|
|
3
|
-
"version": "1.1
|
|
4
|
-
"description": "Avocavo CLI -
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "Avocavo CLI - Structured nutrition data API with USDA-based calculations where applicable. Consistent nutrition estimates for apps and workflows. For informational use only. Not affiliated with or endorsed by USDA.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"avocavo": "bin/avocavo.js"
|