Agency Skills
SkillsMCPCollectionsContributorsContribute
Install MCP
Agency Skills

Skills written by the people who run agencies, consultancies and coaching firms. Free and open source, MIT.

LibraryAll skillsCollectionsContributors
ContributeHow to contributeContributor guideGitHubReport a problem
GiaCompany BrainPowered by Gia
Skills/Run the firm/Pipeline by script
RevOps

Pipeline by script

Use this skill when a small firm runs its pipeline from its own folder with no CRM and an agent records what happens on each deal. Every movement (a send, a reply, a booking, a decision that changes the next step or whose move it is) goes in through one script command that writes the pipeline row, the activity log line, an archive copy and the rebuilt dashboard together, and a drift check at the start of every agent turn names any row left behind its log line. Produces one Markdown pipeline file (open deals, channels, clients won, closed, and a dated activity log), a generated dashboard whose weekly counts come from event tags, and the commands that keep them in step. Also use for "log that reply", "move the deal", "add this to the pipeline", "close it out", "update the client row", "track pipeline without a CRM", "why is this row out of date".

Paul MunroFounder, AppliedGTM
Say something like
“log that reply”“move the deal”“add this to the pipeline”“close it out”“update the client row”“track pipeline without a CRM”
pipelineno-crmactivity-loggingdashboardsmall-firmdrift-check
SKILL.mdView on GitHub

Applies whenever something moves on a deal, a channel or a client at a small firm that keeps its pipeline in one Markdown file and runs no CRM. Produces one recorded movement: the row, the activity log line, an archive copy and the rebuilt dashboard, written together by one command.

The play

  1. Keep the whole pipeline in one Markdown file: open deals, channels (doors that could lead to deals, kept apart from deals), clients won, closed, and an activity log with the newest line first. A dashboard is generated from it. Tables, columns, IDs and archive copies are in references/file-layout.md.
  2. Treat the agent as the interface. The person says what happened in a sentence. The agent turns it into one command with every field explicit: row ID, Last activity, next step, next date, whose move, log text and event tags. The script reads no prose and guesses no field; the only date it supplies is the time of recording, in front of a Last activity entry that starts without one. When the sentence leaves a needed field open, the agent asks.
  3. Pick the subcommand by what happened:
    • Something moved on an open deal or a channel: move.
    • A new deal or a new door: add, with every column given. The script assigns the next free ID.
    • A deal or a door ended: close, with the outcome. The row moves to the closed table and its ID is retired.
    • Something changed on a won client: client, found by account name. Each writes the row and a timestamped log line together, keeps a copy of the file as it stood, and rebuilds the dashboard. A scheduled daily run rebuilds it too. Flags, refusals and a worked example are in references/one-command.md.
  4. Tag every move, add and client with what happened, from a fixed set of ten: sent, received, held, booked, ask, price, yes, post, intro, other. The flag repeats. The dashboard computes the week's counts from these tags. An untagged line counts only on an uppercase marker such as SENT, HELD or BOOKED, and asks, prices, yeses and posts come from tags alone.
  5. Record in the same turn the movement is seen. A send, a reply, a booking, and a decision that changes the next step or whose move it is all count as movements.
  6. Let the drift check run at the start of every agent turn. A hook runs it and prints a line only when a row is stale: its Last activity is older than its newest log line. The check names the row and changes nothing. The fix is a move on that row. How each date is read is in references/drift-check.md.
  7. When the script refuses, it writes nothing and says why. Correct the command and run it again.
  8. When a movement turns up that no subcommand can record, extend the script. New deals, closes and client rows each became a subcommand once hand edits to them were the last movements the script could not record. Test the change on a scratch copy of the file; check prints the parse counts.

What good looks like

  • First thing I check: whether the turn opened with a drift line. The check is silent when the file is clean, so a drift line means someone wrote a row or a log line around the command. I fix it with a move on that row first.
  • The mediocre version is rows and log lines edited by hand. They drift apart, which is why I built the script.
  • I know it is working when the week's counts come straight off the dashboard, because every line carries its event tags and nobody counts by hand.
  • Every row says whose move it is. A change to that is a movement, and I record it like a send or a reply.

Rules

  • MUST record every movement through the command, in the same turn it is seen.
  • MUST name the events on every move, add and client, from the fixed set.
  • MUST pass every field explicitly, and ask the person for any field the sentence left open.
  • MUST fix a drift line with a move on the named row.
  • MUST test any change to the script on a scratch copy of the pipeline file.
  • NEVER edit a pipeline row or an activity log line by hand.
  • NEVER edit the dashboard directly. Every write rebuilds it from the file.
  • NEVER reuse a closed ID.
  • NEVER let the drift check change the file or block a turn. It reports, and the agent acts.
3 reference files
references/drift-check.md

The drift check

One check. A row has drifted when its Last activity is older than its newest log line. That happens when a log line or a row is written around the command: the command writes both together, so movements recorded through it stay in step.

