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
- Very specific: line 106
Use GetResultCollection for collections — it follows @odata.nextLink.
≈ “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.
Files
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 class | Use for | Gives you |
PnPWebCmdlet | Operations on the connected site/web | CurrentWeb, ClientContext |
PnPWebRetrievalsCmdlet<T> | The above, returning objects with selectable properties | RetrievalExpressions, -Includes |
PnPSharePointCmdlet | General SharePoint, no web context needed | ClientContext |
PnPSharePointOnlineAdminCmdlet | Tenant admin operations | Tenant, AdminContext |
PnPGraphCmdlet | Microsoft Graph | Connection, AccessToken, GraphRequestHelper |
PnPAzureManagementApiCmdlet | Azure Management API | |
PnPOfficeManagementApiCmdlet | Office 365 Management API | |
PnPGcsCmdlet, PnPTasksCmdlet | Consumer storage, Planner/Tasks | |
PnPConnectedCmdlet | Needs a connection, none of the above | |
BasePSCmdlet | No 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(), neverProcessRecord()— 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:
- Class — correct base class,
[Cmdlet],[OutputType], permission attributes,[Alias]if renaming. - Parameters — PipeBinds, validation attributes, deliberate
Mandatory/Position/pipeline binding;ParameterSpecifiedwhere a default is meaningful. documentation/<Verb-PnPNoun>.md— full platyPS page: front matter,## SYNOPSIS— carrying a Required Permissions block only when the cmdlet declares API permissions; a cmdlet markedApiPermissionsNotRequiredmust not have one, and none of the ten such pages does —## SYNTAX(one block per parameter set),## DESCRIPTION,## EXAMPLES(at least one,```powershellfenced, each with a sentence of explanation),## PARAMETERSalphabetical, YAML blocks only, including the standard-Connectionand-Verbosesections, then## RELATED LINKS. Copy the structure from a sibling page.CHANGELOG.md— a line under[Current nightly]→Added/Changed/Fixed/Removed, naming the cmdlets in backticks and linking the PR or issue.- 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.
In this repository13 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- pages/llms.txtllms.txt
- api-surface-diffSkill
- cmdlet-scaffolderSkill
- code-reviewSkill
- docs-syncSkill
- dotnet-standardsSkill
- issue-triageSkill
- permissions-auditorSkill
- code-reviewSkill
- new-cmdletSkill
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.

