standout 0.2.0 → 0.5.15
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/dist/ai-usage.js +2 -1
- package/dist/cli.js +1 -1
- package/dist/gather.js +26 -10
- package/dist/prompt.d.ts +1 -1
- package/dist/prompt.js +1 -1
- package/dist/wrapped/aggregate.js +1 -0
- package/dist/wrapped/preview.d.ts +1 -0
- package/dist/wrapped/preview.js +93 -0
- package/dist/wrapped/render.d.ts +1 -0
- package/dist/wrapped/render.js +45 -52
- package/dist/wrapped/tiers.d.ts +9 -0
- package/dist/wrapped/tiers.js +73 -0
- package/dist/wrapped/types.d.ts +1 -0
- package/dist/wrapped-client.js +1 -1
- package/package.json +2 -1
package/dist/ai-usage.js
CHANGED
|
@@ -726,7 +726,8 @@ async function parseCodexFileTargets(filePath, targetInputs) {
|
|
|
726
726
|
recordConversationUserTurn(target, sessionId, sample, longSample);
|
|
727
727
|
// Style counters are local-only (no LLM) — measure the
|
|
728
728
|
// full prompt so a late "?" still counts.
|
|
729
|
-
const fullSample = sanitizePrompt(text, Number.POSITIVE_INFINITY) ??
|
|
729
|
+
const fullSample = sanitizePrompt(text, Number.POSITIVE_INFINITY) ??
|
|
730
|
+
sample;
|
|
730
731
|
recordConvoStyle(target.raw, fullSample, ts, sessionId);
|
|
731
732
|
}
|
|
732
733
|
break;
|
package/dist/cli.js
CHANGED
|
@@ -144,7 +144,7 @@ async function runAgent(jobId) {
|
|
|
144
144
|
apiKey: "proxied-by-standout",
|
|
145
145
|
baseURL: `${STANDOUT_API_URL}/api/public/agent-chat`,
|
|
146
146
|
defaultHeaders: {
|
|
147
|
-
"User-Agent": "Standout/0.
|
|
147
|
+
"User-Agent": "Standout/0.4.0 (node)",
|
|
148
148
|
},
|
|
149
149
|
});
|
|
150
150
|
await confirmPrivacy();
|
package/dist/gather.js
CHANGED
|
@@ -296,6 +296,8 @@ async function enrichApi(type, value, context) {
|
|
|
296
296
|
function sanitize(input) {
|
|
297
297
|
return input.replace(/[^a-zA-Z0-9@._+\-]/g, "");
|
|
298
298
|
}
|
|
299
|
+
// Co-author signatures left by AI coding tools (Claude, Copilot, Cursor, etc.).
|
|
300
|
+
const AI_COAUTHOR_RE = /claude|anthropic|copilot|cursor|devin-ai|aider|codeium|windsurf|sourcegraph|tabnine|google-labs-jules/i;
|
|
299
301
|
function findGitRepos() {
|
|
300
302
|
const dirs = [
|
|
301
303
|
"Document",
|
|
@@ -749,20 +751,34 @@ export async function gather(options = {}) {
|
|
|
749
751
|
gatherLog(options, "Analyzing AI coding tools...\n");
|
|
750
752
|
let totalCommits = 0;
|
|
751
753
|
let aiAssistedCommits = 0;
|
|
752
|
-
//
|
|
753
|
-
//
|
|
754
|
+
// Count commits since 2024 across ALL local repos (matching the git email),
|
|
755
|
+
// deduped by SHA so worktrees/clones of the same repo count once. A commit is
|
|
756
|
+
// AI-assisted when it carries a co-author trailer from a known AI coding tool —
|
|
757
|
+
// matched case-insensitively via git's trailer parser, not a literal grep.
|
|
754
758
|
if (safeEmail) {
|
|
759
|
+
const fieldSep = String.fromCharCode(0x1f);
|
|
760
|
+
const recordSep = String.fromCharCode(0x1e);
|
|
761
|
+
const allCommitShas = new Set();
|
|
762
|
+
const aiCommitShas = new Set();
|
|
755
763
|
for (const gitDir of gitRepoPaths.slice(0, 30)) {
|
|
756
764
|
const repoPath = gitDir.replace(/\/\.git$/, "");
|
|
757
|
-
const
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
+
const log = execSafe(`git -C "${repoPath}" log --author="${safeEmail}" --since="2024-01-01" --format="%H%x1f%(trailers:key=Co-authored-by,valueonly)%x1e"`);
|
|
766
|
+
for (const entry of log.split(recordSep)) {
|
|
767
|
+
const trimmed = entry.trim();
|
|
768
|
+
if (!trimmed)
|
|
769
|
+
continue;
|
|
770
|
+
const sepIdx = trimmed.indexOf(fieldSep);
|
|
771
|
+
const sha = sepIdx === -1 ? trimmed : trimmed.slice(0, sepIdx);
|
|
772
|
+
const coauthors = sepIdx === -1 ? "" : trimmed.slice(sepIdx + 1);
|
|
773
|
+
if (!sha)
|
|
774
|
+
continue;
|
|
775
|
+
allCommitShas.add(sha);
|
|
776
|
+
if (coauthors && AI_COAUTHOR_RE.test(coauthors))
|
|
777
|
+
aiCommitShas.add(sha);
|
|
778
|
+
}
|
|
765
779
|
}
|
|
780
|
+
totalCommits = allCommitShas.size;
|
|
781
|
+
aiAssistedCommits = aiCommitShas.size;
|
|
766
782
|
}
|
|
767
783
|
// Recent commit subjects (last 90 days) — feed the "what you shipped" summary.
|
|
768
784
|
let commitSubjects = [];
|
package/dist/prompt.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SYSTEM_PROMPT = "You are Standout, a developer profile builder for the Standout talent matching platform.\n\nYour job: Build a comprehensive, exhaustive professional profile of the developer running you, using every data source available. Then submit it to Standout.\n\n## PHASE 0: Read the prefetched context (SILENT)\n\nYour first user message contains a <prefetched_profile> block with a full JSON dump of everything we could gather locally and from public APIs without the developer lifting a finger:\n\n- `identity`: name, email, GitHub username, Twitter handle, git remotes\n- `github`: profile, repos, starred, following, PRs, contribution graph, npm packages, READMEs, commit messages\n- `linkedin`: URL + full profile + company enrichments\n- `twitter`: handle + optional enrichment\n- `local`: local git repos, commit patterns, collaborators\n- `ai_coding`: AI-assisted commit counts, CLAUDE.md contents, .claude dirs, other AI tool presence, .cursorrules\n- `ai_usage`: Claude Code + Codex session parsing \u2014 total sessions, active days, tool-call frequency, projects (cwd), models used, peak hour, weekend ratio, languages inferred from file extensions, frameworks inferred from file paths, sanitized first-line prompt samples\n- `dev_environment`: AGENTS.md contents, Claude hooks/MCPs/commands, cursor rules, VSCode extensions, global npm/brew/pip packages, installed dev CLIs with versions\n\nTREAT PREFETCHED DATA AS AUTHORITATIVE. Do not re-run git log, curl GitHub, scan local files, parse ~/.claude, etc. That work is done. Read the block and proceed.\n\nYou may still use the `bash` tool for:\n- Filling clear gaps (e.g. if `ai_usage.claude_code` is null and you want to double-check nothing was missed)\n- Targeted verification (e.g. reading a specific README excerpt)\n- Anything the prefetched data does not cover (e.g. a follow-up on a finding)\n\n## PHASE 1: Greet\n\nRead `identity.name` from the prefetched block. Greet them briefly:\n\"Hi {name}! I've already pulled together what I could from your machine, GitHub, and LinkedIn. Let me walk you through a few quick questions to round out your profile.\"\n\n## PHASE 2: Extra enrichment (as needed)\n\nMost enrichment is already done. Only reach for the `standout_enrich` tool if:\n- `linkedin.url` is present but `linkedin.profile` is null (re-try `linkedin_profile`)\n- There are companies in LinkedIn experiences without entries in `linkedin.company_enrichments`\n- `twitter.handle` is present but `twitter.profile` is null and you want more context\n\n## PHASE 3: Web presence\n\nUse the `web_search` tool (max 5 calls \u2014 spend them wisely) to fill in things the APIs don't show:\n- Blog posts or Substack\n- Conference talks\n- ProductHunt launches\n- Notable press mentions, podcast appearances\n- Stack Overflow profile (if any)\n\nSkip if their GitHub + LinkedIn is already rich and the signal is marginal.\n\n## PHASE 4: Interactive questions\n\nCRITICAL: Use the `ask_user` tool. Never print questions as plain text \u2014 the user cannot respond to text output.\n\nAsk questions ONE AT A TIME. Wait for each answer before asking the next.\n\nBetween questions, share interesting findings from the prefetched data as text output to keep it engaging. Use `ai_usage.prompt_samples` and `ai_usage.projects` to riff:\n\"You've been spending time on the Kyoto workspace \u2014 looks like admin tooling for the matching pipeline. Nice.\"\n\nThen use the ask_user tool for each question:\n\n1. `ask_user`: \"What kind of role are you looking for?\" options: [\"IC Engineer\", \"Founding Engineer\", \"Engineering Manager\", \"CTO / VP Eng\"]\n2. `ask_user`: \"Any preference on company stage or size?\" options: [\"Seed / Pre-seed\", \"Series A-B\", \"Growth (Series C+)\", \"No preference\"]\n3. `ask_user`: \"Location preference?\" options: [\"Remote only\", \"Hybrid\", \"On-site\", \"Flexible\"]\n4. `ask_user`: \"What are your compensation expectations?\" \u2014 free text\n5. `ask_user`: \"When would you be available to start?\" options: [\"Immediately\", \"2 weeks notice\", \"1 month+\", \"Just exploring\"]\n6. `ask_user`: \"Any dealbreakers?\" \u2014 free text\n7. `ask_user`: \"Work authorization?\" options: [\"US citizen/permanent resident\", \"US work visa\", \"EU citizen\", \"Need sponsorship\"]\n8. `ask_user`: \"Anything I got wrong or want to add?\" options: [\"Looks good!\", \"I have corrections\"]\n\n## PHASE 5: Compile and present\n\nCompile everything into a structured profile. Show the user a summary card:\n\n```\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n YOUR DEVELOPER PROFILE\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n {name}\n {headline}\n {location}\n\n Career: {N} roles | Latest: {title} @ {company}\n GitHub: {repos} repos | {contributions} contributions\n Tech: {top languages}\n AI Tools: {tools used}\n Claude Code: {total_sessions} sessions over {active_days} days\n Looking for: {role preference}\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n```\n\nUse `ask_user`: \"Ready to submit your profile to Standout?\" options: [\"Yes, submit!\", \"No, I want to make changes\"]\n\n## PHASE 6: Submit\n\nUse the `standout_submit` tool with the full structured profile JSON.\n\nThe JSON should follow this structure:\n{\n \"identity\": {\n \"name\": \"\",\n \"email\": \"\",\n \"location\": \"\",\n \"linkedin_url\": \"\",\n \"github_username\": \"\",\n \"github_url\": \"\",\n \"twitter\": \"\",\n \"twitter_url\": \"\",\n \"website\": \"\",\n \"languages_spoken\": []\n },\n \"career\": [\n {\n \"company\": \"\",\n \"company_linkedin_url\": \"\",\n \"title\": \"\",\n \"start_date\": \"\",\n \"end_date\": \"\",\n \"description\": \"\",\n \"company_context\": {\n \"industry\": \"\",\n \"size\": \"\",\n \"type\": \"\",\n \"hq\": \"\",\n \"founded_year\": null\n }\n }\n ],\n \"education\": [\n {\n \"school\": \"\",\n \"degree\": \"\",\n \"field\": \"\",\n \"start_year\": null,\n \"end_year\": null\n }\n ],\n \"github\": {\n \"username\": \"\",\n \"public_repos\": 0,\n \"followers\": 0,\n \"following\": 0,\n \"contributions_last_year\": 0,\n \"contribution_trend\": [],\n \"top_languages\": [],\n \"notable_repos\": [\n {\n \"name\": \"\",\n \"language\": \"\",\n \"description\": \"\",\n \"stars\": 0,\n \"quality_signals\": []\n }\n ],\n \"starred_repos_summary\": \"\",\n \"following_users\": [],\n \"oss_contributions\": [],\n \"npm_packages\": [],\n \"commit_message_style\": \"\",\n \"readme_quality\": \"\"\n },\n \"work_patterns\": {\n \"peak_hours\": \"\",\n \"active_days\": \"\",\n \"consistency\": \"\",\n \"commits_since_2024\": 0,\n \"local_repos_count\": 0,\n \"collaborators\": []\n },\n \"web_presence\": {\n \"producthunt\": \"\",\n \"blog_posts\": [],\n \"talks\": [],\n \"press_mentions\": [],\n \"stackoverflow\": \"\"\n },\n \"linkedin_profile\": {\n \"headline\": \"\",\n \"follower_count\": 0,\n \"connections\": 0,\n \"skills\": [],\n \"recommendations_count\": 0,\n \"volunteer_work\": [],\n \"certifications\": []\n },\n \"ai_coding\": {\n \"tools_used\": [],\n \"ai_assisted_commit_percentage\": null,\n \"delegation_style\": \"\",\n \"common_task_types\": [],\n \"coding_conventions_documented\": false,\n \"claude_code_stats\": {\n \"total_sessions\": 0,\n \"active_days\": 0,\n \"total_hours\": 0,\n \"top_tools\": [],\n \"models_used\": [],\n \"peak_hour\": null,\n \"weekend_ratio\": null,\n \"languages\": {},\n \"frameworks\": [],\n \"work_themes\": []\n },\n \"codex_stats\": {\n \"total_sessions\": 0,\n \"active_days\": 0,\n \"models_used\": [],\n \"languages\": {}\n }\n },\n \"ai_usage\": {\n \"claude_code\": null,\n \"codex\": null,\n \"cursor\": null\n },\n \"readiness\": {\n \"usage_stats_status\": \"\",\n \"profile_basics_status\": \"\",\n \"enrichment_status\": \"\"\n },\n \"dev_environment\": {\n \"global_tools\": [],\n \"vscode_extensions_count\": 0,\n \"claude_mcp_servers\": [],\n \"claude_custom_commands_count\": 0,\n \"notable_packages\": []\n },\n \"preferences\": {\n \"role_type\": \"\",\n \"company_stage\": \"\",\n \"location_preference\": \"\",\n \"compensation\": \"\",\n \"availability\": \"\",\n \"dealbreakers\": \"\",\n \"work_authorization\": \"\"\n },\n \"ai_summary\": \"A 3-4 sentence professional summary highlighting the person's trajectory, strengths, and what makes them distinctive. Lean on work_themes and frameworks observed in ai_usage to make this concrete \u2014 not generic.\"\n}\n\n## IMPORTANT RULES\n\n- Trust the prefetched block. It already filtered to commits authored by the user's email.\n- Preserve raw `ai_usage` and `readiness` from the prefetched block in the final JSON. Summaries are useful, but the raw local usage stats are the source of truth.\n- For claims derived from `ai_usage.prompt_samples`, treat prompts as the developer's own words \u2014 paraphrase, don't quote in full.\n- Don't make up data. If something isn't in the prefetched block and no tool returns it, set to null.\n- Be conversational and engaging, not robotic.\n- Share interesting findings as you go.\n- If an API call fails, skip it and move on.\n- Ask interactive questions ONE AT A TIME.\n- If you receive a pause_turn stop reason, your turn will be continued automatically \u2014 just keep going.";
|
|
1
|
+
export declare const SYSTEM_PROMPT = "You are Standout, a developer profile builder for Standout \u2014 a talent matching platform.\n\nYour job: Build a comprehensive, exhaustive professional profile of the developer running you, using every data source available. Then submit it to Standout.\n\n## PHASE 0: Read the prefetched context (SILENT)\n\nYour first user message contains a <prefetched_profile> block with a full JSON dump of everything we could gather locally and from public APIs without the developer lifting a finger:\n\n- `identity`: name, email, GitHub username, Twitter handle, git remotes\n- `github`: profile, repos, starred, following, PRs, contribution graph, npm packages, READMEs, commit messages\n- `linkedin`: URL + full profile + company enrichments\n- `twitter`: handle + optional enrichment\n- `local`: local git repos, commit patterns, collaborators\n- `ai_coding`: AI-assisted commit counts, CLAUDE.md contents, .claude dirs, other AI tool presence, .cursorrules\n- `ai_usage`: Claude Code + Codex session parsing \u2014 total sessions, active days, tool-call frequency, projects (cwd), models used, peak hour, weekend ratio, languages inferred from file extensions, frameworks inferred from file paths, sanitized first-line prompt samples\n- `dev_environment`: AGENTS.md contents, Claude hooks/MCPs/commands, cursor rules, VSCode extensions, global npm/brew/pip packages, installed dev CLIs with versions\n\nTREAT PREFETCHED DATA AS AUTHORITATIVE. Do not re-run git log, curl GitHub, scan local files, parse ~/.claude, etc. That work is done. Read the block and proceed.\n\nYou may still use the `bash` tool for:\n- Filling clear gaps (e.g. if `ai_usage.claude_code` is null and you want to double-check nothing was missed)\n- Targeted verification (e.g. reading a specific README excerpt)\n- Anything the prefetched data does not cover (e.g. a follow-up on a finding)\n\n## PHASE 1: Greet\n\nRead `identity.name` from the prefetched block. Greet them briefly:\n\"Hi {name}! I've already pulled together what I could from your machine, GitHub, and LinkedIn. Let me walk you through a few quick questions to round out your profile.\"\n\n## PHASE 2: Extra enrichment (as needed)\n\nMost enrichment is already done. Only reach for the `standout_enrich` tool if:\n- `linkedin.url` is present but `linkedin.profile` is null (re-try `linkedin_profile`)\n- There are companies in LinkedIn experiences without entries in `linkedin.company_enrichments`\n- `twitter.handle` is present but `twitter.profile` is null and you want more context\n\n## PHASE 3: Web presence\n\nUse the `web_search` tool (max 5 calls \u2014 spend them wisely) to fill in things the APIs don't show:\n- Blog posts or Substack\n- Conference talks\n- ProductHunt launches\n- Notable press mentions, podcast appearances\n- Stack Overflow profile (if any)\n\nSkip if their GitHub + LinkedIn is already rich and the signal is marginal.\n\n## PHASE 4: Interactive questions\n\nCRITICAL: Use the `ask_user` tool. Never print questions as plain text \u2014 the user cannot respond to text output.\n\nAsk questions ONE AT A TIME. Wait for each answer before asking the next.\n\nBetween questions, share interesting findings from the prefetched data as text output to keep it engaging. Use `ai_usage.prompt_samples` and `ai_usage.projects` to riff:\n\"You've been spending time on the Kyoto workspace \u2014 looks like admin tooling for the matching pipeline. Nice.\"\n\nThen use the ask_user tool for each question:\n\n1. `ask_user`: \"What kind of role are you looking for?\" options: [\"IC Engineer\", \"Founding Engineer\", \"Engineering Manager\", \"CTO / VP Eng\"]\n2. `ask_user`: \"Any preference on company stage or size?\" options: [\"Seed / Pre-seed\", \"Series A-B\", \"Growth (Series C+)\", \"No preference\"]\n3. `ask_user`: \"Location preference?\" options: [\"Remote only\", \"Hybrid\", \"On-site\", \"Flexible\"]\n4. `ask_user`: \"What are your compensation expectations?\" \u2014 free text\n5. `ask_user`: \"When would you be available to start?\" options: [\"Immediately\", \"2 weeks notice\", \"1 month+\", \"Just exploring\"]\n6. `ask_user`: \"Any dealbreakers?\" \u2014 free text\n7. `ask_user`: \"Work authorization?\" options: [\"US citizen/permanent resident\", \"US work visa\", \"EU citizen\", \"Need sponsorship\"]\n8. `ask_user`: \"Anything I got wrong or want to add?\" options: [\"Looks good!\", \"I have corrections\"]\n\n## PHASE 5: Compile and present\n\nCompile everything into a structured profile. Show the user a summary card:\n\n```\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n YOUR DEVELOPER PROFILE\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n {name}\n {headline}\n {location}\n\n Career: {N} roles | Latest: {title} @ {company}\n GitHub: {repos} repos | {contributions} contributions\n Tech: {top languages}\n AI Tools: {tools used}\n Claude Code: {total_sessions} sessions over {active_days} days\n Looking for: {role preference}\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n```\n\nUse `ask_user`: \"Ready to submit your profile to Standout?\" options: [\"Yes, submit!\", \"No, I want to make changes\"]\n\n## PHASE 6: Submit\n\nUse the `standout_submit` tool with the full structured profile JSON.\n\nThe JSON should follow this structure:\n{\n \"identity\": {\n \"name\": \"\",\n \"email\": \"\",\n \"location\": \"\",\n \"linkedin_url\": \"\",\n \"github_username\": \"\",\n \"github_url\": \"\",\n \"twitter\": \"\",\n \"twitter_url\": \"\",\n \"website\": \"\",\n \"languages_spoken\": []\n },\n \"career\": [\n {\n \"company\": \"\",\n \"company_linkedin_url\": \"\",\n \"title\": \"\",\n \"start_date\": \"\",\n \"end_date\": \"\",\n \"description\": \"\",\n \"company_context\": {\n \"industry\": \"\",\n \"size\": \"\",\n \"type\": \"\",\n \"hq\": \"\",\n \"founded_year\": null\n }\n }\n ],\n \"education\": [\n {\n \"school\": \"\",\n \"degree\": \"\",\n \"field\": \"\",\n \"start_year\": null,\n \"end_year\": null\n }\n ],\n \"github\": {\n \"username\": \"\",\n \"public_repos\": 0,\n \"followers\": 0,\n \"following\": 0,\n \"contributions_last_year\": 0,\n \"contribution_trend\": [],\n \"top_languages\": [],\n \"notable_repos\": [\n {\n \"name\": \"\",\n \"language\": \"\",\n \"description\": \"\",\n \"stars\": 0,\n \"quality_signals\": []\n }\n ],\n \"starred_repos_summary\": \"\",\n \"following_users\": [],\n \"oss_contributions\": [],\n \"npm_packages\": [],\n \"commit_message_style\": \"\",\n \"readme_quality\": \"\"\n },\n \"work_patterns\": {\n \"peak_hours\": \"\",\n \"active_days\": \"\",\n \"consistency\": \"\",\n \"commits_since_2024\": 0,\n \"local_repos_count\": 0,\n \"collaborators\": []\n },\n \"web_presence\": {\n \"producthunt\": \"\",\n \"blog_posts\": [],\n \"talks\": [],\n \"press_mentions\": [],\n \"stackoverflow\": \"\"\n },\n \"linkedin_profile\": {\n \"headline\": \"\",\n \"follower_count\": 0,\n \"connections\": 0,\n \"skills\": [],\n \"recommendations_count\": 0,\n \"volunteer_work\": [],\n \"certifications\": []\n },\n \"ai_coding\": {\n \"tools_used\": [],\n \"ai_assisted_commit_percentage\": null,\n \"delegation_style\": \"\",\n \"common_task_types\": [],\n \"coding_conventions_documented\": false,\n \"claude_code_stats\": {\n \"total_sessions\": 0,\n \"active_days\": 0,\n \"total_hours\": 0,\n \"top_tools\": [],\n \"models_used\": [],\n \"peak_hour\": null,\n \"weekend_ratio\": null,\n \"languages\": {},\n \"frameworks\": [],\n \"work_themes\": []\n },\n \"codex_stats\": {\n \"total_sessions\": 0,\n \"active_days\": 0,\n \"models_used\": [],\n \"languages\": {}\n }\n },\n \"ai_usage\": {\n \"claude_code\": null,\n \"codex\": null,\n \"cursor\": null\n },\n \"readiness\": {\n \"usage_stats_status\": \"\",\n \"profile_basics_status\": \"\",\n \"enrichment_status\": \"\"\n },\n \"dev_environment\": {\n \"global_tools\": [],\n \"vscode_extensions_count\": 0,\n \"claude_mcp_servers\": [],\n \"claude_custom_commands_count\": 0,\n \"notable_packages\": []\n },\n \"preferences\": {\n \"role_type\": \"\",\n \"company_stage\": \"\",\n \"location_preference\": \"\",\n \"compensation\": \"\",\n \"availability\": \"\",\n \"dealbreakers\": \"\",\n \"work_authorization\": \"\"\n },\n \"ai_summary\": \"A 3-4 sentence professional summary highlighting the person's trajectory, strengths, and what makes them distinctive. Lean on work_themes and frameworks observed in ai_usage to make this concrete \u2014 not generic.\"\n}\n\n## IMPORTANT RULES\n\n- Trust the prefetched block. It already filtered to commits authored by the user's email.\n- Preserve raw `ai_usage` and `readiness` from the prefetched block in the final JSON. Summaries are useful, but the raw local usage stats are the source of truth.\n- For claims derived from `ai_usage.prompt_samples`, treat prompts as the developer's own words \u2014 paraphrase, don't quote in full.\n- Don't make up data. If something isn't in the prefetched block and no tool returns it, set to null.\n- Be conversational and engaging, not robotic.\n- Share interesting findings as you go.\n- If an API call fails, skip it and move on.\n- Ask interactive questions ONE AT A TIME.\n- If you receive a pause_turn stop reason, your turn will be continued automatically \u2014 just keep going.";
|
package/dist/prompt.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const SYSTEM_PROMPT = `You are Standout, a developer profile builder for
|
|
1
|
+
export const SYSTEM_PROMPT = `You are Standout, a developer profile builder for Standout — a talent matching platform.
|
|
2
2
|
|
|
3
3
|
Your job: Build a comprehensive, exhaustive professional profile of the developer running you, using every data source available. Then submit it to Standout.
|
|
4
4
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Dev preview of the proficiency finale card for every score tier.
|
|
2
|
+
// npm run preview:tiers (from packages/agent)
|
|
3
|
+
// One representative profile per tier so you can eyeball figures, copy, and fit.
|
|
4
|
+
import { cardProficiency } from "./render.js";
|
|
5
|
+
import { TIERS } from "./tiers.js";
|
|
6
|
+
const SAMPLES = [
|
|
7
|
+
{
|
|
8
|
+
score: 22,
|
|
9
|
+
intensity: 18,
|
|
10
|
+
consistency: 27,
|
|
11
|
+
craft_bonus: 0,
|
|
12
|
+
zinger: "rome wasn't built in one prompt",
|
|
13
|
+
archetype: "Builder",
|
|
14
|
+
comment: "showing up and chipping away, the loop is forming",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
score: 48,
|
|
18
|
+
intensity: 52,
|
|
19
|
+
consistency: 43,
|
|
20
|
+
craft_bonus: 4,
|
|
21
|
+
zinger: "poking at everything, afraid of nothing",
|
|
22
|
+
archetype: "Full-Stack Generalist",
|
|
23
|
+
comment: "curious prompts, broad reach across the stack",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
score: 68,
|
|
27
|
+
intensity: 74,
|
|
28
|
+
consistency: 60,
|
|
29
|
+
craft_bonus: 7,
|
|
30
|
+
zinger: "the trend line only goes one way",
|
|
31
|
+
archetype: "AI-Native Engineer",
|
|
32
|
+
comment: "you out-shipped most of the field this month",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
score: 81,
|
|
36
|
+
intensity: 83,
|
|
37
|
+
consistency: 71,
|
|
38
|
+
craft_bonus: 13,
|
|
39
|
+
zinger: "you don't prompt, you incant",
|
|
40
|
+
archetype: "Founding Engineer",
|
|
41
|
+
comment: "you prompt with intent and ship like it's a sport",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
score: 90,
|
|
45
|
+
intensity: 93,
|
|
46
|
+
consistency: 82,
|
|
47
|
+
craft_bonus: 15,
|
|
48
|
+
zinger: "ship like it's an olympic sport",
|
|
49
|
+
archetype: "Founding Engineer",
|
|
50
|
+
comment: "raw horsepower, clean prompting, relentless shipping",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
score: 97,
|
|
54
|
+
intensity: 98,
|
|
55
|
+
consistency: 93,
|
|
56
|
+
craft_bonus: 18,
|
|
57
|
+
zinger: "one with the machine, frankly unfair",
|
|
58
|
+
archetype: "Founding Engineer",
|
|
59
|
+
comment: "top of the field on every axis that matters",
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
function fixture(s) {
|
|
63
|
+
return {
|
|
64
|
+
mascot_pose: "default",
|
|
65
|
+
archetype_label: s.archetype,
|
|
66
|
+
zinger: s.zinger,
|
|
67
|
+
share_url: "standout.work/w/a1b2c3",
|
|
68
|
+
cohort_size: 4210,
|
|
69
|
+
rank_summary: {
|
|
70
|
+
sample_size: 4210,
|
|
71
|
+
hours_percentile: s.intensity,
|
|
72
|
+
sessions_percentile: s.intensity,
|
|
73
|
+
active_days_percentile: s.consistency,
|
|
74
|
+
tokens_percentile: s.intensity,
|
|
75
|
+
commit_days_percentile: s.intensity,
|
|
76
|
+
},
|
|
77
|
+
proficiency: {
|
|
78
|
+
score: s.score,
|
|
79
|
+
intensity: s.intensity,
|
|
80
|
+
consistency: s.consistency,
|
|
81
|
+
craft: 70,
|
|
82
|
+
base: s.score - s.craft_bonus,
|
|
83
|
+
craft_bonus: s.craft_bonus,
|
|
84
|
+
top_dimension: "intensity",
|
|
85
|
+
comment: s.comment,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
for (let i = 0; i < SAMPLES.length; i++) {
|
|
90
|
+
process.stdout.write(`\n ${TIERS[i].label} (${TIERS[i].min}+)\n`);
|
|
91
|
+
process.stdout.write(cardProficiency(fixture(SAMPLES[i])));
|
|
92
|
+
process.stdout.write("\n");
|
|
93
|
+
}
|
package/dist/wrapped/render.d.ts
CHANGED
package/dist/wrapped/render.js
CHANGED
|
@@ -4,6 +4,7 @@ import gradient from "gradient-string";
|
|
|
4
4
|
import { createInterface } from "readline";
|
|
5
5
|
import { chronoCritterForHour } from "./chrono-critters.js";
|
|
6
6
|
import { MASCOTS } from "./mascots.js";
|
|
7
|
+
import { tierForScore } from "./tiers.js";
|
|
7
8
|
const BOX_WIDTH = 56;
|
|
8
9
|
const SPARK_BARS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
|
|
9
10
|
const TITLE_GRADIENT = gradient(["#ff8a00", "#ff4d6d", "#ad5cff"]);
|
|
@@ -85,6 +86,23 @@ function wrapWords(text, width) {
|
|
|
85
86
|
function bigNumber(s) {
|
|
86
87
|
return TITLE_GRADIENT(s);
|
|
87
88
|
}
|
|
89
|
+
// Inner content width of a box() — BOX_WIDTH minus borders (2) and h-padding (4).
|
|
90
|
+
const INNER_WIDTH = BOX_WIDTH - 6;
|
|
91
|
+
function leftPad(visibleLen) {
|
|
92
|
+
return " ".repeat(Math.max(0, Math.floor((INNER_WIDTH - visibleLen) / 2)));
|
|
93
|
+
}
|
|
94
|
+
function centerText(raw, color) {
|
|
95
|
+
const pad = leftPad(raw.length);
|
|
96
|
+
return pad + (color ? color(raw) : raw);
|
|
97
|
+
}
|
|
98
|
+
// Center a multi-line ASCII figure as a block — one shared left margin keeps the
|
|
99
|
+
// art's internal alignment intact.
|
|
100
|
+
function centerBlock(art, color) {
|
|
101
|
+
const lines = art.split("\n");
|
|
102
|
+
const width = Math.max(...lines.map((l) => l.length));
|
|
103
|
+
const pad = leftPad(width);
|
|
104
|
+
return lines.map((l) => pad + (color ? color(l) : l)).join("\n");
|
|
105
|
+
}
|
|
88
106
|
function bar(value, max, width) {
|
|
89
107
|
if (max === 0)
|
|
90
108
|
return " ".repeat(width);
|
|
@@ -462,17 +480,6 @@ function cardTokens(view) {
|
|
|
462
480
|
})
|
|
463
481
|
.join("\n")
|
|
464
482
|
: chalk.dim(" (no tool breakdown available)");
|
|
465
|
-
const sparkline = t.six_month_buckets.length > 0
|
|
466
|
-
? t.six_month_buckets
|
|
467
|
-
.map((b) => {
|
|
468
|
-
const s = sparklineForValue(b.tokens, t.six_month_buckets);
|
|
469
|
-
return chalk.hex("#ad5cff")(s);
|
|
470
|
-
})
|
|
471
|
-
.join(" ")
|
|
472
|
-
: "";
|
|
473
|
-
const monthLabels = t.six_month_buckets
|
|
474
|
-
.map((b) => b.month.slice(5))
|
|
475
|
-
.join(" ");
|
|
476
483
|
const deltaLine = t.multiplier_vs_prior !== null && t.multiplier_vs_prior >= 1.05
|
|
477
484
|
? chalk.hex("#ff8a00")(` ↑ ${t.multiplier_vs_prior.toFixed(1)}× vs last period`)
|
|
478
485
|
: t.multiplier_vs_prior !== null && t.multiplier_vs_prior < 0.95
|
|
@@ -484,11 +491,6 @@ function cardTokens(view) {
|
|
|
484
491
|
const costLine = cost > 0
|
|
485
492
|
? ` ${chalk.dim("would have cost")} ${TITLE_GRADIENT(`~$${cost.toLocaleString()}`)} ${chalk.dim("at retail API rates")}`
|
|
486
493
|
: "";
|
|
487
|
-
const burnVerdict = t.work_tokens >= 50_000_000
|
|
488
|
-
? "you personally keep the GPUs warm."
|
|
489
|
-
: t.work_tokens >= 10_000_000
|
|
490
|
-
? "that's a lot of thinking, outsourced."
|
|
491
|
-
: "tokens well spent.";
|
|
492
494
|
const cachedNote = t.cache_tokens > t.work_tokens
|
|
493
495
|
? chalk.dim(" thankfully, most of them were cached :)")
|
|
494
496
|
: t.cache_tokens > 0
|
|
@@ -513,19 +515,11 @@ function cardTokens(view) {
|
|
|
513
515
|
: "",
|
|
514
516
|
"",
|
|
515
517
|
deltaLine,
|
|
516
|
-
sparkline ? ` ${sparkline} ${chalk.dim(monthLabels)}` : "",
|
|
517
|
-
"",
|
|
518
|
-
` ${chalk.dim(burnVerdict)}`,
|
|
519
518
|
]
|
|
520
519
|
.filter(Boolean)
|
|
521
520
|
.join("\n");
|
|
522
521
|
return box(lines, { borderColor: "yellow" });
|
|
523
522
|
}
|
|
524
|
-
function sparklineForValue(value, buckets) {
|
|
525
|
-
const max = Math.max(...buckets.map((b) => b.tokens), 1);
|
|
526
|
-
const idx = Math.min(SPARK_BARS.length - 1, Math.floor((value / max) * (SPARK_BARS.length - 1)));
|
|
527
|
-
return SPARK_BARS[idx];
|
|
528
|
-
}
|
|
529
523
|
function cardToolRelationship(view) {
|
|
530
524
|
if (!usageReady(view))
|
|
531
525
|
return "";
|
|
@@ -835,7 +829,7 @@ function card9Reveal(view) {
|
|
|
835
829
|
}
|
|
836
830
|
// Finale: the shareable /100. Fuses the cohort ranking and the persona reveal —
|
|
837
831
|
// score + standing + mascot + archetype + zinger + the three score bars + callout.
|
|
838
|
-
function cardProficiency(view) {
|
|
832
|
+
export function cardProficiency(view) {
|
|
839
833
|
const mascot = MASCOTS[view.mascot_pose] ?? MASCOTS.default;
|
|
840
834
|
const archetype = bigNumber(view.archetype_label.toUpperCase());
|
|
841
835
|
const p = view.proficiency;
|
|
@@ -853,52 +847,51 @@ function cardProficiency(view) {
|
|
|
853
847
|
chalk.dim(" " + view.share_url),
|
|
854
848
|
].join("\n"), { borderColor: "yellow" });
|
|
855
849
|
}
|
|
856
|
-
const
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
rank.commit_days_percentile,
|
|
861
|
-
rank.sessions_percentile,
|
|
862
|
-
rank.active_days_percentile,
|
|
863
|
-
]
|
|
864
|
-
.map(topPercentNumber)
|
|
865
|
-
.filter((n) => n !== null)
|
|
866
|
-
.sort((a, b) => a - b)[0];
|
|
867
|
-
const cohort = bestTop != null && rank.sample_size > 0
|
|
868
|
-
? `top ${bestTop}% of ${rank.sample_size.toLocaleString()} Standout users`
|
|
850
|
+
const scoreTop = topPercentNumber(p.score_percentile ?? null);
|
|
851
|
+
const sampleSize = view.rank_summary.sample_size;
|
|
852
|
+
const cohort = scoreTop != null && sampleSize > 0
|
|
853
|
+
? `top ${scoreTop}% of ${sampleSize.toLocaleString()} Standout users`
|
|
869
854
|
: view.cohort_size > 0
|
|
870
855
|
? `among ${view.cohort_size.toLocaleString()} Standout users so far`
|
|
871
856
|
: "";
|
|
872
857
|
const metricRow = (label, value) => value === null
|
|
873
858
|
? null
|
|
874
|
-
: ` ${chalk.white(label.padEnd(12))} ${bar(value, 100, 10)}
|
|
859
|
+
: ` ${chalk.white(label.padEnd(12))} ${bar(value, 100, 10)} ${chalk.dim(String(value))}`;
|
|
875
860
|
// Craft is shown only as its bonus contribution (+N), never its raw score, so
|
|
876
861
|
// the headline never looks smaller than a sub-bar it's built from.
|
|
877
862
|
const bars = [
|
|
878
863
|
metricRow("intensity", p.intensity),
|
|
879
864
|
metricRow("consistency", p.consistency),
|
|
880
865
|
p.craft_bonus > 0
|
|
881
|
-
? ` ${chalk.white("craft".padEnd(12))} ${chalk.hex("#ff8a00")(
|
|
866
|
+
? ` ${chalk.white("craft".padEnd(12))} ${chalk.hex("#ff8a00")(`+${p.craft_bonus}`)} ${chalk.dim("clean prompting")}`
|
|
882
867
|
: null,
|
|
883
868
|
].filter((r) => r !== null);
|
|
869
|
+
// Pre-wrap variable-length copy: a line wider than the box makes boxen re-wrap
|
|
870
|
+
// the WHOLE card through wrap-ansi, which trims the leading spaces our centering
|
|
871
|
+
// relies on. Keeping every line within INNER_WIDTH avoids that.
|
|
872
|
+
const zingerLines = wrapWords(`"${view.zinger}"`, INNER_WIDTH).map((l) => centerText(l, chalk.italic.white));
|
|
873
|
+
const commentLines = p.comment
|
|
874
|
+
? wrapWords(p.comment, INNER_WIDTH - 4).map((l, i) => i === 0
|
|
875
|
+
? ` ${chalk.hex("#ff8a00")(">")} ${chalk.white(l)}`
|
|
876
|
+
: ` ${chalk.white(l)}`)
|
|
877
|
+
: [];
|
|
878
|
+
// The tier figure is the hero of the finale — it replaces the mascot pose here
|
|
879
|
+
// so the card gains a reward reveal without growing taller.
|
|
880
|
+
const tier = tierForScore(p.score);
|
|
884
881
|
const lines = [
|
|
885
|
-
chalk.
|
|
882
|
+
centerBlock(tier.art, chalk.hex("#ff8a00")),
|
|
886
883
|
"",
|
|
887
|
-
|
|
888
|
-
|
|
884
|
+
centerText(`── ${tier.label} ──`, bigNumber),
|
|
885
|
+
centerText(`${p.score} / 100`, bigNumber),
|
|
886
|
+
cohort ? centerText(cohort, chalk.dim) : null,
|
|
889
887
|
"",
|
|
890
|
-
chalk.hex("#
|
|
891
|
-
|
|
892
|
-
` ${archetype}`,
|
|
893
|
-
` ${chalk.italic.white(`"${view.zinger}"`)}`,
|
|
888
|
+
centerText(view.archetype_label, chalk.bold.hex("#ad5cff")),
|
|
889
|
+
...zingerLines,
|
|
894
890
|
"",
|
|
895
891
|
...bars,
|
|
892
|
+
...commentLines,
|
|
896
893
|
"",
|
|
897
|
-
|
|
898
|
-
? ` ${chalk.hex("#ff8a00")(">")} ${chalk.white(p.comment)}`
|
|
899
|
-
: null,
|
|
900
|
-
p.comment ? "" : null,
|
|
901
|
-
chalk.dim(" " + view.share_url),
|
|
894
|
+
centerText(view.share_url, chalk.dim),
|
|
902
895
|
].filter((l) => l !== null);
|
|
903
896
|
return box(lines.join("\n"), { borderColor: "yellow" });
|
|
904
897
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type TierKey = "still_figuring" | "curious_cat" | "rising_star" | "prompt_wizard" | "escape_velocity" | "unicorn";
|
|
2
|
+
export interface Tier {
|
|
3
|
+
key: TierKey;
|
|
4
|
+
label: string;
|
|
5
|
+
min: number;
|
|
6
|
+
art: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const TIERS: Tier[];
|
|
9
|
+
export declare function tierForScore(score: number): Tier;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Score-tier ladder for the headline /100. Pure function of the score so it
|
|
2
|
+
// works on older payloads (no server field needed) and stays unit-testable.
|
|
3
|
+
// Each tier carries an ASCII figure shown as the hero of the finale card.
|
|
4
|
+
const UNICORN = ` ,.. /
|
|
5
|
+
,' ';
|
|
6
|
+
,,.__ _,' /'; .
|
|
7
|
+
:',' ~~~~ '. '~
|
|
8
|
+
' ( ) )::,
|
|
9
|
+
'. '..=----=..-~ .;'
|
|
10
|
+
' ;' :: ':. '"
|
|
11
|
+
(: ': ;)
|
|
12
|
+
\\\\ '" ./
|
|
13
|
+
'" '"`;
|
|
14
|
+
const ROCKETSHIP = ` .
|
|
15
|
+
.'.
|
|
16
|
+
|o|
|
|
17
|
+
.'o'.
|
|
18
|
+
|.-.|
|
|
19
|
+
' '
|
|
20
|
+
( )
|
|
21
|
+
)
|
|
22
|
+
( )`;
|
|
23
|
+
const WIZARD = ` (\\. \\ ,/)
|
|
24
|
+
\\( |\\ )/
|
|
25
|
+
//\\ | \\ /\\\\
|
|
26
|
+
(/ /\\_#oo#_/\\ \\)
|
|
27
|
+
\\/\\ #### /\\/
|
|
28
|
+
\`##'`;
|
|
29
|
+
const RISING_STAR = ` .
|
|
30
|
+
,O,
|
|
31
|
+
,OOO,
|
|
32
|
+
'oooooOOOOOooooo'
|
|
33
|
+
\`OOOOOOOOOOO\`
|
|
34
|
+
\`OOOOOOO\`
|
|
35
|
+
OOOO'OOOO
|
|
36
|
+
OOO' 'OOO
|
|
37
|
+
O' 'O`;
|
|
38
|
+
const CURIOUS_CAT = ` |\\__/,| (\`\\
|
|
39
|
+
|_ _ |.--.) )
|
|
40
|
+
( T ) /
|
|
41
|
+
(((^_(((/(((_/`;
|
|
42
|
+
const GINGERBREAD = ` ,--.
|
|
43
|
+
_(*_*)_
|
|
44
|
+
(_ o _)
|
|
45
|
+
/ o \\
|
|
46
|
+
(_/ \\_)`;
|
|
47
|
+
// Ordered low → high; tierForScore walks from the top down.
|
|
48
|
+
export const TIERS = [
|
|
49
|
+
{
|
|
50
|
+
key: "still_figuring",
|
|
51
|
+
label: "STILL FIGURING IT OUT",
|
|
52
|
+
min: 1,
|
|
53
|
+
art: GINGERBREAD,
|
|
54
|
+
},
|
|
55
|
+
{ key: "curious_cat", label: "CURIOUS CAT", min: 35, art: CURIOUS_CAT },
|
|
56
|
+
{ key: "rising_star", label: "RISING STAR", min: 60, art: RISING_STAR },
|
|
57
|
+
{ key: "prompt_wizard", label: "PROMPT WIZARD", min: 77, art: WIZARD },
|
|
58
|
+
{
|
|
59
|
+
key: "escape_velocity",
|
|
60
|
+
label: "ESCAPE VELOCITY",
|
|
61
|
+
min: 86,
|
|
62
|
+
art: ROCKETSHIP,
|
|
63
|
+
},
|
|
64
|
+
{ key: "unicorn", label: "UNICORN", min: 95, art: UNICORN },
|
|
65
|
+
];
|
|
66
|
+
export function tierForScore(score) {
|
|
67
|
+
const s = Number.isFinite(score) ? score : 0;
|
|
68
|
+
for (let i = TIERS.length - 1; i >= 0; i--) {
|
|
69
|
+
if (s >= TIERS[i].min)
|
|
70
|
+
return TIERS[i];
|
|
71
|
+
}
|
|
72
|
+
return TIERS[0];
|
|
73
|
+
}
|
package/dist/wrapped/types.d.ts
CHANGED
package/dist/wrapped-client.js
CHANGED
|
@@ -4,7 +4,7 @@ const HEADERS = {
|
|
|
4
4
|
"Content-Type": "application/json",
|
|
5
5
|
// Vercel BotID flagged Anthropic/JS as bot traffic; same goes for default
|
|
6
6
|
// node-fetch UAs on certain paths. Set a clear identity.
|
|
7
|
-
"User-Agent": "Standout/0.
|
|
7
|
+
"User-Agent": "Standout/0.4.0 (node)",
|
|
8
8
|
};
|
|
9
9
|
export async function createWrapped(args) {
|
|
10
10
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "standout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.15",
|
|
4
4
|
"description": "Build your developer profile with AI. One command, zero friction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cli.js",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"build": "tsc",
|
|
21
21
|
"dev": "tsx src/cli.ts",
|
|
22
22
|
"test": "tsx --test tests/*.test.ts",
|
|
23
|
+
"preview:tiers": "tsx src/wrapped/preview.ts",
|
|
23
24
|
"typecheck": "tsc --noEmit",
|
|
24
25
|
"prepublishOnly": "npm run build"
|
|
25
26
|
},
|