When it runs

  • At the start of every agent turn. A hook runs the check in its quiet form before the agent reads the prompt. When the file is clean it prints nothing. When rows are stale it prints one line naming them and saying to fix them with a move. It has a short timeout, it never blocks the turn, and any failure inside it is swallowed, so it can never break a prompt.
  • On demand. Run drift at any time.

It reads the open deals and channels tables. It changes nothing.

What it prints

D4 <row name>: log <Day Mon D> vs row <Day Mon D>

One line per stale row, with the newest log date first and the row's date second.

How the log date is read

The newest dated activity log line whose text, right after the timestamp, opens with the row's ID. A row with no log line is skipped.

How the row date is read

The newest date that starts a sentence in the Last activity cell: at the start of the cell, or right after ". " or "; " (a closing quote or bracket before the space is allowed). A cell is a history, and its sentences are in no fixed order, so the leading date alone gives false alarms. --leading reads only the date at the very start of the cell, for when that literal reading is wanted.

A date is a month name and a day, with an optional weekday in front, or a full year-month-day. The year is the most recent one that puts the date on or before today. A stated weekday must match the date; if it matches in one nearby year, that year is used, and if it matches none, the date goes unread. A row with no readable date is skipped.

Fixing a drift line

Run a move on the named row with a --last that records the movement in the log line. If the movement happened on an earlier day, start --last with that date, since a --last with no date gets the time of recording.

references/file-layout.md

File layout

One Markdown file holds the whole pipeline. Each part sits under a level-two heading, with one table below it, and the script finds each part by the start of its heading text. Columns are found the same way, by the start of the header name, so a header can carry a short note in brackets, as in Stage (since). A generated dashboard and a folder of archive copies sit beside the file.

Open deals

Heading starts with Deals, open.

IDAccountContact (function)SizeSourceOfferValueStage (since)Last activityNext stepNext dateWhose move
  • Last activity holds dated sentences. Each move puts the new one in front of the old, joined by ". ", so the cell reads as a short history. The drift check reads its dates.
  • Next step and Next date say what happens next and when.
  • Whose move says who acts next. A change here is a movement, recorded like any other.

Channels

Heading starts with Channels. A channel is a door that could lead to deals. Doors live in their own table so the deals table holds deals only.

IDDoorContact (type)SourceCould lead toStatusLast activityNext stepNext dateWhose move

Status takes the place of Size and Value. The dashboard reads four statuses: Active, Waiting, Dormant and Closed.

Clients (won)

AccountContactWhat is liveBillingNext

This table has no ID column. The client subcommand finds a row by its Account cell. An account name ending in "(past)" marks an engagement that has ended. The cells are descriptions, so each update replaces a cell in full.

Closed

RecordClosedOutcome

close writes the Record cell as the row's name with its ID in brackets, the Closed cell as the day it closed, and the Outcome in the person's words.

Activity log

Heading starts with Activity log. Newest line first, one line per movement:

- YYYY-MM-DD, H:MM AM TZ: D7 <what happened> [events: sent, price]
- YYYY-MM-DD, H:MM AM TZ: C3 NEW ROW, <why the door was added> [events: intro]
- YYYY-MM-DD, H:MM AM TZ: D5 CLOSED, <outcome and why>
- YYYY-MM-DD, H:MM AM TZ: <Account> <what changed> [events: yes]

The ID right after the timestamp is what ties a line to its row. Client lines carry the account name in its place. The events tag closes the line when the command names events.

IDs

An ID is one capital letter and a number. Open deals take D numbers and channels take C numbers. add assigns the next free number in that series unless an ID is given. A closed ID stays retired, so every log line keeps pointing at one record.

The dashboard

A builder script reads the pipeline file and the archive copies and writes a dashboard page into its own folder, and nothing outside it. Its parsing is deterministic: anything it cannot read shows as "not logged" or as the raw text. Every command rebuilds it, and a scheduled daily run rebuilds it as well. If the firm publishes the dashboard somewhere, the command's output carries a reminder to republish it.

Each render also writes a weekly record for the most recent Friday, covering Saturday to Friday. A record for that week that came from another source is left alone. What the record counts is in one-command.md.

Archive copies

Before every write, the script copies the file as it stood into an archive folder, named with the date and minute (a number is added when two writes share a minute). It then writes to a temporary file and swaps it in, so the pipeline file is always whole.

The copies give the weekly record its starting point. It compares the copy from just before the week began (taken within the seven days before the week) with the file at the week's end. That comparison yields the week's commitments due and kept, and its new clients. With no copy in that window, those counts stay empty.

references/one-command.md

One command

The script has six subcommands. Four of them record movements, and each of those writes the row and one timestamped log line together, keeps an archive copy of the file as it stood, writes the file, and rebuilds the dashboard. A refusal writes nothing.

From a sentence to a command

The person speaks in a sentence. The agent writes the command, with every field explicit and every date concrete.

"Sent the proposal to them this morning. It's on them now. I'll follow up Friday if I hear nothing."

