@eddacraft/anvil-aps 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 (121) hide show
  1. package/AGENTS.md +155 -0
  2. package/LICENSE +14 -0
  3. package/README.md +57 -0
  4. package/TODO.md +40 -0
  5. package/dist/filter/context-bundle.d.ts +81 -0
  6. package/dist/filter/context-bundle.d.ts.map +1 -0
  7. package/dist/filter/context-bundle.js +230 -0
  8. package/dist/filter/index.d.ts +85 -0
  9. package/dist/filter/index.d.ts.map +1 -0
  10. package/dist/filter/index.js +169 -0
  11. package/dist/index.d.ts +16 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +15 -0
  14. package/dist/loader/index.d.ts +80 -0
  15. package/dist/loader/index.d.ts.map +1 -0
  16. package/dist/loader/index.js +253 -0
  17. package/dist/parser/index.d.ts +24 -0
  18. package/dist/parser/index.d.ts.map +1 -0
  19. package/dist/parser/index.js +22 -0
  20. package/dist/parser/parse-document.d.ts +17 -0
  21. package/dist/parser/parse-document.d.ts.map +1 -0
  22. package/dist/parser/parse-document.js +219 -0
  23. package/dist/parser/parse-index.d.ts +31 -0
  24. package/dist/parser/parse-index.d.ts.map +1 -0
  25. package/dist/parser/parse-index.js +251 -0
  26. package/dist/parser/parse-task.d.ts +30 -0
  27. package/dist/parser/parse-task.d.ts.map +1 -0
  28. package/dist/parser/parse-task.js +261 -0
  29. package/dist/state/index.d.ts +307 -0
  30. package/dist/state/index.d.ts.map +1 -0
  31. package/dist/state/index.js +689 -0
  32. package/dist/templates/generator.d.ts +71 -0
  33. package/dist/templates/generator.d.ts.map +1 -0
  34. package/dist/templates/generator.js +723 -0
  35. package/dist/templates/index.d.ts +5 -0
  36. package/dist/templates/index.d.ts.map +1 -0
  37. package/dist/templates/index.js +4 -0
  38. package/dist/types/index.d.ts +131 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/dist/types/index.js +107 -0
  41. package/dist/validator/index.d.ts +83 -0
  42. package/dist/validator/index.d.ts.map +1 -0
  43. package/dist/validator/index.js +611 -0
  44. package/docs/APS-Anvil-Integration.md +750 -0
  45. package/docs/APS-Conventions.md +635 -0
  46. package/docs/APS-NonGoals.md +455 -0
  47. package/docs/APS-Planning-Spec-v0.1.md +362 -0
  48. package/examples/README.md +170 -0
  49. package/examples/feature-auth.aps.md +87 -0
  50. package/examples/refactor-error-handling.aps.md +119 -0
  51. package/examples/system-ecommerce/APS.md +57 -0
  52. package/examples/system-ecommerce/modules/auth.aps.md +38 -0
  53. package/examples/system-ecommerce/modules/cart.aps.md +53 -0
  54. package/examples/system-ecommerce/modules/payments.aps.md +68 -0
  55. package/examples/system-ecommerce/modules/products.aps.md +53 -0
  56. package/package.json +34 -0
  57. package/project.json +37 -0
  58. package/scripts/generate-templates.js +33 -0
  59. package/src/filter/context-bundle.ts +312 -0
  60. package/src/filter/filter.test.ts +317 -0
  61. package/src/filter/index.ts +249 -0
  62. package/src/index.ts +16 -0
  63. package/src/loader/index.ts +364 -0
  64. package/src/loader/loader.test.ts +224 -0
  65. package/src/parser/__fixtures__/invalid-task-id-not-padded.aps.md +7 -0
  66. package/src/parser/__fixtures__/invalid-task-id.aps.md +8 -0
  67. package/src/parser/__fixtures__/minimal-task.aps.md +7 -0
  68. package/src/parser/__fixtures__/non-scope-hyphenated.aps.md +10 -0
  69. package/src/parser/__fixtures__/simple-index.aps.md +35 -0
  70. package/src/parser/__fixtures__/simple-plan.aps.md +19 -0
  71. package/src/parser/index.ts +30 -0
  72. package/src/parser/parse-document.test.ts +603 -0
  73. package/src/parser/parse-document.ts +262 -0
  74. package/src/parser/parse-index.test.ts +316 -0
  75. package/src/parser/parse-index.ts +298 -0
  76. package/src/parser/parse-task.test.ts +476 -0
  77. package/src/parser/parse-task.ts +325 -0
  78. package/src/state/__fixtures__/invalid-plan.aps.md +9 -0
  79. package/src/state/__fixtures__/test-plan.aps.md +20 -0
  80. package/src/state/index.ts +879 -0
  81. package/src/state/state.test.ts +645 -0
  82. package/src/templates/generator.test.ts +378 -0
  83. package/src/templates/generator.ts +776 -0
  84. package/src/templates/index.ts +5 -0
  85. package/src/types/index.ts +168 -0
  86. package/src/validator/__fixtures__/broken-links.aps.md +10 -0
  87. package/src/validator/__fixtures__/circular-deps-index.aps.md +26 -0
  88. package/src/validator/__fixtures__/circular-modules/module-a.aps.md +9 -0
  89. package/src/validator/__fixtures__/circular-modules/module-b.aps.md +9 -0
  90. package/src/validator/__fixtures__/circular-modules/module-c.aps.md +9 -0
  91. package/src/validator/__fixtures__/dup-modules/module-a.aps.md +9 -0
  92. package/src/validator/__fixtures__/dup-modules/module-b.aps.md +9 -0
  93. package/src/validator/__fixtures__/duplicate-ids-index.aps.md +15 -0
  94. package/src/validator/__fixtures__/invalid-task-id.aps.md +17 -0
  95. package/src/validator/__fixtures__/missing-confidence.aps.md +9 -0
  96. package/src/validator/__fixtures__/missing-h1.aps.md +5 -0
  97. package/src/validator/__fixtures__/missing-intent.aps.md +9 -0
  98. package/src/validator/__fixtures__/missing-modules-section.aps.md +7 -0
  99. package/src/validator/__fixtures__/missing-tasks-section.aps.md +7 -0
  100. package/src/validator/__fixtures__/modules/auth.aps.md +17 -0
  101. package/src/validator/__fixtures__/modules/payments.aps.md +13 -0
  102. package/src/validator/__fixtures__/scope-mismatch.aps.md +14 -0
  103. package/src/validator/__fixtures__/valid-index.aps.md +24 -0
  104. package/src/validator/__fixtures__/valid-leaf.aps.md +22 -0
  105. package/src/validator/index.ts +776 -0
  106. package/src/validator/validator.test.ts +269 -0
  107. package/templates/index-full.md +94 -0
  108. package/templates/index-minimal.md +16 -0
  109. package/templates/index-template.md +63 -0
  110. package/templates/leaf-full.md +76 -0
  111. package/templates/leaf-minimal.md +14 -0
  112. package/templates/leaf-template.md +55 -0
  113. package/templates/simple-full.md +56 -0
  114. package/templates/simple-minimal.md +14 -0
  115. package/templates/simple-template.md +30 -0
  116. package/tsconfig.json +19 -0
  117. package/tsconfig.lib.json +14 -0
  118. package/tsconfig.lib.tsbuildinfo +1 -0
  119. package/tsconfig.spec.json +9 -0
  120. package/tsconfig.tsbuildinfo +1 -0
  121. package/vitest.config.ts +15 -0
