File size: 9,664 Bytes
eca5751
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
"""Code Documentation Skill - Sinh docstring/comment tự động.

Hỗ trợ Python (Google/NumPy/Sphinx), JS (JSDoc), TS (TSDoc), Go (godoc),
Rust (rustdoc), Java (Javadoc), với template per style.

Author: Hieu Louis (2026)
"""
from __future__ import annotations

from typing import Dict, List

from .base import Skill, SkillContext, SkillCategory, SkillPriority, SkillResult


class CodeDocumentationSkill(Skill):
    """Sinh docstring và comment cho function/class/module."""

    category = SkillCategory.DOCUMENTATION
    priority = SkillPriority.MEDIUM
    keywords: List[str] = [
        "docstring", "document function", "document class",
        "jsdoc", "javadoc", "godoc", "rustdoc", "tsdoc",
        "generate docs", "documentation", "tài liệu",
        "viết docstring", "comment code", "annotate",
    ]
    examples = [
        "Generate Google-style docstring for this Python function",
        "Write JSDoc for this JavaScript function",
        "Document all public methods of this class",
    ]

    @property
    def name(self) -> str:
        return "code_documentation"

    @property
    def description(self) -> str:
        return (
            "Sinh docstring/comment cho Python (Google/NumPy/Sphinx), "
            "JS (JSDoc), TS (TSDoc), Go (godoc), Rust (rustdoc), Java (Javadoc)."
        )

    def can_handle(self, prompt: str, context: SkillContext = None) -> float:
        prompt_lower = prompt.lower()
        score = 0.0
        for kw in self.keywords:
            if kw in prompt_lower:
                score += 0.16
        if "def " in prompt or "function " in prompt or "func " in prompt:
            score += 0.15
        return min(1.0, score)

    def execute(self, context: SkillContext) -> SkillResult:
        lang = (context.language or "python").lower()
        return SkillResult(
            success=True,
            output=f"[CodeDocumentation/{lang}] Docstring templates ready.",
            artifacts=[
                {"path": "docs/templates.md", "content": _DOCSTRING_TEMPLATES},
                {"path": "docs/jsdoc_template.md", "content": _JSDOC_TEMPLATE},
                {"path": "docs/strategy.md", "content": _DOC_STRATEGY},
            ],
            metadata={
                "skill": self.name,
                "language": lang,
                "styles": {
                    "python": ["google", "numpy", "sphinx", "rest"],
                    "javascript": ["jsdoc"],
                    "typescript": ["tsdoc (typedoc)"],
                    "go": ["godoc (no annotations)"],
                    "rust": ["rustdoc markdown"],
                    "java": ["javadoc"],
                    "kotlin": ["kdoc"],
                },
                "extraction_targets": [
                    "purpose (first sentence)",
                    "parameters (name, type, meaning, default, constraints)",
                    "return value (type, meaning, conditions)",
                    "raises/throws (exception types + when)",
                    "examples (doctest-runnable when possible)",
                    "side effects",
                    "deprecated + replacement",
                    "see also",
                ],
                "tooling": {
                    "python": "Sphinx + autodoc + napoleon + intersphinx",
                    "js": "TypeDoc (TS) / JSDoc (JS)",
                    "go": "godoc / pkg.go.dev",
                    "rust": "cargo doc",
                    "java": "Javadoc + Maven Javadoc plugin",
                },
                "validation": [
                    "doctest for Python examples",
                    "mypy/pyright on type annotations",
                    "lint: every public symbol has docs (CI check)",
                ],
            },
            suggestions=[
                "Pick style explicitly: 'google' / 'numpy' / 'sphinx' for Python",
                "Ask for doctest-runnable examples when applicable",
                "Document exceptions explicitly even if not raised directly",
            ],
        )