move D4 --last "Proposal sent" --next "Follow up on the proposal" \
  --date "<Mon D of that Friday>" --whose "Theirs" \
  --log "Proposal sent; their move" --event sent --event price

The agent resolves "Friday" to a date before it runs the command. If the sentence did not say which row, or left the next step open, the agent asks.

move

move ID --log TEXT plus at least one cell: --last, --next, --date, --whose, --stage, --offer (Could lead to, on a channel), --value, --status. Optional: --replace-last, --event TYPE (repeatable).

  • Finds the row by ID in the open deals or channels table.
  • Replaces only the cells given.
  • --last goes in front of the existing Last activity, joined by ". ". --replace-last overwrites the cell instead.
  • A --last that starts without a date gets the time of recording put in front, in the form "Mon D, H:MM AM TZ: ", so the drift check can read it. To record a movement from an earlier day, start --last with that date.
  • A --log text that opens with the row's ID has that copy trimmed, since the timestamp already carries the ID.

add

add --log TEXT plus every column: --account (the Door, on a channel), --contact, --source, --offer (Could lead to, on a channel), --stage, --last, --next, --date, --whose, and --size and --value for a deal or --status for a channel. Optional: --table deals|channels (deals by default), --id, --event TYPE.

  • Every column needs a value. A missing one refuses and names it.
  • The ID is the next free D or C number unless --id is given. An ID that already exists refuses.
  • The log line reads "ID NEW ROW, <text>".

close

close ID --outcome TEXT --log TEXT, optional --closed TEXT (today's "Mon D" by default).

  • Removes the row from open deals or channels and appends "<name> (<ID>)", the closed day and the outcome to the closed table.
  • The log line reads "ID CLOSED, <text>".
  • The ID is retired.

client

client ACCOUNT --log TEXT plus at least one of --contact, --live (What is live), --billing, --next. Optional: --event TYPE.

  • Finds the row whose Account cell equals ACCOUNT, or starts with it followed by a space or " (", ignoring case.
  • No match refuses and lists the accounts. More than one match refuses and asks for a longer name.
  • Each cell given is replaced in full.
  • The log line names the account, without any "(past)".

drift and check

drift prints one line per row whose Last activity is older than its newest log line, and nothing when the file is clean. --quiet is the form the hook runs. --leading reads only the date at the very start of the cell. Details in drift-check.md.

check prints, for each table, the rows, the columns and the rows it could not read, then the number of log lines, then OK or FAIL.

A workspace option points any subcommand at another folder. Tests run that way, on a scratch copy.

Refusals

The script refuses, and writes nothing, when:

  • the ID is not in the open deals or channels table, or a new ID already exists or is not a letter and a number;
  • no cell was given;
  • a flag names a column the table lacks;
  • a value holds a line break (a row stays on one line);
  • the new row would have a different number of cells than the header (a pipe in a value is escaped first);
  • a needed heading or table is missing from the file.

Event tags

--event works on move, add and client, and repeats. The fixed set is sent, received, held, booked, ask, price, yes, post, intro and other. The log line then ends " [events: sent, price]". Without --event the line carries no tag.

The weekly record computes its counts from dated log lines in the week:

TagCounts toward
sentnotes sent
heldsessions held; on a deal row, buyer conversations held too
received, intro, bookedcame in
askasks made
pricepriced asks
yespaid yeses
postposts published
otherrecorded, counted toward no total

An untagged line counts only on an uppercase marker: SENT, HELD, BOOKED, or a reply marker such as REPLIED. "NOT sent" counts as nothing. Asks, priced asks, paid yeses and posts come from tags alone, so a week with no tagged lines leaves those counts empty. The record's own note says how many lines were counted by tag, how many by marker, and how many went uncounted.

Commitments due and kept, and new clients, come from comparing archive copies (see file-layout.md). A commitment is a row whose next date fell in the week. It counts as kept when the row's Last activity or a log line for it is dated on or after that next date, within the week. A new client is an account new to the clients won table that week. A row added already marked "(past)" records an old engagement and counts as no win.

Install this skill

Pick where your agent runs.

View prompt
Help me install the skill named "pipeline-by-script" from this GitHub URL in Claude:
https://github.com/agiea-brain/agency-skills/tree/main/skills/paul-munro/pipeline-by-script

Prepare only the "pipeline-by-script" skill, not other skills in the repository.

Inspect each complete skill folder, not just SKILL.md. Preserve the original instructions and include all supporting files (such as references, scripts, templates and assets), keeping their relative paths intact. Package each skill as a downloadable ZIP containing its skill folder with SKILL.md directly inside that folder. If a skill only contains SKILL.md, package that folder too.

Give me the download link for each ZIP and walk me through uploading and enabling it under Settings, Capabilities, Skills in Claude. If you cannot access GitHub or create downloadable files here, explain what I need to download and how to package and upload it myself. Do not invent missing files or claim the skill is installed before I upload it.

Paste this prompt into Claude. It packages the skill folder, supporting files included, and walks you through uploading it under Settings, Capabilities, Skills.

0installs
RevOpscategory
MITlicense