AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Generate Tech Profile Json

skill-jasonchen0604-codebase-to-portfolio-generate-tech-profile-json · by jasonChen0604

Generate structured JSON tech profile files from all latest-version project docs. Outputs a split-file directory tech-profile/ (not monolithic JSON) for efficient incremental updates. Triggers when the user says "generate tech profile json", "build json profile", "export json profile", "生成 JSON 技術檔案", "產出 JSON 技術樹", or "建立 JSON 技術報告".

No reviews yet
0 installs
42 views
0.0% view→install

Install

$ agentstack add skill-jasonchen0604-codebase-to-portfolio-generate-tech-profile-json

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-jasonchen0604-codebase-to-portfolio-generate-tech-profile-json)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Generate Tech Profile Json? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Generate Tech Profile JSON Skill

Goal

Read all project docs (doc_filename) with the latest skill_version, extract structured frontmatter data, then generate a tech-profile/ split-file directory (schema version 2.0).

Output layout:

tech-profile/
├── meta.json                  # meta + profile + linkedin + id_map
├── domains-.json        # one per configured language
├── tag-index-.json      # one per configured language
├── product-groups-.json # one per configured language
└── projects/
    ├── ..json     # one file per project, per configured language

Why split? Each file is 1–15KB. Incremental runs only rewrite changed project files + rebuild the index files. Token cost scales with changed projects, not total project count.

