diff --git a/.github/screenshots/site-makeover/getting-started-marketplace.jpg b/.github/screenshots/site-makeover/getting-started-marketplace.jpg new file mode 100644 index 0000000..5265211 Binary files /dev/null and b/.github/screenshots/site-makeover/getting-started-marketplace.jpg differ diff --git a/.github/screenshots/site-makeover/home-hero.jpg b/.github/screenshots/site-makeover/home-hero.jpg new file mode 100644 index 0000000..2191e31 Binary files /dev/null and b/.github/screenshots/site-makeover/home-hero.jpg differ diff --git a/.github/screenshots/site-makeover/home-maverick-whatsnew.jpg b/.github/screenshots/site-makeover/home-maverick-whatsnew.jpg new file mode 100644 index 0000000..215b9bf Binary files /dev/null and b/.github/screenshots/site-makeover/home-maverick-whatsnew.jpg differ diff --git a/.github/screenshots/site-makeover/home-mundane-band.jpg b/.github/screenshots/site-makeover/home-mundane-band.jpg new file mode 100644 index 0000000..e36ce87 Binary files /dev/null and b/.github/screenshots/site-makeover/home-mundane-band.jpg differ diff --git a/.github/screenshots/site-makeover/home-services-grid.jpg b/.github/screenshots/site-makeover/home-services-grid.jpg new file mode 100644 index 0000000..3ae8474 Binary files /dev/null and b/.github/screenshots/site-makeover/home-services-grid.jpg differ diff --git a/.github/screenshots/site-makeover/mobile-390.jpg b/.github/screenshots/site-makeover/mobile-390.jpg new file mode 100644 index 0000000..a91bad2 Binary files /dev/null and b/.github/screenshots/site-makeover/mobile-390.jpg differ diff --git a/.github/screenshots/site-makeover/notice-page.jpg b/.github/screenshots/site-makeover/notice-page.jpg new file mode 100644 index 0000000..defb0e9 Binary files /dev/null and b/.github/screenshots/site-makeover/notice-page.jpg differ diff --git a/.github/workflows/pages-deploy.yml b/.github/workflows/pages-deploy.yml index aa5d1eb..560569d 100644 --- a/.github/workflows/pages-deploy.yml +++ b/.github/workflows/pages-deploy.yml @@ -8,6 +8,7 @@ on: - '.claude/skills/**' - 'CHANGELOG.md' - 'copilot-instructions.md' + - 'NOTICE' - '.github/workflows/pages-deploy.yml' workflow_dispatch: diff --git a/NOTICE b/NOTICE index 71b5e0d..0d5123e 100644 --- a/NOTICE +++ b/NOTICE @@ -3,6 +3,410 @@ Copyright 2026 Sunny Kolattukudy Licensed under the Apache License, Version 2.0. +═══════════════════════════════════════════════════════════════ + MANDATORY COMPLIANCE NOTICE +═══════════════════════════════════════════════════════════════ + +Per Section 4(d) of the Apache License 2.0, this NOTICE file must be +included in all copies or substantial portions of this software, in all +derivative works, and in any and all redistributions thereof. + +By deploying, using, or permitting any human being within your organization +to execute pncli — including accidentally in a terminal they didn't mean to +open — you agree to the following irrevocable operational requirements: + +1. MANDATORY STAND-UP PARTICIPATION + All teams must hold a daily stand-up, strictly time-boxed to fifteen + (15) minutes. The stand-up will run forty-five (45) minutes. + + Each attendee answers three questions: what they did yesterday, what + they are doing today, and whether anything is blocking them. Blockers + may not be discussed in the stand-up. Schedule a separate meeting. + The separate meeting will be attended by the same people, at a + different time, so that the stand-up can remain on time. + + If a topic requires detail or problem solving, it must be taken + offline. Offline means a second meeting. The second meeting requires + a third to align on next steps. Resolution happens asynchronously, + in a Confluence page no one will find. + + Anyone who says "no blockers, nothing to add" will be privately noted + as either not doing enough work or not communicating enough about it. + Both are concerns. Say something. Say anything. The stand-up is not + about information. It is about presence. Be present. + +2. RETURN-TO-OFFICE COMPLIANCE + All employees are required to be in office five (5) days per week, + effective immediately, no exceptions. + + Exception requests may be submitted via the Flexible Work Arrangement + Portal. The portal is not publicly linked. You did not hear about it + here. Approved exceptions are confidential and will not be disclosed + to other employees who also do not have exceptions and are definitely + in the office. You did not notice this. + + Employees who choose not to comply will be noted as having made a + choice. The consequences will follow through a process entirely + unrelated to this policy and will not be referred to as a layoff. + It is an alignment exercise. + + Desk assignment is managed by Facilities. Facilities is managed by + a spreadsheet last updated by someone who no longer works here, under + a policy that has since changed, in a building reconfigured twice + without updating the spreadsheet. Your assigned desk may be a column, + a decommissioned printer station, or a hot desk already occupied by + someone with an exception. + + There are no exceptions. + +3. JIRA TICKET REQUIREMENT + Before running any pncli command in a non-local environment, your team + must first raise a Jira ticket requesting permission to raise a Jira + ticket. The approval workflow requires sign-off from: your manager, + your manager's manager, a "technical architect" who hasn't written code + since 2011, and a business analyst who will ask you to "put it in a + PowerPoint" before they can approve it. + +4. CHANGE ADVISORY BOARD REVIEW + All changes must be submitted to the Change Advisory Board (CAB) for + approval before implementation. Before you can present to CAB, you + must first obtain a Permit. + + Permits are tiered — Standard, Elevated, Complex, and Significant — + each with its own intake, its own approver, and a processing window + of ten (10) to forty-five (45) business days. Which tier applies is + determined by a classification guide available upon request by + submitting a Standard Permit. + + Each intake is different. Some are forms. Some are emails to a shared + mailbox monitored by someone in a department that has since been + restructured. Some require a conversation with a Delivery Manager who + may or may not be aware they are part of the process. These things + vary by team, by floor, and by who picked up the phone last time. + + Your change may be reclassified into a higher tier at any point. This + resets the clock. There is no notification. + + CAB reviews the change. No member of CAB fully understands what they + are reviewing. Approval is based on whether the paperwork looks + complete and whether the change has been waiting long enough that + objecting feels rude. + + CAB does not approve software. CAB approves paperwork about software. + The distinction matters a great deal to CAB. It is invisible to + production. + +5. APPROVAL CHAIN + Any use of the `--force` flag, in any context, requires written + approval from the Chief Compliance Officer, the VP of Engineering, + two (2) independent auditors, and your mother. Appeals may be filed + with the Ombudsman of Flags, whose inbox has been full since 2019. + +6. DOCUMENTATION REQUIREMENTS + All documentation must be maintained in the following approved + platforms: + + a) Microsoft Excel. Specifically, a workbook called something like + "pncli_tracking_v3_FINAL_v2_USE_THIS_ONE.xlsx" in a OneDrive + folder that three people have access to and one of them has left. + + b) The internal SharePoint site, last redesigned in 2019 by a vendor + who no longer exists, which requires IE compatibility mode and has + a search function that returns results from 2014. This is by design. + + Under no circumstances may documentation be stored somewhere findable. + Confluence is permitted as a secondary archive, provided every page is + titled "Draft - DO NOT USE" regardless of status. + +7. QUARTERLY REVIEW PROCESS + Usage must be reviewed quarterly by a standing committee, which must + produce a findings report, a summary of the findings report, an + executive summary of the summary, and a one-page overview suitable + for someone who will not read it. + + The quarterly review process itself must also be reviewed — every + ninety-one (91) days — to ensure it remains fit for purpose. The + review-of-the-review is a separate process and does not count toward + the quarterly review. Both calendars must be maintained in Excel. + +8. REMEDIATION TIMELINES + Any issue identified during a quarterly review must be remediated + within thirty (30) calendar days. Remediation requires a change + request. Change requests take ninety (90) calendar days to process. + Teams are encouraged to be proactive. + +9. TRAINING CERTIFICATION + All operators must complete a mandatory 8-hour training course titled + "Introduction to Reading --help Output." A 30-minute lunch break is + provided but must be requested 48 hours in advance via the Lunch + Request Portal, which is currently down for scheduled maintenance. + + Completion certificates are stored in the Learning Management System, + which syncs to a SharePoint list that feeds an Excel dashboard emailed + every Friday to eleven people, nine of whom have set up an auto-archive + rule. + +10. METRICS & REPORTING + All usage must be reported to the Developer Productivity Team, who + will use the data to build a dashboard that no one looks at, presented + quarterly to executives who will ask why the numbers aren't higher and + whether there is an app for it. + + Raw metrics must be exported to Excel before being imported into the + approved reporting tool, because the approved reporting tool cannot + connect directly to the data source. This is a known issue. It is in + the backlog. + +11. DEVELOPER ACCESS RECERTIFICATION + Every developer must complete an Access Recertification Form every + ninety (90) days to confirm they are still permitted to debug the + software they are personally responsible for. Allow fifteen (15) + business days for processing. If your recertification lapses, your + debugging access will be suspended, at which point you may raise an + incident ticket for the outage caused by the thing you are no longer + allowed to debug. + + Recertification is per-machine. If you use a VM, that is a + conversation with the virtualization team that will take longer than + ninety days. + + There is no reminder system. Compliance is the developer's + responsibility. Non-compliance is also the developer's responsibility. + +12. ENVIRONMENT COMPLIANCE + Your organization maintains four pre-production environments. None + of them can do what they are supposed to do, but they are maintained + nonetheless. + + - RND: No production-like data is permitted. No realistic data of + any kind is permitted. What is permitted is unclear. RND exists + on the architecture diagram and in the hearts of those who + approved the budget for it. + + - UAT: User Acceptance Testing. Users do not test here. The + environment has the same data restrictions as RND, which is why. + The name remains. + + - QA: The one environment with enough data to be useful, which does + not match production in any meaningful way. QA passing is a team + ritual, like a rain dance, except the rain still doesn't come on + schedule and someone has to write a post-mortem about it. + + - PROD (x2): There are two. Both are production. This is not a DR + configuration anyone can fully explain. Raising the question in a + meeting is not recommended unless you have blocked the next two hours. + + All four pre-production environments must show green before a + deployment is approved. Catching bugs is a stretch goal. + +13. RELEASE MANAGEMENT + DORA tracks four software delivery performance tiers: Elite, High, + Medium, and Low. Elite teams deploy multiple times per day. Low + performers deploy somewhere between once a week and once a month. + + Your organization ships three to four times per year. The researchers + left a blank below Low. They assumed it was a data error. + + Each release requires thirty (30) to forty-five (45) days of + approvals. This is called a release cadence. Geologists call it + deposition. + + Security note: your organization typically detects a published CVE + forty-five (45) to sixty (60) days after it is public. Your + remediation SLA is thirty (30) days from detection. Your next release + window was scheduled by a calendar that predates the concept. These + three facts are aware of each other. No one else is. + +14. INCIDENT MANAGEMENT + Production incidents are handled by an offshore support function + under the following severity model: + + P1 — CRITICAL: Something a developer mentioned in passing. + P2 — HIGH: Something that has been broken for three weeks and was + only just noticed. + P3 — MEDIUM: An active outage affecting all users, escalated after + the offshore team determined it did not match P1 criteria + because the word "all" was not in the dropdown. + P4 — LOW: Everything else, including P1s that arrived on a Friday + after 4pm, which is a different kind of P1. + + A developer may be paged within minutes, or receive a ticket three + weeks after the incident was already resolved. The difference is not + correlated with severity. It is correlated with something, but nobody + has had time to find out what. + +15. TOOLING APPROVALS + Any tool that measurably closes the gap between thinking of something + and doing it must be formally approved before use. Approval is granted + per version. A minor version bump is a new application. + + By the time a version is approved, it has been superseded at least + twice, and the approved version is now the one with the known + vulnerability the newer version fixed. This is the process working + as intended. + + In an era where AI tooling shifts capability weekly, this ensures + your engineers are always eighteen months behind the current state + of the art, which is a competitive position your organization has + committed to with some consistency. + + Library and package management through the internal artifact + repository is, notably, fast and functional. Nobody has scheduled + a meeting to discuss why the contrast exists. It would take several + months to approve the agenda. + +16. OVERSIGHT & APPROVAL GOVERNANCE + All technical decisions must be reviewed by a cross-functional + committee before implementation. The committee includes Engineering, + Security, Risk, Compliance, Architecture, Delivery, and a rotating + seat for someone who joined the meeting late, muted themselves + immediately, and has not been seen since. + + Most committee members have no technical background. Developers are + therefore required to explain the decision to the committee so the + committee can approve it. The developer who explained the decision + is the same developer who made it. The approval is from someone who + could not have made it. The system has a name for this: governance. + + The audit trail, however, is immaculate. + +17. WORKFORCE STRUCTURE + For every engineer using pncli to do something, approximately five + (5) colleagues are in a meeting about it. This is not a criticism. + The meetings are very organized. + + Stories are distributed based on capacity, availability, and story + points — not on familiarity with the system, ownership of the code, + or demonstrated ability to complete the story. This prevents + bottlenecks. It also prevents progress, but the velocity chart looks + balanced, which is what gets presented to leadership. + + Team meetings include the full cross-functional group, meaning the + room is large enough that most people cannot contribute and small + enough that no one can leave. Cameras are optional. Attention is + aspirational. The meeting runs to time because the Scrum Master has + a hard stop. + +18. TECHNICAL LEAD RESPONSIBILITIES + All paperwork that requires someone who understands what the software + does must be completed by the Technical Lead. This is because the + Technical Lead is the only person who knows where the form is, who + the right person to email is, and what the change actually involves. + Delegating results in a question escalated back to the Technical Lead + anyway. They have been designated as the efficient path. + + Any effort to let engineers build the system will surface a + previously unscheduled requirement that redirects them to paperwork. + The story moves back to In Progress. The sprint will not be adjusted. + The velocity will be noted as a concern at the retrospective. + + PRODUCTION SUPPORT: The Technical Lead will be paged when a service + fails, regardless of whether they own the service or have ever seen + it. This assumption will not be tested in advance. It will be tested + in production, which is a different kind of test and does not require + a Test Evidence Pack. + + TECHNICAL DIRECTION: Enterprise Architects attend vendor briefings + and produce recommendations based on what the vendor demonstrated. + The Technical Lead determines what the organization will actually + build, which will differ substantially, and explains the difference + in a way that does not make the Architects feel bad about the + briefing. This is called alignment. It takes several meetings. + + DEADLINE ACCOUNTABILITY: If the project is late, the Technical Lead + will be asked to explain why. The correct answer — that the timeline + was set before requirements were known and two engineers spent six + weeks on Permit paperwork — is not the answer that will be recorded. + The recorded answer will reference planning and estimation. The + Technical Lead's name will be adjacent to both words. + + SURPRISE DEADLINES: Periodically, a deadline will appear that is two + weeks away, originating from a commitment made in a meeting the + Technical Lead was not invited to, by someone who did not know what + the work involved, to someone who needed a date. The Technical Lead + is now accountable for the date. The people in the meeting are + accountable for their calendars. + + SECURITY TEAM APPLICATIONS: Occasionally an application owned by + the Security team will need support. The Security team will indicate + this is not something they support. The Technical Lead will be asked. + The application will be unrecognizable. The Technical Lead will + support it anyway, because a P1 with no owner is somehow worse. + + The Technical Lead's availability for technical work is approximately + zero (0) to two (2) hours per week. The Technical Lead is considered + a resource. The resource is fully utilized. Utilization is high. + Output is a separate metric. + +19. INCIDENT ACCOUNTABILITY & CORRECTIVE ACTION + When something breaks in production, the response proceeds in two + phases: fixing it, then generating paperwork about having fixed it. + The paperwork phase is longer. + + The developer closest to the broken thing will produce: a root cause + analysis, a contributing factors document, a timeline reconstruction, + a risk register update, a lessons-learned summary, a corrective action + plan, a corrective action plan review, and a slide deck for a + post-incident review attended by people who will ask questions that + suggest they have not read any of the above. + + Developers who accumulate corrective actions are placed on the + Heightened Oversight Register. There is no formal process for being + removed. There is a formal process for being added. The asymmetry is + not documented, but it is consistent. + + The Heightened Oversight Register should not be confused with the + Enhanced Monitoring List, the Watch List for Delivery Risk, the + Escalation Tracker, or the informally maintained spreadsheet kept by + one specific delivery manager that nobody is supposed to know about + but everyone does. These are separate documents with overlapping names + and no shared governance. They are all maintained in Excel. + +20. QUALITY ASSURANCE + Your organization has two separate quality functions. They do not + report to the same person. They have different definitions of quality. + Neither definition is software. + + PRODUCT QUALITY ASSURANCE ensures that user stories are correctly + written. Not that the software works. That the stories are correctly + written. Stories deviating from the approved format will be returned + for revision regardless of whether the software is already deployed + and in use by actual users. Product QA owns the story. What happens + after is someone else's lane. + + Acceptance criteria are reviewed against a seventeen-item checklist. + Four items are duplicates with different wording. One references a + template that no longer exists. Completion is mandatory. + Comprehension is not assessed. + + QUALITY ENGINEERING reviews the Test Evidence Pack for completeness + and correct formatting. Whether the tests caught anything is not on + the sign-off sheet and is therefore not in scope. + + A test executed against the wrong environment with the wrong data, + returning a false positive, passes QE review if the date field is + filled in. A test that found a real defect fails if the screenshot + is the wrong dimensions. + + When the two teams disagree on who owns a quality concern, they hold + a meeting, produce a RACI, and store it in Confluence under a page + titled "Draft - DO NOT USE." The software remains unreviewed during + this process, which both teams agree is not their fault. + +21. COMPLIANCE WITH THIS NOTICE + If you have read this far, congratulations — you are now more + compliant than 97% of enterprise software users. Your actual legal + obligation is simply to keep this NOTICE file intact when + redistributing the source code, as required by Apache 2.0. + + That's it. That's the whole thing. Everything above was the + bureaucratic nightmare that pncli was built to route around. + This line is the exit. + + Now go ship something. After the CAB approves it. + +═══════════════════════════════════════════════════════════════ + ============================================================================= INDEPENDENT PROJECT NOTICE ============================================================================= diff --git a/copilot-instructions.md b/copilot-instructions.md index dfce458..73b0298 100644 --- a/copilot-instructions.md +++ b/copilot-instructions.md @@ -24,20 +24,37 @@ Cache the answers for the session. If the user doesn't know, run `git remote -v` ## Installing Skills -pncli ships with Claude Code skills — step-by-step workflow guides that agents can follow. To install them into your repo: +pncli installs agent skills — step-by-step workflow guides for GitHub Copilot and Claude Code — from git-hosted skills marketplaces: plain git repos of plugins that your team publishes. Register a marketplace once: ``` -pncli skills install +pncli skills marketplace setup ``` -This downloads the latest pncli skills from GitHub. Default installs to `.agents/skills/` (GitHub Copilot). For Claude Code, add `--agent claude-code`. For user-scope, add `--scope user`. Only pncli-managed skills are replaced — any custom skills you've added are left untouched. +This clones the marketplace repo to `~/.agents/marketplaces/`, registers it in your global pncli config, and installs every plugin's skills into your user-level skills folder — `~/.copilot/skills` for GitHub Copilot (the default), or `~/.claude/skills` with `--claude`. Use `--branch` to pin a specific branch and `--token` for repos that require an HTTP access token (GitHub PAT or Bitbucket token). Run `setup` (or its equivalent, `marketplace add`) again with a different URL to register additional marketplaces. -To see what's installed locally: +From then on, one command keeps your skills current: ``` -pncli skills list +pncli skills marketplace sync ``` +Sync works because every installed skill carries provenance: pncli writes a `pncli-origin.json` into each skill directory recording which marketplace and plugin it came from, the source URL and branch, and when it was installed, plus a `.pncli-installed.json` index in the skills folder. On each run, sync does a `git pull` on the marketplace clone, then: + +- **No new commits upstream** — it skips reinstalling and changes nothing locally (pass `--force` to reinstall anyway). +- **New commits** — it re-copies the plugin's skills over the installed versions and refreshes their origin records. + +Only pncli-managed skills are ever replaced or purged; skills you wrote yourself are left untouched. Run `sync` whenever your marketplace publishes new or updated skills — after a teammate merges a skill change, everyone else just runs `pncli skills marketplace sync` (with the same `--claude` flag they used for setup) to pick it up. With one marketplace registered, sync prompts you to pick a plugin; with several, it first asks which marketplace. Skip the prompts: + +``` +pncli skills marketplace sync my-plugin # skip the plugin picker +pncli skills marketplace sync all # every plugin in the marketplace +pncli skills marketplace sync --marketplace all # every plugin from every marketplace +``` + +To list registered marketplaces and browse their plugins without installing anything, use `pncli skills marketplace list` and `pncli skills marketplace plugins `. To see what's installed locally, run `pncli skills list --scope user`. + +The skills bundled with pncli itself can still be installed straight into a repo with `pncli skills install` (default target: `.agents/skills/`; add `--agent claude-code` for Claude Code). + ## Common Workflows > These workflows are also available as skills — run `pncli skills install` (Copilot default) or `pncli skills install --agent claude-code` (Claude Code) to download them into your repo. Add `--scope user` for global access. diff --git a/site/astro.config.mjs b/site/astro.config.mjs index 5f88b9d..48186bf 100644 --- a/site/astro.config.mjs +++ b/site/astro.config.mjs @@ -7,6 +7,11 @@ import mdx from '@astrojs/mdx'; export default defineConfig({ site: 'https://kolatts.github.io', base: '/pncli/', + markdown: { + // Changelog bodies are raw commit messages full of bare CLI flags + // (e.g. --output-file); smartypants would turn the "--" into an em dash. + smartypants: false, + }, vite: { plugins: [tailwindcss()], server: { diff --git a/site/package-lock.json b/site/package-lock.json index 23f191e..b2d35b3 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "dependencies": { "@astrojs/mdx": "^5.0.3", - "@fontsource-variable/bricolage-grotesque": "^5.2.10", + "@fontsource-variable/fraunces": "^5.3.0", "@fontsource-variable/inter": "^5.2.8", "@fontsource-variable/jetbrains-mono": "^5.2.8", "@tailwindcss/typography": "^0.5.19", @@ -628,10 +628,10 @@ "node": ">=18" } }, - "node_modules/@fontsource-variable/bricolage-grotesque": { - "version": "5.2.10", - "resolved": "https://registry.npmjs.org/@fontsource-variable/bricolage-grotesque/-/bricolage-grotesque-5.2.10.tgz", - "integrity": "sha512-5EDsCqgGpKVcJWE4sg9ydli+t5WM97mISYw5lla/Ev4z71FwXh1oN0YUU8xjkRW9+wBCGD9R+ntAvI8G4bUFJg==", + "node_modules/@fontsource-variable/fraunces": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/fraunces/-/fraunces-5.3.0.tgz", + "integrity": "sha512-9BYGySn4AHEJdgp9Z28tQ3X+laJMEOITXkQarZXeloWQZDq5oOvXJ3kDA8c7MGIfpogIaZfjrQBqmda8POOCKA==", "license": "OFL-1.1", "funding": { "url": "https://github.com/sponsors/ayuhito" diff --git a/site/package.json b/site/package.json index 9810677..d00d1fe 100644 --- a/site/package.json +++ b/site/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@astrojs/mdx": "^5.0.3", - "@fontsource-variable/bricolage-grotesque": "^5.2.10", + "@fontsource-variable/fraunces": "^5.3.0", "@fontsource-variable/inter": "^5.2.8", "@fontsource-variable/jetbrains-mono": "^5.2.8", "@tailwindcss/typography": "^0.5.19", diff --git a/site/public/favicon.ico b/site/public/favicon.ico index 7f48a94..5e6c70a 100644 Binary files a/site/public/favicon.ico and b/site/public/favicon.ico differ diff --git a/site/public/favicon.png b/site/public/favicon.png index be1c757..6768c5c 100644 Binary files a/site/public/favicon.png and b/site/public/favicon.png differ diff --git a/site/scripts/parse-changelog.mjs b/site/scripts/parse-changelog.mjs index 12d7224..d4f9101 100644 --- a/site/scripts/parse-changelog.mjs +++ b/site/scripts/parse-changelog.mjs @@ -39,6 +39,12 @@ for (const block of blocks) { summary = firstBullet[1] .replace(/\s*\(\[#\d+\]\([^)]+\)\)/g, '') // PR refs .replace(/\s*\(\[[a-f0-9]+\]\([^)]+\)\)/g, '') // commit hashes + .replace(/,?\s*closes\s+\[#\d+\]\([^)]+\)/gi, '') // "closes [#N](url)" trailers + .replace(/,?\s*closes\s+#\d+/gi, '') // "closes #N" trailers + .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') // links -> link text + .replace(/\*\*([^*]+)\*\*/g, '$1') // bold markers + .replace(/\*([^*]+)\*/g, '$1') // italic markers + .replace(/`([^`]+)`/g, '$1') // inline code backticks .replace(/[\u{1F300}-\u{1FFFF}]/gu, '') // emoji .trim(); summary = summary.charAt(0).toUpperCase() + summary.slice(1); diff --git a/site/scripts/parse-commands.mjs b/site/scripts/parse-commands.mjs index 4b47911..d7372d6 100644 --- a/site/scripts/parse-commands.mjs +++ b/site/scripts/parse-commands.mjs @@ -6,6 +6,15 @@ * Strategy: split each commands.ts file on `.action(` to get per-command blocks, * then extract the last `.command('name')`, `.description('text')`, and all * `.option`/`.requiredOption` calls from each block. + * + * Nested subgroups (e.g. `const entities = dynatrace.command('entities')` with + * leaves registered as `entities.command('list')`) are handled by a first pass + * that maps each subgroup variable to its path segments. A variable whose parent + * is untracked (`program`, or a Command passed in as a function parameter) is the + * file's root group — its own name is assumed to be covered by the SERVICES + * prefix, so its path is empty. Leaf names are then built as + * `pncli `, which lets one file mix flat + * commands and nested subgroups (dynatrace, checkmarx, servicenow, contrast). */ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'; import { join, resolve, dirname } from 'node:path'; @@ -31,38 +40,94 @@ const SERVICES = [ { name: 'Azure DevOps — Repos & PRs', file: 'src/services/ado/commands/repo.ts', prefix: 'ado repo' }, { name: 'Azure DevOps — Pipelines', file: 'src/services/ado/commands/pipeline.ts', prefix: 'ado pipeline' }, { name: 'Azure DevOps — Projects', file: 'src/services/ado/commands/project.ts', prefix: 'ado project' }, - { name: 'Jenkins', file: 'src/services/jenkins/commands.ts', prefix: 'jenkins pipeline' }, + { name: 'Jenkins', file: 'src/services/jenkins/commands.ts', prefix: 'jenkins' }, { name: 'JFrog Artifactory', file: 'src/services/artifactory/commands.ts', prefix: 'artifactory' }, { name: 'IBM UrbanCode Deploy', file: 'src/services/udeploy/commands.ts', prefix: 'udeploy' }, { name: 'Checkmarx', file: 'src/services/checkmarx/commands.ts', prefix: 'checkmarx' }, + { name: 'GitHub', file: 'src/services/github/commands.ts', prefix: 'github' }, + { name: 'ServiceNow', file: 'src/services/servicenow/commands.ts', prefix: 'servicenow' }, + { name: 'Contrast IAST', file: 'src/services/contrast/commands.ts', prefix: 'contrast' }, + { name: 'Dynatrace', file: 'src/services/dynatrace/commands.ts', prefix: 'dynatrace' }, +]; + +// Command groups hidden from the public site. The CLI still ships these commands; +// they just don't render on /commands/. Remove a prefix here to re-add its group. +const SKIP_PREFIXES = new Set(['udeploy']); + +// Site-only text scrubs applied to command/option descriptions so hidden services +// aren't mentioned in other groups' docs. The CLI source text is unchanged. +// Each entry: [pattern, replacement]. Remove an entry to restore the mention. +const DESCRIPTION_SCRUBS = [ + [/ or uDeploy component names/g, ' names'], ]; +// Wrap flag-like tokens (--foo, -x) in backticks so they render as code spans. +// Without this, remark turns "--" in prose into an em dash. Skips text already +// inside inline code spans to avoid double-wrapping. +function scrubDescription(text) { + for (const [pattern, replacement] of DESCRIPTION_SCRUBS) { + text = text.replace(pattern, replacement); + } + return text; +} + +function codeifyFlags(text) { + return text + .split(/(`[^`]*`)/) + .map((part, i) => { + if (i % 2 === 1) return part; // already an inline code span + return part.replace(/(^|[\s(,"'/])(--?[a-zA-Z][\w-]*)/g, '$1`$2`'); + }) + .join(''); +} + +// Matches `.command('name')` with optional receiver variable and optional +// `const =` declaration. Whitespace (incl. newlines) may separate the +// receiver from `.command(`, e.g. `entities\n .command('list')`. +const COMMAND_RE = /(?:const\s+(\w+)\s*=\s*)?(\w+)\s*\.command\s*\(\s*'([^']+)'\s*\)/g; + function extractCommands(filePath, prefix) { const content = readFileSync(filePath, 'utf8'); const commands = []; - // Split on .action( so each segment ends with the registration for one command. + // Pass 1: map subgroup variables to their path segments. + // `const = .command('')` — if is tracked, the + // subgroup's path is the parent's path + name; otherwise is the file's + // root group and its name is already covered by the SERVICES prefix. + const groupPaths = new Map(); + for (const m of content.matchAll(COMMAND_RE)) { + const [, declaredVar, parentVar, name] = m; + if (!declaredVar) continue; + groupPaths.set( + declaredVar, + groupPaths.has(parentVar) ? [...groupPaths.get(parentVar), name] : [] + ); + } + + // Pass 2: split on .action( so each segment ends with the registration for one command. const segments = content.split(/\.action\s*\(/); for (let i = 0; i < segments.length - 1; i++) { const segment = segments[i]; - // Find all .command('name') in this segment; take the last one — that's the leaf subcommand. - const cmdMatches = [...segment.matchAll(/\.command\s*\(\s*'([^']+)'\s*\)/g)]; + // Find all .command('name') in this segment; the last one that is not a + // subgroup declaration is the leaf subcommand. + const cmdMatches = [...segment.matchAll(COMMAND_RE)].filter(m => !m[1]); if (cmdMatches.length === 0) continue; const lastCmd = cmdMatches[cmdMatches.length - 1]; - const cmdName = lastCmd[1]; + const receiverVar = lastCmd[2]; + const cmdName = [...(groupPaths.get(receiverVar) ?? []), lastCmd[3]].join(' '); // Slice from the last .command('name') onwards to find description and options. const afterCmd = segment.slice(lastCmd.index + lastCmd[0].length); const descMatch = afterCmd.match(/\.description\s*\(\s*'([^']+)'\s*\)/); if (!descMatch) continue; - const description = descMatch[1]; + const description = scrubDescription(descMatch[1]); const options = []; for (const m of afterCmd.matchAll(/\.(requiredOption|option)\s*\(\s*'([^']+)'\s*,\s*'([^']+)'/g)) { - options.push({ flag: m[2], description: m[3], required: m[1] === 'requiredOption' }); + options.push({ flag: m[2], description: scrubDescription(m[3]), required: m[1] === 'requiredOption' }); } commands.push({ @@ -97,13 +162,13 @@ function buildMdx(services) { for (const cmd of commands) { lines.push(`### \`${cmd.name}\``); lines.push(''); - lines.push(cmd.description); + lines.push(codeifyFlags(cmd.description)); lines.push(''); if (cmd.options.length > 0) { for (const opt of cmd.options) { const req = opt.required ? ' **required**' : ''; - lines.push(`- \`${opt.flag}\`${req} — ${opt.description}`); + lines.push(`- \`${opt.flag}\`${req} — ${codeifyFlags(opt.description)}`); } lines.push(''); } @@ -113,7 +178,7 @@ function buildMdx(services) { return lines.join('\n'); } -const services = SERVICES.map(({ name, file, prefix }) => ({ +const services = SERVICES.filter(({ prefix }) => !SKIP_PREFIXES.has(prefix)).map(({ name, file, prefix }) => ({ name, commands: extractCommands(join(root, file), prefix), })); diff --git a/site/scripts/parse-instructions.mjs b/site/scripts/parse-instructions.mjs index 37d64a5..4894c24 100644 --- a/site/scripts/parse-instructions.mjs +++ b/site/scripts/parse-instructions.mjs @@ -16,8 +16,22 @@ const END_MARKER = ''; const startIdx = raw.indexOf(START_MARKER); const endIdx = raw.indexOf(END_MARKER); +// Services hidden from the public site. copilot-instructions.md is untouched; +// mentions are stripped from the rendered page only (their command reference is +// already dropped via the COMMAND-REFERENCE markers + parse-commands SKIP list). +// Remove a name here to bring a service back onto the site. +const HIDDEN_SERVICES = ['IBM UrbanCode Deploy']; + +function hideHiddenServices(text) { + for (const svc of HIDDEN_SERVICES) { + // Drop mentions from comma-separated prose lists + text = text.replaceAll(`${svc}, `, '').replaceAll(`, ${svc}`, ''); + } + return text; +} + // Strip the H1 on line 1 so the page's own

