agentleFS
noltenolte/claude-shared/skills/tech-stack-capture/SKILL.md

tech-stack-capture

Skill0 starsChanged 3 months agoMITView on GitHub

Captures or refreshes the `tech_stack:` block in a Portfolio-Member's `project/portfolio.yml` per the canonical-language file under `spec/portfolio/tech-stack-discovery/` §Discovery sequence. Probes repo signals (lockfiles, `Taskfile.yml`, `.github/workflows/`, `renovate.json5`, `mkdocs.yml`, `.vale.ini`, `.pre-commit-config.yaml`), classifies hits against the closed `kind` (12) and `group` (5) enums from `spec/portfolio/tech-stack/`, compares each candidate against the global stack, drops inherited matches, and confirms every proposed change interactively before writing. Invoke when the user asks to "capture the tech stack", "scaffold a tech_stack block", "refresh the tech_stack section", or equivalent German-language requests. Don't use to author `portfolio/tech-stack.yml` (hand-curated only) or to run signal-verification audits (use `portfolio-audit`). Supports resume on re-invocation per `spec/claude/resumable-work/`.

The file

Show its rules and text

We split this skill into the rules it gives an agent

17 rules. About 0 are common, 3 are distinctive, and 14 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 14

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If the active repository carries no project/portfolio.yml at all, stop and route the operator to portfolio-audit Bootstrap.

    line 50
  2. Very specific:

    If the active repository ships its own portfolio/tech-stack.yml and isn't claude-shared, stop with a Critical warning: a Portfolio-Member …

    line 52
  3. Distinctive:

    Before any signal probe, confirm:

    line 56
  4. Very specific:

    When project/portfolio.yml already carries a non-empty tech_stack: block, parse it into three working sets:

    line 72
  5. Very specific:

    Read the signal-source set in the order documented in references/signal-source-map.md, and read that file now to ground every …

    line 82
  6. Distinctive:

    Don't proceed to step 8 until every proposed addition, override, and regroup has been individually confirmed, edited, or rejected.

    line 112
  7. Very specific:

    Read examples/01-fresh-capture-empty-additions.md when running a fresh capture on a project with no existing tech_stack: additions.

    line 135
  8. Very specific:

    Read examples/02-refresh-after-dep-bot-swap.md when refreshing the tech-stack capture after Dependabot or Renovate has swapped a dependency.

    line 136
  9. Very specific:

    Read examples/03-deviation-with-override-and-regroup.md when the project deviates from the portfolio baseline and you need to see how …

    line 137
  10. Very specific:

    Never write project/portfolio.yml without an interactive per-entry confirmation round (steps 7) on every addition, override, and regroup.

    line 145
  11. Very specific:

    Never modify the global manifest portfolio/tech-stack.yml from this skill, in any repository.

    line 146
  12. Very specific:

    Never declare an additions[] entry that matches an inherited entry on both name and kind.

    line 147
  13. Very specific:

    Never write an overrides[] record with an empty or whitespace-only rationale.

    line 148
  14. Very specific:

    Never write a regroup[] record whose new group equals the inherited entry's group (no-op regroup) or whose rationale is empty.

    line 149
  15. Distinctive:

    Never invent a signal hit.

    line 150
  16. Very specific:

    Never commit a "candidates not picked" log to the repository.

    line 152
  17. Very specific:

    When spec/portfolio/tech-stack-discovery/ or spec/portfolio/tech-stack/ disagrees with this skill, the spec wins.

    line 153

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Tech Stack Capture

Drives the §Discovery sequence per repository defined in spec/portfolio/tech-stack-discovery/<canonical_language>.md and produces a tech_stack: block in project/portfolio.yml that conforms to the entry schema, the inheritance contract, the kind enum (12 values), the group enum (5 values), and the regroup mechanism declared in spec/portfolio/tech-stack/<canonical_language>.md. One operation — fresh capture and refresh share the same path because the spec doesn't distinguish them.

German trigger phrases

This skill also triggers on equivalent German-language requests, including:

  • "Tech-Stack erfassen"
  • "tech_stack-Block aktualisieren"

Why this is a skill, not an agent

  • Per-entry interactive confirmation is a MUST. The discovery spec requires the maintainer to accept, reject, or edit every proposed entry, override, and regroup before any write. An agent's fire-and-forget contract would lose that checkpoint per candidate.
  • Persistent on-disk artefact under iterative drafting. The skill mutates project/portfolio.yml (a top-level repo artefact alongside project/mission.md and project/roadmap.md); the iterative drafting (signal probe → propose → confirm → write) flows back into the main conversation naturally.
  • Cross-spec orchestration. The skill reads the global manifest, the schema spec, and the discovery spec, then writes per the project's primary YAML conventions. Multi-source orchestration belongs in the skill layer per spec/claude/skill-vs-agent/.
  • Counter-dimension considered: a tool-restricted signal-probing agent could absorb the file-system reads more cleanly, but the load-bearing work is the per-entry conversation with the maintainer, not the probing — skill wins.