Incremental update logic:

  1. Load existing tech-profile/meta.json → get id_map and source_version
  2. Load all tech-profile/projects/*..json → build in-memory cache
  3. For each project: compute hash, compare cached skill_version
  • Unchanged → skip (reuse cached file, zero token cost)
  • New or changed → read the doc file, write ..json for each configured language
  1. Always rebuild index files (domains, tag-index, product-groups) from all project files
  2. Write meta.json with updated totals

On first run (no existing tech-profile/), process all projects.

Step 0: Read config

Read profile.config.json from the current working directory. If missing, tell the user to copy examples/profile.config.example.json to profile.config.json and fill it in, then stop.

  • profile.name, profile.email, profile.title, profile.years_of_experience — used directly in the profile JSON block (if years_of_experience is absent, derive from the oldest project's last commit)
  • doc_filename (default CLAUDE.md)
  • languages (default ["en"])
  • timezone (default +00:00)
  • privacy_blocklist (default [])
  • custom_domains (default {}) — merged into the built-in domain table in Step 4

JSON Schema

Each language's file set conforms to this schema:

{
  "meta": {
    "generated_at": "2026-06-19T12:00:00+00:00",  // ISO8601, configured timezone
    "source_version": "2.0",
    "total_projects": 95,
    "lang": "en"
  },

  "profile": {
    "name": "",
    "title": "",
    "title_alt": "",
    "email": "",
    "summary": "",                 // 3–5 sentences, synthesized from all Highlights, in this file's language
    "linkedin_about": "",          // LinkedIn-ready About section, in this file's language
    "years_of_experience": 8,                 // from config, or derived from oldest project last_commit
    "total_projects": 95
  },

  "domains": [
    {
      "id": "frontend",                       // snake_case, stable identifier
      "label": "Frontend",                    // translated per this file's language
      "icon": "🖥",
      "project_count": 10,
      "summary": "",               // domain expertise paragraph, in this file's language
      "skills": [
        {
          "tag": "React",
          "level": "expert",                  // "expert" | "proficient" | "familiar" — derived from project count + status
          "project_count": 8,                 // MUST equal projects.length — derived from it, not estimated separately
          "projects": [                       // ALL project ids that have this tag — no sampling, no truncation
            "project-management-system-frontend",
            "lab-space-rack-management-frontend"
            // ... every matching project id
          ]
        }
      ]
    }
  ],

  "projects": [
    {
      "id": "3a7f2c1b09e4",                            // SHA-256[:12] of project folder path relative to scan_root
      "name": "Project Management System — Frontend",  // display_name (privacy-safe), never a brand/client name
      "category": "Enterprise Web Application",   // translated per this file's language
      "status": "Production",                      // translated per this file's language
      "status_badge": "[P]",
      "featured": false,
      "tags": ["React", "NestJS", "Azure AD", "WebSocket", "CASL"],
      "core_tech": "TypeScript / React 18 + Vite 3",
      "database": "PostgreSQL, Redis",
      "deployment": "Docker Swarm / GitLab CI",
      "description": "...",                        // translated per this file's language
      "domain_primary": "frontend",                // primary domain id
      "domains": ["frontend", "backend"]           // all applicable domain ids
    }
  ],

  "tag_index": [
    {
      "tag": "React",
      "domain_id": "frontend",
      "domain_label": "Frontend",             // translated per this file's language
      "project_count": 8,
      "level": "expert"
    }
  ],

  "linkedin": {
    "headline": " | key tech A · key tech B · key tech C",   // translated per this file's language
    "about": "",      // same as profile.linkedin_about, formatted for direct paste
    "skills_list": ["TypeScript", "React", "NestJS", "Next.js", "Docker", "PostgreSQL", "LangGraph", "Azure OpenAI"],
    // top 20 skills sorted by: featured projects first, then project_count desc
    "experience_highlights": [
      // For each domain with ≥3 projects, one bullet point summarizing key projects by display_name (no raw project codes)
      "Led development of an enterprise platform serving multiple business units, built with NestJS + Next.js + Docker Swarm."
    ]
  },

  "product_groups": [
    {
      "product_name": "Lab Space & Rack Management",
      "project_count": 4,
      "roles": ["Frontend", "Backend (Laravel)", "Backend (NestJS)", "Docker / Infra"],
      "status": "Production",
      "projects": [
        { "id": "lab-space-rack-management-frontend", "role": "Frontend", "status": "Production" }
      ]
      // projects[] contains id + role + status only — full data lives in projects[]
    }
  ]
}

Execution Steps

Step 1: Get the latest version

Run the /get-latest-version skill.

  • If none, abort and tell the user to run /batch-init first.

Step 2: Filter projects

Read projects-list.md, filter rows where:

  1. "Active" column is ✅
  2. Doc-file column is ✅
  3. "Version" column equals the latest version string

Collect each matching project's Path column. Expand ~ via $HOME.

Step 3: Read project doc frontmatter (batch up to 10 in parallel)

For each project, read / and extract:

  • project_name, category, status, tags (array), one_line_description
  • core_tech, database, deployment, featured
  • Also extract the Highlights bullet text from ## 📋 Portfolio Summary section body
  • Also record path — the project's absolute path (used for grouping in Step 3b)

After extracting fields, derive a display_name for each project:

Privacy-Safe Display Name Rules

The core rule: display_name must describe only system function — never a client, company, brand, or product name.

Two categories of names require sanitization:

Category 1 — Project code prefixes (structural identifiers in project_name):

  • Patterns: any 2–3 letter prefix followed by 2 digits (e.g. pa27, pt07, wp18)
  • Action: strip prefix, derive name from one_line_description

Category 2 — Client/brand/product names (appear in project_name directly or as the display name after stripping suffixes):

  • Names listed in the config's privacy_blocklist — apply judgment for any unfamiliar name that looks like a client/company/brand token too
  • Also applies when the derived display name itself still contains a brand/client name — re-derive from one_line_description
  • Action: replace with a purely functional description from one_line_description

Deriving a safe display name:

  1. Extract the core system/feature name from one_line_description — translate per each configured language's file
  2. Append role suffix: -web— Frontend, -server— Backend, -nest-server— Backend (NestJS), -docker— Docker / Infra, -ios / -android— iOS App / — Android App; or derive from core_tech
  3. If one_line_description also contains the client/brand name, paraphrase around the function not the brand

Final check before writing output: scan every display_name against privacy_blocklist. If found, re-derive.

  • Assign each project a stable id: SHA-256(relPath)[:12] where relPath is the project folder path relative to scan_root (e.g. "clients/acme/acme-lab-space-and-rack-management-web"). This keeps IDs opaque (no brand/client leak) and stable (path doesn't change unless project is moved). In case of collision (extremely rare), append __N suffix before re-hashing.
  • Use display_name as the name field in projects[]. Store the original project_name as raw_name for internal reference only (omit from output JSON).
  • In domains[].skills[].projects[]: store only the id strings — no other fields. Consumer looks up full data in projects[] by id.
  • In product_groups[].projects[]: store { id, role, status } only — no display_name field.

Step 3b: Group projects by product

After deriving display names, group projects belonging to the same product/system:

Grouping logic (apply both; either match triggers grouping):

  1. Projects sharing the same parent directory path → one product group
  2. Projects whose project_name shares a common prefix after stripping role suffixes (-web, -server, -nest-server, -docker, -backend, -frontend, -api, -cli) → one product group

For each group:

  • product_name: the shared functional name without role suffix (anonymized per display name rules)
  • projects: array of members, each with display_name, role (Frontend / Backend / Backend NestJS / Docker / Infra / CLI), and status

Add a product_groups top-level array to every language's file set (after tag_index):

"product_groups": [
  {
    "product_name": "Lab Space & Rack Management",
    "project_count": 4,
    "roles": ["Frontend", "Backend (Laravel)", "Backend (NestJS)", "Docker / Infra"],
    "status": "Production",   // highest-priority status among members
    "projects": [
      { "id": "3a7f2c1b09e4", "role": "Frontend", "status": "Production" },
      { "id": "9f1bc234a5e7", "role": "Backend (Laravel)", "status": "Production" },
      { "id": "c82d0f6173ab", "role": "Backend (NestJS)", "status": "Production" },
      { "id": "7e45a912b0cf", "role": "Docker / Infra", "status": "Production" }
    ]
  }
]

Individual projects still appear in the projects[] array — product_groups is additive.

Step 4: Build domain mapping

Use the domain → tags mapping from the generate-tech-profile skill (same built-in table), merged with custom_domains from config.

For each tag in any project, assign it to a domain. A tag may belong to multiple domains.

Step 5: Derive skill levels

For each (domain, tag) pair, collect every project that has this tag — no sampling, no truncation. Then:

  • Set projects = the complete array of those project ids (strings only) — ALL of them
  • Set project_count = projects.length exactly — this is the source of truth, not a separate estimate
  • Derive level from project_count and the statuses of the matched projects:
  • expert: project_count ≥ 5, and ≥1 is Production or In Progress
  • proficient: project_count 2–4, or project_count = 1 with Production status
  • familiar: project_count = 1, status is Archived / Prototype / Completed only

Step 6: Derive yearsofexperience

Use profile.years_of_experience from config if set. Otherwise, not reliably derivable from project docs — output null and note in the summary that the user should set it manually.

Step 7: Build profile.summary and linkedin fields

Synthesize from all Highlights texts, once per configured language:

  • profile.summary: 3–5 sentence paragraph covering top domains, key technologies, and scale
  • linkedin.about: LinkedIn-optimized About section, 3–5 sentences, first-person, action verbs

Step 8: Build linkedin.skills_list

Take all tags, sort by:

  1. Tags appearing in featured: true projects first
  2. Then by project_count descending

Take top 20.

Step 9: Build linkedin.experience_highlights

For each domain with ≥3 projects, write one bullet, first-person, action verb, mentioning key projects by display name, including scale/impact if available, in this file's language.

Step 10: Write split files

Write to tech-profile/ under the current working directory:

  1. tech-profile/projects/..json for each project and each configured language (only new/changed ones on incremental runs)
  2. tech-profile/domains-.json for each configured language
  3. tech-profile/tag-index-.json for each configured language
  4. tech-profile/product-groups-.json for each configured language
  5. tech-profile/meta.json — include id_map: { "": "" } for all projects

meta.json structure:

{
  "meta": { "generated_at", "source_version", "total_projects", "schema_version": "2.0", "lang": "" },
  "profile": { ...same as before... },
  "linkedin": { ...same as before... },
  "id_map": {
    "3a7f2c1b09e4": "Lab Space & Rack Management — Frontend",
    ...
  }
}

Use 2-space indentation. Ensure valid JSON (no trailing commas, no comments).

Step 11: Report results

✅ Tech profile JSON generated (split-file format)
- Projects: N total, M updated (skill_version: X.Y)
- Unique tags: T
- Domains: [frontend, backend, ai_llm, ...]
- Languages: [en, ...]
- tech-profile/ → 
  ├── meta.json (id_map: N entries)
  ├── domains-.json (one per language)
  ├── tag-index-.json (one per language)
  ├── product-groups-.json (one per language)
  └── projects/ (N × languages files)

Domain IDs and Labels

| id | Label | |----|----------| | frontend | Frontend | | backend | Backend | | ai_llm | AI / LLM | | database | Database | | devops | DevOps / Infrastructure | | cloud | Cloud Services | | mobile | Mobile | | tools | Tools & Automation | | languages | Languages | | other | Other |

Translate labels per configured language using the same translation approach as generate-tech-profile (Domain name translation table). Use icon: 🖥 frontend, ⚙️ backend, 🤖 ai_llm, 🗄 database, 🚀 devops, ☁️ cloud, 📱 mobile, 🔧 tools, 💻 languages, 📦 other.

Cloud domain tags

Tags that belong to cloud (remove from other/mobile/ai_llm if present): Firebase, Firebase Admin, Firestore, Firebase FCM, Firebase Hosting, Azure AD, Azure OpenAI, Azure Functions, Google Cloud Storage, Google Maps, Google Sign-In, Gmail API, Google Maps Geocoding API, Serverless, Self-Hosted

Status Translation

| English | badge | |----|-------| | Production | [P] | | In Progress | [IP] | | Maintenance | [M] | | Side Project | [S] | | Archived | [A] | | Prototype | [Proto] | | Completed | [C] |

Translate status labels per configured language using the same translation table as generate-tech-profile.

Notes

  • Read project docs in parallel batches of up to 10
  • Write project files in parallel batches; write index files after all projects are done
  • Skip malformed doc files, log warning, continue
  • For each non-source language file: translate all description/summary text into natural target-language text
  • For the file matching the source data's original language: descriptions may be used as-is; still translate domain/status labels
  • featured: true projects are marked with "featured": true in the JSON — do NOT add ⭐ emoji inside JSON strings
  • Always overwrite index files (domains, tag-index, product-groups, meta) on each run; project files are only rewritten when changed
  • The linkedin_about field must be copy-paste ready — no placeholders, no markdown formatting, plain text only
  • tag_index must be sorted by project_count descending
  • projects array sorted by: status priority (Production first)

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.