package/AGENTS.md ADDED
@@ -0,0 +1,155 @@
1
+ # APS Package (@eddacraft/anvil-aps)
2
+
3
+ > APS document parsing, validation, state management, and template generation
4
+
5
+ **Parent**: See root `AGENTS.md` for project-wide conventions.
6
+
7
+ ## Structure
8
+
9
+ ```
10
+ packages/aps/src/
11
+ ├── parser/ # Markdown AST parsing (remark/unified)
12
+ │ └── index.ts # Extract tasks, modules, metadata from .aps.md
13
+ ├── loader/ # Document loading and graph resolution
14
+ │ └── index.ts # Load plans, resolve dependencies
15
+ ├── validator/ # Validation rules (745 lines)
16
+ │ └── index.ts # 8 validation rules for APS documents
17
+ ├── filter/ # Task/module filtering
18
+ │ └── index.ts # Context bundle generation
19
+ ├── state/ # Task state management (844 lines)
20
+ │ └── index.ts # .anvil/state.json, locking
21
+ ├── templates/ # Template generation (607 lines)
22
+ │ └── generator.ts # Create .aps.md from prompts
23
+ ├── types/ # Zod schemas and TypeScript types
24
+ │ └── index.ts # APSDocument, Task, Module schemas
25
+ └── index.ts # Barrel exports with subpath access
26
+ ```
27
+
28
+ ## Where to Look
29
+
30
+ | Task | Location | Notes |
31
+ | ------------------- | ------------------------ | ---------------------------- |
32
+ | Add validation rule | `validator/index.ts` | Follow existing rule pattern |
33
+ | Modify parsing | `parser/index.ts` | Uses remark AST visitors |
34
+ | Add state operation | `state/index.ts` | Modify state.json schema |
35
+ | Add template | `templates/generator.ts` | Template string functions |
36
+ | Add filter | `filter/index.ts` | Task selection logic |
37
+
38
+ ## Validation Rules
39
+
40
+ 8 built-in rules in `validator/index.ts`:
41
+
42
+ | Rule | Purpose |
43
+ | ----------------------- | ------------------------------------ |
44
+ | `required-sections` | Ensure mandatory sections exist |
45
+ | `task-format` | Validate task ID format (TASK-001) |
46
+ | `task-intent` | Tasks must have clear intent |
47
+ | `broken-links` | Detect references to missing modules |
48
+ | `duplicate-ids` | No duplicate task/module IDs |
49
+ | `circular-dependencies` | Detect circular module dependencies |
50
+ | `scope-mismatch` | Task scope matches module boundary |
51
+ | `orphan-modules` | Modules must be referenced |
52
+
53
+ ## Adding a Validation Rule
54
+
55
+ ```typescript
56
+ // In validator/index.ts
57
+ function validateMyRule(doc: APSDocument): ValidationIssue[] {
58
+ const issues: ValidationIssue[] = [];
59
+
60
+ // Check something...
61
+ if (condition) {
62
+ issues.push({
63
+ type: 'error', // or 'warning'
64
+ rule: 'my-rule',
65
+ message: 'What went wrong',
66
+ path: doc.path,
67
+ line: lineNumber, // Optional
68
+ });
69
+ }
70
+
71
+ return issues;
72
+ }
73
+
74
+ // Add to validateDocument():
75
+ issues.push(...validateMyRule(doc));
76
+ ```
77
+
78
+ ## State Management
79
+
80
+ Task execution state stored in `.anvil/state.json`:
81
+
82
+ ```typescript
83
+ import {
84
+ loadState,
85
+ saveState,
86
+ lockTask,
87
+ unlockTask,
88
+ } from '@eddacraft/anvil-aps/state';
89
+
90
+ // Load current state
91
+ const state = await loadState(projectRoot);
92
+
93
+ // Lock a task for execution
94
+ const lock = await lockTask(state, 'TASK-001', {
95
+ executor: 'user@example.com',
96
+ reason: 'Implementing feature',
97
+ });
98
+
99
+ // Update task status
100
+ await updateTaskStatus(state, 'TASK-001', 'in_progress');
101
+
102
+ // Unlock when done
103
+ await unlockTask(state, 'TASK-001');
104
+ ```
105
+
106
+ ## Parser Usage
107
+
108
+ ```typescript
109
+ import { parseAPSDocument } from '@eddacraft/anvil-aps/parser';
110
+
111
+ const doc = await parseAPSDocument(content, { path: 'plans/index.aps.md' });
112
+
113
+ // Access parsed structure
114
+ doc.metadata; // Title, version, status
115
+ doc.modules; // Array of Module objects
116
+ doc.tasks; // Array of Task objects
117
+ doc.dependencies; // Module dependency graph
118
+ ```
119
+
120
+ ## Template Generation
121
+
122
+ ```typescript
123
+ import { generateTemplate } from '@eddacraft/anvil-aps/templates';
124
+
125
+ const content = await generateTemplate('module', {
126
+ name: 'my-feature',
127
+ description: 'Implements something cool',
128
+ tasks: ['FEAT-001', 'FEAT-002'],
129
+ });
130
+
131
+ // Write to file
132
+ await writeFile('plans/modules/my-feature.aps.md', content);
133
+ ```
134
+
135
+ ## Scripts
136
+
137
+ ```bash
138
+ nx test aps # All APS tests
139
+ nx test aps --testNamePattern="validator" # Validator tests only
140
+ pnpm -F aps run generate:templates # Regenerate template examples
141
+ ```
142
+
143
+ ## Anti-Patterns (This Package)
144
+
145
+ - Never modify state.json directly - use state management functions
146
+ - Never skip validation before loading documents
147
+ - Always handle circular dependencies gracefully
148
+ - Always preserve AST positions for error reporting
149
+
150
+ ## Testing
151
+
152
+ - Fixture-based with realistic APS documents
153
+ - Test files in `validator/__fixtures__/`, `parser/__fixtures__/`
154
+ - Comprehensive edge cases for validation rules
155
+ - State management tests with temp directories
package/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright (c) 2026 EddaCraft. All rights reserved.
2
+
3
+ PROPRIETARY AND CONFIDENTIAL
4
+
5
+ This software and associated documentation files (the "Software") are the
6
+ exclusive property of EddaCraft. Unauthorised copying, modification,
7
+ distribution, or use of this Software, via any medium, is strictly prohibited
8
+ without the express written permission of EddaCraft.
9
+
10
+ The Software is provided for evaluation and testing purposes only to authorised
11
+ beta testers. No licence is granted to use, copy, modify, or distribute the
12
+ Software for any other purpose.
13
+
14
+ For licensing enquiries, contact: legal@eddacraft.com
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @eddacraft/anvil-aps
2
+
3
+ Anvil Planning Spec (APS) library for parsing, loading, validating, and managing
4
+ planning documents.
5
+
6
+ ## Overview
7
+
8
+ The `@eddacraft/anvil-aps` package provides a complete toolkit for working with
9
+ Anvil Planning Spec documents:
10
+
11
+ - **Parser**: Parse Markdown-based planning documents using remark AST
12
+ - **Loader**: Load and resolve planning document graphs with dependency tracking
13
+ - **Validator**: Validate planning documents against APS rules
14
+ - **State**: Manage task state and locking for concurrent execution
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pnpm add @eddacraft/anvil-aps
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ Coming soon - see `docs/` directory for detailed documentation.
25
+
26
+ ## Documentation
27
+
28
+ - [APS Planning Spec](./docs/APS-Planning-Spec-v0.1.md) - Full specification
29
+ - [APS Conventions](./docs/APS-Conventions.md) - Markdown conventions and
30
+ patterns
31
+ - [Non-Goals](./docs/APS-NonGoals.md) - What APS explicitly does not do
32
+ - [Anvil Integration](./docs/APS-Anvil-Integration.md) - How APS integrates with
33
+ Anvil CLI
34
+
35
+ ## Development
36
+
37
+ ```bash
38
+ # Build the package
39
+ nx build aps
40
+
41
+ # Run tests
42
+ nx test aps
43
+
44
+ # Run tests in watch mode
45
+ nx test aps --watch
46
+
47
+ # Type check
48
+ pnpm typecheck
49
+
50
+ # Lint
51
+ nx lint aps
52
+ ```
53
+
54
+ ## Licence
55
+
56
+ Copyright (c) 2026 EddaCraft. All rights reserved. See [LICENSE](../../LICENSE)
57
+ for details.
package/TODO.md ADDED
@@ -0,0 +1,40 @@
1
+ # APS Package Status
2
+
3
+ > **Full Plan**: `plans/index.aps.md` (module: aps-markdown-adapter)
4
+ >
5
+ > This package is **complete**. See the plan document for adapter work.
6
+
7
+ ---
8
+
9
+ ## Package Status: Complete ✅
10
+
11
+ The `@eddacraft/anvil-aps` library provides:
12
+
13
+ - **Parser** — `parseDocument()`, `parseIndex()`, `parseTask()`
14
+ - **Loader** — `loadPlan()` with multi-module support
15
+ - **Filter** — `filterPlan()` with scope/tag/owner/task filtering
16
+ - **Validator** — `validatePlanningDoc()` with issue reporting
17
+ - **State** — Task locking/unlocking via `.anvil/state.json`
18
+ - **Templates** — `generateIndexTemplate()`, `generateLeafTemplate()`
19
+
20
+ ### Test Coverage
21
+
22
+ | Module | Coverage | Tests |
23
+ | --------- | --------- | ------- |
24
+ | parser | 89.5% | 31 |
25
+ | loader | 96% | 18 |
26
+ | validator | 97.9% | 27 |
27
+ | state | 94.2% | 35 |
28
+ | filter | 89.6% | 30 |
29
+ | templates | 100% | 26 |
30
+ | **Total** | **93.1%** | **167** |
31
+
32
+ ---
33
+
34
+ ## Related Work
35
+
36
+ See `plans/modules/aps-markdown-adapter.aps.md` for adapter integration.
37
+
38
+ ---
39
+
40
+ _Last updated: January 2026_
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Context Bundle Builder
3
+ *
4
+ * Generates context bundles for LLM consumption from filtered plans.
5
+ * Outputs both text (Markdown) and JSON formats.
6
+ */
7
+ import type { FilteredPlan } from './index.js';
8
+ /**
9
+ * Context bundle in JSON format
10
+ */
11
+ export interface ContextBundleJSON {
12
+ /** Plan title */
13
+ title: string;
14
+ /** Filter criteria that was applied */
15
+ filter: {
16
+ scopes?: string[];
17
+ modules?: string[];
18
+ tasks?: string[];
19
+ owners?: string[];
20
+ tags?: string[];
21
+ priorities?: string[];
22
+ confidences?: string[];
23
+ };
24
+ /** Summary statistics */
25
+ summary: {
26
+ totalModules: number;
27
+ totalTasks: number;
28
+ tasksByConfidence: {
29
+ high: number;
30
+ medium: number;
31
+ low: number;
32
+ };
33
+ tasksByStatus: {
34
+ open: number;
35
+ locked: number;
36
+ completed: number;
37
+ cancelled: number;
38
+ };
39
+ };
40
+ /** Modules with their tasks */
41
+ modules: Array<{
42
+ id: string;
43
+ scope?: string;
44
+ owner?: string;
45
+ priority?: string;
46
+ tags?: string[];
47
+ tasks: Array<{
48
+ id: string;
49
+ title: string;
50
+ intent: string;
51
+ confidence: string;
52
+ status: string;
53
+ scopes?: string[];
54
+ tags?: string[];
55
+ dependencies?: string[];
56
+ inputs?: string[];
57
+ expectedOutcome?: string;
58
+ }>;
59
+ }>;
60
+ /** Dependency graph (module -> dependencies) */
61
+ dependencyGraph: Record<string, string[]>;
62
+ }
63
+ /**
64
+ * Build a context bundle from a filtered plan
65
+ *
66
+ * @param filtered - The filtered plan
67
+ * @returns Context bundle in JSON format
68
+ */
69
+ export declare function buildContextBundleJSON(filtered: FilteredPlan): ContextBundleJSON;
70
+ /**
71
+ * Build a context bundle in Markdown text format
72
+ *
73
+ * @param filtered - The filtered plan
74
+ * @returns Markdown text suitable for LLM context
75
+ */
76
+ export declare function buildContextBundleText(filtered: FilteredPlan): string;
77
+ /**
78
+ * Build context for a single task (focused view)
79
+ */
80
+ export declare function buildTaskContext(filtered: FilteredPlan, taskId: string): string | null;
81
+ //# sourceMappingURL=context-bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-bundle.d.ts","sourceRoot":"","sources":["../../src/filter/context-bundle.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IAEd,uCAAuC;IACvC,MAAM,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;KACxB,CAAC;IAEF,yBAAyB;IACzB,OAAO,EAAE;QACP,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,iBAAiB,EAAE;YACjB,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,GAAG,EAAE,MAAM,CAAC;SACb,CAAC;QACF,aAAa,EAAE;YACb,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,SAAS,EAAE,MAAM,CAAC;YAClB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC;IAEF,+BAA+B;IAC/B,OAAO,EAAE,KAAK,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,KAAK,EAAE,KAAK,CAAC;YACX,EAAE,EAAE,MAAM,CAAC;YACX,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,MAAM,CAAC;YACf,UAAU,EAAE,MAAM,CAAC;YACnB,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;YAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;YAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;YACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;YAClB,eAAe,CAAC,EAAE,MAAM,CAAC;SAC1B,CAAC,CAAC;KACJ,CAAC,CAAC;IAEH,gDAAgD;IAChD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC3C;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,YAAY,GAAG,iBAAiB,CAqEhF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CA+FrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2DtF"}
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Context Bundle Builder
3
+ *
4
+ * Generates context bundles for LLM consumption from filtered plans.
5
+ * Outputs both text (Markdown) and JSON formats.
6
+ */
7
+ /**
8
+ * Build a context bundle from a filtered plan
9
+ *
10
+ * @param filtered - The filtered plan
11
+ * @returns Context bundle in JSON format
12
+ */
13
+ export function buildContextBundleJSON(filtered) {
14
+ const { plan, modules, tasks, criteria } = filtered;
15
+ // Build summary statistics
16
+ const summary = {
17
+ totalModules: modules.length,
18
+ totalTasks: tasks.length,
19
+ tasksByConfidence: {
20
+ high: tasks.filter((t) => t.confidence === 'high').length,
21
+ medium: tasks.filter((t) => t.confidence === 'medium').length,
22
+ low: tasks.filter((t) => t.confidence === 'low').length,
23
+ },
24
+ tasksByStatus: {
25
+ open: tasks.filter((t) => (t.status ?? 'open') === 'open').length,
26
+ locked: tasks.filter((t) => t.status === 'locked').length,
27
+ completed: tasks.filter((t) => t.status === 'completed').length,
28
+ cancelled: tasks.filter((t) => t.status === 'cancelled').length,
29
+ },
30
+ };
31
+ // Build module data with tasks
32
+ const moduleData = modules.map((module) => {
33
+ const moduleTasks = tasks.filter((t) => module.tasks.some((mt) => mt.id === t.id));
34
+ return {
35
+ id: module.id,
36
+ scope: module.metadata.scope,
37
+ owner: module.metadata.owner,
38
+ priority: module.metadata.priority,
39
+ tags: module.metadata.tags,
40
+ tasks: moduleTasks.map((t) => ({
41
+ id: t.id,
42
+ title: t.title,
43
+ intent: t.intent,
44
+ confidence: t.confidence,
45
+ status: t.status ?? 'open',
46
+ scopes: t.scopes,
47
+ tags: t.tags,
48
+ dependencies: t.dependencies,
49
+ inputs: t.inputs,
50
+ expectedOutcome: t.expectedOutcome,
51
+ })),
52
+ };
53
+ });
54
+ // Build dependency graph for filtered modules
55
+ const dependencyGraph = {};
56
+ for (const module of modules) {
57
+ const deps = plan.dependencyGraph.get(module.id) ?? [];
58
+ // Only include dependencies that are in the filtered set
59
+ const filteredDeps = deps.filter((d) => modules.some((m) => m.id === d));
60
+ dependencyGraph[module.id] = filteredDeps;
61
+ }
62
+ return {
63
+ title: plan.title,
64
+ filter: {
65
+ scopes: criteria.scopes,
66
+ modules: criteria.modules,
67
+ tasks: criteria.tasks,
68
+ owners: criteria.owners,
69
+ tags: criteria.tags,
70
+ priorities: criteria.priorities,
71
+ confidences: criteria.confidences,
72
+ },
73
+ summary,
74
+ modules: moduleData,
75
+ dependencyGraph,
76
+ };
77
+ }
78
+ /**
79
+ * Build a context bundle in Markdown text format
80
+ *
81
+ * @param filtered - The filtered plan
82
+ * @returns Markdown text suitable for LLM context
83
+ */
84
+ export function buildContextBundleText(filtered) {
85
+ const { plan, modules, tasks, criteria } = filtered;
86
+ const lines = [];
87
+ // Header
88
+ lines.push(`# ${plan.title}`);
89
+ lines.push('');
90
+ // Filter info
91
+ const filterParts = [];
92
+ if (criteria.scopes?.length)
93
+ filterParts.push(`scopes: ${criteria.scopes.join(', ')}`);
94
+ if (criteria.modules?.length)
95
+ filterParts.push(`modules: ${criteria.modules.join(', ')}`);
96
+ if (criteria.tasks?.length)
97
+ filterParts.push(`tasks: ${criteria.tasks.join(', ')}`);
98
+ if (criteria.owners?.length)
99
+ filterParts.push(`owners: ${criteria.owners.join(', ')}`);
100
+ if (criteria.tags?.length)
101
+ filterParts.push(`tags: ${criteria.tags.join(', ')}`);
102
+ if (criteria.priorities?.length)
103
+ filterParts.push(`priorities: ${criteria.priorities.join(', ')}`);
104
+ if (criteria.confidences?.length)
105
+ filterParts.push(`confidences: ${criteria.confidences.join(', ')}`);
106
+ if (filterParts.length > 0) {
107
+ lines.push(`> Filtered by: ${filterParts.join('; ')}`);
108
+ lines.push('');
109
+ }
110
+ // Summary
111
+ lines.push('## Summary');
112
+ lines.push('');
113
+ lines.push(`- **Modules:** ${modules.length}`);
114
+ lines.push(`- **Tasks:** ${tasks.length}`);
115
+ const highConfidence = tasks.filter((t) => t.confidence === 'high').length;
116
+ const mediumConfidence = tasks.filter((t) => t.confidence === 'medium').length;
117
+ const lowConfidence = tasks.filter((t) => t.confidence === 'low').length;
118
+ lines.push(`- **Confidence:** ${highConfidence} high, ${mediumConfidence} medium, ${lowConfidence} low`);
119
+ const openTasks = tasks.filter((t) => (t.status ?? 'open') === 'open').length;
120
+ const lockedTasks = tasks.filter((t) => t.status === 'locked').length;
121
+ const completedTasks = tasks.filter((t) => t.status === 'completed').length;
122
+ lines.push(`- **Status:** ${openTasks} open, ${lockedTasks} locked, ${completedTasks} completed`);
123
+ lines.push('');
124
+ // Modules and tasks
125
+ for (const module of modules) {
126
+ lines.push(`## Module: ${module.id}`);
127
+ lines.push('');
128
+ if (module.metadata.scope)
129
+ lines.push(`**Scope:** ${module.metadata.scope}`);
130
+ if (module.metadata.owner)
131
+ lines.push(`**Owner:** ${module.metadata.owner}`);
132
+ if (module.metadata.priority)
133
+ lines.push(`**Priority:** ${module.metadata.priority}`);
134
+ if (module.metadata.tags?.length)
135
+ lines.push(`**Tags:** ${module.metadata.tags.join(', ')}`);
136
+ const deps = plan.dependencyGraph.get(module.id) ?? [];
137
+ if (deps.length > 0)
138
+ lines.push(`**Dependencies:** ${deps.join(', ')}`);
139
+ lines.push('');
140
+ // Tasks for this module
141
+ const moduleTasks = tasks.filter((t) => module.tasks.some((mt) => mt.id === t.id));
142
+ if (moduleTasks.length === 0) {
143
+ lines.push('*No tasks match the filter criteria.*');
144
+ lines.push('');
145
+ continue;
146
+ }
147
+ lines.push('### Tasks');
148
+ lines.push('');
149
+ for (const task of moduleTasks) {
150
+ lines.push(`#### ${task.id}: ${task.title}`);
151
+ lines.push('');
152
+ lines.push(`**Intent:** ${task.intent}`);
153
+ lines.push(`**Confidence:** ${task.confidence}`);
154
+ lines.push(`**Status:** ${task.status ?? 'open'}`);
155
+ if (task.scopes?.length)
156
+ lines.push(`**Scopes:** ${task.scopes.join(', ')}`);
157
+ if (task.tags?.length)
158
+ lines.push(`**Tags:** ${task.tags.join(', ')}`);
159
+ if (task.dependencies?.length)
160
+ lines.push(`**Dependencies:** ${task.dependencies.join(', ')}`);
161
+ if (task.expectedOutcome)
162
+ lines.push(`**Expected Outcome:** ${task.expectedOutcome}`);
163
+ if (task.inputs?.length) {
164
+ lines.push('**Inputs:**');
165
+ for (const input of task.inputs) {
166
+ lines.push(`- ${input}`);
167
+ }
168
+ }
169
+ lines.push('');
170
+ }
171
+ }
172
+ return lines.join('\n');
173
+ }
174
+ /**
175
+ * Build context for a single task (focused view)
176
+ */
177
+ export function buildTaskContext(filtered, taskId) {
178
+ const task = filtered.tasks.find((t) => t.id === taskId);
179
+ if (!task)
180
+ return null;
181
+ const lines = [];
182
+ lines.push(`# Task: ${task.id}`);
183
+ lines.push('');
184
+ lines.push(`## ${task.title}`);
185
+ lines.push('');
186
+ lines.push(`**Intent:** ${task.intent}`);
187
+ lines.push('');
188
+ lines.push(`**Confidence:** ${task.confidence}`);
189
+ lines.push(`**Status:** ${task.status ?? 'open'}`);
190
+ if (task.scopes?.length) {
191
+ lines.push('');
192
+ lines.push(`**Scopes:** ${task.scopes.join(', ')}`);
193
+ lines.push('');
194
+ lines.push('> These scopes define what files/modules this task is allowed to modify.');
195
+ }
196
+ if (task.tags?.length) {
197
+ lines.push('');
198
+ lines.push(`**Tags:** ${task.tags.join(', ')}`);
199
+ }
200
+ if (task.dependencies?.length) {
201
+ lines.push('');
202
+ lines.push('## Dependencies');
203
+ lines.push('');
204
+ lines.push('This task depends on:');
205
+ for (const dep of task.dependencies) {
206
+ const depTask = filtered.plan.allTasks.find((t) => t.id === dep);
207
+ if (depTask) {
208
+ lines.push(`- **${dep}:** ${depTask.title} (${depTask.status ?? 'open'})`);
209
+ }
210
+ else {
211
+ lines.push(`- **${dep}:** (not found)`);
212
+ }
213
+ }
214
+ }
215
+ if (task.inputs?.length) {
216
+ lines.push('');
217
+ lines.push('## Inputs');
218
+ lines.push('');
219
+ for (const input of task.inputs) {
220
+ lines.push(`- ${input}`);
221
+ }
222
+ }
223
+ if (task.expectedOutcome) {
224
+ lines.push('');
225
+ lines.push('## Expected Outcome');
226
+ lines.push('');
227
+ lines.push(task.expectedOutcome);
228
+ }
229
+ return lines.join('\n');
230
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Filter module - Task and module filtering for APS plans
3
+ *
4
+ * Provides filtering capabilities for:
5
+ * - Scope-based filtering (by module scope or task scopes)
6
+ * - Module-based filtering (by module ID)
7
+ * - Task-based filtering (by task ID)
8
+ * - Metadata filtering (owner, tags, priority, confidence)
9
+ *
10
+ * Also provides context bundle generation for LLM consumption.
11
+ */
12
+ export { buildContextBundleJSON, buildContextBundleText, buildTaskContext, type ContextBundleJSON, } from './context-bundle.js';
13
+ import type { LoadedPlan, LoadedModule } from '../loader/index.js';
14
+ import type { Task, Priority, Confidence } from '../types/index.js';
15
+ /**
16
+ * Filter criteria for tasks and modules
17
+ */
18
+ export interface FilterCriteria {
19
+ /** Filter by scope (matches module scope or task scopes) */
20
+ scopes?: string[];
21
+ /** Filter by module ID */
22
+ modules?: string[];
23
+ /** Filter by specific task IDs */
24
+ tasks?: string[];
25
+ /** Filter by owner (e.g., @alice) */
26
+ owners?: string[];
27
+ /** Filter by tags (matches any) */
28
+ tags?: string[];
29
+ /** Filter by priority levels */
30
+ priorities?: Priority[];
31
+ /** Filter by confidence levels */
32
+ confidences?: Confidence[];
33
+ /** Filter by task status */
34
+ statuses?: Array<'open' | 'locked' | 'completed' | 'cancelled'>;
35
+ }
36
+ /**
37
+ * Result of filtering a plan
38
+ */
39
+ export interface FilteredPlan {
40
+ /** Original plan reference */
41
+ plan: LoadedPlan;
42
+ /** Filtered modules (only those matching criteria) */
43
+ modules: LoadedModule[];
44
+ /** Filtered tasks (only those matching criteria) */
45
+ tasks: Task[];
46
+ /** Applied filter criteria */
47
+ criteria: FilterCriteria;
48
+ }
49
+ /**
50
+ * Filter a loaded plan by the given criteria
51
+ *
52
+ * @param plan - The loaded plan to filter
53
+ * @param criteria - Filter criteria to apply
54
+ * @returns Filtered plan with matching modules and tasks
55
+ */
56
+ export declare function filterPlan(plan: LoadedPlan, criteria: FilterCriteria): FilteredPlan;
57
+ /**
58
+ * Filter tasks by scope (convenience function)
59
+ */
60
+ export declare function filterByScope(plan: LoadedPlan, scopes: string[]): Task[];
61
+ /**
62
+ * Filter tasks by module (convenience function)
63
+ */
64
+ export declare function filterByModule(plan: LoadedPlan, moduleIds: string[]): Task[];
65
+ /**
66
+ * Filter tasks by tags (convenience function)
67
+ */
68
+ export declare function filterByTags(plan: LoadedPlan, tags: string[]): Task[];
69
+ /**
70
+ * Filter tasks by owner (convenience function)
71
+ */
72
+ export declare function filterByOwner(plan: LoadedPlan, owners: string[]): Task[];
73
+ /**
74
+ * Filter tasks by priority (convenience function)
75
+ */
76
+ export declare function filterByPriority(plan: LoadedPlan, priorities: Priority[]): Task[];
77
+ /**
78
+ * Filter tasks by confidence (convenience function)
79
+ */
80
+ export declare function filterByConfidence(plan: LoadedPlan, confidences: Confidence[]): Task[];
81
+ /**
82
+ * Get tasks matching specific IDs
83
+ */
84
+ export declare function getTasksById(plan: LoadedPlan, taskIds: string[]): Task[];
85
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/filter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,gBAAgB,EAChB,KAAK,iBAAiB,GACvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAEjB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB,gCAAgC;IAChC,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;IAExB,kCAAkC;IAClC,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAE3B,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC,CAAC;CACjE;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,IAAI,EAAE,UAAU,CAAC;IAEjB,sDAAsD;IACtD,OAAO,EAAE,YAAY,EAAE,CAAC;IAExB,oDAAoD;IACpD,KAAK,EAAE,IAAI,EAAE,CAAC;IAEd,8BAA8B;IAC9B,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,GAAG,YAAY,CA+GnF;AAcD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAExE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAE5E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAErE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAExE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,CAEjF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,CAEtF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAExE"}