agentleFS
pnppnp/powershell/.agents/skills/new-cmdlet/SKILL.md

new-cmdlet

Skill906 starsChanged 2 months agoMITView on GitHub

The conventions a PnP PowerShell cmdlet must satisfy - base class selection, permission attributes, PipeBinds, parameter validation, Graph and CSOM call patterns, and the documentation plus changelog artefacts that make a cmdlet complete. Use when writing, modifying or reviewing cmdlet code, or when deciding which base class or permission attribute applies.

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

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

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

    Use GetResultCollection for collections — it follows @odata.nextLink.

    line 106

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 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

Playbook: new-cmdlet

The conventions a cmdlet in this repository must satisfy. A reference — read it before writing or reviewing cmdlet code. For the step-by-step generation procedure see cmdlet-scaffolder; for language-level rules see dotnet-standards.

Pick the base class first

It determines the connection, the resource, the permission flavour and which helpers exist.

Base classUse forGives you
PnPWebCmdletOperations on the connected site/webCurrentWeb, ClientContext
PnPWebRetrievalsCmdlet<T>The above, returning objects with selectable propertiesRetrievalExpressions, -Includes
PnPSharePointCmdletGeneral SharePoint, no web context neededClientContext
PnPSharePointOnlineAdminCmdletTenant admin operationsTenant, AdminContext
PnPGraphCmdletMicrosoft GraphConnection, AccessToken, GraphRequestHelper
PnPAzureManagementApiCmdletAzure Management API
PnPOfficeManagementApiCmdletOffice 365 Management API
PnPGcsCmdlet, PnPTasksCmdletConsumer storage, Planner/Tasks
PnPConnectedCmdletNeeds a connection, none of the above
BasePSCmdletNo connection at all

Choosing wrong is a real defect: a tenant-admin operation on PnPWebCmdlet will use the wrong context and fail confusingly in someone's tenant.

Shape

using System.Management.Automation;
using PnP.PowerShell.Commands.Attributes;
using PnP.PowerShell.Commands.Base;
using PnP.PowerShell.Commands.Base.PipeBinds;

namespace PnP.PowerShell.Commands.FeatureArea
{
    [Cmdlet(VerbsCommon.Get, "PnPSomething")]
    [OutputType(typeof(SomeType))]
    [RequiredApiDelegatedPermissions("sharepoint/AllSites.Read")]
    [RequiredApiApplicationPermissions("sharepoint/Sites.Read.All")]
    public class GetSomething : PnPWebRetrievalsCmdlet<SomeType>
    {
        [Parameter(Mandatory = false, ValueFromPipeline = true, Position = 0)]
        [ValidateNotNull]
        public SomePipeBind Identity { get; set; }

        protected override void ExecuteCmdlet()
        {
            // ...
            WriteObject(result, true);
        }
    }
}
  • Namespace mirrors the folder: src/Commands/Lists/ → PnP.PowerShell.Commands.Lists.
  • Override ExecuteCmdlet(), never ProcessRecord() — the base class owns connection handling and error translation.
  • Renaming an existing cmdlet requires [Alias("Old-PnPName")] for backward compatibility.

Permission attributes

"<resource>/<scope>". Multiple attributes are ORed; multiple scopes inside one attribute are ANDed.

[RequiredApiDelegatedPermissions("sharepoint/AllSites.FullControl", "sharepoint/User.ReadWrite.All")]
[RequiredApiApplicationPermissions("sharepoint/Sites.FullControl.All", "sharepoint/User.ReadWrite.All")]

Also available: RequiredApiDelegatedOrApplicationPermissions, ApiNotAvailableUnderDelegatedPermissions, ApiNotAvailableUnderApplicationPermissions, ApiPermissionsNotRequired, ApiPermissionsDependOnResource.

An unrecognised resource prefix is silently treated as SharePoint. TokenHandler.DefineResourceTypeFromAudience defaults every audience it does not recognise to SharePoint (only blank input becomes Unknown), so "garph/Group.Read.All" declares a SharePoint Group.Read.All rather than failing. A string with no / at all fails the regex and is dropped. Declare least privilege, and verify the scope against Microsoft Learn rather than from memory. See permissions-auditor.

Common patterns

SharePoint retrievals

DefaultRetrievalExpressions = [l => l.Id, l => l.Title, l => l.RootFolder.ServerRelativeUrl];

var list = Identity.GetList(CurrentWeb);
list?.EnsureProperties(RetrievalExpressions);
WriteObject(list);

Graph

var result = GraphRequestHelper.GetResultCollection<SomeType>(this, "v1.0/groups?$select=id,displayName");
WriteObject(result, true);

Use GetResultCollection for collections — it follows @odata.nextLink. Get returns the first page only. Match the signature of a neighbouring cmdlet in the same folder; these helpers have evolved and older call shapes still exist in the tree.

CSOM — always ExecuteQueryRetry(), never ExecuteQuery().

Definition of done

A cmdlet is not finished until all of these exist in the same PR:

  1. Class — correct base class, [Cmdlet], [OutputType], permission attributes, [Alias] if renaming.
  2. Parameters — PipeBinds, validation attributes, deliberate Mandatory/Position/pipeline binding; ParameterSpecified where a default is meaningful.
  3. documentation/<Verb-PnPNoun>.md — full platyPS page: front matter, ## SYNOPSIS — carrying a Required Permissions block only when the cmdlet declares API permissions; a cmdlet marked ApiPermissionsNotRequired must not have one, and none of the ten such pages does — ## SYNTAX (one block per parameter set), ## DESCRIPTION, ## EXAMPLES (at least one, ```powershell fenced, each with a sentence of explanation), ## PARAMETERS alphabetical, YAML blocks only, including the standard -Connection and -Verbose sections, then ## RELATED LINKS. Copy the structure from a sibling page.
  4. CHANGELOG.md — a line under [Current nightly] → Added / Changed / Fixed / Removed, naming the cmdlets in backticks and linking the PR or issue.
  5. Build clean — dotnet build src/PnP.PowerShell.sln, no new warnings.

Removing a cmdlet means deleting its documentation page too.

"In the same PR" means the same working tree. Never commit, push, or open the PR yourself — see Human in the loop.

What an agent cannot do here

You cannot run the cmdlet. Anything calling an API you have not invoked is a draft: say which API shapes, response fields and permission scopes you inferred rather than verified, so a maintainer knows exactly what to check against a tenant.

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.

Reviews can't be read right now.

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.