User-language policy

Detect the user's language and respond in it. YAML content written to project/portfolio.yml stays English (the file's existing convention; identifiers, kind/group enum tokens, and the rationale: prose all stay English regardless of the user's conversation language, matching how every other portfolio manifest in the nolte/* portfolio is authored).

Detection: am I in the right repository?

The skill runs in two roles depending on the active repository:

  • Inside any Portfolio-Member repository (the typical adopter — repo under nolte/* with a project/portfolio.yml): the operation runs end-to-end against that repo's project/portfolio.yml. Detection: presence of project/portfolio.yml AND absence of .claude-plugin/plugin.json (so this isn't claude-shared itself unless explicitly so).
  • Inside claude-shared (where the global stack lives plus this repository's own project/portfolio.yml): the operation runs against claude-shared's own project/portfolio.yml. Detection: presence of .claude-plugin/plugin.json AND portfolio/tech-stack.yml AND project/portfolio.yml.

If the active repository carries no project/portfolio.yml at all, stop and route the operator to portfolio-audit Bootstrap; the per-repo tech_stack: block requires the manifest to exist first per spec/portfolio/portfolio-management/ §Capability inventory per repository.

If the active repository ships its own portfolio/tech-stack.yml and isn't claude-shared, stop with a Critical warning: a Portfolio-Member shipping its own global manifest is forbidden per spec/portfolio/tech-stack/ §Global tech-stack manifest.

Preconditions

Before any signal probe, confirm:

  • project/portfolio.yml exists and parses as YAML. Missing or malformed manifests block the skill; route the operator to portfolio-audit Bootstrap or to fixing the parse error first.
  • The global manifest at claude-shared:portfolio/tech-stack.yml is reachable. When the active repository is claude-shared, read it locally. From any other repo, fetch via gh api repos/nolte/claude-shared/contents/portfolio/tech-stack.yml --jq .content | base64 -d and discard the raw content once a structured per-entry summary (name, kind, group, status) is in hand. Network-unreachable global manifests block the skill with an explicit error; don't proceed against a stale local cache without operator acknowledgement.
  • The active repository is on a feature branch (per spec/project/branching-model/), not develop or main. Confirm via git rev-parse --abbrev-ref HEAD. Capturing on a protected branch is a structural error and is refused even if file writes would technically succeed.

Operations

The single operation produces a tech_stack: block in project/portfolio.yml (creating it on a fresh capture, replacing it on a refresh). Run the eight steps in order; reordering breaks the spec's discovery sequence.

1. Resolve the inherited set

Parse the fetched global manifest into the inherited set: every entry whose status is active or experimental. Drop deprecated entries from the inherited working set but keep them visible for later step 7 (deprecation-deviation prompt). Record each inherited entry's name, kind, and group — those are the three fields needed to detect candidate matches and to decide whether a per-repo proposal would shadow an inherited entry.

2. Read the existing tech_stack: block (refresh path)

When project/portfolio.yml already carries a non-empty tech_stack: block, parse it into three working sets:

  • existing_additions — every entry under tech_stack.additions[]
  • existing_overrides — every record under tech_stack.overrides[]
  • existing_regroup — every record under tech_stack.regroup[]

On a fresh capture, all three working sets are empty. The operator's confirmations in step 7 decide which existing entries persist, get edited, or get dropped; never silently preserve or remove an existing entry across a refresh.

3. Probe repository signals

Read the signal-source set in the order documented in references/signal-source-map.md, and read that file now to ground every kind/group/lifecycle proposal that follows: it carries the authoritative per-signal mapping table and is the only source the skill consults for "what does this signal imply".

The probe is read-only. Don't shell out to package managers, don't run installers, don't invoke the language runtime. A missing signal file is an absence, not an error.

For every signal hit, draft a candidate entry with: name (kebab-case, derived from the signal), kind (from the table), provisional group (from the table; refined in step 5), provisional lifecycle (from the table), version (only when the signal carries an unambiguous version per the spec's SHOULD; otherwise leave blank), and source_of_truth (the relative path of the signal that justified the candidate). The candidate's role and status are deferred until step 7 (operator confirmation) because they aren't signal-derivable.

4. Drop inherited matches from the candidate set

For every candidate whose name and kind both match an inherited entry from step 1, drop it from the candidate additions set unconditionally — per the schema spec's MUST NOT, a consumer never re-declares an inherited entry. Record the dropped match in a session-local "inherited-confirmed" list so step 7 can reassure the operator that the inherited entry is already part of their effective stack.

A candidate whose name matches an inherited entry but whose kind differs is a Critical finding — re-declaring under a different kind silently shadows inheritance. Surface it in step 7 with the recommended resolution: rename the candidate, or open a coordination PR against portfolio/tech-stack.yml to amend the global entry.

5. Propose group: per candidate

group: is a MUST on every entry (in both additions[] and inherited via the global manifest). For every surviving candidate from step 4, propose a group: value following the Deterministic group proposal rules in references/signal-source-map.md: auto-fill the deterministic kind → group mappings without prompting (docs, test, ci, dep-bot, build, package-manager, plugin-shaped framework, claude-code runtime), decide the lint-kind case from the source_of_truth path (docs/-scoped → documentation, source-tree → quality), and prompt the operator for the ask-the-maintainer cases (language, non-claude-code runtime, non-plugin framework, deploy-target, other, and any ambiguous lint). Heuristic guesses on the ask cases mislead and are forbidden by the spec.

6. Propose lifecycle: per candidate

lifecycle: is optional but SHOULD be proposed. Follow the Deterministic lifecycle proposal rules in references/signal-source-map.md: auto-fill the unambiguous kind → lifecycle mappings (test / lint / dep-bot / package-manager → development; ci / build / docs → build; deploy-target → runtime) and prompt for the ambiguous kinds (language, runtime, framework, other), accepting "skip" (leaves the field absent) as a legitimate answer per the SHOULD.

7. Interactive per-entry confirmation

Per the discovery spec's MUST, present every proposed delta to the maintainer before any write. The confirmation is a single round through three working sets:

  • Proposed additions (from steps 3–6): for each candidate, present (a) the full field set (name, kind, group, lifecycle, version, source_of_truth, plus the pending role and status slots), (b) the signal source(s) that justified the candidate, and (c) the explicit "this is a repo-specific addition, not inherited" classification. The operator answers per entry — accept, reject, or edit. On accept, the operator supplies role (one sentence) and confirms status (default active; experimental is a legitimate alternative).
  • Proposed overrides — for every inherited entry the operator wants to suppress, build an override record with name, inherit: false, and a non-empty rationale. Empty rationale blocks the write. When the operator wants a different version of an inherited entry, surface the spec's two-step path (overrides: suppression PLUS a repo-specific additions: replacement) — never edit the inherited entry's fields directly.
  • Proposed regroup — for every inherited entry whose repo-specific purpose differs from the portfolio default (for example python used only by the MkDocs build pipeline in this repo), offer a regroup record with name, the new group:, and a non-empty rationale. Refuse a no-op regroup (new group equals inherited group) and refuse a regroup paired with an override on the same name (the override already removes the entry, so the regroup would be dead code).

Free-form additions (entries the operator declares despite no matching signal) are permitted under the spec's SHOULD, but the operator must acknowledge that portfolio-audit will Warning the missing signal. Record that acknowledgement verbatim in the entry's rationale with the marker phrase acknowledged-missing-signal: plus a one-sentence justification; the marker downgrades the audit finding to Suggestion.

Don't proceed to step 8 until every proposed addition, override, and regroup has been individually confirmed, edited, or rejected. An empty tech_stack: {} is a legitimate outcome and is written only after explicit confirmation — silent emptiness is forbidden.

8. Compose and write

Compose the final tech_stack: block from the three confirmed working sets. Before writing, run the local validation checklist in references/write-validation.md (mandatory-field, shadow-without-override, override-rationale, regroup, and enum checks); reject or block the write on any failure, surfacing the offending entry.

Then write project/portfolio.yml atomically. Preserve unrelated top-level keys (capabilities:, audiences:, peers: — whatever the existing manifest carries); only the tech_stack: key is rewritten. Re-parse the file after writing to confirm the result is valid YAML; on parse failure, restore the prior version and surface the parse error.

Confirm in the user's language with the path of the rewritten manifest, a per-section count (additions: N, overrides: M, regroup: K, inherited-confirmed P), and the reminder that opening a PR via pull-request-create is the next step — the skill stops at the write.

The skill MAY emit a "candidates not picked" log alongside the confirmation summary — signal-derived candidates the operator rejected in step 7, each with a one-phrase reason. The log is for the operator's audit awareness only; it is NOT committed to the repository.

Gotchas

  • tech_stack: {} is a legitimate empty result, not a missing block. A repository that uses the full inherited stack unmodified and has no repo-specific additions writes tech_stack: {} after operator confirmation — never omit the key entirely. The audit treats an absent tech_stack: key as a Critical "manifest doesn't conform to the schema spec"; an empty mapping is the explicit "inherited as-is" affirmation. Don't shortcut by leaving the key out even when the confirmation result has zero additions, overrides, and regroups.
  • The inherit: field is a fixed-literal, not a boolean knob. Every overrides[] record carries inherit: false exactly — the field is named explicitly for readability and to leave room for a future opt-in semantic without re-shaping the record (per schema §Inheritance semantics). Writing inherit: true or omitting the field is a parse error against the schema.
  • Regroup vs. overrides-plus-additions is a one-or-the-other choice. A consumer who needs a different group value on an inherited entry uses regroup:. A consumer who needs a different version, role, kind, or any other field on the same entry uses overrides: (to suppress) plus additions: (to redeclare). Mixing both records for the same name is a Warning audit finding; the skill refuses to write the combination.
  • Free-form additions need the acknowledged-missing-signal marker. When the operator insists on declaring an entry that no signal supports (for example a documented deployment target that isn't yet wired into a workflow file), the skill writes rationale: "acknowledged-missing-signal: <operator-sentence>". Without the marker phrase the audit emits a regular Warning; with the marker the audit downgrades to Suggestion. Don't reword the marker phrase — portfolio-audit recognises the exact acknowledged-missing-signal: prefix.
  • The global manifest is read-only from this skill. Even when running inside claude-shared itself, the skill never modifies portfolio/tech-stack.yml. Promoting a repo-specific addition to portfolio-wide is a hand-curated PR against the global manifest, not a skill operation. When the operator asks the skill to "add this to the global stack", stop and route them to a hand-authored PR.
  • Refresh preserves operator-edited entries that survived a prior round. When existing_additions from step 2 contains entries the current signal probe wouldn't surface (for example a hand-added kind: deploy-target entry that doesn't have a signal class), the skill keeps presenting those entries in step 7 with their existing field set so the operator can re-confirm, edit, or drop them. Silently deleting them across a refresh would lose deliberate operator authoring.

Examples

  • Read examples/01-fresh-capture-empty-additions.md when running a fresh capture on a project with no existing tech_stack: additions.
  • Read examples/02-refresh-after-dep-bot-swap.md when refreshing the tech-stack capture after Dependabot or Renovate has swapped a dependency.
  • Read examples/03-deviation-with-override-and-regroup.md when the project deviates from the portfolio baseline and you need to see how overrides and regroup records are authored.

Resumability

Per spec/claude/resumable-work/, this skill is resumable: true. State is persisted to .resume/tech-stack-capture/<run-id>.yml after every successful user-approval gate and after each named phase boundary. On re-invocation, scan that directory for files with status: in_progress whose inputs: snapshot matches the current invocation; if one matches, prompt the operator with Resume run <run_id> from phase <phase> (last checkpoint <last_checkpoint_at>)? [resume / start-new / discard]. The state-file envelope (schema_version, run_id, inputs, phase, decisions[], status, ...) and the fail-closed semantics on schema or YAML errors are load-bearing in the spec; don't duplicate those rules here.

Hard rules

  • Never write project/portfolio.yml without an interactive per-entry confirmation round (steps 7) on every addition, override, and regroup. Skipping confirmation violates the MUST in spec/portfolio/tech-stack-discovery/ §Discovery sequence per repository regardless of whether the result is technically a no-op.
  • Never modify the global manifest portfolio/tech-stack.yml from this skill, in any repository. The global manifest is hand-curated per the spec's MUST; automated detection is forbidden.
  • Never declare an additions[] entry that matches an inherited entry on both name and kind. Drop inherited matches automatically in step 4; never present them as if they were repo-specific.
  • Never write an overrides[] record with an empty or whitespace-only rationale. The spec's MUST requires a non-empty prose sentence; the audit treats empty rationale as a Warning.
  • Never write a regroup[] record whose new group equals the inherited entry's group (no-op regroup) or whose rationale is empty. Both are Warning-grade audit findings — refuse the write upstream.
  • Never invent a signal hit. Every candidate entry traces back to a file the skill actually read. Free-form operator additions are permitted via the acknowledged-missing-signal marker; fabricating a signal source the skill didn't probe is forbidden.
  • Never edit any field of an inherited entry other than via the schema spec's two mechanisms (overrides[] to suppress, regroup[] to re-classify the group). The inherited entries' name, kind, role, version, status, lifecycle, since, and source_of_truth stay portfolio-curated; the per-repo tech_stack: block never mutates them.
  • Never commit a "candidates not picked" log to the repository. The log is session-local and exists only for the operator's audit awareness; persisting it would couple portfolio-audit to in-band log files that aren't part of the schema.
  • When spec/portfolio/tech-stack-discovery/ or spec/portfolio/tech-stack/ disagrees with this skill, the spec wins. Propose updating this skill rather than silently diverging.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.