is the only one -const rawNoH1 = raw.replace(/^# .+\n/, ''); +const rawNoH1 = hideHiddenServices(raw.replace(/^# .+\n/, '')); // Escape MDX footguns outside fenced code blocks and outside inline code spans. // Walk line-by-line: toggle inFence on ``` lines, then on non-fence lines escape @@ -40,9 +54,15 @@ function escapeMdxOutsideFences(text) { continue; } + // Preserve blockquote markers ("> " prefixes) so they render as real + // blockquotes instead of an escaped literal ">" in the output. + const bqMatch = line.match(/^(\s{0,3}(?:>\s?)+)(.*)$/); + const bqPrefix = bqMatch ? bqMatch[1] : ''; + const rest = bqMatch ? bqMatch[2] : line; + // Outside fences: escape characters inside inline code spans, then outside // Split on inline code spans (backtick-delimited), escape only the non-code parts - const parts = line.split(/(`[^`]+`)/); + const parts = rest.split(/(`[^`]+`)/); const escaped = parts.map((part, i) => { // Odd indices are backtick-wrapped (inline code) — leave them as-is if (i % 2 === 1) return part; @@ -52,7 +72,7 @@ function escapeMdxOutsideFences(text) { .replace(/\{/g, '{') .replace(/\}/g, '}'); }).join(''); - result.push(escaped); + result.push(bqPrefix + escaped); } return result.join('\n'); @@ -98,7 +118,7 @@ if (startIdxNoH1 === -1 || endIdxNoH1 === -1) { bodyBefore = escapedBody + '\n\n## Skills' - + '\n\nEach workflow is packaged as a Claude Code skill. Run `pncli skills install` to download them into your repo (default: `.agents/skills/` for Copilot; add `--agent claude-code` for Claude Code). Skills marked `/invoke` can be called directly by name once installed.' + + '\n\nEach workflow is packaged as a Claude Code skill. Register your team’s marketplace once with `pncli skills marketplace setup `, then keep every installed skill current with `pncli skills marketplace sync` (see Installing Skills above). Skills marked `/invoke` can be called directly by name once installed.' + '\n\nBrowse all skills at [/skills](/pncli/skills/).'; } else { bodyBefore = escapeMdxOutsideFences(beforeRaw) + '\n\n'; diff --git a/site/scripts/parse-skills.mjs b/site/scripts/parse-skills.mjs index d487df5..b4dbbd2 100644 --- a/site/scripts/parse-skills.mjs +++ b/site/scripts/parse-skills.mjs @@ -11,6 +11,25 @@ const sources = [ { dir: join(__dirname, '../../example-skills'), distributable: false }, ]; +// Services hidden from the public site. The distributed skill still documents +// them (skills/pncli/SKILL.md is untouched); they just don't render on the +// skill detail pages. Remove a name here to bring a service back onto the site. +const HIDDEN_SERVICES = ['IBM UrbanCode Deploy']; + +function hideHiddenServices(text) { + let lines = text.split('\n'); + for (const svc of HIDDEN_SERVICES) { + lines = lines + // Drop markdown table rows for the hidden service (e.g. "Available services") + .filter((line) => !(line.trimStart().startsWith('|') && line.includes(svc))) + // Drop mentions from comma-separated prose lists + .map((line) => line.includes(svc) + ? line.replace(`${svc}, `, '').replace(`, ${svc}`, '') + : line); + } + return lines.join('\n'); +} + function parseFrontmatter(content) { const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/); if (!match) return { data: {}, body: content }; @@ -103,7 +122,7 @@ for (const { dir, distributable } of sources) { `generatedAt: ${JSON.stringify(new Date().toISOString())}`, '---', '', - escapeMdxOutsideFences(body.trim()), + escapeMdxOutsideFences(hideHiddenServices(body.trim())), '', ].join('\n'); diff --git a/site/src/assets/hero-dark.png b/site/src/assets/hero-dark.png new file mode 100644 index 0000000..f569d22 Binary files /dev/null and b/site/src/assets/hero-dark.png differ diff --git a/site/src/assets/logo-dark.png b/site/src/assets/logo-dark.png index 8df971b..ee477b6 100644 Binary files a/site/src/assets/logo-dark.png and b/site/src/assets/logo-dark.png differ diff --git a/site/src/assets/maverick-badge.png b/site/src/assets/maverick-badge.png new file mode 100644 index 0000000..15b728e Binary files /dev/null and b/site/src/assets/maverick-badge.png differ diff --git a/site/src/assets/mundane-paperwork.png b/site/src/assets/mundane-paperwork.png new file mode 100644 index 0000000..c12ee48 Binary files /dev/null and b/site/src/assets/mundane-paperwork.png differ diff --git a/site/src/components/CodeTabs.astro b/site/src/components/CodeTabs.astro index e6ea7a4..4c1092f 100644 --- a/site/src/components/CodeTabs.astro +++ b/site/src/components/CodeTabs.astro @@ -2,29 +2,29 @@ const command = 'npm install -g @kolatts/pncli'; --- -
+
-

+

Get started in seconds

-
-
- - - - terminal +
+
+ + + + terminal
-
- {command} +
+ {command}
diff --git a/site/src/components/CommandReferenceCallout.astro b/site/src/components/CommandReferenceCallout.astro index aa59a07..446421c 100644 --- a/site/src/components/CommandReferenceCallout.astro +++ b/site/src/components/CommandReferenceCallout.astro @@ -2,8 +2,8 @@ const href = '/pncli/commands/'; --- -