_DOCSTRING_TEMPLATES = '''# Python Docstring Templates

## Google style

```python
def compute_discount(cart, customer_tier, coupon=None):
    """Compute discount for a cart.

    Applies tiered discount rules based on cart total and customer tier.
    Discount is capped at 40% for retail customers.

    Args:
        cart: List of (sku, unit_price, quantity) tuples. Must be non-empty.
        customer_tier: One of "bronze", "silver", "gold". Case-insensitive.
        coupon: Optional coupon code. None for no coupon.

    Returns:
        Tuple of (discount_amount, final_total). discount_amount in
        [0, cart_subtotal]. final_total is non-negative.

    Raises:
        ValueError: If cart is empty or customer_tier is unknown.
        CouponExpiredError: If coupon code is past its expiry date.

    Examples:
        >>> compute_discount([("A1", 100, 2)], "gold")
        (20.0, 180.0)
    """
    ...
```

## NumPy style

```python
def compute_discount(cart, customer_tier, coupon=None):
    """Compute discount for a cart.

    Applies tiered discount rules based on cart total and customer tier.

    Parameters
    ----------
    cart : list[tuple[str, float, int]]
        List of (sku, unit_price, quantity) tuples. Must be non-empty.
    customer_tier : {"bronze", "silver", "gold"}
        Customer loyalty tier. Case-insensitive.
    coupon : str, optional
        Optional coupon code. None for no coupon.

    Returns
    -------
    tuple[float, float]
        (discount_amount, final_total). discount_amount in [0, subtotal].

    Raises
    ------
    ValueError
        If cart is empty or customer_tier is unknown.
    CouponExpiredError
        If coupon code is past its expiry date.

    Examples
    --------
    >>> compute_discount([("A1", 100, 2)], "gold")
    (20.0, 180.0)
    """
    ...
```

## Sphinx (reST) style

```python
def compute_discount(cart, customer_tier, coupon=None):
    """Compute discount for a cart.

    Applies tiered discount rules based on cart total and customer tier.

    :param cart: List of (sku, unit_price, quantity) tuples. Must be non-empty.
    :type cart: list[tuple[str, float, int]]
    :param customer_tier: One of "bronze", "silver", "gold". Case-insensitive.
    :type customer_tier: str
    :param coupon: Optional coupon code. None for no coupon.
    :type coupon: str | None
    :returns: (discount_amount, final_total).
    :rtype: tuple[float, float]
    :raises ValueError: If cart is empty or customer_tier is unknown.
    :raises CouponExpiredError: If coupon code is past expiry.

    Example::

        >>> compute_discount([("A1", 100, 2)], "gold")
        (20.0, 180.0)
    """
    ...
```
'''


_JSDOC_TEMPLATE = '''# JSDoc / TSDoc Template

```javascript
/**
 * Compute discount for a cart.
 *
 * Applies tiered discount rules based on cart total and customer tier.
 * Discount is capped at 40% for retail customers.
 *
 * @param {Array<{sku: string, unitPrice: number, quantity: number}>} cart
 *   List of cart items. Must be non-empty.
 * @param {"bronze" | "silver" | "gold"} customerTier
 *   Customer loyalty tier. Case-insensitive.
 * @param {string | null} [coupon=null]
 *   Optional coupon code. Pass null for no coupon.
 * @returns {{discountAmount: number, finalTotal: number}}
 *   Discount amount (0 <= d <= subtotal) and final total.
 * @throws {TypeError} If cart is empty.
 * @throws {CouponExpiredError} If coupon code is past expiry.
 *
 * @example
 * const { discountAmount, finalTotal } = computeDiscount(
 *   [{ sku: "A1", unitPrice: 100, quantity: 2 }],
 *   "gold"
 * );
 * // => { discountAmount: 20, finalTotal: 180 }
 *
 * @see {@link applyCoupon} for coupon resolution logic.
 * @since 1.2.0
 * @public
 */
function computeDiscount(cart, customerTier, coupon = null) {
  // ...
}
```

## TSDoc (TypeScript) variant

```typescript
/**
 * Compute discount for a cart.
 *
 * @param cart - List of cart items. Must be non-empty.
 * @param customerTier - Customer loyalty tier. Case-insensitive.
 * @param coupon - Optional coupon code. Pass null for no coupon.
 * @returns Discount amount and final total.
 * @throws {TypeError} If cart is empty.
 *
 * @example
 * ```ts
 * const r = computeDiscount([{ sku: "A1", unitPrice: 100, quantity: 2 }], "gold");
 * ```
 */
function computeDiscount(
  cart: CartItem[],
  customerTier: "bronze" | "silver" | "gold",
  coupon: string | null = null,
): { discountAmount: number; finalTotal: number } {
  // ...
}
```
'''


_DOC_STRATEGY = """# Documentation Generation Strategy

## Phase 1: Static Extraction
- Parse AST, collect: function signatures, parameter types, return types,
  raised exceptions, decorators, class hierarchy.
- Infer types when missing (mypy/pyright inference).

## Phase 2: Purpose Inference
- Heuristics: function name + first assignment + last return + called functions.
- LLM fallback: ask for one-sentence summary, validate against signature.

## Phase 3: Parameter Description
- Per parameter: infer from usage (read once? written? returned?).
- Look at type hints + constraint annotations.
- Generate description: "<param> is the <role>: <constraint>".

## Phase 4: Examples
- Generate 1 happy-path + 1 error example.
- Make examples doctest-runnable (Python) or runnable snippets (JS).

## Phase 5: Style Compliance
- Match existing docstring style in module (auto-detect: google/numpy/sphinx).
- Match indentation, line length, terminology.

## Phase 6: Validation
- doctest: every `>>>` block must pass.
- darglint / pydocstyle / flake8-docstrings lint.
- Verify all params in signature have `Args:` entries.
"""