@aliaksei-raketski/pi-angular-developer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +44 -0
  2. package/overlays/angular-developer/references/docs-helpers.md +36 -0
  3. package/overlays/angular-developer/scripts/get-best-practices.mjs +268 -0
  4. package/overlays/angular-developer/scripts/search-documentation.mjs +396 -0
  5. package/package.json +41 -0
  6. package/scripts/sync-angular-skill.mjs +307 -0
  7. package/skills/angular-developer/SKILL.md +133 -0
  8. package/skills/angular-developer/UPSTREAM.md +9 -0
  9. package/skills/angular-developer/data/best-practices.md +56 -0
  10. package/skills/angular-developer/references/angular-animations.md +160 -0
  11. package/skills/angular-developer/references/angular-aria.md +597 -0
  12. package/skills/angular-developer/references/cli.md +86 -0
  13. package/skills/angular-developer/references/component-harnesses.md +57 -0
  14. package/skills/angular-developer/references/component-styling.md +91 -0
  15. package/skills/angular-developer/references/components.md +117 -0
  16. package/skills/angular-developer/references/creating-services.md +97 -0
  17. package/skills/angular-developer/references/data-resolvers.md +69 -0
  18. package/skills/angular-developer/references/define-routes.md +67 -0
  19. package/skills/angular-developer/references/defining-providers.md +72 -0
  20. package/skills/angular-developer/references/di-fundamentals.md +118 -0
  21. package/skills/angular-developer/references/docs-helpers.md +36 -0
  22. package/skills/angular-developer/references/e2e-testing.md +66 -0
  23. package/skills/angular-developer/references/effects.md +83 -0
  24. package/skills/angular-developer/references/environment-configuration.md +132 -0
  25. package/skills/angular-developer/references/hierarchical-injectors.md +43 -0
  26. package/skills/angular-developer/references/host-elements.md +80 -0
  27. package/skills/angular-developer/references/injection-context.md +63 -0
  28. package/skills/angular-developer/references/inputs.md +101 -0
  29. package/skills/angular-developer/references/linked-signal.md +59 -0
  30. package/skills/angular-developer/references/loading-strategies.md +61 -0
  31. package/skills/angular-developer/references/migrations.md +30 -0
  32. package/skills/angular-developer/references/navigate-to-routes.md +69 -0
  33. package/skills/angular-developer/references/outputs.md +86 -0
  34. package/skills/angular-developer/references/reactive-forms.md +118 -0
  35. package/skills/angular-developer/references/rendering-strategies.md +44 -0
  36. package/skills/angular-developer/references/resource.md +74 -0
  37. package/skills/angular-developer/references/route-animations.md +56 -0
  38. package/skills/angular-developer/references/route-guards.md +52 -0
  39. package/skills/angular-developer/references/router-lifecycle.md +45 -0
  40. package/skills/angular-developer/references/router-testing.md +87 -0
  41. package/skills/angular-developer/references/show-routes-with-outlets.md +68 -0
  42. package/skills/angular-developer/references/signal-forms.md +907 -0
  43. package/skills/angular-developer/references/signals-overview.md +94 -0
  44. package/skills/angular-developer/references/tailwind-css.md +69 -0
  45. package/skills/angular-developer/references/template-driven-forms.md +114 -0
  46. package/skills/angular-developer/references/testing-fundamentals.md +63 -0
  47. package/skills/angular-developer/scripts/get-best-practices.mjs +268 -0
  48. package/skills/angular-developer/scripts/search-documentation.mjs +396 -0
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Angular Developer Pi Package
2
+
3
+ A Pi package that vendors the official Angular `angular-developer` Agent Skill and replaces Angular MCP guidance with local helper scripts.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pi install npm:@aliaksei-raketski/pi-angular-developer
9
+ # or project-local
10
+ pi install -l npm:@aliaksei-raketski/pi-angular-developer
11
+ ```
12
+
13
+ ## Installed skill
14
+
15
+ - `skills/angular-developer`
16
+
17
+ ## Key helpers
18
+
19
+ - `skills/angular-developer/scripts/get-best-practices.mjs`
20
+ - `skills/angular-developer/scripts/search-documentation.mjs`
21
+
22
+ From the skill directory:
23
+
24
+ ```bash
25
+ node scripts/get-best-practices.mjs --help
26
+ node scripts/search-documentation.mjs "signals" --version 22 --limit 3 --json
27
+ ```
28
+
29
+ ## Sync
30
+
31
+ Run from package root:
32
+
33
+ ```bash
34
+ node scripts/sync-angular-skill.mjs
35
+ ```
36
+
37
+ Use `ANGULAR_SKILLS_REF` to override the synced ref.
38
+
39
+ After sync, consider validating the helper scripts:
40
+
41
+ ```bash
42
+ node skills/angular-developer/scripts/search-documentation.mjs "signals" --version 22 --limit 2 --json
43
+ node skills/angular-developer/scripts/get-best-practices.mjs --help
44
+ ```
@@ -0,0 +1,36 @@
1
+ # Angular Documentation Helpers
2
+
3
+ Use the local helper scripts bundled with this skill for:
4
+
5
+ - version-aware Angular best practices lookup
6
+ - official angular.dev documentation search
7
+
8
+ ## Best practices
9
+
10
+ Before writing or modifying Angular code in an existing workspace, run one of the following commands:
11
+
12
+ ```bash
13
+ # From skills/angular-developer/
14
+ node scripts/get-best-practices.mjs /absolute/path/to/workspace
15
+
16
+ # From any directory
17
+ node /absolute/path/to/skills/angular-developer/scripts/get-best-practices.mjs /absolute/path/to/workspace
18
+ ```
19
+
20
+ If you do not pass a workspace path, the script searches upward from the current directory for `angular.json`.
21
+
22
+ ## Angular documentation search
23
+
24
+ Use the local search helper:
25
+
26
+ ```bash
27
+ node scripts/search-documentation.mjs "signals resource" --version 22 --limit 5
28
+ node scripts/search-documentation.mjs "signal forms validation" --version 22 --include-top-content
29
+ ```
30
+
31
+ ## Usage notes
32
+
33
+ - Run the scripts from `skills/angular-developer/`, or by using absolute script paths.
34
+ - If the project Angular version is known, pass the major version to `search-documentation.mjs` with `--version`.
35
+ - If deeper or current docs are needed, add `--include-top-content` to retrieve concise top-page context.
36
+ - Prefer vendored references under `references/` first, then use `search-documentation.mjs` for fresher/cross-topic lookup.
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import {createRequire} from 'node:module';
7
+ import {pathToFileURL, fileURLToPath} from 'node:url';
8
+
9
+ const MAX_BEST_PRACTICES_BYTES = 1_048_576;
10
+ const BUILTIN_FALLBACK = `Bundled Angular best-practices guidance is unavailable in this workspace.\n\nTo get version-aware guidance, run this script from an Angular workspace with @angular/core installed and accessible via Node resolution.`;
11
+
12
+ main().catch((error) => {
13
+ console.error(`Fatal: ${error instanceof Error ? error.message : String(error)}`);
14
+ process.exit(1);
15
+ });
16
+
17
+ async function main() {
18
+ const parsed = parseArguments(process.argv.slice(2));
19
+
20
+ if (parsed.help) {
21
+ printHelp();
22
+ process.exit(0);
23
+ }
24
+
25
+ if (parsed.errors.length > 0) {
26
+ console.error(parsed.errors.join('\n'));
27
+ console.error('Use --help for usage.');
28
+ process.exit(2);
29
+ }
30
+
31
+ const workspacePath = parsed.workspacePath || null;
32
+ const source = await resolveSource(workspacePath);
33
+
34
+ const payload = {
35
+ source: source.source,
36
+ content: source.content,
37
+ };
38
+
39
+ if (parsed.json) {
40
+ const json = parsed.pretty
41
+ ? JSON.stringify(payload, null, 2)
42
+ : JSON.stringify(payload);
43
+ process.stdout.write(json);
44
+ return;
45
+ }
46
+
47
+ console.log(`<!-- Source: ${payload.source} -->`);
48
+ console.log('');
49
+ console.log((payload.content || '').trimEnd());
50
+ }
51
+
52
+ function parseArguments(argv) {
53
+ const result = {
54
+ workspacePath: null,
55
+ json: false,
56
+ pretty: false,
57
+ help: false,
58
+ errors: [],
59
+ };
60
+
61
+ const positional = [];
62
+
63
+ for (let i = 0; i < argv.length; i++) {
64
+ const arg = argv[i];
65
+
66
+ if (!arg.startsWith('--')) {
67
+ positional.push(arg);
68
+ continue;
69
+ }
70
+
71
+ if (arg === '--json') {
72
+ result.json = true;
73
+ continue;
74
+ }
75
+
76
+ if (arg === '--pretty') {
77
+ result.pretty = true;
78
+ continue;
79
+ }
80
+
81
+ if (arg === '--help') {
82
+ result.help = true;
83
+ continue;
84
+ }
85
+
86
+ if (arg.startsWith('--')) {
87
+ result.errors.push(`Unknown option: ${arg}`);
88
+ continue;
89
+ }
90
+ }
91
+
92
+ if (positional.length > 0) {
93
+ result.workspacePath = positional[0];
94
+ }
95
+
96
+ return result;
97
+ }
98
+
99
+ async function resolveSource(workspacePath) {
100
+ if (workspacePath) {
101
+ try {
102
+ const workspaceRoot = await resolveWorkspaceFromInput(path.resolve(workspacePath));
103
+
104
+ if (!workspaceRoot) {
105
+ console.error(`Warning: could not locate an Angular workspace at "${workspacePath}". Falling back to bundled best-practices.md.`);
106
+ } else {
107
+ try {
108
+ const source = await loadWorkspaceBestPractices(workspaceRoot);
109
+ return source;
110
+ } catch (error) {
111
+ console.error(
112
+ `Warning: failed to read workspace-specific best practices from ${workspaceRoot}: ${error instanceof Error ? error.message : String(error)}. Falling back to bundled best-practices.md.`,
113
+ );
114
+ }
115
+ }
116
+ } catch (error) {
117
+ console.error(
118
+ `Warning: failed to locate workspace from ${workspacePath}: ${error instanceof Error ? error.message : String(error)}. Falling back to bundled best-practices.md.`,
119
+ );
120
+ }
121
+ } else {
122
+ const workspaceRoot = await findWorkspaceFromCwd(process.cwd());
123
+
124
+ if (workspaceRoot) {
125
+ try {
126
+ const source = await loadWorkspaceBestPractices(workspaceRoot);
127
+ return source;
128
+ } catch (error) {
129
+ console.error(
130
+ `Warning: failed to read workspace-specific best practices from ${workspaceRoot}: ${error instanceof Error ? error.message : String(error)}. Falling back to bundled best-practices.md.`,
131
+ );
132
+ }
133
+ }
134
+ }
135
+
136
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
137
+ const fallbackPath = path.join(path.dirname(scriptDir), 'data', 'best-practices.md');
138
+ try {
139
+ const content = await fs.readFile(fallbackPath, 'utf8');
140
+ return {source: 'bundled best-practices fallback', content};
141
+ } catch (error) {
142
+ console.error(`Warning: bundled fallback not found at ${fallbackPath}: ${error instanceof Error ? error.message : String(error)}.`);
143
+ return {source: 'built-in fallback', content: BUILTIN_FALLBACK};
144
+ }
145
+ }
146
+
147
+ async function loadWorkspaceBestPractices(workspaceRoot) {
148
+ const packageJsonPath = path.join(workspaceRoot, 'package.json');
149
+ const probePath = path.join(workspaceRoot, '.pi-scripts-probe.mjs');
150
+ const requireBase = (await fileExists(packageJsonPath)) ? packageJsonPath : probePath;
151
+ const require = createRequire(pathToFileURL(requireBase).href);
152
+
153
+ const corePackageJsonPath = require.resolve('@angular/core/package.json');
154
+ const corePackageJson = JSON.parse(await fs.readFile(corePackageJsonPath, 'utf8'));
155
+
156
+ const bestPracticesMeta = corePackageJson.angular?.bestPractices;
157
+
158
+ if (!bestPracticesMeta || bestPracticesMeta.format !== 'markdown' || typeof bestPracticesMeta.path !== 'string') {
159
+ throw new Error('Unsupported Angular best-practices metadata format.');
160
+ }
161
+
162
+ const packageDirectory = path.dirname(corePackageJsonPath);
163
+ const bestPracticesPath = path.resolve(packageDirectory, bestPracticesMeta.path);
164
+
165
+ const rel = path.relative(packageDirectory, bestPracticesPath);
166
+ if (
167
+ rel.startsWith('..') ||
168
+ rel.startsWith(path.sep) ||
169
+ path.isAbsolute(bestPracticesMeta.path)
170
+ ) {
171
+ throw new Error('Best-practices path does not stay within @angular/core package directory.');
172
+ }
173
+
174
+ const stats = await fs.stat(bestPracticesPath);
175
+ if (!stats.isFile()) {
176
+ throw new Error(`Best-practices path is not a file: ${bestPracticesPath}`);
177
+ }
178
+
179
+ if (stats.size > MAX_BEST_PRACTICES_BYTES) {
180
+ throw new Error(`Best-practices file is too large (${stats.size} bytes).`);
181
+ }
182
+
183
+ const content = await fs.readFile(bestPracticesPath, 'utf8');
184
+ if (!content.trim()) {
185
+ throw new Error(`Best-practices file is empty: ${bestPracticesPath}`);
186
+ }
187
+
188
+ return {
189
+ source: `framework version ${corePackageJson.version}`,
190
+ content,
191
+ };
192
+ }
193
+
194
+ async function resolveWorkspaceFromInput(inputPath) {
195
+ const stats = await fs.stat(inputPath);
196
+
197
+ if (stats.isFile()) {
198
+ if (path.basename(inputPath) === 'angular.json') {
199
+ return path.dirname(inputPath);
200
+ }
201
+
202
+ throw new Error('Provided file path is not angular.json.');
203
+ }
204
+
205
+ if (stats.isDirectory()) {
206
+ const candidate = path.join(inputPath, 'angular.json');
207
+ if (await isFile(candidate)) {
208
+ return inputPath;
209
+ }
210
+ throw new Error('No angular.json found at the provided directory.');
211
+ }
212
+
213
+ throw new Error('Provided workspace path is neither a file nor directory.');
214
+ }
215
+
216
+ async function findWorkspaceFromCwd(startDir) {
217
+ let current = path.resolve(startDir);
218
+
219
+ for (;;) {
220
+ const candidate = path.join(current, 'angular.json');
221
+ if (await isFile(candidate)) {
222
+ return current;
223
+ }
224
+
225
+ const parent = path.dirname(current);
226
+ if (parent === current) {
227
+ return null;
228
+ }
229
+
230
+ current = parent;
231
+ }
232
+ }
233
+
234
+ async function isFile(candidate) {
235
+ try {
236
+ return (await fs.stat(candidate)).isFile();
237
+ } catch {
238
+ return false;
239
+ }
240
+ }
241
+
242
+ async function fileExists(candidate) {
243
+ try {
244
+ await fs.access(candidate);
245
+ return true;
246
+ } catch {
247
+ return false;
248
+ }
249
+ }
250
+
251
+ function printHelp() {
252
+ console.log(`
253
+ Usage:
254
+ node scripts/get-best-practices.mjs [workspacePath] [--json] [--pretty] [--help]
255
+
256
+ Arguments:
257
+ workspacePath Optional Angular workspace directory or angular.json file path.
258
+
259
+ Options:
260
+ --json Output JSON only.
261
+ --pretty Pretty-print JSON output.
262
+ --help Show this help.
263
+
264
+ Output:
265
+ Default: markdown with a source comment on top.
266
+ JSON: { source, content }
267
+ `);
268
+ }